scsi_cmdparse.c revision 298753
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 298753 2016-04-28 18:41:55Z ngie $");
3984199Sdillon
40103382Smike#include <sys/types.h>
41103382Smike
4239209Sgibbs#include <stdlib.h>
4339209Sgibbs#include <stdio.h>
4439209Sgibbs#include <ctype.h>
4539209Sgibbs#include <string.h>
4639209Sgibbs#include <sys/errno.h>
4739209Sgibbs#include <stdarg.h>
4839209Sgibbs#include <fcntl.h>
4939209Sgibbs
5039209Sgibbs#include <cam/cam.h>
5139209Sgibbs#include <cam/cam_ccb.h>
5239209Sgibbs#include <cam/scsi/scsi_message.h>
5339209Sgibbs#include "camlib.h"
5439209Sgibbs
5539209Sgibbs/*
5639209Sgibbs * Decode: Decode the data section of a scsireq.  This decodes
5739209Sgibbs * trivial grammar:
5839209Sgibbs *
5939209Sgibbs * fields : field fields
6039209Sgibbs *        ;
6139209Sgibbs *
6239209Sgibbs * field : field_specifier
6339209Sgibbs *       | control
6439209Sgibbs *       ;
6539209Sgibbs *
6639209Sgibbs * control : 's' seek_value
6739209Sgibbs *       | 's' '+' seek_value
6839209Sgibbs *       ;
6939209Sgibbs *
7039209Sgibbs * seek_value : DECIMAL_NUMBER
7139209Sgibbs *       | 'v'				// For indirect seek, i.e., value from the arg list
7239209Sgibbs *       ;
7339209Sgibbs *
7439209Sgibbs * field_specifier : type_specifier field_width
7539209Sgibbs *       | '{' NAME '}' type_specifier field_width
7639209Sgibbs *       ;
7739209Sgibbs *
7839209Sgibbs * field_width : DECIMAL_NUMBER
7939209Sgibbs *       ;
8039209Sgibbs *
8139209Sgibbs * type_specifier : 'i'	// Integral types (i1, i2, i3, i4)
8239209Sgibbs *       | 'b'				// Bits
8339209Sgibbs *       | 't'				// Bits
8439209Sgibbs *       | 'c'				// Character arrays
8539209Sgibbs *       | 'z'				// Character arrays with zeroed trailing spaces
8639209Sgibbs *       ;
8739209Sgibbs *
8839209Sgibbs * Notes:
8939209Sgibbs * 1. Integral types are swapped into host order.
9039209Sgibbs * 2. Bit fields are allocated MSB to LSB to match the SCSI spec documentation.
9139209Sgibbs * 3. 's' permits "seeking" in the string.  "s+DECIMAL" seeks relative to
9239209Sgibbs *    DECIMAL; "sDECIMAL" seeks absolute to decimal.
9339209Sgibbs * 4. 's' permits an indirect reference.  "sv" or "s+v" will get the
9439209Sgibbs *    next integer value from the arg array.
9539209Sgibbs * 5. Field names can be anything between the braces
9639209Sgibbs *
9739209Sgibbs * BUGS:
9839209Sgibbs * i and b types are promoted to ints.
9939209Sgibbs *
10039209Sgibbs */
10139209Sgibbs
10239209Sgibbsstatic int
10339209Sgibbsdo_buff_decode(u_int8_t *databuf, size_t len,
10439209Sgibbs	       void (*arg_put)(void *, int , void *, int, char *),
105298753Sngie	       void *puthook, const char *fmt, va_list *ap)
10639209Sgibbs{
10739209Sgibbs	int assigned = 0;
10839209Sgibbs	int width;
10939209Sgibbs	int suppress;
11039209Sgibbs	int plus;
11139209Sgibbs	int done = 0;
11239209Sgibbs	static u_char mask[] = {0, 0x01, 0x03, 0x07, 0x0f,
11339209Sgibbs				   0x1f, 0x3f, 0x7f, 0xff};
11439209Sgibbs	int value;
11539209Sgibbs	u_char *base = databuf;
11688090Skbyanc	char *intendp;
11739209Sgibbs	char letter;
11839209Sgibbs	char field_name[80];
11939209Sgibbs
12039209Sgibbs#	define ARG_PUT(ARG) \
12139209Sgibbs	do \
12239209Sgibbs	{ \
12339209Sgibbs		if (!suppress) \
12439209Sgibbs		{ \
12539209Sgibbs			if (arg_put) \
12639209Sgibbs				(*arg_put)(puthook, (letter == 't' ? \
12739209Sgibbs					'b' : letter), \
12864382Skbyanc					(void *)((long)(ARG)), width, \
12964382Skbyanc					field_name); \
13039209Sgibbs			else \
131298753Sngie				*(va_arg(*ap, int *)) = (ARG); \
13239209Sgibbs			assigned++; \
13339209Sgibbs		} \
13439209Sgibbs		field_name[0] = 0; \
13539209Sgibbs		suppress = 0; \
13639209Sgibbs	} while (0)
13739209Sgibbs
13839209Sgibbs	u_char bits = 0;	/* For bit fields */
13939209Sgibbs	int shift = 0;		/* Bits already shifted out */
14039209Sgibbs	suppress = 0;
14139209Sgibbs	field_name[0] = 0;
14239209Sgibbs
14339209Sgibbs	while (!done) {
14439209Sgibbs		switch(letter = *fmt) {
14539209Sgibbs		case ' ':	/* White space */
14639209Sgibbs		case '\t':
14739209Sgibbs		case '\r':
14839209Sgibbs		case '\n':
14939209Sgibbs		case '\f':
15039209Sgibbs			fmt++;
15139209Sgibbs			break;
15239209Sgibbs
15339209Sgibbs		case '#':	/* Comment */
15439209Sgibbs			while (*fmt && (*fmt != '\n'))
15539209Sgibbs				fmt++;
15639209Sgibbs			if (fmt)
15739209Sgibbs				fmt++;	/* Skip '\n' */
15839209Sgibbs			break;
15939209Sgibbs
16039209Sgibbs		case '*':	/* Suppress assignment */
16139209Sgibbs			fmt++;
16239209Sgibbs			suppress = 1;
16339209Sgibbs			break;
16439209Sgibbs
16539209Sgibbs		case '{':	/* Field Name */
16639209Sgibbs		{
16739209Sgibbs			int i = 0;
16839209Sgibbs			fmt++;	/* Skip '{' */
16939209Sgibbs			while (*fmt && (*fmt != '}')) {
17039209Sgibbs				if (i < sizeof(field_name))
17139209Sgibbs					field_name[i++] = *fmt;
17239209Sgibbs
17339209Sgibbs				fmt++;
17439209Sgibbs			}
17539209Sgibbs			if (fmt)
17639209Sgibbs				fmt++;	/* Skip '}' */
17739209Sgibbs			field_name[i] = 0;
17839209Sgibbs			break;
17939209Sgibbs		}
18039209Sgibbs
18139209Sgibbs		case 't':	/* Bit (field) */
18239209Sgibbs		case 'b':	/* Bits */
18339209Sgibbs			fmt++;
18488090Skbyanc			width = strtol(fmt, &intendp, 10);
18588090Skbyanc			fmt = intendp;
18639209Sgibbs			if (width > 8)
18739209Sgibbs				done = 1;
18839209Sgibbs			else {
18939209Sgibbs				if (shift <= 0) {
19039209Sgibbs					bits = *databuf++;
19139209Sgibbs					shift = 8;
19239209Sgibbs				}
19339209Sgibbs				value = (bits >> (shift - width)) &
19439209Sgibbs					 mask[width];
19539209Sgibbs
19639209Sgibbs#if 0
19739209Sgibbs				printf("shift %2d bits %02x value %02x width %2d mask %02x\n",
19839209Sgibbs				shift, bits, value, width, mask[width]);
19939209Sgibbs#endif
20039209Sgibbs
20139209Sgibbs				ARG_PUT(value);
20239209Sgibbs
20339209Sgibbs				shift -= width;
20439209Sgibbs			}
20539209Sgibbs			break;
20639209Sgibbs
20739209Sgibbs		case 'i':	/* Integral values */
20839209Sgibbs			shift = 0;
20939209Sgibbs			fmt++;
21088090Skbyanc			width = strtol(fmt, &intendp, 10);
21188090Skbyanc			fmt = intendp;
21239209Sgibbs			switch(width) {
21339209Sgibbs			case 1:
21439209Sgibbs				ARG_PUT(*databuf);
21539209Sgibbs				databuf++;
21639209Sgibbs				break;
21739209Sgibbs
21839209Sgibbs			case 2:
21939209Sgibbs				ARG_PUT((*databuf) << 8 | *(databuf + 1));
22039209Sgibbs				databuf += 2;
22139209Sgibbs				break;
22239209Sgibbs
22339209Sgibbs			case 3:
22439209Sgibbs				ARG_PUT((*databuf) << 16 |
22539209Sgibbs					(*(databuf + 1)) << 8 | *(databuf + 2));
22639209Sgibbs				databuf += 3;
22739209Sgibbs				break;
22839209Sgibbs
22939209Sgibbs			case 4:
23039209Sgibbs				ARG_PUT((*databuf) << 24 |
23139209Sgibbs					(*(databuf + 1)) << 16 |
23239209Sgibbs					(*(databuf + 2)) << 8 |
23339209Sgibbs					*(databuf + 3));
23439209Sgibbs				databuf += 4;
23539209Sgibbs				break;
23639209Sgibbs
23739209Sgibbs			default:
23839209Sgibbs				done = 1;
23939209Sgibbs				break;
24039209Sgibbs			}
24139209Sgibbs
24239209Sgibbs			break;
24339209Sgibbs
24439209Sgibbs		case 'c':	/* Characters (i.e., not swapped) */
24539209Sgibbs		case 'z':	/* Characters with zeroed trailing
24639209Sgibbs					   spaces  */
24739209Sgibbs			shift = 0;
24839209Sgibbs			fmt++;
24988090Skbyanc			width = strtol(fmt, &intendp, 10);
25088090Skbyanc			fmt = intendp;
25139209Sgibbs			if (!suppress) {
25239209Sgibbs				if (arg_put)
25339209Sgibbs					(*arg_put)(puthook,
25439209Sgibbs						(letter == 't' ? 'b' : letter),
25539209Sgibbs						databuf, width, field_name);
25639209Sgibbs				else {
25739209Sgibbs					char *dest;
258298753Sngie					dest = va_arg(*ap, char *);
25939209Sgibbs					bcopy(databuf, dest, width);
26039209Sgibbs					if (letter == 'z') {
26139209Sgibbs						char *p;
26239209Sgibbs						for (p = dest + width - 1;
26339209Sgibbs						     (p >= (char *)dest)
26439209Sgibbs						     && (*p == ' '); p--)
26539209Sgibbs							*p = 0;
26639209Sgibbs					}
26739209Sgibbs				}
26839209Sgibbs				assigned++;
26939209Sgibbs			}
27039209Sgibbs			databuf += width;
27139209Sgibbs			field_name[0] = 0;
27239209Sgibbs			suppress = 0;
27339209Sgibbs			break;
27439209Sgibbs
27539209Sgibbs		case 's':	/* Seek */
27639209Sgibbs			shift = 0;
27739209Sgibbs			fmt++;
27839209Sgibbs			if (*fmt == '+') {
27939209Sgibbs				plus = 1;
28039209Sgibbs				fmt++;
28139209Sgibbs			} else
28239209Sgibbs				plus = 0;
28339209Sgibbs
28439209Sgibbs			if (tolower(*fmt) == 'v') {
28539209Sgibbs				/*
28639209Sgibbs				 * You can't suppress a seek value.  You also
28739209Sgibbs				 * can't have a variable seek when you are using
28839209Sgibbs				 * "arg_put".
28939209Sgibbs				 */
290298753Sngie				width = (arg_put) ? 0 : va_arg(*ap, int);
29139209Sgibbs				fmt++;
29288090Skbyanc			} else {
29388090Skbyanc				width = strtol(fmt, &intendp, 10);
29488090Skbyanc				fmt = intendp;
29588090Skbyanc			}
29639209Sgibbs
29739209Sgibbs			if (plus)
29839209Sgibbs				databuf += width;	/* Relative seek */
29939209Sgibbs			else
30039209Sgibbs				databuf = base + width;	/* Absolute seek */
30139209Sgibbs
30239209Sgibbs			break;
30339209Sgibbs
30439209Sgibbs		case 0:
30539209Sgibbs			done = 1;
30639209Sgibbs			break;
30739209Sgibbs
30839209Sgibbs		default:
30939209Sgibbs			fprintf(stderr, "Unknown letter in format: %c\n",
31039209Sgibbs				letter);
31139209Sgibbs			fmt++;
31239209Sgibbs			break;
31339209Sgibbs		}
31439209Sgibbs	}
31539209Sgibbs
31639209Sgibbs	return (assigned);
31739209Sgibbs}
31839209Sgibbs
31939209Sgibbs/* next_field: Return the next field in a command specifier.  This
32039209Sgibbs * builds up a SCSI command using this trivial grammar:
32139209Sgibbs *
32239209Sgibbs * fields : field fields
32339209Sgibbs *        ;
32439209Sgibbs *
32539209Sgibbs * field : value
32639209Sgibbs *       | value ':' field_width
32739209Sgibbs *       ;
32839209Sgibbs *
32939209Sgibbs * field_width : digit
33039209Sgibbs *       | 'i' digit		// i2 = 2 byte integer, i3 = 3 byte integer etc.
33139209Sgibbs *       ;
33239209Sgibbs *
33339209Sgibbs * value : HEX_NUMBER
33439209Sgibbs *       | 'v'				// For indirection.
33539209Sgibbs *       ;
33639209Sgibbs *
33739209Sgibbs * Notes:
33839209Sgibbs *  Bit fields are specified MSB first to match the SCSI spec.
33939209Sgibbs *
34039209Sgibbs * Examples:
34139209Sgibbs *  TUR: "0 0 0 0 0 0"
34239209Sgibbs *  WRITE BUFFER: "38 v:3 0:2 0:3 v v:i3 v:i3 0", mode, buffer_id, list_length
34339209Sgibbs *
34439209Sgibbs * The function returns the value:
34539209Sgibbs *  0: For reached end, with error_p set if an error was found
34639209Sgibbs *  1: For valid stuff setup
34739209Sgibbs *  2: For "v" was entered as the value (implies use varargs)
34839209Sgibbs *
34939209Sgibbs */
35039209Sgibbs
35139209Sgibbsstatic int
35288090Skbyancnext_field(const char **pp, char *fmt, int *width_p, int *value_p, char *name,
35339209Sgibbs	   int n_name, int *error_p, int *suppress_p)
35439209Sgibbs{
35588090Skbyanc	const char *p = *pp;
35688090Skbyanc	char *intendp;
35739209Sgibbs
35839209Sgibbs	int something = 0;
35939209Sgibbs
36039209Sgibbs	enum {
36139209Sgibbs		BETWEEN_FIELDS,
36239209Sgibbs		START_FIELD,
36339209Sgibbs		GET_FIELD,
36439209Sgibbs		DONE,
36539209Sgibbs	} state;
36639209Sgibbs
36739209Sgibbs	int value = 0;
36839209Sgibbs	int field_size;		/* Default to byte field type... */
36939209Sgibbs	int field_width;	/* 1 byte wide */
37039209Sgibbs	int is_error = 0;
37139209Sgibbs	int suppress = 0;
37239209Sgibbs
37339209Sgibbs	field_size = 8;		/* Default to byte field type... */
37439209Sgibbs	*fmt = 'i';
37539209Sgibbs	field_width = 1;	/* 1 byte wide */
37639209Sgibbs	if (name)
37739209Sgibbs		*name = 0;
37839209Sgibbs
37939209Sgibbs	state = BETWEEN_FIELDS;
38039209Sgibbs
38139209Sgibbs	while (state != DONE) {
38239209Sgibbs		switch(state) {
38339209Sgibbs		case BETWEEN_FIELDS:
38439209Sgibbs			if (*p == 0)
38539209Sgibbs				state = DONE;
38639209Sgibbs			else if (isspace(*p))
38739209Sgibbs				p++;
38839209Sgibbs			else if (*p == '#') {
38939209Sgibbs				while (*p && *p != '\n')
39039209Sgibbs					p++;
39139209Sgibbs				if (p)
39239209Sgibbs					p++;
39339209Sgibbs			} else if (*p == '{') {
39439209Sgibbs				int i = 0;
39539209Sgibbs
39639209Sgibbs				p++;
39739209Sgibbs
39839209Sgibbs				while (*p && *p != '}') {
39939209Sgibbs					if(name && i < n_name) {
40039209Sgibbs						name[i] = *p;
40139209Sgibbs						i++;
40239209Sgibbs					}
40339209Sgibbs					p++;
40439209Sgibbs				}
40539209Sgibbs
40639209Sgibbs				if(name && i < n_name)
40739209Sgibbs					name[i] = 0;
40839209Sgibbs
40939209Sgibbs				if (*p == '}')
41039209Sgibbs					p++;
41139209Sgibbs			} else if (*p == '*') {
41239209Sgibbs				p++;
41339209Sgibbs				suppress = 1;
41439209Sgibbs			} else if (isxdigit(*p)) {
41539209Sgibbs				something = 1;
41688090Skbyanc				value = strtol(p, &intendp, 16);
41788090Skbyanc				p = intendp;
41839209Sgibbs				state = START_FIELD;
41939209Sgibbs			} else if (tolower(*p) == 'v') {
42039209Sgibbs				p++;
42139209Sgibbs				something = 2;
42239209Sgibbs				value = *value_p;
42339209Sgibbs				state = START_FIELD;
42439209Sgibbs			} else if (tolower(*p) == 'i') {
42539209Sgibbs				/*
42639209Sgibbs				 * Try to work without the "v".
42739209Sgibbs				 */
42839209Sgibbs				something = 2;
42939209Sgibbs				value = *value_p;
43039209Sgibbs				p++;
43139209Sgibbs
43239209Sgibbs				*fmt = 'i';
43339209Sgibbs				field_size = 8;
43488090Skbyanc				field_width = strtol(p, &intendp, 10);
43588090Skbyanc				p = intendp;
43639209Sgibbs				state = DONE;
43739209Sgibbs
43839209Sgibbs			} else if (tolower(*p) == 't') {
43939209Sgibbs				/*
44039209Sgibbs				 * XXX: B can't work: Sees the 'b' as a
44139209Sgibbs				 * hex digit in "isxdigit".  try "t" for
44239209Sgibbs				 * bit field.
44339209Sgibbs				 */
44439209Sgibbs				something = 2;
44539209Sgibbs				value = *value_p;
44639209Sgibbs				p++;
44739209Sgibbs
44839209Sgibbs				*fmt = 'b';
44939209Sgibbs				field_size = 1;
45088090Skbyanc				field_width = strtol(p, &intendp, 10);
45188090Skbyanc				p = intendp;
45239209Sgibbs				state = DONE;
45339209Sgibbs			} else if (tolower(*p) == 's') {
45439209Sgibbs				/* Seek */
45539209Sgibbs				*fmt = 's';
45639209Sgibbs				p++;
45739209Sgibbs				if (tolower(*p) == 'v') {
45839209Sgibbs					p++;
45939209Sgibbs					something = 2;
46039209Sgibbs					value = *value_p;
46139209Sgibbs				} else {
46239209Sgibbs					something = 1;
46388090Skbyanc					value = strtol(p, &intendp, 0);
46488090Skbyanc					p = intendp;
46539209Sgibbs				}
46639209Sgibbs				state = DONE;
46739209Sgibbs			} else {
46839209Sgibbs				fprintf(stderr, "Invalid starting "
46939209Sgibbs					"character: %c\n", *p);
47039209Sgibbs				is_error = 1;
47139209Sgibbs				state = DONE;
47239209Sgibbs			}
47339209Sgibbs			break;
47439209Sgibbs
47539209Sgibbs		case START_FIELD:
47639209Sgibbs			if (*p == ':') {
47739209Sgibbs				p++;
47839209Sgibbs				field_size = 1;		/* Default to bits
47939209Sgibbs							   when specified */
48039209Sgibbs				state = GET_FIELD;
48139209Sgibbs			} else
48239209Sgibbs				state = DONE;
48339209Sgibbs			break;
48439209Sgibbs
48539209Sgibbs		case GET_FIELD:
48639209Sgibbs			if (isdigit(*p)) {
48739209Sgibbs				*fmt = 'b';
48839209Sgibbs				field_size = 1;
48988090Skbyanc				field_width = strtol(p, &intendp, 10);
49088090Skbyanc				p = intendp;
49139209Sgibbs				state = DONE;
49239209Sgibbs			} else if (*p == 'i') {
49339209Sgibbs
49439209Sgibbs				/* Integral (bytes) */
49539209Sgibbs				p++;
49639209Sgibbs
49739209Sgibbs				*fmt = 'i';
49839209Sgibbs				field_size = 8;
49988090Skbyanc				field_width = strtol(p, &intendp, 10);
50088090Skbyanc				p = intendp;
50139209Sgibbs				state = DONE;
50239209Sgibbs			} else if (*p == 'b') {
50339209Sgibbs
50439209Sgibbs				/* Bits */
50539209Sgibbs				p++;
50639209Sgibbs
50739209Sgibbs				*fmt = 'b';
50839209Sgibbs				field_size = 1;
50988090Skbyanc				field_width = strtol(p, &intendp, 10);
51088090Skbyanc				p = intendp;
51139209Sgibbs				state = DONE;
51239209Sgibbs			} else {
51339209Sgibbs				fprintf(stderr, "Invalid startfield %c "
51439209Sgibbs					"(%02x)\n", *p, *p);
51539209Sgibbs				is_error = 1;
51639209Sgibbs				state = DONE;
51739209Sgibbs			}
51839209Sgibbs			break;
51939209Sgibbs
52039209Sgibbs		case DONE:
52139209Sgibbs			break;
52239209Sgibbs		}
52339209Sgibbs	}
52439209Sgibbs
52539209Sgibbs	if (is_error) {
52639209Sgibbs		*error_p = 1;
52739209Sgibbs		return 0;
52839209Sgibbs	}
52939209Sgibbs
53039209Sgibbs	*error_p = 0;
53139209Sgibbs	*pp = p;
53239209Sgibbs	*width_p = field_width * field_size;
53339209Sgibbs	*value_p = value;
53439209Sgibbs	*suppress_p = suppress;
53539209Sgibbs
53639209Sgibbs	return (something);
53739209Sgibbs}
53839209Sgibbs
53939209Sgibbsstatic int
54039209Sgibbsdo_encode(u_char *buff, size_t vec_max, size_t *used,
54188090Skbyanc	  int (*arg_get)(void *, char *), void *gethook, const char *fmt,
542298753Sngie	  va_list *ap)
54339209Sgibbs{
54439209Sgibbs	int ind;
54539209Sgibbs	int shift;
54639209Sgibbs	u_char val;
54739209Sgibbs	int ret;
54839209Sgibbs	int width, value, error, suppress;
54939209Sgibbs	char c;
55039209Sgibbs	int encoded = 0;
55139209Sgibbs	char field_name[80];
55239209Sgibbs
55339209Sgibbs	ind = 0;
55439209Sgibbs	shift = 0;
55539209Sgibbs	val = 0;
55639209Sgibbs
55739209Sgibbs 	while ((ret = next_field(&fmt, &c, &width, &value, field_name,
55839209Sgibbs				 sizeof(field_name), &error, &suppress))) {
55939209Sgibbs		encoded++;
56039209Sgibbs
56139209Sgibbs		if (ret == 2) {
56239209Sgibbs			if (suppress)
56339209Sgibbs				value = 0;
56439209Sgibbs			else
56539209Sgibbs				value = arg_get ?
56639209Sgibbs					(*arg_get)(gethook, field_name) :
567298753Sngie					va_arg(*ap, int);
56839209Sgibbs		}
56939209Sgibbs
57039209Sgibbs#if 0
57139209Sgibbs		printf(
57239209Sgibbs"do_encode: ret %d fmt %c width %d value %d name \"%s\" error %d suppress %d\n",
57339209Sgibbs		ret, c, width, value, field_name, error, suppress);
57439209Sgibbs#endif
57539209Sgibbs		/* Absolute seek */
57639209Sgibbs		if (c == 's') {
57739209Sgibbs			ind = value;
57839209Sgibbs			continue;
57939209Sgibbs		}
58039209Sgibbs
58139209Sgibbs		/* A width of < 8 is a bit field. */
58239209Sgibbs		if (width < 8) {
58339209Sgibbs
58439209Sgibbs			/* This is a bit field.  We start with the high bits
58539209Sgibbs			 * so it reads the same as the SCSI spec.
58639209Sgibbs			 */
58739209Sgibbs
58839209Sgibbs			shift += width;
58939209Sgibbs
59039209Sgibbs			val |= (value << (8 - shift));
59139209Sgibbs
59239209Sgibbs			if (shift == 8) {
59339209Sgibbs				if (ind < vec_max) {
59439209Sgibbs					buff[ind++] = val;
59539209Sgibbs					val = 0;
59639209Sgibbs				}
59739209Sgibbs				shift = 0;
59839209Sgibbs			}
59939209Sgibbs		} else {
60039209Sgibbs			if (shift) {
60139209Sgibbs				if (ind < vec_max) {
60239209Sgibbs					buff[ind++] = val;
60339209Sgibbs					val = 0;
60439209Sgibbs				}
60539209Sgibbs				shift = 0;
60639209Sgibbs			}
60739209Sgibbs			switch(width) {
60839209Sgibbs			case 8:		/* 1 byte integer */
60939209Sgibbs				if (ind < vec_max)
61039209Sgibbs					buff[ind++] = value;
61139209Sgibbs				break;
61239209Sgibbs
61339209Sgibbs			case 16:	/* 2 byte integer */
61439209Sgibbs				if (ind < vec_max - 2 + 1) {
61539209Sgibbs					buff[ind++] = value >> 8;
61639209Sgibbs					buff[ind++] = value;
61739209Sgibbs				}
61839209Sgibbs				break;
61939209Sgibbs
62039209Sgibbs			case 24:	/* 3 byte integer */
62139209Sgibbs				if (ind < vec_max - 3 + 1) {
62239209Sgibbs					buff[ind++] = value >> 16;
62339209Sgibbs					buff[ind++] = value >> 8;
62439209Sgibbs					buff[ind++] = value;
62539209Sgibbs				}
62639209Sgibbs				break;
62739209Sgibbs
62839209Sgibbs			case 32:	/* 4 byte integer */
62939209Sgibbs				if (ind < vec_max - 4 + 1) {
63039209Sgibbs					buff[ind++] = value >> 24;
63139209Sgibbs					buff[ind++] = value >> 16;
63239209Sgibbs					buff[ind++] = value >> 8;
63339209Sgibbs					buff[ind++] = value;
63439209Sgibbs				}
63539209Sgibbs				break;
63639209Sgibbs
63739209Sgibbs			default:
63839209Sgibbs				fprintf(stderr, "do_encode: Illegal width\n");
63939209Sgibbs				break;
64039209Sgibbs			}
64139209Sgibbs		}
64239209Sgibbs	}
64339209Sgibbs
64439209Sgibbs	/* Flush out any remaining bits
64539209Sgibbs	 */
64639209Sgibbs	if (shift && ind < vec_max) {
64739209Sgibbs		buff[ind++] = val;
64839209Sgibbs		val = 0;
64939209Sgibbs	}
65039209Sgibbs
65139209Sgibbs
65239209Sgibbs	if (used)
65339209Sgibbs		*used = ind;
65439209Sgibbs
65539209Sgibbs	if (error)
65639209Sgibbs		return -1;
65739209Sgibbs
65839209Sgibbs	return encoded;
65939209Sgibbs}
66039209Sgibbs
66139209Sgibbsint
66288090Skbyanccsio_decode(struct ccb_scsiio *csio, const char *fmt, ...)
66339209Sgibbs{
66439209Sgibbs	va_list ap;
665298753Sngie	int retval;
66639209Sgibbs
66739209Sgibbs	va_start(ap, fmt);
66839209Sgibbs
669298753Sngie	retval = do_buff_decode(csio->data_ptr, (size_t)csio->dxfer_len, 0, 0,
670298753Sngie		 fmt, &ap);
671298753Sngie
672298753Sngie	va_end(ap);
673298753Sngie
674298753Sngie	return (retval);
67539209Sgibbs}
67639209Sgibbs
67739209Sgibbsint
67888090Skbyanccsio_decode_visit(struct ccb_scsiio *csio, const char *fmt,
67939209Sgibbs		  void (*arg_put)(void *, int, void *, int, char *),
68039209Sgibbs		  void *puthook)
68139209Sgibbs{
68239209Sgibbs
68339381Sken	/*
68439381Sken	 * We need some way to output things; we can't do it without
68539381Sken	 * the arg_put function.
68639381Sken	 */
68739381Sken	if (arg_put == NULL)
688298753Sngie		return (-1);
68939209Sgibbs
690298753Sngie	return (do_buff_decode(csio->data_ptr, (size_t)csio->dxfer_len,
691298753Sngie		    arg_put, puthook, fmt, NULL));
69239209Sgibbs}
69339209Sgibbs
69439209Sgibbsint
69588090Skbyancbuff_decode(u_int8_t *buff, size_t len, const char *fmt, ...)
69639209Sgibbs{
69739209Sgibbs	va_list ap;
698298753Sngie	int retval;
69939209Sgibbs
70039209Sgibbs	va_start(ap, fmt);
70139209Sgibbs
702298753Sngie	retval = do_buff_decode(buff, len, 0, 0, fmt, &ap);
703298753Sngie
704298753Sngie	va_end(ap);
705298753Sngie
706298753Sngie	return (retval);
70739209Sgibbs}
70839209Sgibbs
70939209Sgibbsint
71088090Skbyancbuff_decode_visit(u_int8_t *buff, size_t len, const char *fmt,
71139209Sgibbs		  void (*arg_put)(void *, int, void *, int, char *),
71239209Sgibbs		  void *puthook)
71339209Sgibbs{
71439209Sgibbs
71539381Sken	/*
71639381Sken	 * We need some way to output things; we can't do it without
71739381Sken	 * the arg_put function.
71839381Sken	 */
71939381Sken	if (arg_put == NULL)
72039381Sken		return(-1);
72139209Sgibbs
722298753Sngie	return (do_buff_decode(buff, len, arg_put, puthook, fmt, NULL));
72339209Sgibbs}
72439209Sgibbs
72539209Sgibbs/*
72639209Sgibbs * Build a SCSI CCB, given the command and data pointers and a format
72739209Sgibbs * string describing the
72839209Sgibbs */
72939209Sgibbsint
73039209Sgibbscsio_build(struct ccb_scsiio *csio, u_int8_t *data_ptr, u_int32_t dxfer_len,
73188090Skbyanc	   u_int32_t flags, int retry_count, int timeout, const char *cmd_spec,
73288090Skbyanc	   ...)
73339209Sgibbs{
73439381Sken	size_t cmdlen;
73539209Sgibbs	int retval;
73639209Sgibbs	va_list ap;
73739209Sgibbs
73839209Sgibbs	if (csio == NULL)
739298753Sngie		return (0);
74039209Sgibbs
74139209Sgibbs	bzero(csio, sizeof(struct ccb_scsiio));
74239209Sgibbs
74339209Sgibbs	va_start(ap, cmd_spec);
74439209Sgibbs
74539209Sgibbs	if ((retval = do_encode(csio->cdb_io.cdb_bytes, SCSI_MAX_CDBLEN,
746298753Sngie				&cmdlen, NULL, NULL, cmd_spec, &ap)) == -1)
747298753Sngie		goto done;
74839209Sgibbs
74939209Sgibbs	cam_fill_csio(csio,
75039209Sgibbs		      /* retries */ retry_count,
75139209Sgibbs		      /* cbfcnp */ NULL,
75239209Sgibbs		      /* flags */ flags,
75339209Sgibbs		      /* tag_action */ MSG_SIMPLE_Q_TAG,
75439209Sgibbs		      /* data_ptr */ data_ptr,
75539209Sgibbs		      /* dxfer_len */ dxfer_len,
75639209Sgibbs		      /* sense_len */ SSD_FULL_SIZE,
75739209Sgibbs		      /* cdb_len */ cmdlen,
75839209Sgibbs		      /* timeout */ timeout ? timeout : 5000);
75939209Sgibbs
760298753Sngiedone:
761298753Sngie	va_end(ap);
762298753Sngie
763298753Sngie	return (retval);
76439209Sgibbs}
76539209Sgibbs
76639209Sgibbsint
76739209Sgibbscsio_build_visit(struct ccb_scsiio *csio, u_int8_t *data_ptr,
76839209Sgibbs		 u_int32_t dxfer_len, u_int32_t flags, int retry_count,
76988090Skbyanc		 int timeout, const char *cmd_spec,
77039209Sgibbs		 int (*arg_get)(void *hook, char *field_name), void *gethook)
77139209Sgibbs{
77239381Sken	size_t cmdlen;
77339381Sken	int retval;
77439209Sgibbs
77539209Sgibbs	if (csio == NULL)
77639209Sgibbs		return(0);
77739209Sgibbs
77839381Sken	/*
77939381Sken	 * We need something to encode, but we can't get it without the
78039381Sken	 * arg_get function.
78139381Sken	 */
78239381Sken	if (arg_get == NULL)
78339381Sken		return(-1);
78439209Sgibbs
78539209Sgibbs	bzero(csio, sizeof(struct ccb_scsiio));
78639209Sgibbs
78739209Sgibbs	if ((retval = do_encode(csio->cdb_io.cdb_bytes, SCSI_MAX_CDBLEN,
788298753Sngie				&cmdlen, arg_get, gethook, cmd_spec, NULL)) == -1)
78939209Sgibbs		return(retval);
79039209Sgibbs
79139209Sgibbs	cam_fill_csio(csio,
79239209Sgibbs		      /* retries */ retry_count,
79339209Sgibbs		      /* cbfcnp */ NULL,
79439209Sgibbs		      /* flags */ flags,
79539209Sgibbs		      /* tag_action */ MSG_SIMPLE_Q_TAG,
79639209Sgibbs		      /* data_ptr */ data_ptr,
79739209Sgibbs		      /* dxfer_len */ dxfer_len,
79839209Sgibbs		      /* sense_len */ SSD_FULL_SIZE,
79939209Sgibbs		      /* cdb_len */ cmdlen,
80039209Sgibbs		      /* timeout */ timeout ? timeout : 5000);
80139209Sgibbs
80239209Sgibbs	return(retval);
80339209Sgibbs}
80439209Sgibbs
80539209Sgibbsint
80688090Skbyanccsio_encode(struct ccb_scsiio *csio, const char *fmt, ...)
80739209Sgibbs{
80839209Sgibbs	va_list ap;
809298753Sngie	int retval;
81039209Sgibbs
81139209Sgibbs	if (csio == NULL)
812298753Sngie		return (0);
81339209Sgibbs
81439209Sgibbs	va_start(ap, fmt);
81539209Sgibbs
816298753Sngie	retval = do_encode(csio->data_ptr, csio->dxfer_len, 0, 0, 0, fmt, &ap);
817298753Sngie
818298753Sngie	va_end(ap);
819298753Sngie
820298753Sngie	return (retval);
82139209Sgibbs}
82239209Sgibbs
82339209Sgibbsint
82488090Skbyancbuff_encode_visit(u_int8_t *buff, size_t len, const char *fmt,
82539209Sgibbs		  int (*arg_get)(void *hook, char *field_name), void *gethook)
82639209Sgibbs{
82739209Sgibbs
82839381Sken	/*
82939381Sken	 * We need something to encode, but we can't get it without the
83039381Sken	 * arg_get function.
83139381Sken	 */
83239381Sken	if (arg_get == NULL)
83339381Sken		return(-1);
83439209Sgibbs
835298753Sngie	return (do_encode(buff, len, 0, arg_get, gethook, fmt, NULL));
83639209Sgibbs}
83739209Sgibbs
83839209Sgibbsint
83988090Skbyanccsio_encode_visit(struct ccb_scsiio *csio, const char *fmt,
84039209Sgibbs		  int (*arg_get)(void *hook, char *field_name), void *gethook)
84139209Sgibbs{
84239209Sgibbs
84339381Sken	/*
84439381Sken	 * We need something to encode, but we can't get it without the
84539381Sken	 * arg_get function.
84639381Sken	 */
84739381Sken	if (arg_get == NULL)
84839381Sken		return(-1);
84939209Sgibbs
850298753Sngie	return (do_encode(csio->data_ptr, csio->dxfer_len, 0, arg_get,
851298753Sngie			 gethook, fmt, NULL));
85239209Sgibbs}
853