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