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 {
178673b4daSDavid du Colombier /* commands */
188673b4daSDavid du Colombier Testrdy = 0x00,
198673b4daSDavid du Colombier Reqsense = 0x03,
208673b4daSDavid du Colombier Write10 = 0x2a,
21*27522402SDavid du Colombier Writever10 = 0x2e,
2214cc0f53SDavid du Colombier Readtoc = 0x43,
238673b4daSDavid du Colombier
248673b4daSDavid du Colombier /* sense[2] (key) sense codes */
258673b4daSDavid du Colombier Sensenone = 0,
268673b4daSDavid du Colombier Sensenotrdy = 2,
278673b4daSDavid du Colombier Sensebadreq = 5,
288673b4daSDavid du Colombier
298673b4daSDavid du Colombier /* sense[12] (asc) sense codes */
308673b4daSDavid du Colombier Lunnotrdy = 0x04,
318673b4daSDavid du Colombier Recovnoecc = 0x17,
328673b4daSDavid du Colombier Recovecc = 0x18,
338673b4daSDavid du Colombier Badcdb = 0x24,
348673b4daSDavid du Colombier Newmedium = 0x28,
358673b4daSDavid du Colombier Nomedium = 0x3a,
3614cc0f53SDavid du Colombier };
3714cc0f53SDavid du Colombier
387dd7cddfSDavid du Colombier int scsiverbose;
397dd7cddfSDavid du Colombier
407dd7cddfSDavid du Colombier #define codefile "/sys/lib/scsicodes"
417dd7cddfSDavid du Colombier
427dd7cddfSDavid du Colombier static char *codes;
4380ee5cbfSDavid du Colombier static QLock codeqlock;
4480ee5cbfSDavid du Colombier
457dd7cddfSDavid du Colombier static void
getcodes(void)467dd7cddfSDavid du Colombier getcodes(void)
477dd7cddfSDavid du Colombier {
489a747e4fSDavid du Colombier Dir *d;
497dd7cddfSDavid du Colombier int n, fd;
507dd7cddfSDavid du Colombier
517dd7cddfSDavid du Colombier if(codes != nil)
527dd7cddfSDavid du Colombier return;
537dd7cddfSDavid du Colombier
5480ee5cbfSDavid du Colombier qlock(&codeqlock);
5580ee5cbfSDavid du Colombier if(codes != nil) {
5680ee5cbfSDavid du Colombier qunlock(&codeqlock);
577dd7cddfSDavid du Colombier return;
5880ee5cbfSDavid du Colombier }
5980ee5cbfSDavid du Colombier
609a747e4fSDavid du Colombier if((d = dirstat(codefile)) == nil || (fd = open(codefile, OREAD)) < 0) {
6180ee5cbfSDavid du Colombier qunlock(&codeqlock);
6280ee5cbfSDavid du Colombier return;
6380ee5cbfSDavid du Colombier }
647dd7cddfSDavid du Colombier
659a747e4fSDavid du Colombier codes = malloc(1+d->length+1);
6659cc4ca5SDavid du Colombier if(codes == nil) {
6759cc4ca5SDavid du Colombier close(fd);
6880ee5cbfSDavid du Colombier qunlock(&codeqlock);
699a747e4fSDavid du Colombier free(d);
7059cc4ca5SDavid du Colombier return;
7159cc4ca5SDavid du Colombier }
7259cc4ca5SDavid du Colombier
737dd7cddfSDavid du Colombier codes[0] = '\n'; /* for searches */
749a747e4fSDavid du Colombier n = readn(fd, codes+1, d->length);
757dd7cddfSDavid du Colombier close(fd);
769a747e4fSDavid du Colombier free(d);
777dd7cddfSDavid du Colombier
787dd7cddfSDavid du Colombier if(n < 0) {
797dd7cddfSDavid du Colombier free(codes);
807dd7cddfSDavid du Colombier codes = nil;
8180ee5cbfSDavid du Colombier qunlock(&codeqlock);
827dd7cddfSDavid du Colombier return;
837dd7cddfSDavid du Colombier }
847dd7cddfSDavid du Colombier codes[n] = '\0';
8580ee5cbfSDavid du Colombier qunlock(&codeqlock);
867dd7cddfSDavid du Colombier }
877dd7cddfSDavid du Colombier
887dd7cddfSDavid du Colombier char*
scsierror(int asc,int ascq)897dd7cddfSDavid du Colombier scsierror(int asc, int ascq)
907dd7cddfSDavid du Colombier {
917dd7cddfSDavid du Colombier char *p, *q;
927dd7cddfSDavid du Colombier static char search[32];
937dd7cddfSDavid du Colombier static char buf[128];
947dd7cddfSDavid du Colombier
957dd7cddfSDavid du Colombier getcodes();
967dd7cddfSDavid du Colombier
977dd7cddfSDavid du Colombier if(codes) {
988673b4daSDavid du Colombier snprint(search, sizeof search, "\n%.2ux%.2ux ", asc, ascq);
997dd7cddfSDavid du Colombier if(p = strstr(codes, search)) {
1007dd7cddfSDavid du Colombier p += 6;
1017dd7cddfSDavid du Colombier if((q = strchr(p, '\n')) == nil)
1027dd7cddfSDavid du Colombier q = p+strlen(p);
1037dd7cddfSDavid du Colombier snprint(buf, sizeof buf, "%.*s", (int)(q-p), p);
1047dd7cddfSDavid du Colombier return buf;
1057dd7cddfSDavid du Colombier }
1067dd7cddfSDavid du Colombier
1078673b4daSDavid du Colombier snprint(search, sizeof search, "\n%.2ux00", asc);
1087dd7cddfSDavid du Colombier if(p = strstr(codes, search)) {
1097dd7cddfSDavid du Colombier p += 6;
1107dd7cddfSDavid du Colombier if((q = strchr(p, '\n')) == nil)
1117dd7cddfSDavid du Colombier q = p+strlen(p);
1127dd7cddfSDavid du Colombier snprint(buf, sizeof buf, "(ascq #%.2ux) %.*s", ascq, (int)(q-p), p);
1137dd7cddfSDavid du Colombier return buf;
1147dd7cddfSDavid du Colombier }
1157dd7cddfSDavid du Colombier }
1167dd7cddfSDavid du Colombier
1178673b4daSDavid du Colombier snprint(buf, sizeof buf, "scsi #%.2ux %.2ux", asc, ascq);
1187dd7cddfSDavid du Colombier return buf;
1197dd7cddfSDavid du Colombier }
1207dd7cddfSDavid du Colombier
1217dd7cddfSDavid du Colombier
12280ee5cbfSDavid du Colombier static int
_scsicmd(Scsi * s,uchar * cmd,int ccount,void * data,int dcount,int io,int dolock)12380ee5cbfSDavid du Colombier _scsicmd(Scsi *s, uchar *cmd, int ccount, void *data, int dcount, int io, int dolock)
1247dd7cddfSDavid du Colombier {
1257dd7cddfSDavid du Colombier uchar resp[16];
1267dd7cddfSDavid du Colombier int n;
1277dd7cddfSDavid du Colombier long status;
1287dd7cddfSDavid du Colombier
12980ee5cbfSDavid du Colombier if(dolock)
13080ee5cbfSDavid du Colombier qlock(s);
1317dd7cddfSDavid du Colombier if(write(s->rawfd, cmd, ccount) != ccount) {
1327dd7cddfSDavid du Colombier werrstr("cmd write: %r");
13380ee5cbfSDavid du Colombier if(dolock)
13480ee5cbfSDavid du Colombier qunlock(s);
1357dd7cddfSDavid du Colombier return -1;
1367dd7cddfSDavid du Colombier }
1377dd7cddfSDavid du Colombier
1387dd7cddfSDavid du Colombier switch(io){
1397dd7cddfSDavid du Colombier case Sread:
1407dd7cddfSDavid du Colombier n = read(s->rawfd, data, dcount);
14114cc0f53SDavid du Colombier /* read toc errors are frequent and not very interesting */
14214cc0f53SDavid du Colombier if(n < 0 && (scsiverbose == 1 ||
14314cc0f53SDavid du Colombier scsiverbose == 2 && cmd[0] != Readtoc))
1447dd7cddfSDavid du Colombier fprint(2, "dat read: %r: cmd 0x%2.2uX\n", cmd[0]);
1457dd7cddfSDavid du Colombier break;
1467dd7cddfSDavid du Colombier case Swrite:
1477dd7cddfSDavid du Colombier n = write(s->rawfd, data, dcount);
1487dd7cddfSDavid du Colombier if(n != dcount && scsiverbose)
1497dd7cddfSDavid du Colombier fprint(2, "dat write: %r: cmd 0x%2.2uX\n", cmd[0]);
1507dd7cddfSDavid du Colombier break;
1517dd7cddfSDavid du Colombier default:
1527dd7cddfSDavid du Colombier case Snone:
1537dd7cddfSDavid du Colombier n = write(s->rawfd, resp, 0);
1547dd7cddfSDavid du Colombier if(n != 0 && scsiverbose)
1557dd7cddfSDavid du Colombier fprint(2, "none write: %r: cmd 0x%2.2uX\n", cmd[0]);
1567dd7cddfSDavid du Colombier break;
1577dd7cddfSDavid du Colombier }
1587dd7cddfSDavid du Colombier
1597dd7cddfSDavid du Colombier memset(resp, 0, sizeof(resp));
1607dd7cddfSDavid du Colombier if(read(s->rawfd, resp, sizeof(resp)) < 0) {
1617dd7cddfSDavid du Colombier werrstr("resp read: %r\n");
16280ee5cbfSDavid du Colombier if(dolock)
16380ee5cbfSDavid du Colombier qunlock(s);
1647dd7cddfSDavid du Colombier return -1;
1657dd7cddfSDavid du Colombier }
16680ee5cbfSDavid du Colombier if(dolock)
16780ee5cbfSDavid du Colombier qunlock(s);
1687dd7cddfSDavid du Colombier
1697dd7cddfSDavid du Colombier resp[sizeof(resp)-1] = '\0';
1707dd7cddfSDavid du Colombier status = atoi((char*)resp);
1717dd7cddfSDavid du Colombier if(status == 0)
1727dd7cddfSDavid du Colombier return n;
1737dd7cddfSDavid du Colombier
1747dd7cddfSDavid du Colombier werrstr("cmd %2.2uX: status %luX dcount %d n %d", cmd[0], status, dcount, n);
1757dd7cddfSDavid du Colombier return -1;
1767dd7cddfSDavid du Colombier }
1777dd7cddfSDavid du Colombier
1787dd7cddfSDavid du Colombier int
scsicmd(Scsi * s,uchar * cmd,int ccount,void * data,int dcount,int io)17980ee5cbfSDavid du Colombier scsicmd(Scsi *s, uchar *cmd, int ccount, void *data, int dcount, int io)
18080ee5cbfSDavid du Colombier {
18180ee5cbfSDavid du Colombier return _scsicmd(s, cmd, ccount, data, dcount, io, 1);
18280ee5cbfSDavid du Colombier }
18380ee5cbfSDavid du Colombier
18480ee5cbfSDavid du Colombier static int
_scsiready(Scsi * s,int dolock)18580ee5cbfSDavid du Colombier _scsiready(Scsi *s, int dolock)
1867dd7cddfSDavid du Colombier {
187194f7e8cSDavid du Colombier char err[ERRMAX];
1887dd7cddfSDavid du Colombier uchar cmd[6], resp[16];
1897dd7cddfSDavid du Colombier int status, i;
1907dd7cddfSDavid du Colombier
19180ee5cbfSDavid du Colombier if(dolock)
19280ee5cbfSDavid du Colombier qlock(s);
193194f7e8cSDavid du Colombier werrstr("");
1947dd7cddfSDavid du Colombier for(i=0; i<3; i++) {
1957dd7cddfSDavid du Colombier memset(cmd, 0, sizeof(cmd));
1968673b4daSDavid du Colombier cmd[0] = Testrdy; /* unit ready */
1977dd7cddfSDavid du Colombier if(write(s->rawfd, cmd, sizeof(cmd)) != sizeof(cmd)) {
1987dd7cddfSDavid du Colombier if(scsiverbose)
1997dd7cddfSDavid du Colombier fprint(2, "ur cmd write: %r\n");
200194f7e8cSDavid du Colombier werrstr("short unit-ready raw write");
2018673b4daSDavid du Colombier continue;
2027dd7cddfSDavid du Colombier }
2037dd7cddfSDavid du Colombier write(s->rawfd, resp, 0);
2047dd7cddfSDavid du Colombier if(read(s->rawfd, resp, sizeof(resp)) < 0) {
2057dd7cddfSDavid du Colombier if(scsiverbose)
2067dd7cddfSDavid du Colombier fprint(2, "ur resp read: %r\n");
2078673b4daSDavid du Colombier continue;
2087dd7cddfSDavid du Colombier }
2097dd7cddfSDavid du Colombier resp[sizeof(resp)-1] = '\0';
2107dd7cddfSDavid du Colombier status = atoi((char*)resp);
21180ee5cbfSDavid du Colombier if(status == 0 || status == 0x02) {
21280ee5cbfSDavid du Colombier if(dolock)
21380ee5cbfSDavid du Colombier qunlock(s);
2147dd7cddfSDavid du Colombier return 0;
21580ee5cbfSDavid du Colombier }
2167dd7cddfSDavid du Colombier if(scsiverbose)
2177dd7cddfSDavid du Colombier fprint(2, "target: bad status: %x\n", status);
2187dd7cddfSDavid du Colombier }
219194f7e8cSDavid du Colombier rerrstr(err, sizeof err);
220194f7e8cSDavid du Colombier if(err[0] == '\0')
221194f7e8cSDavid du Colombier werrstr("unit did not become ready");
22280ee5cbfSDavid du Colombier if(dolock)
22380ee5cbfSDavid du Colombier qunlock(s);
2247dd7cddfSDavid du Colombier return -1;
2257dd7cddfSDavid du Colombier }
2267dd7cddfSDavid du Colombier
2277dd7cddfSDavid du Colombier int
scsiready(Scsi * s)22880ee5cbfSDavid du Colombier scsiready(Scsi *s)
22980ee5cbfSDavid du Colombier {
23080ee5cbfSDavid du Colombier return _scsiready(s, 1);
23180ee5cbfSDavid du Colombier }
23280ee5cbfSDavid du Colombier
23380ee5cbfSDavid du Colombier int
scsi(Scsi * s,uchar * cmd,int ccount,void * v,int dcount,int io)2347dd7cddfSDavid du Colombier scsi(Scsi *s, uchar *cmd, int ccount, void *v, int dcount, int io)
2357dd7cddfSDavid du Colombier {
2367dd7cddfSDavid du Colombier uchar req[6], sense[255], *data;
2377dd7cddfSDavid du Colombier int tries, code, key, n;
2387dd7cddfSDavid du Colombier char *p;
2397dd7cddfSDavid du Colombier
2407dd7cddfSDavid du Colombier data = v;
2417dd7cddfSDavid du Colombier SET(key, code);
24280ee5cbfSDavid du Colombier qlock(s);
2437dd7cddfSDavid du Colombier for(tries=0; tries<2; tries++) {
24480ee5cbfSDavid du Colombier n = _scsicmd(s, cmd, ccount, data, dcount, io, 0);
24580ee5cbfSDavid du Colombier if(n >= 0) {
24680ee5cbfSDavid du Colombier qunlock(s);
2477dd7cddfSDavid du Colombier return n;
24880ee5cbfSDavid du Colombier }
2497dd7cddfSDavid du Colombier
2507dd7cddfSDavid du Colombier /*
2517dd7cddfSDavid du Colombier * request sense
2527dd7cddfSDavid du Colombier */
2537dd7cddfSDavid du Colombier memset(req, 0, sizeof(req));
2548673b4daSDavid du Colombier req[0] = Reqsense;
2557dd7cddfSDavid du Colombier req[4] = sizeof(sense);
2567dd7cddfSDavid du Colombier memset(sense, 0xFF, sizeof(sense));
25780ee5cbfSDavid du Colombier if((n=_scsicmd(s, req, sizeof(req), sense, sizeof(sense), Sread, 0)) < 14)
2587dd7cddfSDavid du Colombier if(scsiverbose)
2597dd7cddfSDavid du Colombier fprint(2, "reqsense scsicmd %d: %r\n", n);
2607dd7cddfSDavid du Colombier
26180ee5cbfSDavid du Colombier if(_scsiready(s, 0) < 0)
2627dd7cddfSDavid du Colombier if(scsiverbose)
2637dd7cddfSDavid du Colombier fprint(2, "unit not ready\n");
2647dd7cddfSDavid du Colombier
2658673b4daSDavid du Colombier key = sense[2] & 0xf;
2668673b4daSDavid du Colombier code = sense[12]; /* asc */
2678673b4daSDavid du Colombier if(code == Recovnoecc || code == Recovecc) { /* recovered errors */
26880ee5cbfSDavid du Colombier qunlock(s);
2697dd7cddfSDavid du Colombier return dcount;
27080ee5cbfSDavid du Colombier }
2718673b4daSDavid du Colombier
2728673b4daSDavid du Colombier /* retry various odd cases */
2738673b4daSDavid du Colombier if(code == Newmedium && cmd[0] == Readtoc) {
27414cc0f53SDavid du Colombier /* read toc and media changed */
2757dd7cddfSDavid du Colombier s->nchange++;
2767dd7cddfSDavid du Colombier s->changetime = time(0);
277*27522402SDavid du Colombier } else if((cmd[0] == Write10 || cmd[0] == Writever10) &&
278*27522402SDavid du Colombier key == Sensenotrdy &&
2798673b4daSDavid du Colombier code == Lunnotrdy && sense[13] == 0x08) {
2808673b4daSDavid du Colombier /* long write in progress, per mmc-6 */
2818673b4daSDavid du Colombier tries = 0;
2828673b4daSDavid du Colombier sleep(1);
2837dd7cddfSDavid du Colombier }
2847dd7cddfSDavid du Colombier }
2857dd7cddfSDavid du Colombier
2867dd7cddfSDavid du Colombier /* drive not ready, or medium not present */
2878673b4daSDavid du Colombier if(cmd[0] == Readtoc && key == Sensenotrdy &&
2888673b4daSDavid du Colombier (code == Nomedium || code == Lunnotrdy)) {
2897dd7cddfSDavid du Colombier s->changetime = 0;
29080ee5cbfSDavid du Colombier qunlock(s);
2917dd7cddfSDavid du Colombier return -1;
2927dd7cddfSDavid du Colombier }
29380ee5cbfSDavid du Colombier qunlock(s);
2947dd7cddfSDavid du Colombier
2958673b4daSDavid du Colombier if(cmd[0] == Readtoc && key == Sensebadreq && code == Badcdb)
2968673b4daSDavid du Colombier return -1; /* blank media */
2977dd7cddfSDavid du Colombier
2987dd7cddfSDavid du Colombier p = scsierror(code, sense[13]);
2997dd7cddfSDavid du Colombier
3007dd7cddfSDavid du Colombier werrstr("cmd #%.2ux: %s", cmd[0], p);
3017dd7cddfSDavid du Colombier
3027dd7cddfSDavid du Colombier if(scsiverbose)
30314cc0f53SDavid du Colombier fprint(2, "scsi cmd #%.2ux: %.2ux %.2ux %.2ux: %s\n",
30414cc0f53SDavid du Colombier cmd[0], key, code, sense[13], p);
3057dd7cddfSDavid du Colombier
3068673b4daSDavid du Colombier // if(key == Sensenone)
3077dd7cddfSDavid du Colombier // return dcount;
3087dd7cddfSDavid du Colombier return -1;
3097dd7cddfSDavid du Colombier }
3107dd7cddfSDavid du Colombier
3117dd7cddfSDavid du Colombier Scsi*
openscsi(char * dev)3127dd7cddfSDavid du Colombier openscsi(char *dev)
3137dd7cddfSDavid du Colombier {
3147dd7cddfSDavid du Colombier Scsi *s;
3157dd7cddfSDavid du Colombier int rawfd, ctlfd, l, n;
3167dd7cddfSDavid du Colombier char *name, *p, buf[512];
3177dd7cddfSDavid du Colombier
3187dd7cddfSDavid du Colombier l = strlen(dev)+1+3+1;
31959cc4ca5SDavid du Colombier name = malloc(l);
32059cc4ca5SDavid du Colombier if(name == nil)
32159cc4ca5SDavid du Colombier return nil;
3227dd7cddfSDavid du Colombier
3237dd7cddfSDavid du Colombier snprint(name, l, "%s/raw", dev);
32459cc4ca5SDavid du Colombier if((rawfd = open(name, ORDWR)) < 0) {
32559cc4ca5SDavid du Colombier free(name);
3267dd7cddfSDavid du Colombier return nil;
32759cc4ca5SDavid du Colombier }
3287dd7cddfSDavid du Colombier
3297dd7cddfSDavid du Colombier snprint(name, l, "%s/ctl", dev);
3307dd7cddfSDavid du Colombier if((ctlfd = open(name, ORDWR)) < 0) {
3317dd7cddfSDavid du Colombier Error:
332194f7e8cSDavid du Colombier free(name);
3337dd7cddfSDavid du Colombier close(rawfd);
3347dd7cddfSDavid du Colombier return nil;
3357dd7cddfSDavid du Colombier }
3367dd7cddfSDavid du Colombier
3377dd7cddfSDavid du Colombier n = readn(ctlfd, buf, sizeof buf);
3387dd7cddfSDavid du Colombier close(ctlfd);
339194f7e8cSDavid du Colombier if(n <= 0) {
340194f7e8cSDavid du Colombier if(n == 0)
341194f7e8cSDavid du Colombier werrstr("eof on %s", name);
3427dd7cddfSDavid du Colombier goto Error;
343194f7e8cSDavid du Colombier }
3447dd7cddfSDavid du Colombier
345194f7e8cSDavid du Colombier if(strncmp(buf, "inquiry ", 8) != 0 || (p = strchr(buf, '\n')) == nil) {
346194f7e8cSDavid du Colombier werrstr("inquiry mal-formatted in %s", name);
3477dd7cddfSDavid du Colombier goto Error;
348194f7e8cSDavid du Colombier }
3497dd7cddfSDavid du Colombier *p = '\0';
350194f7e8cSDavid du Colombier free(name);
351194f7e8cSDavid du Colombier name = nil;
3527dd7cddfSDavid du Colombier
3537dd7cddfSDavid du Colombier if((p = strdup(buf+8)) == nil)
3547dd7cddfSDavid du Colombier goto Error;
3557dd7cddfSDavid du Colombier
356766ccd67SDavid du Colombier s = mallocz(sizeof(*s), 1);
35759cc4ca5SDavid du Colombier if(s == nil) {
35859cc4ca5SDavid du Colombier Error1:
35959cc4ca5SDavid du Colombier free(p);
36059cc4ca5SDavid du Colombier goto Error;
36159cc4ca5SDavid du Colombier }
36259cc4ca5SDavid du Colombier
3637dd7cddfSDavid du Colombier s->rawfd = rawfd;
3647dd7cddfSDavid du Colombier s->inquire = p;
3657dd7cddfSDavid du Colombier s->changetime = time(0);
3667dd7cddfSDavid du Colombier
36759cc4ca5SDavid du Colombier if(scsiready(s) < 0)
36859cc4ca5SDavid du Colombier goto Error1;
3697dd7cddfSDavid du Colombier
3707dd7cddfSDavid du Colombier return s;
3717dd7cddfSDavid du Colombier }
372af6a38e6SDavid du Colombier
373af6a38e6SDavid du Colombier void
closescsi(Scsi * s)374af6a38e6SDavid du Colombier closescsi(Scsi *s)
375af6a38e6SDavid du Colombier {
376af6a38e6SDavid du Colombier close(s->rawfd);
377af6a38e6SDavid du Colombier free(s->inquire);
378af6a38e6SDavid du Colombier free(s);
379af6a38e6SDavid du Colombier }
380