xref: /plan9/sys/src/cmd/cwfs/scsi.c (revision 01a344a29f2ff35133953eaef092a50fc8c3163b)
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