180ee5cbfSDavid du Colombier /* 280ee5cbfSDavid du Colombier * Now thread-safe. 380ee5cbfSDavid du Colombier * 480ee5cbfSDavid du Colombier * The codeqlock guarantees that once codes != nil, that pointer will never 580ee5cbfSDavid du Colombier * change nor become invalid. 680ee5cbfSDavid du Colombier * 780ee5cbfSDavid du Colombier * The QLock in the Scsi structure moderates access to the raw device. 880ee5cbfSDavid du Colombier * We should probably export some of the already-locked routines, but 980ee5cbfSDavid du Colombier * there hasn't been a need. 1080ee5cbfSDavid du Colombier */ 1180ee5cbfSDavid du Colombier 127dd7cddfSDavid du Colombier #include <u.h> 137dd7cddfSDavid du Colombier #include <libc.h> 147dd7cddfSDavid du Colombier #include <disk.h> 157dd7cddfSDavid du Colombier 1614cc0f53SDavid du Colombier enum { 1714cc0f53SDavid du Colombier Readtoc = 0x43, 1814cc0f53SDavid du Colombier }; 1914cc0f53SDavid du Colombier 207dd7cddfSDavid du Colombier int scsiverbose; 217dd7cddfSDavid du Colombier 227dd7cddfSDavid du Colombier #define codefile "/sys/lib/scsicodes" 237dd7cddfSDavid du Colombier 247dd7cddfSDavid du Colombier static char *codes; 2580ee5cbfSDavid du Colombier static QLock codeqlock; 2680ee5cbfSDavid du Colombier 277dd7cddfSDavid du Colombier static void 287dd7cddfSDavid du Colombier getcodes(void) 297dd7cddfSDavid du Colombier { 309a747e4fSDavid du Colombier Dir *d; 317dd7cddfSDavid du Colombier int n, fd; 327dd7cddfSDavid du Colombier 337dd7cddfSDavid du Colombier if(codes != nil) 347dd7cddfSDavid du Colombier return; 357dd7cddfSDavid du Colombier 3680ee5cbfSDavid du Colombier qlock(&codeqlock); 3780ee5cbfSDavid du Colombier if(codes != nil) { 3880ee5cbfSDavid du Colombier qunlock(&codeqlock); 397dd7cddfSDavid du Colombier return; 4080ee5cbfSDavid du Colombier } 4180ee5cbfSDavid du Colombier 429a747e4fSDavid du Colombier if((d = dirstat(codefile)) == nil || (fd = open(codefile, OREAD)) < 0) { 4380ee5cbfSDavid du Colombier qunlock(&codeqlock); 4480ee5cbfSDavid du Colombier return; 4580ee5cbfSDavid du Colombier } 467dd7cddfSDavid du Colombier 479a747e4fSDavid du Colombier codes = malloc(1+d->length+1); 4859cc4ca5SDavid du Colombier if(codes == nil) { 4959cc4ca5SDavid du Colombier close(fd); 5080ee5cbfSDavid du Colombier qunlock(&codeqlock); 519a747e4fSDavid du Colombier free(d); 5259cc4ca5SDavid du Colombier return; 5359cc4ca5SDavid du Colombier } 5459cc4ca5SDavid du Colombier 557dd7cddfSDavid du Colombier codes[0] = '\n'; /* for searches */ 569a747e4fSDavid du Colombier n = readn(fd, codes+1, d->length); 577dd7cddfSDavid du Colombier close(fd); 589a747e4fSDavid du Colombier free(d); 597dd7cddfSDavid du Colombier 607dd7cddfSDavid du Colombier if(n < 0) { 617dd7cddfSDavid du Colombier free(codes); 627dd7cddfSDavid du Colombier codes = nil; 6380ee5cbfSDavid du Colombier qunlock(&codeqlock); 647dd7cddfSDavid du Colombier return; 657dd7cddfSDavid du Colombier } 667dd7cddfSDavid du Colombier codes[n] = '\0'; 6780ee5cbfSDavid du Colombier qunlock(&codeqlock); 687dd7cddfSDavid du Colombier } 697dd7cddfSDavid du Colombier 707dd7cddfSDavid du Colombier char* 717dd7cddfSDavid du Colombier scsierror(int asc, int ascq) 727dd7cddfSDavid du Colombier { 737dd7cddfSDavid du Colombier char *p, *q; 747dd7cddfSDavid du Colombier static char search[32]; 757dd7cddfSDavid du Colombier static char buf[128]; 767dd7cddfSDavid du Colombier 777dd7cddfSDavid du Colombier getcodes(); 787dd7cddfSDavid du Colombier 797dd7cddfSDavid du Colombier if(codes) { 807dd7cddfSDavid du Colombier sprint(search, "\n%.2ux%.2ux ", asc, ascq); 817dd7cddfSDavid du Colombier if(p = strstr(codes, search)) { 827dd7cddfSDavid du Colombier p += 6; 837dd7cddfSDavid du Colombier if((q = strchr(p, '\n')) == nil) 847dd7cddfSDavid du Colombier q = p+strlen(p); 857dd7cddfSDavid du Colombier snprint(buf, sizeof buf, "%.*s", (int)(q-p), p); 867dd7cddfSDavid du Colombier return buf; 877dd7cddfSDavid du Colombier } 887dd7cddfSDavid du Colombier 897dd7cddfSDavid du Colombier sprint(search, "\n%.2ux00", asc); 907dd7cddfSDavid du Colombier if(p = strstr(codes, search)) { 917dd7cddfSDavid du Colombier p += 6; 927dd7cddfSDavid du Colombier if((q = strchr(p, '\n')) == nil) 937dd7cddfSDavid du Colombier q = p+strlen(p); 947dd7cddfSDavid du Colombier snprint(buf, sizeof buf, "(ascq #%.2ux) %.*s", ascq, (int)(q-p), p); 957dd7cddfSDavid du Colombier return buf; 967dd7cddfSDavid du Colombier } 977dd7cddfSDavid du Colombier } 987dd7cddfSDavid du Colombier 997dd7cddfSDavid du Colombier sprint(buf, "scsi #%.2ux %.2ux", asc, ascq); 1007dd7cddfSDavid du Colombier return buf; 1017dd7cddfSDavid du Colombier } 1027dd7cddfSDavid du Colombier 1037dd7cddfSDavid du Colombier 10480ee5cbfSDavid du Colombier static int 10580ee5cbfSDavid du Colombier _scsicmd(Scsi *s, uchar *cmd, int ccount, void *data, int dcount, int io, int dolock) 1067dd7cddfSDavid du Colombier { 1077dd7cddfSDavid du Colombier uchar resp[16]; 1087dd7cddfSDavid du Colombier int n; 1097dd7cddfSDavid du Colombier long status; 1107dd7cddfSDavid du Colombier 11180ee5cbfSDavid du Colombier if(dolock) 11280ee5cbfSDavid du Colombier qlock(s); 1137dd7cddfSDavid du Colombier if(write(s->rawfd, cmd, ccount) != ccount) { 1147dd7cddfSDavid du Colombier werrstr("cmd write: %r"); 11580ee5cbfSDavid du Colombier if(dolock) 11680ee5cbfSDavid du Colombier qunlock(s); 1177dd7cddfSDavid du Colombier return -1; 1187dd7cddfSDavid du Colombier } 1197dd7cddfSDavid du Colombier 1207dd7cddfSDavid du Colombier switch(io){ 1217dd7cddfSDavid du Colombier case Sread: 1227dd7cddfSDavid du Colombier n = read(s->rawfd, data, dcount); 12314cc0f53SDavid du Colombier /* read toc errors are frequent and not very interesting */ 12414cc0f53SDavid du Colombier if(n < 0 && (scsiverbose == 1 || 12514cc0f53SDavid du Colombier scsiverbose == 2 && cmd[0] != Readtoc)) 1267dd7cddfSDavid du Colombier fprint(2, "dat read: %r: cmd 0x%2.2uX\n", cmd[0]); 1277dd7cddfSDavid du Colombier break; 1287dd7cddfSDavid du Colombier case Swrite: 1297dd7cddfSDavid du Colombier n = write(s->rawfd, data, dcount); 1307dd7cddfSDavid du Colombier if(n != dcount && scsiverbose) 1317dd7cddfSDavid du Colombier fprint(2, "dat write: %r: cmd 0x%2.2uX\n", cmd[0]); 1327dd7cddfSDavid du Colombier break; 1337dd7cddfSDavid du Colombier default: 1347dd7cddfSDavid du Colombier case Snone: 1357dd7cddfSDavid du Colombier n = write(s->rawfd, resp, 0); 1367dd7cddfSDavid du Colombier if(n != 0 && scsiverbose) 1377dd7cddfSDavid du Colombier fprint(2, "none write: %r: cmd 0x%2.2uX\n", cmd[0]); 1387dd7cddfSDavid du Colombier break; 1397dd7cddfSDavid du Colombier } 1407dd7cddfSDavid du Colombier 1417dd7cddfSDavid du Colombier memset(resp, 0, sizeof(resp)); 1427dd7cddfSDavid du Colombier if(read(s->rawfd, resp, sizeof(resp)) < 0) { 1437dd7cddfSDavid du Colombier werrstr("resp read: %r\n"); 14480ee5cbfSDavid du Colombier if(dolock) 14580ee5cbfSDavid du Colombier qunlock(s); 1467dd7cddfSDavid du Colombier return -1; 1477dd7cddfSDavid du Colombier } 14880ee5cbfSDavid du Colombier if(dolock) 14980ee5cbfSDavid du Colombier qunlock(s); 1507dd7cddfSDavid du Colombier 1517dd7cddfSDavid du Colombier resp[sizeof(resp)-1] = '\0'; 1527dd7cddfSDavid du Colombier status = atoi((char*)resp); 1537dd7cddfSDavid du Colombier if(status == 0) 1547dd7cddfSDavid du Colombier return n; 1557dd7cddfSDavid du Colombier 1567dd7cddfSDavid du Colombier werrstr("cmd %2.2uX: status %luX dcount %d n %d", cmd[0], status, dcount, n); 1577dd7cddfSDavid du Colombier return -1; 1587dd7cddfSDavid du Colombier } 1597dd7cddfSDavid du Colombier 1607dd7cddfSDavid du Colombier int 16180ee5cbfSDavid du Colombier scsicmd(Scsi *s, uchar *cmd, int ccount, void *data, int dcount, int io) 16280ee5cbfSDavid du Colombier { 16380ee5cbfSDavid du Colombier return _scsicmd(s, cmd, ccount, data, dcount, io, 1); 16480ee5cbfSDavid du Colombier } 16580ee5cbfSDavid du Colombier 16680ee5cbfSDavid du Colombier static int 16780ee5cbfSDavid du Colombier _scsiready(Scsi *s, int dolock) 1687dd7cddfSDavid du Colombier { 169*194f7e8cSDavid du Colombier char err[ERRMAX]; 1707dd7cddfSDavid du Colombier uchar cmd[6], resp[16]; 1717dd7cddfSDavid du Colombier int status, i; 1727dd7cddfSDavid du Colombier 17380ee5cbfSDavid du Colombier if(dolock) 17480ee5cbfSDavid du Colombier qlock(s); 175*194f7e8cSDavid du Colombier werrstr(""); 1767dd7cddfSDavid du Colombier for(i=0; i<3; i++) { 1777dd7cddfSDavid du Colombier memset(cmd, 0, sizeof(cmd)); 1787dd7cddfSDavid du Colombier cmd[0] = 0x00; /* unit ready */ 1797dd7cddfSDavid du Colombier if(write(s->rawfd, cmd, sizeof(cmd)) != sizeof(cmd)) { 1807dd7cddfSDavid du Colombier if(scsiverbose) 1817dd7cddfSDavid du Colombier fprint(2, "ur cmd write: %r\n"); 182*194f7e8cSDavid du Colombier werrstr("short unit-ready raw write"); 1837dd7cddfSDavid du Colombier goto bad; 1847dd7cddfSDavid du Colombier } 1857dd7cddfSDavid du Colombier write(s->rawfd, resp, 0); 1867dd7cddfSDavid du Colombier if(read(s->rawfd, resp, sizeof(resp)) < 0) { 1877dd7cddfSDavid du Colombier if(scsiverbose) 1887dd7cddfSDavid du Colombier fprint(2, "ur resp read: %r\n"); 1897dd7cddfSDavid du Colombier goto bad; 1907dd7cddfSDavid du Colombier } 1917dd7cddfSDavid du Colombier resp[sizeof(resp)-1] = '\0'; 1927dd7cddfSDavid du Colombier status = atoi((char*)resp); 19380ee5cbfSDavid du Colombier if(status == 0 || status == 0x02) { 19480ee5cbfSDavid du Colombier if(dolock) 19580ee5cbfSDavid du Colombier qunlock(s); 1967dd7cddfSDavid du Colombier return 0; 19780ee5cbfSDavid du Colombier } 1987dd7cddfSDavid du Colombier if(scsiverbose) 1997dd7cddfSDavid du Colombier fprint(2, "target: bad status: %x\n", status); 2007dd7cddfSDavid du Colombier bad:; 2017dd7cddfSDavid du Colombier } 202*194f7e8cSDavid du Colombier rerrstr(err, sizeof err); 203*194f7e8cSDavid du Colombier if(err[0] == '\0') 204*194f7e8cSDavid du Colombier werrstr("unit did not become ready"); 20580ee5cbfSDavid du Colombier if(dolock) 20680ee5cbfSDavid du Colombier qunlock(s); 2077dd7cddfSDavid du Colombier return -1; 2087dd7cddfSDavid du Colombier } 2097dd7cddfSDavid du Colombier 2107dd7cddfSDavid du Colombier int 21180ee5cbfSDavid du Colombier scsiready(Scsi *s) 21280ee5cbfSDavid du Colombier { 21380ee5cbfSDavid du Colombier return _scsiready(s, 1); 21480ee5cbfSDavid du Colombier } 21580ee5cbfSDavid du Colombier 21680ee5cbfSDavid du Colombier int 2177dd7cddfSDavid du Colombier scsi(Scsi *s, uchar *cmd, int ccount, void *v, int dcount, int io) 2187dd7cddfSDavid du Colombier { 2197dd7cddfSDavid du Colombier uchar req[6], sense[255], *data; 2207dd7cddfSDavid du Colombier int tries, code, key, n; 2217dd7cddfSDavid du Colombier char *p; 2227dd7cddfSDavid du Colombier 2237dd7cddfSDavid du Colombier data = v; 2247dd7cddfSDavid du Colombier SET(key, code); 22580ee5cbfSDavid du Colombier qlock(s); 2267dd7cddfSDavid du Colombier for(tries=0; tries<2; tries++) { 22780ee5cbfSDavid du Colombier n = _scsicmd(s, cmd, ccount, data, dcount, io, 0); 22880ee5cbfSDavid du Colombier if(n >= 0) { 22980ee5cbfSDavid du Colombier qunlock(s); 2307dd7cddfSDavid du Colombier return n; 23180ee5cbfSDavid du Colombier } 2327dd7cddfSDavid du Colombier 2337dd7cddfSDavid du Colombier /* 2347dd7cddfSDavid du Colombier * request sense 2357dd7cddfSDavid du Colombier */ 2367dd7cddfSDavid du Colombier memset(req, 0, sizeof(req)); 2377dd7cddfSDavid du Colombier req[0] = 0x03; 2387dd7cddfSDavid du Colombier req[4] = sizeof(sense); 2397dd7cddfSDavid du Colombier memset(sense, 0xFF, sizeof(sense)); 24080ee5cbfSDavid du Colombier if((n=_scsicmd(s, req, sizeof(req), sense, sizeof(sense), Sread, 0)) < 14) 2417dd7cddfSDavid du Colombier if(scsiverbose) 2427dd7cddfSDavid du Colombier fprint(2, "reqsense scsicmd %d: %r\n", n); 2437dd7cddfSDavid du Colombier 24480ee5cbfSDavid du Colombier if(_scsiready(s, 0) < 0) 2457dd7cddfSDavid du Colombier if(scsiverbose) 2467dd7cddfSDavid du Colombier fprint(2, "unit not ready\n"); 2477dd7cddfSDavid du Colombier 2487dd7cddfSDavid du Colombier key = sense[2]; 2497dd7cddfSDavid du Colombier code = sense[12]; 25080ee5cbfSDavid du Colombier if(code == 0x17 || code == 0x18) { /* recovered errors */ 25180ee5cbfSDavid du Colombier qunlock(s); 2527dd7cddfSDavid du Colombier return dcount; 25380ee5cbfSDavid du Colombier } 25414cc0f53SDavid du Colombier if(code == 0x28 && cmd[0] == Readtoc) { 25514cc0f53SDavid du Colombier /* read toc and media changed */ 2567dd7cddfSDavid du Colombier s->nchange++; 2577dd7cddfSDavid du Colombier s->changetime = time(0); 2587dd7cddfSDavid du Colombier continue; 2597dd7cddfSDavid du Colombier } 2607dd7cddfSDavid du Colombier } 2617dd7cddfSDavid du Colombier 2627dd7cddfSDavid du Colombier /* drive not ready, or medium not present */ 26314cc0f53SDavid du Colombier if(cmd[0] == Readtoc && key == 2 && (code == 0x3a || code == 0x04)) { 2647dd7cddfSDavid du Colombier s->changetime = 0; 26580ee5cbfSDavid du Colombier qunlock(s); 2667dd7cddfSDavid du Colombier return -1; 2677dd7cddfSDavid du Colombier } 26880ee5cbfSDavid du Colombier qunlock(s); 2697dd7cddfSDavid du Colombier 27014cc0f53SDavid du Colombier if(cmd[0] == Readtoc && key == 5 && code == 0x24) /* blank media */ 2717dd7cddfSDavid du Colombier return -1; 2727dd7cddfSDavid du Colombier 2737dd7cddfSDavid du Colombier p = scsierror(code, sense[13]); 2747dd7cddfSDavid du Colombier 2757dd7cddfSDavid du Colombier werrstr("cmd #%.2ux: %s", cmd[0], p); 2767dd7cddfSDavid du Colombier 2777dd7cddfSDavid du Colombier if(scsiverbose) 27814cc0f53SDavid du Colombier fprint(2, "scsi cmd #%.2ux: %.2ux %.2ux %.2ux: %s\n", 27914cc0f53SDavid du Colombier cmd[0], key, code, sense[13], p); 2807dd7cddfSDavid du Colombier 281*194f7e8cSDavid du Colombier // if(key == 0) /* no sense? */ 2827dd7cddfSDavid du Colombier // return dcount; 2837dd7cddfSDavid du Colombier return -1; 2847dd7cddfSDavid du Colombier } 2857dd7cddfSDavid du Colombier 2867dd7cddfSDavid du Colombier Scsi* 2877dd7cddfSDavid du Colombier openscsi(char *dev) 2887dd7cddfSDavid du Colombier { 2897dd7cddfSDavid du Colombier Scsi *s; 2907dd7cddfSDavid du Colombier int rawfd, ctlfd, l, n; 2917dd7cddfSDavid du Colombier char *name, *p, buf[512]; 2927dd7cddfSDavid du Colombier 2937dd7cddfSDavid du Colombier l = strlen(dev)+1+3+1; 29459cc4ca5SDavid du Colombier name = malloc(l); 29559cc4ca5SDavid du Colombier if(name == nil) 29659cc4ca5SDavid du Colombier return nil; 2977dd7cddfSDavid du Colombier 2987dd7cddfSDavid du Colombier snprint(name, l, "%s/raw", dev); 29959cc4ca5SDavid du Colombier if((rawfd = open(name, ORDWR)) < 0) { 30059cc4ca5SDavid du Colombier free(name); 3017dd7cddfSDavid du Colombier return nil; 30259cc4ca5SDavid du Colombier } 3037dd7cddfSDavid du Colombier 3047dd7cddfSDavid du Colombier snprint(name, l, "%s/ctl", dev); 3057dd7cddfSDavid du Colombier if((ctlfd = open(name, ORDWR)) < 0) { 3067dd7cddfSDavid du Colombier Error: 307*194f7e8cSDavid du Colombier free(name); 3087dd7cddfSDavid du Colombier close(rawfd); 3097dd7cddfSDavid du Colombier return nil; 3107dd7cddfSDavid du Colombier } 3117dd7cddfSDavid du Colombier 3127dd7cddfSDavid du Colombier n = readn(ctlfd, buf, sizeof buf); 3137dd7cddfSDavid du Colombier close(ctlfd); 314*194f7e8cSDavid du Colombier if(n <= 0) { 315*194f7e8cSDavid du Colombier if(n == 0) 316*194f7e8cSDavid du Colombier werrstr("eof on %s", name); 3177dd7cddfSDavid du Colombier goto Error; 318*194f7e8cSDavid du Colombier } 3197dd7cddfSDavid du Colombier 320*194f7e8cSDavid du Colombier if(strncmp(buf, "inquiry ", 8) != 0 || (p = strchr(buf, '\n')) == nil) { 321*194f7e8cSDavid du Colombier werrstr("inquiry mal-formatted in %s", name); 3227dd7cddfSDavid du Colombier goto Error; 323*194f7e8cSDavid du Colombier } 3247dd7cddfSDavid du Colombier *p = '\0'; 325*194f7e8cSDavid du Colombier free(name); 326*194f7e8cSDavid du Colombier name = nil; 3277dd7cddfSDavid du Colombier 3287dd7cddfSDavid du Colombier if((p = strdup(buf+8)) == nil) 3297dd7cddfSDavid du Colombier goto Error; 3307dd7cddfSDavid du Colombier 331766ccd67SDavid du Colombier s = mallocz(sizeof(*s), 1); 33259cc4ca5SDavid du Colombier if(s == nil) { 33359cc4ca5SDavid du Colombier Error1: 33459cc4ca5SDavid du Colombier free(p); 33559cc4ca5SDavid du Colombier goto Error; 33659cc4ca5SDavid du Colombier } 33759cc4ca5SDavid du Colombier 3387dd7cddfSDavid du Colombier s->rawfd = rawfd; 3397dd7cddfSDavid du Colombier s->inquire = p; 3407dd7cddfSDavid du Colombier s->changetime = time(0); 3417dd7cddfSDavid du Colombier 34259cc4ca5SDavid du Colombier if(scsiready(s) < 0) 34359cc4ca5SDavid du Colombier goto Error1; 3447dd7cddfSDavid du Colombier 3457dd7cddfSDavid du Colombier return s; 3467dd7cddfSDavid du Colombier } 347af6a38e6SDavid du Colombier 348af6a38e6SDavid du Colombier void 349af6a38e6SDavid du Colombier closescsi(Scsi *s) 350af6a38e6SDavid du Colombier { 351af6a38e6SDavid du Colombier close(s->rawfd); 352af6a38e6SDavid du Colombier free(s->inquire); 353af6a38e6SDavid du Colombier free(s); 354af6a38e6SDavid du Colombier } 355