1 /* 2 * interface to scsi devices via scsi(2) to sd(3), 3 * which does not implement LUNs. 4 */ 5 #include "all.h" 6 #include "io.h" 7 8 enum { 9 Ninquiry = 255, 10 Nsense = 255, 11 12 CMDtest = 0x00, 13 CMDreqsense = 0x03, 14 CMDread6 = 0x08, 15 CMDwrite6 = 0x0A, 16 CMDinquiry = 0x12, 17 CMDstart = 0x1B, 18 CMDread10 = 0x28, 19 CMDwrite10 = 0x2A, 20 }; 21 22 typedef struct { 23 Target target[NTarget]; 24 } Ctlr; 25 static Ctlr scsictlr[MaxScsi]; 26 27 extern int scsiverbose; 28 29 void 30 scsiinit(void) 31 { 32 Ctlr *ctlr; 33 int ctlrno, targetno; 34 Target *tp; 35 36 scsiverbose = 1; 37 for(ctlrno = 0; ctlrno < MaxScsi; ctlrno++){ 38 ctlr = &scsictlr[ctlrno]; 39 memset(ctlr, 0, sizeof(Ctlr)); 40 for(targetno = 0; targetno < NTarget; targetno++){ 41 tp = &ctlr->target[targetno]; 42 43 qlock(tp); 44 qunlock(tp); 45 sprint(tp->id, "scsictlr#%d.%d", ctlrno, targetno); 46 47 tp->ctlrno = ctlrno; 48 tp->targetno = targetno; 49 tp->inquiry = malloc(Ninquiry); 50 tp->sense = malloc(Nsense); 51 } 52 } 53 } 54 55 static uchar lastcmd[16]; 56 static int lastcmdsz; 57 58 static int 59 sense2stcode(uchar *sense) 60 { 61 switch(sense[2] & 0x0F){ 62 case 6: /* unit attention */ 63 /* 64 * 0x28 - not ready to ready transition, 65 * medium may have changed. 66 * 0x29 - power on, RESET or BUS DEVICE RESET occurred. 67 */ 68 if(sense[12] != 0x28 && sense[12] != 0x29) 69 return STcheck; 70 /*FALLTHROUGH*/ 71 case 0: /* no sense */ 72 case 1: /* recovered error */ 73 return STok; 74 case 8: /* blank data */ 75 return STblank; 76 case 2: /* not ready */ 77 if(sense[12] == 0x3A) /* medium not present */ 78 return STcheck; 79 /*FALLTHROUGH*/ 80 default: 81 /* 82 * If unit is becoming ready, rather than not ready, 83 * then wait a little then poke it again; should this 84 * be here or in the caller? 85 */ 86 if((sense[12] == 0x04 && sense[13] == 0x01)) { 87 // delay(500); 88 // scsitest(tp, lun); 89 fprint(2, "sense2stcode: unit becoming ready\n"); 90 return STcheck; /* not exactly right */ 91 } 92 return STcheck; 93 } 94 } 95 96 /* 97 * issue the SCSI command via scsi(2). lun must already be in cmd[1]. 98 */ 99 static int 100 doscsi(Target* tp, int rw, uchar* cmd, int cbytes, void* data, int* dbytes) 101 { 102 int lun, db = 0; 103 uchar reqcmd[6], reqdata[Nsense], dummy[1]; 104 Scsi *sc; 105 106 sc = tp->sc; 107 if (sc == nil) 108 panic("doscsi: nil tp->sc"); 109 lun = cmd[1] >> 5; /* save lun in case we need it for reqsense */ 110 111 /* cope with zero arguments */ 112 if (dbytes != nil) 113 db = *dbytes; 114 if (data == nil) 115 data = dummy; 116 117 if (scsi(sc, cmd, cbytes, data, db, rw) >= 0) 118 return STok; 119 120 /* cmd failed, get whatever sense data we can */ 121 memset(reqcmd, 0, sizeof reqcmd); 122 reqcmd[0] = CMDreqsense; 123 reqcmd[1] = lun<<5; 124 reqcmd[4] = Nsense; 125 memset(reqdata, 0, sizeof reqdata); 126 if (scsicmd(sc, reqcmd, sizeof reqcmd, reqdata, sizeof reqdata, 127 Sread) < 0) 128 return STharderr; 129 130 /* translate sense data to ST* codes */ 131 return sense2stcode(reqdata); 132 } 133 134 static int 135 scsiexec(Target* tp, int rw, uchar* cmd, int cbytes, void* data, int* dbytes) 136 { 137 int s; 138 139 /* 140 * issue the SCSI command. lun must already be in cmd[1]. 141 */ 142 s = doscsi(tp, rw, cmd, cbytes, data, dbytes); 143 switch(s){ 144 145 case STcheck: 146 memmove(lastcmd, cmd, cbytes); 147 lastcmdsz = cbytes; 148 /*FALLTHROUGH*/ 149 150 default: 151 /* 152 * It's more complicated than this. There are conditions which 153 * are 'ok' but for which the returned status code is not 'STok'. 154 * Also, not all conditions require a reqsense, there may be a 155 * need to do a reqsense here when necessary and making it 156 * available to the caller somehow. 157 * 158 * Later. 159 */ 160 break; 161 } 162 163 return s; 164 } 165 166 static int 167 scsitest(Target* tp, char lun) 168 { 169 uchar cmd[6]; 170 171 memset(cmd, 0, sizeof cmd); 172 cmd[0] = CMDtest; 173 cmd[1] = lun<<5; 174 return scsiexec(tp, SCSIread, cmd, sizeof cmd, 0, 0); 175 176 } 177 178 static int 179 scsistart(Target* tp, char lun, int start) 180 { 181 uchar cmd[6]; 182 183 memset(cmd, 0, sizeof cmd); 184 cmd[0] = CMDstart; 185 cmd[1] = lun<<5; 186 if(start) 187 cmd[4] = 1; 188 return scsiexec(tp, SCSIread, cmd, sizeof cmd, 0, 0); 189 } 190 191 static int 192 scsiinquiry(Target* tp, char lun, int* nbytes) 193 { 194 uchar cmd[6]; 195 196 memset(cmd, 0, sizeof cmd); 197 cmd[0] = CMDinquiry; 198 cmd[1] = lun<<5; 199 *nbytes = Ninquiry; 200 cmd[4] = *nbytes; 201 return scsiexec(tp, SCSIread, cmd, sizeof cmd, tp->inquiry, nbytes); 202 } 203 204 static char *key[] = 205 { 206 "no sense", 207 "recovered error", 208 "not ready", 209 "medium error", 210 "hardware error", 211 "illegal request", 212 "unit attention", 213 "data protect", 214 "blank check", 215 "vendor specific", 216 "copy aborted", 217 "aborted command", 218 "equal", 219 "volume overflow", 220 "miscompare", 221 "reserved" 222 }; 223 224 static int 225 scsireqsense(Target* tp, char lun, int* nbytes, int quiet) 226 { 227 char *s; 228 int n, status, try; 229 uchar cmd[6], *sense; 230 231 sense = tp->sense; 232 for(try = 0; try < 20; try++) { 233 memset(cmd, 0, sizeof cmd); 234 cmd[0] = CMDreqsense; 235 cmd[1] = lun<<5; 236 cmd[4] = Ninquiry; 237 memset(sense, 0, Ninquiry); 238 239 *nbytes = Ninquiry; 240 status = scsiexec(tp, SCSIread, cmd, sizeof cmd, sense, nbytes); 241 if(status != STok) 242 return status; 243 *nbytes = sense[0x07]+8; 244 245 switch(sense[2] & 0x0F){ 246 case 6: /* unit attention */ 247 /* 248 * 0x28 - not ready to ready transition, 249 * medium may have changed. 250 * 0x29 - power on, RESET or BUS DEVICE RESET occurred. 251 */ 252 if(sense[12] != 0x28 && sense[12] != 0x29) 253 goto buggery; 254 /*FALLTHROUGH*/ 255 case 0: /* no sense */ 256 case 1: /* recovered error */ 257 return STok; 258 case 8: /* blank data */ 259 return STblank; 260 case 2: /* not ready */ 261 if(sense[12] == 0x3A) /* medium not present */ 262 goto buggery; 263 /*FALLTHROUGH*/ 264 default: 265 /* 266 * If unit is becoming ready, rather than not ready, 267 * then wait a little then poke it again; should this 268 * be here or in the caller? 269 */ 270 if((sense[12] == 0x04 && sense[13] == 0x01)){ 271 delay(500); 272 scsitest(tp, lun); 273 break; 274 } 275 goto buggery; 276 } 277 } 278 279 buggery: 280 if(quiet == 0){ 281 s = key[sense[2]&0x0F]; 282 print("%s: reqsense: '%s' code #%2.2ux #%2.2ux\n", 283 tp->id, s, sense[12], sense[13]); 284 print("%s: byte 2: #%2.2ux, bytes 15-17: #%2.2ux #%2.2ux #%2.2ux\n", 285 tp->id, sense[2], sense[15], sense[16], sense[17]); 286 print("lastcmd (%d): ", lastcmdsz); 287 for(n = 0; n < lastcmdsz; n++) 288 print(" #%2.2ux", lastcmd[n]); 289 print("\n"); 290 } 291 292 return STcheck; 293 } 294 295 static Target* 296 scsitarget(Device* d) 297 { 298 int ctlrno, targetno; 299 300 ctlrno = d->wren.ctrl; 301 if(ctlrno < 0 || ctlrno >= MaxScsi /* || scsictlr[ctlrno].io == nil */) 302 return 0; 303 targetno = d->wren.targ; 304 if(targetno < 0 || targetno >= NTarget) 305 return 0; 306 return &scsictlr[ctlrno].target[targetno]; 307 } 308 309 static void 310 scsiprobe(Device* d) 311 { 312 Target *tp; 313 int nbytes, s; 314 uchar *sense; 315 int acount; 316 317 if((tp = scsitarget(d)) == 0) 318 panic("scsiprobe: device = %Z", d); 319 320 acount = 0; 321 again: 322 s = scsitest(tp, d->wren.lun); 323 if(s < STok){ 324 print("%s: test, status %d\n", tp->id, s); 325 return; 326 } 327 328 /* 329 * Determine if the drive exists and is not ready or 330 * is simply not responding. 331 * If the status is OK but the drive came back with a 'power on' or 332 * 'reset' status, try the test again to make sure the drive is really 333 * ready. 334 * If the drive is not ready and requires intervention, try to spin it 335 * up. 336 */ 337 s = scsireqsense(tp, d->wren.lun, &nbytes, acount); 338 sense = tp->sense; 339 switch(s){ 340 case STok: 341 if ((sense[2] & 0x0F) == 0x06 && 342 (sense[12] == 0x28 || sense[12] == 0x29)) 343 if(acount == 0){ 344 acount = 1; 345 goto again; 346 } 347 break; 348 case STcheck: 349 if((sense[2] & 0x0F) == 0x02){ 350 if(sense[12] == 0x3A) 351 break; 352 if(sense[12] == 0x04 && sense[13] == 0x02){ 353 print("%s: starting...\n", tp->id); 354 if(scsistart(tp, d->wren.lun, 1) == STok) 355 break; 356 s = scsireqsense(tp, d->wren.lun, &nbytes, 0); 357 } 358 } 359 /*FALLTHROUGH*/ 360 default: 361 print("%s: unavailable, status %d\n", tp->id, s); 362 return; 363 } 364 365 /* 366 * Inquire to find out what the device is. 367 * Hardware drivers may need some of the info. 368 */ 369 s = scsiinquiry(tp, d->wren.lun, &nbytes); 370 if(s != STok) { 371 print("%s: inquiry failed, status %d\n", tp->id, s); 372 return; 373 } 374 print("%s: %s\n", tp->id, (char*)tp->inquiry+8); 375 tp->ok = 1; 376 } 377 378 int 379 scsiio(Device* d, int rw, uchar* cmd, int cbytes, void* data, int dbytes) 380 { 381 Target *tp; 382 int e, nbytes, s; 383 384 if((tp = scsitarget(d)) == 0) 385 panic("scsiio: device = %Z", d); 386 387 qlock(tp); 388 if(tp->ok == 0) 389 scsiprobe(d); 390 qunlock(tp); 391 392 s = STinit; 393 for(e = 0; e < 10; e++){ 394 for(;;){ 395 nbytes = dbytes; 396 s = scsiexec(tp, rw, cmd, cbytes, data, &nbytes); 397 if(s == STok) 398 break; 399 s = scsireqsense(tp, d->wren.lun, &nbytes, 0); 400 if(s == STblank && rw == SCSIread) { 401 memset(data, 0, dbytes); 402 return STok; 403 } 404 if(s != STok) 405 break; 406 } 407 if(s == STok) 408 break; 409 } 410 if(e) 411 print("%s: retry %d cmd #%x\n", tp->id, e, cmd[0]); 412 return s; 413 } 414 415 void 416 newscsi(Device *d, Scsi *sc) 417 { 418 Target *tp; 419 420 if((tp = scsitarget(d)) == nil) 421 panic("newscsi: device = %Z", d); 422 tp->sc = sc; /* connect Target to Scsi */ 423 } 424