Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
	/* commands */
18
	Testrdy		= 0x00,
19
	Reqsense	= 0x03,
20
	Write10		= 0x2a,
21
	Writever10	= 0x2e,
22
	Readtoc		= 0x43,
23
 
24
	/* sense[2] (key) sense codes */
25
	Sensenone	= 0,
26
	Sensenotrdy	= 2,
27
	Sensebadreq	= 5,
28
 
29
	/* sense[12] (asc) sense codes */
30
	Lunnotrdy	= 0x04,
31
	Recovnoecc	= 0x17,
32
	Recovecc	= 0x18,
33
	Badcdb		= 0x24,
34
	Newmedium	= 0x28,
35
	Nomedium	= 0x3a,
36
};
37
 
38
int scsiverbose;
39
 
40
#define codefile "/sys/lib/scsicodes"
41
 
42
static char *codes;
43
static QLock codeqlock;
44
 
45
static void
46
getcodes(void)
47
{
48
	Dir *d;
49
	int n, fd;
50
 
51
	if(codes != nil)
52
		return;
53
 
54
	qlock(&codeqlock);
55
	if(codes != nil) {
56
		qunlock(&codeqlock);
57
		return;
58
	}
59
 
60
	if((d = dirstat(codefile)) == nil || (fd = open(codefile, OREAD)) < 0) {
61
		qunlock(&codeqlock);
62
		return;
63
	}
64
 
65
	codes = malloc(1+d->length+1);
66
	if(codes == nil) {
67
		close(fd);
68
		qunlock(&codeqlock);
69
		free(d);
70
		return;
71
	}
72
 
73
	codes[0] = '\n';	/* for searches */
74
	n = readn(fd, codes+1, d->length);
75
	close(fd);
76
	free(d);
77
 
78
	if(n < 0) {
79
		free(codes);
80
		codes = nil;
81
		qunlock(&codeqlock);
82
		return;
83
	}
84
	codes[n] = '\0';
85
	qunlock(&codeqlock);
86
}
87
 
88
char*
89
scsierror(int asc, int ascq)
90
{
91
	char *p, *q;
92
	static char search[32];
93
	static char buf[128];
94
 
95
	getcodes();
96
 
97
	if(codes) {
98
		snprint(search, sizeof search, "\n%.2ux%.2ux ", asc, ascq);
99
		if(p = strstr(codes, search)) {
100
			p += 6;
101
			if((q = strchr(p, '\n')) == nil)
102
				q = p+strlen(p);
103
			snprint(buf, sizeof buf, "%.*s", (int)(q-p), p);
104
			return buf;
105
		}
106
 
107
		snprint(search, sizeof search, "\n%.2ux00", asc);
108
		if(p = strstr(codes, search)) {
109
			p += 6;
110
			if((q = strchr(p, '\n')) == nil)
111
				q = p+strlen(p);
112
			snprint(buf, sizeof buf, "(ascq #%.2ux) %.*s", ascq, (int)(q-p), p);
113
			return buf;
114
		}
115
	}
116
 
117
	snprint(buf, sizeof buf, "scsi #%.2ux %.2ux", asc, ascq);
118
	return buf;
119
}
120
 
121
 
