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