summaryrefslogtreecommitdiffstats
path: root/lib/libcam/scsi_cmdparse.c
diff options
context:
space:
mode:
authorgibbs <gibbs@FreeBSD.org>1998-09-15 06:16:46 +0000
committergibbs <gibbs@FreeBSD.org>1998-09-15 06:16:46 +0000
commit5987d73d1d29fb02fde99b7f92cad6f990cb97ce (patch)
tree6cbef733487fe8cc4dda5e08c5d58a0c0abff682 /lib/libcam/scsi_cmdparse.c
parentb12009dccc8fdf4722588d378418b455cc8b3673 (diff)
downloadFreeBSD-src-5987d73d1d29fb02fde99b7f92cad6f990cb97ce.zip
FreeBSD-src-5987d73d1d29fb02fde99b7f92cad6f990cb97ce.tar.gz
CAM userland utility library, a replacement for libscsi.
Submitted by: "Kenneth D. Merry" <ken@FreeBSD.org>
Diffstat (limited to 'lib/libcam/scsi_cmdparse.c')
-rw-r--r--lib/libcam/scsi_cmdparse.c792
1 files changed, 792 insertions, 0 deletions
diff --git a/lib/libcam/scsi_cmdparse.c b/lib/libcam/scsi_cmdparse.c
new file mode 100644
index 0000000..ce30659
--- /dev/null
+++ b/lib/libcam/scsi_cmdparse.c
@@ -0,0 +1,792 @@
+/*
+ * Taken from the original FreeBSD user SCSI library.
+ */
+/* Copyright (c) 1994 HD Associates
+ * (contact: dufault@hda.com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by HD Associates
+ * 4. Neither the name of the HD Associaates nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL HD ASSOCIATES OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * From: scsi.c,v 1.8 1997/02/22 15:07:54 peter Exp $
+ * $Id$
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/errno.h>
+#include <stdarg.h>
+#include <fcntl.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/scsi/scsi_message.h>
+#include "camlib.h"
+
+/*
+ * Decode: Decode the data section of a scsireq. This decodes
+ * trivial grammar:
+ *
+ * fields : field fields
+ * ;
+ *
+ * field : field_specifier
+ * | control
+ * ;
+ *
+ * control : 's' seek_value
+ * | 's' '+' seek_value
+ * ;
+ *
+ * seek_value : DECIMAL_NUMBER
+ * | 'v' // For indirect seek, i.e., value from the arg list
+ * ;
+ *
+ * field_specifier : type_specifier field_width
+ * | '{' NAME '}' type_specifier field_width
+ * ;
+ *
+ * field_width : DECIMAL_NUMBER
+ * ;
+ *
+ * type_specifier : 'i' // Integral types (i1, i2, i3, i4)
+ * | 'b' // Bits
+ * | 't' // Bits
+ * | 'c' // Character arrays
+ * | 'z' // Character arrays with zeroed trailing spaces
+ * ;
+ *
+ * Notes:
+ * 1. Integral types are swapped into host order.
+ * 2. Bit fields are allocated MSB to LSB to match the SCSI spec documentation.
+ * 3. 's' permits "seeking" in the string. "s+DECIMAL" seeks relative to
+ * DECIMAL; "sDECIMAL" seeks absolute to decimal.
+ * 4. 's' permits an indirect reference. "sv" or "s+v" will get the
+ * next integer value from the arg array.
+ * 5. Field names can be anything between the braces
+ *
+ * BUGS:
+ * i and b types are promoted to ints.
+ *
+ */
+
+static int
+do_buff_decode(u_int8_t *databuf, size_t len,
+ void (*arg_put)(void *, int , void *, int, char *),
+ void *puthook, char *fmt, va_list ap)
+{
+ int assigned = 0;
+ int width;
+ int suppress;
+ int plus;
+ int done = 0;
+ static u_char mask[] = {0, 0x01, 0x03, 0x07, 0x0f,
+ 0x1f, 0x3f, 0x7f, 0xff};
+ int value;
+ u_char *base = databuf;
+ char letter;
+ char field_name[80];
+
+# define ARG_PUT(ARG) \
+ do \
+ { \
+ if (!suppress) \
+ { \
+ if (arg_put) \
+ (*arg_put)(puthook, (letter == 't' ? \
+ 'b' : letter), \
+ (void *)((long)(ARG)), 1, field_name); \
+ else \
+ *(va_arg(ap, int *)) = (ARG); \
+ assigned++; \
+ } \
+ field_name[0] = 0; \
+ suppress = 0; \
+ } while (0)
+
+ u_char bits = 0; /* For bit fields */
+ int shift = 0; /* Bits already shifted out */
+ suppress = 0;
+ field_name[0] = 0;
+
+ while (!done) {
+ switch(letter = *fmt) {
+ case ' ': /* White space */
+ case '\t':
+ case '\r':
+ case '\n':
+ case '\f':
+ fmt++;
+ break;
+
+ case '#': /* Comment */
+ while (*fmt && (*fmt != '\n'))
+ fmt++;
+ if (fmt)
+ fmt++; /* Skip '\n' */
+ break;
+
+ case '*': /* Suppress assignment */
+ fmt++;
+ suppress = 1;
+ break;
+
+ case '{': /* Field Name */
+ {
+ int i = 0;
+ fmt++; /* Skip '{' */
+ while (*fmt && (*fmt != '}')) {
+ if (i < sizeof(field_name))
+ field_name[i++] = *fmt;
+
+ fmt++;
+ }
+ if (fmt)
+ fmt++; /* Skip '}' */
+ field_name[i] = 0;
+ break;
+ }
+
+ case 't': /* Bit (field) */
+ case 'b': /* Bits */
+ fmt++;
+ width = strtol(fmt, &fmt, 10);
+ if (width > 8)
+ done = 1;
+ else {
+ if (shift <= 0) {
+ bits = *databuf++;
+ shift = 8;
+ }
+ value = (bits >> (shift - width)) &
+ mask[width];
+
+#if 0
+ printf("shift %2d bits %02x value %02x width %2d mask %02x\n",
+ shift, bits, value, width, mask[width]);
+#endif
+
+ ARG_PUT(value);
+
+ shift -= width;
+ }
+ break;
+
+ case 'i': /* Integral values */
+ shift = 0;
+ fmt++;
+ width = strtol(fmt, &fmt, 10);
+ switch(width) {
+ case 1:
+ ARG_PUT(*databuf);
+ databuf++;
+ break;
+
+ case 2:
+ ARG_PUT((*databuf) << 8 | *(databuf + 1));
+ databuf += 2;
+ break;
+
+ case 3:
+ ARG_PUT((*databuf) << 16 |
+ (*(databuf + 1)) << 8 | *(databuf + 2));
+ databuf += 3;
+ break;
+
+ case 4:
+ ARG_PUT((*databuf) << 24 |
+ (*(databuf + 1)) << 16 |
+ (*(databuf + 2)) << 8 |
+ *(databuf + 3));
+ databuf += 4;
+ break;
+
+ default:
+ done = 1;
+ break;
+ }
+
+ break;
+
+ case 'c': /* Characters (i.e., not swapped) */
+ case 'z': /* Characters with zeroed trailing
+ spaces */
+ shift = 0;
+ fmt++;
+ width = strtol(fmt, &fmt, 10);
+ if (!suppress) {
+ if (arg_put)
+ (*arg_put)(puthook,
+ (letter == 't' ? 'b' : letter),
+ databuf, width, field_name);
+ else {
+ char *dest;
+ dest = va_arg(ap, char *);
+ bcopy(databuf, dest, width);
+ if (letter == 'z') {
+ char *p;
+ for (p = dest + width - 1;
+ (p >= (char *)dest)
+ && (*p == ' '); p--)
+ *p = 0;
+ }
+ }
+ assigned++;
+ }
+ databuf += width;
+ field_name[0] = 0;
+ suppress = 0;
+ break;
+
+ case 's': /* Seek */
+ shift = 0;
+ fmt++;
+ if (*fmt == '+') {
+ plus = 1;
+ fmt++;
+ } else
+ plus = 0;
+
+ if (tolower(*fmt) == 'v') {
+ /*
+ * You can't suppress a seek value. You also
+ * can't have a variable seek when you are using
+ * "arg_put".
+ */
+ width = (arg_put) ? 0 : va_arg(ap, int);
+ fmt++;
+ } else
+ width = strtol(fmt, &fmt, 10);
+
+ if (plus)
+ databuf += width; /* Relative seek */
+ else
+ databuf = base + width; /* Absolute seek */
+
+ break;
+
+ case 0:
+ done = 1;
+ break;
+
+ default:
+ fprintf(stderr, "Unknown letter in format: %c\n",
+ letter);
+ fmt++;
+ break;
+ }
+ }
+
+ return (assigned);
+}
+
+/* next_field: Return the next field in a command specifier. This
+ * builds up a SCSI command using this trivial grammar:
+ *
+ * fields : field fields
+ * ;
+ *
+ * field : value
+ * | value ':' field_width
+ * ;
+ *
+ * field_width : digit
+ * | 'i' digit // i2 = 2 byte integer, i3 = 3 byte integer etc.
+ * ;
+ *
+ * value : HEX_NUMBER
+ * | 'v' // For indirection.
+ * ;
+ *
+ * Notes:
+ * Bit fields are specified MSB first to match the SCSI spec.
+ *
+ * Examples:
+ * TUR: "0 0 0 0 0 0"
+ * WRITE BUFFER: "38 v:3 0:2 0:3 v v:i3 v:i3 0", mode, buffer_id, list_length
+ *
+ * The function returns the value:
+ * 0: For reached end, with error_p set if an error was found
+ * 1: For valid stuff setup
+ * 2: For "v" was entered as the value (implies use varargs)
+ *
+ */
+
+static int
+next_field(char **pp, char *fmt, int *width_p, int *value_p, char *name,
+ int n_name, int *error_p, int *suppress_p)
+{
+ char *p = *pp;
+
+ int something = 0;
+
+ enum {
+ BETWEEN_FIELDS,
+ START_FIELD,
+ GET_FIELD,
+ DONE,
+ } state;
+
+ int value = 0;
+ int field_size; /* Default to byte field type... */
+ int field_width; /* 1 byte wide */
+ int is_error = 0;
+ int suppress = 0;
+
+ field_size = 8; /* Default to byte field type... */
+ *fmt = 'i';
+ field_width = 1; /* 1 byte wide */
+ if (name)
+ *name = 0;
+
+ state = BETWEEN_FIELDS;
+
+ while (state != DONE) {
+ switch(state) {
+ case BETWEEN_FIELDS:
+ if (*p == 0)
+ state = DONE;
+ else if (isspace(*p))
+ p++;
+ else if (*p == '#') {
+ while (*p && *p != '\n')
+ p++;
+ if (p)
+ p++;
+ } else if (*p == '{') {
+ int i = 0;
+
+ p++;
+
+ while (*p && *p != '}') {
+ if(name && i < n_name) {
+ name[i] = *p;
+ i++;
+ }
+ p++;
+ }
+
+ if(name && i < n_name)
+ name[i] = 0;
+
+ if (*p == '}')
+ p++;
+ } else if (*p == '*') {
+ p++;
+ suppress = 1;
+ } else if (isxdigit(*p)) {
+ something = 1;
+ value = strtol(p, &p, 16);
+ state = START_FIELD;
+ } else if (tolower(*p) == 'v') {
+ p++;
+ something = 2;
+ value = *value_p;
+ state = START_FIELD;
+ } else if (tolower(*p) == 'i') {
+ /*
+ * Try to work without the "v".
+ */
+ something = 2;
+ value = *value_p;
+ p++;
+
+ *fmt = 'i';
+ field_size = 8;
+ field_width = strtol(p, &p, 10);
+ state = DONE;
+
+ } else if (tolower(*p) == 't') {
+ /*
+ * XXX: B can't work: Sees the 'b' as a
+ * hex digit in "isxdigit". try "t" for
+ * bit field.
+ */
+ something = 2;
+ value = *value_p;
+ p++;
+
+ *fmt = 'b';
+ field_size = 1;
+ field_width = strtol(p, &p, 10);
+ state = DONE;
+ } else if (tolower(*p) == 's') {
+ /* Seek */
+ *fmt = 's';
+ p++;
+ if (tolower(*p) == 'v') {
+ p++;
+ something = 2;
+ value = *value_p;
+ } else {
+ something = 1;
+ value = strtol(p, &p, 0);
+ }
+ state = DONE;
+ } else {
+ fprintf(stderr, "Invalid starting "
+ "character: %c\n", *p);
+ is_error = 1;
+ state = DONE;
+ }
+ break;
+
+ case START_FIELD:
+ if (*p == ':') {
+ p++;
+ field_size = 1; /* Default to bits
+ when specified */
+ state = GET_FIELD;
+ } else
+ state = DONE;
+ break;
+
+ case GET_FIELD:
+ if (isdigit(*p)) {
+ *fmt = 'b';
+ field_size = 1;
+ field_width = strtol(p, &p, 10);
+ state = DONE;
+ } else if (*p == 'i') {
+
+ /* Integral (bytes) */
+ p++;
+
+ *fmt = 'i';
+ field_size = 8;
+ field_width = strtol(p, &p, 10);
+ state = DONE;
+ } else if (*p == 'b') {
+
+ /* Bits */
+ p++;
+
+ *fmt = 'b';
+ field_size = 1;
+ field_width = strtol(p, &p, 10);
+ state = DONE;
+ } else {
+ fprintf(stderr, "Invalid startfield %c "
+ "(%02x)\n", *p, *p);
+ is_error = 1;
+ state = DONE;
+ }
+ break;
+
+ case DONE:
+ break;
+ }
+ }
+
+ if (is_error) {
+ *error_p = 1;
+ return 0;
+ }
+
+ *error_p = 0;
+ *pp = p;
+ *width_p = field_width * field_size;
+ *value_p = value;
+ *suppress_p = suppress;
+
+ return (something);
+}
+
+static int
+do_encode(u_char *buff, size_t vec_max, size_t *used,
+ int (*arg_get)(void *, char *), void *gethook, char *fmt, va_list ap)
+{
+ int ind;
+ int shift;
+ u_char val;
+ int ret;
+ int width, value, error, suppress;
+ char c;
+ int encoded = 0;
+ char field_name[80];
+
+ ind = 0;
+ shift = 0;
+ val = 0;
+
+ while ((ret = next_field(&fmt, &c, &width, &value, field_name,
+ sizeof(field_name), &error, &suppress))) {
+ encoded++;
+
+ if (ret == 2) {
+ if (suppress)
+ value = 0;
+ else
+ value = arg_get ?
+ (*arg_get)(gethook, field_name) :
+ va_arg(ap, int);
+ }
+
+#if 0
+ printf(
+"do_encode: ret %d fmt %c width %d value %d name \"%s\" error %d suppress %d\n",
+ ret, c, width, value, field_name, error, suppress);
+#endif
+ /* Absolute seek */
+ if (c == 's') {
+ ind = value;
+ continue;
+ }
+
+ /* A width of < 8 is a bit field. */
+ if (width < 8) {
+
+ /* This is a bit field. We start with the high bits
+ * so it reads the same as the SCSI spec.
+ */
+
+ shift += width;
+
+ val |= (value << (8 - shift));
+
+ if (shift == 8) {
+ if (ind < vec_max) {
+ buff[ind++] = val;
+ val = 0;
+ }
+ shift = 0;
+ }
+ } else {
+ if (shift) {
+ if (ind < vec_max) {
+ buff[ind++] = val;
+ val = 0;
+ }
+ shift = 0;
+ }
+ switch(width) {
+ case 8: /* 1 byte integer */
+ if (ind < vec_max)
+ buff[ind++] = value;
+ break;
+
+ case 16: /* 2 byte integer */
+ if (ind < vec_max - 2 + 1) {
+ buff[ind++] = value >> 8;
+ buff[ind++] = value;
+ }
+ break;
+
+ case 24: /* 3 byte integer */
+ if (ind < vec_max - 3 + 1) {
+ buff[ind++] = value >> 16;
+ buff[ind++] = value >> 8;
+ buff[ind++] = value;
+ }
+ break;
+
+ case 32: /* 4 byte integer */
+ if (ind < vec_max - 4 + 1) {
+ buff[ind++] = value >> 24;
+ buff[ind++] = value >> 16;
+ buff[ind++] = value >> 8;
+ buff[ind++] = value;
+ }
+ break;
+
+ default:
+ fprintf(stderr, "do_encode: Illegal width\n");
+ break;
+ }
+ }
+ }
+
+ /* Flush out any remaining bits
+ */
+ if (shift && ind < vec_max) {
+ buff[ind++] = val;
+ val = 0;
+ }
+
+
+ if (used)
+ *used = ind;
+
+ if (error)
+ return -1;
+
+ return encoded;
+}
+
+int
+csio_decode(struct ccb_scsiio *csio, char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ return(do_buff_decode(csio->data_ptr, (size_t)csio->dxfer_len,
+ 0, 0, fmt, ap));
+}
+
+int
+csio_decode_visit(struct ccb_scsiio *csio, char *fmt,
+ void (*arg_put)(void *, int, void *, int, char *),
+ void *puthook)
+{
+ va_list ap;
+
+ ap = (va_list)0;
+
+ return(do_buff_decode(csio->data_ptr, (size_t)csio->dxfer_len,
+ arg_put, puthook, fmt, ap));
+}
+
+int
+buff_decode(u_int8_t *buff, size_t len, char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ return(do_buff_decode(buff, len, 0, 0, fmt, ap));
+}
+
+int
+buff_decode_visit(u_int8_t *buff, size_t len, char *fmt,
+ void (*arg_put)(void *, int, void *, int, char *),
+ void *puthook)
+{
+ va_list ap;
+
+ ap = (va_list)0;
+
+ return(do_buff_decode(buff, len, arg_put, puthook, fmt, ap));
+}
+
+/*
+ * Build a SCSI CCB, given the command and data pointers and a format
+ * string describing the
+ */
+int
+csio_build(struct ccb_scsiio *csio, u_int8_t *data_ptr, u_int32_t dxfer_len,
+ u_int32_t flags, int retry_count, int timeout, char *cmd_spec, ...)
+{
+ int cmdlen;
+ int retval;
+ va_list ap;
+
+ if (csio == NULL)
+ return(0);
+
+ bzero(csio, sizeof(struct ccb_scsiio));
+
+ va_start(ap, cmd_spec);
+
+ if ((retval = do_encode(csio->cdb_io.cdb_bytes, SCSI_MAX_CDBLEN,
+ &cmdlen, NULL, NULL, cmd_spec, ap)) == -1)
+ return(retval);
+
+ cam_fill_csio(csio,
+ /* retries */ retry_count,
+ /* cbfcnp */ NULL,
+ /* flags */ flags,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* data_ptr */ data_ptr,
+ /* dxfer_len */ dxfer_len,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* cdb_len */ cmdlen,
+ /* timeout */ timeout ? timeout : 5000);
+
+ return(retval);
+}
+
+int
+csio_build_visit(struct ccb_scsiio *csio, u_int8_t *data_ptr,
+ u_int32_t dxfer_len, u_int32_t flags, int retry_count,
+ int timeout, char *cmd_spec,
+ int (*arg_get)(void *hook, char *field_name), void *gethook)
+{
+ va_list ap;
+ int cmdlen, retval;
+
+ if (csio == NULL)
+ return(0);
+
+ ap = (va_list)0;
+
+ bzero(csio, sizeof(struct ccb_scsiio));
+
+ if ((retval = do_encode(csio->cdb_io.cdb_bytes, SCSI_MAX_CDBLEN,
+ &cmdlen, arg_get, gethook, cmd_spec, ap)) == -1)
+ return(retval);
+
+ cam_fill_csio(csio,
+ /* retries */ retry_count,
+ /* cbfcnp */ NULL,
+ /* flags */ flags,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* data_ptr */ data_ptr,
+ /* dxfer_len */ dxfer_len,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* cdb_len */ cmdlen,
+ /* timeout */ timeout ? timeout : 5000);
+
+ return(retval);
+}
+
+int
+csio_encode(struct ccb_scsiio *csio, char *fmt, ...)
+{
+ va_list ap;
+
+ if (csio == NULL)
+ return(0);
+
+ va_start(ap, fmt);
+
+ return(do_encode(csio->data_ptr, csio->dxfer_len, 0, 0, 0, fmt, ap));
+}
+
+int
+buff_encode_visit(u_int8_t *buff, size_t len, char *fmt,
+ int (*arg_get)(void *hook, char *field_name), void *gethook)
+{
+ va_list ap;
+
+ ap = (va_list)0;
+
+ return(do_encode(buff, len, 0, arg_get, gethook, fmt, ap));
+}
+
+int
+csio_encode_visit(struct ccb_scsiio *csio, char *fmt,
+ int (*arg_get)(void *hook, char *field_name), void *gethook)
+{
+ va_list ap;
+
+ ap = (va_list) 0;
+
+ return(do_encode(csio->data_ptr, csio->dxfer_len, 0, arg_get,
+ gethook, fmt, ap));
+}
+
OpenPOWER on IntegriCloud