122
static int
123
_scsicmd(Scsi *s, uchar *cmd, int ccount, void *data, int dcount, int io, int dolock)
124
{
125
	uchar resp[16];
126
	int n;
127
	long status;
128
 
129
	if(dolock)
130
		qlock(s);
131
	if(write(s->rawfd, cmd, ccount) != ccount) {
132
		werrstr("cmd write: %r");
133
		if(dolock)
134
			qunlock(s);
135
		return -1;
136
	}
137
 
138
	switch(io){
139
	case Sread:
140
		n = read(s->rawfd, data, dcount);
141
		/* read toc errors are frequent and not very interesting */
142
		if(n < 0 && (scsiverbose == 1 ||
143
		    scsiverbose == 2 && cmd[0] != Readtoc))
144
			fprint(2, "dat read: %r: cmd 0x%2.2uX\n", cmd[0]);
145
		break;
146
	case Swrite:
147
		n = write(s->rawfd, data, dcount);
148
		if(n != dcount && scsiverbose)
149
			fprint(2, "dat write: %r: cmd 0x%2.2uX\n", cmd[0]);
150
		break;
151
	default:
152
	case Snone:
153
		n = write(s->rawfd, resp, 0);
154
		if(n != 0 && scsiverbose)
155
			fprint(2, "none write: %r: cmd 0x%2.2uX\n", cmd[0]);
156
		break;
157
	}
158
 
159
	memset(resp, 0, sizeof(resp));
160
	if(read(s->rawfd, resp, sizeof(resp)) < 0) {
161
		werrstr("resp read: %r\n");
162
		if(dolock)
163
			qunlock(s);
164
		return -1;
165
	}
166
	if(dolock)
167
		qunlock(s);
168
 
169
	resp[sizeof(resp)-1] = '\0';
170
	status = atoi((char*)resp);
171
	if(status == 0)
172
		return n;
173
 
174
	werrstr("cmd %2.2uX: status %luX dcount %d n %d", cmd[0], status, dcount, n);
175
	return -1;
176
}
177
 
178
int
179
scsicmd(Scsi *s, uchar *cmd, int ccount, void *data, int dcount, int io)
180
{
181
	return _scsicmd(s, cmd, ccount, data, dcount, io, 1);
182
}
183
 
184
static int
185
_scsiready(Scsi *s, int dolock)
186
{
187
	char err[ERRMAX];
188
	uchar cmd[6], resp[16];
189
	int status, i;
190
 
191
	if(dolock)
192
		qlock(s);
193
	werrstr("");
194
	for(i=0; i<3; i++) {
195
		memset(cmd, 0, sizeof(cmd));
196
		cmd[0] = Testrdy;	/* unit ready */
197
		if(write(s->rawfd, cmd, sizeof(cmd)) != sizeof(cmd)) {
198
			if(scsiverbose)
199
				fprint(2, "ur cmd write: %r\n");
200
			werrstr("short unit-ready raw write");
201
			continue;
202
		}
203
		write(s->rawfd, resp, 0);
204
		if(read(s->rawfd, resp, sizeof(resp)) < 0) {
205
			if(scsiverbose)
206
				fprint(2, "ur resp read: %r\n");
207
			continue;
208
		}
209
		resp[sizeof(resp)-1] = '\0';
210
		status = atoi((char*)resp);
211
		if(status == 0 || status == 0x02) {
212
			if(dolock)
213
				qunlock(s);
214
			return 0;
215
		}
216
		if(scsiverbose)
217
			fprint(2, "target: bad status: %x\n", status);
218
	}
219
	rerrstr(err, sizeof err);
220
	if(err[0] == '\0')
221
		werrstr("unit did not become ready");
222
	if(dolock)
223
		qunlock(s);
224
	return -1;
225
}
226
 
227
int
228
scsiready(Scsi *s)
229
{
230
	return _scsiready(s, 1);
231
}
232
 
