2073 lines
66 KiB
C
2073 lines
66 KiB
C
#include "fx.h"
|
|
#include "dklabel.h"
|
|
#include <sys/termio.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/iobuf.h>
|
|
#include <sys/elog.h>
|
|
#include <sys/dksc.h> /* scsi.h included by dklabel.h */
|
|
#include <stddef.h> /* for offsetof() */
|
|
|
|
typedef struct mode_sense_data sndata_t;
|
|
typedef struct mode_select_data sldata_t;
|
|
|
|
#define CDC_BUF_UNITS 256 /* divisor for buffer full/empty ratios
|
|
for most newer drives */
|
|
|
|
/* if geometry or format page can't be read, use these
|
|
* values. I did default to 32 and 1, since I saw this most often
|
|
* on some vendors cd drives, but since number of cyls is a 16 bit
|
|
* quantity, those values overflowed on drives with a capacity
|
|
* > 1 Gbytes, keeping you from using all of the drive. This
|
|
* will take us up to 16 Gb for faked drives
|
|
*/
|
|
#define DEF_SECSTRK 64
|
|
#define DEF_HEADS 8
|
|
|
|
/* these pages are getting gross enough that the next time
|
|
around, I probably ought to just make an array of 0x40
|
|
of them.... */
|
|
sndata_t current_err_params;
|
|
sndata_t current_acc_params;
|
|
sndata_t current_geom_params;
|
|
sndata_t current_blk_params;
|
|
sndata_t current_cach_params;
|
|
sndata_t current_cach2_params;
|
|
sndata_t current_conn_params;
|
|
struct err_recov *current_err_page;
|
|
struct dev_format *current_acc_page;
|
|
struct dev_geometry *current_geom_page;
|
|
struct block_descrip *current_blk_page;
|
|
struct cachectrl *current_cach_page;
|
|
struct connparms *current_conn_page;
|
|
struct cachescsi2 *current_cach2_page;
|
|
struct common changeable[ALL+1]; /* for masking changeable pages */
|
|
struct common currentbits[ALL+1]; /* for masking changeable pages */
|
|
u_char pglengths[ALL+1]; /* lengths of each page for mode select,
|
|
sense. len of 0 means page not supported */
|
|
|
|
unsigned scsi_cap; /* need it global now for readinvh() */
|
|
|
|
static unsigned bd_blklen;
|
|
static unsigned long sel_bits;
|
|
|
|
#ifdef SMFD_NUMBER
|
|
# include <sys/smfd.h>
|
|
#endif /* SMFD_NUMBER */
|
|
|
|
struct dev_fdgeometry *current_fdgeom_page;
|
|
sndata_t current_fdgeom_params;
|
|
|
|
|
|
/* len is length of additional data plus 4 byte sense header
|
|
plus 8 byte block descriptor + 2 byte page header */
|
|
#define SENSE_LEN_ADD (8+4+2)
|
|
|
|
#undef DEBUGMODE
|
|
|
|
static void maskch(u_char *, u_char *, u_char *, u_char);
|
|
static void scsi_dumppage(unsigned pg, u_char *pbuf, int hasblk);
|
|
static int scsi_modesense(sndata_t *addr, u_char pgcode);
|
|
static int scsi_modeXselect(sndata_t *addr, int verbose);
|
|
static int scsi_modeselect(sldata_t *addr, int verbose);
|
|
static void defsub(int ix, struct defect_entry *bp, char *tgt);
|
|
static void print_def_list(struct defect_entry *def_start, size_t defect_cnt, char *header, int log);
|
|
void scsi_getsupported(void);
|
|
|
|
static int istoshiba156; /* hack hack */
|
|
|
|
/* used to convert all the two byte sequences from scsi modesense, etc.
|
|
* to shorts for printing, etc. */
|
|
static ushort
|
|
sc_bytes_sh(u_char *a)
|
|
{
|
|
return (((ushort)a[0])<<8) + (ushort)a[1];
|
|
}
|
|
|
|
/* used to convert all the 3 byte sequences from scsi modesense, etc.
|
|
* to uints for printing, etc. */
|
|
static uint
|
|
sc_3bytes_uint(u_char *a)
|
|
{
|
|
return (((uint)a[0])<<16) + (((uint)a[1])<<8) + (uint)a[2];
|
|
}
|
|
|
|
/* used to convert all the 4 byte sequences from scsi modesense, etc.
|
|
* to uints for printing, etc. */
|
|
static uint
|
|
sc_bytes_uint(u_char *a)
|
|
{
|
|
return (((uint)a[0])<<24) + (((uint)a[1])<<16) + (((uint)a[2])<<8)
|
|
+ (uint)a[3];
|
|
}
|
|
|
|
|
|
/* used to convert to the two byte sequences for scsi modesel, etc. */
|
|
static void
|
|
sc_sh_bytes(int from, u_char *to)
|
|
{
|
|
to[0] = (u_char)(from >> 8);
|
|
to[1] = (u_char)from;
|
|
}
|
|
|
|
/* used to convert to the three byte sequences for scsi modesel, etc. */
|
|
static void
|
|
sc_uint_3bytes(int from, u_char *to)
|
|
{
|
|
to[0] = (u_char)(from >> 16);
|
|
to[1] = (u_char)(from >> 8);
|
|
to[2] = (u_char)from;
|
|
}
|
|
|
|
|
|
void
|
|
scsi_dname(char *s, int len)
|
|
{
|
|
char p[SCSI_DEVICE_NAME_SIZE];
|
|
|
|
istoshiba156 = 0;
|
|
if(gioctl(DIOCDRIVETYPE, p) < 0) {
|
|
scerrwarn("problem getting drivetype");
|
|
*s = '\0';
|
|
}
|
|
else { /* copy and ensure null-termination */
|
|
if(len > sizeof(p))
|
|
len = sizeof(p) + 1;
|
|
strncpy(s, p, len);
|
|
s[len-1] = '\0';
|
|
/* Toshiba can't handle the PF bit on mode selects; everyone
|
|
else can, and some drives require it. All of them allow
|
|
saved params so far; WORMs, etc. may not, so this may need
|
|
to be modified later. */
|
|
if(bcmp("TOSHIBA MK156FB", p, 15) == 0) {
|
|
istoshiba156 = 1;
|
|
sel_bits = 1;
|
|
}
|
|
#ifdef SMFD_NUMBER
|
|
else if(drivernum == SMFD_NUMBER)
|
|
sel_bits = 0; /* floppies support neither */
|
|
#endif
|
|
else
|
|
sel_bits = 0x11;
|
|
/* EINVAL means we are really running under 3.2, so don't complain */
|
|
if(gioctl(DIOCSELFLAGS, (void *)sel_bits) < 0 && errno != EINVAL) {
|
|
err_fmt("Warning: unable to set modeselect flags");
|
|
sel_bits = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* unfortunately, the time to format isn't a linear function of
|
|
anything in particular; this is purely empirical. We print
|
|
the length of time because otherwise people might
|
|
think the drive is hung, since nothing is printed. Then they
|
|
reset the system, and of course the drive is hosed until they
|
|
re-format it, usually after calling the hotline.
|
|
Now that we support different block sizes, adjust for that also.
|
|
*/
|
|
|
|
void
|
|
do_scsiformat(int noask)
|
|
{
|
|
unsigned blksize=0, minit;
|
|
|
|
if(scsi_readcapacity(&blksize) == -1 || !blksize) {
|
|
/* make a reasonable guess... Happens sometimes
|
|
when the drive was only partially formatted before being
|
|
reset (or an earlier format failed) for some reason. As
|
|
with dksc driver, assume worst case for this. */
|
|
minit = 480;
|
|
goto doit;
|
|
}
|
|
|
|
/* allow for different block sizes when computing time.
|
|
* we are real close to overflowing 32 bits if we
|
|
* aren't careful, so divide first. */
|
|
blksize = (blksize/DEV_BSIZE) * DP(&vh)->dp_secbytes;
|
|
|
|
if(blksize < 1000) /* 48 tpi floppies */
|
|
minit = 1;
|
|
else if(blksize < 3000) /* 96 tpi floppies and most 3.5" */
|
|
minit = 2;
|
|
else if(blksize < 42000) /* 3.5" floptical */
|
|
minit = 25;
|
|
else if(blksize < (1024*1024))
|
|
/* less than .5 GB, allow fixed time */
|
|
minit = 45;
|
|
else {
|
|
/* aproximate as 50 MB/minute format time, round up to 5 min;
|
|
* formula still pretty much works for 9 and 18 GB drives, but
|
|
* increased the max... */
|
|
minit = (blksize/102400);
|
|
minit = (minit + 4) / 5;
|
|
minit *= 5;
|
|
if(minit > 600) minit = 600;
|
|
}
|
|
|
|
doit:
|
|
printf("format will take approximately %u minute%s ...\n",
|
|
minit, minit>1 ? "s" : "");
|
|
printf(
|
|
"\nA low level format is almost never necessary, and may cause drive\n"
|
|
"\tproblems. We suggest that you do not do a low level format\n"
|
|
"\tunless the disk is completely unusable, or known to be damaged\n"
|
|
"\tin a way that a low level format might fix.\n\n");
|
|
if (blksize >= 38 * 1000 * 1000) /* >= 19GB capacity */
|
|
printf("\n\t!!Warning!! If the drive takes more than 8 hours to format, then\n"
|
|
"\tit may not finish before the format command times out. Certain\n"
|
|
"\tdrives of over 18GB capacity are projected to require 8 hours,\n"
|
|
"\tand thus may not successfully complete a format with 'fx'.\n\n");
|
|
flushoutp(); /* primarily for -c, but also for output to a pipe */
|
|
if(!noask)
|
|
lastchance();
|
|
|
|
if (gioctl(DIOCFORMAT, 0) < 0)
|
|
scerrwarn("format of drive failed");
|
|
else
|
|
printf("format completed successfully\n");
|
|
/* re-read params, capacity, etc. after formatting. Some drives
|
|
won't report back changed capacity, etc. after a modesel, until
|
|
the format is done. (Do even if format fails, since format
|
|
might be done with default params instead of current). */
|
|
scsiset_dp(DP(&vh));
|
|
flushoutp(); /* primarily for -c, but also for output to a pipe */
|
|
}
|
|
|
|
static
|
|
scsi_modeselect(sldata_t *addr, int verbose)
|
|
{
|
|
struct dk_ioctl_data modeselect_data;
|
|
u_char pgcode, opgcode;
|
|
|
|
/* If the same data was last used for a sense, the high
|
|
bit on the page code will be set. Rather than fixing
|
|
all possible callers, just ensure that it's not set. */
|
|
opgcode = addr->dk_pages.common.pg_code;
|
|
addr->dk_pages.common.pg_code &= ALL;
|
|
pgcode = addr->dk_pages.common.pg_code;
|
|
if(pglengths[pgcode] == 0) {
|
|
#ifdef DEBUGMODE
|
|
err_fmt("page %d not supported for MODE SELECT\n",
|
|
pgcode);
|
|
#endif /* DEBUGMODE */
|
|
return -1;
|
|
}
|
|
/* +4 for the header block, +2 for the pgcode */
|
|
modeselect_data.i_len = pglengths[pgcode] + 4 + 2;
|
|
modeselect_data.i_addr = (caddr_t)addr;
|
|
|
|
/* mask off the non-changeable bits. etc. */
|
|
maskch(addr->dk_pages.common.pg_maxlen, changeable[pgcode].pg_maxlen,
|
|
currentbits[pgcode].pg_maxlen, pglengths[pgcode]);
|
|
/* no block descr to mask */
|
|
if(verbose) {
|
|
printf("will set:\n");
|
|
scsi_dumppage(opgcode, (u_char *)addr, 0);
|
|
}
|
|
if (gioctl(DIOCSELECT, &modeselect_data) < 0) {
|
|
if(sel_bits & 1) { /* maybe doesn't support saving;
|
|
* can't count on bit in sense header since too many
|
|
* SCSI 1 drives out there */
|
|
sel_bits &= ~1;
|
|
if(gioctl(DIOCSELFLAGS, (void *)sel_bits) == 0 &&
|
|
gioctl(DIOCSELECT, &modeselect_data) == 0)
|
|
return 0;
|
|
}
|
|
err_fmt("Warning: error setting drive parameters (page %d)",
|
|
pgcode);
|
|
return -1; /* caller should do a modesense again if
|
|
this struct will be re-used; otherwise may
|
|
print bogus info. */
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* doing a mode select in extended form with block desc. Usually
|
|
done with data from a mode sense. Note that some bits that are
|
|
valid in the header on a sense are reserved on a select (such
|
|
as DPOFUA), so we always clear those. This actually shows up
|
|
on new scsi 2 drives, such as the ELITE 2. */
|
|
static
|
|
scsi_modeXselect(sndata_t *addr, int verbose)
|
|
{
|
|
struct dk_ioctl_data modeselect_data;
|
|
u_char *pgcode, opgcode;
|
|
|
|
/* The high bit on the page code will be set from the sense;
|
|
clear here instead of all callers. We have to do it this way
|
|
because bd_len may be either 0 or 8... */
|
|
pgcode = &((u_char *)addr)[offsetof(sndata_t, dk_pages) +
|
|
offsetof(struct common, pg_code)];
|
|
addr->dpofua = 0; /* reserved on select */
|
|
if(addr->bd_len==0)
|
|
pgcode -= sizeof(addr->block_descrip);
|
|
else if(bd_blklen)
|
|
sc_uint_3bytes(bd_blklen, &addr->block_descrip[5]);
|
|
opgcode = *pgcode;
|
|
*pgcode &= ALL;
|
|
|
|
/* shouldn't happen, but... (6 because of the 4 byte header, and
|
|
2 bytes for pgcode and pglen */
|
|
if(pglengths[*pgcode] == 0 || addr->sense_len<6) {
|
|
#ifdef DEBUGMODE
|
|
err_fmt("page %d not supported for MODE SELECT\n",
|
|
*pgcode);
|
|
#endif /* DEBUGMODE */
|
|
return -1;
|
|
}
|
|
modeselect_data.i_len = addr->sense_len+1;
|
|
modeselect_data.i_addr = (caddr_t)addr;
|
|
|
|
/* these are reserved when selecting */
|
|
addr->reserv0 = addr->reserv1 = addr->sense_len = 0;
|
|
/* setting these to 0 preserves old semantics; some drives allow
|
|
* some of them to be changed. */
|
|
addr->mediatype = addr->wprot = addr->dpofua = 0;
|
|
|
|
/* mask off the non-changeable bits. etc. */
|
|
maskch(addr->dk_pages.common.pg_maxlen, changeable[*pgcode].pg_maxlen,
|
|
currentbits[*pgcode].pg_maxlen, pglengths[*pgcode]);
|
|
if(verbose) {
|
|
printf("will set:\n");
|
|
scsi_dumppage(opgcode, (u_char *)addr, 1);
|
|
}
|
|
if(gioctl(DIOCSELECT, &modeselect_data) < 0) {
|
|
if(sel_bits & 1) { /* maybe doesn't support saving;
|
|
* can't count on bit in sense header since too many
|
|
* SCSI 1 drives out there */
|
|
sel_bits &= ~1;
|
|
if(gioctl(DIOCSELFLAGS, (void *)sel_bits) == 0 &&
|
|
gioctl(DIOCSELECT, &modeselect_data) == 0)
|
|
return 0;
|
|
}
|
|
err_fmt("Warning: error setting drive parameters (page %d)",
|
|
*pgcode);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
struct defect_list {
|
|
struct defect_header defect_header;
|
|
struct defect_entry defect_entry[8190];
|
|
};
|
|
|
|
/*
|
|
* Get the current defect list.
|
|
* Type is the type of defect list that should be retrieved.
|
|
*/
|
|
static
|
|
get_scsidefects(struct defect_list *addr, uint len, u_char def_type)
|
|
{
|
|
struct dk_ioctl_data getdefects_data;
|
|
|
|
getdefects_data.i_addr = (caddr_t)addr;
|
|
getdefects_data.i_len = len;
|
|
getdefects_data.i_page = def_type;
|
|
|
|
if(gioctl(DIOCRDEFECTS, &getdefects_data) < 0) {
|
|
if(errno != EINVAL || len <= MAX_IOCTL_DATA)
|
|
err_fmt("Warning: error reading drive defects");
|
|
/* else the 3.3 fx is being used on 3.2, so don't report */
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* if log is non-zero, defects are shown as logical, not physical.
|
|
Unfortunately, some mfg (such as Toshiba) return the phys
|
|
format, even if you request log (which is ok by std), BUT
|
|
the returned fmt type is log, NOT phys (which is NOT ok by std).
|
|
(This was for drives made in 1988!)
|
|
We used to default to CHS, we now (1997) default to logical.
|
|
See showbb_func() in bb.c
|
|
|
|
To print just grown, we try that first, and if it fails, we do
|
|
it by getting all, then mfg, the eliminate mfg from entire
|
|
list. This is needed because many mfg's don't return just
|
|
grown list. Return 0 on success, 1 if error.
|
|
|
|
logical value really means:
|
|
-1 == byte from index, 1 == logical, 0 == c|h|s
|
|
*/
|
|
int
|
|
print_scsidefects(int logical, int listtype)
|
|
{
|
|
struct defect_list def_list;
|
|
size_t defect_cnt, cntdiv;
|
|
int rtype, type;
|
|
char *dtype;
|
|
|
|
/*** NOTE: logical won't work right for Toshiba, and maybe others ***/
|
|
if(logical == 0)
|
|
type = 5;
|
|
else if(logical == 1)
|
|
type = 0;
|
|
else if(logical == -1)
|
|
type = 4;
|
|
else {
|
|
errwarn("(internal) unknown badblock format requested\n");
|
|
type = 5; /* physical supported by the most mfg */
|
|
}
|
|
|
|
if(listtype == 1) { /* just mfg */
|
|
type |= 2<<3;
|
|
dtype = "manufacturers";
|
|
}
|
|
else if(listtype == 2) { /* just grown */
|
|
type |= 2<<2;
|
|
dtype = "grown";
|
|
}
|
|
else { /* combined, if neither set... */
|
|
type |= 3<<3;
|
|
dtype = "complete";
|
|
}
|
|
if (get_scsidefects(&def_list, sizeof(def_list), type))
|
|
return 1;
|
|
|
|
/* Check return type because drives (at least CDC Wren,
|
|
and HItachi 514C) return their default format with
|
|
a recovered error if given a type they don't support. */
|
|
rtype = def_list.defect_header.format_bits;
|
|
if(rtype != type) {
|
|
printf("drive doesn't support requested badblock format\n");
|
|
if(!(rtype&0x4))
|
|
logical = 1; /* logical block */
|
|
else if((rtype&7) == 5)
|
|
logical = 0; /* physical: cyl/head/sec */
|
|
else if((rtype&7) == 4)
|
|
logical = -1; /* byte from index: cyl/head/bytes */
|
|
else {
|
|
printf("returned type is a 'reserved' type, can't handle it\n");
|
|
return 1;
|
|
}
|
|
}
|
|
cntdiv = logical==1 ? sizeof(daddr_t) : sizeof(struct defect_entry);
|
|
defect_cnt = sc_bytes_sh(def_list.defect_header.defect_listlen);
|
|
defect_cnt /= cntdiv;
|
|
if((defect_cnt*cntdiv) > sizeof(def_list)) {
|
|
printf("claimed size of list (%d) is greater than requested, "
|
|
"probably not valid\n", defect_cnt);
|
|
defect_cnt = sizeof(def_list) / cntdiv;
|
|
}
|
|
if(defect_cnt == 0 && rtype != type)
|
|
return 1; /* something wrong, don't try to print.
|
|
* probably drive doesn't support format requested.
|
|
* See bug #554444, and calling code in bb.c */
|
|
print_def_list(def_list.defect_entry, defect_cnt,
|
|
dtype, logical);
|
|
return 0;
|
|
}
|
|
|
|
/* if logical, just treat as array of 4 byte longs (msb first)
|
|
* ix not used, but colprint passes it.
|
|
*/
|
|
/*ARGSUSED*/
|
|
void
|
|
deflogical( int ix, u_char *bp, char *tgt)
|
|
{
|
|
u_long bn = sc_bytes_uint(bp);
|
|
sprintf(tgt, "%lu", bn);
|
|
}
|
|
|
|
/* convert 4 bytes (either bytes to index, or sector in track) as returned
|
|
* by the read defect command into a long */
|
|
uint
|
|
defbytes_to_uint(u_char *a)
|
|
{
|
|
return sc_bytes_uint(a);
|
|
}
|
|
|
|
static
|
|
sort_log_def(const void *a, const void *b)
|
|
{
|
|
return (int)(defbytes_to_uint((u_char *)a) - defbytes_to_uint((u_char *)b));
|
|
}
|
|
|
|
/* note that def_sector can be either a sector #, or bytes from index; so
|
|
* for now no validity checking is done on it.
|
|
* ix not used, but colprint passes it.
|
|
*/
|
|
/*ARGSUSED*/
|
|
static void
|
|
defsub(int ix, struct defect_entry *bp, char *tgt)
|
|
{
|
|
u_int defect_cyl = sc_3bytes_uint(bp->def_cyl);
|
|
uint secnum = defbytes_to_uint(bp->def_sector);
|
|
|
|
if(secnum == 0xffffffff) /* whole track */
|
|
sprintf(tgt, "(%u/track %d)", defect_cyl, bp->def_head);
|
|
else
|
|
sprintf(tgt, "(%u/%d/%u)", defect_cyl, bp->def_head, secnum);
|
|
}
|
|
|
|
static
|
|
sort_phys_def(const void *av, const void *bv)
|
|
{
|
|
u_int cyla, cylb;
|
|
struct defect_entry *a = (struct defect_entry *)av;
|
|
struct defect_entry *b = (struct defect_entry *)bv;
|
|
|
|
cyla = sc_3bytes_uint(a->def_cyl);
|
|
cylb = sc_3bytes_uint(b->def_cyl);
|
|
if(cyla != cylb)
|
|
return cyla - cylb;
|
|
if(a->def_head != b->def_head)
|
|
return a->def_head - b->def_head;
|
|
return (int)(defbytes_to_uint(a->def_sector) - defbytes_to_uint(b->def_sector));
|
|
}
|
|
|
|
static void
|
|
print_def_list(struct defect_entry *def_start, size_t defect_cnt,
|
|
char *header, int log)
|
|
{
|
|
newline();
|
|
if(!defect_cnt) {
|
|
printf("No defects in %s list\n", header);
|
|
return;
|
|
}
|
|
|
|
printf("total %d in %s defect list\n", defect_cnt, header);
|
|
if(log>0) {
|
|
printf("Format == logical block\n");
|
|
/* sort so display looks 'better'. Not all mfg return
|
|
a sorted list */
|
|
fxsort((char *)def_start, defect_cnt,
|
|
sizeof(daddr_t), sort_log_def);
|
|
colprint(def_start, (int)defect_cnt, sizeof (daddr_t), 8, deflogical);
|
|
}
|
|
else {
|
|
if(log == 0)
|
|
printf("Format == (cylinder/head/sector) (physical, not logical)\n");
|
|
else
|
|
printf("Format == (cylinder/head/bytes from index) (physical, not logical)\n");
|
|
/* sort so display looks 'better'. Not all mfg return
|
|
a sorted list */
|
|
fxsort(def_start, defect_cnt,
|
|
sizeof(struct defect_entry), sort_phys_def);
|
|
colprint(def_start, (int)defect_cnt, sizeof *def_start, 11, defsub);
|
|
}
|
|
printf("total %d in %s defect list\n", defect_cnt, header);
|
|
}
|
|
|
|
scsi_addbb(uint bn)
|
|
{
|
|
u_char bbuf[MAX_BSIZE]; /* gwrite will use 'correct' secsz */
|
|
if(gioctl(DIOCADDBB, (void *)(ulong)bn))
|
|
return -1;
|
|
/* on at least some mfg's controllers (i.e. Toshiba 156FB),
|
|
the sparing doesn't take effect until the block is
|
|
re-written, so write a block to it. This could be enhanced
|
|
later to try to read the block first, in case error is
|
|
soft. */
|
|
TestPatFill(bbuf, DP(&vh)->dp_secbytes);
|
|
return gwrite(bn, bbuf, 1);
|
|
}
|
|
|
|
static
|
|
scsi_modesense(sndata_t *addr, u_char pgcode)
|
|
{
|
|
struct dk_ioctl_data modesense_data;
|
|
|
|
modesense_data.i_page = pgcode; /* before AND ! */
|
|
pgcode &= ALL;
|
|
if(!pglengths[pgcode]) {
|
|
#ifdef DEBUGMODE
|
|
err_fmt("page 0x%xd not supported for MODE SENSE\n", pgcode);
|
|
#endif /* DEBUGMODE */
|
|
return 1;
|
|
}
|
|
modesense_data.i_addr = (caddr_t)addr;
|
|
|
|
/* len is length of additional data plus 4 byte sense header
|
|
plus 8 byte block descriptor + 2 byte page header */
|
|
modesense_data.i_len = pglengths[pgcode] + SENSE_LEN_ADD;
|
|
if(modesense_data.i_len > 0xff) /* only one byte! (don't want modulo) */
|
|
modesense_data.i_len = 0xff;
|
|
|
|
if (gioctl(DIOCSENSE, &modesense_data) < 0) {
|
|
/* don't complain if ALL, caller gives slightly different msg */
|
|
if(pgcode != ALL)
|
|
err_fmt("Warning: problem reading drive parameters (page %d)", pgcode);
|
|
/* else get 'better' message in scsi_getsupported() */
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
scsi_readcapacity(unsigned *addr)
|
|
{
|
|
if (gioctl(DIOCREADCAPACITY, addr) < 0) {
|
|
*addr = 0; /* be paranoid */
|
|
err_fmt("Error reading drive capacity");
|
|
return -1;
|
|
}
|
|
/* always set drivecap when readcapacity succeeds, since pre-kudzu
|
|
* systems would not have filled it in, and besides, we use it in
|
|
* a number of places in fx, so we want it to be accurate. Doesn't
|
|
* matter too much if it's different from what was on disk, and doesn't
|
|
* get written out */
|
|
DP(&vh)->dp_drivecap = scsi_cap = *addr;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/* called early from check_dp() to be sure we have all needed
|
|
parameters. Also called after a 'set param'. It's really misnamed,
|
|
in that it sets (initializes) the params used by fx, but doesn't
|
|
set them for the device.
|
|
*/
|
|
void
|
|
scsiset_dp(struct device_parameters *dp)
|
|
{
|
|
unsigned blksize;
|
|
int flags, qdepth;
|
|
|
|
scsi_getsupported(); /* find supported pages and lengths */
|
|
get_scsi_param();
|
|
blksize = 0;
|
|
|
|
(void)scsi_readcapacity(&blksize);
|
|
flags = dp->dp_flags & DP_CTQ_EN;
|
|
qdepth = dp->dp_ctq_depth;
|
|
bzero((char *)dp, sizeof *dp);
|
|
dp->dp_drivecap = scsi_cap;
|
|
|
|
#ifdef SMFD_NUMBER
|
|
if(current_fdgeom_page && (drivernum == SMFD_NUMBER ||
|
|
(pglengths[DEV_FDGEOMETRY] && !pglengths[DEV_GEOMETRY]
|
|
&& !pglengths[DEV_FORMAT]))) {
|
|
if(drivernum != SMFD_NUMBER)
|
|
errwarn("device appears to be a floppy\n");
|
|
if(bd_blklen)
|
|
dp->dp_secbytes = bd_blklen;
|
|
else
|
|
dp->dp_secbytes = sc_bytes_sh(current_fdgeom_page->g_bytes_sec);
|
|
dp->dp_flags = flags;
|
|
}
|
|
else
|
|
#endif /* SMFD_NUMBER */
|
|
/* Too many places in this block to prefix each of them... */
|
|
if(pglengths[DEV_FORMAT] && current_acc_page &&
|
|
pglengths[DEV_GEOMETRY] && current_geom_page) {
|
|
/* protect against drives that don't implement these, and so
|
|
* (incorrectly IMHO) leave it 0. This has actually been
|
|
* observed on some SCSI RAM disks. */
|
|
if(!sc_bytes_sh(current_acc_page->f_trk_zone))
|
|
sc_sh_bytes(1, current_acc_page->f_trk_zone);
|
|
if(!sc_bytes_sh(current_acc_page->f_sec_trac)) {
|
|
sc_sh_bytes(DEF_SECSTRK, current_acc_page->f_sec_trac);
|
|
/* reasonable default; avoid divide by 0 */
|
|
errwarn("drive reports 0 sectors per track, assume %d\n",
|
|
sc_bytes_sh(current_acc_page->f_sec_trac));
|
|
}
|
|
|
|
if(!current_geom_page->g_nhead) {
|
|
current_geom_page->g_nhead = 1;
|
|
errwarn("drive reports 0 heads, assuming %d\n",
|
|
current_geom_page->g_nhead);
|
|
}
|
|
|
|
dp->dp_flags = flags;
|
|
|
|
if(!*LP(&sg)->d_name) /* may not have it yet */
|
|
scsi_dname(LP(&sg)->d_name, sizeof(LP(&sg)->d_name));
|
|
if(bd_blklen)
|
|
dp->dp_secbytes = bd_blklen;
|
|
else if(istoshiba156 && pglengths[BLOCK_DESCRIP] &&
|
|
current_blk_page && current_blk_page->b_blen)
|
|
dp->dp_secbytes = current_blk_page->b_blen;
|
|
else if(pglengths[DEV_FORMAT] && current_acc_page)
|
|
dp->dp_secbytes = sc_bytes_sh(current_acc_page->f_bytes_sec);
|
|
else if(pglengths[DEV_FDGEOMETRY])
|
|
dp->dp_secbytes = sc_bytes_sh(current_fdgeom_page->g_bytes_sec);
|
|
/* this horrible hack is done only so if we setup a drive with
|
|
* kudzu, that it won't crash a pre-kudzu prtvtoc, or cause a
|
|
* pre-kudzu fx to bitch. Do not use these fields elsewhere */
|
|
dp->_dp_cylinders = sc_bytes_sh(current_acc_page->f_alttrk_vol);
|
|
dp->_dp_heads = current_geom_page->g_nhead;
|
|
dp->_dp_sect = sc_bytes_sh(current_acc_page->f_sec_trac);
|
|
}
|
|
else {
|
|
/* This shows up on things like CD-ROM. This is sort of gross,
|
|
* but works and avoids alarming people (not to mention later
|
|
* coredumps in the partition code)... */
|
|
if(expert)
|
|
printf("Unable to get device geometry\n");
|
|
}
|
|
|
|
/* somewhat match old hack of couldn't get geometry */
|
|
if(!dp->_dp_heads) dp->_dp_heads = DEF_HEADS;
|
|
if(!dp->_dp_sect) dp->_dp_sect = DEF_SECSTRK;
|
|
if(!dp->_dp_cylinders)
|
|
dp->_dp_cylinders = scsi_cap/(dp->_dp_heads*dp->_dp_sect);
|
|
|
|
dp->dp_ctq_depth = qdepth;
|
|
if(!dp->dp_secbytes)
|
|
dp->dp_secbytes = DEV_BSIZE; /* best guess */
|
|
}
|
|
|
|
|
|
/* find supported pages and lengths */
|
|
void
|
|
scsi_getsupported(void)
|
|
{
|
|
sndata_t data;
|
|
register i, maxd, pgnum;
|
|
u_char *d = (u_char *)&data;
|
|
|
|
bzero(&data, sizeof(data));
|
|
bzero(pglengths, sizeof(pglengths)); /* in case not first call */
|
|
pglengths[ALL] = sizeof(data)-(1+SENSE_LEN_ADD);
|
|
/* some devices, like maxtor optical won't return default if no media
|
|
in drive (or unformatted), but will return current... This is
|
|
supposed to be fixed in new firmware soon, but it doesn't cost
|
|
anything but a kernel error message to try both. */
|
|
if(scsi_modesense(&data, ALL|CURRENT) &&
|
|
scsi_modesense(&data, ALL|DEFAULT)) {
|
|
pglengths[ALL] = 0;
|
|
err_fmt("Unable to get list of supported configuration pages");
|
|
return;
|
|
}
|
|
/* sense_len doesn't include itself; set pglengths[ALL] for completeness */
|
|
pglengths[ALL] = maxd = data.sense_len + 1;
|
|
if(data.bd_len > 7) {
|
|
/* Note that bd_blklen is the *logical* block size, and if
|
|
* available, is preferred over the *physical* sector size
|
|
* in f_bytes_sec, which may be different. (In fact, this
|
|
* is the case with syquest drives, and also with the
|
|
* Maxtor Tahiti II.) */
|
|
bd_blklen = sc_3bytes_uint(&data.block_descrip[5]);
|
|
i = 4 + data.bd_len; /* skip header and block descr */
|
|
}
|
|
else {
|
|
bd_blklen = 0; /* just in case we changed drives */
|
|
i = 4; /* just the header */
|
|
}
|
|
while(i < maxd) {
|
|
pgnum = d[i] & ALL;
|
|
pglengths[pgnum] = d[i+1];
|
|
i += pglengths[pgnum] + 2; /* +2 for header */
|
|
}
|
|
|
|
/* now get all the changeable fields in the pages so they can be
|
|
used as masks in modeselect. Similarly for the current bits,
|
|
so they can be OR'ed in wherever the changeable bits are 0
|
|
NOTE: this could be a problem, since some drives want to see
|
|
0 for non-changeable bits, and others want to see the current
|
|
bits. Neither SCSI1 nor SCSI2 are really quite clear what to
|
|
do about this....
|
|
Also note that the current bits don't really need to be re-obtained
|
|
after each change, since theoretically, we are only using them for
|
|
non-changeable values. In fact, however, some non-changeable bits
|
|
do get set as a side effect of other changes. Thus it is a GOOD
|
|
thing that this routine does in fact get called after each set
|
|
of changes to the parameters is made.
|
|
*/
|
|
for(i=0; i< ALL; i++) {
|
|
if(pglengths[i]) {
|
|
if(scsi_modesense(&data, i|CHANGEABLE)==0)
|
|
bcopy(&data.dk_pages.common, &changeable[i], pglengths[i]+2);
|
|
else /* probably a transient error of some kind, since they
|
|
just told us they supported the page.... */
|
|
printf("can't get changeable bits for page %x\n", i);
|
|
if(scsi_modesense(&data, i|CURRENT)==0)
|
|
bcopy(&data.dk_pages.common, ¤tbits[i], pglengths[i]+2);
|
|
else /* probably a transient error of some kind, since they
|
|
just told us they supported the page.... */
|
|
printf("can't get current values for page %x\n", i);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* create a 'standard' sgilabel for scsi drives */
|
|
void
|
|
scsiset_label(CBLOCK *sgijunk, int showname)
|
|
{
|
|
static struct disk_label generic_scsi_disk;
|
|
char name[SCSI_DEVICE_NAME_SIZE+1]; /* max len of inquiry string */
|
|
|
|
generic_scsi_disk.d_magic = D_MAGIC;
|
|
scsi_dname(name, sizeof (name));
|
|
if(showname)
|
|
printf("Scsi drive type == %s\n", name);
|
|
else
|
|
printf("...creating default sgiinfo\n");
|
|
#ifdef SMFD_NUMBER
|
|
if(drivernum == SMFD_NUMBER)
|
|
return; /* no label on floppies */
|
|
#endif /* SMFD_NUMBER */
|
|
strncpy(generic_scsi_disk.d_name, name, sizeof(name));
|
|
bcopy((char *)&generic_scsi_disk, sgijunk, sizeof(generic_scsi_disk));
|
|
}
|
|
|
|
/* Show the drive non-geometry parameters, either current, saved,
|
|
* mfg default, or changeable. We show geometry and params
|
|
* seperately, to be consistent with the 'set' menu.
|
|
* All the data should be zeroed if the sense might fail, OR if
|
|
* the page isn't supported. Otherwise bogus values can
|
|
* get printed.
|
|
*/
|
|
static void
|
|
showparam(u_char pgtype, char *dp_header)
|
|
{
|
|
sndata_t err_params;
|
|
sndata_t cach_params;
|
|
sndata_t cach2_params;
|
|
sndata_t conn_params;
|
|
sndata_t fdgeom_params;
|
|
struct err_recov *err_page;
|
|
struct cachectrl *cach_page;
|
|
struct cachescsi2 *cach2_page;
|
|
struct connparms *conn_page;
|
|
struct dev_fdgeometry *fdgeom_page;
|
|
|
|
/* bzero all the param blocks first, because sometimes earlier
|
|
firmware revs didn't support all the fields (and return less
|
|
data), and we sometimes need to check for non-zero fields.
|
|
*/
|
|
bzero(&err_params, sizeof(err_params));
|
|
bzero(&conn_params, sizeof(conn_params));
|
|
bzero(&cach2_params, sizeof(cach2_params));
|
|
bzero(&cach_params, sizeof(cach_params));
|
|
bzero(&fdgeom_params, sizeof(fdgeom_params));
|
|
|
|
if(pglengths[DEV_FDGEOMETRY]) { /* floppy */
|
|
/* need this for params, because for floppies, some of
|
|
* the non-geometry info is in this page */
|
|
scsi_modesense(&fdgeom_params, pgtype | DEV_FDGEOMETRY);
|
|
fdgeom_page = (struct dev_fdgeometry *)(fdgeom_params.block_descrip +
|
|
fdgeom_params.bd_len);
|
|
}
|
|
if(pglengths[CACHE_SCSI2]) { /* prefer 'standard' if avail */
|
|
scsi_modesense(&cach2_params, pgtype | CACHE_SCSI2);
|
|
cach2_page = (struct cachescsi2 *)
|
|
(cach2_params.block_descrip +
|
|
cach2_params.bd_len);
|
|
}
|
|
else
|
|
cach2_page = (struct cachescsi2 *)cach2_params.block_descrip;
|
|
if(pglengths[CACHE_CONTR]) { /* BUT, always do this
|
|
if supported, because some CDC/Imprimis/Seagate
|
|
drives need both bits set, and there is also c_ccen */
|
|
scsi_modesense(&cach_params, pgtype | CACHE_CONTR);
|
|
cach_page = (struct cachectrl *)(cach_params.block_descrip +
|
|
cach_params.bd_len);
|
|
}
|
|
else
|
|
cach_page = (struct cachectrl *)cach_params.block_descrip;
|
|
if(pglengths[CONN_PARMS]) {
|
|
scsi_modesense(&conn_params, pgtype | CONN_PARMS);
|
|
conn_page = (struct connparms *)(conn_params.block_descrip +
|
|
conn_params.bd_len);
|
|
}
|
|
else
|
|
conn_page = (struct connparms *)conn_params.block_descrip;
|
|
|
|
if(pglengths[ERR_RECOV]) {
|
|
scsi_modesense(&err_params, pgtype | ERR_RECOV);
|
|
err_page = (struct err_recov *)(err_params.block_descrip +
|
|
err_params.bd_len);
|
|
}
|
|
else
|
|
err_page = (struct err_recov *)err_params.block_descrip;
|
|
|
|
setoff("%s%s", dp_header, pgtype==CHANGEABLE?"":" parameters");
|
|
if(pgtype == CHANGEABLE) { /* no graceful way to get this simpler */
|
|
printf("error correction %x ",
|
|
(err_page->e_err_bits & E_DCR));
|
|
printf("data transfer on error %x\n",
|
|
(err_page->e_err_bits & E_DTE) ? 1 : 0);
|
|
printf("report recovered errors %x ",
|
|
(err_page->e_err_bits & E_PER) ? 1 : 0);
|
|
printf("delay for error recovery %x\n",
|
|
(err_page->e_err_bits & E_RC) ? 1 : 0);
|
|
printf("transfer bad data blocks %x ",
|
|
(err_page->e_err_bits & E_TB) ? 1 : 0);
|
|
printf("Error retry attempts %4x\n",
|
|
err_page->e_retry_count);
|
|
printf("auto bad block reallocation (write) %x\n",
|
|
(err_page->e_err_bits & E_AWRE) ? 1 : 0);
|
|
printf("auto bad block reallocation (read) %x\n",
|
|
(err_page->e_err_bits & E_ARRE) ? 1 : 0);
|
|
if(pglengths[CACHE_SCSI2]) { /* prefer scsi2 way */
|
|
printf("Drive read-ahead %4x ",
|
|
cach2_page->c_rcd ? 1 : 0);
|
|
printf("Drive buffered writes %4x\n",
|
|
cach2_page->c_wce ? 1 : 0);
|
|
printf("Drive disable prefetch %4x ",
|
|
sc_bytes_sh(cach2_page->c_predislen));
|
|
printf("Drive minimum prefetch %4x\n",
|
|
sc_bytes_sh(cach2_page->c_minpre));
|
|
printf("Drive maximum prefetch %4x ",
|
|
sc_bytes_sh(cach2_page->c_maxpre));
|
|
printf("Drive prefetch ceiling %4x\n",
|
|
sc_bytes_sh(cach2_page->c_maxpreceil));
|
|
printf("Number of cache segments %2x\n",
|
|
cach2_page->c_numseg);
|
|
}
|
|
else if(pglengths[CACHE_CONTR])
|
|
printf("Drive read-ahead %x\n",
|
|
cach_page->c_ce);
|
|
if(pglengths[CONN_PARMS]) {
|
|
printf("Read buffer ratio %4x ",
|
|
conn_page->c_bfull);
|
|
printf("Write buffer ratio %4x\n",
|
|
conn_page->c_bempty);
|
|
}
|
|
}
|
|
else {
|
|
printf("Error correction %s ",
|
|
(err_page->e_err_bits & E_DCR) ? "disabled" : "enabled ");
|
|
printf("%s data transfer on error\n",
|
|
(err_page->e_err_bits & E_DTE) ? "Disable" : "Enable");
|
|
printf("%s report recovered errors ",
|
|
(err_page->e_err_bits & E_PER) ? "Do" : "Don't");
|
|
if(err_page->e_err_bits & E_PER) printf(" ");
|
|
printf("%s delay for error recovery\n",
|
|
(err_page->e_err_bits & E_RC) ? "Don't" : "Do");
|
|
printf("%s transfer bad blocks ",
|
|
(err_page->e_err_bits & E_TB) ? "Do" : "Don't");
|
|
if(err_page->e_err_bits & E_TB) printf(" ");
|
|
printf("Error retry attempts %2d\n",
|
|
err_page->e_retry_count);
|
|
printf("%s auto bad block reallocation (read)\n",
|
|
(err_page->e_err_bits & E_ARRE) ? "Do" : "No");
|
|
printf("%s auto bad block reallocation (write)\n",
|
|
(err_page->e_err_bits & E_AWRE) ? "Do" : "No");
|
|
if(pglengths[CACHE_SCSI2]) { /* prefer scsi2 way */
|
|
printf("Drive readahead %sabled ",
|
|
cach2_page->c_rcd ? "dis" : " en");
|
|
printf("Drive buffered writes %sabled\n",
|
|
cach2_page->c_wce ? " en" : "dis");
|
|
printf("Drive disable prefetch %5u ",
|
|
sc_bytes_sh(cach2_page->c_predislen));
|
|
printf("Drive minimum prefetch %5u\n",
|
|
sc_bytes_sh(cach2_page->c_minpre));
|
|
printf("Drive maximum prefetch %5u ",
|
|
sc_bytes_sh(cach2_page->c_maxpre));
|
|
printf("Drive prefetch ceiling %5u\n",
|
|
sc_bytes_sh(cach2_page->c_maxpreceil));
|
|
printf("Number of cache segments %4u\n",
|
|
cach2_page->c_numseg ? cach2_page->c_numseg : 1);
|
|
}
|
|
else if(pglengths[CACHE_CONTR])
|
|
printf("Drive read-ahead %sabled\n",
|
|
cach_page->c_ce ? " en" : "dis");
|
|
if(pglengths[CONN_PARMS]) {
|
|
printf("Read buffer ratio %3d/%d",
|
|
conn_page->c_bfull, CDC_BUF_UNITS);
|
|
printf(" Write buffer ratio %3d/%d\n",
|
|
conn_page->c_bempty, CDC_BUF_UNITS);
|
|
}
|
|
printf("Command Tag Queueing ");
|
|
if (DP(&vh)->dp_flags & DP_CTQ_EN)
|
|
printf("enabled, maximum depth %3u\n",
|
|
DP(&vh)->dp_ctq_depth > 0 ? DP(&vh)->dp_ctq_depth : 255);
|
|
else
|
|
printf("disabled\n");
|
|
}
|
|
newline();
|
|
|
|
if(pglengths[DEV_FDGEOMETRY]) { /* floppy */
|
|
char *f;
|
|
int n;
|
|
if(pgtype == CHANGEABLE)
|
|
f = "%17s = %4x";
|
|
else
|
|
f = "%17s = %4d";
|
|
printf(f, "Spinup (.1 sec)",
|
|
fdgeom_page->g_moton);
|
|
printf(f, "Spindown on idle",
|
|
fdgeom_page->g_motoff);
|
|
n = sc_bytes_sh(fdgeom_page->g_headset);
|
|
/* assume 500Kbits/sec transfer rate */
|
|
if(pgtype != CHANGEABLE)
|
|
n /= 10; /* else show all changeable bits */
|
|
printf(f, " Head settle (ms)", n);
|
|
newline();
|
|
}
|
|
}
|
|
|
|
/* Show the drive geometry parameters, either current, saved,
|
|
* mfg default, or changeable. We show geometry and params
|
|
* seperately, to be consistent with the 'set' menu.
|
|
* All the data should be zeroed if the sense might fail, OR if
|
|
* the page isn't supported. Otherwise bogus values can
|
|
* get printed.
|
|
*/
|
|
static void
|
|
showgeom(u_char pgtype, char *dp_header)
|
|
{
|
|
char *f;
|
|
int n;
|
|
sndata_t acc_params;
|
|
sndata_t geom_params;
|
|
sndata_t fdgeom_params;
|
|
struct dev_format *acc_page;
|
|
struct dev_geometry *geom_page;
|
|
struct dev_fdgeometry *fdgeom_page;
|
|
|
|
/* bzero all the param blocks first, because sometimes earlier
|
|
firmware revs didn't support all the fields (and return less
|
|
data), and we sometimes need to check for non-zero fields.
|
|
*/
|
|
bzero(&geom_params, sizeof(geom_params));
|
|
bzero(&acc_params, sizeof(acc_params));
|
|
bzero(&fdgeom_params, sizeof(fdgeom_params));
|
|
|
|
if(pglengths[DEV_FDGEOMETRY]) { /* floppy */
|
|
scsi_modesense(&fdgeom_params, pgtype | DEV_FDGEOMETRY);
|
|
fdgeom_page = (struct dev_fdgeometry *)(fdgeom_params.block_descrip +
|
|
fdgeom_params.bd_len);
|
|
}
|
|
|
|
if(pglengths[DEV_FORMAT]) {
|
|
scsi_modesense(&acc_params, pgtype | DEV_FORMAT);
|
|
acc_page = (struct dev_format *)(acc_params.block_descrip +
|
|
acc_params.bd_len);
|
|
}
|
|
else if(!pglengths[DEV_FDGEOMETRY]) { /* only for non-floppy */
|
|
acc_page = (struct dev_format *)acc_params.block_descrip;
|
|
/* set these to avoid divide by 0 errors later; some drives,
|
|
such as Sony SMO-C501 optical don't support this page... */
|
|
sc_sh_bytes(1, acc_page->f_trk_zone);
|
|
sc_sh_bytes(1, acc_page->f_interleave);
|
|
sc_sh_bytes(DEF_SECSTRK, acc_page->f_sec_trac);
|
|
if(expert)
|
|
printf("Can't get drive geometry, assuming %d sectors/track\n",
|
|
sc_bytes_sh(acc_page->f_sec_trac));
|
|
}
|
|
if(pglengths[DEV_GEOMETRY]) {
|
|
scsi_modesense(&geom_params, pgtype | DEV_GEOMETRY);
|
|
geom_page = (struct dev_geometry *)(geom_params.block_descrip +
|
|
geom_params.bd_len);
|
|
}
|
|
else if(!pglengths[DEV_FDGEOMETRY]) { /* only for non-floppy */
|
|
unsigned blksize;
|
|
geom_page = (struct dev_geometry *)geom_params.block_descrip;
|
|
/* set these to avoid divide by 0 errors later; some drives,
|
|
such as Sony SMO-C501 optical don't support his page...
|
|
SCSI-2 says we should be able to get some of this info from
|
|
the density code in the param descr block, but the Sony
|
|
doesn't give us a descr block... */
|
|
geom_page->g_nhead = DEF_HEADS;
|
|
if(expert)
|
|
printf("Can't get drive geometry, assuming %d heads\n", DEF_HEADS);
|
|
/* do a readcapacity and then fake sec_trk and cyls */
|
|
if(scsi_readcapacity(&blksize) == 0) {
|
|
blksize /= sc_bytes_sh(acc_page->f_sec_trac) * DEF_HEADS;
|
|
sc_uint_3bytes(blksize, geom_page->g_ncyl);
|
|
}
|
|
else /* set to single cyl to avoid divide by zero problems later */
|
|
sc_uint_3bytes(1, geom_page->g_ncyl);
|
|
}
|
|
|
|
setoff("%s%s", dp_header, pgtype==CHANGEABLE?"":" geometry");
|
|
|
|
if(pgtype == CHANGEABLE)
|
|
f = "%17s = %4x";
|
|
else
|
|
f = "%17s = %4d";
|
|
|
|
/* set up so that 'related' items follow each other in
|
|
column's, not rows */
|
|
if(!pglengths[DEV_FDGEOMETRY]) { /* non-floppy */
|
|
printf(f, "Tracks/zone", sc_bytes_sh(acc_page->f_trk_zone));
|
|
printf(f, "Sect/track", sc_bytes_sh(acc_page->f_sec_trac));
|
|
newline();
|
|
printf(f, "Alt sect/zone", sc_bytes_sh(acc_page->f_altsec));
|
|
printf(f, "Interleave", sc_bytes_sh(acc_page->f_interleave));
|
|
printf(f, "Cylinders", sc_3bytes_uint(geom_page->g_ncyl));
|
|
newline();
|
|
printf(f, "Alt track/volume", sc_bytes_sh(acc_page->f_alttrk_vol));
|
|
printf(f, "Cylinder skew", sc_bytes_sh(acc_page->f_cylskew));
|
|
printf(f, "Heads", geom_page->g_nhead);
|
|
newline();
|
|
printf(f, "Alt track/zone", sc_bytes_sh(acc_page->f_alttrk_zone));
|
|
printf(f, "Track skew", sc_bytes_sh(acc_page->f_trkskew));
|
|
n = 0; /* for check below */
|
|
if(bd_blklen)
|
|
n = bd_blklen; /* use value from block descr */
|
|
else if(istoshiba156 && pglengths[BLOCK_DESCRIP] &&
|
|
current_blk_page && current_blk_page->b_blen) {
|
|
n = current_blk_page->b_blen;
|
|
}
|
|
else if(pglengths[DEV_FORMAT] && sc_bytes_sh(acc_page->f_bytes_sec))
|
|
n = sc_bytes_sh(acc_page->f_bytes_sec);
|
|
if(!n) { /* make it obvious if we don't know */
|
|
if(DP(&vh)->dp_secbytes) {
|
|
n = DP(&vh)->dp_secbytes;
|
|
}
|
|
else /* duplicate code in scsiset_dp */
|
|
n = DP(&vh)->dp_secbytes = DEV_BSIZE; /* best guess */
|
|
if(expert)
|
|
printf(" bytes/sec UNKNOWN, using %d", n);
|
|
else
|
|
printf(f, "Data bytes/sec", n);
|
|
}
|
|
else
|
|
printf(f, "Data bytes/sec", n);
|
|
|
|
if(geom_page->g_rotatrate[0] || geom_page->g_rotatrate[1]) {
|
|
newline();
|
|
printf(f, "Rotational rate", sc_bytes_sh(geom_page->g_rotatrate));
|
|
}
|
|
}
|
|
else { /* floppy */
|
|
printf(f, "Sectors/Track", fdgeom_page->g_spt);
|
|
printf(f, "Steps/Cylinder", fdgeom_page->g_stpcyl);
|
|
newline();
|
|
|
|
printf(f, "Cylinders", sc_bytes_sh(fdgeom_page->g_ncyl));
|
|
printf(f, "Heads", fdgeom_page->g_nhead);
|
|
newline();
|
|
printf(f, "Block len", sc_bytes_sh(fdgeom_page->g_bytes_sec));
|
|
}
|
|
newline();
|
|
}
|
|
|
|
static void
|
|
scsi_showdata(void (*showfunc)(u_char, char *))
|
|
{
|
|
char flags[4];
|
|
|
|
checkflags("cmds", flags);
|
|
|
|
if(flags[1])
|
|
(*showfunc)(CHANGEABLE, "modifiable fields: fields that are 0 aren't changeable");
|
|
else if(flags[2])
|
|
(*showfunc)(DEFAULT, "drive manufacturer's default");
|
|
else if(flags[3]) {
|
|
#ifdef SMFD_NUMBER
|
|
if(drivernum == SMFD_NUMBER) {
|
|
printf("no saved parameters for floppy disks\n");
|
|
return;
|
|
}
|
|
#endif
|
|
(*showfunc)(SAVED, "saved drive");
|
|
}
|
|
else /* 'c' or none */
|
|
(*showfunc)(CURRENT, "current drive");
|
|
}
|
|
|
|
|
|
/* show non-geometry parameters, defaulting to current
|
|
* values, but optionally allowing the others */
|
|
void
|
|
scsi_showparam_func(void)
|
|
{
|
|
scsi_showdata(showparam);
|
|
|
|
}
|
|
|
|
/* show geometry parameters, defaulting to current
|
|
* values, but optionally allowing the others */
|
|
void
|
|
scsi_showgeom_func(void)
|
|
{
|
|
scsi_showdata(showgeom);
|
|
}
|
|
|
|
|
|
ITEM set_ecc_items[] = {
|
|
{"enabled", 0},
|
|
{"disabled", 1},
|
|
{0},
|
|
};
|
|
|
|
ITEM set_ecc_items_bar[] = {
|
|
{"disabled", 0},
|
|
{"enabled", 1},
|
|
{0},
|
|
};
|
|
|
|
MENU set_ecc_menu = {
|
|
set_ecc_items, "ecc parameters"};
|
|
MENU set_ecc_menu_bar = {
|
|
set_ecc_items_bar, "ecc parameters"};
|
|
|
|
/* set geometry drive parameters. called via scsidata.M */
|
|
void
|
|
scsi_setgeom_func(void)
|
|
{
|
|
int current_val;
|
|
uint n;
|
|
STRBUF s;
|
|
sldata_t geom_params;
|
|
sndata_t acc_params;
|
|
int geomchange = 0;
|
|
|
|
sprintf(s.c,"NOTE: you will need to reformat the disk after changing\n"
|
|
"the drive geometry. This will cause all data on the drive\n"
|
|
"to be lost. Continue");
|
|
|
|
/* warn them they will have to reformat, unless they are already
|
|
* committed to it. If they are already committed, repeated
|
|
* warnings are annoying */
|
|
if(!formatrequired && !yesno(-1, s.c) )
|
|
return;
|
|
|
|
if(!current_geom_page) /* haven't read current yet! */
|
|
readin_dp();
|
|
|
|
bzero(&geom_params, sizeof(geom_params));
|
|
|
|
/* bcopy from current to get the block descr filled in */
|
|
if(current_acc_params.bd_len)
|
|
bcopy(¤t_acc_params, &acc_params,
|
|
pglengths[DEV_FORMAT] + 4 + 2);
|
|
else
|
|
bzero(&acc_params, sizeof(acc_params));
|
|
|
|
if(pglengths[DEV_GEOMETRY] && pglengths[DEV_FORMAT]) {
|
|
/* really need both, or have huge number of checks
|
|
* in this block. check in case fd opened as dksc */
|
|
/* max # of tracks / zone is number of tracks on drive */
|
|
n = sc_3bytes_uint(current_geom_page->g_ncyl);
|
|
if(!current_geom_page->g_nhead) {
|
|
current_geom_page->g_nhead = 1;
|
|
errwarn("drive reports 0 heads, assuming %d\n",
|
|
current_geom_page->g_nhead);
|
|
}
|
|
n *= current_geom_page->g_nhead;
|
|
argnum(&n, sc_bytes_sh(current_acc_page->f_trk_zone), n+1, "Tracks/zone");
|
|
if(sc_bytes_sh(current_acc_page->f_trk_zone) != n) {
|
|
geomchange = 1;
|
|
sc_sh_bytes(n, current_acc_page->f_trk_zone);
|
|
}
|
|
|
|
/* if they one more than a tracks worth, they should use trk_zone */
|
|
argnum(&n, sc_bytes_sh(current_acc_page->f_altsec),
|
|
sc_bytes_sh(current_acc_page->f_sec_trac), "Alt sect/zone");
|
|
if(sc_bytes_sh(current_acc_page->f_altsec) != n) {
|
|
geomchange = 1;
|
|
sc_sh_bytes(n, current_acc_page->f_altsec);
|
|
}
|
|
|
|
argnum(&n, sc_bytes_sh(current_acc_page->f_alttrk_zone),
|
|
sc_bytes_sh(current_acc_page->f_trk_zone)-1, "Alt track/zone");
|
|
if(sc_bytes_sh(current_acc_page->f_alttrk_zone) != n) {
|
|
geomchange = 1;
|
|
sc_sh_bytes(n, current_acc_page->f_alttrk_zone);
|
|
}
|
|
|
|
argnum(&n, sc_bytes_sh(current_acc_page->f_alttrk_vol),
|
|
current_geom_page->g_nhead*100, "Alt track/volume");
|
|
if(sc_bytes_sh(current_acc_page->f_alttrk_vol) != n) {
|
|
geomchange = 1;
|
|
sc_sh_bytes(n, current_acc_page->f_alttrk_vol);
|
|
}
|
|
|
|
argnum(&n, sc_bytes_sh(current_acc_page->f_trkskew),
|
|
sc_bytes_sh(current_acc_page->f_sec_trac), "Track Skew");
|
|
if(sc_bytes_sh(current_acc_page->f_trkskew) != n) {
|
|
geomchange = 1;
|
|
sc_sh_bytes(n, current_acc_page->f_trkskew);
|
|
}
|
|
|
|
argnum(&n, sc_bytes_sh(current_acc_page->f_cylskew),
|
|
sc_bytes_sh(current_acc_page->f_sec_trac), "Cylinder Skew");
|
|
if(sc_bytes_sh(current_acc_page->f_cylskew) != n) {
|
|
geomchange = 1;
|
|
sc_sh_bytes(n, current_acc_page->f_cylskew);
|
|
}
|
|
|
|
if(bd_blklen)
|
|
current_val = bd_blklen; /* use value from block descr */
|
|
else if(istoshiba156 && pglengths[BLOCK_DESCRIP] &&
|
|
current_blk_page && current_blk_page->b_blen)
|
|
current_val = current_blk_page->b_blen;
|
|
else if(pglengths[DEV_FORMAT] && sc_bytes_sh(current_acc_page->f_bytes_sec))
|
|
current_val = sc_bytes_sh(current_acc_page->f_bytes_sec);
|
|
else /* best guess... */
|
|
current_val = DEV_BSIZE;
|
|
|
|
argnum(&n, current_val, MAX_BSIZE+1, "Data bytes/sec");
|
|
/* some drives, such as Imprimis/Seagate only allow you to change
|
|
the block size by changing the block descr. Thus we do
|
|
a modeXselect for the acc page, and set it in the block
|
|
descr, as well as putting it in f_bytes_sec, which may
|
|
not be entirely correct (see bd_blklen disccusion in
|
|
scsi_getsupported()) */
|
|
if(current_val != n) {
|
|
geomchange = 1;
|
|
sc_sh_bytes(n, current_acc_page->f_bytes_sec);
|
|
bd_blklen = n;
|
|
}
|
|
|
|
}
|
|
#ifdef SMFD_NUMBER
|
|
else if(pglengths[DEV_FDGEOMETRY] && current_fdgeom_page) {
|
|
long on;
|
|
on = n = sc_bytes_sh(current_fdgeom_page->g_bytes_sec);
|
|
argnum(&n, n, 0x10000, "bytes/sect");
|
|
if(on != n) {
|
|
geomchange = 1;
|
|
sc_sh_bytes(n, current_fdgeom_page->g_bytes_sec);
|
|
}
|
|
|
|
on = n = sc_bytes_sh(current_fdgeom_page->g_ncyl);
|
|
argnum(&n, n, 0x10000, "Cylinders");
|
|
if(on != n) {
|
|
geomchange = 1;
|
|
sc_sh_bytes(n, current_fdgeom_page->g_ncyl);
|
|
}
|
|
|
|
argnum(&n, current_fdgeom_page->g_nhead, 0xff, "Heads");
|
|
if(current_fdgeom_page->g_nhead != n) {
|
|
geomchange = 1;
|
|
current_fdgeom_page->g_nhead = n;
|
|
}
|
|
|
|
argnum(&n, current_fdgeom_page->g_spt, 0xff, "Sectors/Track");
|
|
if(current_fdgeom_page->g_spt != n) {
|
|
geomchange = 1;
|
|
current_fdgeom_page->g_spt = n;
|
|
}
|
|
}
|
|
#endif /* SMFD_NUMBER */
|
|
else {
|
|
printf("Can't get any geometry info, sorry\n");
|
|
return;
|
|
}
|
|
|
|
lastchance_dp();
|
|
if(geomchange)
|
|
formatrequired = 1;
|
|
|
|
if(pglengths[DEV_FORMAT]) {
|
|
bcopy(current_acc_page, &acc_params.dk_pages.dev_format,
|
|
pglengths[DEV_FORMAT] + 4 + 2);
|
|
acc_params.bd_len = 8;
|
|
acc_params.sense_len = 13 + pglengths[DEV_FORMAT]; /* 8 + 6 - 1 */
|
|
/* Xselect because we want to set the block len in the
|
|
block descr. Force use of descr even if sense
|
|
didn't return it. */
|
|
scsi_modeXselect(&acc_params, 0);
|
|
}
|
|
if(pglengths[DEV_GEOMETRY]) {
|
|
bcopy(current_geom_page, &geom_params.dk_pages.dev_geometry,
|
|
pglengths[DEV_GEOMETRY] + 4 + 2);
|
|
scsi_modeselect(&geom_params, 0);
|
|
}
|
|
#ifdef SMFD_NUMBER
|
|
if(drivernum == SMFD_NUMBER && pglengths[DEV_FDGEOMETRY]) {
|
|
sldata_t fdgeom_params;
|
|
bzero(&fdgeom_params, sizeof(fdgeom_params));
|
|
|
|
/* NCR won't accept media type of 0 as being current... */
|
|
fdgeom_params.mediatype = current_fdgeom_params.mediatype;
|
|
|
|
bcopy(current_fdgeom_page,
|
|
&fdgeom_params.dk_pages.dev_fdgeometry,
|
|
pglengths[DEV_FDGEOMETRY] + 4 + 2);
|
|
scsi_modeselect(&fdgeom_params, 0);
|
|
scsi_modesense(¤t_fdgeom_params, DEV_FDGEOMETRY|CURRENT);
|
|
}
|
|
#endif /* SMFD_NUMBER */
|
|
|
|
if(pglengths[DEV_GEOMETRY])
|
|
scsi_modesense(¤t_geom_params, DEV_GEOMETRY|CURRENT);
|
|
if(pglengths[DEV_FORMAT])
|
|
scsi_modesense(¤t_acc_params, DEV_FORMAT|CURRENT);
|
|
|
|
/* and now set the values in the vh */
|
|
scsiset_dp(DP(&vh));
|
|
}
|
|
|
|
/* set non-geometry drive parameters. called via scsidata.M */
|
|
void
|
|
scsi_setparam_func(void)
|
|
{
|
|
int ecc_bit;
|
|
int current_val;
|
|
uint n;
|
|
sndata_t err_params;
|
|
sndata_t acc_params;
|
|
sldata_t cach_params;
|
|
sldata_t cach2_params;
|
|
sldata_t conn_params;
|
|
sldata_t fdgeom_params;
|
|
|
|
if(!current_err_page) /* haven't read current yet! */
|
|
readin_dp();
|
|
|
|
bzero(&err_params, sizeof(err_params));
|
|
bzero(&cach2_params, sizeof(cach2_params));
|
|
bzero(&cach_params, sizeof(cach_params));
|
|
bzero(&conn_params, sizeof(conn_params));
|
|
bzero(&fdgeom_params, sizeof(fdgeom_params));
|
|
|
|
/* bcopy from current to get the block descr filled in */
|
|
if(current_acc_params.bd_len)
|
|
bcopy(¤t_acc_params, &acc_params,
|
|
pglengths[DEV_FORMAT] + 4 + 2);
|
|
else
|
|
bzero(&acc_params, sizeof(acc_params));
|
|
|
|
#ifdef SMFD_NUMBER
|
|
if(drivernum == SMFD_NUMBER && pglengths[DEV_FDGEOMETRY]) {
|
|
argnum(&n, current_fdgeom_page->g_stpcyl, 0xff, "Step Pulses/Cylinder");
|
|
current_fdgeom_page->g_stpcyl = n;
|
|
|
|
argnum(&n, current_fdgeom_page->g_moton, 0xff,
|
|
"Time to spinup (.1 sec)");
|
|
current_fdgeom_page->g_moton = n;
|
|
|
|
argnum(&n, current_fdgeom_page->g_motoff, 0xff,
|
|
"Spin down after idle (.1 sec)");
|
|
current_fdgeom_page->g_motoff = n;
|
|
|
|
n = sc_bytes_sh(current_fdgeom_page->g_headset);
|
|
n /= 10; /* assume 500Kbits/sec transfer rate */
|
|
argnum(&n, n, 0x10000, "Head settle (ms)");
|
|
n *= 10;
|
|
sc_sh_bytes(n, current_fdgeom_page->g_headset);
|
|
|
|
bzero(&fdgeom_params, sizeof(fdgeom_params));
|
|
|
|
/* NCR won't accept media type of 0 as being current... */
|
|
fdgeom_params.mediatype = current_fdgeom_params.mediatype;
|
|
|
|
bcopy(current_fdgeom_page,
|
|
&fdgeom_params.dk_pages.dev_fdgeometry,
|
|
pglengths[DEV_FDGEOMETRY] + 4 + 2);
|
|
scsi_modeselect(&fdgeom_params, 0);
|
|
scsi_modesense(¤t_fdgeom_params, DEV_FDGEOMETRY|CURRENT);
|
|
|
|
return; /* none of these go in the vh struct */
|
|
}
|
|
#endif /* SMFD_NUMBER */
|
|
|
|
newline();
|
|
current_val = (current_err_page->e_err_bits & E_DCR) ? 1: 0;
|
|
argchoice(&ecc_bit, current_val, &set_ecc_menu, "Error correction");
|
|
if (ecc_bit)
|
|
current_err_page->e_err_bits |= E_DCR;
|
|
else
|
|
current_err_page->e_err_bits &= ~E_DCR;
|
|
|
|
current_val = (current_err_page->e_err_bits & E_DTE) ? 1: 0;
|
|
argchoice(&ecc_bit, current_val,
|
|
&set_ecc_menu, "Data transfer on error");
|
|
if (ecc_bit)
|
|
current_err_page->e_err_bits |= E_DTE;
|
|
else
|
|
current_err_page->e_err_bits &= ~E_DTE;
|
|
|
|
current_val = (current_err_page->e_err_bits & E_PER) ? 1: 0;
|
|
argchoice(&ecc_bit, current_val,
|
|
&set_ecc_menu_bar, "Report recovered errors");
|
|
if (ecc_bit)
|
|
current_err_page->e_err_bits |= E_PER;
|
|
else
|
|
current_err_page->e_err_bits &= ~E_PER;
|
|
|
|
current_val = (current_err_page->e_err_bits & E_RC) ? 1: 0;
|
|
argchoice(&ecc_bit, current_val,
|
|
&set_ecc_menu, "Delay for error recovery");
|
|
if (ecc_bit)
|
|
current_err_page->e_err_bits |= E_RC;
|
|
else
|
|
current_err_page->e_err_bits &= ~E_RC;
|
|
|
|
argnum(&n, current_err_page->e_retry_count, 100, "Err retry count");
|
|
current_err_page->e_retry_count = n;
|
|
current_val = (current_err_page->e_err_bits & E_TB) ? 1: 0;
|
|
argchoice(&ecc_bit, current_val,
|
|
&set_ecc_menu_bar, "Transfer of bad data blocks");
|
|
if (ecc_bit)
|
|
current_err_page->e_err_bits |= E_TB;
|
|
else
|
|
current_err_page->e_err_bits &= ~E_TB;
|
|
|
|
current_val = (current_err_page->e_err_bits & E_AWRE) ? 1: 0;
|
|
argchoice(&ecc_bit, current_val,
|
|
&set_ecc_menu_bar, "Auto bad block reallocation (write)");
|
|
if (ecc_bit)
|
|
current_err_page->e_err_bits |= E_AWRE;
|
|
else
|
|
current_err_page->e_err_bits &= ~E_AWRE;
|
|
current_val = (current_err_page->e_err_bits & E_ARRE) ? 1: 0;
|
|
argchoice(&ecc_bit, current_val,
|
|
&set_ecc_menu_bar, "Auto bad block reallocation (read)");
|
|
if (ecc_bit)
|
|
current_err_page->e_err_bits |= E_ARRE;
|
|
else
|
|
current_err_page->e_err_bits &= ~E_ARRE;
|
|
|
|
if(pglengths[CACHE_SCSI2]) { /* prefer 'standard' if avail */
|
|
current_val = current_cach2_page->c_rcd ? 0 : 1;
|
|
argchoice(&ecc_bit, current_val,
|
|
&set_ecc_menu_bar, "Read ahead caching");
|
|
current_cach2_page->c_rcd = ecc_bit ? 0 : 1;
|
|
|
|
current_val = current_cach2_page->c_wce;
|
|
argchoice(&ecc_bit, current_val,
|
|
&set_ecc_menu_bar, "Write buffering");
|
|
current_cach2_page->c_wce = ecc_bit ? 1 : 0;
|
|
|
|
n = sc_bytes_sh(current_cach2_page->c_predislen);
|
|
argnum(&n, n, 0x10000, "Drive disable prefetch");
|
|
sc_sh_bytes(n, current_cach2_page->c_predislen);
|
|
|
|
n = sc_bytes_sh(current_cach2_page->c_minpre);
|
|
argnum(&n, n, 0x10000, "Drive minimum prefetch");
|
|
sc_sh_bytes(n, current_cach2_page->c_minpre);
|
|
|
|
n = sc_bytes_sh(current_cach2_page->c_maxpre);
|
|
argnum(&n, n, 0x10000, "Drive maximum prefetch");
|
|
sc_sh_bytes(n, current_cach2_page->c_maxpre);
|
|
|
|
n = sc_bytes_sh(current_cach2_page->c_maxpreceil);
|
|
argnum(&n, n, 0x10000, "Drive prefetch ceiling");
|
|
sc_sh_bytes(n, current_cach2_page->c_maxpreceil);
|
|
if(pglengths[CACHE_CONTR]) {/* some of the CDC drives need it set */
|
|
/* in both pages, and the ccen is only in CACHE_CONTR */
|
|
current_cach_page->c_ccen = current_cach_page->c_ce =
|
|
~current_cach2_page->c_rcd;
|
|
}
|
|
|
|
if (((struct cachescsi2 *) &changeable[CACHE_SCSI2])->c_numseg)
|
|
{
|
|
n = (uint) current_cach2_page->c_numseg;
|
|
argnum(&n, n, 256, "Number of cache segments");
|
|
current_cach2_page->c_numseg = (u_char) n;
|
|
}
|
|
}
|
|
else {
|
|
current_val = current_cach_page->c_ce;
|
|
argchoice(&ecc_bit, current_val,
|
|
&set_ecc_menu_bar, "Read ahead caching");
|
|
if (ecc_bit)
|
|
current_cach_page->c_ce = 1;
|
|
else
|
|
current_cach_page->c_ce = 0;
|
|
/* set cyl cross enable to same as the cache enable */
|
|
current_cach_page->c_ccen = current_cach_page->c_ce;
|
|
}
|
|
|
|
current_val = (DP(&vh)->dp_flags & DP_CTQ_EN) ? 1: 0;
|
|
argchoice(&ecc_bit, current_val,
|
|
&set_ecc_menu_bar, "Enable CTQ");
|
|
if (ecc_bit) {
|
|
DP(&vh)->dp_flags |= DP_CTQ_EN;
|
|
n = (uint)(DP(&vh)->dp_ctq_depth);
|
|
if (n == 0)
|
|
n = 2;
|
|
argnum(&n, n, 255, "CTQ depth");
|
|
DP(&vh)->dp_ctq_depth = (u_char) n;
|
|
} else {
|
|
DP(&vh)->dp_flags &= ~DP_CTQ_EN;
|
|
DP(&vh)->dp_ctq_depth = 0;
|
|
}
|
|
|
|
/* connection params */
|
|
current_conn_page->c_bfull = getfrac(current_conn_page->c_bfull,
|
|
CDC_BUF_UNITS, "Read buffer ratio");
|
|
current_conn_page->c_bempty = getfrac(current_conn_page->c_bempty,
|
|
CDC_BUF_UNITS, "Write buffer ratio");
|
|
|
|
sc_uint_3bytes(n, &acc_params.block_descrip[5]);
|
|
sc_uint_3bytes(n, &err_params.block_descrip[5]);
|
|
|
|
lastchance_dp();
|
|
|
|
if(pglengths[ERR_RECOV]) {
|
|
/* this is a hack for Toshiba CDROM; I really should just
|
|
the bullet and convert all of these to use Xselect; Toshiba
|
|
doesn't support the format page, but you can change the
|
|
blocklen in the block descr, and it does support err page */
|
|
bcopy(current_err_page, &err_params.dk_pages.err_recov,
|
|
pglengths[ERR_RECOV] + 4 + 2);
|
|
err_params.bd_len = 8;
|
|
err_params.sense_len = 13 + pglengths[ERR_RECOV]; /* 8 + 6 - 1 */
|
|
/* Xselect because we want to set the block len in the
|
|
block descr. Force use of descr even if sense
|
|
didn't return it. */
|
|
scsi_modeXselect(&err_params, 0);
|
|
}
|
|
if(pglengths[CACHE_SCSI2]) { /* prefer 'standard' if avail */
|
|
bcopy(current_cach2_page, &cach2_params.dk_pages.cachescsi2,
|
|
pglengths[CACHE_SCSI2] + 4 + 2);
|
|
scsi_modeselect(&cach2_params, 0);
|
|
}
|
|
if(pglengths[CACHE_CONTR]) { /* BUT, always do this
|
|
if supported, because some CDC/Imprimis/Seagate
|
|
drives need both bits set, and there is also c_ccen */
|
|
bcopy(current_cach_page, &cach_params.dk_pages.cachctrl,
|
|
pglengths[CACHE_CONTR] + 4 + 2);
|
|
scsi_modeselect(&cach_params, 0);
|
|
}
|
|
if(pglengths[CONN_PARMS]) {
|
|
bcopy(current_conn_page, &conn_params.dk_pages.cparms,
|
|
pglengths[CONN_PARMS] + 4 + 2);
|
|
scsi_modeselect(&conn_params, 0);
|
|
}
|
|
|
|
/* NOTE: a sense is always done following the selects, since
|
|
* some drives are setting values in other pages as a side
|
|
* effect... Also, if the sector length was changed, then
|
|
* we want the global pages to get their blk descr's set;
|
|
* otherwise we might inadvertently set it back on a later
|
|
* Xselect. */
|
|
if(pglengths[CONN_PARMS])
|
|
scsi_modesense(¤t_conn_params, CONN_PARMS|CURRENT);
|
|
if(pglengths[CACHE_CONTR])
|
|
scsi_modesense(¤t_cach_params, CACHE_CONTR|CURRENT);
|
|
if(pglengths[CACHE_SCSI2])
|
|
scsi_modesense(¤t_cach2_params, CACHE_SCSI2|CURRENT);
|
|
if(pglengths[ERR_RECOV])
|
|
scsi_modesense(¤t_err_params, ERR_RECOV|CURRENT);
|
|
|
|
/* and now set the values in the vh; 2nd arg is 0 so
|
|
* we don't complain about geometry differences, since this
|
|
* routine doesn't change geometry. */
|
|
scsiset_dp(DP(&vh));
|
|
}
|
|
|
|
|
|
/* set all parameters to drive manufacturer's defaults, which might
|
|
* be different than the way SGI ships the drives. If the geometry
|
|
* would be different, warn them before doing anything.
|
|
*/
|
|
void
|
|
scsi_defparm_func(void)
|
|
{
|
|
sndata_t params;
|
|
int geomchanges = 0;
|
|
|
|
if(!current_geom_page) /* haven't read current yet! */
|
|
readin_dp();
|
|
|
|
#ifdef SMFD_NUMBER
|
|
if(drivernum == SMFD_NUMBER && pglengths[DEV_FDGEOMETRY]) {
|
|
struct dev_fdgeometry *fdg;
|
|
scsi_modesense(¶ms, DEV_FDGEOMETRY|DEFAULT);
|
|
fdg = (struct dev_fdgeometry *)(params.block_descrip + params.bd_len);
|
|
if(fdg->g_bytes_sec[1] != current_fdgeom_page->g_bytes_sec[1] ||
|
|
fdg->g_bytes_sec[0] != current_fdgeom_page->g_bytes_sec[0] ||
|
|
fdg->g_ncyl[1] != current_fdgeom_page->g_ncyl[1] ||
|
|
fdg->g_ncyl[0] != current_fdgeom_page->g_ncyl[0] ||
|
|
fdg->g_nhead != current_fdgeom_page->g_nhead ||
|
|
fdg->g_spt != current_fdgeom_page->g_spt)
|
|
geomchanges = 1;
|
|
bd_blklen = sc_bytes_sh(fdg->g_bytes_sec);
|
|
}
|
|
#endif /* SMFD_NUMBER */
|
|
|
|
if(pglengths[DEV_GEOMETRY] && !geomchanges) {
|
|
struct dev_geometry *geom;
|
|
scsi_modesense(¶ms, DEV_GEOMETRY|DEFAULT);
|
|
geom = (struct dev_geometry *)(params.block_descrip + params.bd_len);
|
|
if(!current_geom_page->g_nhead) {
|
|
/* no printf here; should have been reported before */
|
|
current_geom_page->g_nhead = 1;
|
|
}
|
|
if(geom->g_ncyl[2] != current_geom_page->g_ncyl[2] ||
|
|
geom->g_ncyl[1] != current_geom_page->g_ncyl[1] ||
|
|
geom->g_ncyl[0] != current_geom_page->g_ncyl[0] ||
|
|
geom->g_spindlesync != current_geom_page->g_spindlesync ||
|
|
geom->g_nhead != current_geom_page->g_nhead)
|
|
geomchanges = 1;
|
|
}
|
|
if(pglengths[DEV_FORMAT] && !geomchanges) {
|
|
struct dev_format *fmt;
|
|
scsi_modesense(¶ms, DEV_FORMAT|DEFAULT);
|
|
fmt = (struct dev_format *)(params.block_descrip + params.bd_len);
|
|
if(params.bd_len && bd_blklen != sc_3bytes_uint(¶ms.block_descrip[5]) ||
|
|
fmt->f_trk_zone[0] != current_acc_page->f_trk_zone[0] ||
|
|
fmt->f_trk_zone[1] != current_acc_page->f_trk_zone[1] ||
|
|
fmt->f_altsec[0] != current_acc_page->f_altsec[0] ||
|
|
fmt->f_altsec[1] != current_acc_page->f_altsec[1] ||
|
|
fmt->f_alttrk_zone[0] != current_acc_page->f_alttrk_zone[0] ||
|
|
fmt->f_alttrk_zone[1] != current_acc_page->f_alttrk_zone[1] ||
|
|
fmt->f_alttrk_vol[0] != current_acc_page->f_alttrk_vol[0] ||
|
|
fmt->f_alttrk_vol[1] != current_acc_page->f_alttrk_vol[1] ||
|
|
fmt->f_trkskew[0] != current_acc_page->f_trkskew[0] ||
|
|
fmt->f_trkskew[1] != current_acc_page->f_trkskew[1] ||
|
|
fmt->f_cylskew[0] != current_acc_page->f_cylskew[0] ||
|
|
fmt->f_cylskew[1] != current_acc_page->f_cylskew[1] ||
|
|
fmt->f_bytes_sec[0] != current_acc_page->f_bytes_sec[0] ||
|
|
fmt->f_bytes_sec[1] != current_acc_page->f_bytes_sec[1])
|
|
geomchanges = 1;
|
|
sc_uint_3bytes(bd_blklen, ¶ms.block_descrip[5]);
|
|
}
|
|
|
|
if(geomchanges) {
|
|
if(!yesno(-1, "NOTE: restoring drive manufacture default parameters will\n"
|
|
"change the drive geometry, which requires that the drive be\n"
|
|
"re-formatted. This will cause all data on the drive to be lost.\n"
|
|
"Continue"))
|
|
return;
|
|
}
|
|
|
|
do_scsidefault();
|
|
|
|
DP(&vh)->dp_flags = 0;
|
|
|
|
/* set the values in the vh struct from the current params */
|
|
scsiset_dp(DP(&vh));
|
|
return;
|
|
}
|
|
|
|
|
|
/* Set all parameters on all supported pages to drive manufacturer's defaults.
|
|
* Called from fx.c, smfd,c and scsi_defparm_func() above
|
|
*/
|
|
void
|
|
do_scsidefault(void)
|
|
{
|
|
sndata_t params;
|
|
register pg;
|
|
|
|
for(pg=0; pg<ALL; pg++)
|
|
if(pglengths[pg]) {
|
|
scsi_modesense(¶ms, DEFAULT | pg);
|
|
scsi_modeXselect(¶ms, 0);
|
|
}
|
|
}
|
|
|
|
|
|
/* get the current (not default) parameters for the scsi device */
|
|
void
|
|
get_scsi_param(void)
|
|
{
|
|
/* readin all the supported pages that fx uses.
|
|
data for unsupported pages should be zero'ed in
|
|
case user switches to a different drive. The
|
|
page ptrs should always be set to simplify the
|
|
code in other parts of fx. */
|
|
if(pglengths[DEV_FORMAT]) {
|
|
scsi_modesense(¤t_acc_params, DEV_FORMAT);
|
|
current_acc_page = (struct dev_format *)
|
|
(current_acc_params.block_descrip
|
|
+ current_acc_params.bd_len);
|
|
}
|
|
else if(!pglengths[DEV_FDGEOMETRY]) { /* only for non-floppy */
|
|
current_acc_page = (struct dev_format *)
|
|
(current_acc_params.block_descrip);
|
|
bzero(current_acc_page, sizeof(*current_acc_page));
|
|
/* set these to avoid divide by 0 errors later; some drives,
|
|
such as Sony SMO-C501 optical don't support this page... */
|
|
sc_sh_bytes(1, current_acc_page->f_trk_zone);
|
|
sc_sh_bytes(1, current_acc_page->f_interleave);
|
|
sc_sh_bytes(DEF_SECSTRK, current_acc_page->f_sec_trac);
|
|
if(expert)
|
|
printf("Can't get drive geometry, assuming %d sectors/track\n",
|
|
DEF_SECSTRK);
|
|
}
|
|
if(pglengths[DEV_GEOMETRY]) {
|
|
scsi_modesense(¤t_geom_params, DEV_GEOMETRY);
|
|
current_geom_page = (struct dev_geometry *)
|
|
(current_geom_params.block_descrip
|
|
+ current_geom_params.bd_len);
|
|
if(!current_geom_page->g_nhead) {
|
|
current_geom_page->g_nhead = 1;
|
|
errwarn("drive reports 0 heads, assuming %d\n",
|
|
current_geom_page->g_nhead);
|
|
}
|
|
}
|
|
else if(!pglengths[DEV_FDGEOMETRY]) { /* only for non-floppy */
|
|
unsigned blksize;
|
|
current_geom_page = (struct dev_geometry *)
|
|
(current_geom_params.block_descrip);
|
|
bzero(current_geom_page, sizeof(*current_geom_page));
|
|
/* set these to avoid divide by 0 errors later; some drives,
|
|
such as Sony SMO-C501 optical don't support his page...
|
|
SCSI-2 says we should be able to get some of this info from
|
|
the density code in the param descr block, but the Sony
|
|
doesn't give us a descr block... */
|
|
current_geom_page->g_nhead = DEF_HEADS;
|
|
if(expert)
|
|
printf("Can't get drive geometry, assuming %d heads\n", DEF_HEADS);
|
|
/* do a readcapacity and then fake sec_trk and cyls */
|
|
if(scsi_readcapacity(&blksize) == 0) {
|
|
blksize /= sc_bytes_sh(current_acc_page->f_sec_trac) * DEF_HEADS;
|
|
sc_uint_3bytes(blksize, current_geom_page->g_ncyl);
|
|
}
|
|
else /* set to single cyl to avoid divide by zero problems later */
|
|
sc_uint_3bytes(1, current_geom_page->g_ncyl);
|
|
}
|
|
if(pglengths[BLOCK_DESCRIP]) { /* damn toshiba */
|
|
scsi_modesense(¤t_blk_params, BLOCK_DESCRIP);
|
|
current_blk_page = (struct block_descrip *)
|
|
(current_blk_params.block_descrip +
|
|
current_blk_params.bd_len);
|
|
}
|
|
else {
|
|
current_blk_page = (struct block_descrip *)
|
|
(current_blk_params.block_descrip);
|
|
bzero(current_blk_page, sizeof(*current_blk_page));
|
|
}
|
|
|
|
if(pglengths[CACHE_SCSI2]) { /* prefer 'standard' if avail */
|
|
scsi_modesense(¤t_cach2_params, CACHE_SCSI2);
|
|
current_cach2_page = (struct cachescsi2 *)
|
|
(current_cach2_params.block_descrip +
|
|
current_cach2_params.bd_len);
|
|
}
|
|
else {
|
|
current_cach2_page = (struct cachescsi2 *)
|
|
(current_cach2_params.block_descrip);
|
|
bzero(current_cach2_page, sizeof(*current_cach2_page));
|
|
}
|
|
if(pglengths[CACHE_CONTR]) { /* BUT, always do this
|
|
if supported, because some CDC/Imprimis/Seagate
|
|
drives need both bits set, and there is also c_ccen */
|
|
scsi_modesense(¤t_cach_params, CACHE_CONTR);
|
|
current_cach_page = (struct cachectrl *)
|
|
(current_cach_params.block_descrip +
|
|
current_cach_params.bd_len);
|
|
}
|
|
else {
|
|
current_cach_page = (struct cachectrl *)
|
|
(current_cach_params.block_descrip);
|
|
bzero(current_cach_page, sizeof(*current_cach_page));
|
|
}
|
|
|
|
if(pglengths[CONN_PARMS]) {
|
|
scsi_modesense(¤t_conn_params, CONN_PARMS);
|
|
current_conn_page = (struct connparms *)
|
|
(current_conn_params.block_descrip +
|
|
current_conn_params.bd_len);
|
|
}
|
|
else {
|
|
current_conn_page = (struct connparms *)
|
|
(current_conn_params.block_descrip);
|
|
bzero(current_conn_page, sizeof(*current_conn_page));
|
|
}
|
|
/* fall through */
|
|
if(pglengths[DEV_FDGEOMETRY]) {
|
|
scsi_modesense(¤t_fdgeom_params, DEV_FDGEOMETRY);
|
|
current_fdgeom_page = (struct dev_fdgeometry *)
|
|
(current_fdgeom_params.block_descrip
|
|
+ current_fdgeom_params.bd_len);
|
|
}
|
|
if(pglengths[ERR_RECOV]) {
|
|
scsi_modesense(¤t_err_params, ERR_RECOV);
|
|
current_err_page = (struct err_recov *)
|
|
(current_err_params.block_descrip
|
|
+ current_err_params.bd_len);
|
|
}
|
|
else {
|
|
current_err_page = (struct err_recov *)
|
|
(current_err_params.block_descrip);
|
|
bzero(current_err_page, sizeof(*current_err_page));
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
lastchance_dp(void)
|
|
{
|
|
STRBUF s;
|
|
sprintf(s.c, "about to modify drive parameters on disk %s! ok", dksub());
|
|
banner("WARNING");
|
|
if( !yesno(-1, s.c) ) {
|
|
/* if not changing, re-read current values! */
|
|
get_scsi_param();
|
|
mpop();
|
|
}
|
|
changed = 1;
|
|
}
|
|
|
|
|
|
/* dump out a scsi mode-select page, which may or may not contain
|
|
* a block descriptor
|
|
*/
|
|
static void
|
|
scsi_dumppage(unsigned pg, u_char *pbuf, int hasblk)
|
|
{
|
|
u_char *a;
|
|
char *l;
|
|
int i;
|
|
#define PGTYPE(pg) ((pg)&(SAVED|CURRENT|DEFAULT|CHANGEABLE))
|
|
|
|
if(PGTYPE(pg)==SAVED)
|
|
l = "(saved";
|
|
else if(PGTYPE(pg)==DEFAULT)
|
|
l = "(default";
|
|
else if(PGTYPE(pg)==CHANGEABLE)
|
|
l = "(modifable";
|
|
else if(PGTYPE(pg)==CURRENT)
|
|
l = "(current";
|
|
printf("0x%02x %12s): 0x%02x", pg&ALL, l, pglengths[pg&ALL]);
|
|
printf(" hdr: %3x%3x%3x%3x", pbuf[0], pbuf[1],
|
|
pbuf[2], pbuf[3]);
|
|
if(hasblk && pbuf[3]) {
|
|
printf(" blk desc: %3x%3x%3x%3x%3x%3x%3x%3x",
|
|
pbuf[4], pbuf[5], pbuf[6], pbuf[7],
|
|
pbuf[8], pbuf[9], pbuf[10], pbuf[11]);
|
|
a = &pbuf[12];
|
|
}
|
|
else
|
|
a = &pbuf[4];
|
|
printf("\n pgdata: ");
|
|
for(i=0; i<(pglengths[pg&ALL]+2); i++) {
|
|
if((i%18) == 17)
|
|
printf("\n%13s", " ");
|
|
printf("%3x", *a++);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
/* called from the top level debug menu if expert mode. Show the
|
|
supported pages and their lengths.
|
|
*/
|
|
void
|
|
showpages_func(void)
|
|
{
|
|
register u_char pg;
|
|
int which = 0, anyflags = -1;
|
|
char flags[4];
|
|
|
|
checkflags("cmds", flags);
|
|
|
|
for(pg=0; pg < ALL; pg++) {
|
|
if(pglengths[pg]) {
|
|
register i;
|
|
if(which)
|
|
printf(" ");
|
|
if(anyflags) for(i=0; i<sizeof(flags); i++) {
|
|
if(flags[i]) {
|
|
sndata_t addr;
|
|
u_char type;
|
|
anyflags = 1;
|
|
if(i==0) type = CURRENT;
|
|
else if(i==1) type = CHANGEABLE;
|
|
else if(i==2) type = DEFAULT;
|
|
else if(i==3) type = SAVED;
|
|
if(scsi_modesense(&addr, pg|type) == 0)
|
|
scsi_dumppage(pg|type, (u_char *)&addr, 1);
|
|
}
|
|
}
|
|
if(anyflags <= 0) {
|
|
anyflags = 0; /* so for loop only done first page */
|
|
printf("0x%02x: 0x%02x", pg, pglengths[pg]);
|
|
if(++which == 6) {
|
|
which = 0;
|
|
printf("\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* do a mode select on an arbitrary page with arbitrary data. data not
|
|
entered at the end of the requested data is interpreted to be 0
|
|
*/
|
|
void
|
|
setpage_func(void)
|
|
{
|
|
uint i, pglen;
|
|
void *addr;
|
|
u_char *d, pg;
|
|
uint64_t *pgdata;
|
|
sndata_t sense;
|
|
/* large enough for largest possible returned data */
|
|
uint64_t pdata[256/sizeof(uint64_t)];
|
|
|
|
argnum(&i, 0, ALL, "page #");
|
|
pg = (u_char)i;
|
|
if(!pglengths[pg]) {
|
|
printf("device says page 0x%x isn't supported\n", pg);
|
|
return;
|
|
}
|
|
pglen = pglengths[pg];
|
|
pgdata = pdata;
|
|
addr = (void *)&sense;
|
|
|
|
/* in addition to being informative, this sets the header 'correctly' */
|
|
if(scsi_modesense((sndata_t *)addr, CURRENT | pg) == 0) {
|
|
if(((sndata_t *)addr)->bd_len == 0)
|
|
printf("block descriptors don't appear to be supported\n");
|
|
scsi_dumppage(pg|CURRENT, (u_char *)addr, 1);
|
|
}
|
|
|
|
if(!no("use block descriptor")) {
|
|
sense.bd_len = 8;
|
|
sense.sense_len = 13 + pglengths[pg]; /* 8 + 6 - 1 */
|
|
d = (u_char *)&sense.dk_pages;
|
|
printf("enter up to 8 bytes: ");
|
|
bzero(pgdata, 8*sizeof(*pgdata));
|
|
i = get_nums(0x100LL, pgdata, 8);
|
|
while(i-- > 0)
|
|
sense.block_descrip[i] = (u_char)pgdata[i];
|
|
}
|
|
else {
|
|
sense.sense_len = 0;
|
|
((sldata_t *)addr)->blk_len = 0;
|
|
d = (u_char *)&((sldata_t *)addr)->dk_pages;
|
|
}
|
|
printf("enter up to 0x%x bytes (skipping pgnum and len): ", pglen);
|
|
i = get_nums(0x100LL, pgdata, pglen);
|
|
if(i > 0) {
|
|
int zero = pglen - i;
|
|
*d++ = pg;
|
|
*d++ = pglen;
|
|
while(i-- > 0)
|
|
*d++ = (u_char)*pgdata++;
|
|
bzero(d, zero); /* zero rest */
|
|
if(sense.bd_len == 8)
|
|
i = scsi_modeXselect((sndata_t *)addr, 1);
|
|
else
|
|
i = scsi_modeselect((sldata_t *)addr, 1);
|
|
if(!i) {
|
|
printf("current parameters are:\n");
|
|
if(scsi_modesense(&sense, CURRENT | pg))
|
|
/* add a bit more to the message already printed */
|
|
printf("Strange: sense after select failed\n");
|
|
else
|
|
scsi_dumppage(pg|CURRENT, (u_char *)&sense, 1);
|
|
}
|
|
/* else error already reported */
|
|
}
|
|
/* else out of range or invalid #; already reported */
|
|
scsiset_dp(DP(&vh));
|
|
}
|
|
|
|
|
|
/* called from the top level debug menu if expert mode. Show the
|
|
capacity as returned by the readcapacity command.
|
|
*/
|
|
void
|
|
showcapacity_func(void)
|
|
{
|
|
unsigned blksize;
|
|
|
|
if(scsi_readcapacity(&blksize) == 0)
|
|
printf("capacity is %u blocks\n", blksize);
|
|
/* else error message already printed */
|
|
}
|
|
|
|
/* used to mask off bits that aren't changeable. Some drives are
|
|
not tolerant of attempts to change them, even if they are the
|
|
same as the current/default value.
|
|
|
|
NOTE: this could be a problem, since some drives want to see
|
|
0 for non-changeable bits, and others want to see the current
|
|
bits. Neither SCSI1 nor SCSI2 are really quite clear what to
|
|
do about this.... We have prevailed on most of our suppliers
|
|
to always accept the current values, even if the bits are NOT
|
|
changeable.
|
|
*/
|
|
static void
|
|
maskch(register u_char *set, register u_char *change, register u_char *cur,
|
|
u_char len)
|
|
{
|
|
register u_char i;
|
|
|
|
for(i=0; i < len; i++) {
|
|
*set &= *change;
|
|
*set |= ~(*change) & *cur;
|
|
set++, change++, cur++;
|
|
}
|
|
}
|
|
|