Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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 = malloc(Ninquiry);
50
			tp->sense = malloc(Nsense);
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
		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*
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
		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
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
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
}