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
scsiinit(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 = malloc(Ninquiry);
50 tp->sense = malloc(Nsense);
51 }
52 }
53 }
54
55 static uchar lastcmd[16];
56 static int lastcmdsz;
57
58 static int
sense2stcode(uchar * sense)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
doscsi(Target * tp,int rw,uchar * cmd,int cbytes,void * data,int * dbytes)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
scsiexec(Target * tp,int rw,uchar * cmd,int cbytes,void * data,int * dbytes)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
scsitest(Target * tp,char lun)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
scsistart(Target * tp,char lun,int start)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
scsiinquiry(Target * tp,char lun,int * nbytes)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
scsireqsense(Target * tp,char lun,int * nbytes,int quiet)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 print("%s: reqsense: '%s' code #%2.2ux #%2.2ux\n",
283 tp->id, s, sense[12], sense[13]);
284 print("%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 print("lastcmd (%d): ", lastcmdsz);
287 for(n = 0; n < lastcmdsz; n++)
288 print(" #%2.2ux", lastcmd[n]);
289 print("\n");
290 }
291
292 return STcheck;
293 }
294
295 static Target*
scsitarget(Device * d)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
scsiprobe(Device * d)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 print("%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 print("%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 print("%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 print("%s: inquiry failed, status %d\n", tp->id, s);
372 return;
373 }
374 print("%s: %s\n", tp->id, (char*)tp->inquiry+8);
375 tp->ok = 1;
376 }
377
378 int
scsiio(Device * d,int rw,uchar * cmd,int cbytes,void * data,int dbytes)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 print("%s: retry %d cmd #%x\n", tp->id, e, cmd[0]);
412 return s;
413 }
414
415 void
newscsi(Device * d,Scsi * sc)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 }
424