scsi_cmdparse.c revision 84199
139209Sgibbs/*
239209Sgibbs * Taken from the original FreeBSD user SCSI library.
339209Sgibbs */
439209Sgibbs/* Copyright (c) 1994 HD Associates
539209Sgibbs * (contact: dufault@hda.com)
639209Sgibbs * All rights reserved.
739209Sgibbs *
839209Sgibbs * Redistribution and use in source and binary forms, with or without
939209Sgibbs * modification, are permitted provided that the following conditions
1039209Sgibbs * are met:
1139209Sgibbs * 1. Redistributions of source code must retain the above copyright
1239209Sgibbs *    notice, this list of conditions and the following disclaimer.
1339209Sgibbs * 2. Redistributions in binary form must reproduce the above copyright
1439209Sgibbs *    notice, this list of conditions and the following disclaimer in the
1539209Sgibbs *    documentation and/or other materials provided with the distribution.
1639209Sgibbs * 3. All advertising materials mentioning features or use of this software
1739209Sgibbs *    must display the following acknowledgement:
1839209Sgibbs * This product includes software developed by HD Associates
1939209Sgibbs * 4. Neither the name of the HD Associaates nor the names of its contributors
2039209Sgibbs *    may be used to endorse or promote products derived from this software
2139209Sgibbs *    without specific prior written permission.
2239209Sgibbs *
2339209Sgibbs * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES``AS IS'' AND
2439209Sgibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2539209Sgibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2639209Sgibbs * ARE DISCLAIMED.  IN NO EVENT SHALL HD ASSOCIATES OR CONTRIBUTORS BE LIABLE
2739209Sgibbs * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2839209Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2939209Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3039209Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3139209Sgibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3239209Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3339209Sgibbs * SUCH DAMAGE.
3439209Sgibbs * From: scsi.c,v 1.8 1997/02/22 15:07:54 peter Exp $
3539209Sgibbs */
3684199Sdillon
3784199Sdillon#include <sys/cdefs.h>
3884199Sdillon__FBSDID("$FreeBSD: head/lib/libcam/scsi_cmdparse.c 84199 2001-09-30 21:13:43Z dillon $");
3984199Sdillon
4039209Sgibbs#include <stdlib.h>
4139209Sgibbs#include <stdio.h>
4239209Sgibbs#include <ctype.h>
4339209Sgibbs#include <string.h>
4439209Sgibbs#include <sys/errno.h>
4539209Sgibbs#include <stdarg.h>
4639209Sgibbs#include <fcntl.h>
4739209Sgibbs
4839209Sgibbs#include <cam/cam.h>
4939209Sgibbs#include <cam/cam_ccb.h>
5039209Sgibbs#include <cam/scsi/scsi_message.h>
5139209Sgibbs#include "camlib.h"
5239209Sgibbs
5339209Sgibbs/*
5439209Sgibbs * Decode: Decode the data section of a scsireq.  This decodes
5539209Sgibbs * trivial grammar:
5639209Sgibbs *
5739209Sgibbs * fields : field fields
5839209Sgibbs *        ;
5939209Sgibbs *
6039209Sgibbs * field : field_specifier
6139209Sgibbs *       | control
6239209Sgibbs *       ;
6339209Sgibbs *
6439209Sgibbs * control : 's' seek_value
6539209Sgibbs *       | 's' '+' seek_value
6639209Sgibbs *       ;
6739209Sgibbs *
6839209Sgibbs * seek_value : DECIMAL_NUMBER
6939209Sgibbs *       | 'v'				// For indirect seek, i.e., value from the arg list
7039209Sgibbs *       ;
7139209Sgibbs *
7239209Sgibbs * field_specifier : type_specifier field_width
7339209Sgibbs *       | '{' NAME '}' type_specifier field_width
7439209Sgibbs *       ;
7539209Sgibbs *
7639209Sgibbs * field_width : DECIMAL_NUMBER
7739209Sgibbs *       ;
7839209Sgibbs *
7939209Sgibbs * type_specifier : 'i'	// Integral types (i1, i2, i3, i4)
8039209Sgibbs *       | 'b'				// Bits
8139209Sgibbs *       | 't'				// Bits
8239209Sgibbs *       | 'c'				// Character arrays
8339209Sgibbs *       | 'z'				// Character arrays with zeroed trailing spaces
8439209Sgibbs *       ;
8539209Sgibbs *
8639209Sgibbs * Notes:
8739209Sgibbs * 1. Integral types are swapped into host order.
8839209Sgibbs * 2. Bit fields are allocated MSB to LSB to match the SCSI spec documentation.
8939209Sgibbs * 3. 's' permits "seeking" in the string.  "s+DECIMAL" seeks relative to
9039209Sgibbs *    DECIMAL; "sDECIMAL" seeks absolute to decimal.
9139209Sgibbs * 4. 's' permits an indirect reference.  "sv" or "s+v" will get the
9239209Sgibbs *    next integer value from the arg array.
9339209Sgibbs * 5. Field names can be anything between the braces
9439209Sgibbs *
9539209Sgibbs * BUGS:
9639209Sgibbs * i and b types are promoted to ints.
9739209Sgibbs *
9839209Sgibbs */
9939209Sgibbs
10039209Sgibbsstatic int
10139209Sgibbsdo_buff_decode(u_int8_t *databuf, size_t len,
10239209Sgibbs	       void (*arg_put)(void *, int , void *, int, char *),
10339209Sgibbs	       void *puthook, char *fmt, va_list ap)
10439209Sgibbs{
10539209Sgibbs	int assigned = 0;
10639209Sgibbs	int width;
10739209Sgibbs	int suppress;
10839209Sgibbs	int plus;
10939209Sgibbs	int done = 0;
11039209Sgibbs	static u_char mask[] = {0, 0x01, 0x03, 0x07, 0x0f,
11139209Sgibbs				   0x1f, 0x3f, 0x7f, 0xff};
11239209Sgibbs	int value;
11339209Sgibbs	u_char *base = databuf;
11439209Sgibbs	char letter;
11539209Sgibbs	char field_name[80];
11639209Sgibbs
11739209Sgibbs#	define ARG_PUT(ARG) \
11839209Sgibbs	do \
11939209Sgibbs	{ \
12039209Sgibbs		if (!suppress) \
12139209Sgibbs		{ \
12239209Sgibbs			if (arg_put) \
12339209Sgibbs				(*arg_put)(puthook, (letter == 't' ? \
12439209Sgibbs					'b' : letter), \
12564382Skbyanc					(void *)((long)(ARG)), width, \
12664382Skbyanc					field_name); \
12739209Sgibbs			else \
12839209Sgibbs				*(va_arg(ap, int *)) = (ARG); \
12939209Sgibbs			assigned++; \
13039209Sgibbs		} \
13139209Sgibbs		field_name[0] = 0; \
13239209Sgibbs		suppress = 0; \
13339209Sgibbs	} while (0)
13439209Sgibbs
13539209Sgibbs	u_char bits = 0;	/* For bit fields */
13639209Sgibbs	int shift = 0;		/* Bits already shifted out */
13739209Sgibbs	suppress = 0;
13839209Sgibbs	field_name[0] = 0;
13939209Sgibbs
14039209Sgibbs	while (!done) {
14139209Sgibbs		switch(letter = *fmt) {
14239209Sgibbs		case ' ':	/* White space */
14339209Sgibbs		case '\t':
14439209Sgibbs		case '\r':
14539209Sgibbs		case '\n':
14639209Sgibbs		case '\f':
14739209Sgibbs			fmt++;
14839209Sgibbs			break;
14939209Sgibbs
15039209Sgibbs		case '#':	/* Comment */
15139209Sgibbs			while (*fmt && (*fmt != '\n'))
15239209Sgibbs				fmt++;
15339209Sgibbs			if (fmt)
15439209Sgibbs				fmt++;	/* Skip '\n' */
15539209Sgibbs			break;
15639209Sgibbs
15739209Sgibbs		case '*':	/* Suppress assignment */
15839209Sgibbs			fmt++;
15939209Sgibbs			suppress = 1;
16039209Sgibbs			break;
16139209Sgibbs
16239209Sgibbs		case '{':	/* Field Name */
16339209Sgibbs		{
16439209Sgibbs			int i = 0;
16539209Sgibbs			fmt++;	/* Skip '{' */
16639209Sgibbs			while (*fmt && (*fmt != '}')) {
16739209Sgibbs				if (i < sizeof(field_name))
16839209Sgibbs					field_name[i++] = *fmt;
16939209Sgibbs
17039209Sgibbs				fmt++;
17139209Sgibbs			}
17239209Sgibbs			if (fmt)
17339209Sgibbs				fmt++;	/* Skip '}' */
17439209Sgibbs			field_name[i] = 0;
17539209Sgibbs			break;
17639209Sgibbs		}
17739209Sgibbs
17839209Sgibbs		case 't':	/* Bit (field) */
17939209Sgibbs		case 'b':	/* Bits */
18039209Sgibbs			fmt++;
18139209Sgibbs			width = strtol(fmt, &fmt, 10);
18239209Sgibbs			if (width > 8)
18339209Sgibbs				done = 1;
18439209Sgibbs			else {
18539209Sgibbs				if (shift <= 0) {
18639209Sgibbs					bits = *databuf++;
18739209Sgibbs					shift = 8;
18839209Sgibbs				}
18939209Sgibbs				value = (bits >> (shift - width)) &
19039209Sgibbs					 mask[width];
19139209Sgibbs
19239209Sgibbs#if 0
19339209Sgibbs				printf("shift %2d bits %02x value %02x width %2d mask %02x\n",
19439209Sgibbs				shift, bits, value, width, mask[width]);
19539209Sgibbs#endif
19639209Sgibbs
19739209Sgibbs				ARG_PUT(value);
19839209Sgibbs
19939209Sgibbs				shift -= width;
20039209Sgibbs			}
20139209Sgibbs			break;
20239209Sgibbs
20339209Sgibbs		case 'i':	/* Integral values */
20439209Sgibbs			shift = 0;
20539209Sgibbs			fmt++;
20639209Sgibbs			width = strtol(fmt, &fmt, 10);
20739209Sgibbs			switch(width) {
20839209Sgibbs			case 1:
20939209Sgibbs				ARG_PUT(*databuf);
21039209Sgibbs				databuf++;
21139209Sgibbs				break;
21239209Sgibbs
21339209Sgibbs			case 2:
21439209Sgibbs				ARG_PUT((*databuf) << 8 | *(databuf + 1));
21539209Sgibbs				databuf += 2;
21639209Sgibbs				break;
21739209Sgibbs
21839209Sgibbs			case 3:
21939209Sgibbs				ARG_PUT((*databuf) << 16 |
22039209Sgibbs					(*(databuf + 1)) << 8 | *(databuf + 2));
22139209Sgibbs				databuf += 3;
22239209Sgibbs				break;
22339209Sgibbs
22439209Sgibbs			case 4:
22539209Sgibbs				ARG_PUT((*databuf) << 24 |
22639209Sgibbs					(*(databuf + 1)) << 16 |
22739209Sgibbs					(*(databuf + 2)) << 8 |
22839209Sgibbs					*(databuf + 3));
22939209Sgibbs				databuf += 4;
23039209Sgibbs				break;
23139209Sgibbs
23239209Sgibbs			default:
23339209Sgibbs				done = 1;
23439209Sgibbs				break;
23539209Sgibbs			}
23639209Sgibbs
23739209Sgibbs			break;
23839209Sgibbs
23939209Sgibbs		case 'c':	/* Characters (i.e., not swapped) */
24039209Sgibbs		case 'z':	/* Characters with zeroed trailing
24139209Sgibbs					   spaces  */
24239209Sgibbs			shift = 0;
24339209Sgibbs			fmt++;
24439209Sgibbs			width = strtol(fmt, &fmt, 10);
24539209Sgibbs			if (!suppress) {
24639209Sgibbs				if (arg_put)
24739209Sgibbs					(*arg_put)(puthook,
24839209Sgibbs						(letter == 't' ? 'b' : letter),
24939209Sgibbs						databuf, width, field_name);
25039209Sgibbs				else {
25139209Sgibbs					char *dest;
25239209Sgibbs					dest = va_arg(ap, char *);
25339209Sgibbs					bcopy(databuf, dest, width);
25439209Sgibbs					if (letter == 'z') {
25539209Sgibbs						char *p;
25639209Sgibbs						for (p = dest + width - 1;
25739209Sgibbs						     (p >= (char *)dest)
25839209Sgibbs						     && (*p == ' '); p--)
25939209Sgibbs							*p = 0;
26039209Sgibbs					}
26139209Sgibbs				}
26239209Sgibbs				assigned++;
26339209Sgibbs			}
26439209Sgibbs			databuf += width;
26539209Sgibbs			field_name[0] = 0;
26639209Sgibbs			suppress = 0;
26739209Sgibbs			break;
26839209Sgibbs
26939209Sgibbs		case 's':	/* Seek */
27039209Sgibbs			shift = 0;
27139209Sgibbs			fmt++;
27239209Sgibbs			if (*fmt == '+') {
27339209Sgibbs				plus = 1;
27439209Sgibbs				fmt++;
27539209Sgibbs			} else
27639209Sgibbs				plus = 0;
27739209Sgibbs
27839209Sgibbs			if (tolower(*fmt) == 'v') {
27939209Sgibbs				/*
28039209Sgibbs				 * You can't suppress a seek value.  You also
28139209Sgibbs				 * can't have a variable seek when you are using
28239209Sgibbs				 * "arg_put".
28339209Sgibbs				 */
28439209Sgibbs				width = (arg_put) ? 0 : va_arg(ap, int);
28539209Sgibbs				fmt++;
28639209Sgibbs			} else
28739209Sgibbs				width = strtol(fmt, &fmt, 10);
28839209Sgibbs
28939209Sgibbs			if (plus)
29039209Sgibbs				databuf += width;	/* Relative seek */
29139209Sgibbs			else
29239209Sgibbs				databuf = base + width;	/* Absolute seek */
29339209Sgibbs
29439209Sgibbs			break;
29539209Sgibbs
29639209Sgibbs		case 0:
29739209Sgibbs			done = 1;
29839209Sgibbs			break;
29939209Sgibbs
30039209Sgibbs		default:
30139209Sgibbs			fprintf(stderr, "Unknown letter in format: %c\n",
30239209Sgibbs				letter);
30339209Sgibbs			fmt++;
30439209Sgibbs			break;
30539209Sgibbs		}
30639209Sgibbs	}
30739209Sgibbs
30839209Sgibbs	return (assigned);
30939209Sgibbs}
31039209Sgibbs
31139209Sgibbs/* next_field: Return the next field in a command specifier.  This
31239209Sgibbs * builds up a SCSI command using this trivial grammar:
31339209Sgibbs *
31439209Sgibbs * fields : field fields
31539209Sgibbs *        ;
31639209Sgibbs *
31739209Sgibbs * field : value
31839209Sgibbs *       | value ':' field_width
31939209Sgibbs *       ;
32039209Sgibbs *
32139209Sgibbs * field_width : digit
32239209Sgibbs *       | 'i' digit		// i2 = 2 byte integer, i3 = 3 byte integer etc.
32339209Sgibbs *       ;
32439209Sgibbs *
32539209Sgibbs * value : HEX_NUMBER
32639209Sgibbs *       | 'v'				// For indirection.
32739209Sgibbs *       ;
32839209Sgibbs *
32939209Sgibbs * Notes:
33039209Sgibbs *  Bit fields are specified MSB first to match the SCSI spec.
33139209Sgibbs *
33239209Sgibbs * Examples:
33339209Sgibbs *  TUR: "0 0 0 0 0 0"
33439209Sgibbs *  WRITE BUFFER: "38 v:3 0:2 0:3 v v:i3 v:i3 0", mode, buffer_id, list_length
33539209Sgibbs *
33639209Sgibbs * The function returns the value:
33739209Sgibbs *  0: For reached end, with error_p set if an error was found
33839209Sgibbs *  1: For valid stuff setup
33939209Sgibbs *  2: For "v" was entered as the value (implies use varargs)
34039209Sgibbs *
34139209Sgibbs */
34239209Sgibbs
34339209Sgibbsstatic int
34439209Sgibbsnext_field(char **pp, char *fmt, int *width_p, int *value_p, char *name,
34539209Sgibbs	   int n_name, int *error_p, int *suppress_p)
34639209Sgibbs{
34739209Sgibbs	char *p = *pp;
34839209Sgibbs
34939209Sgibbs	int something = 0;
35039209Sgibbs
35139209Sgibbs	enum {
35239209Sgibbs		BETWEEN_FIELDS,
35339209Sgibbs		START_FIELD,
35439209Sgibbs		GET_FIELD,
35539209Sgibbs		DONE,
35639209Sgibbs	} state;
35739209Sgibbs
35839209Sgibbs	int value = 0;
35939209Sgibbs	int field_size;		/* Default to byte field type... */
36039209Sgibbs	int field_width;	/* 1 byte wide */
36139209Sgibbs	int is_error = 0;
36239209Sgibbs	int suppress = 0;
36339209Sgibbs
36439209Sgibbs	field_size = 8;		/* Default to byte field type... */
36539209Sgibbs	*fmt = 'i';
36639209Sgibbs	field_width = 1;	/* 1 byte wide */
36739209Sgibbs	if (name)
36839209Sgibbs		*name = 0;
36939209Sgibbs
37039209Sgibbs	state = BETWEEN_FIELDS;
37139209Sgibbs
37239209Sgibbs	while (state != DONE) {
37339209Sgibbs		switch(state) {
37439209Sgibbs		case BETWEEN_FIELDS:
37539209Sgibbs			if (*p == 0)
37639209Sgibbs				state = DONE;
37739209Sgibbs			else if (isspace(*p))
37839209Sgibbs				p++;
37939209Sgibbs			else if (*p == '#') {
38039209Sgibbs				while (*p && *p != '\n')
38139209Sgibbs					p++;
38239209Sgibbs				if (p)
38339209Sgibbs					p++;
38439209Sgibbs			} else if (*p == '{') {
38539209Sgibbs				int i = 0;
38639209Sgibbs
38739209Sgibbs				p++;
38839209Sgibbs
38939209Sgibbs				while (*p && *p != '}') {
39039209Sgibbs					if(name && i < n_name) {
39139209Sgibbs						name[i] = *p;
39239209Sgibbs						i++;
39339209Sgibbs					}
39439209Sgibbs					p++;
39539209Sgibbs				}
39639209Sgibbs
39739209Sgibbs				if(name && i < n_name)
39839209Sgibbs					name[i] = 0;
39939209Sgibbs
40039209Sgibbs				if (*p == '}')
40139209Sgibbs					p++;
40239209Sgibbs			} else if (*p == '*') {
40339209Sgibbs				p++;
40439209Sgibbs				suppress = 1;
40539209Sgibbs			} else if (isxdigit(*p)) {
40639209Sgibbs				something = 1;
40739209Sgibbs				value = strtol(p, &p, 16);
40839209Sgibbs				state = START_FIELD;
40939209Sgibbs			} else if (tolower(*p) == 'v') {
41039209Sgibbs				p++;
41139209Sgibbs				something = 2;
41239209Sgibbs				value = *value_p;
41339209Sgibbs				state = START_FIELD;
41439209Sgibbs			} else if (tolower(*p) == 'i') {
41539209Sgibbs				/*
41639209Sgibbs				 * Try to work without the "v".
41739209Sgibbs				 */
41839209Sgibbs				something = 2;
41939209Sgibbs				value = *value_p;
42039209Sgibbs				p++;
42139209Sgibbs
42239209Sgibbs				*fmt = 'i';
42339209Sgibbs				field_size = 8;
42439209Sgibbs				field_width = strtol(p, &p, 10);
42539209Sgibbs				state = DONE;
42639209Sgibbs
42739209Sgibbs			} else if (tolower(*p) == 't') {
42839209Sgibbs				/*
42939209Sgibbs				 * XXX: B can't work: Sees the 'b' as a
43039209Sgibbs				 * hex digit in "isxdigit".  try "t" for
43139209Sgibbs				 * bit field.
43239209Sgibbs				 */
43339209Sgibbs				something = 2;
43439209Sgibbs				value = *value_p;
43539209Sgibbs				p++;
43639209Sgibbs
43739209Sgibbs				*fmt = 'b';
43839209Sgibbs				field_size = 1;
43939209Sgibbs				field_width = strtol(p, &p, 10);
44039209Sgibbs				state = DONE;
44139209Sgibbs			} else if (tolower(*p) == 's') {
44239209Sgibbs				/* Seek */
44339209Sgibbs				*fmt = 's';
44439209Sgibbs				p++;
44539209Sgibbs				if (tolower(*p) == 'v') {
44639209Sgibbs					p++;
44739209Sgibbs					something = 2;
44839209Sgibbs					value = *value_p;
44939209Sgibbs				} else {
45039209Sgibbs					something = 1;
45139209Sgibbs					value = strtol(p, &p, 0);
45239209Sgibbs				}
45339209Sgibbs				state = DONE;
45439209Sgibbs			} else {
45539209Sgibbs				fprintf(stderr, "Invalid starting "
45639209Sgibbs					"character: %c\n", *p);
45739209Sgibbs				is_error = 1;
45839209Sgibbs				state = DONE;
45939209Sgibbs			}
46039209Sgibbs			break;
46139209Sgibbs
46239209Sgibbs		case START_FIELD:
46339209Sgibbs			if (*p == ':') {
46439209Sgibbs				p++;
46539209Sgibbs				field_size = 1;		/* Default to bits
46639209Sgibbs							   when specified */
46739209Sgibbs				state = GET_FIELD;
46839209Sgibbs			} else
46939209Sgibbs				state = DONE;
47039209Sgibbs			break;
47139209Sgibbs
47239209Sgibbs		case GET_FIELD:
47339209Sgibbs			if (isdigit(*p)) {
47439209Sgibbs				*fmt = 'b';
47539209Sgibbs				field_size = 1;
47639209Sgibbs				field_width = strtol(p, &p, 10);
47739209Sgibbs				state = DONE;
47839209Sgibbs			} else if (*p == 'i') {
47939209Sgibbs
48039209Sgibbs				/* Integral (bytes) */
48139209Sgibbs				p++;
48239209Sgibbs
48339209Sgibbs				*fmt = 'i';
48439209Sgibbs				field_size = 8;
48539209Sgibbs				field_width = strtol(p, &p, 10);
48639209Sgibbs				state = DONE;
48739209Sgibbs			} else if (*p == 'b') {
48839209Sgibbs
48939209Sgibbs				/* Bits */
49039209Sgibbs				p++;
49139209Sgibbs
49239209Sgibbs				*fmt = 'b';
49339209Sgibbs				field_size = 1;
49439209Sgibbs				field_width = strtol(p, &p, 10);
49539209Sgibbs				state = DONE;
49639209Sgibbs			} else {
49739209Sgibbs				fprintf(stderr, "Invalid startfield %c "
49839209Sgibbs					"(%02x)\n", *p, *p);
49939209Sgibbs				is_error = 1;
50039209Sgibbs				state = DONE;
50139209Sgibbs			}
50239209Sgibbs			break;
50339209Sgibbs
50439209Sgibbs		case DONE:
50539209Sgibbs			break;
50639209Sgibbs		}
50739209Sgibbs	}
50839209Sgibbs
50939209Sgibbs	if (is_error) {
51039209Sgibbs		*error_p = 1;
51139209Sgibbs		return 0;
51239209Sgibbs	}
51339209Sgibbs
51439209Sgibbs	*error_p = 0;
51539209Sgibbs	*pp = p;
51639209Sgibbs	*width_p = field_width * field_size;
51739209Sgibbs	*value_p = value;
51839209Sgibbs	*suppress_p = suppress;
51939209Sgibbs
52039209Sgibbs	return (something);
52139209Sgibbs}
52239209Sgibbs
52339209Sgibbsstatic int
52439209Sgibbsdo_encode(u_char *buff, size_t vec_max, size_t *used,
52539209Sgibbs	  int (*arg_get)(void *, char *), void *gethook, char *fmt, va_list ap)
52639209Sgibbs{
52739209Sgibbs	int ind;
52839209Sgibbs	int shift;
52939209Sgibbs	u_char val;
53039209Sgibbs	int ret;
53139209Sgibbs	int width, value, error, suppress;
53239209Sgibbs	char c;
53339209Sgibbs	int encoded = 0;
53439209Sgibbs	char field_name[80];
53539209Sgibbs
53639209Sgibbs	ind = 0;
53739209Sgibbs	shift = 0;
53839209Sgibbs	val = 0;
53939209Sgibbs
54039209Sgibbs 	while ((ret = next_field(&fmt, &c, &width, &value, field_name,
54139209Sgibbs				 sizeof(field_name), &error, &suppress))) {
54239209Sgibbs		encoded++;
54339209Sgibbs
54439209Sgibbs		if (ret == 2) {
54539209Sgibbs			if (suppress)
54639209Sgibbs				value = 0;
54739209Sgibbs			else
54839209Sgibbs				value = arg_get ?
54939209Sgibbs					(*arg_get)(gethook, field_name) :
55039209Sgibbs					va_arg(ap, int);
55139209Sgibbs		}
55239209Sgibbs
55339209Sgibbs#if 0
55439209Sgibbs		printf(
55539209Sgibbs"do_encode: ret %d fmt %c width %d value %d name \"%s\" error %d suppress %d\n",
55639209Sgibbs		ret, c, width, value, field_name, error, suppress);
55739209Sgibbs#endif
55839209Sgibbs		/* Absolute seek */
55939209Sgibbs		if (c == 's') {
56039209Sgibbs			ind = value;
56139209Sgibbs			continue;
56239209Sgibbs		}
56339209Sgibbs
56439209Sgibbs		/* A width of < 8 is a bit field. */
56539209Sgibbs		if (width < 8) {
56639209Sgibbs
56739209Sgibbs			/* This is a bit field.  We start with the high bits
56839209Sgibbs			 * so it reads the same as the SCSI spec.
56939209Sgibbs			 */
57039209Sgibbs
57139209Sgibbs			shift += width;
57239209Sgibbs
57339209Sgibbs			val |= (value << (8 - shift));
57439209Sgibbs
57539209Sgibbs			if (shift == 8) {
57639209Sgibbs				if (ind < vec_max) {
57739209Sgibbs					buff[ind++] = val;
57839209Sgibbs					val = 0;
57939209Sgibbs				}
58039209Sgibbs				shift = 0;
58139209Sgibbs			}
58239209Sgibbs		} else {
58339209Sgibbs			if (shift) {
58439209Sgibbs				if (ind < vec_max) {
58539209Sgibbs					buff[ind++] = val;
58639209Sgibbs					val = 0;
58739209Sgibbs				}
58839209Sgibbs				shift = 0;
58939209Sgibbs			}
59039209Sgibbs			switch(width) {
59139209Sgibbs			case 8:		/* 1 byte integer */
59239209Sgibbs				if (ind < vec_max)
59339209Sgibbs					buff[ind++] = value;
59439209Sgibbs				break;
59539209Sgibbs
59639209Sgibbs			case 16:	/* 2 byte integer */
59739209Sgibbs				if (ind < vec_max - 2 + 1) {
59839209Sgibbs					buff[ind++] = value >> 8;
59939209Sgibbs					buff[ind++] = value;
60039209Sgibbs				}
60139209Sgibbs				break;
60239209Sgibbs
60339209Sgibbs			case 24:	/* 3 byte integer */
60439209Sgibbs				if (ind < vec_max - 3 + 1) {
60539209Sgibbs					buff[ind++] = value >> 16;
60639209Sgibbs					buff[ind++] = value >> 8;
60739209Sgibbs					buff[ind++] = value;
60839209Sgibbs				}
60939209Sgibbs				break;
61039209Sgibbs
61139209Sgibbs			case 32:	/* 4 byte integer */
61239209Sgibbs				if (ind < vec_max - 4 + 1) {
61339209Sgibbs					buff[ind++] = value >> 24;
61439209Sgibbs					buff[ind++] = value >> 16;
61539209Sgibbs					buff[ind++] = value >> 8;
61639209Sgibbs					buff[ind++] = value;
61739209Sgibbs				}
61839209Sgibbs				break;
61939209Sgibbs
62039209Sgibbs			default:
62139209Sgibbs				fprintf(stderr, "do_encode: Illegal width\n");
62239209Sgibbs				break;
62339209Sgibbs			}
62439209Sgibbs		}
62539209Sgibbs	}
62639209Sgibbs
62739209Sgibbs	/* Flush out any remaining bits
62839209Sgibbs	 */
62939209Sgibbs	if (shift && ind < vec_max) {
63039209Sgibbs		buff[ind++] = val;
63139209Sgibbs		val = 0;
63239209Sgibbs	}
63339209Sgibbs
63439209Sgibbs
63539209Sgibbs	if (used)
63639209Sgibbs		*used = ind;
63739209Sgibbs
63839209Sgibbs	if (error)
63939209Sgibbs		return -1;
64039209Sgibbs
64139209Sgibbs	return encoded;
64239209Sgibbs}
64339209Sgibbs
64439209Sgibbsint
64539209Sgibbscsio_decode(struct ccb_scsiio *csio, char *fmt, ...)
64639209Sgibbs{
64739209Sgibbs	va_list ap;
64839209Sgibbs
64939209Sgibbs	va_start(ap, fmt);
65039209Sgibbs
65139209Sgibbs	return(do_buff_decode(csio->data_ptr, (size_t)csio->dxfer_len,
65239209Sgibbs			      0, 0, fmt, ap));
65339209Sgibbs}
65439209Sgibbs
65539209Sgibbsint
65639209Sgibbscsio_decode_visit(struct ccb_scsiio *csio, char *fmt,
65739209Sgibbs		  void (*arg_put)(void *, int, void *, int, char *),
65839209Sgibbs		  void *puthook)
65939209Sgibbs{
66039209Sgibbs	va_list ap;
66139209Sgibbs
66239381Sken	/*
66339381Sken	 * We need some way to output things; we can't do it without
66439381Sken	 * the arg_put function.
66539381Sken	 */
66639381Sken	if (arg_put == NULL)
66739381Sken		return(-1);
66839209Sgibbs
66939381Sken	bzero(&ap, sizeof(ap));
67039381Sken
67139209Sgibbs	return(do_buff_decode(csio->data_ptr, (size_t)csio->dxfer_len,
67239209Sgibbs			      arg_put, puthook, fmt, ap));
67339209Sgibbs}
67439209Sgibbs
67539209Sgibbsint
67639209Sgibbsbuff_decode(u_int8_t *buff, size_t len, char *fmt, ...)
67739209Sgibbs{
67839209Sgibbs	va_list ap;
67939209Sgibbs
68039209Sgibbs	va_start(ap, fmt);
68139209Sgibbs
68239209Sgibbs	return(do_buff_decode(buff, len, 0, 0, fmt, ap));
68339209Sgibbs}
68439209Sgibbs
68539209Sgibbsint
68639209Sgibbsbuff_decode_visit(u_int8_t *buff, size_t len, char *fmt,
68739209Sgibbs		  void (*arg_put)(void *, int, void *, int, char *),
68839209Sgibbs		  void *puthook)
68939209Sgibbs{
69039209Sgibbs	va_list ap;
69139209Sgibbs
69239381Sken	/*
69339381Sken	 * We need some way to output things; we can't do it without
69439381Sken	 * the arg_put function.
69539381Sken	 */
69639381Sken	if (arg_put == NULL)
69739381Sken		return(-1);
69839209Sgibbs
69939381Sken	bzero(&ap, sizeof(ap));
70039381Sken
70139209Sgibbs	return(do_buff_decode(buff, len, arg_put, puthook, fmt, ap));
70239209Sgibbs}
70339209Sgibbs
70439209Sgibbs/*
70539209Sgibbs * Build a SCSI CCB, given the command and data pointers and a format
70639209Sgibbs * string describing the
70739209Sgibbs */
70839209Sgibbsint
70939209Sgibbscsio_build(struct ccb_scsiio *csio, u_int8_t *data_ptr, u_int32_t dxfer_len,
71039209Sgibbs	   u_int32_t flags, int retry_count, int timeout, char *cmd_spec, ...)
71139209Sgibbs{
71239381Sken	size_t cmdlen;
71339209Sgibbs	int retval;
71439209Sgibbs	va_list ap;
71539209Sgibbs
71639209Sgibbs	if (csio == NULL)
71739209Sgibbs		return(0);
71839209Sgibbs
71939209Sgibbs	bzero(csio, sizeof(struct ccb_scsiio));
72039209Sgibbs
72139209Sgibbs	va_start(ap, cmd_spec);
72239209Sgibbs
72339209Sgibbs	if ((retval = do_encode(csio->cdb_io.cdb_bytes, SCSI_MAX_CDBLEN,
72439209Sgibbs				&cmdlen, NULL, NULL, cmd_spec, ap)) == -1)
72539209Sgibbs		return(retval);
72639209Sgibbs
72739209Sgibbs	cam_fill_csio(csio,
72839209Sgibbs		      /* retries */ retry_count,
72939209Sgibbs		      /* cbfcnp */ NULL,
73039209Sgibbs		      /* flags */ flags,
73139209Sgibbs		      /* tag_action */ MSG_SIMPLE_Q_TAG,
73239209Sgibbs		      /* data_ptr */ data_ptr,
73339209Sgibbs		      /* dxfer_len */ dxfer_len,
73439209Sgibbs		      /* sense_len */ SSD_FULL_SIZE,
73539209Sgibbs		      /* cdb_len */ cmdlen,
73639209Sgibbs		      /* timeout */ timeout ? timeout : 5000);
73739209Sgibbs
73839209Sgibbs	return(retval);
73939209Sgibbs}
74039209Sgibbs
74139209Sgibbsint
74239209Sgibbscsio_build_visit(struct ccb_scsiio *csio, u_int8_t *data_ptr,
74339209Sgibbs		 u_int32_t dxfer_len, u_int32_t flags, int retry_count,
74439209Sgibbs		 int timeout, char *cmd_spec,
74539209Sgibbs		 int (*arg_get)(void *hook, char *field_name), void *gethook)
74639209Sgibbs{
74739209Sgibbs	va_list ap;
74839381Sken	size_t cmdlen;
74939381Sken	int retval;
75039209Sgibbs
75139209Sgibbs	if (csio == NULL)
75239209Sgibbs		return(0);
75339209Sgibbs
75439381Sken	/*
75539381Sken	 * We need something to encode, but we can't get it without the
75639381Sken	 * arg_get function.
75739381Sken	 */
75839381Sken	if (arg_get == NULL)
75939381Sken		return(-1);
76039209Sgibbs
76139381Sken	bzero(&ap, sizeof(ap));
76239381Sken
76339209Sgibbs	bzero(csio, sizeof(struct ccb_scsiio));
76439209Sgibbs
76539209Sgibbs	if ((retval = do_encode(csio->cdb_io.cdb_bytes, SCSI_MAX_CDBLEN,
76639209Sgibbs				&cmdlen, arg_get, gethook, cmd_spec, ap)) == -1)
76739209Sgibbs		return(retval);
76839209Sgibbs
76939209Sgibbs	cam_fill_csio(csio,
77039209Sgibbs		      /* retries */ retry_count,
77139209Sgibbs		      /* cbfcnp */ NULL,
77239209Sgibbs		      /* flags */ flags,
77339209Sgibbs		      /* tag_action */ MSG_SIMPLE_Q_TAG,
77439209Sgibbs		      /* data_ptr */ data_ptr,
77539209Sgibbs		      /* dxfer_len */ dxfer_len,
77639209Sgibbs		      /* sense_len */ SSD_FULL_SIZE,
77739209Sgibbs		      /* cdb_len */ cmdlen,
77839209Sgibbs		      /* timeout */ timeout ? timeout : 5000);
77939209Sgibbs
78039209Sgibbs	return(retval);
78139209Sgibbs}
78239209Sgibbs
78339209Sgibbsint
78439209Sgibbscsio_encode(struct ccb_scsiio *csio, char *fmt, ...)
78539209Sgibbs{
78639209Sgibbs	va_list ap;
78739209Sgibbs
78839209Sgibbs	if (csio == NULL)
78939209Sgibbs		return(0);
79039209Sgibbs
79139209Sgibbs	va_start(ap, fmt);
79239209Sgibbs
79339209Sgibbs	return(do_encode(csio->data_ptr, csio->dxfer_len, 0, 0, 0, fmt, ap));
79439209Sgibbs}
79539209Sgibbs
79639209Sgibbsint
79739209Sgibbsbuff_encode_visit(u_int8_t *buff, size_t len, char *fmt,
79839209Sgibbs		  int (*arg_get)(void *hook, char *field_name), void *gethook)
79939209Sgibbs{
80039209Sgibbs	va_list ap;
80139209Sgibbs
80239381Sken	/*
80339381Sken	 * We need something to encode, but we can't get it without the
80439381Sken	 * arg_get function.
80539381Sken	 */
80639381Sken	if (arg_get == NULL)
80739381Sken		return(-1);
80839209Sgibbs
80939381Sken	bzero(&ap, sizeof(ap));
81039381Sken
81139209Sgibbs	return(do_encode(buff, len, 0, arg_get, gethook, fmt, ap));
81239209Sgibbs}
81339209Sgibbs
81439209Sgibbsint
81539209Sgibbscsio_encode_visit(struct ccb_scsiio *csio, char *fmt,
81639209Sgibbs		  int (*arg_get)(void *hook, char *field_name), void *gethook)
81739209Sgibbs{
81839209Sgibbs	va_list ap;
81939209Sgibbs
82039381Sken	/*
82139381Sken	 * We need something to encode, but we can't get it without the
82239381Sken	 * arg_get function.
82339381Sken	 */
82439381Sken	if (arg_get == NULL)
82539381Sken		return(-1);
82639209Sgibbs
82739381Sken	bzero(&ap, sizeof(ap));
82839381Sken
82939209Sgibbs	return(do_encode(csio->data_ptr, csio->dxfer_len, 0, arg_get,
83039209Sgibbs			 gethook, fmt, ap));
83139209Sgibbs}
832