]> git.lizzy.rs Git - plan9front.git/blob - sys/src/libdisk/scsi.c
newt: remove fn f, replace with walk
[plan9front.git] / sys / src / libdisk / scsi.c
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 }