Subversion Repositories planix.SVN

Rev

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

Rev Author Line No. Line
2 - 1
#include <u.h>
2
#include <libc.h>
3
/*
4
 * BUGS:
5
 *	no luns
6
 *	and incomplete in many other ways
7
 */
8
#include <disk.h>
9
#include "scsireq.h"
10
 
11
enum {
12
	Debug = 0,
13
};
14
 
15
/*
16
 * exabyte tape drives, at least old ones like the 8200 and 8505,
17
 * are dumb: you have to read the exact block size on the tape,
18
 * they don't take 10-byte SCSI commands, and various other fine points.
19
 */
20
extern int exabyte, force6bytecmds;
21
 
22
static int debug = Debug;
23
 
24
long
25
SRready(ScsiReq *rp)
26
{
27
	uchar cmd[6];
28
 
29
	memset(cmd, 0, sizeof cmd);
30
	rp->cmd.p = cmd;
31
	rp->cmd.count = sizeof cmd;
32
	rp->data.p = cmd;
33
	rp->data.count = 0;
34
	rp->data.write = 1;
35
	return SRrequest(rp);
36
}
37
 
38
long
39
SRrewind(ScsiReq *rp)
40
{
41
	uchar cmd[6];
42
 
43
	memset(cmd, 0, sizeof cmd);
44
	cmd[0] = ScmdRewind;
45
	rp->cmd.p = cmd;
46
	rp->cmd.count = sizeof cmd;
47
	rp->data.p = cmd;
48
	rp->data.count = 0;
49
	rp->data.write = 1;
50
	if(SRrequest(rp) >= 0){
51
		rp->offset = 0;
52
		return 0;
53
	}
54
	return -1;
55
}
56
 
57
long
58
SRreqsense(ScsiReq *rp)
59
{
60
	uchar cmd[6];
61
	ScsiReq req;
62
	long status;
63
 
64
	if(rp->status == Status_SD){
65
		rp->status = STok;
66
		return 0;
67
	}
68
	memset(cmd, 0, sizeof cmd);
69
	cmd[0] = ScmdRsense;
70
	cmd[4] = sizeof(req.sense);
71
	memset(&req, 0, sizeof(req));
72
	if(rp->flags&Fusb)
73
		req.flags |= Fusb;
74
	req.fd = rp->fd;
75
	req.umsc = rp->umsc;
76
	req.cmd.p = cmd;
77
	req.cmd.count = sizeof cmd;
78
	req.data.p = rp->sense;
79
	req.data.count = sizeof(rp->sense);
80
	req.data.write = 0;
81
	status = SRrequest(&req);
82
	rp->status = req.status;
83
	return status;
84
}
85
 
86
long
87
SRformat(ScsiReq *rp)
88
{
89
	uchar cmd[6];
90
 
91
	memset(cmd, 0, sizeof cmd);
92
	cmd[0] = ScmdFormat;
93
	rp->cmd.p = cmd;
94
	rp->cmd.count = sizeof cmd;
95
	rp->data.p = cmd;
96
	rp->data.count = 6;
97
	rp->data.write = 0;
98
	return SRrequest(rp);
99
}
100
 
101
long
102
SRrblimits(ScsiReq *rp, uchar *list)
103
{
104
	uchar cmd[6];
105
 
106
	memset(cmd, 0, sizeof cmd);
107
	cmd[0] = ScmdRblimits;
108
	rp->cmd.p = cmd;
109
	rp->cmd.count = sizeof cmd;
110
	rp->data.p = list;
111
	rp->data.count = 6;
112
	rp->data.write = 0;
113
	return SRrequest(rp);
114
}
115
 
116
static int
117
dirdevrw(ScsiReq *rp, uchar *cmd, long nbytes)
118
{
119
	long n;
120
 
121
	n = nbytes / rp->lbsize;
122
	if(rp->offset <= Max24off && n <= 256 && (rp->flags & Frw10) == 0){
123
		PUTBE24(cmd+1, rp->offset);
124
		cmd[4] = n;
125
		cmd[5] = 0;
126
		return 6;
127
	}
128
	cmd[0] |= ScmdExtread;
129
	cmd[1] = 0;
130
	PUTBELONG(cmd+2, rp->offset);
131
	cmd[6] = 0;
132
	cmd[7] = n>>8;
133
	cmd[8] = n;
134
	cmd[9] = 0;
135
	return 10;
136
}
137
 
