xref: /plan9/sys/src/cmd/scuzz/scuzz.c (revision ec59a3ddbfceee0efe34584c2c9981a5e5ff1ec4)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 
5 #include "scsireq.h"
6 
7 #define MIN(a, b)	((a) < (b) ? (a): (b))
8 
9 Biobuf bin, bout;
10 static char rwbuf[MaxIOsize];
11 static int verbose = 1;
12 long maxiosize = MaxIOsize;
13 
14 typedef struct {
15 	char *name;
16 	long (*f)(ScsiReq *, int, char *[]);
17 	int open;
18 	char *help;
19 } ScsiCmd;
20 static ScsiCmd scsicmd[];
21 
22 static long
23 cmdready(ScsiReq *rp, int argc, char *argv[])
24 {
25 	USED(argc, argv);
26 	return SRready(rp);
27 }
28 
29 static long
30 cmdrewind(ScsiReq *rp, int argc, char *argv[])
31 {
32 	USED(argc, argv);
33 	return SRrewind(rp);
34 }
35 
36 static long
37 cmdreqsense(ScsiReq *rp, int argc, char *argv[])
38 {
39 	long nbytes;
40 
41 	USED(argc, argv);
42 	if((nbytes = SRreqsense(rp)) != -1)
43 		makesense(rp);
44 	return nbytes;
45 }
46 
47 static long
48 cmdformat(ScsiReq *rp, int argc, char *argv[])
49 {
50 	USED(argc, argv);
51 	return SRformat(rp);
52 }
53 
54 static long
55 cmdrblimits(ScsiReq *rp, int argc, char *argv[])
56 {
57 	uchar l[6];
58 	long n;
59 
60 	USED(argc, argv);
61 	if((n = SRrblimits(rp, l)) == -1)
62 		return -1;
63 	Bprint(&bout, " %2.2uX %2.2uX %2.2uX %2.2uX %2.2uX %2.2uX\n",
64 		l[0], l[1], l[2], l[3], l[4], l[5]);
65 	return n;
66 }
67 
68 static int
69 mkfile(char *file, int omode, int *pid)
70 {
71 	int fd[2];
72 
73 	if(*file != '|'){
74 		*pid = -1;
75 		if(omode == OWRITE)
76 			return create(file, OWRITE, 0666);
77 		else if(omode == OREAD)
78 			return open(file, OREAD);
79 		return -1;
80 	}
81 
82 	file++;
83 	if(*file == 0 || pipe(fd) == -1)
84 		return -1;
85 	if((*pid = fork()) == -1){
86 		close(fd[0]);
87 		close(fd[1]);
88 		return -1;
89 	}
90 	if(*pid == 0){
91 		switch(omode){
92 
93 		case OREAD:
94 			dup(fd[0], 1);
95 			break;
96 
97 		case OWRITE:
98 			dup(fd[0], 0);
99 			break;
100 		}
101 		close(fd[0]);
102 		close(fd[1]);
103 		execl("/bin/rc", "rc", "-c", file, nil);
104 		exits("exec");
105 	}
106 	close(fd[0]);
107 	return fd[1];
108 }
109 
110 int
111 waitfor(int pid)
112 {
113 	int msg;
114 	Waitmsg *w;
115 
116 	while((w = wait()) != nil){
117 		if(w->pid != pid){
118 			free(w);
119 			continue;
120 		}
121 		msg = (w->msg[0] != '\0');
122 		free(w);
123 		return msg;
124 	}
125 	return -1;
126 }
127 
128 static long
129 cmdread(ScsiReq *rp, int argc, char *argv[])
130 {
131 	long n, iosize;
132 	vlong nbytes, total;
133 	int fd, pid;
134 	char *p;
135 
136 	iosize = maxiosize;
137 	nbytes = ~0ULL >> 1;
138 	switch(argc){
139 
140 	default:
141 		rp->status = Status_BADARG;
142 		return -1;
143 
144 	case 2:
145 		nbytes = strtoll(argv[1], &p, 0);
146 		if(nbytes == 0 && p == argv[1]){
147 			rp->status = Status_BADARG;
148 			return -1;
149 		}
150 		/*FALLTHROUGH*/
151 
152 	case 1:
153 		if((fd = mkfile(argv[0], OWRITE, &pid)) == -1){
154 			rp->status = Status_BADARG;
155 			return -1;
156 		}
157 		break;
158 	}
159 	print("bsize=%lud\n", rp->lbsize);
160 	total = 0;
161 	while(nbytes){
162 		n = iosize;
163 		if(n > nbytes)
164 			n = nbytes;
165 		if((n = SRread(rp, rwbuf, n)) <= 0){
166 			if(total == 0)
167 				total = -1;
168 			break;
169 		}
170 		if(write(fd, rwbuf, n) != n){
171 			if(total == 0)
172 				total = -1;
173 			if(rp->status == STok)
174 				rp->status = Status_SW;
175 			break;
176 		}
177 		nbytes -= n;
178 		total += n;
179 	}
180 	close(fd);
181 	if(pid >= 0 && waitfor(pid)){
182 		rp->status = Status_SW;
183 		return -1;
184 	}
185 	return total;
186 }
187 
188 static long
189 cmdwrite(ScsiReq *rp, int argc, char *argv[])
190 {
191 	long n;
192 	vlong nbytes, total;
193 	int fd, pid;
194 	char *p;
195 
196 	nbytes = ~0ULL >> 1;
197 	switch(argc){
198 
199 	default:
200 		rp->status = Status_BADARG;
201 		return -1;
202 
203 	case 2:
204 		nbytes = strtoll(argv[1], &p, 0);
205 		if(nbytes == 0 && p == argv[1]){
206 			rp->status = Status_BADARG;
207 			return -1;
208 		}
209 		/*FALLTHROUGH*/
210 
211 	case 1:
212 		if((fd = mkfile(argv[0], OREAD, &pid)) == -1){
213 			rp->status = Status_BADARG;
214 			return -1;
215 		}
216 		break;
217 	}
218 	total = 0;
219 	while(nbytes){
220 		n = maxiosize;
221 		if(n > nbytes)
222 			n = nbytes;
223 		if((n = readn(fd, rwbuf, n)) <= 0){
224 			if(total == 0)
225 				total = -1;
226 			break;
227 		}
228 		if(SRwrite(rp, rwbuf, n) != n){
229 			if(total == 0)
230 				total = -1;
231 			if(rp->status == STok)
232 				rp->status = Status_SW;
233 			break;
234 		}
235 		nbytes -= n;
236 		total += n;
237 	}
238 	close(fd);
239 	if(pid >= 0 && waitfor(pid)){
240 		rp->status = Status_SW;
241 		return -1;
242 	}
243 	return total;
244 }
245 
246 static long
247 cmdseek(ScsiReq *rp, int argc, char *argv[])
248 {
249 	char *p;
250 	long offset;
251 	int type;
252 
253 	type = 0;
254 	switch(argc){
255 
256 	default:
257 		rp->status = Status_BADARG;
258 		return -1;
259 
260 	case 2:
261 		if((type = strtol(argv[1], &p, 0)) == 0 && p == argv[1]){
262 			rp->status = Status_BADARG;
263 			return -1;
264 		}
265 		/*FALLTHROUGH*/
266 
267 	case 1:
268 		if((offset = strtol(argv[0], &p, 0)) == 0 && p == argv[0]){
269 			rp->status = Status_BADARG;
270 			return -1;
271 		}
272 		break;
273 	}
274 	return SRseek(rp, offset, type);
275 }
276 
277 static long
278 cmdfilemark(ScsiReq *rp, int argc, char *argv[])
279 {
280 	char *p;
281 	ulong howmany;
282 
283 	howmany = 1;
284 	if(argc && (howmany = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){
285 		rp->status = Status_BADARG;
286 		return -1;
287 	}
288 	return SRfilemark(rp, howmany);
289 }
290 
291 static long
292 cmdspace(ScsiReq *rp, int argc, char *argv[])
293 {
294 	uchar code;
295 	long howmany;
296 	char option, *p;
297 
298 	code = 0x00;
299 	howmany = 1;
300 	while(argc && (*argv)[0] == '-'){
301 		while(option = *++argv[0]){
302 			switch(option){
303 
304 			case '-':
305 				break;
306 
307 			case 'b':
308 				code = 0x00;
309 				break;
310 
311 			case 'f':
312 				code = 0x01;
313 				break;
314 
315 			default:
316 				rp->status = Status_BADARG;
317 				return -1;
318 			}
319 			break;
320 		}
321 		argc--; argv++;
322 		if(option == '-')
323 			break;
324 	}
325 	if(argc && ((howmany = strtol(argv[0], &p, 0)) == 0 && p == argv[0])){
326 		rp->status = Status_BADARG;
327 		return -1;
328 	}
329 	return SRspace(rp, code, howmany);
330 }
331 
332 static long
333 cmdinquiry(ScsiReq *rp, int argc, char *argv[])
334 {
335 	long status;
336 	int i, n;
337 	uchar *p;
338 
339 	USED(argc, argv);
340 	if((status = SRinquiry(rp)) != -1){
341 		n = rp->inquiry[4]+4;
342 		for(i = 0; i < MIN(8, n); i++)
343 			Bprint(&bout, " %2.2uX", rp->inquiry[i]);
344 		p = &rp->inquiry[8];
345 		n = MIN(n, sizeof(rp->inquiry)-8);
346 		while(n && (*p == ' ' || *p == '\t' || *p == '\n')){
347 			n--;
348 			p++;
349 		}
350 		Bprint(&bout, "\t%.*s\n", n, (char*)p);
351 	}
352 	return status;
353 }
354 
355 static long
356 cmdmodeselect6(ScsiReq *rp, int argc, char *argv[])
357 {
358 	uchar list[MaxDirData];
359 	long nbytes, ul;
360 	char *p;
361 
362 	memset(list, 0, sizeof(list));
363 	for(nbytes = 0; argc; argc--, argv++, nbytes++){
364 		if((ul = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){
365 			rp->status = Status_BADARG;
366 			return -1;
367 		}
368 		list[nbytes] = ul;
369 
370 	}
371 	if(!(rp->flags & Finqok) && SRinquiry(rp) == -1)
372 		Bprint(&bout, "warning: couldn't determine whether SCSI-1/SCSI-2 mode");
373 	return SRmodeselect6(rp, list, nbytes);
374 }
375 
376 static long
377 cmdmodeselect10(ScsiReq *rp, int argc, char *argv[])
378 {
379 	uchar list[MaxDirData];
380 	long nbytes, ul;
381 	char *p;
382 
383 	memset(list, 0, sizeof(list));
384 	for(nbytes = 0; argc; argc--, argv++, nbytes++){
385 		if((ul = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){
386 			rp->status = Status_BADARG;
387 			return -1;
388 		}
389 		list[nbytes] = ul;
390 
391 	}
392 	if(!(rp->flags & Finqok) && SRinquiry(rp) == -1)
393 		Bprint(&bout, "warning: couldn't determine whether SCSI-1/SCSI-2 mode");
394 	return SRmodeselect10(rp, list, nbytes);
395 }
396 
397 static long
398 cmdmodesense6(ScsiReq *rp, int argc, char *argv[])
399 {
400 	uchar list[MaxDirData], *lp, page;
401 	long i, n, nbytes, status;
402 	char *p;
403 
404 	nbytes = sizeof(list);
405 	switch(argc){
406 
407 	default:
408 		rp->status = Status_BADARG;
409 		return -1;
410 
411 	case 2:
412 		if((nbytes = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){
413 			rp->status = Status_BADARG;
414 			return -1;
415 		}
416 		/*FALLTHROUGH*/
417 
418 	case 1:
419 		if((page = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){
420 			rp->status = Status_BADARG;
421 			return -1;
422 		}
423 		break;
424 
425 	case 0:
426 		page = 0x3F;
427 		break;
428 	}
429 	if((status = SRmodesense6(rp, page, list, nbytes)) == -1)
430 		return -1;
431 	lp = list;
432 	nbytes = list[0];
433 	Bprint(&bout, " Header\n   ");
434 	for(i = 0; i < 4; i++){				/* header */
435 		Bprint(&bout, " %2.2uX", *lp);
436 		lp++;
437 	}
438 	Bputc(&bout, '\n');
439 
440 	if(list[3]){					/* block descriptors */
441 		for(n = 0; n < list[3]/8; n++){
442 			Bprint(&bout, " Block %ld\n   ", n);
443 			for(i = 0; i < 8; i++)
444 				Bprint(&bout, " %2.2uX", lp[i]);
445 			Bprint(&bout, "    (density %2.2uX", lp[0]);
446 			Bprint(&bout, " blocks %d", (lp[1]<<16)|(lp[2]<<8)|lp[3]);
447 			Bprint(&bout, " length %d)", (lp[5]<<16)|(lp[6]<<8)|lp[7]);
448 			lp += 8;
449 			nbytes -= 8;
450 			Bputc(&bout, '\n');
451 		}
452 	}
453 
454 	while(nbytes > 0){				/* pages */
455 		i = *(lp+1);
456 		nbytes -= i+2;
457 		Bprint(&bout, " Page %2.2uX %d\n   ", *lp & 0x3F, *(lp+1));
458 		lp += 2;
459 		for(n = 0; n < i; n++){
460 			if(n && ((n & 0x0F) == 0))
461 				Bprint(&bout, "\n   ");
462 			Bprint(&bout, " %2.2uX", *lp);
463 			lp++;
464 		}
465 		if(n && (n & 0x0F))
466 			Bputc(&bout, '\n');
467 	}
468 	return status;
469 }
470 
471 static long
472 cmdmodesense10(ScsiReq *rp, int argc, char *argv[])
473 {
474 	uchar *list, *lp, page;
475 	long blen, i, n, nbytes, status;
476 	char *p;
477 
478 	nbytes = MaxDirData;
479 	switch(argc){
480 
481 	default:
482 		rp->status = Status_BADARG;
483 		return -1;
484 
485 	case 2:
486 		if((nbytes = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){
487 			rp->status = Status_BADARG;
488 			return -1;
489 		}
490 		/*FALLTHROUGH*/
491 
492 	case 1:
493 		if((page = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){
494 			rp->status = Status_BADARG;
495 			return -1;
496 		}
497 		break;
498 
499 	case 0:
500 		page = 0x3F;
501 		break;
502 	}
503 	list = malloc(nbytes);
504 	if(list == 0){
505 		rp->status = STnomem;
506 		return -1;
507 	}
508 	if((status = SRmodesense10(rp, page, list, nbytes)) == -1)
509 		return -1;
510 	lp = list;
511 	nbytes = ((list[0]<<8)|list[1]);
512 	Bprint(&bout, " Header\n   ");
513 	for(i = 0; i < 8; i++){				/* header */
514 		Bprint(&bout, " %2.2uX", *lp);
515 		lp++;
516 	}
517 	Bputc(&bout, '\n');
518 
519 	blen = (list[6]<<8)|list[7];
520 	if(blen){					/* block descriptors */
521 		for(n = 0; n < blen/8; n++){
522 			Bprint(&bout, " Block %ld\n   ", n);
523 			for(i = 0; i < 8; i++)
524 				Bprint(&bout, " %2.2uX", lp[i]);
525 			Bprint(&bout, "    (density %2.2uX", lp[0]);
526 			Bprint(&bout, " blocks %d", (lp[1]<<16)|(lp[2]<<8)|lp[3]);
527 			Bprint(&bout, " length %d)", (lp[5]<<16)|(lp[6]<<8)|lp[7]);
528 			lp += 8;
529 			nbytes -= 8;
530 			Bputc(&bout, '\n');
531 		}
532 	}
533 
534 	/*
535 	 * Special for ATA drives, page 0 is the drive info in 16-bit
536 	 * chunks, little-endian, 256 in total. No decoding for now.
537 	 */
538 	if(page == 0){
539 		for(n = 0; n < nbytes; n += 2){
540 			if(n && ((n & 0x1F) == 0))
541 				Bprint(&bout, "\n");
542 			Bprint(&bout, " %4.4uX", (*(lp+1)<<8)|*lp);
543 			lp += 2;
544 		}
545 		Bputc(&bout, '\n');
546 	}
547 	else while(nbytes > 0){				/* pages */
548 		i = *(lp+1);
549 		nbytes -= i+2;
550 		Bprint(&bout, " Page %2.2uX %d\n   ", *lp & 0x3F, *(lp+1));
551 		lp += 2;
552 		for(n = 0; n < i; n++){
553 			if(n && ((n & 0x0F) == 0))
554 				Bprint(&bout, "\n   ");
555 			Bprint(&bout, " %2.2uX", *lp);
556 			lp++;
557 		}
558 		if(n && (n & 0x0F))
559 			Bputc(&bout, '\n');
560 	}
561 	free(list);
562 	return status;
563 }
564 
565 static long
566 start(ScsiReq *rp, int argc, char *argv[], uchar code)
567 {
568 	char *p;
569 
570 	if(argc && (code = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){
571 		rp->status = Status_BADARG;
572 		return -1;
573 	}
574 	return SRstart(rp, code);
575 }
576 
577 static long
578 cmdstart(ScsiReq *rp, int argc, char *argv[])
579 {
580 	return start(rp, argc, argv, 1);
581 }
582 
583 static long
584 cmdstop(ScsiReq *rp, int argc, char *argv[])
585 {
586 	return start(rp, argc, argv, 0);
587 }
588 
589 static long
590 cmdeject(ScsiReq *rp, int argc, char *argv[])
591 {
592 	return start(rp, argc, argv, 2);
593 }
594 
595 static long
596 cmdingest(ScsiReq *rp, int argc, char *argv[])
597 {
598 	return start(rp, argc, argv, 3);
599 }
600 
601 static long
602 cmdcapacity(ScsiReq *rp, int argc, char *argv[])
603 {
604 	uchar d[8];
605 	long n;
606 
607 	USED(argc, argv);
608 	if((n = SRrcapacity(rp, d)) == -1)
609 		return -1;
610 	Bprint(&bout, " %ud %ud\n",
611 		d[0]<<24|d[1]<<16|d[2]<<8|d[3],
612 		d[4]<<24|d[5]<<16|d[6]<<8|d[7]);
613 	return n;
614 }
615 
616 static long
617 cmdblank(ScsiReq *rp, int argc, char *argv[])
618 {
619 	uchar type, track;
620 	char *sp;
621 
622 	type = track = 0;
623 	switch(argc){
624 
625 	default:
626 		rp->status = Status_BADARG;
627 		return -1;
628 
629 	case 2:
630 		if((type = strtoul(argv[1], &sp, 0)) == 0 && sp == argv[1]){
631 			rp->status = Status_BADARG;
632 			return -1;
633 		}
634 		if(type < 0 || type > 6){
635 			rp->status = Status_BADARG;
636 			return -1;
637 		}
638 		/*FALLTHROUGH*/
639 
640 	case 1:
641 		if((track = strtoul(argv[0], &sp, 0)) == 0 && sp == argv[0]){
642 			rp->status = Status_BADARG;
643 			return -1;
644 		}
645 		/*FALLTHROUGH*/
646 
647 	case 0:
648 		break;
649 	}
650 	return SRblank(rp, type, track);
651 }
652 
653 static long
654 cmdsynccache(ScsiReq *rp, int argc, char *argv[])
655 {
656 	USED(argc, argv);
657 	return SRsynccache(rp);
658 }
659 
660 static long
661 cmdrtoc(ScsiReq *rp, int argc, char *argv[])
662 {
663 	uchar d[100*8+4], format, track, *p;
664 	char *sp;
665 	long n, nbytes;
666 	int tdl;
667 
668 	format = track = 0;
669 	switch(argc){
670 
671 	default:
672 		rp->status = Status_BADARG;
673 		return -1;
674 
675 	case 2:
676 		if((format = strtoul(argv[1], &sp, 0)) == 0 && sp == argv[1]){
677 			rp->status = Status_BADARG;
678 			return -1;
679 		}
680 		if(format < 0 || format > 4){
681 			rp->status = Status_BADARG;
682 			return -1;
683 		}
684 		/*FALLTHROUGH*/
685 
686 	case 1:
687 		if((track = strtoul(argv[0], &sp, 0)) == 0 && sp == argv[0]){
688 			rp->status = Status_BADARG;
689 			return -1;
690 		}
691 		/*FALLTHROUGH*/
692 
693 	case 0:
694 		break;
695 	}
696 	if((nbytes = SRTOC(rp, d, sizeof(d), format, track)) == -1){
697 		if(rp->status == STok)
698 			Bprint(&bout, "\t(probably empty)\n");
699 		return -1;
700 	}
701 	tdl = (d[0]<<8)|d[1];
702 	switch(format){
703 
704 	case 0:
705 		Bprint(&bout, "\ttoc/pma data length: 0x%uX\n", tdl);
706 		Bprint(&bout, "\tfirst track number: %d\n", d[2]);
707 		Bprint(&bout, "\tlast track number: %d\n", d[3]);
708 		for(p = &d[4], n = tdl-2; n; n -= 8, p += 8){
709 			Bprint(&bout, "\ttrack number: 0x%2.2uX\n", p[2]);
710 			Bprint(&bout, "\t\tcontrol: 0x%2.2uX\n", p[1] & 0x0F);
711 			Bprint(&bout, "\t\tblock address: 0x%uX\n",
712 				(p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7]);
713 		}
714 		break;
715 
716 	case 1:
717 		Bprint(&bout, "\tsessions data length: 0x%uX\n", tdl);
718 		Bprint(&bout, "\tnumber of finished sessions: %d\n", d[2]);
719 		Bprint(&bout, "\tunfinished session number: %d\n", d[3]);
720 		for(p = &d[4], n = tdl-2; n; n -= 8, p += 8){
721 			Bprint(&bout, "\tsession number: 0x%2.2uX\n", p[0]);
722 			Bprint(&bout, "\t\tfirst track number in session: 0x%2.2uX\n",
723 				p[2]);
724 			Bprint(&bout, "\t\tlogical start address: 0x%uX\n",
725 				(p[5]<<16)|(p[6]<<8)|p[7]);
726 		}
727 		break;
728 
729 	case 2:
730 		Bprint(&bout, "\tfull TOC data length: 0x%uX\n", tdl);
731 		Bprint(&bout, "\tnumber of finished sessions: %d\n", d[2]);
732 		Bprint(&bout, "\tunfinished session number: %d\n", d[3]);
733 		for(p = &d[4], n = tdl-2; n > 0; n -= 11, p += 11){
734 			Bprint(&bout, "\tsession number: 0x%2.2uX\n", p[0]);
735 			Bprint(&bout, "\t\tcontrol: 0x%2.2uX\n", p[1] & 0x0F);
736 			Bprint(&bout, "\t\tADR: 0x%2.2uX\n", (p[1]>>4) & 0x0F);
737 			Bprint(&bout, "\t\tTNO: 0x%2.2uX\n", p[2]);
738 			Bprint(&bout, "\t\tPOINT: 0x%2.2uX\n", p[3]);
739 			Bprint(&bout, "\t\tMin: 0x%2.2uX\n", p[4]);
740 			Bprint(&bout, "\t\tSec: 0x%2.2uX\n", p[5]);
741 			Bprint(&bout, "\t\tFrame: 0x%2.2uX\n", p[6]);
742 			Bprint(&bout, "\t\tZero: 0x%2.2uX\n", p[7]);
743 			Bprint(&bout, "\t\tPMIN: 0x%2.2uX\n", p[8]);
744 			Bprint(&bout, "\t\tPSEC: 0x%2.2uX\n", p[9]);
745 			Bprint(&bout, "\t\tPFRAME: 0x%2.2uX\n", p[10]);
746 		}
747 		break;
748 	case 3:
749 		Bprint(&bout, "\tPMA data length: 0x%uX\n", tdl);
750 		for(p = &d[4], n = tdl-2; n > 0; n -= 11, p += 11){
751 			Bprint(&bout, "\t\tcontrol: 0x%2.2uX\n", p[1] & 0x0F);
752 			Bprint(&bout, "\t\tADR: 0x%2.2uX\n", (p[1]>>4) & 0x0F);
753 			Bprint(&bout, "\t\tTNO: 0x%2.2uX\n", p[2]);
754 			Bprint(&bout, "\t\tPOINT: 0x%2.2uX\n", p[3]);
755 			Bprint(&bout, "\t\tMin: 0x%2.2uX\n", p[4]);
756 			Bprint(&bout, "\t\tSec: 0x%2.2uX\n", p[5]);
757 			Bprint(&bout, "\t\tFrame: 0x%2.2uX\n", p[6]);
758 			Bprint(&bout, "\t\tZero: 0x%2.2uX\n", p[7]);
759 			Bprint(&bout, "\t\tPMIN: 0x%2.2uX\n", p[8]);
760 			Bprint(&bout, "\t\tPSEC: 0x%2.2uX\n", p[9]);
761 			Bprint(&bout, "\t\tPFRAME: 0x%2.2uX\n", p[10]);
762 		}
763 		break;
764 
765 	case 4:
766 		Bprint(&bout, "\tATIP data length: 0x%uX\n", tdl);
767 		break;
768 
769 	}
770 	for(n = 0; n < nbytes; n++){
771 		if(n && ((n & 0x0F) == 0))
772 			Bprint(&bout, "\n");
773 		Bprint(&bout, " %2.2uX", d[n]);
774 	}
775 	if(n && (n & 0x0F))
776 		Bputc(&bout, '\n');
777 	return nbytes;
778 }
779 
780 static long
781 cmdrdiscinfo(ScsiReq *rp, int argc, char*[])
782 {
783 	uchar d[MaxDirData];
784 	int dl;
785 	long n, nbytes;
786 
787 	switch(argc){
788 
789 	default:
790 		rp->status = Status_BADARG;
791 		return -1;
792 
793 	case 0:
794 		break;
795 	}
796 	if((nbytes = SRrdiscinfo(rp, d, sizeof(d))) == -1)
797 		return -1;
798 
799 	dl = (d[0]<<8)|d[1];
800 	Bprint(&bout, "\tdata length: 0x%uX\n", dl);
801 	Bprint(&bout, "\tinfo[2] 0x%2.2uX\n", d[2]);
802 	switch(d[2] & 0x03){
803 
804 	case 0:
805 		Bprint(&bout, "\t\tEmpty\n");
806 		break;
807 
808 	case 1:
809 		Bprint(&bout, "\t\tIncomplete disc (Appendable)\n");
810 		break;
811 
812 	case 2:
813 		Bprint(&bout, "\t\tComplete (CD-ROM or last session is closed and has no next session pointer)\n");
814 		break;
815 
816 	case 3:
817 		Bprint(&bout, "\t\tReserved\n");
818 		break;
819 	}
820 	switch((d[2]>>2) & 0x03){
821 
822 	case 0:
823 		Bprint(&bout, "\t\tEmpty Session\n");
824 		break;
825 
826 	case 1:
827 		Bprint(&bout, "\t\tIncomplete Session\n");
828 		break;
829 
830 	case 2:
831 		Bprint(&bout, "\t\tReserved\n");
832 		break;
833 
834 	case 3:
835 		Bprint(&bout, "\t\tComplete Session (only possible when disc Status is Complete)\n");
836 		break;
837 	}
838 	if(d[2] & 0x10)
839 		Bprint(&bout, "\t\tErasable\n");
840 	Bprint(&bout, "\tNumber of First Track on Disc %ud\n", d[3]);
841 	Bprint(&bout, "\tNumber of Sessions %ud\n", d[4]);
842 	Bprint(&bout, "\tFirst Track Number in Last Session %ud\n", d[5]);
843 	Bprint(&bout, "\tLast Track Number in Last Session %ud\n", d[6]);
844 	Bprint(&bout, "\tinfo[7] 0x%2.2uX\n", d[7]);
845 	if(d[7] & 0x20)
846 		Bprint(&bout, "\t\tUnrestricted Use Disc\n");
847 	if(d[7] & 0x40)
848 		Bprint(&bout, "\t\tDisc Bar Code Valid\n");
849 	if(d[7] & 0x80)
850 		Bprint(&bout, "\t\tDisc ID Valid\n");
851 	Bprint(&bout, "\tinfo[8] 0x%2.2uX\n", d[8]);
852 	switch(d[8]){
853 
854 	case 0x00:
855 		Bprint(&bout, "\t\tCD-DA or CD-ROM Disc\n");
856 		break;
857 
858 	case 0x10:
859 		Bprint(&bout, "\t\tCD-I Disc\n");
860 		break;
861 
862 	case 0x20:
863 		Bprint(&bout, "\t\tCD-ROM XA Disc\n");
864 		break;
865 
866 	case 0xFF:
867 		Bprint(&bout, "\t\tUndefined\n");
868 		break;
869 
870 	default:
871 		Bprint(&bout, "\t\tReserved\n");
872 		break;
873 	}
874 	Bprint(&bout, "\tLast Session lead-in Start Time M/S/F: 0x%2.2uX/0x%2.2uX/0x%2.2uX\n",
875 		d[17], d[18], d[19]);
876 	Bprint(&bout, "\tLast Possible Start Time for Start of lead-out M/S/F: 0x%2.2uX/0x%2.2uX/0x%2.2uX\n",
877 		d[21], d[22], d[23]);
878 
879 	for(n = 0; n < nbytes; n++){
880 		if(n && ((n & 0x0F) == 0))
881 			Bprint(&bout, "\n");
882 		Bprint(&bout, " %2.2uX", d[n]);
883 	}
884 	if(n && (n & 0x0F))
885 		Bputc(&bout, '\n');
886 
887 	return nbytes;
888 }
889 
890 static long
891 cmdrtrackinfo(ScsiReq *rp, int argc, char *argv[])
892 {
893 	uchar d[MaxDirData], track;
894 	char *sp;
895 	long n, nbytes;
896 	int dl;
897 
898 	track = 0;
899 	switch(argc){
900 
901 	default:
902 		rp->status = Status_BADARG;
903 		return -1;
904 
905 	case 1:
906 		if((track = strtoul(argv[0], &sp, 0)) == 0 && sp == argv[0]){
907 			rp->status = Status_BADARG;
908 			return -1;
909 		}
910 		/*FALLTHROUGH*/
911 
912 	case 0:
913 		break;
914 	}
915 	if((nbytes = SRrtrackinfo(rp, d, sizeof(d), track)) == -1)
916 		return -1;
917 
918 	dl = (d[0]<<8)|d[1];
919 	Bprint(&bout, "\tdata length: 0x%uX\n", dl);
920 	Bprint(&bout, "\Track Number %d\n", d[2]);
921 	Bprint(&bout, "\Session Number %d\n", d[3]);
922 	Bprint(&bout, "\tinfo[4] 0x%2.2uX\n", d[5]);
923 	Bprint(&bout, "\t\tTrack Mode 0x%2.2uX: ", d[5] & 0x0F);
924 	switch(d[5] & 0x0F){
925 	case 0x00:
926 	case 0x02:
927 		Bprint(&bout, "2 audio channels without pre-emphasis\n");
928 		break;
929 	case 0x01:
930 	case 0x03:
931 		Bprint(&bout, "2 audio channels with pre-emphasis of 50/15µs\n");
932 		break;
933 	case 0x08:
934 	case 0x0A:
935 		Bprint(&bout, "audio channels without pre-emphasis (reserved in CD-R/RW)\n");
936 		break;
937 	case 0x09:
938 	case 0x0B:
939 		Bprint(&bout, "audio channels with pre-emphasis of 50/15µs (reserved in CD-R/RW)\n");
940 		break;
941 	case 0x04:
942 	case 0x06:
943 		Bprint(&bout, "Data track, recorded uninterrupted\n");
944 		break;
945 	case 0x05:
946 	case 0x07:
947 		Bprint(&bout, "Data track, recorded incremental\n");
948 		break;
949 	default:
950 		Bprint(&bout, "(mode unknown)\n");
951 		break;
952 	}
953 	if(d[5] & 0x10)
954 		Bprint(&bout, "\t\tCopy\n");
955 	if(d[5] & 0x20)
956 		Bprint(&bout, "\t\tDamage\n");
957 	Bprint(&bout, "\tinfo[6] 0x%2.2uX\n", d[6]);
958 	Bprint(&bout, "\t\tData Mode 0x%2.2uX: ", d[6] & 0x0F);
959 	switch(d[6] & 0x0F){
960 	case 0x01:
961 		Bprint(&bout, "Mode 1 (ISO/IEC 10149)\n");
962 		break;
963 	case 0x02:
964 		Bprint(&bout, "Mode 2 (ISO/IEC 10149 or CD-ROM XA)\n");
965 		break;
966 	case 0x0F:
967 		Bprint(&bout, "Data Block Type unknown (no track descriptor block)\n");
968 		break;
969 	default:
970 		Bprint(&bout, "(Reserved)\n");
971 		break;
972 	}
973 	if(d[6] & 0x10)
974 		Bprint(&bout, "\t\tFP\n");
975 	if(d[6] & 0x20)
976 		Bprint(&bout, "\t\tPacket\n");
977 	if(d[6] & 0x40)
978 		Bprint(&bout, "\t\tBlank\n");
979 	if(d[6] & 0x80)
980 		Bprint(&bout, "\t\tRT\n");
981 	Bprint(&bout, "\tTrack Start Address 0x%8.8uX\n",
982 		(d[8]<<24)|(d[9]<<16)|(d[10]<<8)|d[11]);
983 	if(d[7] & 0x01)
984 		Bprint(&bout, "\tNext Writeable Address 0x%8.8uX\n",
985 			(d[12]<<24)|(d[13]<<16)|(d[14]<<8)|d[15]);
986 	Bprint(&bout, "\tFree Blocks 0x%8.8uX\n",
987 		(d[16]<<24)|(d[17]<<16)|(d[18]<<8)|d[19]);
988 	if((d[6] & 0x30) == 0x30)
989 		Bprint(&bout, "\tFixed Packet Size 0x%8.8uX\n",
990 			(d[20]<<24)|(d[21]<<16)|(d[22]<<8)|d[23]);
991 	Bprint(&bout, "\tTrack Size 0x%8.8uX\n",
992 		(d[24]<<24)|(d[25]<<16)|(d[26]<<8)|d[27]);
993 
994 	for(n = 0; n < nbytes; n++){
995 		if(n && ((n & 0x0F) == 0))
996 			Bprint(&bout, "\n");
997 		Bprint(&bout, " %2.2uX", d[n]);
998 	}
999 	if(n && (n & 0x0F))
1000 		Bputc(&bout, '\n');
1001 
1002 	return nbytes;
1003 }
1004 
1005 static long
1006 cmdcdpause(ScsiReq *rp, int argc, char *argv[])
1007 {
1008 	USED(argc, argv);
1009 	return SRcdpause(rp, 0);
1010 }
1011 
1012 static long
1013 cmdcdresume(ScsiReq *rp, int argc, char *argv[])
1014 {
1015 	USED(argc, argv);
1016 	return SRcdpause(rp, 1);
1017 }
1018 
1019 static long
1020 cmdcdstop(ScsiReq *rp, int argc, char *argv[])
1021 {
1022 	USED(argc, argv);
1023 	return SRcdstop(rp);
1024 }
1025 
1026 static long
1027 cmdcdplay(ScsiReq *rp, int argc, char *argv[])
1028 {
1029 	long length, start;
1030 	char *sp;
1031 	int raw;
1032 
1033 	raw = 0;
1034 	start = 0;
1035 	if(argc && strcmp("-r", argv[0]) == 0){
1036 		raw = 1;
1037 		argc--, argv++;
1038 	}
1039 
1040 	length = 0xFFFFFFFF;
1041 	switch(argc){
1042 
1043 	default:
1044 		rp->status = Status_BADARG;
1045 		return -1;
1046 
1047 	case 2:
1048 		if(!raw || ((length = strtol(argv[1], &sp, 0)) == 0 && sp == argv[1])){
1049 			rp->status = Status_BADARG;
1050 			return -1;
1051 		}
1052 		/*FALLTHROUGH*/
1053 
1054 	case 1:
1055 		if((start = strtol(argv[0], &sp, 0)) == 0 && sp == argv[0]){
1056 			rp->status = Status_BADARG;
1057 			return -1;
1058 		}
1059 		/*FALLTHROUGH*/
1060 
1061 	case 0:
1062 		break;
1063 	}
1064 
1065 	return SRcdplay(rp, raw, start, length);
1066 }
1067 
1068 static long
1069 cmdcdload(ScsiReq *rp, int argc, char *argv[])
1070 {
1071 	char *p;
1072 	ulong slot;
1073 
1074 	slot = 0;
1075 	if(argc && (slot = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){
1076 		rp->status = Status_BADARG;
1077 		return -1;
1078 	}
1079 	return SRcdload(rp, 1, slot);
1080 }
1081 
1082 static long
1083 cmdcdunload(ScsiReq *rp, int argc, char *argv[])
1084 {
1085 	char *p;
1086 	ulong slot;
1087 
1088 	slot = 0;
1089 	if(argc && (slot = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){
1090 		rp->status = Status_BADARG;
1091 		return -1;
1092 	}
1093 	return SRcdload(rp, 0, slot);
1094 }
1095 
1096 static long
1097 cmdcdstatus(ScsiReq *rp, int argc, char *argv[])
1098 {
1099 	uchar *list, *lp;
1100 	long nbytes, status;
1101 	int i, slots;
1102 
1103 	USED(argc, argv);
1104 
1105 	nbytes = 4096;
1106 	list = malloc(nbytes);
1107 	if(list == 0){
1108 		rp->status = STnomem;
1109 		return -1;
1110 	}
1111 	status = SRcdstatus(rp, list, nbytes);
1112 	if(status == -1){
1113 		free(list);
1114 		return -1;
1115 	}
1116 
1117 	lp = list;
1118 	Bprint(&bout, " Header\n   ");
1119 	for(i = 0; i < 8; i++){				/* header */
1120 		Bprint(&bout, " %2.2uX", *lp);
1121 		lp++;
1122 	}
1123 	Bputc(&bout, '\n');
1124 
1125 	slots = ((list[6]<<8)|list[7])/4;
1126 	Bprint(&bout, " Slots\n   ");
1127 	while(slots--){
1128 		Bprint(&bout, " %2.2uX %2.2uX %2.2uX %2.2uX\n   ",
1129 			*lp, *(lp+1), *(lp+2), *(lp+3));
1130 		lp += 4;
1131 	}
1132 
1133 	free(list);
1134 	return status;
1135 }
1136 
1137 static long
1138 cmdgetconf(ScsiReq *rp, int argc, char *argv[])
1139 {
1140 	uchar *list;
1141 	long nbytes, status;
1142 
1143 	USED(argc, argv);
1144 
1145 	nbytes = 4096;
1146 	list = malloc(nbytes);
1147 	if(list == 0){
1148 		rp->status = STnomem;
1149 		return -1;
1150 	}
1151 	status = SRgetconf(rp, list, nbytes);
1152 	if(status == -1){
1153 		free(list);
1154 		return -1;
1155 	}
1156 	/* to be done... */
1157 	free(list);
1158 	return status;
1159 }
1160 
1161 static long
1162 cmdfwaddr(ScsiReq *rp, int argc, char *argv[])
1163 {
1164 	uchar d[MaxDirData], npa, track, mode;
1165 	long n;
1166 	char *p;
1167 
1168 	npa = mode = track = 0;
1169 	switch(argc){
1170 
1171 	default:
1172 		rp->status = Status_BADARG;
1173 		return -1;
1174 
1175 	case 3:
1176 		if((npa = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){
1177 			rp->status = Status_BADARG;
1178 			return -1;
1179 		}
1180 		/*FALLTHROUGH*/
1181 
1182 	case 2:
1183 		if((mode = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){
1184 			rp->status = Status_BADARG;
1185 			return -1;
1186 		}
1187 		/*FALLTHROUGH*/
1188 
1189 	case 1:
1190 		if((track = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){
1191 			rp->status = Status_BADARG;
1192 			return -1;
1193 		}
1194 		break;
1195 
1196 	case 0:
1197 		break;
1198 	}
1199 	if((n = SRfwaddr(rp, track, mode, npa, d)) == -1)
1200 		return -1;
1201 	Bprint(&bout, "%ud %ud\n", d[0], (d[1]<<24)|(d[2]<<16)|(d[3]<<8)|d[4]);
1202 	return n;
1203 }
1204 
1205 static long
1206 cmdtreserve(ScsiReq *rp, int argc, char *argv[])
1207 {
1208 	long nbytes;
1209 	char *p;
1210 
1211 	if(argc != 1 || ((nbytes = strtoul(argv[0], &p, 0)) == 0 && p == argv[0])){
1212 		rp->status = Status_BADARG;
1213 		return -1;
1214 	}
1215 	return SRtreserve(rp, nbytes);
1216 }
1217 
1218 static long
1219 cmdtrackinfo(ScsiReq *rp, int argc, char *argv[])
1220 {
1221 	uchar d[MaxDirData], track;
1222 	long n;
1223 	ulong ul;
1224 	char *p;
1225 
1226 	track = 0;
1227 	if(argc && (track = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){
1228 		rp->status = Status_BADARG;
1229 		return -1;
1230 	}
1231 	if((n = SRtinfo(rp, track, d)) == -1)
1232 		return -1;
1233 	Bprint(&bout, "buffer length: 0x%uX\n", d[0]);
1234 	Bprint(&bout, "number of tracks: 0x%uX\n", d[1]);
1235 	ul = (d[2]<<24)|(d[3]<<16)|(d[4]<<8)|d[5];
1236 	Bprint(&bout, "start address: 0x%luX\n", ul);
1237 	ul = (d[6]<<24)|(d[7]<<16)|(d[8]<<8)|d[9];
1238 	Bprint(&bout, "track length: 0x%luX\n", ul);
1239 	Bprint(&bout, "track mode: 0x%uX\n", d[0x0A] & 0x0F);
1240 	Bprint(&bout, "track status: 0x%uX\n", (d[0x0A]>>4) & 0x0F);
1241 	Bprint(&bout, "data mode: 0x%uX\n", d[0x0B] & 0x0F);
1242 	ul = (d[0x0C]<<24)|(d[0x0D]<<16)|(d[0x0E]<<8)|d[0x0F];
1243 	Bprint(&bout, "free blocks: 0x%luX\n", ul);
1244 	return n;
1245 }
1246 
1247 static long
1248 cmdwtrack(ScsiReq *rp, int argc, char *argv[])
1249 {
1250 	uchar mode, track;
1251 	long n, nbytes, total, x;
1252 	int fd, pid;
1253 	char *p;
1254 
1255 	mode = track = 0;
1256 	nbytes = 0;
1257 	switch(argc){
1258 
1259 	default:
1260 		rp->status = Status_BADARG;
1261 		return -1;
1262 
1263 	case 4:
1264 		if((mode = strtoul(argv[3], &p, 0)) == 0 && p == argv[3]){
1265 			rp->status = Status_BADARG;
1266 			return -1;
1267 		}
1268 		/*FALLTHROUGH*/
1269 
1270 	case 3:
1271 		if((track = strtoul(argv[2], &p, 0)) == 0 && p == argv[2]){
1272 			rp->status = Status_BADARG;
1273 			return -1;
1274 		}
1275 		/*FALLTHROUGH*/
1276 
1277 	case 2:
1278 		if((nbytes = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){
1279 			rp->status = Status_BADARG;
1280 			return -1;
1281 		}
1282 		/*FALLTHROUGH*/
1283 
1284 	case 1:
1285 		if((fd = mkfile(argv[0], OREAD, &pid)) == -1){
1286 			rp->status = Status_BADARG;
1287 			return -1;
1288 		}
1289 		break;
1290 	}
1291 	total = 0;
1292 	n = MIN(nbytes, maxiosize);
1293 	if((n = readn(fd, rwbuf, n)) == -1){
1294 		fprint(2, "file read failed %r\n");
1295 		close(fd);
1296 		return -1;
1297 	}
1298 	if((x = SRwtrack(rp, rwbuf, n, track, mode)) != n){
1299 		fprint(2, "wtrack: write incomplete: asked %ld, did %ld\n", n, x);
1300 		if(rp->status == STok)
1301 			rp->status = Status_SW;
1302 		close(fd);
1303 		return -1;
1304 	}
1305 	nbytes -= n;
1306 	total += n;
1307 	while(nbytes){
1308 		n = MIN(nbytes, maxiosize);
1309 		if((n = read(fd, rwbuf, n)) == -1){
1310 			break;
1311 		}
1312 		if((x = SRwrite(rp, rwbuf, n)) != n){
1313 			fprint(2, "write: write incomplete: asked %ld, did %ld\n", n, x);
1314 			if(rp->status == STok)
1315 				rp->status = Status_SW;
1316 			break;
1317 		}
1318 		nbytes -= n;
1319 		total += n;
1320 	}
1321 	close(fd);
1322 	if(pid >= 0 && waitfor(pid)){
1323 		rp->status = Status_SW;
1324 		return -1;
1325 	}
1326 	return total;
1327 }
1328 
1329 static long
1330 cmdload(ScsiReq *rp, int argc, char *argv[])
1331 {
1332 	USED(argc, argv);
1333 	return SRmload(rp, 0);
1334 }
1335 
1336 static long
1337 cmdunload(ScsiReq *rp, int argc, char *argv[])
1338 {
1339 	USED(argc, argv);
1340 	return SRmload(rp, 1);
1341 }
1342 
1343 static long
1344 cmdfixation(ScsiReq *rp, int argc, char *argv[])
1345 {
1346 	uchar type;
1347 	char *p;
1348 
1349 	type = 0;
1350 	if(argc && (type = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){
1351 		rp->status = Status_BADARG;
1352 		return -1;
1353 	}
1354 	return SRfixation(rp, type);
1355 }
1356 
1357 static long
1358 cmdeinit(ScsiReq *rp, int argc, char *argv[])
1359 {
1360 	USED(argc, argv);
1361 	return SReinitialise(rp);
1362 }
1363 
1364 static long
1365 cmdmmove(ScsiReq *rp, int argc, char *argv[])
1366 {
1367 	int transport, source, destination, invert;
1368 	char *p;
1369 
1370 	invert = 0;
1371 
1372 	switch(argc){
1373 
1374 	default:
1375 		rp->status = Status_BADARG;
1376 		return -1;
1377 
1378 	case 4:
1379 		if((invert = strtoul(argv[3], &p, 0)) == 0 && p == argv[3]){
1380 			rp->status = Status_BADARG;
1381 			return -1;
1382 		}
1383 		/*FALLTHROUGH*/
1384 
1385 	case 3:
1386 		if((transport = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){
1387 			rp->status = Status_BADARG;
1388 			return -1;
1389 		}
1390 		if((source = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){
1391 			rp->status = Status_BADARG;
1392 			return -1;
1393 		}
1394 		if((destination = strtoul(argv[2], &p, 0)) == 0 && p == argv[2]){
1395 			rp->status = Status_BADARG;
1396 			return -1;
1397 		}
1398 		break;
1399 	}
1400 
1401 	return SRmmove(rp, transport, source, destination, invert);
1402 }
1403 
1404 static long
1405 cmdestatus(ScsiReq *rp, int argc, char *argv[])
1406 {
1407 	uchar *list, *lp, type;
1408 	long d, i, n, nbytes, status;
1409 	char *p;
1410 
1411 	type = 0;
1412 	nbytes = 4096;
1413 
1414 	switch(argc){
1415 
1416 	default:
1417 		rp->status = Status_BADARG;
1418 		return -1;
1419 
1420 	case 2:
1421 		if((nbytes = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){
1422 			rp->status = Status_BADARG;
1423 			return -1;
1424 		}
1425 		/*FALLTHROUGH*/
1426 
1427 	case 1:
1428 		if((type = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){
1429 			rp->status = Status_BADARG;
1430 			return -1;
1431 		}
1432 		break;
1433 
1434 	case 0:
1435 		break;
1436 	}
1437 
1438 	list = malloc(nbytes);
1439 	if(list == 0){
1440 		rp->status = STnomem;
1441 		return -1;
1442 	}
1443 	status = SRestatus(rp, type, list, nbytes);
1444 	if(status == -1){
1445 		free(list);
1446 		return -1;
1447 	}
1448 
1449 	lp = list;
1450 	nbytes = ((lp[5]<<16)|(lp[6]<<8)|lp[7])-8;
1451 	Bprint(&bout, " Header\n   ");
1452 	for(i = 0; i < 8; i++){				/* header */
1453 		Bprint(&bout, " %2.2uX", *lp);
1454 		lp++;
1455 	}
1456 	Bputc(&bout, '\n');
1457 
1458 	while(nbytes > 0){				/* pages */
1459 		i = ((lp[5]<<16)|(lp[6]<<8)|lp[7]);
1460 		nbytes -= i+8;
1461 		Bprint(&bout, " Type");
1462 		for(n = 0; n < 8; n++)			/* header */
1463 			Bprint(&bout, " %2.2uX", lp[n]);
1464 		Bprint(&bout, "\n   ");
1465 		d = (lp[2]<<8)|lp[3];
1466 		lp += 8;
1467 		for(n = 0; n < i; n++){
1468 			if(n && (n % d) == 0)
1469 				Bprint(&bout, "\n   ");
1470 			Bprint(&bout, " %2.2uX", *lp);
1471 			lp++;
1472 		}
1473 		if(n && (n % d))
1474 			Bputc(&bout, '\n');
1475 	}
1476 
1477 	free(list);
1478 	return status;
1479 }
1480 
1481 static long
1482 cmdhelp(ScsiReq *rp, int argc, char *argv[])
1483 {
1484 	ScsiCmd *cp;
1485 	char *p;
1486 
1487 	USED(rp);
1488 	if(argc)
1489 		p = argv[0];
1490 	else
1491 		p = 0;
1492 	for(cp = scsicmd; cp->name; cp++){
1493 		if(p == 0 || strcmp(p, cp->name) == 0)
1494 			Bprint(&bout, "%s\n", cp->help);
1495 	}
1496 	return 0;
1497 }
1498 
1499 static int atatable[4] = {
1500 	'C', 'D', 'E', 'F',
1501 };
1502 static int scsitable[16] = {
1503 	'0', '1', '2', '3', '4', '5', '6', '7',
1504 	'8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
1505 };
1506 static int unittable[16] = {
1507 	'0', '1', '2', '3', '4', '5', '6', '7',
1508 	'8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
1509 };
1510 
1511 static long
1512 cmdprobe(ScsiReq *rp, int argc, char *argv[])
1513 {
1514 	char buf[32];
1515 	ScsiReq scsireq;
1516 	char *ctlr, *unit;
1517 
1518 	USED(argc, argv);
1519 	rp->status = STok;
1520 	scsireq.flags = 0;
1521 
1522 	for(ctlr="CDEF0123456789abcdef"; *ctlr; ctlr++) {
1523 		/*
1524 		 * I can guess how many units you have.
1525 		 */
1526 		if(*ctlr >= 'C' && *ctlr <= 'F')
1527 			unit = "01";
1528 		else if((*ctlr >= '0' && *ctlr <= '9')
1529 		     || (*ctlr >= 'a' && *ctlr <= 'f'))
1530 			unit = "0123456789abcdef";
1531 		else
1532 			unit = "012345678";
1533 
1534 		for(; *unit; unit++){
1535 			sprint(buf, "/dev/sd%c%c", *ctlr, *unit);
1536 			if(SRopenraw(&scsireq, buf) == -1)
1537 				/*
1538 				return -1;
1539 				 */
1540 				continue;
1541 			SRreqsense(&scsireq);
1542 			switch(scsireq.status){
1543 
1544 			default:
1545 				break;
1546 
1547 			case STok:
1548 			case Status_SD:
1549 				Bprint(&bout, "%s: ", buf);
1550 				cmdinquiry(&scsireq, 0, 0);
1551 				break;
1552 			}
1553 			SRclose(&scsireq);
1554 		}
1555 	}
1556 	return 0;
1557 }
1558 
1559 static long
1560 cmdclose(ScsiReq *rp, int argc, char *argv[])
1561 {
1562 	USED(argc, argv);
1563 	return SRclose(rp);
1564 }
1565 
1566 static long
1567 cmdopen(ScsiReq *rp, int argc, char *argv[])
1568 {
1569 	int raw;
1570 	long status;
1571 
1572 	raw = 0;
1573 	if(argc && strcmp("-r", argv[0]) == 0){
1574 		raw = 1;
1575 		argc--, argv++;
1576 	}
1577 	if(argc != 1){
1578 		rp->status = Status_BADARG;
1579 		return -1;
1580 	}
1581 	if(raw == 0){
1582 		if((status = SRopen(rp, argv[0])) != -1 && verbose)
1583 			Bprint(&bout, "%sblock size: %ld\n",
1584 				rp->flags&Fbfixed? "fixed ": "", rp->lbsize);
1585 	}
1586 	else {
1587 		status = SRopenraw(rp, argv[0]);
1588 		rp->lbsize = 512;
1589 	}
1590 	return status;
1591 }
1592 
1593 static ScsiCmd scsicmd[] = {
1594 	{ "ready",	cmdready,	1,		/*[0x00]*/
1595 	  "ready",
1596 	},
1597 	{ "rewind",	cmdrewind,	1,		/*[0x01]*/
1598 	  "rewind",
1599 	},
1600 	{ "rezero",	cmdrewind,	1,		/*[0x01]*/
1601 	  "rezero",
1602 	},
1603 	{ "reqsense",	cmdreqsense,	1,		/*[0x03]*/
1604 	  "reqsense",
1605 	},
1606 	{ "format",	cmdformat,	0,		/*[0x04]*/
1607 	  "format",
1608 	},
1609 	{ "rblimits",	cmdrblimits,	1,		/*[0x05]*/
1610 	  "rblimits",
1611 	},
1612 	{ "read",	cmdread,	1,		/*[0x08]*/
1613 	  "read [|]file [nbytes]",
1614 	},
1615 	{ "write",	cmdwrite,	1,		/*[0x0A]*/
1616 	  "write [|]file [nbytes]",
1617 	},
1618 	{ "seek",	cmdseek,	1,		/*[0x0B]*/
1619 	  "seek offset [whence]",
1620 	},
1621 	{ "filemark",	cmdfilemark,	1,		/*[0x10]*/
1622 	  "filemark [howmany]",
1623 	},
1624 	{ "space",	cmdspace,	1,		/*[0x11]*/
1625 	  "space [-f] [-b] [[--] howmany]",
1626 	},
1627 	{ "inquiry",	cmdinquiry,	1,		/*[0x12]*/
1628 	  "inquiry",
1629 	},
1630 	{ "modeselect6",cmdmodeselect6,	1,		/*[0x15] */
1631 	  "modeselect6 bytes...",
1632 	},
1633 	{ "modeselect",	cmdmodeselect10, 1,		/*[0x55] */
1634 	  "modeselect bytes...",
1635 	},
1636 	{ "modesense6",	cmdmodesense6,	1,		/*[0x1A]*/
1637 	  "modesense6 [page [nbytes]]",
1638 	},
1639 	{ "modesense",	cmdmodesense10, 1,		/*[0x5A]*/
1640 	  "modesense [page [nbytes]]",
1641 	},
1642 	{ "start",	cmdstart,	1,		/*[0x1B]*/
1643 	  "start [code]",
1644 	},
1645 	{ "stop",	cmdstop,	1,		/*[0x1B]*/
1646 	  "stop",
1647 	},
1648 	{ "eject",	cmdeject,	1,		/*[0x1B]*/
1649 	  "eject",
1650 	},
1651 	{ "ingest",	cmdingest,	1,		/*[0x1B]*/
1652 	  "ingest",
1653 	},
1654 	{ "capacity",	cmdcapacity,	1,		/*[0x25]*/
1655 	  "capacity",
1656 	},
1657 
1658 	{ "blank",	cmdblank,	1,		/*[0xA1]*/
1659 	  "blank [track/LBA [type]]",
1660 	},
1661 //	{ "synccache",	cmdsynccache,	1,		/*[0x35]*/
1662 //	  "synccache",
1663 //	},
1664 	{ "rtoc",	cmdrtoc,	1,		/*[0x43]*/
1665 	  "rtoc [track/session-number [format]]",
1666 	},
1667 	{ "rdiscinfo",	cmdrdiscinfo,	1,		/*[0x51]*/
1668 	  "rdiscinfo",
1669 	},
1670 	{ "rtrackinfo",	cmdrtrackinfo,	1,		/*[0x52]*/
1671 	  "rtrackinfo [track]",
1672 	},
1673 
1674 	{ "cdpause",	cmdcdpause,	1,		/*[0x4B]*/
1675 	  "cdpause",
1676 	},
1677 	{ "cdresume",	cmdcdresume,	1,		/*[0x4B]*/
1678 	  "cdresume",
1679 	},
1680 	{ "cdstop",	cmdcdstop,	1,		/*[0x4E]*/
1681 	  "cdstop",
1682 	},
1683 	{ "cdplay",	cmdcdplay,	1,		/*[0xA5]*/
1684 	  "cdplay [track-number] or [-r [LBA [length]]]",
1685 	},
1686 	{ "cdload",	cmdcdload,	1,		/*[0xA6*/
1687 	  "cdload [slot]",
1688 	},
1689 	{ "cdunload",	cmdcdunload,	1,		/*[0xA6]*/
1690 	  "cdunload [slot]",
1691 	},
1692 	{ "cdstatus",	cmdcdstatus,	1,		/*[0xBD]*/
1693 	  "cdstatus",
1694 	},
1695 //	{ "getconf",	cmdgetconf,	1,		/*[0x46]*/
1696 //	  "getconf",
1697 //	},
1698 
1699 //	{ "fwaddr",	cmdfwaddr,	1,		/*[0xE2]*/
1700 //	  "fwaddr [track [mode [npa]]]",
1701 //	},
1702 //	{ "treserve",	cmdtreserve,	1,		/*[0xE4]*/
1703 //	  "treserve nbytes",
1704 //	},
1705 //	{ "trackinfo",	cmdtrackinfo,	1,		/*[0xE5]*/
1706 //	  "trackinfo [track]",
1707 //	},
1708 //	{ "wtrack",	cmdwtrack,	1,		/*[0xE6]*/
1709 //	  "wtrack [|]file [nbytes [track [mode]]]",
1710 //	},
1711 //	{ "load",	cmdload,	1,		/*[0xE7]*/
1712 //	  "load",
1713 //	},
1714 //	{ "unload",	cmdunload,	1,		/*[0xE7]*/
1715 //	  "unload",
1716 //	},
1717 //	{ "fixation",	cmdfixation,	1,		/*[0xE9]*/
1718 //	  "fixation [toc-type]",
1719 //	},
1720 	{ "einit",	cmdeinit,	1,		/*[0x07]*/
1721 	  "einit",
1722 	},
1723 	{ "estatus",	cmdestatus,	1,		/*[0xB8]*/
1724 	  "estatus",
1725 	},
1726 	{ "mmove",	cmdmmove,	1,		/*[0xA5]*/
1727 	  "mmove transport source destination [invert]",
1728 	},
1729 
1730 	{ "help",	cmdhelp,	0,
1731 	  "help",
1732 	},
1733 	{ "probe",	cmdprobe,	0,
1734 	  "probe",
1735 	},
1736 	{ "close",	cmdclose,	1,
1737 	  "close",
1738 	},
1739 	{ "open",	cmdopen,	0,
1740 	  "open [-r] sddev",
1741 	},
1742 	{ 0, 0 },
1743 };
1744 
1745 #define	SEP(c)	(((c)==' ')||((c)=='\t')||((c)=='\n'))
1746 
1747 static char *
1748 tokenise(char *s, char **start, char **end)
1749 {
1750 	char *to;
1751 	Rune r;
1752 	int n;
1753 
1754 	while(*s && SEP(*s))				/* skip leading white space */
1755 		s++;
1756 	to = *start = s;
1757 	while(*s){
1758 		n = chartorune(&r, s);
1759 		if(SEP(r)){
1760 			if(to != *start)		/* we have data */
1761 				break;
1762 			s += n;				/* null string - keep looking */
1763 			while(*s && SEP(*s))
1764 				s++;
1765 			to = *start = s;
1766 		}
1767 		else if(r == '\''){
1768 			s += n;				/* skip leading quote */
1769 			while(*s){
1770 				n = chartorune(&r, s);
1771 				if(r == '\''){
1772 					if(s[1] != '\'')
1773 						break;
1774 					s++;		/* embedded quote */
1775 				}
1776 				while (n--)
1777 					*to++ = *s++;
1778 			}
1779 			if(!*s)				/* no trailing quote */
1780 				break;
1781 			s++;				/* skip trailing quote */
1782 		}
1783 		else  {
1784 			while(n--)
1785 				*to++ = *s++;
1786 		}
1787 	}
1788 	*end = to;
1789 	return s;
1790 }
1791 
1792 static int
1793 parse(char *s, char *fields[], int nfields)
1794 {
1795 	int c, argc;
1796 	char *start, *end;
1797 
1798 	argc = 0;
1799 	c = *s;
1800 	while(c){
1801 		s = tokenise(s, &start, &end);
1802 		c = *s++;
1803 		if(*start == 0)
1804 			break;
1805 		if(argc >= nfields-1)
1806 			return -1;
1807 		*end = 0;
1808 		fields[argc++] = start;
1809 	}
1810 	fields[argc] = 0;
1811 	return argc;
1812 }
1813 
1814 static void
1815 usage(void)
1816 {
1817 	fprint(2, "%s: usage: %s [-q] [-m maxiosize] [/dev/sdXX]\n", argv0, argv0);
1818 	exits("usage");
1819 }
1820 
1821 static struct {
1822 	int	status;
1823 	char*	description;
1824 } description[] = {
1825 	STnomem,	"buffer allocation failed",
1826 	STtimeout,	"bus timeout",
1827 	STharderr,	"controller error of some kind",
1828 	STok,		"good",
1829 	STcheck,	"check condition",
1830 	STcondmet,	"condition met/good",
1831 	STbusy,		"busy ",
1832 	STintok,	"intermediate/good",
1833 	STintcondmet,	"intermediate/condition met/good",
1834 	STresconf,	"reservation conflict",
1835 	STterminated,	"command terminated",
1836 	STqfull,	"queue full",
1837 
1838 	Status_SD,	"sense-data available",
1839 	Status_SW,	"internal software error",
1840 	Status_BADARG,	"bad argument to request",
1841 
1842 	0, 0,
1843 };
1844 
1845 void
1846 main(int argc, char *argv[])
1847 {
1848 	ScsiReq target;
1849 	char *ap, *av[256];
1850 	int ac, i;
1851 	ScsiCmd *cp;
1852 	long status;
1853 
1854 	ARGBEGIN {
1855 	case 'q':
1856 		verbose = 0;
1857 		break;
1858 	case 'm':
1859 		ap = ARGF();
1860 		if(ap == nil)
1861 			usage();
1862 		maxiosize = atol(ap);
1863 		if(maxiosize < 512 || maxiosize > MaxIOsize)
1864 			usage();
1865 		break;
1866 	default:
1867 		usage();
1868 	} ARGEND
1869 
1870 	if(Binit(&bin, 0, OREAD) == Beof || Binit(&bout, 1, OWRITE) == Beof){
1871 		fprint(2, "%s: can't init bio: %r\n", argv0);
1872 		exits("Binit");
1873 	}
1874 
1875 	memset(&target, 0, sizeof(target));
1876 	if(argc && cmdopen(&target, argc, argv) == -1) {
1877 		fprint(2, "open failed\n");
1878 		usage();
1879 	}
1880 	Bflush(&bout);
1881 
1882 	while(ap = Brdline(&bin, '\n')){
1883 		ap[Blinelen(&bin)-1] = 0;
1884 		switch(ac = parse(ap, av, nelem(av))){
1885 
1886 		default:
1887 			for(cp = scsicmd; cp->name; cp++){
1888 				if(strcmp(cp->name, av[0]) == 0)
1889 					break;
1890 			}
1891 			if(cp->name == 0){
1892 				Bprint(&bout, "eh?\n");
1893 				break;
1894 			}
1895 			if((target.flags & Fopen) == 0 && cp->open){
1896 				Bprint(&bout, "no current target\n");
1897 				break;
1898 			}
1899 			if((status = (*cp->f)(&target, ac-1, &av[1])) != -1){
1900 				if(verbose)
1901 					Bprint(&bout, "ok %ld\n", status);
1902 				break;
1903 			}
1904 			for(i = 0; description[i].description; i++){
1905 				if(target.status != description[i].status)
1906 					continue;
1907 				if(target.status == Status_SD)
1908 					makesense(&target);
1909 				else
1910 					Bprint(&bout, "%s\n", description[i].description);
1911 				break;
1912 			}
1913 			break;
1914 
1915 		case -1:
1916 			Bprint(&bout, "eh?\n");
1917 			break;
1918 
1919 		case 0:
1920 			break;
1921 		}
1922 		Bflush(&bout);
1923 	}
1924 	exits(0);
1925 }
1926