1 /* $OpenBSD: libscsi.c,v 1.3 2005/04/09 02:10:01 cloder Exp $ */
2
3 /* Copyright (c) 1994 HD Associates
4 * (contact: dufault@hda.com)
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by HD Associates
18 * 4. Neither the name of the HD Associaates nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL HD ASSOCIATES OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * $FreeBSD: scsi.c,v 1.6 1995/05/30 05:47:26 rgrimes Exp $
35 */
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <ctype.h>
39 #include <string.h>
40 #include <sys/scsiio.h>
41 #include <sys/errno.h>
42 #include <stdarg.h>
43 #include <fcntl.h>
44
45 #include "libscsi.h"
46
47 static struct {
48 FILE *db_f;
49 int db_level;
50 int db_trunc;
51 } behave;
52
53 /* scsireq_reset: Reset a scsireq structure.
54 */
55 scsireq_t *
scsireq_reset(scsireq_t * scsireq)56 scsireq_reset(scsireq_t *scsireq)
57 {
58 if (scsireq == 0)
59 return scsireq;
60
61 scsireq->flags = 0; /* info about the request status and type */
62 scsireq->timeout = 2000; /* 2 seconds */
63 bzero(scsireq->cmd, sizeof(scsireq->cmd));
64 scsireq->cmdlen = 0;
65 /* Leave scsireq->databuf alone */
66 /* Leave scsireq->datalen alone */
67 scsireq->datalen_used = 0;
68 bzero(scsireq->sense, sizeof(scsireq->sense));
69 scsireq->senselen = sizeof(scsireq->sense);
70 scsireq->senselen_used = 0;
71 scsireq->status = 0;
72 scsireq->retsts = 0;
73 scsireq->error = 0;
74
75 return scsireq;
76 }
77
78 /* scsireq_new: Allocate and initialize a new scsireq.
79 */
80 scsireq_t *
scsireq_new(void)81 scsireq_new(void)
82 {
83 scsireq_t *p = (scsireq_t *)malloc(sizeof(scsireq_t));
84
85 if (p)
86 scsireq_reset(p);
87
88 return p;
89 }
90
91 /*
92 * Decode: Decode the data section of a scsireq. This decodes
93 * trivial grammar:
94 *
95 * fields : field fields
96 * ;
97 *
98 * field : field_specifier
99 * | control
100 * ;
101 *
102 * control : 's' seek_value
103 * | 's' '+' seek_value
104 * ;
105 *
106 * seek_value : DECIMAL_NUMBER
107 * | 'v' // For indirect seek, i.e., value from the arg list
108 * ;
109 *
110 * field_specifier : type_specifier field_width
111 * | '{' NAME '}' type_specifier field_width
112 * ;
113 *
114 * field_width : DECIMAL_NUMBER
115 * ;
116 *
117 * type_specifier : 'i' // Integral types (i1, i2, i3, i4)
118 * | 'b' // Bits
119 * | 't' // Bits
120 * | 'c' // Character arrays
121 * | 'z' // Character arrays with zeroed trailing spaces
122 * ;
123 *
124 * Notes:
125 * 1. Integral types are swapped into host order.
126 * 2. Bit fields are allocated MSB to LSB to match the SCSI spec documentation.
127 * 3. 's' permits "seeking" in the string. "s+DECIMAL" seeks relative to
128 * DECIMAL; "sDECIMAL" seeks absolute to decimal.
129 * 4. 's' permits an indirect reference. "sv" or "s+v" will get the
130 * next integer value from the arg array.
131 * 5. Field names can be anything between the braces
132 *
133 * BUGS:
134 * i and b types are promoted to ints.
135 *
136 */
137 static int
do_buff_decode(u_char * databuf,size_t len,void (* arg_put)(void *,int,void *,int,char *),void * puthook,char * fmt,va_list ap)138 do_buff_decode(u_char *databuf, size_t len,
139 void (*arg_put)(void *, int , void *, int, char *),
140 void *puthook, char *fmt, va_list ap)
141 {
142 int assigned = 0;
143 int width;
144 int suppress;
145 int plus;
146 int done = 0;
147 static u_char mask[] = {0, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
148 int value;
149 u_char *base = databuf;
150 char letter;
151 char field_name[80];
152
153 # define ARG_PUT(ARG) \
154 do \
155 { \
156 if (!suppress) { \
157 if (arg_put) \
158 (*arg_put)(puthook, (letter == 't' ? 'b' : letter), \
159 (void *)((long)(ARG)), 1, field_name); \
160 else \
161 *(va_arg(ap, int *)) = (ARG); \
162 assigned++; \
163 } \
164 field_name[0] = 0; \
165 suppress = 0; \
166 } while (0)
167
168 u_char bits = 0; /* For bit fields */
169 int shift = 0; /* Bits already shifted out */
170 suppress = 0;
171 field_name[0] = 0;
172
173 while (!done) {
174 switch (letter = *fmt) {
175 case ' ': /* White space */
176 case '\t':
177 case '\r':
178 case '\n':
179 case '\f':
180 fmt++;
181 break;
182
183 case '#': /* Comment */
184 while (*fmt && (*fmt != '\n'))
185 fmt++;
186 if (fmt)
187 fmt++; /* Skip '\n' */
188 break;
189
190 case '*': /* Suppress assignment */
191 fmt++;
192 suppress = 1;
193 break;
194
195 case '{': /* Field Name */
196 {
197 int i = 0;
198 fmt++; /* Skip '{' */
199 while (*fmt && (*fmt != '}')) {
200 if (i < sizeof(field_name))
201 field_name[i++] = *fmt;
202
203 fmt++;
204 }
205 if (fmt)
206 fmt++; /* Skip '}' */
207 field_name[i] = 0;
208 }
209 break;
210
211 case 't': /* Bit (field) */
212 case 'b': /* Bits */
213 fmt++;
214 width = strtol(fmt, &fmt, 10);
215 if (width > 8)
216 done = 1;
217 else {
218 if (shift <= 0) {
219 bits = *databuf++;
220 shift = 8;
221 }
222 value = (bits >> (shift - width)) & mask[width];
223
224 #if 0
225 printf("shift %2d bits %02x value %02x width %2d mask %02x\n",
226 shift, bits, value, width, mask[width]);
227 #endif
228
229 ARG_PUT(value);
230
231 shift -= width;
232 }
233
234 break;
235
236 case 'i': /* Integral values */
237 shift = 0;
238 fmt++;
239 width = strtol(fmt, &fmt, 10);
240 switch (width) {
241 case 1:
242 ARG_PUT(*databuf);
243 databuf++;
244 break;
245
246 case 2:
247 ARG_PUT((*databuf) << 8 | *(databuf + 1));
248 databuf += 2;
249 break;
250
251 case 3:
252 ARG_PUT(
253 (*databuf) << 16 |
254 (*(databuf + 1)) << 8 |
255 *(databuf + 2));
256 databuf += 3;
257 break;
258
259 case 4:
260 ARG_PUT(
261 (*databuf) << 24 |
262 (*(databuf + 1)) << 16 |
263 (*(databuf + 2)) << 8 |
264 *(databuf + 3));
265 databuf += 4;
266 break;
267
268 default:
269 done = 1;
270 }
271
272 break;
273
274 case 'c': /* Characters (i.e., not swapped) */
275 case 'z': /* Characters with zeroed trailing spaces */
276 shift = 0;
277 fmt++;
278 width = strtol(fmt, &fmt, 10);
279 if (!suppress) {
280 if (arg_put)
281 (*arg_put)(puthook, (letter == 't' ? 'b' : letter),
282 databuf, width, field_name);
283 else {
284 char *dest;
285 dest = va_arg(ap, char *);
286 memmove(dest, databuf, width);
287 if (letter == 'z') {
288 char *p;
289 for (p = dest + width - 1;
290 (p >= (char *)dest) && (*p == ' '); p--)
291 *p = 0;
292 }
293 }
294 assigned++;
295 }
296 databuf += width;
297 field_name[0] = 0;
298 suppress = 0;
299 break;
300
301 case 's': /* Seek */
302 shift = 0;
303 fmt++;
304 if (*fmt == '+') {
305 plus = 1;
306 fmt++;
307 } else
308 plus = 0;
309
310 if (tolower(*fmt) == 'v') {
311 /* You can't suppress a seek value. You also
312 * can't have a variable seek when you are using
313 * "arg_put".
314 */
315 width = (arg_put) ? 0 : va_arg(ap, int);
316 fmt++;
317 } else
318 width = strtol(fmt, &fmt, 10);
319
320 if (plus)
321 databuf += width; /* Relative seek */
322 else
323 databuf = base + width; /* Absolute seek */
324
325 break;
326
327 case 0:
328 done = 1;
329 break;
330
331 default:
332 fprintf(stderr, "Unknown letter in format: %c\n", letter);
333 fmt++;
334 }
335 }
336
337 return assigned;
338 }
339
340 int
scsireq_decode(scsireq_t * scsireq,char * fmt,...)341 scsireq_decode(scsireq_t *scsireq, char *fmt, ...)
342 {
343 va_list ap;
344 int ret;
345
346 va_start (ap, fmt);
347 ret = do_buff_decode(scsireq->databuf, (size_t)scsireq->datalen,
348 0, 0, fmt, ap);
349 va_end (ap);
350 return (ret);
351 }
352
353 int
scsireq_decode_visit(scsireq_t * scsireq,char * fmt,void (* arg_put)(void *,int,void *,int,char *),void * puthook)354 scsireq_decode_visit(scsireq_t *scsireq, char *fmt,
355 void (*arg_put)(void *, int , void *, int, char *), void *puthook)
356 {
357 va_list ap;
358 int ret;
359
360 ret = do_buff_decode(scsireq->databuf, (size_t)scsireq->datalen,
361 arg_put, puthook, fmt, ap);
362 va_end (ap);
363 return (ret);
364 }
365
366 int
scsireq_buff_decode(u_char * buff,size_t len,char * fmt,...)367 scsireq_buff_decode(u_char *buff, size_t len, char *fmt, ...)
368 {
369 va_list ap;
370 int ret;
371
372 va_start (ap, fmt);
373 ret = do_buff_decode(buff, len, 0, 0, fmt, ap);
374 va_end (ap);
375 return (ret);
376 }
377
378 int
scsireq_buff_decode_visit(u_char * buff,size_t len,char * fmt,void (* arg_put)(void *,int,void *,int,char *),void * puthook)379 scsireq_buff_decode_visit(u_char *buff, size_t len, char *fmt,
380 void (*arg_put)(void *, int, void *, int, char *), void *puthook)
381 {
382 va_list ap;
383
384 /* XXX */
385 return do_buff_decode(buff, len, arg_put, puthook, fmt, ap);
386 }
387
388 /* next_field: Return the next field in a command specifier. This
389 * builds up a SCSI command using this trivial grammar:
390 *
391 * fields : field fields
392 * ;
393 *
394 * field : value
395 * | value ':' field_width
396 * ;
397 *
398 * field_width : digit
399 * | 'i' digit // i2 = 2 byte integer, i3 = 3 byte integer etc.
400 * ;
401 *
402 * value : HEX_NUMBER
403 * | 'v' // For indirection.
404 * ;
405 *
406 * Notes:
407 * Bit fields are specified MSB first to match the SCSI spec.
408 *
409 * Examples:
410 * TUR: "0 0 0 0 0 0"
411 * WRITE BUFFER: "38 v:3 0:2 0:3 v v:i3 v:i3 0", mode, buffer_id, list_length
412 *
413 * The function returns the value:
414 * 0: For reached end, with error_p set if an error was found
415 * 1: For valid stuff setup
416 * 2: For "v" was entered as the value (implies use varargs)
417 *
418 */
419 static int
next_field(char ** pp,char * fmt,int * width_p,int * value_p,char * name,int n_name,int * error_p,int * suppress_p)420 next_field(char **pp, char *fmt, int *width_p, int *value_p, char *name,
421 int n_name, int *error_p, int *suppress_p)
422 {
423 char *p = *pp;
424
425 int something = 0;
426
427 enum { BETWEEN_FIELDS, START_FIELD, GET_FIELD, DONE } state;
428
429 int value = 0;
430 int field_size; /* Default to byte field type... */
431 int field_width; /* 1 byte wide */
432 int is_error = 0;
433 int suppress = 0;
434
435 field_size = 8; /* Default to byte field type... */
436 *fmt = 'i';
437 field_width = 1; /* 1 byte wide */
438 if (name)
439 *name = 0;
440
441 state = BETWEEN_FIELDS;
442
443 while (state != DONE)
444 switch (state)
445 {
446 case BETWEEN_FIELDS:
447 if (*p == 0)
448 state = DONE;
449 else if (isspace(*p))
450 p++;
451 else if (*p == '#') {
452 while (*p && *p != '\n')
453 p++;
454
455 if (p)
456 p++;
457 } else if (*p == '{') {
458 int i = 0;
459
460 p++;
461
462 while (*p && *p != '}') {
463 if (name && i < n_name) {
464 name[i] = *p;
465 i++;
466 }
467 p++;
468 }
469
470 if (name && i < n_name)
471 name[i] = 0;
472
473 if (*p == '}')
474 p++;
475 } else if (*p == '*') {
476 p++;
477 suppress = 1;
478 } else if (isxdigit(*p)) {
479 something = 1;
480 value = strtol(p, &p, 16);
481 state = START_FIELD;
482 } else if (tolower(*p) == 'v') {
483 p++;
484 something = 2;
485 value = *value_p;
486 state = START_FIELD;
487 }
488 /* try to work without the 'v' */
489 else if (tolower(*p) == 'i') {
490 something = 2;
491 value = *value_p;
492 p++;
493
494 *fmt = 'i';
495 field_size = 8;
496 field_width = strtol(p, &p, 10);
497 state = DONE;
498 } else if (tolower(*p) == 't') {
499 /* XXX: B can't work: Sees the 'b'
500 * as a hex digit in "isxdigit".
501 * try "t" for bit field.
502 */
503 something = 2;
504 value = *value_p;
505 p++;
506
507 *fmt = 'b';
508 field_size = 1;
509 field_width = strtol(p, &p, 10);
510 state = DONE;
511 } else if (tolower(*p) == 's') { /* Seek */
512 *fmt = 's';
513 p++;
514 if (tolower(*p) == 'v') {
515 p++;
516 something = 2;
517 value = *value_p;
518 } else {
519 something = 1;
520 value = strtol(p, &p, 0);
521 }
522 state = DONE;
523 } else {
524 fprintf(stderr, "Invalid starting character: %c\n", *p);
525 is_error = 1;
526 state = DONE;
527 }
528 break;
529
530 case START_FIELD:
531 if (*p == ':') {
532 p++;
533 field_size = 1; /* Default to bits when specified */
534 state = GET_FIELD;
535 } else
536 state = DONE;
537 break;
538
539 case GET_FIELD:
540 if (isdigit(*p)) {
541 *fmt = 'b';
542 field_size = 1;
543 field_width = strtol(p, &p, 10);
544 state = DONE;
545 } else if (*p == 'i') { /* Integral (bytes) */
546 p++;
547
548 *fmt = 'i';
549 field_size = 8;
550 field_width = strtol(p, &p, 10);
551 state = DONE;
552 } else if (*p == 'b') { /* Bits */
553 p++;
554
555 *fmt = 'b';
556 field_size = 1;
557 field_width = strtol(p, &p, 10);
558 state = DONE;
559 } else {
560 fprintf(stderr, "Invalid startfield %c (%02x)\n",
561 *p, *p);
562 is_error = 1;
563 state = DONE;
564 }
565 break;
566
567 case DONE:
568 break;
569 }
570
571 if (is_error) {
572 *error_p = 1;
573 return 0;
574 }
575
576 *error_p = 0;
577 *pp = p;
578 *width_p = field_width * field_size;
579 *value_p = value;
580 *suppress_p = suppress;
581
582 return something;
583 }
584
585 static int
do_encode(u_char * buff,size_t vec_max,size_t * used,int (* arg_get)(void *,char *),void * gethook,char * fmt,va_list ap)586 do_encode(u_char *buff, size_t vec_max, size_t *used,
587 int (*arg_get)(void *, char *),
588 void *gethook, char *fmt, va_list ap)
589 {
590 int ind;
591 int shift;
592 u_char val;
593 int ret;
594 int width, value, error, suppress;
595 char c;
596 int encoded = 0;
597 char field_name[80];
598
599 ind = 0;
600 shift = 0;
601 val = 0;
602
603 while ((ret = next_field(&fmt, &c, &width, &value, field_name,
604 sizeof(field_name), &error, &suppress)))
605 {
606 encoded++;
607
608 if (ret == 2) {
609 if (suppress)
610 value = 0;
611 else
612 value = arg_get ? (*arg_get)(gethook, field_name) : va_arg(ap, int);
613 }
614
615 #if 0
616 printf(
617 "do_encode: ret %d fmt %c width %d value %d name \"%s\""
618 "error %d suppress %d\n",
619 ret, c, width, value, field_name, error, suppress);
620 #endif
621
622 if (c == 's') /* Absolute seek */
623 {
624 ind = value;
625 continue;
626 }
627
628 if (width < 8) /* A width of < 8 is a bit field. */
629 {
630
631 /* This is a bit field. We start with the high bits
632 * so it reads the same as the SCSI spec.
633 */
634
635 shift += width;
636
637 val |= (value << (8 - shift));
638
639 if (shift == 8) {
640 if (ind < vec_max) {
641 buff[ind++] = val;
642 val = 0;
643 }
644 shift = 0;
645 }
646 } else {
647 if (shift) {
648 if (ind < vec_max) {
649 buff[ind++] = val;
650 val = 0;
651 }
652 shift = 0;
653 }
654 switch (width)
655 {
656 case 8: /* 1 byte integer */
657 if (ind < vec_max)
658 buff[ind++] = value;
659 break;
660
661 case 16: /* 2 byte integer */
662 if (ind < vec_max - 2 + 1) {
663 buff[ind++] = value >> 8;
664 buff[ind++] = value;
665 }
666 break;
667
668 case 24: /* 3 byte integer */
669 if (ind < vec_max - 3 + 1) {
670 buff[ind++] = value >> 16;
671 buff[ind++] = value >> 8;
672 buff[ind++] = value;
673 }
674 break;
675
676 case 32: /* 4 byte integer */
677 if (ind < vec_max - 4 + 1) {
678 buff[ind++] = value >> 24;
679 buff[ind++] = value >> 16;
680 buff[ind++] = value >> 8;
681 buff[ind++] = value;
682 }
683 break;
684
685 default:
686 fprintf(stderr, "do_encode: Illegal width\n");
687 break;
688 }
689 }
690 }
691
692 /* Flush out any remaining bits */
693 if (shift && ind < vec_max) {
694 buff[ind++] = val;
695 val = 0;
696 }
697
698
699 if (used)
700 *used = ind;
701
702 if (error)
703 return -1;
704
705 return encoded;
706 }
707
708 /* XXX: Should be a constant in scsiio.h
709 */
710 #define CMD_BUFLEN 16
711
712 scsireq_t *
scsireq_build(scsireq_t * scsireq,u_long datalen,caddr_t databuf,u_long flags,char * cmd_spec,...)713 scsireq_build(scsireq_t *scsireq, u_long datalen, caddr_t databuf,
714 u_long flags, char *cmd_spec, ...)
715 {
716 size_t cmdlen;
717 va_list ap;
718
719 if (scsireq == 0)
720 return 0;
721
722 scsireq_reset(scsireq);
723
724 if (databuf) {
725 scsireq->databuf = databuf;
726 scsireq->datalen = datalen;
727 scsireq->flags = flags;
728 }
729 else if (datalen) {
730 /* XXX: Good way to get a memory leak. Perhaps this should be
731 * removed.
732 */
733 if ( (scsireq->databuf = malloc(datalen)) == 0)
734 return 0;
735
736 scsireq->datalen = datalen;
737 scsireq->flags = flags;
738 }
739
740 va_start(ap, cmd_spec);
741
742 if (do_encode(scsireq->cmd, CMD_BUFLEN, &cmdlen, 0, 0, cmd_spec, ap) == -1)
743 return 0;
744 va_end (ap);
745
746 scsireq->cmdlen = cmdlen;
747 return scsireq;
748 }
749
750 scsireq_t
scsireq_build_visit(scsireq_t * scsireq,u_long datalen,caddr_t databuf,u_long flags,char * cmd_spec,int (* arg_get)(void * hook,char * field_name),void * gethook)751 *scsireq_build_visit(scsireq_t *scsireq, u_long datalen, caddr_t databuf,
752 u_long flags, char *cmd_spec,
753 int (*arg_get)(void *hook, char *field_name), void *gethook)
754 {
755 size_t cmdlen;
756 va_list ap;
757
758 if (scsireq == 0)
759 return 0;
760
761 scsireq_reset(scsireq);
762
763 if (databuf) {
764 scsireq->databuf = databuf;
765 scsireq->datalen = datalen;
766 scsireq->flags = flags;
767 } else if (datalen) {
768 /* XXX: Good way to get a memory leak. Perhaps this should be
769 * removed.
770 */
771 if ( (scsireq->databuf = malloc(datalen)) == 0)
772 return 0;
773
774 scsireq->datalen = datalen;
775 scsireq->flags = flags;
776 }
777
778 if (do_encode(scsireq->cmd, CMD_BUFLEN, &cmdlen, arg_get, gethook,
779 cmd_spec, ap) == -1)
780 return 0;
781
782 scsireq->cmdlen = cmdlen;
783
784 return scsireq;
785 }
786
787 int
scsireq_encode(scsireq_t * scsireq,char * fmt,...)788 scsireq_encode(scsireq_t *scsireq, char *fmt, ...)
789 {
790 va_list ap;
791 int ret;
792
793 if (scsireq == 0)
794 return 0;
795
796 va_start(ap, fmt);
797
798 ret = do_encode(scsireq->databuf, scsireq->datalen, 0, 0, 0, fmt, ap);
799 va_end (ap);
800 return (ret);
801 }
802
803 int
scsireq_buff_encode_visit(u_char * buff,size_t len,char * fmt,int (* arg_get)(void * hook,char * field_name),void * gethook)804 scsireq_buff_encode_visit(u_char *buff, size_t len, char *fmt,
805 int (*arg_get)(void *hook, char *field_name), void *gethook)
806 {
807 va_list ap;
808 return do_encode(buff, len, 0, arg_get, gethook, fmt, ap);
809 }
810
811 int
scsireq_encode_visit(scsireq_t * scsireq,char * fmt,int (* arg_get)(void * hook,char * field_name),void * gethook)812 scsireq_encode_visit(scsireq_t *scsireq, char *fmt,
813 int (*arg_get)(void *hook, char *field_name), void *gethook)
814 {
815 va_list ap;
816 return do_encode(scsireq->databuf, scsireq->datalen, 0,
817 arg_get, gethook, fmt, ap);
818 }
819
820 FILE *
scsi_debug_output(char * s)821 scsi_debug_output(char *s)
822 {
823 if (s == 0)
824 behave.db_f = 0;
825 else {
826 behave.db_f = fopen(s, "w");
827
828 if (behave.db_f == 0)
829 behave.db_f = stderr;
830 }
831
832 return behave.db_f;
833 }
834
835 #define SCSI_TRUNCATE -1
836
837 typedef struct scsi_assoc {
838 int code;
839 char *text;
840 } scsi_assoc_t;
841
842 static scsi_assoc_t retsts[] =
843 {
844 { SCCMD_OK, "No error" },
845 { SCCMD_TIMEOUT, "Command Timeout" },
846 { SCCMD_BUSY, "Busy" },
847 { SCCMD_SENSE, "Sense Returned" },
848 { SCCMD_UNKNOWN, "Unknown return status" },
849
850 { 0, 0 }
851 };
852
853 static char *
scsi_assoc_text(int code,scsi_assoc_t * tab)854 scsi_assoc_text(int code, scsi_assoc_t *tab)
855 {
856 while (tab->text) {
857 if (tab->code == code)
858 return tab->text;
859
860 tab++;
861 }
862
863 return "Unknown code";
864 }
865
866 void
scsi_dump(FILE * f,char * text,u_char * p,int req,int got,int dump_print)867 scsi_dump(FILE *f, char *text, u_char *p, int req, int got, int dump_print)
868 {
869 int i;
870 int trunc = 0;
871
872 if (f == 0 || req == 0)
873 return;
874
875 fprintf(f, "%s (%d of %d):\n", text, got, req);
876
877 if (behave.db_trunc != -1 && got > behave.db_trunc) {
878 trunc = 1;
879 got = behave.db_trunc;
880 }
881
882 for (i = 0; i < got; i++) {
883 fprintf(f, "%02x", p[i]);
884
885 putc(' ', f);
886
887 if ((i % 16) == 15 || i == got - 1) {
888 int j;
889 if (dump_print) {
890 fprintf(f, " # ");
891 for (j = i - 15; j <= i; j++)
892 putc((isprint(p[j]) ? p[j] : '.'), f);
893
894 putc('\n', f);
895 } else
896 putc('\n', f);
897 }
898 }
899
900 fprintf(f, "%s", (trunc) ? "(truncated)...\n" : "\n");
901 }
902
903 /* XXX: sense_7x_dump and scsi_sense dump was just sort of
904 * grabbed out of the old ds
905 * library and not really merged in carefully. It should use the
906 * new buffer decoding stuff.
907 */
908
909 /* Get unsigned long.
910 */
911 static u_long
g_u_long(u_char * s)912 g_u_long(u_char *s)
913 {
914 return (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
915 }
916
917 /* In the old software you could patch in a special error table:
918 */
919 static scsi_assoc_t *error_table = 0;
920
921 static void
sense_7x_dump(FILE * f,scsireq_t * scsireq)922 sense_7x_dump(FILE *f, scsireq_t *scsireq)
923 {
924 int code;
925 u_char *s = (u_char *)scsireq->sense;
926 int valid = (*s) & 0x80;
927 u_long val;
928
929 static scsi_assoc_t sense[] = {
930 { 0, "No sense" },
931 { 1, "Recovered error" },
932 { 2, "Not Ready" },
933 { 3, "Medium error" },
934 { 4, "Hardware error" },
935 { 5, "Illegal request" },
936 { 6, "Unit attention" },
937 { 7, "Data protect" },
938 { 8, "Blank check" },
939 { 9, "Vendor specific" },
940 { 0xa, "Copy aborted" },
941 { 0xb, "Aborted Command" },
942 { 0xc, "Equal" },
943 { 0xd, "Volume overflow" },
944 { 0xe, "Miscompare" },
945 { 0, 0 },
946 };
947
948 static scsi_assoc_t code_tab[] = {
949 {0x70, "current errors"},
950 {0x71, "deferred errors"},
951 };
952
953 fprintf(f, "Error code is \"%s\"\n", scsi_assoc_text(s[0]&0x7F, code_tab));
954 fprintf(f, "Segment number is %02x\n", s[1]);
955
956 if (s[2] & 0x20)
957 fprintf(f, "Incorrect Length Indicator is set.\n");
958
959 fprintf(f, "Sense key is \"%s\"\n", scsi_assoc_text(s[2] & 0x7, sense));
960
961 val = g_u_long(s + 3);
962 fprintf(f, "The Information field is%s %08lx (%ld).\n",
963 valid ? "" : " not valid but contains", (long)val, (long)val);
964
965 val = g_u_long(s + 8);
966 fprintf(f, "The Command Specific Information field is %08lx (%ld).\n",
967 (long)val, (long)val);
968
969 fprintf(f, "Additional sense code: %02x\n", s[12]);
970 fprintf(f, "Additional sense code qualifier: %02x\n", s[13]);
971
972 code = (s[12] << 8) | s[13];
973
974 if (error_table)
975 fprintf(f, "%s\n", scsi_assoc_text(code, error_table));
976
977 if (s[15] & 0x80) {
978 if ((s[2] & 0x7) == 0x05) /* Illegal request */
979 {
980 int byte;
981 u_char value, bit;
982 int bad_par = ((s[15] & 0x40) == 0);
983 fprintf(f, "Illegal value in the %s.\n",
984 (bad_par ? "parameter list" : "command descriptor block"));
985 byte = ((s[16] << 8) | s[17]);
986 value = bad_par ? (u_char)scsireq->databuf[byte] : (u_char)scsireq->cmd[byte];
987 bit = s[15] & 0x7;
988 if (s[15] & 0x08)
989 fprintf(f, "Bit %d of byte %d (value %02x) is illegal.\n",
990 bit, byte, value);
991 else
992 fprintf(f, "Byte %d (value %02x) is illegal.\n", byte, value);
993 } else {
994 fprintf(f, "Sense Key Specific (valid but not illegal request):\n");
995 fprintf(f, "%02x %02x %02x\n", s[15] & 0x7f, s[16], s[17]);
996 }
997 }
998 }
999
1000 /* scsi_sense_dump: Dump the sense portion of the scsireq structure.
1001 */
1002 static void
scsi_sense_dump(FILE * f,scsireq_t * scsireq)1003 scsi_sense_dump(FILE *f, scsireq_t *scsireq)
1004 {
1005 u_char *s = (u_char *)scsireq->sense;
1006 int code = (*s) & 0x7f;
1007
1008 if (scsireq->senselen_used == 0) {
1009 fprintf(f, "No sense sent.\n");
1010 return;
1011 }
1012
1013 #if 0
1014 if (!valid)
1015 fprintf(f, "The sense data is not valid.\n");
1016 #endif
1017
1018 switch (code) {
1019 case 0x70:
1020 case 0x71:
1021 sense_7x_dump(f, scsireq);
1022 break;
1023
1024 default:
1025 fprintf(f, "No sense dump for error code %02x.\n", code);
1026 }
1027 scsi_dump(f, "sense", s, scsireq->senselen, scsireq->senselen_used, 0);
1028 }
1029
1030 static void
scsi_retsts_dump(FILE * f,scsireq_t * scsireq)1031 scsi_retsts_dump(FILE *f, scsireq_t *scsireq)
1032 {
1033 if (scsireq->retsts == 0)
1034 return;
1035
1036 fprintf(f, "return status %d (%s)",
1037 scsireq->retsts, scsi_assoc_text(scsireq->retsts, retsts));
1038
1039 switch (scsireq->retsts) {
1040 case SCCMD_TIMEOUT:
1041 fprintf(f, " after %ld ms", scsireq->timeout);
1042 break;
1043
1044 default:
1045 break;
1046 }
1047 }
1048
1049 int
scsi_debug(FILE * f,int ret,scsireq_t * scsireq)1050 scsi_debug(FILE *f, int ret, scsireq_t *scsireq)
1051 {
1052 char *d;
1053 if (f == 0)
1054 return 0;
1055
1056 fprintf(f, "SCIOCCOMMAND ioctl");
1057
1058 if (ret == 0)
1059 fprintf(f, ": Command accepted.");
1060 else {
1061 if (ret != -1)
1062 fprintf(f, ", return value %d?", ret);
1063
1064 if (errno) {
1065 fprintf(f, ": %s", strerror(errno));
1066 errno = 0;
1067 }
1068 }
1069
1070 fputc('\n', f);
1071
1072 if (ret == 0 && (scsireq->status || scsireq->retsts || behave.db_level))
1073 {
1074 scsi_retsts_dump(f, scsireq);
1075
1076 if (scsireq->status)
1077 fprintf(f, " host adapter status %d\n", scsireq->status);
1078
1079 if (scsireq->flags & SCCMD_READ)
1080 d = "Data in";
1081 else if (scsireq->flags & SCCMD_WRITE)
1082 d = "Data out";
1083 else
1084 d = "No data transfer?";
1085
1086 if (scsireq->cmdlen == 0)
1087 fprintf(f, "Zero length command????\n");
1088
1089 scsi_dump(f, "Command out",
1090 (u_char *)scsireq->cmd, scsireq->cmdlen, scsireq->cmdlen, 0);
1091 scsi_dump(f, d,
1092 (u_char *)scsireq->databuf, scsireq->datalen,
1093 scsireq->datalen_used, 1);
1094 scsi_sense_dump(f, scsireq);
1095 }
1096
1097 fflush(f);
1098
1099 return ret;
1100 }
1101
1102 static char *debug_output;
1103
1104 int
scsi_open(const char * path,int flags)1105 scsi_open(const char *path, int flags)
1106 {
1107 int fd = open(path, flags);
1108
1109 if (fd != -1) {
1110 char *p;
1111 debug_output = getenv("SU_DEBUG_OUTPUT");
1112 (void)scsi_debug_output(debug_output);
1113
1114 if ((p = getenv("SU_DEBUG_LEVEL")))
1115 sscanf(p, "%d", &behave.db_level);
1116
1117 if ((p = getenv("SU_DEBUG_TRUNCATE")))
1118 sscanf(p, "%d", &behave.db_trunc);
1119 else
1120 behave.db_trunc = SCSI_TRUNCATE;
1121 }
1122
1123 return fd;
1124 }
1125
1126 int
scsireq_enter(int fid,scsireq_t * scsireq)1127 scsireq_enter(int fid, scsireq_t *scsireq)
1128 {
1129 int ret;
1130
1131 if (scsireq == 0)
1132 return EFAULT;
1133
1134 ret = ioctl(fid, SCIOCCOMMAND, (void *)scsireq);
1135
1136 if (behave.db_f)
1137 scsi_debug(behave.db_f, ret, scsireq);
1138
1139 return ret;
1140 }
1141