138
static int
139
seqdevrw(ScsiReq *rp, uchar *cmd, long nbytes)
140
{
141
	long n;
142
 
143
	/* don't set Cmd1sili; we want the ILI bit instead of a fatal error */
144
	cmd[1] = rp->flags&Fbfixed? Cmd1fixed: 0;
145
	n = nbytes / rp->lbsize;
146
	PUTBE24(cmd+2, n);
147
	cmd[5] = 0;
148
	return 6;
149
}
150
 
151
long
152
SRread(ScsiReq *rp, void *buf, long nbytes)
153
{
154
	uchar cmd[10];
155
	long n;
156
 
157
	if((nbytes % rp->lbsize) || nbytes > maxiosize){
158
		if(debug)
159
			if (nbytes % rp->lbsize)
160
				fprint(2, "scuzz: i/o size %ld %% %ld != 0\n",
161
					nbytes, rp->lbsize);
162
			else
163
				fprint(2, "scuzz: i/o size %ld > %ld\n",
164
					nbytes, maxiosize);
165
		rp->status = Status_BADARG;
166
		return -1;
167
	}
168
 
169
	/* set up scsi read cmd */
170
	cmd[0] = ScmdRead;
171
	if(rp->flags & Fseqdev)
172
		rp->cmd.count = seqdevrw(rp, cmd, nbytes);
173
	else
174
		rp->cmd.count = dirdevrw(rp, cmd, nbytes);
175
	rp->cmd.p = cmd;
176
	rp->data.p = buf;
177
	rp->data.count = nbytes;
178
	rp->data.write = 0;
179
 
180
	/* issue it */
181
	n = SRrequest(rp);
182
	if(n != -1){			/* it worked? */
183
		rp->offset += n / rp->lbsize;
184
		return n;
185
	}
186
 
187
	/* request failed; maybe we just read a short record? */
188
	if (exabyte) {
189
		fprint(2, "read error\n");
190
		rp->status = STcheck;
191
		return n;
192
	}
193
	if(rp->status != Status_SD || !(rp->sense[0] & Sd0valid))
194
		return -1;
195
	/* compute # of bytes not read */
196
	n = GETBELONG(rp->sense+3) * rp->lbsize;
197
	if (debug)
198
		fprint(2,
199
	"SRread: request failed with sense data; sense byte count %ld\n",
200
			n);
201
	if(!(rp->flags & Fseqdev))
202
		return -1;
203
 
204
	/* device is a tape or something similar */
205
	if (rp->sense[2] == Sd2filemark || rp->sense[2] == 0x08 ||
206
	    rp->sense[2] & Sd2ili && n > 0)
207
		rp->data.count = nbytes - n;
208
	else
209
		return -1;
210
	n = rp->data.count;
211
	if (!rp->readblock++ || debug)
212
		fprint(2, "SRread: tape data count %ld%s\n", n,
213
			(rp->sense[2] & Sd2ili? " with ILI": ""));
214
	rp->status = STok;
215
	rp->offset += n / rp->lbsize;
216
	return n;
217
}
218
 
219
long
220
SRwrite(ScsiReq *rp, void *buf, long nbytes)
221
{
222
	uchar cmd[10];
223
	long n;
224
 
225
	if((nbytes % rp->lbsize) || nbytes > maxiosize){
226
		if(debug)
227
			if (nbytes % rp->lbsize)
228
				fprint(2, "scuzz: i/o size %ld %% %ld != 0\n",
229
					nbytes, rp->lbsize);
230
			else
231
				fprint(2, "scuzz: i/o size %ld > %ld\n",
232
					nbytes, maxiosize);
233
		rp->status = Status_BADARG;
234
		return -1;
235
	}
236
 
237
	/* set up scsi write cmd */
238
	cmd[0] = ScmdWrite;
239
	if(rp->flags & Fseqdev)
240
		rp->cmd.count = seqdevrw(rp, cmd, nbytes);
241
	else
242
		rp->cmd.count = dirdevrw(rp, cmd, nbytes);
243
	rp->cmd.p = cmd;
244
	rp->data.p = buf;
245
	rp->data.count = nbytes;
246
	rp->data.write = 1;
247
 
248
	/* issue it */
249
	if((n = SRrequest(rp)) == -1){
250
		if (exabyte) {
251
			fprint(2, "write error\n");
252
			rp->status = STcheck;
253
			return n;
254
		}
255
		if(rp->status != Status_SD || rp->sense[2] != Sd2eom)
256
			return -1;
257
		if(rp->sense[0] & Sd0valid){
258
			n -= GETBELONG(rp->sense+3) * rp->lbsize;
259
			rp->data.count = nbytes - n;
260
		}
261
		else
262
			rp->data.count = nbytes;
263
		n = rp->data.count;
264
	}
265
	rp->offset += n / rp->lbsize;
266
	return n;
267
}
268
 
