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