libscsi.c revision 1.4
1/*	$OpenBSD: libscsi.c,v 1.4 2005/10/24 22:56:17 deraadt 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
47static struct {
48	FILE	*db_f;
49	int	db_level;
50	int	db_trunc;
51} behave;
52
53/* scsireq_reset: Reset a scsireq structure.
54 */
55scsireq_t *
56scsireq_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 */
80scsireq_t *
81scsireq_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 */
137static int
138do_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					bcopy(databuf, dest, 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
340int
341scsireq_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
353int
354scsireq_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
366int
367scsireq_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
378int
379scsireq_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 */
419static int
420next_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
585static int
586do_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
712scsireq_t *
713scsireq_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
750scsireq_t
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
787int
788scsireq_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
803int
804scsireq_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
811int
812scsireq_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
820FILE *
821scsi_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
837typedef struct scsi_assoc {
838	int code;
839	char *text;
840} scsi_assoc_t;
841
842static 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
853static char *
854scsi_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
866void
867scsi_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 */
911static u_long
912g_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 */
919static scsi_assoc_t *error_table = 0;
920
921static void
922sense_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 */
1002static void
1003scsi_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
1030static void
1031scsi_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
1049int
1050scsi_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
1102static char *debug_output;
1103
1104int
1105scsi_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
1126int
1127scsireq_enter(int fid, scsireq_t *scsireq)
1128{
1129	int ret;
1130
1131	ret = ioctl(fid, SCIOCCOMMAND, (void *)scsireq);
1132
1133	if (behave.db_f)
1134		scsi_debug(behave.db_f, ret, scsireq);
1135
1136	return ret;
1137}
1138