269
long
270
SRseek(ScsiReq *rp, long offset, int type)
271
{
272
	uchar cmd[10];
273
 
274
	switch(type){
275
 
276
	case 0:
277
		break;
278
 
279
	case 1:
280
		offset += rp->offset;
281
		if(offset >= 0)
282
			break;
283
		/*FALLTHROUGH*/
284
 
285
	default:
286
		if(debug)
287
			fprint(2, "scuzz: seek failed\n");
288
		rp->status = Status_BADARG;
289
		return -1;
290
	}
291
	memset(cmd, 0, sizeof cmd);
292
	if(offset <= Max24off && (rp->flags & Frw10) == 0){
293
		cmd[0] = ScmdSeek;
294
		PUTBE24(cmd+1, offset & Max24off);
295
		rp->cmd.count = 6;
296
	}else{
297
		cmd[0] = ScmdExtseek;
298
		PUTBELONG(cmd+2, offset);
299
		rp->cmd.count = 10;
300
	}
301
	rp->cmd.p = cmd;
302
	rp->data.p = cmd;
303
	rp->data.count = 0;
304
	rp->data.write = 1;
305
	SRrequest(rp);
306
	if(rp->status == STok)
307
		return rp->offset = offset;
308
	return -1;
309
}
310
 
311
long
312
SRfilemark(ScsiReq *rp, ulong howmany)
313
{
314
	uchar cmd[6];
315
 
316
	memset(cmd, 0, sizeof cmd);
317
	cmd[0] = ScmdFmark;
318
	PUTBE24(cmd+2, howmany);
319
	rp->cmd.p = cmd;
320
	rp->cmd.count = sizeof cmd;
321
	rp->data.p = cmd;
322
	rp->data.count = 0;
323
	rp->data.write = 1;
324
	return SRrequest(rp);
325
}
326
 
327
long
328
SRspace(ScsiReq *rp, uchar code, long howmany)
329
{
330
	uchar cmd[6];
331
 
332
	memset(cmd, 0, sizeof cmd);
333
	cmd[0] = ScmdSpace;
334
	cmd[1] = code;
335
	PUTBE24(cmd+2, howmany);
336
	rp->cmd.p = cmd;
337
	rp->cmd.count = sizeof cmd;
338
	rp->data.p = cmd;
339
	rp->data.count = 0;
340
	rp->data.write = 1;
341
	/*
342
	 * what about rp->offset?
343
	 */
344
	return SRrequest(rp);
345
}
346
 
347
long
348
SRinquiry(ScsiReq *rp)
349
{
350
	uchar cmd[6];
351
 
352
	memset(cmd, 0, sizeof cmd);
353
	cmd[0] = ScmdInq;
354
	cmd[4] = sizeof rp->inquiry;
355
	rp->cmd.p = cmd;
356
	rp->cmd.count = sizeof cmd;
357
	memset(rp->inquiry, 0, sizeof rp->inquiry);
358
	rp->data.p = rp->inquiry;
359
	rp->data.count = sizeof rp->inquiry;
360
	rp->data.write = 0;
361
	if(SRrequest(rp) >= 0){
362
		rp->flags |= Finqok;
363
		return 0;
364
	}
365
	rp->flags &= ~Finqok;
366
	return -1;
367
}
368
 
369
long
370
SRmodeselect6(ScsiReq *rp, uchar *list, long nbytes)
371
{
372
	uchar cmd[6];
373
 
374
	memset(cmd, 0, sizeof cmd);
375
	cmd[0] = ScmdMselect6;
376
	if((rp->flags & Finqok) && (rp->inquiry[2] & 0x07) >= 2)
377
		cmd[1] = 0x10;
378
	cmd[4] = nbytes;
379
	rp->cmd.p = cmd;
380
	rp->cmd.count = sizeof cmd;
381
	rp->data.p = list;
382
	rp->data.count = nbytes;
383
	rp->data.write = 1;
384
	return SRrequest(rp);
385
}
386
 
