123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423 |
- /*
- * interface to scsi devices via scsi(2) to sd(3),
- * which does not implement LUNs.
- */
- #include "all.h"
- #include "io.h"
- enum {
- Ninquiry = 255,
- Nsense = 255,
- CMDtest = 0x00,
- CMDreqsense = 0x03,
- CMDread6 = 0x08,
- CMDwrite6 = 0x0A,
- CMDinquiry = 0x12,
- CMDstart = 0x1B,
- CMDread10 = 0x28,
- CMDwrite10 = 0x2A,
- };
- typedef struct {
- Target target[NTarget];
- } Ctlr;
- static Ctlr scsictlr[MaxScsi];
- extern int scsiverbose;
- void
- scsiinit(void)
- {
- Ctlr *ctlr;
- int ctlrno, targetno;
- Target *tp;
- scsiverbose = 1;
- for(ctlrno = 0; ctlrno < MaxScsi; ctlrno++){
- ctlr = &scsictlr[ctlrno];
- memset(ctlr, 0, sizeof(Ctlr));
- for(targetno = 0; targetno < NTarget; targetno++){
- tp = &ctlr->target[targetno];
- qlock(tp);
- qunlock(tp);
- sprint(tp->id, "scsictlr#%d.%d", ctlrno, targetno);
- tp->ctlrno = ctlrno;
- tp->targetno = targetno;
- tp->inquiry = malloc(Ninquiry);
- tp->sense = malloc(Nsense);
- }
- }
- }
- static uchar lastcmd[16];
- static int lastcmdsz;
- 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;
- if (scsi(sc, cmd, cbytes, data, db, rw) >= 0)
- return STok;
- /* 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];
- memset(cmd, 0, sizeof cmd);
- cmd[0] = CMDtest;
- cmd[1] = lun<<5;
- return scsiexec(tp, SCSIread, cmd, sizeof cmd, 0, 0);
- }
- static int
- scsistart(Target* tp, char lun, int start)
- {
- uchar cmd[6];
- memset(cmd, 0, sizeof cmd);
- cmd[0] = CMDstart;
- cmd[1] = lun<<5;
- if(start)
- cmd[4] = 1;
- return scsiexec(tp, SCSIread, cmd, sizeof cmd, 0, 0);
- }
- static int
- scsiinquiry(Target* tp, char lun, int* nbytes)
- {
- uchar cmd[6];
- memset(cmd, 0, sizeof cmd);
- cmd[0] = CMDinquiry;
- cmd[1] = lun<<5;
- *nbytes = Ninquiry;
- cmd[4] = *nbytes;
- return scsiexec(tp, SCSIread, cmd, sizeof cmd, tp->inquiry, nbytes);
- }
- static char *key[] =
- {
- "no sense",
- "recovered error",
- "not ready",
- "medium error",
- "hardware error",
- "illegal request",
- "unit attention",
- "data protect",
- "blank check",
- "vendor specific",
- "copy aborted",
- "aborted command",
- "equal",
- "volume overflow",
- "miscompare",
- "reserved"
- };
- static int
- scsireqsense(Target* tp, char lun, int* nbytes, int quiet)
- {
- char *s;
- int n, status, try;
- uchar cmd[6], *sense;
- sense = tp->sense;
- for(try = 0; try < 20; try++) {
- memset(cmd, 0, sizeof cmd);
- cmd[0] = CMDreqsense;
- cmd[1] = lun<<5;
- cmd[4] = Ninquiry;
- memset(sense, 0, Ninquiry);
- *nbytes = Ninquiry;
- status = scsiexec(tp, SCSIread, cmd, sizeof cmd, sense, nbytes);
- if(status != STok)
- return status;
- *nbytes = sense[0x07]+8;
- 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;
- }
- }
- buggery:
- if(quiet == 0){
- s = key[sense[2]&0x0F];
- print("%s: reqsense: '%s' code #%2.2ux #%2.2ux\n",
- tp->id, s, sense[12], sense[13]);
- print("%s: byte 2: #%2.2ux, bytes 15-17: #%2.2ux #%2.2ux #%2.2ux\n",
- tp->id, sense[2], sense[15], sense[16], sense[17]);
- print("lastcmd (%d): ", lastcmdsz);
- for(n = 0; n < lastcmdsz; n++)
- print(" #%2.2ux", lastcmd[n]);
- print("\n");
- }
- return STcheck;
- }
- static Target*
- scsitarget(Device* d)
- {
- int ctlrno, targetno;
- ctlrno = d->wren.ctrl;
- if(ctlrno < 0 || ctlrno >= MaxScsi /* || scsictlr[ctlrno].io == nil */)
- return 0;
- targetno = d->wren.targ;
- if(targetno < 0 || targetno >= NTarget)
- return 0;
- return &scsictlr[ctlrno].target[targetno];
- }
- static void
- scsiprobe(Device* d)
- {
- Target *tp;
- int nbytes, s;
- uchar *sense;
- int acount;
- if((tp = scsitarget(d)) == 0)
- panic("scsiprobe: device = %Z", d);
- acount = 0;
- again:
- s = scsitest(tp, d->wren.lun);
- if(s < STok){
- print("%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){
- print("%s: starting...\n", tp->id);
- if(scsistart(tp, d->wren.lun, 1) == STok)
- break;
- s = scsireqsense(tp, d->wren.lun, &nbytes, 0);
- }
- }
- /*FALLTHROUGH*/
- default:
- print("%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) {
- print("%s: inquiry failed, status %d\n", tp->id, s);
- return;
- }
- print("%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;
- if((tp = scsitarget(d)) == 0)
- panic("scsiio: device = %Z", d);
- qlock(tp);
- if(tp->ok == 0)
- scsiprobe(d);
- qunlock(tp);
- s = STinit;
- for(e = 0; e < 10; e++){
- for(;;){
- nbytes = dbytes;
- s = scsiexec(tp, rw, cmd, cbytes, data, &nbytes);
- if(s == STok)
- break;
- s = scsireqsense(tp, d->wren.lun, &nbytes, 0);
- if(s == STblank && rw == SCSIread) {
- memset(data, 0, dbytes);
- return STok;
- }
- if(s != STok)
- break;
- }
- if(s == STok)
- break;
- }
- if(e)
- print("%s: retry %d cmd #%x\n", tp->id, e, cmd[0]);
- return s;
- }
- void
- newscsi(Device *d, Scsi *sc)
- {
- Target *tp;
- if((tp = scsitarget(d)) == nil)
- panic("newscsi: device = %Z", d);
- tp->sc = sc; /* connect Target to Scsi */
- }
|