xref: /plan9-contrib/sys/src/libdisk/scsi.c (revision 60014d6756a98ad10929607ca84a1b7488a16cfc)
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 	Readtoc	= 0x43,
18 };
19 
20 int scsiverbose;
21 
22 #define codefile "/sys/lib/scsicodes"
23 
24 static char *codes;
25 static QLock codeqlock;
26 
27 static void
28 getcodes(void)
29 {
30 	Dir *d;
31 	int n, fd;
32 
33 	if(codes != nil)
34 		return;
35 
36 	qlock(&codeqlock);
37 	if(codes != nil) {
38 		qunlock(&codeqlock);
39 		return;
40 	}
41 
42 	if((d = dirstat(codefile)) == nil || (fd = open(codefile, OREAD)) < 0) {
43 		qunlock(&codeqlock);
44 		return;
45 	}
46 
47 	codes = malloc(1+d->length+1);
48 	if(codes == nil) {
49 		close(fd);
50 		qunlock(&codeqlock);
51 		free(d);
52 		return;
53 	}
54 
55 	codes[0] = '\n';	/* for searches */
56 	n = readn(fd, codes+1, d->length);
57 	close(fd);
58 	free(d);
59 
60 	if(n < 0) {
61 		free(codes);
62 		codes = nil;
63 		qunlock(&codeqlock);
64 		return;
65 	}
66 	codes[n] = '\0';
67 	qunlock(&codeqlock);
68 }
69 
70 char*
71 scsierror(int asc, int ascq)
72 {
73 	char *p, *q;
74 	static char search[32];
75 	static char buf[128];
76 
77 	getcodes();
78 
79 	if(codes) {
80 		sprint(search, "\n%.2ux%.2ux ", asc, ascq);
81 		if(p = strstr(codes, search)) {
82 			p += 6;
83 			if((q = strchr(p, '\n')) == nil)
84 				q = p+strlen(p);
85 			snprint(buf, sizeof buf, "%.*s", (int)(q-p), p);
86 			return buf;
87 		}
88 
89 		sprint(search, "\n%.2ux00", asc);
90 		if(p = strstr(codes, search)) {
91 			p += 6;
92 			if((q = strchr(p, '\n')) == nil)
93 				q = p+strlen(p);
94 			snprint(buf, sizeof buf, "(ascq #%.2ux) %.*s", ascq, (int)(q-p), p);
95 			return buf;
96 		}
97 	}
98 
99 	sprint(buf, "scsi #%.2ux %.2ux", asc, ascq);
100 	return buf;
101 }
102 
103 
104 static int
105 _scsicmd(Scsi *s, uchar *cmd, int ccount, void *data, int dcount, int io, int dolock)
106 {
107 	uchar resp[16];
108 	int n;
109 	long status;
110 
111 	if(dolock)
112 		qlock(s);
113 	if(write(s->rawfd, cmd, ccount) != ccount) {
114 		werrstr("cmd write: %r");
115 		if(dolock)
116 			qunlock(s);
117 		return -1;
118 	}
119 
120 	switch(io){
121 	case Sread:
122 		n = read(s->rawfd, data, dcount);
123 		/* read toc errors are frequent and not very interesting */
124 		if(n < 0 && (scsiverbose == 1 ||
125 		    scsiverbose == 2 && cmd[0] != Readtoc))
126 			fprint(2, "dat read: %r: cmd 0x%2.2uX\n", cmd[0]);
127 		break;
128 	case Swrite:
129 		n = write(s->rawfd, data, dcount);
130 		if(n != dcount && scsiverbose)
131 			fprint(2, "dat write: %r: cmd 0x%2.2uX\n", cmd[0]);
132 		break;
133 	default:
134 	case Snone:
135 		n = write(s->rawfd, resp, 0);
136 		if(n != 0 && scsiverbose)
137 			fprint(2, "none write: %r: cmd 0x%2.2uX\n", cmd[0]);
138 		break;
139 	}
140 
141 	memset(resp, 0, sizeof(resp));
142 	if(read(s->rawfd, resp, sizeof(resp)) < 0) {
143 		werrstr("resp read: %r\n");
144 		if(dolock)
145 			qunlock(s);
146 		return -1;
147 	}
148 	if(dolock)
149 		qunlock(s);
150 
151 	resp[sizeof(resp)-1] = '\0';
152 	status = atoi((char*)resp);
153 	if(status == 0)
154 		return n;
155 
156 	werrstr("cmd %2.2uX: status %luX dcount %d n %d", cmd[0], status, dcount, n);
157 	return -1;
158 }
159 
160 int
161 scsicmd(Scsi *s, uchar *cmd, int ccount, void *data, int dcount, int io)
162 {
163 	return _scsicmd(s, cmd, ccount, data, dcount, io, 1);
164 }
165 
166 static int
167 _scsiready(Scsi *s, int dolock)
168 {
169 	uchar cmd[6], resp[16];
170 	int status, i;
171 
172 	if(dolock)
173 		qlock(s);
174 	for(i=0; i<3; i++) {
175 		memset(cmd, 0, sizeof(cmd));
176 		cmd[0] = 0x00;	/* unit ready */
177 		if(write(s->rawfd, cmd, sizeof(cmd)) != sizeof(cmd)) {
178 			if(scsiverbose)
179 				fprint(2, "ur cmd write: %r\n");
180 			goto bad;
181 		}
182 		write(s->rawfd, resp, 0);
183 		if(read(s->rawfd, resp, sizeof(resp)) < 0) {
184 			if(scsiverbose)
185 				fprint(2, "ur resp read: %r\n");
186 			goto bad;
187 		}
188 		resp[sizeof(resp)-1] = '\0';
189 		status = atoi((char*)resp);
190 		if(status == 0 || status == 0x02) {
191 			if(dolock)
192 				qunlock(s);
193 			return 0;
194 		}
195 		if(scsiverbose)
196 			fprint(2, "target: bad status: %x\n", status);
197 	bad:;
198 	}
199 	if(dolock)
200 		qunlock(s);
201 	return -1;
202 }
203 
204 int
205 scsiready(Scsi *s)
206 {
207 	return _scsiready(s, 1);
208 }
209 
210 int
211 scsi(Scsi *s, uchar *cmd, int ccount, void *v, int dcount, int io)
212 {
213 	uchar req[6], sense[255], *data;
214 	int tries, code, key, n;
215 	char *p;
216 
217 	data = v;
218 	SET(key, code);
219 	qlock(s);
220 	for(tries=0; tries<2; tries++) {
221 		n = _scsicmd(s, cmd, ccount, data, dcount, io, 0);
222 		if(n >= 0) {
223 			qunlock(s);
224 			return n;
225 		}
226 
227 		/*
228 		 * request sense
229 		 */
230 		memset(req, 0, sizeof(req));
231 		req[0] = 0x03;
232 		req[4] = sizeof(sense);
233 		memset(sense, 0xFF, sizeof(sense));
234 		if((n=_scsicmd(s, req, sizeof(req), sense, sizeof(sense), Sread, 0)) < 14)
235 			if(scsiverbose)
236 				fprint(2, "reqsense scsicmd %d: %r\n", n);
237 
238 		if(_scsiready(s, 0) < 0)
239 			if(scsiverbose)
240 				fprint(2, "unit not ready\n");
241 
242 		key = sense[2];
243 		code = sense[12];
244 		if(code == 0x17 || code == 0x18) {	/* recovered errors */
245 			qunlock(s);
246 			return dcount;
247 		}
248 		if(code == 0x28 && cmd[0] == Readtoc) {
249 			/* read toc and media changed */
250 			s->nchange++;
251 			s->changetime = time(0);
252 			continue;
253 		}
254 	}
255 
256 	/* drive not ready, or medium not present */
257 	if(cmd[0] == Readtoc && key == 2 && (code == 0x3a || code == 0x04)) {
258 		s->changetime = 0;
259 		qunlock(s);
260 		return -1;
261 	}
262 	qunlock(s);
263 
264 	if(cmd[0] == Readtoc && key == 5 && code == 0x24) /* blank media */
265 		return -1;
266 
267 	p = scsierror(code, sense[13]);
268 
269 	werrstr("cmd #%.2ux: %s", cmd[0], p);
270 
271 	if(scsiverbose)
272 		fprint(2, "scsi cmd #%.2ux: %.2ux %.2ux %.2ux: %s\n",
273 			cmd[0], key, code, sense[13], p);
274 
275 //	if(key == 0)
276 //		return dcount;
277 	return -1;
278 }
279 
280 Scsi*
281 openscsi(char *dev)
282 {
283 	Scsi *s;
284 	int rawfd, ctlfd, l, n;
285 	char *name, *p, buf[512];
286 
287 	l = strlen(dev)+1+3+1;
288 	name = malloc(l);
289 	if(name == nil)
290 		return nil;
291 
292 	snprint(name, l, "%s/raw", dev);
293 	if((rawfd = open(name, ORDWR)) < 0) {
294 		free(name);
295 		return nil;
296 	}
297 
298 	snprint(name, l, "%s/ctl", dev);
299 	if((ctlfd = open(name, ORDWR)) < 0) {
300 		free(name);
301 	Error:
302 		close(rawfd);
303 		return nil;
304 	}
305 	free(name);
306 
307 	n = readn(ctlfd, buf, sizeof buf);
308 	close(ctlfd);
309 	if(n <= 0)
310 		goto Error;
311 
312 	if(strncmp(buf, "inquiry ", 8) != 0 || (p = strchr(buf, '\n')) == nil)
313 		goto Error;
314 	*p = '\0';
315 
316 	if((p = strdup(buf+8)) == nil)
317 		goto Error;
318 
319 	s = mallocz(sizeof(*s), 1);
320 	if(s == nil) {
321 	Error1:
322 		free(p);
323 		goto Error;
324 	}
325 
326 	s->rawfd = rawfd;
327 	s->inquire = p;
328 	s->changetime = time(0);
329 
330 	if(scsiready(s) < 0)
331 		goto Error1;
332 
333 	return s;
334 }
335 
336 void
337 closescsi(Scsi *s)
338 {
339 	close(s->rawfd);
340 	free(s->inquire);
341 	free(s);
342 }
343