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