GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/scsi/libscsi.c Lines: 0 421 0.0 %
Date: 2016-12-06 Branches: 0 306 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: libscsi.c,v 1.11 2016/01/28 17:26:10 gsoares 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 <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 *
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 *
81
scsireq_new(void)
82
{
83
	scsireq_t *p = 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
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)-1)
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((unsigned char)*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
341
scsireq_decode_visit(scsireq_t *scsireq, char *fmt,
342
    void (*arg_put)(void *, int , void *, int, char *), void *puthook)
343
{
344
	va_list ap;
345
	int ret;
346
347
	ret = do_buff_decode(scsireq->databuf, (size_t)scsireq->datalen,
348
	    arg_put, puthook, fmt, ap);
349
	va_end (ap);
350
	return (ret);
351
}
352
353
int
354
scsireq_buff_decode_visit(u_char *buff, size_t len, char *fmt,
355
    void (*arg_put)(void *, int, void *, int, char *), void *puthook)
356
{
357
	va_list ap;
358
359
	/* XXX */
360
	return do_buff_decode(buff, len, arg_put, puthook, fmt, ap);
361
}
362
363
/* next_field: Return the next field in a command specifier.  This
364
 * builds up a SCSI command using this trivial grammar:
365
 *
366
 * fields : field fields
367
 *        ;
368
 *
369
 * field : value
370
 *       | value ':' field_width
371
 *       ;
372
 *
373
 * field_width : digit
374
 *       | 'i' digit		// i2 = 2 byte integer, i3 = 3 byte integer etc.
375
 *       ;
376
 *
377
 * value : HEX_NUMBER
378
 *       | 'v'				// For indirection.
379
 *       ;
380
 *
381
 * Notes:
382
 *  Bit fields are specified MSB first to match the SCSI spec.
383
 *
384
 * Examples:
385
 *  TUR: "0 0 0 0 0 0"
386
 *  WRITE BUFFER: "38 v:3 0:2 0:3 v v:i3 v:i3 0", mode, buffer_id, list_length
387
 *
388
 * The function returns the value:
389
 *  0: For reached end, with error_p set if an error was found
390
 *  1: For valid stuff setup
391
 *  2: For "v" was entered as the value (implies use varargs)
392
 *
393
 */
394
static int
395
next_field(char **pp, char *fmt, int *width_p, int *value_p, char *name,
396
    int n_name, int *error_p, int *suppress_p)
397
{
398
	char *p = *pp;
399
400
	int something = 0;
401
402
	enum { BETWEEN_FIELDS, START_FIELD, GET_FIELD, DONE } state;
403
404
	int value = 0;
405
	int field_size;		/* Default to byte field type... */
406
	int field_width;	/* 1 byte wide */
407
	int is_error = 0;
408
	int suppress = 0;
409
410
	field_size = 8;		/* Default to byte field type... */
411
	*fmt = 'i';
412
	field_width = 1;	/* 1 byte wide */
413
	if (name)
414
		*name = 0;
415
416
	state = BETWEEN_FIELDS;
417
418
	while (state != DONE)
419
		switch (state)
420
		{
421
		case BETWEEN_FIELDS:
422
			if (*p == 0)
423
				state = DONE;
424
			else if (isspace((unsigned char)*p))
425
				p++;
426
			else if (*p == '#') {
427
				while (*p && *p != '\n')
428
					p++;
429
430
				if (p)
431
					p++;
432
				} else if (*p == '{') {
433
					int i = 0;
434
435
					p++;
436
437
					while (*p && *p != '}') {
438
						if (name && i < n_name) {
439
							name[i] = *p;
440
							i++;
441
						}
442
						p++;
443
					}
444
445
					if (name && i < n_name)
446
						name[i] = 0;
447
448
					if (*p == '}')
449
						p++;
450
				} else if (*p == '*') {
451
					p++;
452
					suppress = 1;
453
				} else if (isxdigit((unsigned char)*p)) {
454
					something = 1;
455
					value = strtol(p, &p, 16);
456
					state = START_FIELD;
457
				} else if (tolower((unsigned char)*p) == 'v') {
458
					p++;
459
					something = 2;
460
					value = *value_p;
461
					state = START_FIELD;
462
				}
463
				/* try to work without the 'v' */
464
				else if (tolower((unsigned char)*p) == 'i') {
465
					something = 2;
466
					value = *value_p;
467
					p++;
468
469
					*fmt = 'i';
470
					field_size = 8;
471
					field_width = strtol(p, &p, 10);
472
					state = DONE;
473
				} else if (tolower((unsigned char)*p) == 't') {
474
					/* XXX: B can't work: Sees the 'b'
475
					 * as a hex digit in "isxdigit".
476
					 * try "t" for bit field.
477
					 */
478
					something = 2;
479
					value = *value_p;
480
					p++;
481
482
					*fmt = 'b';
483
					field_size = 1;
484
					field_width = strtol(p, &p, 10);
485
					state = DONE;
486
				} else if (tolower((unsigned char)*p) == 's') { /* Seek */
487
					*fmt = 's';
488
					p++;
489
					if (tolower((unsigned char)*p) == 'v') {
490
						p++;
491
						something = 2;
492
						value = *value_p;
493
					} else {
494
						something = 1;
495
						value = strtol(p, &p, 0);
496
					}
497
					state = DONE;
498
				} else {
499
					fprintf(stderr, "Invalid starting character: %c\n", *p);
500
					is_error = 1;
501
					state = DONE;
502
				}
503
			break;
504
505
		case START_FIELD:
506
			if (*p == ':') {
507
				p++;
508
				field_size = 1;		/* Default to bits when specified */
509
				state = GET_FIELD;
510
			} else
511
				state = DONE;
512
			break;
513
514
		case GET_FIELD:
515
			if (isdigit((unsigned char)*p)) {
516
				*fmt = 'b';
517
				field_size = 1;
518
				field_width = strtol(p, &p, 10);
519
				state = DONE;
520
			} else if (*p == 'i') {	/* Integral (bytes) */
521
				p++;
522
523
				*fmt = 'i';
524
				field_size = 8;
525
				field_width = strtol(p, &p, 10);
526
				state = DONE;
527
			} else if (*p == 'b') {	/* Bits */
528
				p++;
529
530
				*fmt = 'b';
531
				field_size = 1;
532
				field_width = strtol(p, &p, 10);
533
				state = DONE;
534
			} else {
535
				fprintf(stderr, "Invalid startfield %c (%02x)\n",
536
				    *p, *p);
537
				is_error = 1;
538
				state = DONE;
539
			}
540
			break;
541
542
		case DONE:
543
			break;
544
		}
545
546
	if (is_error) {
547
		*error_p = 1;
548
		return 0;
549
	}
550
551
	*error_p = 0;
552
	*pp = p;
553
	*width_p = field_width * field_size;
554
	*value_p = value;
555
	*suppress_p = suppress;
556
557
	return something;
558
}
559
560
static int
561
do_encode(u_char *buff, size_t vec_max, size_t *used,
562
    int (*arg_get)(void *, char *),
563
    void *gethook, char *fmt, va_list ap)
564
{
565
	int ind;
566
	int shift;
567
	u_char val;
568
	int ret;
569
	int width, value, error, suppress;
570
	char c;
571
	int encoded = 0;
572
	char field_name[80];
573
574
	ind = 0;
575
	shift = 0;
576
	val = 0;
577
578
 	while ((ret = next_field(&fmt, &c, &width, &value, field_name,
579
	    sizeof(field_name), &error, &suppress)))
580
	{
581
		encoded++;
582
583
		if (ret == 2) {
584
			if (suppress)
585
				value = 0;
586
			else
587
				value = arg_get ? (*arg_get)(gethook, field_name) : va_arg(ap, int);
588
		}
589
590
#if 0
591
		printf(
592
		    "do_encode: ret %d fmt %c width %d value %d name \"%s\""
593
		    "error %d suppress %d\n",
594
		    ret, c, width, value, field_name, error, suppress);
595
#endif
596
597
		if (c == 's')	/* Absolute seek */
598
		{
599
			ind = value;
600
			continue;
601
		}
602
603
		if (width < 8)	/* A width of < 8 is a bit field. */
604
		{
605
606
			/* This is a bit field.  We start with the high bits
607
			 * so it reads the same as the SCSI spec.
608
			 */
609
610
			shift += width;
611
612
			val |= (value << (8 - shift));
613
614
			if (shift == 8) {
615
				if (ind < vec_max) {
616
					buff[ind++] = val;
617
					val = 0;
618
				}
619
				shift = 0;
620
			}
621
		} else {
622
			if (shift) {
623
				if (ind < vec_max) {
624
					buff[ind++] = val;
625
					val = 0;
626
				}
627
				shift = 0;
628
			}
629
			switch (width)
630
			{
631
			case 8:		/* 1 byte integer */
632
				if (ind < vec_max)
633
					buff[ind++] = value;
634
				break;
635
636
			case 16:	/* 2 byte integer */
637
				if (ind < vec_max - 2 + 1) {
638
					buff[ind++] = value >> 8;
639
					buff[ind++] = value;
640
				}
641
				break;
642
643
			case 24:	/* 3 byte integer */
644
				if (ind < vec_max - 3 + 1) {
645
					buff[ind++] = value >> 16;
646
					buff[ind++] = value >> 8;
647
					buff[ind++] = value;
648
				}
649
				break;
650
651
			case 32:	/* 4 byte integer */
652
				if (ind < vec_max - 4 + 1) {
653
					buff[ind++] = value >> 24;
654
					buff[ind++] = value >> 16;
655
					buff[ind++] = value >> 8;
656
					buff[ind++] = value;
657
				}
658
				break;
659
660
			default:
661
				fprintf(stderr, "do_encode: Illegal width\n");
662
				break;
663
			}
664
		}
665
	}
666
667
	/* Flush out any remaining bits */
668
	if (shift && ind < vec_max) {
669
		buff[ind++] = val;
670
		val = 0;
671
	}
672
673
674
	if (used)
675
		*used = ind;
676
677
	if (error)
678
		return -1;
679
680
	return encoded;
681
}
682
683
scsireq_t *
684
scsireq_build(scsireq_t *scsireq, u_long datalen, caddr_t databuf,
685
    u_long flags, char *cmd_spec, ...)
686
{
687
	size_t cmdlen;
688
	va_list ap;
689
690
	if (scsireq == 0)
691
		return 0;
692
693
	scsireq_reset(scsireq);
694
695
	if (databuf) {
696
		scsireq->databuf = databuf;
697
		scsireq->datalen = datalen;
698
		scsireq->flags = flags;
699
	}
700
	else if (datalen) {
701
		/* XXX: Good way to get a memory leak.  Perhaps this should be
702
		 * removed.
703
		 */
704
		if ( (scsireq->databuf = malloc(datalen)) == NULL)
705
			return 0;
706
707
		scsireq->datalen = datalen;
708
		scsireq->flags = flags;
709
	}
710
711
 	va_start(ap, cmd_spec);
712
713
 	if (do_encode(scsireq->cmd, CMDBUFLEN, &cmdlen, 0, 0, cmd_spec, ap) == -1)
714
 		return 0;
715
	va_end (ap);
716
717
	scsireq->cmdlen = cmdlen;
718
	return scsireq;
719
}
720
721
scsireq_t
722
*scsireq_build_visit(scsireq_t *scsireq, u_long datalen, caddr_t databuf,
723
    u_long flags, char *cmd_spec,
724
    int (*arg_get)(void *hook, char *field_name), void *gethook)
725
{
726
	size_t cmdlen;
727
	va_list ap;
728
729
	if (scsireq == 0)
730
		return 0;
731
732
	scsireq_reset(scsireq);
733
734
	if (databuf) {
735
		scsireq->databuf = databuf;
736
		scsireq->datalen = datalen;
737
		scsireq->flags = flags;
738
	} else if (datalen) {
739
		/* XXX: Good way to get a memory leak.  Perhaps this should be
740
		 * removed.
741
		 */
742
		if ( (scsireq->databuf = malloc(datalen)) == NULL)
743
			return 0;
744
745
		scsireq->datalen = datalen;
746
		scsireq->flags = flags;
747
	}
748
749
 	if (do_encode(scsireq->cmd, CMDBUFLEN, &cmdlen, arg_get, gethook,
750
	    cmd_spec, ap) == -1)
751
 		return 0;
752
753
	scsireq->cmdlen = cmdlen;
754
755
	return scsireq;
756
}
757
758
int
759
scsireq_buff_encode_visit(u_char *buff, size_t len, char *fmt,
760
	int (*arg_get)(void *hook, char *field_name), void *gethook)
761
{
762
	va_list ap;
763
	return do_encode(buff, len, 0, arg_get, gethook, fmt, ap);
764
}
765
766
int
767
scsireq_encode_visit(scsireq_t *scsireq, char *fmt,
768
    int (*arg_get)(void *hook, char *field_name), void *gethook)
769
{
770
	va_list ap;
771
	return do_encode(scsireq->databuf, scsireq->datalen, 0,
772
	    arg_get, gethook, fmt, ap);
773
}
774
775
FILE *
776
scsi_debug_output(char *s)
777
{
778
	if (s == 0)
779
		behave.db_f = 0;
780
	else {
781
		behave.db_f = fopen(s, "w");
782
783
		if (behave.db_f == 0)
784
			behave.db_f = stderr;
785
	}
786
787
	return behave.db_f;
788
}
789
790
#define SCSI_TRUNCATE -1
791
792
typedef struct scsi_assoc {
793
	int code;
794
	char *text;
795
} scsi_assoc_t;
796
797
static scsi_assoc_t retsts[] =
798
{
799
	{ SCCMD_OK, "No error" },
800
	{ SCCMD_TIMEOUT, "Command Timeout" },
801
	{ SCCMD_BUSY, "Busy" },
802
	{ SCCMD_SENSE, "Sense Returned" },
803
	{ SCCMD_UNKNOWN, "Unknown return status" },
804
805
	{ 0, 0 }
806
};
807
808
static char *
809
scsi_assoc_text(int code, scsi_assoc_t *tab)
810
{
811
	while (tab->text) {
812
		if (tab->code == code)
813
			return tab->text;
814
815
		tab++;
816
	}
817
818
	return "Unknown code";
819
}
820
821
void
822
scsi_dump(FILE *f, char *text, u_char *p, int req, int got, int dump_print)
823
{
824
	int i;
825
	int trunc = 0;
826
827
	if (f == 0 || req == 0)
828
		return;
829
830
	fprintf(f, "%s (%d of %d):\n", text, got, req);
831
832
	if (behave.db_trunc != -1 && got > behave.db_trunc) {
833
		trunc = 1;
834
		got = behave.db_trunc;
835
	}
836
837
	for (i = 0; i < got; i++) {
838
		fprintf(f, "%02x", p[i]);
839
840
		putc(' ', f);
841
842
		if ((i % 16) == 15 || i == got - 1) {
843
			int j;
844
			if (dump_print) {
845
				fprintf(f, " # ");
846
				for (j = i - 15; j <= i; j++)
847
					putc((isprint(p[j]) ? p[j] : '.'), f);
848
849
				putc('\n', f);
850
			} else
851
				putc('\n', f);
852
		}
853
	}
854
855
	fprintf(f, "%s", (trunc) ? "(truncated)...\n" : "\n");
856
}
857
858
/* XXX: sense_7x_dump and scsi_sense dump was just sort of
859
 * grabbed out of the old ds
860
 * library and not really merged in carefully.  It should use the
861
 * new buffer decoding stuff.
862
 */
863
864
/* Get unsigned long.
865
 */
866
static u_long
867
g_u_long(u_char *s)
868
{
869
	return (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
870
}
871
872
/* In the old software you could patch in a special error table:
873
 */
874
static scsi_assoc_t *error_table = 0;
875
876
static void
877
sense_7x_dump(FILE *f, scsireq_t *scsireq)
878
{
879
	int code;
880
	u_char *s = (u_char *)scsireq->sense;
881
	int valid = (*s) & 0x80;
882
	u_long val;
883
884
	static scsi_assoc_t sense[] = {
885
		{ 0, "No sense" },
886
		{ 1, "Recovered error" },
887
		{ 2, "Not Ready" },
888
		{ 3, "Medium error" },
889
		{ 4, "Hardware error" },
890
		{ 5, "Illegal request" },
891
		{ 6, "Unit attention" },
892
		{ 7, "Data protect" },
893
		{ 8, "Blank check" },
894
		{ 9, "Vendor specific" },
895
		{ 0xa, "Copy aborted" },
896
		{ 0xb, "Aborted Command" },
897
		{ 0xc, "Equal" },
898
		{ 0xd, "Volume overflow" },
899
		{ 0xe, "Miscompare" },
900
		{ 0, 0 },
901
	};
902
903
	static scsi_assoc_t code_tab[] = {
904
		{0x70, "current errors"},
905
		{0x71, "deferred errors"},
906
	};
907
908
	fprintf(f, "Error code is \"%s\"\n", scsi_assoc_text(s[0]&0x7F, code_tab));
909
	fprintf(f, "Segment number is %02x\n", s[1]);
910
911
	if (s[2] & 0x20)
912
		fprintf(f, "Incorrect Length Indicator is set.\n");
913
914
	fprintf(f, "Sense key is \"%s\"\n", scsi_assoc_text(s[2] & 0x7, sense));
915
916
	val = g_u_long(s + 3);
917
	fprintf(f, "The Information field is%s %08lx (%ld).\n",
918
	    valid ? "" : " not valid but contains", (long)val, (long)val);
919
920
	val = g_u_long(s + 8);
921
	fprintf(f, "The Command Specific Information field is %08lx (%ld).\n",
922
	    (long)val, (long)val);
923
924
	fprintf(f, "Additional sense code: %02x\n", s[12]);
925
	fprintf(f, "Additional sense code qualifier: %02x\n", s[13]);
926
927
	code = (s[12] << 8) | s[13];
928
929
	if (error_table)
930
		fprintf(f, "%s\n", scsi_assoc_text(code, error_table));
931
932
	if (s[15] & 0x80) {
933
		if ((s[2] & 0x7) == 0x05)	/* Illegal request */
934
		{
935
			int byte;
936
			u_char value, bit;
937
			int bad_par = ((s[15] & 0x40) == 0);
938
			fprintf(f, "Illegal value in the %s.\n",
939
			    (bad_par ? "parameter list" : "command descriptor block"));
940
			byte = ((s[16] << 8) | s[17]);
941
			value = bad_par ? (u_char)scsireq->databuf[byte] : (u_char)scsireq->cmd[byte];
942
			bit = s[15] & 0x7;
943
			if (s[15] & 0x08)
944
				fprintf(f, "Bit %d of byte %d (value %02x) is illegal.\n",
945
				    bit, byte, value);
946
			else
947
				fprintf(f, "Byte %d (value %02x) is illegal.\n", byte, value);
948
		} else 	{
949
			fprintf(f, "Sense Key Specific (valid but not illegal request):\n");
950
			fprintf(f, "%02x %02x %02x\n", s[15] & 0x7f, s[16], s[17]);
951
		}
952
	}
953
}
954
955
/* scsi_sense_dump: Dump the sense portion of the scsireq structure.
956
 */
957
static void
958
scsi_sense_dump(FILE *f, scsireq_t *scsireq)
959
{
960
	u_char *s = (u_char *)scsireq->sense;
961
	int code = (*s) & 0x7f;
962
963
	if (scsireq->senselen_used == 0) {
964
		fprintf(f, "No sense sent.\n");
965
		return;
966
	}
967
968
#if 0
969
	if (!valid)
970
		fprintf(f, "The sense data is not valid.\n");
971
#endif
972
973
	switch (code) {
974
	case 0x70:
975
	case 0x71:
976
		sense_7x_dump(f, scsireq);
977
		break;
978
979
	default:
980
		fprintf(f, "No sense dump for error code %02x.\n", code);
981
	}
982
	scsi_dump(f, "sense", s, scsireq->senselen, scsireq->senselen_used, 0);
983
}
984
985
static void
986
scsi_retsts_dump(FILE *f, scsireq_t *scsireq)
987
{
988
	if (scsireq->retsts == 0)
989
		return;
990
991
	fprintf(f, "return status %d (%s)",
992
	    scsireq->retsts, scsi_assoc_text(scsireq->retsts, retsts));
993
994
	switch (scsireq->retsts) {
995
	case SCCMD_TIMEOUT:
996
		fprintf(f, " after %ld ms", scsireq->timeout);
997
		break;
998
999
	default:
1000
		break;
1001
	}
1002
}
1003
1004
int
1005
scsi_debug(FILE *f, int ret, scsireq_t *scsireq)
1006
{
1007
	char *d;
1008
	if (f == 0)
1009
		return 0;
1010
1011
	fprintf(f, "SCIOCCOMMAND ioctl");
1012
1013
	if (ret == 0)
1014
		fprintf(f, ": Command accepted.");
1015
	else {
1016
		if (ret != -1)
1017
			fprintf(f, ", return value %d?", ret);
1018
1019
		if (errno) {
1020
			fprintf(f, ": %s", strerror(errno));
1021
			errno = 0;
1022
		}
1023
	}
1024
1025
	fputc('\n', f);
1026
1027
	if (ret == 0 && (scsireq->status || scsireq->retsts || behave.db_level))
1028
	{
1029
		scsi_retsts_dump(f, scsireq);
1030
1031
		if (scsireq->status)
1032
			fprintf(f, " host adapter status %d\n", scsireq->status);
1033
1034
		if (scsireq->flags & SCCMD_READ)
1035
			d = "Data in";
1036
		else if (scsireq->flags & SCCMD_WRITE)
1037
			d = "Data out";
1038
		else
1039
			d = "No data transfer?";
1040
1041
		if (scsireq->cmdlen == 0)
1042
			fprintf(f, "Zero length command????\n");
1043
1044
		scsi_dump(f, "Command out",
1045
		    (u_char *)scsireq->cmd, scsireq->cmdlen, scsireq->cmdlen, 0);
1046
		scsi_dump(f, d,
1047
		    (u_char *)scsireq->databuf, scsireq->datalen,
1048
	 	scsireq->datalen_used, 1);
1049
		scsi_sense_dump(f, scsireq);
1050
	}
1051
1052
	fflush(f);
1053
1054
	return ret;
1055
}
1056
1057
static char *debug_output;
1058
1059
int
1060
scsi_open(const char *path, int flags)
1061
{
1062
	int fd = open(path, flags);
1063
1064
	if (fd != -1) {
1065
		char *p;
1066
		debug_output = getenv("SU_DEBUG_OUTPUT");
1067
		(void)scsi_debug_output(debug_output);
1068
1069
		if ((p = getenv("SU_DEBUG_LEVEL")))
1070
			sscanf(p, "%d", &behave.db_level);
1071
1072
		if ((p = getenv("SU_DEBUG_TRUNCATE")))
1073
			sscanf(p, "%d", &behave.db_trunc);
1074
		else
1075
			behave.db_trunc = SCSI_TRUNCATE;
1076
	}
1077
1078
	return fd;
1079
}
1080
1081
int
1082
scsireq_enter(int fid, scsireq_t *scsireq)
1083
{
1084
	int ret;
1085
1086
	ret = ioctl(fid, SCIOCCOMMAND, (void *)scsireq);
1087
1088
	if (behave.db_f)
1089
		scsi_debug(behave.db_f, ret, scsireq);
1090
1091
	return ret;
1092
}