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