]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/nusb/disk/scsireq.c
add nusb/cam
[plan9front.git] / sys / src / cmd / nusb / disk / scsireq.c
1 /*
2  * This is /sys/src/cmd/scuzz/scsireq.c
3  * changed to add more debug support, to keep
4  * disk compiling without a scuzz that includes these changes.
5  * Also, this includes minor tweaks for usb:
6  *      we set req.lun/unit to rp->lun/unit in SRreqsense
7  *      we set the rp->sense[0] bit Sd0valid in SRreqsense
8  * This does not use libdisk to retrieve the scsi error to make
9  * user see the diagnostics if we boot with debug enabled.
10  *
11  * BUGS:
12  *      no luns
13  *      and incomplete in many other ways
14  */
15
16 #include <u.h>
17 #include <libc.h>
18 #include <fcall.h>
19 #include "scsireq.h"
20
21 enum {
22         Debug = 0,
23 };
24
25 /*
26  * exabyte tape drives, at least old ones like the 8200 and 8505,
27  * are dumb: you have to read the exact block size on the tape,
28  * they don't take 10-byte SCSI commands, and various other fine points.
29  */
30 extern int exabyte, force6bytecmds;
31
32 static int debug = Debug;
33
34 static char *scmdnames[256] = {
35 [ScmdTur]       "Tur",
36 [ScmdRewind]    "Rewind",
37 [ScmdRsense]    "Rsense",
38 [ScmdFormat]    "Format",
39 [ScmdRblimits]  "Rblimits",
40 [ScmdRead]      "Read",
41 [ScmdWrite]     "Write",
42 [ScmdSeek]      "Seek",
43 [ScmdFmark]     "Fmark",
44 [ScmdSpace]     "Space",
45 [ScmdInq]       "Inq",
46 [ScmdMselect6]  "Mselect6",
47 [ScmdMselect10] "Mselect10",
48 [ScmdMsense6]   "Msense6",
49 [ScmdMsense10]  "Msense10",
50 [ScmdStart]     "Start",
51 [ScmdRcapacity] "Rcapacity",
52 [ScmdRcapacity16]       "Rcap16",
53 [ScmdExtread]   "Extread",
54 [ScmdExtwrite]  "Extwrite",
55 [ScmdExtseek]   "Extseek",
56
57 [ScmdSynccache] "Synccache",
58 [ScmdRTOC]      "RTOC",
59 [ScmdRdiscinfo] "Rdiscinfo",
60 [ScmdRtrackinfo]        "Rtrackinfo",
61 [ScmdReserve]   "Reserve",
62 [ScmdBlank]     "Blank",
63
64 [ScmdCDpause]   "CDpause",
65 [ScmdCDstop]    "CDstop",
66 [ScmdCDplay]    "CDplay",
67 [ScmdCDload]    "CDload",
68 [ScmdCDscan]    "CDscan",
69 [ScmdCDstatus]  "CDstatus",
70 [Scmdgetconf]   "getconf",
71 };
72
73 long
74 SRready(ScsiReq *rp)
75 {
76         uchar cmd[6];
77
78         memset(cmd, 0, sizeof cmd);
79         rp->cmd.p = cmd;
80         rp->cmd.count = sizeof cmd;
81         rp->data.p = cmd;
82         rp->data.count = 0;
83         rp->data.write = 1;
84         return SRrequest(rp);
85 }
86
87 long
88 SRrewind(ScsiReq *rp)
89 {
90         uchar cmd[6];
91
92         memset(cmd, 0, sizeof cmd);
93         cmd[0] = ScmdRewind;
94         rp->cmd.p = cmd;
95         rp->cmd.count = sizeof cmd;
96         rp->data.p = cmd;
97         rp->data.count = 0;
98         rp->data.write = 1;
99         if(SRrequest(rp) >= 0){
100                 rp->offset = 0;
101                 return 0;
102         }
103         return -1;
104 }
105
106 long
107 SRreqsense(ScsiReq *rp)
108 {
109         uchar cmd[6];
110         ScsiReq req;
111         long status;
112
113         if(rp->status == Status_SD){
114                 rp->status = STok;
115                 return 0;
116         }
117         memset(cmd, 0, sizeof cmd);
118         cmd[0] = ScmdRsense;
119         cmd[4] = sizeof(req.sense);
120         memset(&req, 0, sizeof(req));
121         if(rp->flags&Fusb)
122                 req.flags |= Fusb;
123         req.lun = rp->lun;
124         req.unit = rp->unit;
125         req.fd = rp->fd;
126         req.umsc = rp->umsc;
127         req.cmd.p = cmd;
128         req.cmd.count = sizeof cmd;
129         req.data.p = rp->sense;
130         req.data.count = sizeof(rp->sense);
131         req.data.write = 0;
132         status = SRrequest(&req);
133         rp->status = req.status;
134         if(status != -1)
135                 rp->sense[0] |= Sd0valid;
136         return status;
137 }
138
139 long
140 SRformat(ScsiReq *rp)
141 {
142         uchar cmd[6];
143
144         memset(cmd, 0, sizeof cmd);
145         cmd[0] = ScmdFormat;
146         rp->cmd.p = cmd;
147         rp->cmd.count = sizeof cmd;
148         rp->data.p = cmd;
149         rp->data.count = 6;
150         rp->data.write = 0;
151         return SRrequest(rp);
152 }
153
154 long
155 SRrblimits(ScsiReq *rp, uchar *list)
156 {
157         uchar cmd[6];
158
159         memset(cmd, 0, sizeof cmd);
160         cmd[0] = ScmdRblimits;
161         rp->cmd.p = cmd;
162         rp->cmd.count = sizeof cmd;
163         rp->data.p = list;
164         rp->data.count = 6;
165         rp->data.write = 0;
166         return SRrequest(rp);
167 }
168
169 static int
170 dirdevrw(ScsiReq *rp, uchar *cmd, long nbytes)
171 {
172         long n;
173
174         n = nbytes / rp->lbsize;
175         if(rp->offset <= Max24off && n <= 256 && (rp->flags & Frw10) == 0){
176                 PUTBE24(cmd+1, rp->offset);
177                 cmd[4] = n;
178                 cmd[5] = 0;
179                 return 6;
180         }
181         cmd[0] |= ScmdExtread;
182         cmd[1] = 0;
183         PUTBELONG(cmd+2, rp->offset);
184         cmd[6] = 0;
185         cmd[7] = n>>8;
186         cmd[8] = n;
187         cmd[9] = 0;
188         return 10;
189 }
190
191 static int
192 seqdevrw(ScsiReq *rp, uchar *cmd, long nbytes)
193 {
194         long n;
195
196         /* don't set Cmd1sili; we want the ILI bit instead of a fatal error */
197         cmd[1] = rp->flags&Fbfixed? Cmd1fixed: 0;
198         n = nbytes / rp->lbsize;
199         PUTBE24(cmd+2, n);
200         cmd[5] = 0;
201         return 6;
202 }
203
204 extern int diskdebug;
205
206 long
207 SRread(ScsiReq *rp, void *buf, long nbytes)
208 {
209         uchar cmd[10];
210         long n;
211
212         if(rp->lbsize == 0 || (nbytes % rp->lbsize) || nbytes > Maxiosize){
213                 if(diskdebug)
214                         if (nbytes % rp->lbsize)
215                                 fprint(2, "disk: i/o size %ld %% %ld != 0\n",
216                                         nbytes, rp->lbsize);
217                         else
218                                 fprint(2, "disk: i/o size %ld > %d\n",
219                                         nbytes, Maxiosize);
220                 rp->status = Status_BADARG;
221                 return -1;
222         }
223
224         /* set up scsi read cmd */
225         cmd[0] = ScmdRead;
226         if(rp->flags & Fseqdev)
227                 rp->cmd.count = seqdevrw(rp, cmd, nbytes);
228         else
229                 rp->cmd.count = dirdevrw(rp, cmd, nbytes);
230         rp->cmd.p = cmd;
231         rp->data.p = buf;
232         rp->data.count = nbytes;
233         rp->data.write = 0;
234
235         /* issue it */
236         n = SRrequest(rp);
237         if(n != -1){                    /* it worked? */
238                 rp->offset += n / rp->lbsize;
239                 return n;
240         }
241
242         /* request failed; maybe we just read a short record? */
243         if (exabyte) {
244                 fprint(2, "read error\n");
245                 rp->status = STcheck;
246                 return n;
247         }
248         if(rp->status != Status_SD || !(rp->sense[0] & Sd0valid))
249                 return -1;
250         /* compute # of bytes not read */
251         n = GETBELONG(rp->sense+3) * rp->lbsize;
252         if(!(rp->flags & Fseqdev))
253                 return -1;
254
255         /* device is a tape or something similar */
256         if (rp->sense[2] == Sd2filemark || rp->sense[2] == 0x08 ||
257             rp->sense[2] & Sd2ili && n > 0)
258                 rp->data.count = nbytes - n;
259         else
260                 return -1;
261         n = rp->data.count;
262         if (!rp->readblock++ || debug)
263                 fprint(2, "SRread: tape data count %ld%s\n", n,
264                         (rp->sense[2] & Sd2ili? " with ILI": ""));
265         rp->status = STok;
266         rp->offset += n / rp->lbsize;
267         return n;
268 }
269
270 long
271 SRwrite(ScsiReq *rp, void *buf, long nbytes)
272 {
273         uchar cmd[10];
274         long n;
275
276         if(rp->lbsize == 0 || (nbytes % rp->lbsize) || nbytes > Maxiosize){
277                 if(diskdebug)
278                         if (nbytes % rp->lbsize)
279                                 fprint(2, "disk: i/o size %ld %% %ld != 0\n",
280                                         nbytes, rp->lbsize);
281                         else
282                                 fprint(2, "disk: i/o size %ld > %d\n",
283                                         nbytes, Maxiosize);
284                 rp->status = Status_BADARG;
285                 return -1;
286         }
287
288         /* set up scsi write cmd */
289         cmd[0] = ScmdWrite;
290         if(rp->flags & Fseqdev)
291                 rp->cmd.count = seqdevrw(rp, cmd, nbytes);
292         else
293                 rp->cmd.count = dirdevrw(rp, cmd, nbytes);
294         rp->cmd.p = cmd;
295         rp->data.p = buf;
296         rp->data.count = nbytes;
297         rp->data.write = 1;
298
299         /* issue it */
300         if((n = SRrequest(rp)) == -1){
301                 if (exabyte) {
302                         fprint(2, "write error\n");
303                         rp->status = STcheck;
304                         return n;
305                 }
306                 if(rp->status != Status_SD || rp->sense[2] != Sd2eom)
307                         return -1;
308                 if(rp->sense[0] & Sd0valid){
309                         n -= GETBELONG(rp->sense+3) * rp->lbsize;
310                         rp->data.count = nbytes - n;
311                 }
312                 else
313                         rp->data.count = nbytes;
314                 n = rp->data.count;
315         }
316         rp->offset += n / rp->lbsize;
317         return n;
318 }
319
320 long
321 SRseek(ScsiReq *rp, long offset, int type)
322 {
323         uchar cmd[10];
324
325         switch(type){
326
327         case 0:
328                 break;
329
330         case 1:
331                 offset += rp->offset;
332                 if(offset >= 0)
333                         break;
334                 /*FALLTHROUGH*/
335
336         default:
337                 if(diskdebug)
338                         fprint(2, "disk: seek failed\n");
339                 rp->status = Status_BADARG;
340                 return -1;
341         }
342         memset(cmd, 0, sizeof cmd);
343         if(offset <= Max24off && (rp->flags & Frw10) == 0){
344                 cmd[0] = ScmdSeek;
345                 PUTBE24(cmd+1, offset & Max24off);
346                 rp->cmd.count = 6;
347         }else{
348                 cmd[0] = ScmdExtseek;
349                 PUTBELONG(cmd+2, offset);
350                 rp->cmd.count = 10;
351         }
352         rp->cmd.p = cmd;
353         rp->data.p = cmd;
354         rp->data.count = 0;
355         rp->data.write = 1;
356         SRrequest(rp);
357         if(rp->status == STok) {
358                 rp->offset = offset;
359                 return offset;
360         }
361         return -1;
362 }
363
364 long
365 SRfilemark(ScsiReq *rp, ulong howmany)
366 {
367         uchar cmd[6];
368
369         memset(cmd, 0, sizeof cmd);
370         cmd[0] = ScmdFmark;
371         PUTBE24(cmd+2, howmany);
372         rp->cmd.p = cmd;
373         rp->cmd.count = sizeof cmd;
374         rp->data.p = cmd;
375         rp->data.count = 0;
376         rp->data.write = 1;
377         return SRrequest(rp);
378 }
379
380 long
381 SRspace(ScsiReq *rp, uchar code, long howmany)
382 {
383         uchar cmd[6];
384
385         memset(cmd, 0, sizeof cmd);
386         cmd[0] = ScmdSpace;
387         cmd[1] = code;
388         PUTBE24(cmd+2, howmany);
389         rp->cmd.p = cmd;
390         rp->cmd.count = sizeof cmd;
391         rp->data.p = cmd;
392         rp->data.count = 0;
393         rp->data.write = 1;
394         /*
395          * what about rp->offset?
396          */
397         return SRrequest(rp);
398 }
399
400 long
401 SRinquiry(ScsiReq *rp)
402 {
403         uchar cmd[6];
404
405         memset(cmd, 0, sizeof cmd);
406         cmd[0] = ScmdInq;
407         cmd[4] = 36;
408         rp->cmd.p = cmd;
409         rp->cmd.count = sizeof cmd;
410         memset(rp->inquiry, 0, sizeof rp->inquiry);
411         rp->data.p = rp->inquiry;
412         rp->data.count = 36;
413         rp->data.write = 0;
414         if(SRrequest(rp) >= 0){
415                 rp->flags |= Finqok;
416                 return 0;
417         }
418         rp->flags &= ~Finqok;
419         return -1;
420 }
421
422 long
423 SRmodeselect6(ScsiReq *rp, uchar *list, long nbytes)
424 {
425         uchar cmd[6];
426
427         memset(cmd, 0, sizeof cmd);
428         cmd[0] = ScmdMselect6;
429         if((rp->flags & Finqok) && (rp->inquiry[2] & 0x07) >= 2)
430                 cmd[1] = 0x10;
431         cmd[4] = nbytes;
432         rp->cmd.p = cmd;
433         rp->cmd.count = sizeof cmd;
434         rp->data.p = list;
435         rp->data.count = nbytes;
436         rp->data.write = 1;
437         return SRrequest(rp);
438 }
439
440 long
441 SRmodeselect10(ScsiReq *rp, uchar *list, long nbytes)
442 {
443         uchar cmd[10];
444
445         memset(cmd, 0, sizeof cmd);
446         if((rp->flags & Finqok) && (rp->inquiry[2] & 0x07) >= 2)
447                 cmd[1] = 0x10;
448         cmd[0] = ScmdMselect10;
449         cmd[7] = nbytes>>8;
450         cmd[8] = nbytes;
451         rp->cmd.p = cmd;
452         rp->cmd.count = sizeof cmd;
453         rp->data.p = list;
454         rp->data.count = nbytes;
455         rp->data.write = 1;
456         return SRrequest(rp);
457 }
458
459 long
460 SRmodesense6(ScsiReq *rp, uchar page, uchar *list, long nbytes)
461 {
462         uchar cmd[6];
463
464         memset(cmd, 0, sizeof cmd);
465         cmd[0] = ScmdMsense6;
466         cmd[2] = page;
467         cmd[4] = nbytes;
468         rp->cmd.p = cmd;
469         rp->cmd.count = sizeof cmd;
470         rp->data.p = list;
471         rp->data.count = nbytes;
472         rp->data.write = 0;
473         return SRrequest(rp);
474 }
475
476 long
477 SRmodesense10(ScsiReq *rp, uchar page, uchar *list, long nbytes)
478 {
479         uchar cmd[10];
480
481         memset(cmd, 0, sizeof cmd);
482         cmd[0] = ScmdMsense10;
483         cmd[2] = page;
484         cmd[7] = nbytes>>8;
485         cmd[8] = nbytes;
486         rp->cmd.p = cmd;
487         rp->cmd.count = sizeof cmd;
488         rp->data.p = list;
489         rp->data.count = nbytes;
490         rp->data.write = 0;
491         return SRrequest(rp);
492 }
493
494 long
495 SRstart(ScsiReq *rp, uchar code)
496 {
497         uchar cmd[6];
498
499         memset(cmd, 0, sizeof cmd);
500         cmd[0] = ScmdStart;
501         cmd[4] = code;
502         rp->cmd.p = cmd;
503         rp->cmd.count = sizeof cmd;
504         rp->data.p = cmd;
505         rp->data.count = 0;
506         rp->data.write = 1;
507         return SRrequest(rp);
508 }
509
510 long
511 SRrcapacity(ScsiReq *rp, uchar *data)
512 {
513         uchar cmd[10];
514
515         memset(cmd, 0, sizeof cmd);
516         cmd[0] = ScmdRcapacity;
517         rp->cmd.p = cmd;
518         rp->cmd.count = sizeof cmd;
519         rp->data.p = data;
520         rp->data.count = 8;
521         rp->data.write = 0;
522         return SRrequest(rp);
523 }
524
525 long
526 SRrcapacity16(ScsiReq *rp, uchar *data)
527 {
528         uchar cmd[16];
529         uint i;
530
531         i = 32;
532         memset(cmd, 0, sizeof cmd);
533         cmd[0] = ScmdRcapacity16;
534         cmd[1] = 0x10;
535         cmd[10] = i>>24;
536         cmd[11] = i>>16;
537         cmd[12] = i>>8;
538         cmd[13] = i;
539
540         rp->cmd.p = cmd;
541         rp->cmd.count = sizeof cmd;
542         rp->data.p = data;
543         rp->data.count = i;
544         rp->data.write = 0;
545         return SRrequest(rp);
546 }
547
548 void
549 scsidebug(int d)
550 {
551         debug = d;
552         if(debug)
553                 fprint(2, "scsidebug on\n");
554 }
555
556 static long
557 request(int fd, ScsiPtr *cmd, ScsiPtr *data, int *status)
558 {
559         long n, r;
560         char buf[16];
561
562         /* this was an experiment but it seems to be a good idea */
563         *status = STok;
564
565         /* send SCSI command */
566         if(write(fd, cmd->p, cmd->count) != cmd->count){
567                 fprint(2, "scsireq: write cmd: %r\n");
568                 *status = Status_SW;
569                 return -1;
570         }
571
572         /* read or write actual data */
573         werrstr("");
574 //      alarm(5*1000);
575         if(data->write)
576                 n = write(fd, data->p, data->count);
577         else {
578                 n = read(fd, data->p, data->count);
579                 if (n < 0)
580                         memset(data->p, 0, data->count);
581                 else if (n < data->count)
582                         memset(data->p + n, 0, data->count - n);
583         }
584 //      alarm(0);
585         if (n != data->count && n <= 0) {
586                 if (debug)
587                         fprint(2,
588         "request: tried to %s %ld bytes of data for cmd 0x%x but got %r\n",
589                                 (data->write? "write": "read"),
590                                 data->count, cmd->p[0]);
591         } else if (n != data->count && (data->write || debug))
592                 fprint(2, "request: %s %ld of %ld bytes of actual data\n",
593                         (data->write? "wrote": "read"), n, data->count);
594
595         /* read status */
596         buf[0] = '\0';
597         r = read(fd, buf, sizeof buf-1);
598         if(exabyte && r <= 0 || !exabyte && r < 0){
599                 fprint(2, "scsireq: read status: %r\n");
600                 *status = Status_SW;
601                 return -1;
602         }
603         if (r >= 0)
604                 buf[r] = '\0';
605         *status = atoi(buf);
606         if(n < 0 && (exabyte || *status != STcheck))
607                 fprint(2, "scsireq: status 0x%2.2uX: data transfer: %r\n",
608                         *status);
609         return n;
610 }
611
612 static char*
613 seprintcmd(char *s, char* e, char *cmd, int count, int args)
614 {
615         uint c;
616
617         if(count < 6)
618                 return seprint(s, e, "<short cmd>");
619         c = cmd[0];
620         if(scmdnames[c] != nil)
621                 s = seprint(s, e, "%s", scmdnames[c]);
622         else
623                 s = seprint(s, e, "cmd:%#02uX", c);
624         if(args != 0)
625                 switch(c){
626                 case ScmdRsense:
627                 case ScmdInq:
628                 case ScmdMselect6:
629                 case ScmdMsense6:
630                         s = seprint(s, e, " sz %d", cmd[4]);
631                         break;
632                 case ScmdSpace:
633                         s = seprint(s, e, " code %d", cmd[1]);
634                         break;
635                 case ScmdStart:
636                         s = seprint(s, e, " code %d", cmd[4]);
637                         break;
638         
639                 }
640         return s;
641 }
642
643 static char*
644 seprintdata(char *s, char *se, uchar *p, int count)
645 {
646         int i;
647
648         if(count == 0)
649                 return s;
650         for(i = 0; i < 20 && i < count; i++)
651                 s = seprint(s, se, " %02x", p[i]);
652         return s;
653 }
654
655 static void
656 SRdumpReq(ScsiReq *rp)
657 {
658         char buf[128];
659         char *s;
660         char *se;
661
662         se = buf+sizeof(buf);
663         s = seprint(buf, se, "lun %d ", rp->lun);
664         s = seprintcmd(s, se, (char*)rp->cmd.p, rp->cmd.count, 1);
665         s = seprint(s, se, " [%ld]", rp->data.count);
666         if(rp->cmd.write)
667                 seprintdata(s, se, rp->data.p, rp->data.count);
668         fprint(2, "scsi⇒ %s\n", buf);
669 }
670
671 static void
672 SRdumpRep(ScsiReq *rp)
673 {
674         char buf[128];
675         char *s;
676         char *se;
677
678         se = buf+sizeof(buf);
679         s = seprint(buf, se, "lun %d ", rp->lun);
680         s = seprintcmd(s, se, (char*)rp->cmd.p, rp->cmd.count, 0);
681         switch(rp->status){
682         case STok:
683                 s = seprint(s, se, " good [%ld] ", rp->data.count);
684                 if(rp->cmd.write == 0)
685                         s = seprintdata(s, se, rp->data.p, rp->data.count);
686                 break;
687         case STnomem:
688                 s = seprint(s, se, " buffer allocation failed");
689                 break;
690         case STharderr:
691                 s = seprint(s, se, " controller error");
692                 break;
693         case STtimeout:
694                 s = seprint(s, se, " bus timeout");
695                 break;
696         case STcheck:
697                 s = seprint(s, se, " check condition");
698                 break;
699         case STcondmet:
700                 s = seprint(s, se, " condition met/good");
701                 break;
702         case STbusy:
703                 s = seprint(s, se, " busy");
704                 break;
705         case STintok:
706                 s = seprint(s, se, " intermediate/good");
707                 break;
708         case STintcondmet:
709                 s = seprint(s, se, " intermediate/condition met/good");
710                 break;
711         case STresconf:
712                 s = seprint(s, se, " reservation conflict");
713                 break;
714         case STterminated:
715                 s = seprint(s, se, " command terminated");
716                 break;
717         case STqfull:
718                 s = seprint(s, se, " queue full");
719                 break;
720         default:
721                 s = seprint(s, se, " sts=%#x", rp->status);
722         }
723         USED(s);
724         fprint(2, "scsi← %s\n", buf);
725 }
726
727 static char*
728 scsierr(ScsiReq *rp)
729 {
730         int ec;
731
732         switch(rp->status){
733         case 0:
734                 return "";
735         case Status_SD:
736                 ec = (rp->sense[12] << 8) | rp->sense[13];
737                 return scsierrmsg(ec);
738         case Status_SW:
739                 return "software error";
740         case Status_BADARG:
741                 return "bad argument";
742         case Status_RO:
743                 return "device is read only";
744         default:
745                 return "unknown";
746         }
747 }
748
749 static void
750 SRdumpErr(ScsiReq *rp)
751 {
752         char buf[128];
753         char *se;
754
755         se = buf+sizeof(buf);
756         seprintcmd(buf, se, (char*)rp->cmd.p, rp->cmd.count, 0);
757         print("\t%s status: %s\n", buf, scsierr(rp));
758 }
759
760 long
761 SRrequest(ScsiReq *rp)
762 {
763         long n;
764         int status;
765
766 retry:
767         if(debug)
768                 SRdumpReq(rp);
769         if(rp->flags&Fusb)
770                 n = umsrequest(rp->umsc, &rp->cmd, &rp->data, &status);
771         else
772                 n = request(rp->fd, &rp->cmd, &rp->data, &status);
773         rp->status = status;
774         if(status == STok)
775                 rp->data.count = n;
776         if(debug)
777                 SRdumpRep(rp);
778         switch(status){
779         case STok:
780                 break;
781         case STcheck:
782                 if(rp->cmd.p[0] != ScmdRsense && SRreqsense(rp) != -1)
783                         rp->status = Status_SD;
784                 if(debug || exabyte)
785                         SRdumpErr(rp);
786                 werrstr("%s", scsierr(rp));
787                 return -1;
788         case STbusy:
789                 sleep(1000);            /* TODO: try a shorter sleep? */
790                 goto retry;
791         default:
792                 if(debug || exabyte)
793                         SRdumpErr(rp);
794                 werrstr("%s", scsierr(rp));
795                 return -1;
796         }
797         return n;
798 }
799
800 int
801 SRclose(ScsiReq *rp)
802 {
803         if((rp->flags & Fopen) == 0){
804                 if(diskdebug)
805                         fprint(2, "disk: closing closed file\n");
806                 rp->status = Status_BADARG;
807                 return -1;
808         }
809         close(rp->fd);
810         rp->flags = 0;
811         return 0;
812 }
813
814 static int
815 dirdevopen(ScsiReq *rp)
816 {
817         uvlong blocks;
818         uchar data[8+4+20];     /* 16-byte result: lba, blksize, reserved */
819
820         memset(data, 0, sizeof data);
821         if(SRstart(rp, 1) == -1 || SRrcapacity(rp, data) == -1)
822                 return -1;
823         rp->lbsize = GETBELONG(data+4);
824         blocks =     GETBELONG(data);
825         if(debug)
826                 fprint(2, "disk: dirdevopen: 10-byte logical block size %lud, "
827                         "# blocks %llud\n", rp->lbsize, blocks);
828         if(blocks == 0xffffffff){
829                 if(SRrcapacity16(rp, data) == -1)
830                         return -1;
831                 rp->lbsize = GETBELONG(data + 8);
832                 blocks = (vlong)GETBELONG(data)<<32 | GETBELONG(data + 4);
833                 if(debug)
834                         fprint(2, "disk: dirdevopen: 16-byte logical block size"
835                                 " %lud, # blocks %llud\n", rp->lbsize, blocks);
836         }
837         /* some newer dev's don't support 6-byte commands */
838         if(blocks > Max24off && !force6bytecmds)
839                 rp->flags |= Frw10;
840         return 0;
841 }
842
843 static int
844 seqdevopen(ScsiReq *rp)
845 {
846         uchar mode[16], limits[6];
847
848         if(SRrblimits(rp, limits) == -1)
849                 return -1;
850         if(limits[1] == 0 && limits[2] == limits[4] && limits[3] == limits[5]){
851                 rp->flags |= Fbfixed;
852                 rp->lbsize = limits[4]<<8 | limits[5];
853                 if(debug)
854                         fprint(2, "disk: seqdevopen: 10-byte logical block size %lud\n",
855                                 rp->lbsize);
856                 return 0;
857         }
858         /*
859          * On some older hardware the optional 10-byte
860          * modeselect command isn't implemented.
861          */
862         if (force6bytecmds)
863                 rp->flags |= Fmode6;
864         if(!(rp->flags & Fmode6)){
865                 /* try 10-byte command first */
866                 memset(mode, 0, sizeof mode);
867                 mode[3] = 0x10;         /* device-specific param. */
868                 mode[7] = 8;            /* block descriptor length */
869                 /*
870                  * exabytes can't handle this, and
871                  * modeselect(10) is optional.
872                  */
873                 if(SRmodeselect10(rp, mode, sizeof mode) != -1){
874                         rp->lbsize = 1;
875                         return 0;       /* success */
876                 }
877                 /* can't do 10-byte commands, back off to 6-byte ones */
878                 rp->flags |= Fmode6;
879         }
880
881         /* 6-byte command */
882         memset(mode, 0, sizeof mode);
883         mode[2] = 0x10;         /* device-specific param. */
884         mode[3] = 8;            /* block descriptor length */
885         /*
886          * bsd sez exabytes need this bit (NBE: no busy enable) in
887          * vendor-specific page (0), but so far we haven't needed it.
888         mode[12] |= 8;
889          */
890         if(SRmodeselect6(rp, mode, 4+8) == -1)
891                 return -1;
892         rp->lbsize = 1;
893         return 0;
894 }
895
896 static int
897 wormdevopen(ScsiReq *rp)
898 {
899         long status;
900         uchar list[MaxDirData];
901
902         if (SRstart(rp, 1) == -1 ||
903             (status = SRmodesense10(rp, Allmodepages, list, sizeof list)) == -1)
904                 return -1;
905         /* nbytes = list[0]<<8 | list[1]; */
906
907         /* # of bytes of block descriptors of 8 bytes each; not even 1? */
908         if((list[6]<<8 | list[7]) < 8)
909                 rp->lbsize = 2048;
910         else
911                 /* last 3 bytes of block 0 descriptor */
912                 rp->lbsize = GETBE24(list+13);
913         if(debug)
914                 fprint(2, "disk: wormdevopen: 10-byte logical block size %lud\n",
915                         rp->lbsize);
916         return status;
917 }
918
919 int
920 SRopenraw(ScsiReq *rp, char *unit)
921 {
922         char name[128];
923
924         if(rp->flags & Fopen){
925                 if(diskdebug)
926                         fprint(2, "disk: opening open file\n");
927                 rp->status = Status_BADARG;
928                 return -1;
929         }
930         memset(rp, 0, sizeof *rp);
931         rp->unit = unit;
932
933         snprint(name, sizeof name, "%s/raw", unit);
934         if((rp->fd = open(name, ORDWR)) == -1){
935                 rp->status = STtimeout;
936                 return -1;
937         }
938         rp->flags = Fopen;
939         return 0;
940 }
941
942 int
943 SRopen(ScsiReq *rp, char *unit)
944 {
945         if(SRopenraw(rp, unit) == -1)
946                 return -1;
947         SRready(rp);
948         if(SRinquiry(rp) >= 0){
949                 switch(rp->inquiry[0]){
950
951                 default:
952                         fprint(2, "unknown device type 0x%.2x\n", rp->inquiry[0]);
953                         rp->status = Status_SW;
954                         break;
955
956                 case Devdir:
957                 case Devcd:
958                 case Devmo:
959                         if(dirdevopen(rp) == -1)
960                                 break;
961                         return 0;
962
963                 case Devseq:
964                         rp->flags |= Fseqdev;
965                         if(seqdevopen(rp) == -1)
966                                 break;
967                         return 0;
968
969                 case Devprint:
970                         rp->flags |= Fprintdev;
971                         return 0;
972
973                 case Devworm:
974                         rp->flags |= Fwormdev;
975                         if(wormdevopen(rp) == -1)
976                                 break;
977                         return 0;
978
979                 case Devjuke:
980                         rp->flags |= Fchanger;
981                         return 0;
982                 }
983         }
984         SRclose(rp);
985         return -1;
986 }