1 /* 2 * Now thread-safe. 3 * 4 * The codeqlock guarantees that once codes != nil, that pointer will never 5 * change nor become invalid. 6 * 7 * The QLock in the Scsi structure moderates access to the raw device. 8 * We should probably export some of the already-locked routines, but 9 * there hasn't been a need. 10 */ 11 12 #include <u.h> 13 #include <libc.h> 14 #include <disk.h> 15 16 enum { 17 /* commands */ 18 Testrdy = 0x00, 19 Reqsense = 0x03, 20 Write10 = 0x2a, 21 Writever10 = 0x2e, 22 Readtoc = 0x43, 23 24 /* sense[2] (key) sense codes */ 25 Sensenone = 0, 26 Sensenotrdy = 2, 27 Sensebadreq = 5, 28 29 /* sense[12] (asc) sense codes */ 30 Lunnotrdy = 0x04, 31 Recovnoecc = 0x17, 32 Recovecc = 0x18, 33 Badcdb = 0x24, 34 Newmedium = 0x28, 35 Nomedium = 0x3a, 36 }; 37 38 int scsiverbose; 39 40 #define codefile "/sys/lib/scsicodes" 41 42 static char *codes; 43 static QLock codeqlock; 44 45 static void 46 getcodes(void) 47 { 48 Dir *d; 49 int n, fd; 50 51 if(codes != nil) 52 return; 53 54 qlock(&codeqlock); 55 if(codes != nil) { 56 qunlock(&codeqlock); 57 return; 58 } 59 60 if((d = dirstat(codefile)) == nil || (fd = open(codefile, OREAD)) < 0) { 61 qunlock(&codeqlock); 62 return; 63 } 64 65 codes = malloc(1+d->length+1); 66 if(codes == nil) { 67 close(fd); 68 qunlock(&codeqlock); 69 free(d); 70 return; 71 } 72 73 codes[0] = '\n'; /* for searches */ 74 n = readn(fd, codes+1, d->length); 75 close(fd); 76 free(d); 77 78 if(n < 0) { 79 free(codes); 80 codes = nil; 81 qunlock(&codeqlock); 82 return; 83 } 84 codes[n] = '\0'; 85 qunlock(&codeqlock); 86 } 87 88 char* 89 scsierror(int asc, int ascq) 90 { 91 char *p, *q; 92 static char search[32]; 93 static char buf[128]; 94 95 getcodes(); 96 97 if(codes) { 98 snprint(search, sizeof search, "\n%.2ux%.2ux ", asc, ascq); 99 if(p = strstr(codes, search)) { 100 p += 6; 101 if((q = strchr(p, '\n')) == nil) 102 q = p+strlen(p); 103 snprint(buf, sizeof buf, "%.*s", (int)(q-p), p); 104 return buf; 105 } 106 107 snprint(search, sizeof search, "\n%.2ux00", asc); 108 if(p = strstr(codes, search)) { 109 p += 6; 110 if((q = strchr(p, '\n')) == nil) 111 q = p+strlen(p); 112 snprint(buf, sizeof buf, "(ascq #%.2ux) %.*s", ascq, (int)(q-p), p); 113 return buf; 114 } 115 } 116 117 snprint(buf, sizeof buf, "scsi #%.2ux %.2ux", asc, ascq); 118 return buf; 119 } 120 121 122 static int 123 _scsicmd(Scsi *s, uchar *cmd, int ccount, void *data, int dcount, int io, int dolock) 124 { 125 uchar resp[16]; 126 int n; 127 long status; 128 129 if(dolock) 130 qlock(s); 131 if(write(s->rawfd, cmd, ccount) != ccount) { 132 werrstr("cmd write: %r"); 133 if(dolock) 134 qunlock(s); 135 return -1; 136 } 137 138 switch(io){ 139 case Sread: 140 n = read(s->rawfd, data, dcount); 141 /* read toc errors are frequent and not very interesting */ 142 if(n < 0 && (scsiverbose == 1 || 143 scsiverbose == 2 && cmd[0] != Readtoc)) 144 fprint(2, "dat read: %r: cmd 0x%2.2uX\n", cmd[0]); 145 break; 146 case Swrite: 147 n = write(s->rawfd, data, dcount); 148 if(n != dcount && scsiverbose) 149 fprint(2, "dat write: %r: cmd 0x%2.2uX\n", cmd[0]); 150 break; 151 default: 152 case Snone: 153 n = write(s->rawfd, resp, 0); 154 if(n != 0 && scsiverbose) 155 fprint(2, "none write: %r: cmd 0x%2.2uX\n", cmd[0]); 156 break; 157 } 158 159 memset(resp, 0, sizeof(resp)); 160 if(read(s->rawfd, resp, sizeof(resp)) < 0) { 161 werrstr("resp read: %r\n"); 162 if(dolock) 163 qunlock(s); 164 return -1; 165 } 166 if(dolock) 167 qunlock(s); 168 169 resp[sizeof(resp)-1] = '\0'; 170 status = atoi((char*)resp); 171 if(status == 0) 172 return n; 173 174 werrstr("cmd %2.2uX: status %luX dcount %d n %d", cmd[0], status, dcount, n); 175 return -1; 176 } 177 178 int 179 scsicmd(Scsi *s, uchar *cmd, int ccount, void *data, int dcount, int io) 180 { 181 return _scsicmd(s, cmd, ccount, data, dcount, io, 1); 182 } 183 184 static int 185 _scsiready(Scsi *s, int dolock) 186 { 187 char err[ERRMAX]; 188 uchar cmd[6], resp[16]; 189 int status, i; 190 191 if(dolock) 192 qlock(s); 193 werrstr(""); 194 for(i=0; i<3; i++) { 195 memset(cmd, 0, sizeof(cmd)); 196 cmd[0] = Testrdy; /* unit ready */ 197 if(write(s->rawfd, cmd, sizeof(cmd)) != sizeof(cmd)) { 198 if(scsiverbose) 199 fprint(2, "ur cmd write: %r\n"); 200 werrstr("short unit-ready raw write"); 201 continue; 202 } 203 write(s->rawfd, resp, 0); 204 if(read(s->rawfd, resp, sizeof(resp)) < 0) { 205 if(scsiverbose) 206 fprint(2, "ur resp read: %r\n"); 207 continue; 208 } 209 resp[sizeof(resp)-1] = '\0'; 210 status = atoi((char*)resp); 211 if(status == 0 || status == 0x02) { 212 if(dolock) 213 qunlock(s); 214 return 0; 215 } 216 if(scsiverbose) 217 fprint(2, "target: bad status: %x\n", status); 218 } 219 rerrstr(err, sizeof err); 220 if(err[0] == '\0') 221 werrstr("unit did not become ready"); 222 if(dolock) 223 qunlock(s); 224 return -1; 225 } 226 227 int 228 scsiready(Scsi *s) 229 { 230 return _scsiready(s, 1); 231 } 232 233 int 234 scsi(Scsi *s, uchar *cmd, int ccount, void *v, int dcount, int io) 235 { 236 uchar req[6], sense[255], *data; 237 int tries, code, key, n; 238 char *p; 239 240 data = v; 241 SET(key, code); 242 qlock(s); 243 for(tries=0; tries<2; tries++) { 244 n = _scsicmd(s, cmd, ccount, data, dcount, io, 0); 245 if(n >= 0) { 246 qunlock(s); 247 return n; 248 } 249 250 /* 251 * request sense 252 */ 253 memset(req, 0, sizeof(req)); 254 req[0] = Reqsense; 255 req[4] = sizeof(sense); 256 memset(sense, 0xFF, sizeof(sense)); 257 if((n=_scsicmd(s, req, sizeof(req), sense, sizeof(sense), Sread, 0)) < 14) 258 if(scsiverbose) 259 fprint(2, "reqsense scsicmd %d: %r\n", n); 260 261 if(_scsiready(s, 0) < 0) 262 if(scsiverbose) 263 fprint(2, "unit not ready\n"); 264 265 key = sense[2] & 0xf; 266 code = sense[12]; /* asc */ 267 if(code == Recovnoecc || code == Recovecc) { /* recovered errors */ 268 qunlock(s); 269 return dcount; 270 } 271 272 /* retry various odd cases */ 273 if(code == Newmedium && cmd[0] == Readtoc) { 274 /* read toc and media changed */ 275 s->nchange++; 276 s->changetime = time(0); 277 } else if((cmd[0] == Write10 || cmd[0] == Writever10) && 278 key == Sensenotrdy && 279 code == Lunnotrdy && sense[13] == 0x08) { 280 /* long write in progress, per mmc-6 */ 281 tries = 0; 282 sleep(1); 283 } else if(cmd[0] == Write10 || cmd[0] == Writever10) 284 break; /* don't retry worm writes */ 285 } 286 287 /* drive not ready, or medium not present */ 288 if(cmd[0] == Readtoc && key == Sensenotrdy && 289 (code == Nomedium || code == Lunnotrdy)) { 290 s->changetime = 0; 291 qunlock(s); 292 return -1; 293 } 294 qunlock(s); 295 296 if(cmd[0] == Readtoc && key == Sensebadreq && code == Badcdb) 297 return -1; /* blank media */ 298 299 p = scsierror(code, sense[13]); 300 301 werrstr("cmd #%.2ux: %s", cmd[0], p); 302 303 if(scsiverbose) 304 fprint(2, "scsi cmd #%.2ux: %.2ux %.2ux %.2ux: %s\n", 305 cmd[0], key, code, sense[13], p); 306 307 // if(key == Sensenone) 308 // return dcount; 309 return -1; 310 } 311 312 Scsi* 313 openscsi(char *dev) 314 { 315 Scsi *s; 316 int rawfd, ctlfd, l, n; 317 char *name, *p, buf[512]; 318 319 l = strlen(dev)+1+3+1; 320 name = malloc(l); 321 if(name == nil) 322 return nil; 323 324 snprint(name, l, "%s/raw", dev); 325 if((rawfd = open(name, ORDWR)) < 0) { 326 free(name); 327 return nil; 328 } 329 330 snprint(name, l, "%s/ctl", dev); 331 if((ctlfd = open(name, ORDWR)) < 0) { 332 Error: 333 free(name); 334 close(rawfd); 335 return nil; 336 } 337 338 n = readn(ctlfd, buf, sizeof buf); 339 close(ctlfd); 340 if(n <= 0) { 341 if(n == 0) 342 werrstr("eof on %s", name); 343 goto Error; 344 } 345 346 if(strncmp(buf, "inquiry ", 8) != 0 || (p = strchr(buf, '\n')) == nil) { 347 werrstr("inquiry mal-formatted in %s", name); 348 goto Error; 349 } 350 *p = '\0'; 351 free(name); 352 name = nil; 353 354 if((p = strdup(buf+8)) == nil) 355 goto Error; 356 357 s = mallocz(sizeof(*s), 1); 358 if(s == nil) { 359 Error1: 360 free(p); 361 goto Error; 362 } 363 364 s->rawfd = rawfd; 365 s->inquire = p; 366 s->changetime = time(0); 367 368 if(scsiready(s) < 0) 369 goto Error1; 370 371 return s; 372 } 373 374 void 375 closescsi(Scsi *s) 376 { 377 close(s->rawfd); 378 free(s->inquire); 379 free(s); 380 } 381