xref: /plan9-contrib/sys/src/cmd/scuzz/scuzz.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
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 int bus;
10 Biobuf bin, bout;
11 static char rwbuf[MaxIOsize];
12 static int verbose = 1;
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 		return -1;
88 	}
89 	if(*pid == 0){
90 		switch(omode){
91 
92 		case OREAD:
93 			dup(fd[0], 1);
94 			break;
95 
96 		case OWRITE:
97 			dup(fd[0], 0);
98 			break;
99 		}
100 		close(fd[0]);
101 		close(fd[1]);
102 		execl("/bin/rc", "rc", "-c", file, 0);
103 		exits("exec");
104 	}
105 	close(fd[0]);
106 	return fd[1];
107 }
108 
109 int
110 waitfor(int pid)
111 {
112 	int rpid;
113 	Waitmsg w;
114 
115 	while((rpid = wait(&w)) != pid && rpid != -1)
116 		;
117 	return w.msg[0];
118 }
119 
120 static long
121 cmdread(ScsiReq *rp, int argc, char *argv[])
122 {
123 	long n, nbytes, total, iosize;
124 	int fd, pid;
125 	char *p;
126 
127 	iosize = MaxIOsize;
128 	nbytes = 0x7FFFFFFF & ~iosize;
129 	switch(argc){
130 
131 	default:
132 		rp->status = Status_BADARG;
133 		return -1;
134 
135 	case 2:
136 		if((nbytes = strtol(argv[1], &p, 0)) == 0 && p == argv[1]){
137 			rp->status = Status_BADARG;
138 			return -1;
139 		}
140 		/*FALLTHROUGH*/
141 
142 	case 1:
143 		if((fd = mkfile(argv[0], OWRITE, &pid)) == -1){
144 			rp->status = Status_BADARG;
145 			return -1;
146 		}
147 		break;
148 	}
149 	print("bsize=%d\n", rp->lbsize);
150 	total = 0;
151 	while(nbytes){
152 		n = MIN(nbytes, iosize);
153 		if((n = SRread(rp, rwbuf, n)) == -1){
154 			if(total == 0)
155 				total = -1;
156 			break;
157 		}
158 		if(write(fd, rwbuf, n) != n){
159 			if(total == 0)
160 				total = -1;
161 			if(rp->status == Status_OK)
162 				rp->status = Status_SW;
163 			break;
164 		}
165 		nbytes -= n;
166 		total += n;
167 	}
168 	close(fd);
169 	if(pid >= 0 && waitfor(pid)){
170 		rp->status = Status_SW;
171 		return -1;
172 	}
173 	return total;
174 }
175 
176 static long
177 cmdwrite(ScsiReq *rp, int argc, char *argv[])
178 {
179 	long n, nbytes, total;
180 	int fd, pid;
181 	char *p;
182 
183 	nbytes = 0x7FFFFFFF & ~MaxIOsize;
184 	switch(argc){
185 
186 	default:
187 		rp->status = Status_BADARG;
188 		return -1;
189 
190 	case 2:
191 		if((nbytes = strtol(argv[1], &p, 0)) == 0 && p == argv[1]){
192 			rp->status = Status_BADARG;
193 			return -1;
194 		}
195 		/*FALLTHROUGH*/
196 
197 	case 1:
198 		if((fd = mkfile(argv[0], OREAD, &pid)) == -1){
199 			rp->status = Status_BADARG;
200 			return -1;
201 		}
202 		break;
203 	}
204 	total = 0;
205 	while(nbytes){
206 		n = MIN(nbytes, MaxIOsize);
207 		if((n = read(fd, rwbuf, n)) == -1){
208 			if(total == 0)
209 				total = -1;
210 			break;
211 		}
212 		if(SRwrite(rp, rwbuf, n) != n){
213 			if(total == 0)
214 				total = -1;
215 			if(rp->status == Status_OK)
216 				rp->status = Status_SW;
217 			break;
218 		}
219 		nbytes -= n;
220 		total += n;
221 	}
222 	close(fd);
223 	if(pid >= 0 && waitfor(pid)){
224 		rp->status = Status_SW;
225 		return -1;
226 	}
227 	return total;
228 }
229 
230 static long
231 cmdseek(ScsiReq *rp, int argc, char *argv[])
232 {
233 	char *p;
234 	long offset;
235 	int type;
236 
237 	type = 0;
238 	switch(argc){
239 
240 	default:
241 		rp->status = Status_BADARG;
242 		return -1;
243 
244 	case 2:
245 		if((type = strtol(argv[1], &p, 0)) == 0 && p == argv[1]){
246 			rp->status = Status_BADARG;
247 			return -1;
248 		}
249 		/*FALLTHROUGH*/
250 
251 	case 1:
252 		if((offset = strtol(argv[0], &p, 0)) == 0 && p == argv[0]){
253 			rp->status = Status_BADARG;
254 			return -1;
255 		}
256 		break;
257 	}
258 	return SRseek(rp, offset, type);
259 }
260 
261 static long
262 cmdfilemark(ScsiReq *rp, int argc, char *argv[])
263 {
264 	char *p;
265 	ulong howmany;
266 
267 	howmany = 1;
268 	if(argc && (howmany = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){
269 		rp->status = Status_BADARG;
270 		return -1;
271 	}
272 	return SRfilemark(rp, howmany);
273 }
274 
275 static long
276 cmdspace(ScsiReq *rp, int argc, char *argv[])
277 {
278 	uchar code;
279 	long howmany;
280 	char option, *p;
281 
282 	code = 0x00;
283 	howmany = 1;
284 	while(argc && (*argv)[0] == '-'){
285 		while(option = *++argv[0]){
286 			switch(option){
287 
288 			case '-':
289 				break;
290 
291 			case 'b':
292 				code = 0x00;
293 				break;
294 
295 			case 'f':
296 				code = 0x01;
297 				break;
298 
299 			default:
300 				rp->status = Status_BADARG;
301 				return -1;
302 			}
303 			break;
304 		}
305 		argc--; argv++;
306 		if(option == '-')
307 			break;
308 	}
309 	if(argc || ((howmany = strtol(argv[0], &p, 0)) == 0 && p == argv[0])){
310 		USED(howmany);
311 		rp->status = Status_BADARG;
312 		return -1;
313 	}
314 	return SRspace(rp, code, howmany);
315 }
316 
317 static long
318 cmdinquiry(ScsiReq *rp, int argc, char *argv[])
319 {
320 	long status;
321 	int i, n;
322 	uchar *p;
323 
324 	USED(argc, argv);
325 	if((status = SRinquiry(rp)) != -1){
326 		n = rp->inquiry[4]+4;
327 		for(i = 0; i < MIN(8, n); i++)
328 			Bprint(&bout, " %2.2ux", rp->inquiry[i]);
329 		p = &rp->inquiry[8];
330 		n = MIN(n, sizeof(rp->inquiry)-8);
331 		while(n && (*p == ' ' || *p == '\t' || *p == '\n')){
332 			n--;
333 			p++;
334 		}
335 		Bprint(&bout, "\t%.*s\n", n, p);
336 	}
337 	return status;
338 }
339 
340 static long
341 cmdmodeselect(ScsiReq *rp, int argc, char *argv[])
342 {
343 	uchar list[MaxDirData];
344 	long nbytes, ul;
345 	char *p;
346 
347 	memset(list, 0, sizeof(list));
348 	for(nbytes = 0; argc; argc--, argv++, nbytes++){
349 		if((ul = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){
350 			rp->status = Status_BADARG;
351 			return -1;
352 		}
353 		list[nbytes] = ul;
354 	}
355 	return SRmodeselect(rp, list, nbytes);
356 }
357 
358 static long
359 cmdmodesense(ScsiReq *rp, int argc, char *argv[])
360 {
361 	uchar list[MaxDirData], *lp, page;
362 	long i, nbytes, status;
363 	char *p;
364 
365 	nbytes = sizeof(list);
366 	switch(argc){
367 
368 	default:
369 		rp->status = Status_BADARG;
370 		return -1;
371 
372 	case 2:
373 		if((nbytes = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){
374 			rp->status = Status_BADARG;
375 			return -1;
376 		}
377 		/*FALLTHROUGH*/
378 
379 	case 1:
380 		if((page = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){
381 			rp->status = Status_BADARG;
382 			return -1;
383 		}
384 		break;
385 
386 	case 0:
387 		page = 0x3F;
388 		break;
389 	}
390 	if((status = SRmodesense(rp, page, list, nbytes)) == -1)
391 		return -1;
392 	lp = list;
393 	nbytes = list[0]-1;
394 	for(i = 0; i < 4; i++){				/* header */
395 		Bprint(&bout, " %2.2ux", *lp);
396 		lp++;
397 	}
398 	nbytes -= 4;
399 	Bputc(&bout, '\n');
400 	for(i = 0; i < 8; i++){				/* block descriptor */
401 		Bprint(&bout, " %2.2ux", *lp);
402 		lp++;
403 	}
404 	nbytes -= 8;
405 	Bputc(&bout, '\n');
406 	while(nbytes > 0){
407 		i = *(lp+1);
408 		nbytes -= i+2;
409 		Bprint(&bout, " %2.2ux %2.2ux", *lp, *(lp+1));
410 		lp += 2;
411 		while(i--){
412 			Bprint(&bout, " %2.2ux", *lp);
413 			lp++;
414 		}
415 		Bputc(&bout, '\n');
416 	}
417 	return status;
418 }
419 
420 static long
421 start(ScsiReq *rp, int argc, char *argv[], uchar code)
422 {
423 	char *p;
424 
425 	if(argc && (code = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){
426 		rp->status = Status_BADARG;
427 		return -1;
428 	}
429 	return SRstart(rp, code);
430 }
431 
432 static long
433 cmdstart(ScsiReq *rp, int argc, char *argv[])
434 {
435 	return start(rp, argc, argv, 1);
436 }
437 
438 static long
439 cmdstop(ScsiReq *rp, int argc, char *argv[])
440 {
441 	return start(rp, argc, argv, 0);
442 }
443 
444 static long
445 cmdeject(ScsiReq *rp, int argc, char *argv[])
446 {
447 	return start(rp, argc, argv, 2);
448 }
449 
450 static long
451 cmdcapacity(ScsiReq *rp, int argc, char *argv[])
452 {
453 	uchar d[6];
454 	long n;
455 
456 	USED(argc, argv);
457 	if((n = SRrcapacity(rp, d)) == -1)
458 		return -1;
459 	Bprint(&bout, " %ld %ld\n", d[0]<<24|d[1]<<16|d[2]<<8|d[3],
460 		 d[4]<<24|d[5]<<16|d[6]<<8|d[7]);
461 	return n;
462 }
463 
464 static long
465 cmdflushcache(ScsiReq *rp, int argc, char *argv[])
466 {
467 	USED(argc, argv);
468 	return SRflushcache(rp);
469 }
470 
471 static long
472 cmdrdiscinfo(ScsiReq *rp, int argc, char *argv[])
473 {
474 	uchar d[MaxDirData], ses, track, *p;
475 	char *sp;
476 	long n, nbytes;
477 
478 	ses = track = 0;
479 	switch(argc){
480 
481 	default:
482 		rp->status = Status_BADARG;
483 		return -1;
484 
485 	case 2:
486 		if((ses = strtoul(argv[1], &sp, 0)) == 0 && sp == argv[1]){
487 			rp->status = Status_BADARG;
488 			return -1;
489 		}
490 		/*FALLTHROUGH*/
491 
492 	case 1:
493 		if((track = strtoul(argv[0], &sp, 0)) == 0 && sp == argv[0]){
494 			rp->status = Status_BADARG;
495 			return -1;
496 		}
497 		/*FALLTHROUGH*/
498 
499 	case 0:
500 		break;
501 	}
502 	if((nbytes = SRrdiscinfo(rp, d, ses, track)) == -1)
503 		return -1;
504 	if(ses == 0){
505 		Bprint(&bout, "\ttoc/pma data length: 0x%ux\n", (d[0]<<8)|d[1]);
506 		Bprint(&bout, "\tfirst track number: %d\n", d[2]);
507 		Bprint(&bout, "\tlast track number: %d\n", d[3]);
508 		for(p = &d[4], n = nbytes-4; n; n -= 8, p += 8){
509 			Bprint(&bout, "\ttrack number: 0x%2.2ux\n", p[2]);
510 			Bprint(&bout, "\t\tcontrol: 0x%2.2ux\n", p[1] & 0x0F);
511 			Bprint(&bout, "\t\tblock address: 0x%lux\n",
512 				(p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7]);
513 		}
514 	}
515 	else{
516 		Bprint(&bout, "\tsessions data length: 0x%ux\n", (d[0]<<8)|d[1]);
517 		Bprint(&bout, "\tnumber of finished sessions: %d\n", d[2]);
518 		Bprint(&bout, "\tunfinished session number: %d\n", d[3]);
519 		for(p = &d[4], n = nbytes-4; n; n -= 8, p += 8){
520 			Bprint(&bout, "\tsession number: 0x%2.2ux\n", p[0]);
521 			Bprint(&bout, "\t\tfirst track number in session: 0x%2.2ux\n",
522 				p[2]);
523 			Bprint(&bout, "\t\tlogical start address: 0x%lux\n",
524 				(p[5]<<16)|(p[6]<<8)|p[7]);
525 		}
526 	}
527 	for(n = 0; n < nbytes; n++)
528 		Bprint(&bout, " %2.2ux", d[n]);
529 	Bprint(&bout, "\n");
530 	return nbytes;
531 }
532 
533 static long
534 cmdfwaddr(ScsiReq *rp, int argc, char *argv[])
535 {
536 	uchar d[MaxDirData], npa, track, mode;
537 	long n;
538 	char *p;
539 
540 	npa = mode = track = 0;
541 	switch(argc){
542 
543 	default:
544 		rp->status = Status_BADARG;
545 		return -1;
546 
547 	case 3:
548 		if((npa = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){
549 			rp->status = Status_BADARG;
550 			return -1;
551 		}
552 		/*FALLTHROUGH*/
553 
554 	case 2:
555 		if((mode = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){
556 			rp->status = Status_BADARG;
557 			return -1;
558 		}
559 		/*FALLTHROUGH*/
560 
561 	case 1:
562 		if((track = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){
563 			rp->status = Status_BADARG;
564 			return -1;
565 		}
566 		break;
567 
568 	case 0:
569 		break;
570 	}
571 	if((n = SRfwaddr(rp, track, mode, npa, d)) == -1)
572 		return -1;
573 	Bprint(&bout, "%ud %ld\n", d[0], (d[1]<<24)|(d[2]<<16)|(d[3]<<8)|d[4]);
574 	return n;
575 }
576 
577 static long
578 cmdtreserve(ScsiReq *rp, int argc, char *argv[])
579 {
580 	long nbytes;
581 	char *p;
582 
583 	if(argc != 1 || ((nbytes = strtoul(argv[0], &p, 0)) == 0 && p == argv[0])){
584 		rp->status = Status_BADARG;
585 		return -1;
586 	}
587 	return SRtreserve(rp, nbytes);
588 }
589 
590 static long
591 cmdtrackinfo(ScsiReq *rp, int argc, char *argv[])
592 {
593 	uchar d[MaxDirData], track;
594 	long n;
595 	ulong ul;
596 	char *p;
597 
598 	track = 0;
599 	if(argc && (track = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){
600 		rp->status = Status_BADARG;
601 		return -1;
602 	}
603 	if((n = SRtinfo(rp, track, d)) == -1)
604 		return -1;
605 	Bprint(&bout, "buffer length: 0x%ux\n", d[0]);
606 	Bprint(&bout, "number of tracks: 0x%ux\n", d[1]);
607 	ul = (d[2]<<24)|(d[3]<<16)|(d[4]<<8)|d[5];
608 	Bprint(&bout, "start address: 0x%lux\n", ul);
609 	ul = (d[6]<<24)|(d[7]<<16)|(d[8]<<8)|d[9];
610 	Bprint(&bout, "track length: 0x%lux\n", ul);
611 	Bprint(&bout, "track mode: 0x%ux\n", d[0x0A] & 0x0F);
612 	Bprint(&bout, "track status: 0x%ux\n", (d[0x0A]>>4) & 0x0F);
613 	Bprint(&bout, "data mode: 0x%ux\n", d[0x0B] & 0x0F);
614 	ul = (d[0x0C]<<24)|(d[0x0D]<<16)|(d[0x0E]<<8)|d[0x0F];
615 	Bprint(&bout, "free blocks: 0x%lux\n", ul);
616 	return n;
617 }
618 
619 static long
620 cmdwtrack(ScsiReq *rp, int argc, char *argv[])
621 {
622 	uchar mode, track;
623 	long n, nbytes, total, x;
624 	int fd, pid;
625 	char *p;
626 
627 	mode = track = 0;
628 	nbytes = 0;
629 	switch(argc){
630 
631 	default:
632 		rp->status = Status_BADARG;
633 		return -1;
634 
635 	case 4:
636 		if((mode = strtoul(argv[3], &p, 0)) == 0 && p == argv[3]){
637 			rp->status = Status_BADARG;
638 			return -1;
639 		}
640 		/*FALLTHROUGH*/
641 
642 	case 3:
643 		if((track = strtoul(argv[2], &p, 0)) == 0 && p == argv[2]){
644 			rp->status = Status_BADARG;
645 			return -1;
646 		}
647 		/*FALLTHROUGH*/
648 
649 	case 2:
650 		if((nbytes = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){
651 			rp->status = Status_BADARG;
652 			return -1;
653 		}
654 		/*FALLTHROUGH*/
655 
656 	case 1:
657 		if((fd = mkfile(argv[0], OREAD, &pid)) == -1){
658 			rp->status = Status_BADARG;
659 			return -1;
660 		}
661 		break;
662 	}
663 	total = 0;
664 	n = MIN(nbytes, MaxIOsize);
665 	if((n = read(fd, rwbuf, n)) == -1){
666 		fprint(2, "file read failed %r\n");
667 		close(fd);
668 		return -1;
669 	}
670 	if((x = SRwtrack(rp, rwbuf, n, track, mode)) != n){
671 		fprint(2, "wtrack: write incomplete: asked %d, did %d\n", n, x);
672 		if(rp->status == Status_OK)
673 			rp->status = Status_SW;
674 		close(fd);
675 		return -1;
676 	}
677 	nbytes -= n;
678 	total += n;
679 	while(nbytes){
680 		n = MIN(nbytes, MaxIOsize);
681 		if((n = read(fd, rwbuf, n)) == -1){
682 			break;
683 		}
684 		if((x = SRwrite(rp, rwbuf, n)) != n){
685 			fprint(2, "write: write incomplete: asked %d, did %d\n", n, x);
686 			if(rp->status == Status_OK)
687 				rp->status = Status_SW;
688 			break;
689 		}
690 		nbytes -= n;
691 		total += n;
692 	}
693 	close(fd);
694 	if(pid >= 0 && waitfor(pid)){
695 		rp->status = Status_SW;
696 		return -1;
697 	}
698 	return total;
699 }
700 
701 static long
702 cmdload(ScsiReq *rp, int argc, char *argv[])
703 {
704 	USED(argc, argv);
705 	return SRmload(rp, 0);
706 }
707 
708 static long
709 cmdunload(ScsiReq *rp, int argc, char *argv[])
710 {
711 	USED(argc, argv);
712 	return SRmload(rp, 1);
713 }
714 
715 static long
716 cmdfixation(ScsiReq *rp, int argc, char *argv[])
717 {
718 	uchar type;
719 	char *p;
720 
721 	type = 0;
722 	if(argc && (type = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){
723 		rp->status = Status_BADARG;
724 		return -1;
725 	}
726 	return SRfixation(rp, type);
727 }
728 
729 static long
730 cmdhelp(ScsiReq *rp, int argc, char *argv[])
731 {
732 	ScsiCmd *cp;
733 	char *p;
734 
735 	USED(rp);
736 	if(argc)
737 		p = argv[0];
738 	else
739 		p = 0;
740 	for(cp = scsicmd; cp->name; cp++){
741 		if(p == 0 || strcmp(p, cp->name) == 0)
742 			Bprint(&bout, "%s\n", cp->help);
743 	}
744 	return 0;
745 }
746 
747 static long
748 cmdprobe(ScsiReq *rp, int argc, char *argv[])
749 {
750 	ScsiReq scsireq;
751 	uchar id;
752 
753 	USED(argc, argv);
754 	rp->status = Status_OK;
755 	scsireq.flags = 0;
756 	for(id = 0; id < CtlrID; id++){
757 		if(SRopenraw(&scsireq, id) == -1)
758 			return -1;
759 		SRreqsense(&scsireq);
760 		switch(scsireq.status){
761 
762 		default:
763 			break;
764 
765 		case Status_OK:
766 		case Status_SD:
767 			Bprint(&bout, "%d: ", id);
768 			cmdinquiry(&scsireq, 0, 0);
769 			break;
770 		}
771 		SRclose(&scsireq);
772 	}
773 	return 0;
774 }
775 
776 static long
777 cmdclose(ScsiReq *rp, int argc, char *argv[])
778 {
779 	USED(argc, argv);
780 	return SRclose(rp);
781 }
782 
783 static long
784 cmdopen(ScsiReq *rp, int argc, char *argv[])
785 {
786 	char *p;
787 	int id, raw;
788 	long status;
789 
790 	raw = 0;
791 	if(argc && strcmp("-r", argv[0]) == 0){
792 		raw = 1;
793 		argc--, argv++;
794 	}
795 	if(argc != 1 || ((id = strtoul(argv[0], &p, 0)) == 0 && p == argv[0])){
796 		rp->status = Status_BADARG;
797 		return -1;
798 	}
799 	if(raw == 0){
800 		if((status = SRopen(rp, id)) != -1 && verbose)
801 			Bprint(&bout, "block size: %ld\n", rp->lbsize);
802 	}
803 	else {
804 		status = SRopenraw(rp, id);
805 		rp->lbsize = 512;
806 	}
807 	return status;
808 }
809 
810 static ScsiCmd scsicmd[] = {
811 	{ "ready",	cmdready,	1,		/*[0x00]*/
812 	  "ready",
813 	},
814 	{ "rewind",	cmdrewind,	1,		/*[0x01]*/
815 	  "rewind",
816 	},
817 	{ "rezero",	cmdrewind,	1,		/*[0x01]*/
818 	  "rezero",
819 	},
820 	{ "reqsense",	cmdreqsense,	1,		/*[0x03]*/
821 	  "reqsense",
822 	},
823 	{ "format",	cmdformat,	0,		/*[0x04]*/
824 	  "format",
825 	},
826 	{ "rblimits",	cmdrblimits,	1,		/*[0x05]*/
827 	  "rblimits",
828 	},
829 	{ "read",	cmdread,	1,		/*[0x08]*/
830 	  "read [|]file [nbytes]",
831 	},
832 	{ "write",	cmdwrite,	1,		/*[0x0A]*/
833 	  "write [|]file [nbytes]",
834 	},
835 	{ "seek",	cmdseek,	1,		/*[0x0B]*/
836 	  "seek offset [whence]",
837 	},
838 	{ "filemark",	cmdfilemark,	1,		/*[0x10]*/
839 	  "filemark [howmany]",
840 	},
841 	{ "space",	cmdspace,	1,		/*[0x11]*/
842 	  "space [-f] [-b] [[--] howmany]",
843 	},
844 	{ "inquiry",	cmdinquiry,	1,		/*[0x12]*/
845 	  "inquiry",
846 	},
847 	{ "modeselect",	cmdmodeselect,	1,		/*[0x15] */
848 	  "modeselect bytes...",
849 	},
850 	{ "modesense",	cmdmodesense,	1,		/*[0x1A]*/
851 	  "modesense [page [nbytes]]",
852 	},
853 	{ "start",	cmdstart,	1,		/*[0x1B]*/
854 	  "start [code]",
855 	},
856 	{ "stop",	cmdstop,	1,		/*[0x1B]*/
857 	  "stop [code]",
858 	},
859 	{ "eject",	cmdeject,	1,		/*[0x1B]*/
860 	  "eject [code]",
861 	},
862 	{ "capacity",	cmdcapacity,	1,		/*[0x25]*/
863 	  "capacity",
864 	},
865 
866 	{ "flushcache",	cmdflushcache,	1,		/*[0x35]*/
867 	  "flushcache",
868 	},
869 	{ "rdiscinfo",	cmdrdiscinfo,	1,		/*[0x43]*/
870 	  "rdiscinfo [track/session-number [ses]]",
871 	},
872 	{ "fwaddr",	cmdfwaddr,	1,		/*[0xE2]*/
873 	  "fwaddr [track [mode [npa]]]",
874 	},
875 	{ "treserve",	cmdtreserve,	1,		/*[0xE4]*/
876 	  "treserve nbytes",
877 	},
878 	{ "trackinfo",	cmdtrackinfo,	1,		/*[0xE5]*/
879 	  "trackinfo [track]",
880 	},
881 	{ "wtrack",	cmdwtrack,	1,		/*[0xE6]*/
882 	  "wtrack [|]file [nbytes [track [mode]]]",
883 	},
884 	{ "load",	cmdload,	1,		/*[0xE7]*/
885 	  "load",
886 	},
887 	{ "unload",	cmdunload,	1,		/*[0xE7]*/
888 	  "unload",
889 	},
890 	{ "fixation",	cmdfixation,	1,		/*[0xE9]*/
891 	  "fixation [toc-type]",
892 	},
893 
894 	{ "help",	cmdhelp,	0,
895 	  "help",
896 	},
897 	{ "probe",	cmdprobe,	0,
898 	  "probe",
899 	},
900 	{ "close",	cmdclose,	1,
901 	  "close",
902 	},
903 	{ "open",	cmdopen,	0,
904 	  "open target-id",
905 	},
906 	{ 0, 0 },
907 };
908 
909 #define	SEP(c)	(((c)==' ')||((c)=='\t')||((c)=='\n'))
910 
911 static char *
912 tokenise(char *s, char **start, char **end)
913 {
914 	char *to;
915 	Rune r;
916 	int n;
917 
918 	while(*s && SEP(*s))				/* skip leading white space */
919 		s++;
920 	to = *start = s;
921 	while(*s){
922 		n = chartorune(&r, s);
923 		if(SEP(r)){
924 			if(to != *start)		/* we have data */
925 				break;
926 			s += n;				/* null string - keep looking */
927 			while(*s && SEP(*s))
928 				s++;
929 			to = *start = s;
930 		}
931 		else if(r == '\''){
932 			s += n;				/* skip leading quote */
933 			while(*s){
934 				n = chartorune(&r, s);
935 				if(r == '\''){
936 					if(s[1] != '\'')
937 						break;
938 					s++;		/* embedded quote */
939 				}
940 				while (n--)
941 					*to++ = *s++;
942 			}
943 			if(!*s)				/* no trailing quote */
944 				break;
945 			s++;				/* skip trailing quote */
946 		}
947 		else  {
948 			while(n--)
949 				*to++ = *s++;
950 		}
951 	}
952 	*end = to;
953 	return s;
954 }
955 
956 static int
957 parse(char *s, char *fields[], int nfields)
958 {
959 	int c, argc;
960 	char *start, *end;
961 
962 	argc = 0;
963 	c = *s;
964 	while(c){
965 		s = tokenise(s, &start, &end);
966 		c = *s++;
967 		if(*start == 0)
968 			break;
969 		if(argc >= nfields-1)
970 			return -1;
971 		*end = 0;
972 		fields[argc++] = start;
973 	}
974 	fields[argc] = 0;
975 	return argc;
976 }
977 
978 static void
979 usage(void)
980 {
981 	fprint(2, "%s: usage: %s [-b bus] [-q] [scsi-id]\n", argv0, argv0);
982 	exits("usage");
983 }
984 
985 void
986 main(int argc, char *argv[])
987 {
988 	ScsiReq target;
989 	char *ap, *av[50];
990 	int ac;
991 	ScsiCmd *cp;
992 	long status;
993 
994 	ARGBEGIN {
995 	case 'b':
996 		bus = atoi(ARGF());
997 		break;
998 
999 	case 'q':
1000 		verbose = 0;
1001 		break;
1002 
1003 	default:
1004 		usage();
1005 	} ARGEND
1006 
1007 	if(Binit(&bin, 0, OREAD) == Beof || Binit(&bout, 1, OWRITE) == Beof){
1008 		fprint(2, "%s: can't init bio: %r\n", argv0);
1009 		exits("Binit");
1010 	}
1011 
1012 	memset(&target, 0, sizeof(target));
1013 	if(argc && cmdopen(&target, argc, argv) == -1)
1014 		usage();
1015 	Bflush(&bout);
1016 
1017 	while(ap = Brdline(&bin, '\n')){
1018 		ap[BLINELEN(&bin)-1] = 0;
1019 		switch(ac = parse(ap, av, nelem(av))){
1020 
1021 		default:
1022 			for(cp = scsicmd; cp->name; cp++){
1023 				if(strcmp(cp->name, av[0]) == 0)
1024 					break;
1025 			}
1026 			if(cp->name == 0){
1027 				Bprint(&bout, "eh?\n");
1028 				break;
1029 			}
1030 			if((target.flags & Fopen) == 0 && cp->open){
1031 				Bprint(&bout, "no current target\n");
1032 				break;
1033 			}
1034 			if((status = (*cp->f)(&target, ac-1, &av[1])) != -1){
1035 				if(verbose)
1036 					Bprint(&bout, "ok %ld\n", status);
1037 				break;
1038 			}
1039 			switch(target.status){
1040 
1041 			case Status_OK:
1042 				if(verbose)
1043 					Bprint(&bout, "ok %ld\n", status);
1044 				break;
1045 
1046 			case Status_SD:
1047 				makesense(&target);
1048 				break;
1049 
1050 			case Status_NX:
1051 				Bprint(&bout, "select timeout\n");
1052 				break;
1053 
1054 			case Status_HW:
1055 				Bprint(&bout, "hardware error\n");
1056 				break;
1057 
1058 			case Status_SW:
1059 				Bprint(&bout, "software error\n");
1060 				break;
1061 
1062 			case Status_BADARG:
1063 				Bprint(&bout, "bad argument\n");
1064 				break;
1065 
1066 			case Status_RO:
1067 				Bprint(&bout, "device is read-only\n");
1068 				break;
1069 
1070 			default:
1071 				Bprint(&bout, "status %ld #%2.2ux\n", status, target.status);
1072 				break;
1073 			}
1074 			break;
1075 
1076 		case -1:
1077 			Bprint(&bout, "eh?\n");
1078 			break;
1079 
1080 		case 0:
1081 			break;
1082 		}
1083 		Bflush(&bout);
1084 	}
1085 	exits(0);
1086 }
1087 
1088