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