139209Sgibbs/*
239209Sgibbs * Taken from the original FreeBSD user SCSI library.
339209Sgibbs */
4331722Seadler/* 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: stable/11/lib/libcam/scsi_cmdparse.c 315123 2017-03-12 04:53:48Z 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
103312564Smavdo_buff_decode(u_int8_t *buff, size_t len,
10439209Sgibbs	       void (*arg_put)(void *, int , void *, int, char *),
105298753Sngie	       void *puthook, const char *fmt, va_list *ap)
10639209Sgibbs{
107312564Smav	int ind = 0;
10839209Sgibbs	int assigned = 0;
10939209Sgibbs	int width;
11039209Sgibbs	int suppress;
11139209Sgibbs	int plus;
11239209Sgibbs	int done = 0;
11339209Sgibbs	static u_char mask[] = {0, 0x01, 0x03, 0x07, 0x0f,
11439209Sgibbs				   0x1f, 0x3f, 0x7f, 0xff};
11539209Sgibbs	int value;
11688090Skbyanc	char *intendp;
11739209Sgibbs	char letter;
11839209Sgibbs	char field_name[80];
11939209Sgibbs
120312564Smav#define ARG_PUT(ARG) \
121312564Smav	do { \
122312564Smav		if (!suppress) { \
12339209Sgibbs			if (arg_put) \
124312564Smav				(*arg_put)(puthook, (letter == 't' ? 'b' : \
125312564Smav				    letter), (void *)((long)(ARG)), width, \
126312564Smav				    field_name); \
12739209Sgibbs			else \
128298753Sngie				*(va_arg(*ap, int *)) = (ARG); \
12939209Sgibbs			assigned++; \
13039209Sgibbs		} \
131315123Sngie		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;
138315123Sngie	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			}
172315123Sngie			if (*fmt != '\0')
17339209Sgibbs				fmt++;	/* Skip '}' */
174315123Sngie			field_name[i] = '\0';
17539209Sgibbs			break;
17639209Sgibbs		}
17739209Sgibbs
17839209Sgibbs		case 't':	/* Bit (field) */
17939209Sgibbs		case 'b':	/* Bits */
18039209Sgibbs			fmt++;
18188090Skbyanc			width = strtol(fmt, &intendp, 10);
18288090Skbyanc			fmt = intendp;
18339209Sgibbs			if (width > 8)
18439209Sgibbs				done = 1;
18539209Sgibbs			else {
18639209Sgibbs				if (shift <= 0) {
187312564Smav					if (ind >= len) {
188312564Smav						done = 1;
189312564Smav						break;
190312564Smav					}
191312564Smav					bits = buff[ind++];
19239209Sgibbs					shift = 8;
19339209Sgibbs				}
19439209Sgibbs				value = (bits >> (shift - width)) &
19539209Sgibbs					 mask[width];
19639209Sgibbs
19739209Sgibbs#if 0
19839209Sgibbs				printf("shift %2d bits %02x value %02x width %2d mask %02x\n",
19939209Sgibbs				shift, bits, value, width, mask[width]);
20039209Sgibbs#endif
20139209Sgibbs
20239209Sgibbs				ARG_PUT(value);
20339209Sgibbs
20439209Sgibbs				shift -= width;
20539209Sgibbs			}
20639209Sgibbs			break;
20739209Sgibbs
20839209Sgibbs		case 'i':	/* Integral values */
20939209Sgibbs			shift = 0;
21039209Sgibbs			fmt++;
21188090Skbyanc			width = strtol(fmt, &intendp, 10);
21288090Skbyanc			fmt = intendp;
213312564Smav			if (ind + width > len) {
214312564Smav				done = 1;
215312564Smav				break;
216312564Smav			}
21739209Sgibbs			switch(width) {
21839209Sgibbs			case 1:
219312564Smav				ARG_PUT(buff[ind]);
220312564Smav				ind++;
22139209Sgibbs				break;
22239209Sgibbs
22339209Sgibbs			case 2:
224312564Smav				ARG_PUT(buff[ind] << 8 | buff[ind + 1]);
225312564Smav				ind += 2;
22639209Sgibbs				break;
22739209Sgibbs
22839209Sgibbs			case 3:
229312564Smav				ARG_PUT(buff[ind] << 16 |
230312564Smav					buff[ind + 1] << 8 | buff[ind + 2]);
231312564Smav				ind += 3;
23239209Sgibbs				break;
23339209Sgibbs
23439209Sgibbs			case 4:
235312564Smav				ARG_PUT(buff[ind] << 24 | buff[ind + 1] << 16 |
236312564Smav					buff[ind + 2] << 8 | buff[ind + 3]);
237312564Smav				ind += 4;
23839209Sgibbs				break;
23939209Sgibbs
24039209Sgibbs			default:
24139209Sgibbs				done = 1;
24239209Sgibbs				break;
24339209Sgibbs			}
24439209Sgibbs
24539209Sgibbs			break;
24639209Sgibbs
24739209Sgibbs		case 'c':	/* Characters (i.e., not swapped) */
248312564Smav		case 'z':	/* Characters with zeroed trailing spaces */
24939209Sgibbs			shift = 0;
25039209Sgibbs			fmt++;
25188090Skbyanc			width = strtol(fmt, &intendp, 10);
25288090Skbyanc			fmt = intendp;
253312564Smav			if (ind + width > len) {
254312564Smav				done = 1;
255312564Smav				break;
256312564Smav			}
25739209Sgibbs			if (!suppress) {
258315123Sngie				if (arg_put != NULL)
25939209Sgibbs					(*arg_put)(puthook,
260312564Smav					    (letter == 't' ? 'b' : letter),
261312564Smav					    &buff[ind], width, field_name);
26239209Sgibbs				else {
26339209Sgibbs					char *dest;
264298753Sngie					dest = va_arg(*ap, char *);
265312564Smav					bcopy(&buff[ind], dest, width);
26639209Sgibbs					if (letter == 'z') {
26739209Sgibbs						char *p;
26839209Sgibbs						for (p = dest + width - 1;
269312564Smav						    p >= dest && *p == ' ';
270312564Smav						    p--)
271315123Sngie							*p = '\0';
27239209Sgibbs					}
27339209Sgibbs				}
27439209Sgibbs				assigned++;
27539209Sgibbs			}
276312564Smav			ind += width;
27739209Sgibbs			field_name[0] = 0;
27839209Sgibbs			suppress = 0;
27939209Sgibbs			break;
28039209Sgibbs
28139209Sgibbs		case 's':	/* Seek */
28239209Sgibbs			shift = 0;
28339209Sgibbs			fmt++;
28439209Sgibbs			if (*fmt == '+') {
28539209Sgibbs				plus = 1;
28639209Sgibbs				fmt++;
28739209Sgibbs			} else
28839209Sgibbs				plus = 0;
28939209Sgibbs
29039209Sgibbs			if (tolower(*fmt) == 'v') {
29139209Sgibbs				/*
29239209Sgibbs				 * You can't suppress a seek value.  You also
29339209Sgibbs				 * can't have a variable seek when you are using
29439209Sgibbs				 * "arg_put".
29539209Sgibbs				 */
296298753Sngie				width = (arg_put) ? 0 : va_arg(*ap, int);
29739209Sgibbs				fmt++;
29888090Skbyanc			} else {
29988090Skbyanc				width = strtol(fmt, &intendp, 10);
30088090Skbyanc				fmt = intendp;
30188090Skbyanc			}
30239209Sgibbs
30339209Sgibbs			if (plus)
304312564Smav				ind += width;	/* Relative seek */
30539209Sgibbs			else
306312564Smav				ind = width;	/* Absolute seek */
30739209Sgibbs
30839209Sgibbs			break;
30939209Sgibbs
31039209Sgibbs		case 0:
31139209Sgibbs			done = 1;
31239209Sgibbs			break;
31339209Sgibbs
31439209Sgibbs		default:
31539209Sgibbs			fprintf(stderr, "Unknown letter in format: %c\n",
31639209Sgibbs				letter);
31739209Sgibbs			fmt++;
31839209Sgibbs			break;
31939209Sgibbs		}
32039209Sgibbs	}
32139209Sgibbs
32239209Sgibbs	return (assigned);
32339209Sgibbs}
32439209Sgibbs
32539209Sgibbs/* next_field: Return the next field in a command specifier.  This
32639209Sgibbs * builds up a SCSI command using this trivial grammar:
32739209Sgibbs *
32839209Sgibbs * fields : field fields
32939209Sgibbs *        ;
33039209Sgibbs *
33139209Sgibbs * field : value
33239209Sgibbs *       | value ':' field_width
33339209Sgibbs *       ;
33439209Sgibbs *
33539209Sgibbs * field_width : digit
33639209Sgibbs *       | 'i' digit		// i2 = 2 byte integer, i3 = 3 byte integer etc.
33739209Sgibbs *       ;
33839209Sgibbs *
33939209Sgibbs * value : HEX_NUMBER
34039209Sgibbs *       | 'v'				// For indirection.
34139209Sgibbs *       ;
34239209Sgibbs *
34339209Sgibbs * Notes:
34439209Sgibbs *  Bit fields are specified MSB first to match the SCSI spec.
34539209Sgibbs *
34639209Sgibbs * Examples:
34739209Sgibbs *  TUR: "0 0 0 0 0 0"
34839209Sgibbs *  WRITE BUFFER: "38 v:3 0:2 0:3 v v:i3 v:i3 0", mode, buffer_id, list_length
34939209Sgibbs *
35039209Sgibbs * The function returns the value:
35139209Sgibbs *  0: For reached end, with error_p set if an error was found
35239209Sgibbs *  1: For valid stuff setup
35339209Sgibbs *  2: For "v" was entered as the value (implies use varargs)
35439209Sgibbs *
35539209Sgibbs */
35639209Sgibbs
35739209Sgibbsstatic int
35888090Skbyancnext_field(const char **pp, char *fmt, int *width_p, int *value_p, char *name,
35939209Sgibbs	   int n_name, int *error_p, int *suppress_p)
36039209Sgibbs{
36188090Skbyanc	const char *p = *pp;
36288090Skbyanc	char *intendp;
36339209Sgibbs
36439209Sgibbs	int something = 0;
36539209Sgibbs
36639209Sgibbs	enum {
36739209Sgibbs		BETWEEN_FIELDS,
36839209Sgibbs		START_FIELD,
36939209Sgibbs		GET_FIELD,
37039209Sgibbs		DONE,
37139209Sgibbs	} state;
37239209Sgibbs
37339209Sgibbs	int value = 0;
37439209Sgibbs	int field_size;		/* Default to byte field type... */
37539209Sgibbs	int field_width;	/* 1 byte wide */
37639209Sgibbs	int is_error = 0;
37739209Sgibbs	int suppress = 0;
37839209Sgibbs
37939209Sgibbs	field_size = 8;		/* Default to byte field type... */
38039209Sgibbs	*fmt = 'i';
38139209Sgibbs	field_width = 1;	/* 1 byte wide */
382315123Sngie	if (name != NULL)
383315123Sngie		*name = '\0';
38439209Sgibbs
38539209Sgibbs	state = BETWEEN_FIELDS;
38639209Sgibbs
38739209Sgibbs	while (state != DONE) {
38839209Sgibbs		switch(state) {
38939209Sgibbs		case BETWEEN_FIELDS:
390315123Sngie			if (*p == '\0')
39139209Sgibbs				state = DONE;
39239209Sgibbs			else if (isspace(*p))
39339209Sgibbs				p++;
39439209Sgibbs			else if (*p == '#') {
39539209Sgibbs				while (*p && *p != '\n')
39639209Sgibbs					p++;
397315123Sngie				if (*p != '\0')
39839209Sgibbs					p++;
39939209Sgibbs			} else if (*p == '{') {
40039209Sgibbs				int i = 0;
40139209Sgibbs
40239209Sgibbs				p++;
40339209Sgibbs
40439209Sgibbs				while (*p && *p != '}') {
40539209Sgibbs					if(name && i < n_name) {
40639209Sgibbs						name[i] = *p;
40739209Sgibbs						i++;
40839209Sgibbs					}
40939209Sgibbs					p++;
41039209Sgibbs				}
41139209Sgibbs
41239209Sgibbs				if(name && i < n_name)
413315123Sngie					name[i] = '\0';
41439209Sgibbs
41539209Sgibbs				if (*p == '}')
41639209Sgibbs					p++;
41739209Sgibbs			} else if (*p == '*') {
41839209Sgibbs				p++;
41939209Sgibbs				suppress = 1;
42039209Sgibbs			} else if (isxdigit(*p)) {
42139209Sgibbs				something = 1;
42288090Skbyanc				value = strtol(p, &intendp, 16);
42388090Skbyanc				p = intendp;
42439209Sgibbs				state = START_FIELD;
42539209Sgibbs			} else if (tolower(*p) == 'v') {
42639209Sgibbs				p++;
42739209Sgibbs				something = 2;
42839209Sgibbs				value = *value_p;
42939209Sgibbs				state = START_FIELD;
43039209Sgibbs			} else if (tolower(*p) == 'i') {
43139209Sgibbs				/*
43239209Sgibbs				 * Try to work without the "v".
43339209Sgibbs				 */
43439209Sgibbs				something = 2;
43539209Sgibbs				value = *value_p;
43639209Sgibbs				p++;
43739209Sgibbs
43839209Sgibbs				*fmt = 'i';
43939209Sgibbs				field_size = 8;
44088090Skbyanc				field_width = strtol(p, &intendp, 10);
44188090Skbyanc				p = intendp;
44239209Sgibbs				state = DONE;
44339209Sgibbs
44439209Sgibbs			} else if (tolower(*p) == 't') {
44539209Sgibbs				/*
44639209Sgibbs				 * XXX: B can't work: Sees the 'b' as a
44739209Sgibbs				 * hex digit in "isxdigit".  try "t" for
44839209Sgibbs				 * bit field.
44939209Sgibbs				 */
45039209Sgibbs				something = 2;
45139209Sgibbs				value = *value_p;
45239209Sgibbs				p++;
45339209Sgibbs
45439209Sgibbs				*fmt = 'b';
45539209Sgibbs				field_size = 1;
45688090Skbyanc				field_width = strtol(p, &intendp, 10);
45788090Skbyanc				p = intendp;
45839209Sgibbs				state = DONE;
45939209Sgibbs			} else if (tolower(*p) == 's') {
46039209Sgibbs				/* Seek */
46139209Sgibbs				*fmt = 's';
46239209Sgibbs				p++;
46339209Sgibbs				if (tolower(*p) == 'v') {
46439209Sgibbs					p++;
46539209Sgibbs					something = 2;
46639209Sgibbs					value = *value_p;
46739209Sgibbs				} else {
46839209Sgibbs					something = 1;
46988090Skbyanc					value = strtol(p, &intendp, 0);
47088090Skbyanc					p = intendp;
47139209Sgibbs				}
47239209Sgibbs				state = DONE;
47339209Sgibbs			} else {
47439209Sgibbs				fprintf(stderr, "Invalid starting "
47539209Sgibbs					"character: %c\n", *p);
47639209Sgibbs				is_error = 1;
47739209Sgibbs				state = DONE;
47839209Sgibbs			}
47939209Sgibbs			break;
48039209Sgibbs
48139209Sgibbs		case START_FIELD:
48239209Sgibbs			if (*p == ':') {
48339209Sgibbs				p++;
48439209Sgibbs				field_size = 1;		/* Default to bits
48539209Sgibbs							   when specified */
48639209Sgibbs				state = GET_FIELD;
48739209Sgibbs			} else
48839209Sgibbs				state = DONE;
48939209Sgibbs			break;
49039209Sgibbs
49139209Sgibbs		case GET_FIELD:
49239209Sgibbs			if (isdigit(*p)) {
49339209Sgibbs				*fmt = 'b';
49439209Sgibbs				field_size = 1;
49588090Skbyanc				field_width = strtol(p, &intendp, 10);
49688090Skbyanc				p = intendp;
49739209Sgibbs				state = DONE;
49839209Sgibbs			} else if (*p == 'i') {
49939209Sgibbs
50039209Sgibbs				/* Integral (bytes) */
50139209Sgibbs				p++;
50239209Sgibbs
50339209Sgibbs				*fmt = 'i';
50439209Sgibbs				field_size = 8;
50588090Skbyanc				field_width = strtol(p, &intendp, 10);
50688090Skbyanc				p = intendp;
50739209Sgibbs				state = DONE;
50839209Sgibbs			} else if (*p == 'b') {
50939209Sgibbs
51039209Sgibbs				/* Bits */
51139209Sgibbs				p++;
51239209Sgibbs
51339209Sgibbs				*fmt = 'b';
51439209Sgibbs				field_size = 1;
51588090Skbyanc				field_width = strtol(p, &intendp, 10);
51688090Skbyanc				p = intendp;
51739209Sgibbs				state = DONE;
51839209Sgibbs			} else {
51939209Sgibbs				fprintf(stderr, "Invalid startfield %c "
52039209Sgibbs					"(%02x)\n", *p, *p);
52139209Sgibbs				is_error = 1;
52239209Sgibbs				state = DONE;
52339209Sgibbs			}
52439209Sgibbs			break;
52539209Sgibbs
52639209Sgibbs		case DONE:
52739209Sgibbs			break;
52839209Sgibbs		}
52939209Sgibbs	}
53039209Sgibbs
53139209Sgibbs	if (is_error) {
53239209Sgibbs		*error_p = 1;
533315123Sngie		return (0);
53439209Sgibbs	}
53539209Sgibbs
53639209Sgibbs	*error_p = 0;
53739209Sgibbs	*pp = p;
53839209Sgibbs	*width_p = field_width * field_size;
53939209Sgibbs	*value_p = value;
54039209Sgibbs	*suppress_p = suppress;
54139209Sgibbs
54239209Sgibbs	return (something);
54339209Sgibbs}
54439209Sgibbs
54539209Sgibbsstatic int
54639209Sgibbsdo_encode(u_char *buff, size_t vec_max, size_t *used,
54788090Skbyanc	  int (*arg_get)(void *, char *), void *gethook, const char *fmt,
548298753Sngie	  va_list *ap)
54939209Sgibbs{
55039209Sgibbs	int ind;
55139209Sgibbs	int shift;
55239209Sgibbs	u_char val;
55339209Sgibbs	int ret;
55439209Sgibbs	int width, value, error, suppress;
55539209Sgibbs	char c;
55639209Sgibbs	int encoded = 0;
55739209Sgibbs	char field_name[80];
55839209Sgibbs
55939209Sgibbs	ind = 0;
56039209Sgibbs	shift = 0;
56139209Sgibbs	val = 0;
56239209Sgibbs
56339209Sgibbs 	while ((ret = next_field(&fmt, &c, &width, &value, field_name,
56439209Sgibbs				 sizeof(field_name), &error, &suppress))) {
56539209Sgibbs		encoded++;
56639209Sgibbs
56739209Sgibbs		if (ret == 2) {
56839209Sgibbs			if (suppress)
56939209Sgibbs				value = 0;
57039209Sgibbs			else
571315123Sngie				value = arg_get != NULL ?
57239209Sgibbs					(*arg_get)(gethook, field_name) :
573298753Sngie					va_arg(*ap, int);
57439209Sgibbs		}
57539209Sgibbs
57639209Sgibbs#if 0
57739209Sgibbs		printf(
57839209Sgibbs"do_encode: ret %d fmt %c width %d value %d name \"%s\" error %d suppress %d\n",
57939209Sgibbs		ret, c, width, value, field_name, error, suppress);
58039209Sgibbs#endif
58139209Sgibbs		/* Absolute seek */
58239209Sgibbs		if (c == 's') {
58339209Sgibbs			ind = value;
58439209Sgibbs			continue;
58539209Sgibbs		}
58639209Sgibbs
58739209Sgibbs		/* A width of < 8 is a bit field. */
58839209Sgibbs		if (width < 8) {
58939209Sgibbs
59039209Sgibbs			/* This is a bit field.  We start with the high bits
59139209Sgibbs			 * so it reads the same as the SCSI spec.
59239209Sgibbs			 */
59339209Sgibbs
59439209Sgibbs			shift += width;
59539209Sgibbs
59639209Sgibbs			val |= (value << (8 - shift));
59739209Sgibbs
59839209Sgibbs			if (shift == 8) {
59939209Sgibbs				if (ind < vec_max) {
60039209Sgibbs					buff[ind++] = val;
60139209Sgibbs					val = 0;
60239209Sgibbs				}
60339209Sgibbs				shift = 0;
60439209Sgibbs			}
60539209Sgibbs		} else {
60639209Sgibbs			if (shift) {
60739209Sgibbs				if (ind < vec_max) {
60839209Sgibbs					buff[ind++] = val;
60939209Sgibbs					val = 0;
61039209Sgibbs				}
61139209Sgibbs				shift = 0;
61239209Sgibbs			}
61339209Sgibbs			switch(width) {
61439209Sgibbs			case 8:		/* 1 byte integer */
61539209Sgibbs				if (ind < vec_max)
61639209Sgibbs					buff[ind++] = value;
61739209Sgibbs				break;
61839209Sgibbs
61939209Sgibbs			case 16:	/* 2 byte integer */
62039209Sgibbs				if (ind < vec_max - 2 + 1) {
62139209Sgibbs					buff[ind++] = value >> 8;
62239209Sgibbs					buff[ind++] = value;
62339209Sgibbs				}
62439209Sgibbs				break;
62539209Sgibbs
62639209Sgibbs			case 24:	/* 3 byte integer */
62739209Sgibbs				if (ind < vec_max - 3 + 1) {
62839209Sgibbs					buff[ind++] = value >> 16;
62939209Sgibbs					buff[ind++] = value >> 8;
63039209Sgibbs					buff[ind++] = value;
63139209Sgibbs				}
63239209Sgibbs				break;
63339209Sgibbs
63439209Sgibbs			case 32:	/* 4 byte integer */
63539209Sgibbs				if (ind < vec_max - 4 + 1) {
63639209Sgibbs					buff[ind++] = value >> 24;
63739209Sgibbs					buff[ind++] = value >> 16;
63839209Sgibbs					buff[ind++] = value >> 8;
63939209Sgibbs					buff[ind++] = value;
64039209Sgibbs				}
64139209Sgibbs				break;
64239209Sgibbs
64339209Sgibbs			default:
64439209Sgibbs				fprintf(stderr, "do_encode: Illegal width\n");
64539209Sgibbs				break;
64639209Sgibbs			}
64739209Sgibbs		}
64839209Sgibbs	}
64939209Sgibbs
65039209Sgibbs	/* Flush out any remaining bits
65139209Sgibbs	 */
65239209Sgibbs	if (shift && ind < vec_max) {
65339209Sgibbs		buff[ind++] = val;
65439209Sgibbs		val = 0;
65539209Sgibbs	}
65639209Sgibbs
65739209Sgibbs
65839209Sgibbs	if (used)
65939209Sgibbs		*used = ind;
66039209Sgibbs
66139209Sgibbs	if (error)
662315123Sngie		return (-1);
66339209Sgibbs
664315123Sngie	return (encoded);
66539209Sgibbs}
66639209Sgibbs
66739209Sgibbsint
66888090Skbyanccsio_decode(struct ccb_scsiio *csio, const char *fmt, ...)
66939209Sgibbs{
67039209Sgibbs	va_list ap;
671298753Sngie	int retval;
67239209Sgibbs
67339209Sgibbs	va_start(ap, fmt);
67439209Sgibbs
675315123Sngie	retval = do_buff_decode(csio->data_ptr, (size_t)csio->dxfer_len,
676315123Sngie	    NULL, NULL, fmt, &ap);
677298753Sngie
678298753Sngie	va_end(ap);
679298753Sngie
680298753Sngie	return (retval);
68139209Sgibbs}
68239209Sgibbs
68339209Sgibbsint
68488090Skbyanccsio_decode_visit(struct ccb_scsiio *csio, const char *fmt,
68539209Sgibbs		  void (*arg_put)(void *, int, void *, int, char *),
68639209Sgibbs		  void *puthook)
68739209Sgibbs{
68839209Sgibbs
68939381Sken	/*
69039381Sken	 * We need some way to output things; we can't do it without
69139381Sken	 * the arg_put function.
69239381Sken	 */
69339381Sken	if (arg_put == NULL)
694298753Sngie		return (-1);
69539209Sgibbs
696298753Sngie	return (do_buff_decode(csio->data_ptr, (size_t)csio->dxfer_len,
697298753Sngie		    arg_put, puthook, fmt, NULL));
69839209Sgibbs}
69939209Sgibbs
70039209Sgibbsint
70188090Skbyancbuff_decode(u_int8_t *buff, size_t len, const char *fmt, ...)
70239209Sgibbs{
70339209Sgibbs	va_list ap;
704298753Sngie	int retval;
70539209Sgibbs
70639209Sgibbs	va_start(ap, fmt);
70739209Sgibbs
708315123Sngie	retval = do_buff_decode(buff, len, NULL, NULL, fmt, &ap);
709298753Sngie
710298753Sngie	va_end(ap);
711298753Sngie
712298753Sngie	return (retval);
71339209Sgibbs}
71439209Sgibbs
71539209Sgibbsint
71688090Skbyancbuff_decode_visit(u_int8_t *buff, size_t len, const char *fmt,
71739209Sgibbs		  void (*arg_put)(void *, int, void *, int, char *),
71839209Sgibbs		  void *puthook)
71939209Sgibbs{
72039209Sgibbs
72139381Sken	/*
72239381Sken	 * We need some way to output things; we can't do it without
72339381Sken	 * the arg_put function.
72439381Sken	 */
72539381Sken	if (arg_put == NULL)
726315123Sngie		return (-1);
72739209Sgibbs
728298753Sngie	return (do_buff_decode(buff, len, arg_put, puthook, fmt, NULL));
72939209Sgibbs}
73039209Sgibbs
73139209Sgibbs/*
73239209Sgibbs * Build a SCSI CCB, given the command and data pointers and a format
73339209Sgibbs * string describing the
73439209Sgibbs */
73539209Sgibbsint
73639209Sgibbscsio_build(struct ccb_scsiio *csio, u_int8_t *data_ptr, u_int32_t dxfer_len,
73788090Skbyanc	   u_int32_t flags, int retry_count, int timeout, const char *cmd_spec,
73888090Skbyanc	   ...)
73939209Sgibbs{
74039381Sken	size_t cmdlen;
74139209Sgibbs	int retval;
74239209Sgibbs	va_list ap;
74339209Sgibbs
74439209Sgibbs	if (csio == NULL)
745298753Sngie		return (0);
74639209Sgibbs
74739209Sgibbs	bzero(csio, sizeof(struct ccb_scsiio));
74839209Sgibbs
74939209Sgibbs	va_start(ap, cmd_spec);
75039209Sgibbs
75139209Sgibbs	if ((retval = do_encode(csio->cdb_io.cdb_bytes, SCSI_MAX_CDBLEN,
752298753Sngie				&cmdlen, NULL, NULL, cmd_spec, &ap)) == -1)
753298753Sngie		goto done;
75439209Sgibbs
75539209Sgibbs	cam_fill_csio(csio,
75639209Sgibbs		      /* retries */ retry_count,
75739209Sgibbs		      /* cbfcnp */ NULL,
75839209Sgibbs		      /* flags */ flags,
75939209Sgibbs		      /* tag_action */ MSG_SIMPLE_Q_TAG,
76039209Sgibbs		      /* data_ptr */ data_ptr,
76139209Sgibbs		      /* dxfer_len */ dxfer_len,
76239209Sgibbs		      /* sense_len */ SSD_FULL_SIZE,
76339209Sgibbs		      /* cdb_len */ cmdlen,
76439209Sgibbs		      /* timeout */ timeout ? timeout : 5000);
76539209Sgibbs
766298753Sngiedone:
767298753Sngie	va_end(ap);
768298753Sngie
769298753Sngie	return (retval);
77039209Sgibbs}
77139209Sgibbs
77239209Sgibbsint
77339209Sgibbscsio_build_visit(struct ccb_scsiio *csio, u_int8_t *data_ptr,
77439209Sgibbs		 u_int32_t dxfer_len, u_int32_t flags, int retry_count,
77588090Skbyanc		 int timeout, const char *cmd_spec,
77639209Sgibbs		 int (*arg_get)(void *hook, char *field_name), void *gethook)
77739209Sgibbs{
77839381Sken	size_t cmdlen;
77939381Sken	int retval;
78039209Sgibbs
78139209Sgibbs	if (csio == NULL)
782315123Sngie		return (0);
78339209Sgibbs
78439381Sken	/*
78539381Sken	 * We need something to encode, but we can't get it without the
78639381Sken	 * arg_get function.
78739381Sken	 */
78839381Sken	if (arg_get == NULL)
789315123Sngie		return (-1);
79039209Sgibbs
79139209Sgibbs	bzero(csio, sizeof(struct ccb_scsiio));
79239209Sgibbs
79339209Sgibbs	if ((retval = do_encode(csio->cdb_io.cdb_bytes, SCSI_MAX_CDBLEN,
794298753Sngie				&cmdlen, arg_get, gethook, cmd_spec, NULL)) == -1)
795315123Sngie		return (retval);
79639209Sgibbs
79739209Sgibbs	cam_fill_csio(csio,
79839209Sgibbs		      /* retries */ retry_count,
79939209Sgibbs		      /* cbfcnp */ NULL,
80039209Sgibbs		      /* flags */ flags,
80139209Sgibbs		      /* tag_action */ MSG_SIMPLE_Q_TAG,
80239209Sgibbs		      /* data_ptr */ data_ptr,
80339209Sgibbs		      /* dxfer_len */ dxfer_len,
80439209Sgibbs		      /* sense_len */ SSD_FULL_SIZE,
80539209Sgibbs		      /* cdb_len */ cmdlen,
80639209Sgibbs		      /* timeout */ timeout ? timeout : 5000);
80739209Sgibbs
808315123Sngie	return (retval);
80939209Sgibbs}
81039209Sgibbs
81139209Sgibbsint
81288090Skbyanccsio_encode(struct ccb_scsiio *csio, const char *fmt, ...)
81339209Sgibbs{
81439209Sgibbs	va_list ap;
815298753Sngie	int retval;
81639209Sgibbs
81739209Sgibbs	if (csio == NULL)
818298753Sngie		return (0);
81939209Sgibbs
82039209Sgibbs	va_start(ap, fmt);
82139209Sgibbs
822315123Sngie	retval = do_encode(csio->data_ptr, csio->dxfer_len, NULL, NULL, NULL,
823315123Sngie	    fmt, &ap);
824298753Sngie
825298753Sngie	va_end(ap);
826298753Sngie
827298753Sngie	return (retval);
82839209Sgibbs}
82939209Sgibbs
83039209Sgibbsint
83188090Skbyancbuff_encode_visit(u_int8_t *buff, size_t len, const char *fmt,
83239209Sgibbs		  int (*arg_get)(void *hook, char *field_name), void *gethook)
83339209Sgibbs{
83439209Sgibbs
83539381Sken	/*
83639381Sken	 * We need something to encode, but we can't get it without the
83739381Sken	 * arg_get function.
83839381Sken	 */
83939381Sken	if (arg_get == NULL)
840315123Sngie		return (-1);
84139209Sgibbs
842315123Sngie	return (do_encode(buff, len, NULL, arg_get, gethook, fmt, NULL));
84339209Sgibbs}
84439209Sgibbs
84539209Sgibbsint
84688090Skbyanccsio_encode_visit(struct ccb_scsiio *csio, const char *fmt,
84739209Sgibbs		  int (*arg_get)(void *hook, char *field_name), void *gethook)
84839209Sgibbs{
84939209Sgibbs
85039381Sken	/*
85139381Sken	 * We need something to encode, but we can't get it without the
85239381Sken	 * arg_get function.
85339381Sken	 */
85439381Sken	if (arg_get == NULL)
855315123Sngie		return (-1);
85639209Sgibbs
857315123Sngie	return (do_encode(csio->data_ptr, csio->dxfer_len, NULL, arg_get,
858298753Sngie			 gethook, fmt, NULL));
85939209Sgibbs}
860