static int
sense2stcode(uchar *sense)
{
switch(sense[2] & 0x0F){
case 6: /* unit attention */
/*
* 0x28 - not ready to ready transition,
* medium may have changed.
* 0x29 - power on, RESET or BUS DEVICE RESET occurred.
*/
if(sense[12] != 0x28 && sense[12] != 0x29)
return STcheck;
/*FALLTHROUGH*/
case 0: /* no sense */
case 1: /* recovered error */
return STok;
case 8: /* blank data */
return STblank;
case 2: /* not ready */
if(sense[12] == 0x3A) /* medium not present */
return STcheck;
/*FALLTHROUGH*/
default:
/*
* If unit is becoming ready, rather than not ready,
* then wait a little then poke it again; should this
* be here or in the caller?
*/
if((sense[12] == 0x04 && sense[13] == 0x01)) {
// delay(500);
// scsitest(tp, lun);
fprint(2, "sense2stcode: unit becoming ready\n");
return STcheck; /* not exactly right */
}
return STcheck;
}
}
/*
* issue the SCSI command via scsi(2). lun must already be in cmd[1].
*/
static int
doscsi(Target* tp, int rw, uchar* cmd, int cbytes, void* data, int* dbytes)
{
int lun, db = 0;
uchar reqcmd[6], reqdata[Nsense], dummy[1];
Scsi *sc;
sc = tp->sc;
if (sc == nil)
panic("doscsi: nil tp->sc");
lun = cmd[1] >> 5; /* save lun in case we need it for reqsense */
/* cope with zero arguments */
if (dbytes != nil)
db = *dbytes;
if (data == nil)
data = dummy;
/* cmd failed, get whatever sense data we can */
memset(reqcmd, 0, sizeof reqcmd);
reqcmd[0] = CMDreqsense;
reqcmd[1] = lun<<5;
reqcmd[4] = Nsense;
memset(reqdata, 0, sizeof reqdata);
if (scsicmd(sc, reqcmd, sizeof reqcmd, reqdata, sizeof reqdata,
Sread) < 0)
return STharderr;
/* translate sense data to ST* codes */
return sense2stcode(reqdata);
}
static int
scsiexec(Target* tp, int rw, uchar* cmd, int cbytes, void* data, int* dbytes)
{
int s;
/*
* issue the SCSI command. lun must already be in cmd[1].
*/
s = doscsi(tp, rw, cmd, cbytes, data, dbytes);
switch(s){
case STcheck:
memmove(lastcmd, cmd, cbytes);
lastcmdsz = cbytes;
/*FALLTHROUGH*/
default:
/*
* It's more complicated than this. There are conditions which
* are 'ok' but for which the returned status code is not 'STok'.
* Also, not all conditions require a reqsense, there may be a
* need to do a reqsense here when necessary and making it
* available to the caller somehow.
*
* Later.
*/
break;
}
return s;
}
static int
scsitest(Target* tp, char lun)
{
uchar cmd[6];
switch(sense[2] & 0x0F){
case 6: /* unit attention */
/*
* 0x28 - not ready to ready transition,
* medium may have changed.
* 0x29 - power on, RESET or BUS DEVICE RESET occurred.
*/
if(sense[12] != 0x28 && sense[12] != 0x29)
goto buggery;
/*FALLTHROUGH*/
case 0: /* no sense */
case 1: /* recovered error */
return STok;
case 8: /* blank data */
return STblank;
case 2: /* not ready */
if(sense[12] == 0x3A) /* medium not present */
goto buggery;
/*FALLTHROUGH*/
default:
/*
* If unit is becoming ready, rather than not ready,
* then wait a little then poke it again; should this
* be here or in the caller?
*/
if((sense[12] == 0x04 && sense[13] == 0x01)){
delay(500);
scsitest(tp, lun);
break;
}
goto buggery;
}
}
acount = 0;
again:
s = scsitest(tp, d->wren.lun);
if(s < STok){
fprint(2, "%s: test, status %d\n", tp->id, s);
return;
}
/*
* Determine if the drive exists and is not ready or
* is simply not responding.
* If the status is OK but the drive came back with a 'power on' or
* 'reset' status, try the test again to make sure the drive is really
* ready.
* If the drive is not ready and requires intervention, try to spin it
* up.
*/
s = scsireqsense(tp, d->wren.lun, &nbytes, acount);
sense = tp->sense;
switch(s){
case STok:
if ((sense[2] & 0x0F) == 0x06 &&
(sense[12] == 0x28 || sense[12] == 0x29))
if(acount == 0){
acount = 1;
goto again;
}
break;
case STcheck:
if((sense[2] & 0x0F) == 0x02){
if(sense[12] == 0x3A)
break;
if(sense[12] == 0x04 && sense[13] == 0x02){
fprint(2, "%s: starting...\n", tp->id);
if(scsistart(tp, d->wren.lun, 1) == STok)
break;
s = scsireqsense(tp, d->wren.lun, &nbytes, 0);
}
}
/*FALLTHROUGH*/
default:
fprint(2, "%s: unavailable, status %d\n", tp->id, s);
return;
}
/*
* Inquire to find out what the device is.
* Hardware drivers may need some of the info.
*/
s = scsiinquiry(tp, d->wren.lun, &nbytes);
if(s != STok) {
fprint(2, "%s: inquiry failed, status %d\n", tp->id, s);
return;
}
fprint(2, "%s: %s\n", tp->id, (char*)tp->inquiry+8);
tp->ok = 1;
}
int
scsiio(Device* d, int rw, uchar* cmd, int cbytes, void* data, int dbytes)
{
Target *tp;
int e, nbytes, s;