387
long
388
SRmodeselect10(ScsiReq *rp, uchar *list, long nbytes)
389
{
390
	uchar cmd[10];
391
 
392
	memset(cmd, 0, sizeof cmd);
393
	if((rp->flags & Finqok) && (rp->inquiry[2] & 0x07) >= 2)
394
		cmd[1] = 0x10;
395
	cmd[0] = ScmdMselect10;
396
	cmd[7] = nbytes>>8;
397
	cmd[8] = nbytes;
398
	rp->cmd.p = cmd;
399
	rp->cmd.count = sizeof cmd;
400
	rp->data.p = list;
401
	rp->data.count = nbytes;
402
	rp->data.write = 1;
403
	return SRrequest(rp);
404
}
405
 
406
long
407
SRmodesense6(ScsiReq *rp, uchar page, uchar *list, long nbytes)
408
{
409
	uchar cmd[6];
410
 
411
	memset(cmd, 0, sizeof cmd);
412
	cmd[0] = ScmdMsense6;
413
	cmd[2] = page;
414
	cmd[4] = nbytes;
415
	rp->cmd.p = cmd;
416
	rp->cmd.count = sizeof cmd;
417
	rp->data.p = list;
418
	rp->data.count = nbytes;
419
	rp->data.write = 0;
420
	return SRrequest(rp);
421
}
422
 
423
long
424
SRmodesense10(ScsiReq *rp, uchar page, uchar *list, long nbytes)
425
{
426
	uchar cmd[10];
427
 
428
	memset(cmd, 0, sizeof cmd);
429
	cmd[0] = ScmdMsense10;
430
	cmd[2] = page;
431
	cmd[7] = nbytes>>8;
432
	cmd[8] = nbytes;
433
	rp->cmd.p = cmd;
434
	rp->cmd.count = sizeof cmd;
435
	rp->data.p = list;
436
	rp->data.count = nbytes;
437
	rp->data.write = 0;
438
	return SRrequest(rp);
439
}
440
 
441
long
442
SRstart(ScsiReq *rp, uchar code)
443
{
444
	uchar cmd[6];
445
 
446
	memset(cmd, 0, sizeof cmd);
447
	cmd[0] = ScmdStart;
448
	cmd[4] = code;
449
	rp->cmd.p = cmd;
450
	rp->cmd.count = sizeof cmd;
451
	rp->data.p = cmd;
452
	rp->data.count = 0;
453
	rp->data.write = 1;
454
	return SRrequest(rp);
455
}
456
 
457
long
458
SRrcapacity(ScsiReq *rp, uchar *data)
459
{
460
	uchar cmd[10];
461
 
462
	memset(cmd, 0, sizeof cmd);
463
	cmd[0] = ScmdRcapacity;
464
	rp->cmd.p = cmd;
465
	rp->cmd.count = sizeof cmd;
466
	rp->data.p = data;
467
	rp->data.count = 8;
468
	rp->data.write = 0;
469
	return SRrequest(rp);
470
}
471
 
472
static long
473
request(int fd, ScsiPtr *cmd, ScsiPtr *data, int *status)
474
{
475
	long n, r;
476
	char buf[16];
477
 
478
	/* this was an experiment but it seems to be a good idea */
479
	*status = STok;
480
 
481
	/* send SCSI command */
482
	if(write(fd, cmd->p, cmd->count) != cmd->count){
483
		fprint(2, "scsireq: write cmd: %r\n");
484
		*status = Status_SW;
485
		return -1;
486
	}
487
 
488
	/* read or write actual data */
489
	werrstr("");
490
	if(data->write)
491
		n = write(fd, data->p, data->count);
492
	else {
493
		n = read(fd, data->p, data->count);
494
		if (n < 0)
495
			memset(data->p, 0, data->count);
496
		else if (n < data->count)
497
			memset(data->p + n, 0, data->count - n);
498
	}
499
	if (n != data->count && n <= 0) {
500
		if (debug)
501
			fprint(2,
502
	"request: tried to %s %ld bytes of data for cmd 0x%x but got %r\n",
503
				(data->write? "write": "read"),
504
				data->count, cmd->p[0]);
505
	} else if (n != data->count && (data->write || debug))
506
		fprint(2, "request: %s %ld of %ld bytes of actual data\n",
507
			(data->write? "wrote": "read"), n, data->count);
508
 
509
	/* read status */
510
	buf[0] = '\0';
511
	r = read(fd, buf, sizeof buf-1);
512
	if(exabyte && r <= 0 || !exabyte && r < 0){
513
		fprint(2, "scsireq: read status: %r\n");
514
		*status = Status_SW;
515
		return -1;
516
	}
517
	if (r >= 0)
518
		buf[r] = '\0';
519
	*status = atoi(buf);
520
	if(n < 0 && (exabyte || *status != STcheck))
521
		fprint(2, "scsireq: status 0x%2.2uX: data transfer: %r\n",
522
			*status);
523
	return n;
524
}
525
 
