xref: /plan9/sys/src/cmd/cdfs/scsi.c (revision 39b91b2bea1bdc6d8e213fc7f7a2911875f95d28)
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 		} 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*
openscsi(char * dev)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
closescsi(Scsi * s)375 closescsi(Scsi *s)
376 {
377 	close(s->rawfd);
378 	free(s->inquire);
379 	free(s);
380 }
381