]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/scuzz/scuzz.c
upas/fs: remove useless loop in rf822()
[plan9front.git] / sys / src / cmd / scuzz / scuzz.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4
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 scsicmd[];
34
35 static vlong
36 vlmin(vlong a, vlong b)
37 {
38         if (a < b)
39                 return a;
40         else
41                 return b;
42 }
43
44 static long
45 cmdready(ScsiReq *rp, int argc, char *argv[])
46 {
47         USED(argc, argv);
48         return SRready(rp);
49 }
50
51 static long
52 cmdrewind(ScsiReq *rp, int argc, char *argv[])
53 {
54         USED(argc, argv);
55         return SRrewind(rp);
56 }
57
58 static long
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
70 cmdformat(ScsiReq *rp, int argc, char *argv[])
71 {
72         USED(argc, argv);
73         return SRformat(rp);
74 }
75
76 static long
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
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
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
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
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
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
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
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
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
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
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
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
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
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
607 cmdstart(ScsiReq *rp, int argc, char *argv[])
608 {
609         return start(rp, argc, argv, 1);
610 }
611
612 static long
613 cmdstop(ScsiReq *rp, int argc, char *argv[])
614 {
615         return start(rp, argc, argv, 0);
616 }
617
618 static long
619 cmdeject(ScsiReq *rp, int argc, char *argv[])
620 {
621         return start(rp, argc, argv, 2);
622 }
623
624 static long
625 cmdingest(ScsiReq *rp, int argc, char *argv[])
626 {
627         return start(rp, argc, argv, 3);
628 }
629
630 static long
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
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
683 cmdsynccache(ScsiReq *rp, int argc, char *argv[])
684 {
685         USED(argc, argv);
686         return SRsynccache(rp);
687 }
688
689 static long
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
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
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
1035 cmdcdpause(ScsiReq *rp, int argc, char *argv[])
1036 {
1037         USED(argc, argv);
1038         return SRcdpause(rp, 0);
1039 }
1040
1041 static long
1042 cmdcdresume(ScsiReq *rp, int argc, char *argv[])
1043 {
1044         USED(argc, argv);
1045         return SRcdpause(rp, 1);
1046 }
1047
1048 static long
1049 cmdcdstop(ScsiReq *rp, int argc, char *argv[])
1050 {
1051         USED(argc, argv);
1052         return SRcdstop(rp);
1053 }
1054
1055 static long
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
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
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
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
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
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
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
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
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
1359 cmdload(ScsiReq *rp, int argc, char *argv[])
1360 {
1361         USED(argc, argv);
1362         return SRmload(rp, 0);
1363 }
1364
1365 static long
1366 cmdunload(ScsiReq *rp, int argc, char *argv[])
1367 {
1368         USED(argc, argv);
1369         return SRmload(rp, 1);
1370 }
1371
1372 static long
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
1387 cmdeinit(ScsiReq *rp, int argc, char *argv[])
1388 {
1389         USED(argc, argv);
1390         return SReinitialise(rp);
1391 }
1392
1393 static long
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
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
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 = scsicmd; 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
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
1571 cmdclose(ScsiReq *rp, int argc, char *argv[])
1572 {
1573         USED(argc, argv);
1574         return SRclose(rp);
1575 }
1576
1577 static long
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 scsicmd[] = {
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 *
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
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
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
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 = scsicmd; 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
1953 umsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status)
1954 {
1955         USED(umsc, data, cmd);
1956         *status = STharderr;
1957         return -1;
1958 }