static int scsirio(SDreq* r) { Proc *up = externup(); /* * Perform an I/O request, returning * -1 failure * 0 ok * 1 no medium present * 2 retry * The contents of r may be altered so the * caller should re-initialise if necesary. */ r->status = ~0; switch(r->unit->dev->ifc->rio(r)){ default: break; case SDcheck: if(!(r->flags & SDvalidsense)) break; switch(r->sense[2] & 0x0F){ case 0x00: /* no sense */ case 0x01: /* recovered error */ return 2; case 0x06: /* check condition */ /* * 0x28 - not ready to ready transition, * medium may have changed. * 0x29 - power on or some type of reset. */ if(r->sense[12] == 0x28 && r->sense[13] == 0) return 2; if(r->sense[12] == 0x29) return 2; break; case 0x02: /* not ready */ /* * If no medium present, bail out. * If unit is becoming ready, rather than not * not ready, wait a little then poke it again. */ if(r->sense[12] == 0x3A) break; if(r->sense[12] != 0x04 || r->sense[13] != 0x01) break; while(waserror()) ; tsleep(&up->sleep, return0, 0, 500); poperror(); scsitest(r); return 2; default: break; } break; case SDok: return 0; } return -1; }
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; }
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]; }
int scsiverify(SDunit* unit) { SDreq *r; int i, status; uchar *inquiry; if((r = malloc(sizeof(SDreq))) == nil) return 0; if((inquiry = sdmalloc(sizeof(unit->inquiry))) == nil){ free(r); return 0; } r->unit = unit; r->lun = 0; /* ??? */ memset(unit->inquiry, 0, sizeof(unit->inquiry)); r->write = 0; r->cmd[0] = 0x12; r->cmd[1] = r->lun<<5; r->cmd[4] = sizeof(unit->inquiry)-1; r->clen = 6; r->data = inquiry; r->dlen = sizeof(unit->inquiry)-1; r->flags = 0; r->status = ~0; if(unit->dev->ifc->rio(r) != SDok){ free(r); return 0; } memmove(unit->inquiry, inquiry, r->dlen); free(inquiry); SET(status); for(i = 0; i < 3; i++){ while((status = scsitest(r)) == SDbusy) ; if(status == SDok || status != SDcheck) break; if(!(r->flags & SDvalidsense)) break; if((r->sense[2] & 0x0F) != 0x02) continue; /* * Unit is 'not ready'. * If it is in the process of becoming ready or needs * an initialising command, set status so it will be spun-up * below. * If there's no medium, that's OK too, but don't * try to spin it up. */ if(r->sense[12] == 0x04){ if(r->sense[13] == 0x02 || r->sense[13] == 0x01){ status = SDok; break; } } if(r->sense[12] == 0x3A) break; } if(status == SDok){ /* * Try to ensure a direct-access device is spinning. * Don't wait for completion, ignore the result. */ if((unit->inquiry[0] & 0x1F) == 0){ memset(r->cmd, 0, sizeof(r->cmd)); r->write = 0; r->cmd[0] = 0x1B; r->cmd[1] = (r->lun<<5)|0x01; r->cmd[4] = 1; r->clen = 6; r->data = nil; r->dlen = 0; r->flags = 0; r->status = ~0; unit->dev->ifc->rio(r); } } free(r); if(status == SDok || status == SDcheck) return 1; return 0; }