]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/scuzz/scsireq.c
grep: error if sbrk fails
[plan9front.git] / sys / src / cmd / scuzz / scsireq.c
1 #include <u.h>
2 #include <libc.h>
3 /*
4  * BUGS:
5  *      no luns
6  *      and incomplete in many other ways
7  */
8 #include "scsireq.h"
9
10 enum {
11         Debug = 0,
12 };
13
14 /*
15  * exabyte tape drives, at least old ones like the 8200 and 8505,
16  * are dumb: you have to read the exact block size on the tape,
17  * they don't take 10-byte SCSI commands, and various other fine points.
18  */
19 extern int exabyte, force6bytecmds;
20
21 static int debug = Debug;
22
23 long
24 SRready(ScsiReq *rp)
25 {
26         uchar cmd[6];
27
28         memset(cmd, 0, sizeof cmd);
29         rp->cmd.p = cmd;
30         rp->cmd.count = sizeof cmd;
31         rp->data.p = cmd;
32         rp->data.count = 0;
33         rp->data.write = 1;
34         return SRrequest(rp);
35 }
36
37 long
38 SRrewind(ScsiReq *rp)
39 {
40         uchar cmd[6];
41
42         memset(cmd, 0, sizeof cmd);
43         cmd[0] = ScmdRewind;
44         rp->cmd.p = cmd;
45         rp->cmd.count = sizeof cmd;
46         rp->data.p = cmd;
47         rp->data.count = 0;
48         rp->data.write = 1;
49         if(SRrequest(rp) >= 0){
50                 rp->offset = 0;
51                 return 0;
52         }
53         return -1;
54 }
55
56 long
57 SRreqsense(ScsiReq *rp)
58 {
59         uchar cmd[6];
60         ScsiReq req;
61         long status;
62
63         if(rp->status == Status_SD){
64                 rp->status = STok;
65                 return 0;
66         }
67         memset(cmd, 0, sizeof cmd);
68         cmd[0] = ScmdRsense;
69         cmd[4] = sizeof(req.sense);
70         memset(&req, 0, sizeof(req));
71         if(rp->flags&Fusb)
72                 req.flags |= Fusb;
73         req.fd = rp->fd;
74         req.umsc = rp->umsc;
75         req.cmd.p = cmd;
76         req.cmd.count = sizeof cmd;
77         req.data.p = rp->sense;
78         req.data.count = sizeof(rp->sense);
79         req.data.write = 0;
80         status = SRrequest(&req);
81         rp->status = req.status;
82         return status;
83 }
84
85 long
86 SRformat(ScsiReq *rp)
87 {
88         uchar cmd[6];
89
90         memset(cmd, 0, sizeof cmd);
91         cmd[0] = ScmdFormat;
92         rp->cmd.p = cmd;
93         rp->cmd.count = sizeof cmd;
94         rp->data.p = cmd;
95         rp->data.count = 6;
96         rp->data.write = 0;
97         return SRrequest(rp);
98 }
99
100 long
101 SRrblimits(ScsiReq *rp, uchar *list)
102 {
103         uchar cmd[6];
104
105         memset(cmd, 0, sizeof cmd);
106         cmd[0] = ScmdRblimits;
107         rp->cmd.p = cmd;
108         rp->cmd.count = sizeof cmd;
109         rp->data.p = list;
110         rp->data.count = 6;
111         rp->data.write = 0;
112         return SRrequest(rp);
113 }
114
115 static int
116 dirdevrw(ScsiReq *rp, uchar *cmd, long nbytes)
117 {
118         long n;
119
120         n = nbytes / rp->lbsize;
121         if(rp->offset <= Max24off && n <= 256 && (rp->flags & Frw10) == 0){
122                 PUTBE24(cmd+1, rp->offset);
123                 cmd[4] = n;
124                 cmd[5] = 0;
125                 return 6;
126         }
127         cmd[0] |= ScmdExtread;
128         cmd[1] = 0;
129         PUTBELONG(cmd+2, rp->offset);
130         cmd[6] = 0;
131         cmd[7] = n>>8;
132         cmd[8] = n;
133         cmd[9] = 0;
134         return 10;
135 }
136
137 static int
138 seqdevrw(ScsiReq *rp, uchar *cmd, long nbytes)
139 {
140         long n;
141
142         /* don't set Cmd1sili; we want the ILI bit instead of a fatal error */
143         cmd[1] = rp->flags&Fbfixed? Cmd1fixed: 0;
144         n = nbytes / rp->lbsize;
145         PUTBE24(cmd+2, n);
146         cmd[5] = 0;
147         return 6;
148 }
149
150 long
151 SRread(ScsiReq *rp, void *buf, long nbytes)
152 {
153         uchar cmd[10];
154         long n;
155
156         if((nbytes % rp->lbsize) || nbytes > maxiosize){
157                 if(debug)
158                         if (nbytes % rp->lbsize)
159                                 fprint(2, "scuzz: i/o size %ld %% %ld != 0\n",
160                                         nbytes, rp->lbsize);
161                         else
162                                 fprint(2, "scuzz: i/o size %ld > %ld\n",
163                                         nbytes, maxiosize);
164                 rp->status = Status_BADARG;
165                 return -1;
166         }
167
168         /* set up scsi read cmd */
169         cmd[0] = ScmdRead;
170         if(rp->flags & Fseqdev)
171                 rp->cmd.count = seqdevrw(rp, cmd, nbytes);
172         else
173                 rp->cmd.count = dirdevrw(rp, cmd, nbytes);
174         rp->cmd.p = cmd;
175         rp->data.p = buf;
176         rp->data.count = nbytes;
177         rp->data.write = 0;
178
179         /* issue it */
180         n = SRrequest(rp);
181         if(n != -1){                    /* it worked? */
182                 rp->offset += n / rp->lbsize;
183                 return n;
184         }
185
186         /* request failed; maybe we just read a short record? */
187         if (exabyte) {
188                 fprint(2, "read error\n");
189                 rp->status = STcheck;
190                 return n;
191         }
192         if(rp->status != Status_SD || !(rp->sense[0] & Sd0valid))
193                 return -1;
194         /* compute # of bytes not read */
195         n = GETBELONG(rp->sense+3) * rp->lbsize;
196         if (debug)
197                 fprint(2,
198         "SRread: request failed with sense data; sense byte count %ld\n",
199                         n);
200         if(!(rp->flags & Fseqdev))
201                 return -1;
202
203         /* device is a tape or something similar */
204         if (rp->sense[2] == Sd2filemark || rp->sense[2] == 0x08 ||
205             rp->sense[2] & Sd2ili && n > 0)
206                 rp->data.count = nbytes - n;
207         else
208                 return -1;
209         n = rp->data.count;
210         if (!rp->readblock++ || debug)
211                 fprint(2, "SRread: tape data count %ld%s\n", n,
212                         (rp->sense[2] & Sd2ili? " with ILI": ""));
213         rp->status = STok;
214         rp->offset += n / rp->lbsize;
215         return n;
216 }
217
218 long
219 SRwrite(ScsiReq *rp, void *buf, long nbytes)
220 {
221         uchar cmd[10];
222         long n;
223
224         if((nbytes % rp->lbsize) || nbytes > maxiosize){
225                 if(debug)
226                         if (nbytes % rp->lbsize)
227                                 fprint(2, "scuzz: i/o size %ld %% %ld != 0\n",
228                                         nbytes, rp->lbsize);
229                         else
230                                 fprint(2, "scuzz: i/o size %ld > %ld\n",
231                                         nbytes, maxiosize);
232                 rp->status = Status_BADARG;
233                 return -1;
234         }
235
236         /* set up scsi write cmd */
237         cmd[0] = ScmdWrite;
238         if(rp->flags & Fseqdev)
239                 rp->cmd.count = seqdevrw(rp, cmd, nbytes);
240         else
241                 rp->cmd.count = dirdevrw(rp, cmd, nbytes);
242         rp->cmd.p = cmd;
243         rp->data.p = buf;
244         rp->data.count = nbytes;
245         rp->data.write = 1;
246
247         /* issue it */
248         if((n = SRrequest(rp)) == -1){
249                 if (exabyte) {
250                         fprint(2, "write error\n");
251                         rp->status = STcheck;
252                         return n;
253                 }
254                 if(rp->status != Status_SD || rp->sense[2] != Sd2eom)
255                         return -1;
256                 if(rp->sense[0] & Sd0valid){
257                         n -= GETBELONG(rp->sense+3) * rp->lbsize;
258                         rp->data.count = nbytes - n;
259                 }
260                 else
261                         rp->data.count = nbytes;
262                 n = rp->data.count;
263         }
264         rp->offset += n / rp->lbsize;
265         return n;
266 }
267
268 long
269 SRseek(ScsiReq *rp, long offset, int type)
270 {
271         uchar cmd[10];
272
273         switch(type){
274
275         case 0:
276                 break;
277
278         case 1:
279                 offset += rp->offset;
280                 if(offset >= 0)
281                         break;
282                 /*FALLTHROUGH*/
283
284         default:
285                 if(debug)
286                         fprint(2, "scuzz: seek failed\n");
287                 rp->status = Status_BADARG;
288                 return -1;
289         }
290         memset(cmd, 0, sizeof cmd);
291         if(offset <= Max24off && (rp->flags & Frw10) == 0){
292                 cmd[0] = ScmdSeek;
293                 PUTBE24(cmd+1, offset & Max24off);
294                 rp->cmd.count = 6;
295         }else{
296                 cmd[0] = ScmdExtseek;
297                 PUTBELONG(cmd+2, offset);
298                 rp->cmd.count = 10;
299         }
300         rp->cmd.p = cmd;
301         rp->data.p = cmd;
302         rp->data.count = 0;
303         rp->data.write = 1;
304         SRrequest(rp);
305         if(rp->status == STok)
306                 return rp->offset = offset;
307         return -1;
308 }
309
310 long
311 SRfilemark(ScsiReq *rp, ulong howmany)
312 {
313         uchar cmd[6];
314
315         memset(cmd, 0, sizeof cmd);
316         cmd[0] = ScmdFmark;
317         PUTBE24(cmd+2, howmany);
318         rp->cmd.p = cmd;
319         rp->cmd.count = sizeof cmd;
320         rp->data.p = cmd;
321         rp->data.count = 0;
322         rp->data.write = 1;
323         return SRrequest(rp);
324 }
325
326 long
327 SRspace(ScsiReq *rp, uchar code, long howmany)
328 {
329         uchar cmd[6];
330
331         memset(cmd, 0, sizeof cmd);
332         cmd[0] = ScmdSpace;
333         cmd[1] = code;
334         PUTBE24(cmd+2, howmany);
335         rp->cmd.p = cmd;
336         rp->cmd.count = sizeof cmd;
337         rp->data.p = cmd;
338         rp->data.count = 0;
339         rp->data.write = 1;
340         /*
341          * what about rp->offset?
342          */
343         return SRrequest(rp);
344 }
345
346 long
347 SRinquiry(ScsiReq *rp)
348 {
349         uchar cmd[6];
350
351         memset(cmd, 0, sizeof cmd);
352         cmd[0] = ScmdInq;
353         cmd[4] = sizeof rp->inquiry;
354         rp->cmd.p = cmd;
355         rp->cmd.count = sizeof cmd;
356         memset(rp->inquiry, 0, sizeof rp->inquiry);
357         rp->data.p = rp->inquiry;
358         rp->data.count = sizeof rp->inquiry;
359         rp->data.write = 0;
360         if(SRrequest(rp) >= 0){
361                 rp->flags |= Finqok;
362                 return 0;
363         }
364         rp->flags &= ~Finqok;
365         return -1;
366 }
367
368 long
369 SRmodeselect6(ScsiReq *rp, uchar *list, long nbytes)
370 {
371         uchar cmd[6];
372
373         memset(cmd, 0, sizeof cmd);
374         cmd[0] = ScmdMselect6;
375         if((rp->flags & Finqok) && (rp->inquiry[2] & 0x07) >= 2)
376                 cmd[1] = 0x10;
377         cmd[4] = nbytes;
378         rp->cmd.p = cmd;
379         rp->cmd.count = sizeof cmd;
380         rp->data.p = list;
381         rp->data.count = nbytes;
382         rp->data.write = 1;
383         return SRrequest(rp);
384 }
385
386 long
387 SRmodeselect10(ScsiReq *rp, uchar *list, long nbytes)
388 {
389         uchar cmd[10];
390
391         memset(cmd, 0, sizeof cmd);
392         if((rp->flags & Finqok) && (rp->inquiry[2] & 0x07) >= 2)
393                 cmd[1] = 0x10;
394         cmd[0] = ScmdMselect10;
395         cmd[7] = nbytes>>8;
396         cmd[8] = nbytes;
397         rp->cmd.p = cmd;
398         rp->cmd.count = sizeof cmd;
399         rp->data.p = list;
400         rp->data.count = nbytes;
401         rp->data.write = 1;
402         return SRrequest(rp);
403 }
404
405 long
406 SRmodesense6(ScsiReq *rp, uchar page, uchar *list, long nbytes)
407 {
408         uchar cmd[6];
409
410         memset(cmd, 0, sizeof cmd);
411         cmd[0] = ScmdMsense6;
412         cmd[2] = page;
413         cmd[4] = nbytes;
414         rp->cmd.p = cmd;
415         rp->cmd.count = sizeof cmd;
416         rp->data.p = list;
417         rp->data.count = nbytes;
418         rp->data.write = 0;
419         return SRrequest(rp);
420 }
421
422 long
423 SRmodesense10(ScsiReq *rp, uchar page, uchar *list, long nbytes)
424 {
425         uchar cmd[10];
426
427         memset(cmd, 0, sizeof cmd);
428         cmd[0] = ScmdMsense10;
429         cmd[2] = page;
430         cmd[7] = nbytes>>8;
431         cmd[8] = 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 = 0;
437         return SRrequest(rp);
438 }
439
440 long
441 SRstart(ScsiReq *rp, uchar code)
442 {
443         uchar cmd[6];
444
445         memset(cmd, 0, sizeof cmd);
446         cmd[0] = ScmdStart;
447         cmd[4] = code;
448         rp->cmd.p = cmd;
449         rp->cmd.count = sizeof cmd;
450         rp->data.p = cmd;
451         rp->data.count = 0;
452         rp->data.write = 1;
453         return SRrequest(rp);
454 }
455
456 long
457 SRrcapacity(ScsiReq *rp, uchar *data)
458 {
459         uchar cmd[10];
460
461         memset(cmd, 0, sizeof cmd);
462         cmd[0] = ScmdRcapacity;
463         rp->cmd.p = cmd;
464         rp->cmd.count = sizeof cmd;
465         rp->data.p = data;
466         rp->data.count = 8;
467         rp->data.write = 0;
468         return SRrequest(rp);
469 }
470
471 static long
472 request(int fd, ScsiPtr *cmd, ScsiPtr *data, int *status)
473 {
474         long n, r;
475         char buf[16];
476
477         /* this was an experiment but it seems to be a good idea */
478         *status = STok;
479
480         /* send SCSI command */
481         if(write(fd, cmd->p, cmd->count) != cmd->count){
482                 fprint(2, "scsireq: write cmd: %r\n");
483                 *status = Status_SW;
484                 return -1;
485         }
486
487         /* read or write actual data */
488         werrstr("");
489         if(data->write)
490                 n = write(fd, data->p, data->count);
491         else {
492                 n = read(fd, data->p, data->count);
493                 if (n < 0)
494                         memset(data->p, 0, data->count);
495                 else if (n < data->count)
496                         memset(data->p + n, 0, data->count - n);
497         }
498         if (n != data->count && n <= 0) {
499                 if (debug)
500                         fprint(2,
501         "request: tried to %s %ld bytes of data for cmd 0x%x but got %r\n",
502                                 (data->write? "write": "read"),
503                                 data->count, cmd->p[0]);
504         } else if (n != data->count && (data->write || debug))
505                 fprint(2, "request: %s %ld of %ld bytes of actual data\n",
506                         (data->write? "wrote": "read"), n, data->count);
507
508         /* read status */
509         buf[0] = '\0';
510         r = read(fd, buf, sizeof buf-1);
511         if(exabyte && r <= 0 || !exabyte && r < 0){
512                 fprint(2, "scsireq: read status: %r\n");
513                 *status = Status_SW;
514                 return -1;
515         }
516         if (r >= 0)
517                 buf[r] = '\0';
518         *status = atoi(buf);
519         if(n < 0 && (exabyte || *status != STcheck))
520                 fprint(2, "scsireq: status 0x%2.2uX: data transfer: %r\n",
521                         *status);
522         return n;
523 }
524
525 long
526 SRrequest(ScsiReq *rp)
527 {
528         long n;
529         int status;
530
531 retry:
532         if(rp->flags&Fusb)
533                 n = umsrequest(rp->umsc, &rp->cmd, &rp->data, &status);
534         else
535                 n = request(rp->fd, &rp->cmd, &rp->data, &status);
536         switch(rp->status = status){
537
538         case STok:
539                 rp->data.count = n;
540                 break;
541
542         case STcheck:
543                 if(rp->cmd.p[0] != ScmdRsense && SRreqsense(rp) != -1)
544                         rp->status = Status_SD;
545                 if (exabyte)
546                         fprint(2, "SRrequest: STcheck, returning -1\n");
547                 return -1;
548
549         case STbusy:
550                 sleep(1000);
551                 goto retry;
552
553         default:
554                 fprint(2, "status 0x%2.2uX\n", status);
555                 return -1;
556         }
557         return n;
558 }
559
560 int
561 SRclose(ScsiReq *rp)
562 {
563         if((rp->flags & Fopen) == 0){
564                 if(debug)
565                         fprint(2, "scuzz: closing closed file\n");
566                 rp->status = Status_BADARG;
567                 return -1;
568         }
569         close(rp->fd);
570         rp->flags = 0;
571         return 0;
572 }
573
574 uint
575 mkascq(ScsiReq *r)
576 {
577         uchar *u;
578
579         u = r->sense;
580         return u[2]<<16 | u[12]<<8 | u[13];
581 }
582
583 static int
584 dirdevopen(ScsiReq *rp)
585 {
586         ulong blocks;
587         uchar data[8];
588
589         if(SRstart(rp, 1) == -1)
590                 /*
591                  * it's okay for removable media to say
592                  * "check condition: medium not present".
593                  * 3a is "medium not present".
594                  */
595                 return rp->inquiry[1] & 0x80 && (mkascq(rp) >> 8) == 0x023a?
596                         0: -1;
597         memset(data, 0, sizeof data);
598         if(SRrcapacity(rp, data) == -1)
599                 return -1;
600         rp->lbsize = GETBELONG(data+4);
601         blocks =     GETBELONG(data);
602         if(debug)
603                 fprint(2, "scuzz: dirdevopen: logical block size %lud, "
604                         "# blocks %lud\n", rp->lbsize, blocks);
605         /* some newer dev's don't support 6-byte commands */
606         if(blocks > Max24off && !force6bytecmds)
607                 rp->flags |= Frw10;
608         return 0;
609 }
610
611 static int
612 seqdevopen(ScsiReq *rp)
613 {
614         uchar mode[16], limits[6];
615
616         if(SRrblimits(rp, limits) == -1)
617                 return -1;
618         if(limits[1] == 0 && limits[2] == limits[4] && limits[3] == limits[5]){
619                 rp->flags |= Fbfixed;
620                 rp->lbsize = limits[4]<<8 | limits[5];
621                 if(debug)
622                         fprint(2, "scuzz: seqdevopen: logical block size %lud\n",
623                                 rp->lbsize);
624                 return 0;
625         }
626         /*
627          * On some older hardware the optional 10-byte
628          * modeselect command isn't implemented.
629          */
630         if (force6bytecmds)
631                 rp->flags |= Fmode6;
632         if(!(rp->flags & Fmode6)){
633                 /* try 10-byte command first */
634                 memset(mode, 0, sizeof mode);
635                 mode[3] = 0x10;         /* device-specific param. */
636                 mode[7] = 8;            /* block descriptor length */
637                 /*
638                  * exabytes can't handle this, and
639                  * modeselect(10) is optional.
640                  */
641                 if(SRmodeselect10(rp, mode, sizeof mode) != -1){
642                         rp->lbsize = 1;
643                         return 0;       /* success */
644                 }
645                 /* can't do 10-byte commands, back off to 6-byte ones */
646                 rp->flags |= Fmode6;
647         }
648
649         /* 6-byte command */
650         memset(mode, 0, sizeof mode);
651         mode[2] = 0x10;         /* device-specific param. */
652         mode[3] = 8;            /* block descriptor length */
653         /*
654          * bsd sez exabytes need this bit (NBE: no busy enable) in
655          * vendor-specific page (0), but so far we haven't needed it.
656         mode[12] |= 8;
657          */
658         if(SRmodeselect6(rp, mode, 4+8) == -1)
659                 return -1;
660         rp->lbsize = 1;
661         return 0;
662 }
663
664 static int
665 wormdevopen(ScsiReq *rp)
666 {
667         long status;
668         uchar list[MaxDirData];
669
670         if (SRstart(rp, 1) == -1 ||
671             (status = SRmodesense10(rp, Allmodepages, list, sizeof list)) == -1)
672                 return -1;
673         /* nbytes = list[0]<<8 | list[1]; */
674
675         /* # of bytes of block descriptors of 8 bytes each; not even 1? */
676         if((list[6]<<8 | list[7]) < 8)
677                 rp->lbsize = 2048;
678         else
679                 /* last 3 bytes of block 0 descriptor */
680                 rp->lbsize = GETBE24(list+13);
681         if(debug)
682                 fprint(2, "scuzz: wormdevopen: logical block size %lud\n",
683                         rp->lbsize);
684         return status;
685 }
686
687 int
688 SRopenraw(ScsiReq *rp, char *unit)
689 {
690         char name[128];
691
692         if(rp->flags & Fopen){
693                 if(debug)
694                         fprint(2, "scuzz: opening open file\n");
695                 rp->status = Status_BADARG;
696                 return -1;
697         }
698         memset(rp, 0, sizeof *rp);
699         rp->unit = unit;
700
701         sprint(name, "%s/raw", unit);
702
703         if((rp->fd = open(name, ORDWR)) == -1){
704                 rp->status = STtimeout;
705                 return -1;
706         }
707         rp->flags = Fopen;
708         return 0;
709 }
710
711 int
712 SRopen(ScsiReq *rp, char *unit)
713 {
714         if(SRopenraw(rp, unit) == -1)
715                 return -1;
716         SRready(rp);
717         if(SRinquiry(rp) >= 0){
718                 switch(rp->inquiry[0]){
719
720                 default:
721                         fprint(2, "unknown device type 0x%.2x\n", rp->inquiry[0]);
722                         rp->status = Status_SW;
723                         break;
724
725                 case 0x00:      /* Direct access (disk) */
726                 case 0x05:      /* CD-ROM */
727                 case 0x07:      /* rewriteable MO */
728                         if(dirdevopen(rp) == -1)
729                                 break;
730                         return 0;
731
732                 case 0x01:      /* Sequential eg: tape */
733                         rp->flags |= Fseqdev;
734                         if(seqdevopen(rp) == -1)
735                                 break;
736                         return 0;
737
738                 case 0x02:      /* Printer */
739                         rp->flags |= Fprintdev;
740                         return 0;
741
742                 case 0x04:      /* Worm */
743                         rp->flags |= Fwormdev;
744                         if(wormdevopen(rp) == -1)
745                                 break;
746                         return 0;
747
748                 case 0x08:      /* medium-changer */
749                         rp->flags |= Fchanger;
750                         return 0;
751                 }
752         }
753         SRclose(rp);
754         return -1;
755 }