233
int
234
scsi(Scsi *s, uchar *cmd, int ccount, void *v, int dcount, int io)
235
{
236
	uchar req[6], sense[255], *data;
237
	int tries, code, key, n;
238
	char *p;
239
 
240
	data = v;
241
	SET(key, code);
242
	qlock(s);
243
	for(tries=0; tries<2; tries++) {
244
		n = _scsicmd(s, cmd, ccount, data, dcount, io, 0);
245
		if(n >= 0) {
246
			qunlock(s);
247
			return n;
248
		}
249
 
250
		/*
251
		 * request sense
252
		 */
253
		memset(req, 0, sizeof(req));
254
		req[0] = Reqsense;
255
		req[4] = sizeof(sense);
256
		memset(sense, 0xFF, sizeof(sense));
257
		if((n=_scsicmd(s, req, sizeof(req), sense, sizeof(sense), Sread, 0)) < 14)
258
			if(scsiverbose)
259
				fprint(2, "reqsense scsicmd %d: %r\n", n);
260
 
261
		if(_scsiready(s, 0) < 0)
262
			if(scsiverbose)
263
				fprint(2, "unit not ready\n");
264
 
265
		key = sense[2] & 0xf;
266
		code = sense[12];			/* asc */
267
		if(code == Recovnoecc || code == Recovecc) { /* recovered errors */
268
			qunlock(s);
269
			return dcount;
270
		}
271
 
272
		/* retry various odd cases */
273
		if(code == Newmedium && cmd[0] == Readtoc) {
274
			/* read toc and media changed */
275
			s->nchange++;
276
			s->changetime = time(0);
277
		} else if((cmd[0] == Write10 || cmd[0] == Writever10) &&
278
		    key == Sensenotrdy &&
279
		    code == Lunnotrdy && sense[13] == 0x08) {
280
			/* long write in progress, per mmc-6 */
281
			tries = 0;
282
			sleep(1);
283
		} else if(cmd[0] == Write10 || cmd[0] == Writever10)
284
			break;		/* don't retry worm writes */
285
	}
286
 
287
	/* drive not ready, or medium not present */
288
	if(cmd[0] == Readtoc && key == Sensenotrdy &&
289
	    (code == Nomedium || code == Lunnotrdy)) {
290
		s->changetime = 0;
291
		qunlock(s);
292
		return -1;
293
	}
294
	qunlock(s);
295
 
296
	if(cmd[0] == Readtoc && key == Sensebadreq && code == Badcdb)
297
		return -1;			/* blank media */
298
 
299
	p = scsierror(code, sense[13]);
300
 
301
	werrstr("cmd #%.2ux: %s", cmd[0], p);
302
 
303
	if(scsiverbose)
304
		fprint(2, "scsi cmd #%.2ux: %.2ux %.2ux %.2ux: %s\n",
305
			cmd[0], key, code, sense[13], p);
306
 
307
//	if(key == Sensenone)
308
//		return dcount;
309
	return -1;
310
}
311
 
312
Scsi*
313
openscsi(char *dev)
314
{
315
	Scsi *s;
316
	int rawfd, ctlfd, l, n;
317
	char *name, *p, buf[512];
318
 
319
	l = strlen(dev)+1+3+1;
320
	name = malloc(l);
321
	if(name == nil)
322
		return nil;
323
 
324
	snprint(name, l, "%s/raw", dev);
325
	if((rawfd = open(name, ORDWR)) < 0) {
326
		free(name);
327
		return nil;
328
	}
329
 
330
	snprint(name, l, "%s/ctl", dev);
331
	if((ctlfd = open(name, ORDWR)) < 0) {
332
	Error:
333
		free(name);
334
		close(rawfd);
335
		return nil;
336
	}
337
 
338
	n = readn(ctlfd, buf, sizeof buf);
339
	close(ctlfd);
340
	if(n <= 0) {
341
		if(n == 0)
342
			werrstr("eof on %s", name);
343
		goto Error;
344
	}
345
 
346
	if(strncmp(buf, "inquiry ", 8) != 0 || (p = strchr(buf, '\n')) == nil) {
347
		werrstr("inquiry mal-formatted in %s", name);
348
		goto Error;
349
	}
350
	*p = '\0';
351
	free(name);
352
	name = nil;
353
 
354
	if((p = strdup(buf+8)) == nil)
355
		goto Error;
356
 
357
	s = mallocz(sizeof(*s), 1);
358
	if(s == nil) {
359
	Error1:
360
		free(p);
361
		goto Error;
362
	}
363
 
364
	s->rawfd = rawfd;
365
	s->inquire = p;
366
	s->changetime = time(0);
367
 
368
	if(scsiready(s) < 0)
369
		goto Error1;
370
 
371
	return s;
372
}
373
 
374
void
375
closescsi(Scsi *s)
376
{
377
	close(s->rawfd);
378
	free(s->inquire);
379
	free(s);
380
}