526
long
527
SRrequest(ScsiReq *rp)
528
{
529
	long n;
530
	int status;
531
 
532
retry:
533
	if(rp->flags&Fusb)
534
		n = umsrequest(rp->umsc, &rp->cmd, &rp->data, &status);
535
	else
536
		n = request(rp->fd, &rp->cmd, &rp->data, &status);
537
	switch(rp->status = status){
538
 
539
	case STok:
540
		rp->data.count = n;
541
		break;
542
 
543
	case STcheck:
544
		if(rp->cmd.p[0] != ScmdRsense && SRreqsense(rp) != -1)
545
			rp->status = Status_SD;
546
		if (exabyte)
547
			fprint(2, "SRrequest: STcheck, returning -1\n");
548
		return -1;
549
 
550
	case STbusy:
551
		sleep(1000);
552
		goto retry;
553
 
554
	default:
555
		fprint(2, "status 0x%2.2uX\n", status);
556
		return -1;
557
	}
558
	return n;
559
}
560
 
561
int
562
SRclose(ScsiReq *rp)
563
{
564
	if((rp->flags & Fopen) == 0){
565
		if(debug)
566
			fprint(2, "scuzz: closing closed file\n");
567
		rp->status = Status_BADARG;
568
		return -1;
569
	}
570
	close(rp->fd);
571
	rp->flags = 0;
572
	return 0;
573
}
574
 
575
uint
576
mkascq(ScsiReq *r)
577
{
578
	uchar *u;
579
 
580
	u = r->sense;
581
	return u[2]<<16 | u[12]<<8 | u[13];
582
}
583
 
584
static int
585
dirdevopen(ScsiReq *rp)
586
{
587
	ulong blocks;
588
	uchar data[8];
589
 
590
	if(SRstart(rp, 1) == -1)
591
		/*
592
		 * it's okay for removable media to say
593
		 * "check condition: medium not present".
594
		 * 3a is "medium not present".
595
		 */
596
		return rp->inquiry[1] & 0x80 && (mkascq(rp) >> 8) == 0x023a?
597
			0: -1;
598
	memset(data, 0, sizeof data);
599
	if(SRrcapacity(rp, data) == -1)
600
		return -1;
601
	rp->lbsize = GETBELONG(data+4);
602
	blocks =     GETBELONG(data);
603
	if(debug)
604
		fprint(2, "scuzz: dirdevopen: logical block size %lud, "
605
			"# blocks %lud\n", rp->lbsize, blocks);
606
	/* some newer dev's don't support 6-byte commands */
607
	if(blocks > Max24off && !force6bytecmds)
608
		rp->flags |= Frw10;
609
	return 0;
610
}
611
 
