xref: /plan9/sys/src/libdisk/scsi.c (revision 194f7e8c198033c8e6f5e1f2ca471b130e94513a)
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