]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/cwfs/scsi.c
cwfs: remove old some assert() debugging
[plan9front.git] / sys / src / cmd / cwfs / scsi.c
1 /*
2  * interface to scsi devices via scsi(2) to sd(3),
3  * which does not implement LUNs.
4  */
5 #include "all.h"
6 #include "io.h"
7
8 enum {
9         Ninquiry        = 255,
10         Nsense          = 255,
11
12         CMDtest         = 0x00,
13         CMDreqsense     = 0x03,
14         CMDread6        = 0x08,
15         CMDwrite6       = 0x0A,
16         CMDinquiry      = 0x12,
17         CMDstart        = 0x1B,
18         CMDread10       = 0x28,
19         CMDwrite10      = 0x2A,
20 };
21
22 typedef struct {
23         Target  target[NTarget];
24 } Ctlr;
25 static Ctlr scsictlr[MaxScsi];
26
27 extern int scsiverbose;
28
29 void
30 scsiinit(void)
31 {
32         Ctlr *ctlr;
33         int ctlrno, targetno;
34         Target *tp;
35
36         scsiverbose = 1;
37         for(ctlrno = 0; ctlrno < MaxScsi; ctlrno++){
38                 ctlr = &scsictlr[ctlrno];
39                 memset(ctlr, 0, sizeof(Ctlr));
40                 for(targetno = 0; targetno < NTarget; targetno++){
41                         tp = &ctlr->target[targetno];
42
43                         qlock(tp);
44                         qunlock(tp);
45                         sprint(tp->id, "scsictlr#%d.%d", ctlrno, targetno);
46
47                         tp->ctlrno = ctlrno;
48                         tp->targetno = targetno;
49                         tp->inquiry = ialloc(Ninquiry, 0);
50                         tp->sense = ialloc(Nsense, 0);
51                 }
52         }
53 }
54
55 static uchar lastcmd[16];
56 static int lastcmdsz;
57
58 static int
59 sense2stcode(uchar *sense)
60 {
61         switch(sense[2] & 0x0F){
62         case 6:                                         /* unit attention */
63                 /*
64                  * 0x28 - not ready to ready transition,
65                  *        medium may have changed.
66                  * 0x29 - power on, RESET or BUS DEVICE RESET occurred.
67                  */
68                 if(sense[12] != 0x28 && sense[12] != 0x29)
69                         return STcheck;
70                 /*FALLTHROUGH*/
71         case 0:                                         /* no sense */
72         case 1:                                         /* recovered error */
73                 return STok;
74         case 8:                                         /* blank data */
75                 return STblank;
76         case 2:                                         /* not ready */
77                 if(sense[12] == 0x3A)                   /* medium not present */
78                         return STcheck;
79                 /*FALLTHROUGH*/
80         default:
81                 /*
82                  * If unit is becoming ready, rather than not ready,
83                  * then wait a little then poke it again; should this
84                  * be here or in the caller?
85                  */
86                 if((sense[12] == 0x04 && sense[13] == 0x01)) {
87                         // delay(500);
88                         // scsitest(tp, lun);
89                         fprint(2, "sense2stcode: unit becoming ready\n");
90                         return STcheck;                 /* not exactly right */
91                 }
92                 return STcheck;
93         }
94 }
95
96 /*
97  * issue the SCSI command via scsi(2).  lun must already be in cmd[1].
98  */
99 static int
100 doscsi(Target* tp, int rw, uchar* cmd, int cbytes, void* data, int* dbytes)
101 {
102         int lun, db = 0;
103         uchar reqcmd[6], reqdata[Nsense], dummy[1];
104         Scsi *sc;
105
106         sc = tp->sc;
107         if (sc == nil)
108                 panic("doscsi: nil tp->sc");
109         lun = cmd[1] >> 5;      /* save lun in case we need it for reqsense */
110
111         /* cope with zero arguments */
112         if (dbytes != nil)
113                 db = *dbytes;
114         if (data == nil)
115                 data = dummy;
116
117         if (scsi(sc, cmd, cbytes, data, db, rw) >= 0)
118                 return STok;
119
120         /* cmd failed, get whatever sense data we can */
121         memset(reqcmd, 0, sizeof reqcmd);
122         reqcmd[0] = CMDreqsense;
123         reqcmd[1] = lun<<5;
124         reqcmd[4] = Nsense;
125         memset(reqdata, 0, sizeof reqdata);
126         if (scsicmd(sc, reqcmd, sizeof reqcmd, reqdata, sizeof reqdata,
127             Sread) < 0)
128                 return STharderr;
129
130         /* translate sense data to ST* codes */
131         return sense2stcode(reqdata);
132 }
133
134 static int
135 scsiexec(Target* tp, int rw, uchar* cmd, int cbytes, void* data, int* dbytes)
136 {
137         int s;
138
139         /*
140          * issue the SCSI command.  lun must already be in cmd[1].
141          */
142         s = doscsi(tp, rw, cmd, cbytes, data, dbytes);
143         switch(s){
144
145         case STcheck:
146                 memmove(lastcmd, cmd, cbytes);
147                 lastcmdsz = cbytes;
148                 /*FALLTHROUGH*/
149
150         default:
151                 /*
152                  * It's more complicated than this.  There are conditions which
153                  * are 'ok' but for which the returned status code is not 'STok'.
154                  * Also, not all conditions require a reqsense, there may be a
155                  * need to do a reqsense here when necessary and making it
156                  * available to the caller somehow.
157                  *
158                  * Later.
159                  */
160                 break;
161         }
162
163         return s;
164 }
165
166 static int
167 scsitest(Target* tp, char lun)
168 {
169         uchar cmd[6];
170
171         memset(cmd, 0, sizeof cmd);
172         cmd[0] = CMDtest;
173         cmd[1] = lun<<5;
174         return scsiexec(tp, SCSIread, cmd, sizeof cmd, 0, 0);
175
176 }
177
178 static int
179 scsistart(Target* tp, char lun, int start)
180 {
181         uchar cmd[6];
182
183         memset(cmd, 0, sizeof cmd);
184         cmd[0] = CMDstart;
185         cmd[1] = lun<<5;
186         if(start)
187                 cmd[4] = 1;
188         return scsiexec(tp, SCSIread, cmd, sizeof cmd, 0, 0);
189 }
190
191 static int
192 scsiinquiry(Target* tp, char lun, int* nbytes)
193 {
194         uchar cmd[6];
195
196         memset(cmd, 0, sizeof cmd);
197         cmd[0] = CMDinquiry;
198         cmd[1] = lun<<5;
199         *nbytes = Ninquiry;
200         cmd[4] = *nbytes;
201         return scsiexec(tp, SCSIread, cmd, sizeof cmd, tp->inquiry, nbytes);
202 }
203
204 static char *key[] =
205 {
206         "no sense",
207         "recovered error",
208         "not ready",
209         "medium error",
210         "hardware error",
211         "illegal request",
212         "unit attention",
213         "data protect",
214         "blank check",
215         "vendor specific",
216         "copy aborted",
217         "aborted command",
218         "equal",
219         "volume overflow",
220         "miscompare",
221         "reserved"
222 };
223
224 static int
225 scsireqsense(Target* tp, char lun, int* nbytes, int quiet)
226 {
227         char *s;
228         int n, status, try;
229         uchar cmd[6], *sense;
230
231         sense = tp->sense;
232         for(try = 0; try < 20; try++) {
233                 memset(cmd, 0, sizeof cmd);
234                 cmd[0] = CMDreqsense;
235                 cmd[1] = lun<<5;
236                 cmd[4] = Ninquiry;
237                 memset(sense, 0, Ninquiry);
238
239                 *nbytes = Ninquiry;
240                 status = scsiexec(tp, SCSIread, cmd, sizeof cmd, sense, nbytes);
241                 if(status != STok)
242                         return status;
243                 *nbytes = sense[0x07]+8;
244
245                 switch(sense[2] & 0x0F){
246                 case 6:                                 /* unit attention */
247                         /*
248                          * 0x28 - not ready to ready transition,
249                          *        medium may have changed.
250                          * 0x29 - power on, RESET or BUS DEVICE RESET occurred.
251                          */
252                         if(sense[12] != 0x28 && sense[12] != 0x29)
253                                 goto buggery;
254                         /*FALLTHROUGH*/
255                 case 0:                                 /* no sense */
256                 case 1:                                 /* recovered error */
257                         return STok;
258                 case 8:                                 /* blank data */
259                         return STblank;
260                 case 2:                                 /* not ready */
261                         if(sense[12] == 0x3A)           /* medium not present */
262                                 goto buggery;
263                         /*FALLTHROUGH*/
264                 default:
265                         /*
266                          * If unit is becoming ready, rather than not ready,
267                          * then wait a little then poke it again; should this
268                          * be here or in the caller?
269                          */
270                         if((sense[12] == 0x04 && sense[13] == 0x01)){
271                                 delay(500);
272                                 scsitest(tp, lun);
273                                 break;
274                         }
275                         goto buggery;
276                 }
277         }
278
279 buggery:
280         if(quiet == 0){
281                 s = key[sense[2]&0x0F];
282                 fprint(2, "%s: reqsense: '%s' code #%2.2ux #%2.2ux\n",
283                         tp->id, s, sense[12], sense[13]);
284                 fprint(2, "%s: byte 2: #%2.2ux, bytes 15-17: #%2.2ux #%2.2ux #%2.2ux\n",
285                         tp->id, sense[2], sense[15], sense[16], sense[17]);
286                 fprint(2, "lastcmd (%d): ", lastcmdsz);
287                 for(n = 0; n < lastcmdsz; n++)
288                         fprint(2, " #%2.2ux", lastcmd[n]);
289                 fprint(2, "\n");
290         }
291
292         return STcheck;
293 }
294
295 static Target*
296 scsitarget(Device* d)
297 {
298         int ctlrno, targetno;
299
300         ctlrno = d->wren.ctrl;
301         if(ctlrno < 0 || ctlrno >= MaxScsi /* || scsictlr[ctlrno].io == nil */)
302                 return 0;
303         targetno = d->wren.targ;
304         if(targetno < 0 || targetno >= NTarget)
305                 return 0;
306         return &scsictlr[ctlrno].target[targetno];
307 }
308
309 static void
310 scsiprobe(Device* d)
311 {
312         Target *tp;
313         int nbytes, s;
314         uchar *sense;
315         int acount;
316
317         if((tp = scsitarget(d)) == 0)
318                 panic("scsiprobe: device = %Z", d);
319
320         acount = 0;
321 again:
322         s = scsitest(tp, d->wren.lun);
323         if(s < STok){
324                 fprint(2, "%s: test, status %d\n", tp->id, s);
325                 return;
326         }
327
328         /*
329          * Determine if the drive exists and is not ready or
330          * is simply not responding.
331          * If the status is OK but the drive came back with a 'power on' or
332          * 'reset' status, try the test again to make sure the drive is really
333          * ready.
334          * If the drive is not ready and requires intervention, try to spin it
335          * up.
336          */
337         s = scsireqsense(tp, d->wren.lun, &nbytes, acount);
338         sense = tp->sense;
339         switch(s){
340         case STok:
341                 if ((sense[2] & 0x0F) == 0x06 &&
342                     (sense[12] == 0x28 || sense[12] == 0x29))
343                         if(acount == 0){
344                                 acount = 1;
345                                 goto again;
346                         }
347                 break;
348         case STcheck:
349                 if((sense[2] & 0x0F) == 0x02){
350                         if(sense[12] == 0x3A)
351                                 break;
352                         if(sense[12] == 0x04 && sense[13] == 0x02){
353                                 fprint(2, "%s: starting...\n", tp->id);
354                                 if(scsistart(tp, d->wren.lun, 1) == STok)
355                                         break;
356                                 s = scsireqsense(tp, d->wren.lun, &nbytes, 0);
357                         }
358                 }
359                 /*FALLTHROUGH*/
360         default:
361                 fprint(2, "%s: unavailable, status %d\n", tp->id, s);
362                 return;
363         }
364
365         /*
366          * Inquire to find out what the device is.
367          * Hardware drivers may need some of the info.
368          */
369         s = scsiinquiry(tp, d->wren.lun, &nbytes);
370         if(s != STok) {
371                 fprint(2, "%s: inquiry failed, status %d\n", tp->id, s);
372                 return;
373         }
374         fprint(2, "%s: %s\n", tp->id, (char*)tp->inquiry+8);
375         tp->ok = 1;
376 }
377
378 int
379 scsiio(Device* d, int rw, uchar* cmd, int cbytes, void* data, int dbytes)
380 {
381         Target *tp;
382         int e, nbytes, s;
383
384         if((tp = scsitarget(d)) == 0)
385                 panic("scsiio: device = %Z", d);
386
387         qlock(tp);
388         if(tp->ok == 0)
389                 scsiprobe(d);
390         qunlock(tp);
391
392         s = STinit;
393         for(e = 0; e < 10; e++){
394                 for(;;){
395                         nbytes = dbytes;
396                         s = scsiexec(tp, rw, cmd, cbytes, data, &nbytes);
397                         if(s == STok)
398                                 break;
399                         s = scsireqsense(tp, d->wren.lun, &nbytes, 0);
400                         if(s == STblank && rw == SCSIread) {
401                                 memset(data, 0, dbytes);
402                                 return STok;
403                         }
404                         if(s != STok)
405                                 break;
406                 }
407                 if(s == STok)
408                         break;
409         }
410         if(e)
411                 fprint(2, "%s: retry %d cmd #%x\n", tp->id, e, cmd[0]);
412         return s;
413 }
414
415 void
416 newscsi(Device *d, Scsi *sc)
417 {
418         Target *tp;
419
420         if((tp = scsitarget(d)) == nil)
421                 panic("newscsi: device = %Z", d);
422         tp->sc = sc;            /* connect Target to Scsi */
423 }