612
static int
613
seqdevopen(ScsiReq *rp)
614
{
615
	uchar mode[16], limits[6];
616
 
617
	if(SRrblimits(rp, limits) == -1)
618
		return -1;
619
	if(limits[1] == 0 && limits[2] == limits[4] && limits[3] == limits[5]){
620
		rp->flags |= Fbfixed;
621
		rp->lbsize = limits[4]<<8 | limits[5];
622
		if(debug)
623
			fprint(2, "scuzz: seqdevopen: logical block size %lud\n",
624
				rp->lbsize);
625
		return 0;
626
	}
627
	/*
628
	 * On some older hardware the optional 10-byte
629
	 * modeselect command isn't implemented.
630
	 */
631
	if (force6bytecmds)
632
		rp->flags |= Fmode6;
633
	if(!(rp->flags & Fmode6)){
634
		/* try 10-byte command first */
635
		memset(mode, 0, sizeof mode);
636
		mode[3] = 0x10;		/* device-specific param. */
637
		mode[7] = 8;		/* block descriptor length */
638
		/*
639
		 * exabytes can't handle this, and
640
		 * modeselect(10) is optional.
641
		 */
642
		if(SRmodeselect10(rp, mode, sizeof mode) != -1){
643
			rp->lbsize = 1;
644
			return 0;	/* success */
645
		}
646
		/* can't do 10-byte commands, back off to 6-byte ones */
647
		rp->flags |= Fmode6;
648
	}
649
 
650
	/* 6-byte command */
651
	memset(mode, 0, sizeof mode);
652
	mode[2] = 0x10;		/* device-specific param. */
653
	mode[3] = 8;		/* block descriptor length */
654
	/*
655
	 * bsd sez exabytes need this bit (NBE: no busy enable) in
656
	 * vendor-specific page (0), but so far we haven't needed it.
657
	mode[12] |= 8;
658
	 */
659
	if(SRmodeselect6(rp, mode, 4+8) == -1)
660
		return -1;
661
	rp->lbsize = 1;
662
	return 0;
663
}
664
 
665
static int
666
wormdevopen(ScsiReq *rp)
667
{
668
	long status;
669
	uchar list[MaxDirData];
670
 
671
	if (SRstart(rp, 1) == -1 ||
672
	    (status = SRmodesense10(rp, Allmodepages, list, sizeof list)) == -1)
673
		return -1;
674
	/* nbytes = list[0]<<8 | list[1]; */
675
 
676
	/* # of bytes of block descriptors of 8 bytes each; not even 1? */
677
	if((list[6]<<8 | list[7]) < 8)
678
		rp->lbsize = 2048;
679
	else
680
		/* last 3 bytes of block 0 descriptor */
681
		rp->lbsize = GETBE24(list+13);
682
	if(debug)
683
		fprint(2, "scuzz: wormdevopen: logical block size %lud\n",
684
			rp->lbsize);
685
	return status;
686
}
687
 
688
int
689
SRopenraw(ScsiReq *rp, char *unit)
690
{
691
	char name[128];
692
 
693
	if(rp->flags & Fopen){
694
		if(debug)
695
			fprint(2, "scuzz: opening open file\n");
696
		rp->status = Status_BADARG;
697
		return -1;
698
	}
699
	memset(rp, 0, sizeof *rp);
700
	rp->unit = unit;
701
 
702
	sprint(name, "%s/raw", unit);
703
 
704
	if((rp->fd = open(name, ORDWR)) == -1){
705
		rp->status = STtimeout;
706
		return -1;
707
	}
708
	rp->flags = Fopen;
709
	return 0;
710
}
711
 
712
int
713
SRopen(ScsiReq *rp, char *unit)
714
{
715
	if(SRopenraw(rp, unit) == -1)
716
		return -1;
717
	SRready(rp);
718
	if(SRinquiry(rp) >= 0){
719
		switch(rp->inquiry[0]){
720
 
721
		default:
722
			fprint(2, "unknown device type 0x%.2x\n", rp->inquiry[0]);
723
			rp->status = Status_SW;
724
			break;
725
 
726
		case 0x00:	/* Direct access (disk) */
727
		case 0x05:	/* CD-ROM */
728
		case 0x07:	/* rewriteable MO */
729
			if(dirdevopen(rp) == -1)
730
				break;
731
			return 0;
732
 
733
		case 0x01:	/* Sequential eg: tape */
734
			rp->flags |= Fseqdev;
735
			if(seqdevopen(rp) == -1)
736
				break;
737
			return 0;
738
 
739
		case 0x02:	/* Printer */
740
			rp->flags |= Fprintdev;
741
			return 0;
742
 
743
		case 0x04:	/* Worm */
744
			rp->flags |= Fwormdev;
745
			if(wormdevopen(rp) == -1)
746
				break;
747
			return 0;
748
 
749
		case 0x08:	/* medium-changer */
750
			rp->flags |= Fchanger;
751
			return 0;
752
		}
753
	}
754
	SRclose(rp);
755
	return -1;
756
}