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