xref: /plan9/sys/src/libdisk/scsi.c (revision 2752240283b2037f93a98c5bab11217cae887208)
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
getcodes(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*
scsierror(int asc,int ascq)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
_scsicmd(Scsi * s,uchar * cmd,int ccount,void * data,int dcount,int io,int dolock)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
scsicmd(Scsi * s,uchar * cmd,int ccount,void * data,int dcount,int io)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
_scsiready(Scsi * s,int dolock)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
scsiready(Scsi * s)228 scsiready(Scsi *s)
229 {
230 	return _scsiready(s, 1);
231 }
232 
233 int
scsi(Scsi * s,uchar * cmd,int ccount,void * v,int dcount,int io)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 		}
284 	}
285 
286 	/* drive not ready, or medium not present */
287 	if(cmd[0] == Readtoc && key == Sensenotrdy &&
288 	    (code == Nomedium || code == Lunnotrdy)) {
289 		s->changetime = 0;
290 		qunlock(s);
291 		return -1;
292 	}
293 	qunlock(s);
294 
295 	if(cmd[0] == Readtoc && key == Sensebadreq && code == Badcdb)
296 		return -1;			/* blank media */
297 
298 	p = scsierror(code, sense[13]);
299 
300 	werrstr("cmd #%.2ux: %s", cmd[0], p);
301 
302 	if(scsiverbose)
303 		fprint(2, "scsi cmd #%.2ux: %.2ux %.2ux %.2ux: %s\n",
304 			cmd[0], key, code, sense[13], p);
305 
306 //	if(key == Sensenone)
307 //		return dcount;
308 	return -1;
309 }
310 
311 Scsi*
openscsi(char * dev)312 openscsi(char *dev)
313 {
314 	Scsi *s;
315 	int rawfd, ctlfd, l, n;
316 	char *name, *p, buf[512];
317 
318 	l = strlen(dev)+1+3+1;
319 	name = malloc(l);
320 	if(name == nil)
321 		return nil;
322 
323 	snprint(name, l, "%s/raw", dev);
324 	if((rawfd = open(name, ORDWR)) < 0) {
325 		free(name);
326 		return nil;
327 	}
328 
329 	snprint(name, l, "%s/ctl", dev);
330 	if((ctlfd = open(name, ORDWR)) < 0) {
331 	Error:
332 		free(name);
333 		close(rawfd);
334 		return nil;
335 	}
336 
337 	n = readn(ctlfd, buf, sizeof buf);
338 	close(ctlfd);
339 	if(n <= 0) {
340 		if(n == 0)
341 			werrstr("eof on %s", name);
342 		goto Error;
343 	}
344 
345 	if(strncmp(buf, "inquiry ", 8) != 0 || (p = strchr(buf, '\n')) == nil) {
346 		werrstr("inquiry mal-formatted in %s", name);
347 		goto Error;
348 	}
349 	*p = '\0';
350 	free(name);
351 	name = nil;
352 
353 	if((p = strdup(buf+8)) == nil)
354 		goto Error;
355 
356 	s = mallocz(sizeof(*s), 1);
357 	if(s == nil) {
358 	Error1:
359 		free(p);
360 		goto Error;
361 	}
362 
363 	s->rawfd = rawfd;
364 	s->inquire = p;
365 	s->changetime = time(0);
366 
367 	if(scsiready(s) < 0)
368 		goto Error1;
369 
370 	return s;
371 }
372 
373 void
closescsi(Scsi * s)374 closescsi(Scsi *s)
375 {
376 	close(s->rawfd);
377 	free(s->inquire);
378 	free(s);
379 }
380