Remove NO_WERROR.
[dragonfly/port-amd64.git] / lib / libcam / scsi_cmdparse.c
blob91d53a2a2478e38965a64f65bc939473f6f6860b
1 /*
2 * Taken from the original FreeBSD user SCSI library.
3 */
4 /* Copyright (c) 1994 HD Associates
5 * (contact: dufault@hda.com)
6 * All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by HD Associates
19 * 4. Neither the name of the HD Associaates nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL HD ASSOCIATES OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 * From: scsi.c,v 1.8 1997/02/22 15:07:54 peter Exp $
35 * $FreeBSD: src/lib/libcam/scsi_cmdparse.c,v 1.3.2.1 2000/08/14 05:42:30 kbyanc Exp $
36 * $DragonFly: src/lib/libcam/scsi_cmdparse.c,v 1.2 2003/06/17 04:26:48 dillon Exp $
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <ctype.h>
41 #include <string.h>
42 #include <sys/errno.h>
43 #include <stdarg.h>
44 #include <fcntl.h>
46 #include <cam/cam.h>
47 #include <cam/cam_ccb.h>
48 #include <cam/scsi/scsi_message.h>
49 #include "camlib.h"
52 * Decode: Decode the data section of a scsireq. This decodes
53 * trivial grammar:
55 * fields : field fields
56 * ;
58 * field : field_specifier
59 * | control
60 * ;
62 * control : 's' seek_value
63 * | 's' '+' seek_value
64 * ;
66 * seek_value : DECIMAL_NUMBER
67 * | 'v' // For indirect seek, i.e., value from the arg list
68 * ;
70 * field_specifier : type_specifier field_width
71 * | '{' NAME '}' type_specifier field_width
72 * ;
74 * field_width : DECIMAL_NUMBER
75 * ;
77 * type_specifier : 'i' // Integral types (i1, i2, i3, i4)
78 * | 'b' // Bits
79 * | 't' // Bits
80 * | 'c' // Character arrays
81 * | 'z' // Character arrays with zeroed trailing spaces
82 * ;
84 * Notes:
85 * 1. Integral types are swapped into host order.
86 * 2. Bit fields are allocated MSB to LSB to match the SCSI spec documentation.
87 * 3. 's' permits "seeking" in the string. "s+DECIMAL" seeks relative to
88 * DECIMAL; "sDECIMAL" seeks absolute to decimal.
89 * 4. 's' permits an indirect reference. "sv" or "s+v" will get the
90 * next integer value from the arg array.
91 * 5. Field names can be anything between the braces
93 * BUGS:
94 * i and b types are promoted to ints.
98 static int
99 do_buff_decode(u_int8_t *databuf, size_t len,
100 void (*arg_put)(void *, int , void *, int, char *),
101 void *puthook, char *fmt, va_list ap)
103 int assigned = 0;
104 int width;
105 int suppress;
106 int plus;
107 int done = 0;
108 static u_char mask[] = {0, 0x01, 0x03, 0x07, 0x0f,
109 0x1f, 0x3f, 0x7f, 0xff};
110 int value;
111 u_char *base = databuf;
112 char letter;
113 char field_name[80];
115 # define ARG_PUT(ARG) \
116 do \
118 if (!suppress) \
120 if (arg_put) \
121 (*arg_put)(puthook, (letter == 't' ? \
122 'b' : letter), \
123 (void *)((long)(ARG)), width, \
124 field_name); \
125 else \
126 *(va_arg(ap, int *)) = (ARG); \
127 assigned++; \
129 field_name[0] = 0; \
130 suppress = 0; \
131 } while (0)
133 u_char bits = 0; /* For bit fields */
134 int shift = 0; /* Bits already shifted out */
135 suppress = 0;
136 field_name[0] = 0;
138 while (!done) {
139 switch(letter = *fmt) {
140 case ' ': /* White space */
141 case '\t':
142 case '\r':
143 case '\n':
144 case '\f':
145 fmt++;
146 break;
148 case '#': /* Comment */
149 while (*fmt && (*fmt != '\n'))
150 fmt++;
151 if (fmt)
152 fmt++; /* Skip '\n' */
153 break;
155 case '*': /* Suppress assignment */
156 fmt++;
157 suppress = 1;
158 break;
160 case '{': /* Field Name */
162 int i = 0;
163 fmt++; /* Skip '{' */
164 while (*fmt && (*fmt != '}')) {
165 if (i < sizeof(field_name))
166 field_name[i++] = *fmt;
168 fmt++;
170 if (fmt)
171 fmt++; /* Skip '}' */
172 field_name[i] = 0;
173 break;
176 case 't': /* Bit (field) */
177 case 'b': /* Bits */
178 fmt++;
179 width = strtol(fmt, &fmt, 10);
180 if (width > 8)
181 done = 1;
182 else {
183 if (shift <= 0) {
184 bits = *databuf++;
185 shift = 8;
187 value = (bits >> (shift - width)) &
188 mask[width];
190 #if 0
191 printf("shift %2d bits %02x value %02x width %2d mask %02x\n",
192 shift, bits, value, width, mask[width]);
193 #endif
195 ARG_PUT(value);
197 shift -= width;
199 break;
201 case 'i': /* Integral values */
202 shift = 0;
203 fmt++;
204 width = strtol(fmt, &fmt, 10);
205 switch(width) {
206 case 1:
207 ARG_PUT(*databuf);
208 databuf++;
209 break;
211 case 2:
212 ARG_PUT((*databuf) << 8 | *(databuf + 1));
213 databuf += 2;
214 break;
216 case 3:
217 ARG_PUT((*databuf) << 16 |
218 (*(databuf + 1)) << 8 | *(databuf + 2));
219 databuf += 3;
220 break;
222 case 4:
223 ARG_PUT((*databuf) << 24 |
224 (*(databuf + 1)) << 16 |
225 (*(databuf + 2)) << 8 |
226 *(databuf + 3));
227 databuf += 4;
228 break;
230 default:
231 done = 1;
232 break;
235 break;
237 case 'c': /* Characters (i.e., not swapped) */
238 case 'z': /* Characters with zeroed trailing
239 spaces */
240 shift = 0;
241 fmt++;
242 width = strtol(fmt, &fmt, 10);
243 if (!suppress) {
244 if (arg_put)
245 (*arg_put)(puthook,
246 (letter == 't' ? 'b' : letter),
247 databuf, width, field_name);
248 else {
249 char *dest;
250 dest = va_arg(ap, char *);
251 bcopy(databuf, dest, width);
252 if (letter == 'z') {
253 char *p;
254 for (p = dest + width - 1;
255 (p >= (char *)dest)
256 && (*p == ' '); p--)
257 *p = 0;
260 assigned++;
262 databuf += width;
263 field_name[0] = 0;
264 suppress = 0;
265 break;
267 case 's': /* Seek */
268 shift = 0;
269 fmt++;
270 if (*fmt == '+') {
271 plus = 1;
272 fmt++;
273 } else
274 plus = 0;
276 if (tolower(*fmt) == 'v') {
278 * You can't suppress a seek value. You also
279 * can't have a variable seek when you are using
280 * "arg_put".
282 width = (arg_put) ? 0 : va_arg(ap, int);
283 fmt++;
284 } else
285 width = strtol(fmt, &fmt, 10);
287 if (plus)
288 databuf += width; /* Relative seek */
289 else
290 databuf = base + width; /* Absolute seek */
292 break;
294 case 0:
295 done = 1;
296 break;
298 default:
299 fprintf(stderr, "Unknown letter in format: %c\n",
300 letter);
301 fmt++;
302 break;
306 return (assigned);
309 /* next_field: Return the next field in a command specifier. This
310 * builds up a SCSI command using this trivial grammar:
312 * fields : field fields
315 * field : value
316 * | value ':' field_width
319 * field_width : digit
320 * | 'i' digit // i2 = 2 byte integer, i3 = 3 byte integer etc.
323 * value : HEX_NUMBER
324 * | 'v' // For indirection.
327 * Notes:
328 * Bit fields are specified MSB first to match the SCSI spec.
330 * Examples:
331 * TUR: "0 0 0 0 0 0"
332 * WRITE BUFFER: "38 v:3 0:2 0:3 v v:i3 v:i3 0", mode, buffer_id, list_length
334 * The function returns the value:
335 * 0: For reached end, with error_p set if an error was found
336 * 1: For valid stuff setup
337 * 2: For "v" was entered as the value (implies use varargs)
341 static int
342 next_field(char **pp, char *fmt, int *width_p, int *value_p, char *name,
343 int n_name, int *error_p, int *suppress_p)
345 char *p = *pp;
347 int something = 0;
349 enum {
350 BETWEEN_FIELDS,
351 START_FIELD,
352 GET_FIELD,
353 DONE,
354 } state;
356 int value = 0;
357 int field_size; /* Default to byte field type... */
358 int field_width; /* 1 byte wide */
359 int is_error = 0;
360 int suppress = 0;
362 field_size = 8; /* Default to byte field type... */
363 *fmt = 'i';
364 field_width = 1; /* 1 byte wide */
365 if (name)
366 *name = 0;
368 state = BETWEEN_FIELDS;
370 while (state != DONE) {
371 switch(state) {
372 case BETWEEN_FIELDS:
373 if (*p == 0)
374 state = DONE;
375 else if (isspace(*p))
376 p++;
377 else if (*p == '#') {
378 while (*p && *p != '\n')
379 p++;
380 if (p)
381 p++;
382 } else if (*p == '{') {
383 int i = 0;
385 p++;
387 while (*p && *p != '}') {
388 if(name && i < n_name) {
389 name[i] = *p;
390 i++;
392 p++;
395 if(name && i < n_name)
396 name[i] = 0;
398 if (*p == '}')
399 p++;
400 } else if (*p == '*') {
401 p++;
402 suppress = 1;
403 } else if (isxdigit(*p)) {
404 something = 1;
405 value = strtol(p, &p, 16);
406 state = START_FIELD;
407 } else if (tolower(*p) == 'v') {
408 p++;
409 something = 2;
410 value = *value_p;
411 state = START_FIELD;
412 } else if (tolower(*p) == 'i') {
414 * Try to work without the "v".
416 something = 2;
417 value = *value_p;
418 p++;
420 *fmt = 'i';
421 field_size = 8;
422 field_width = strtol(p, &p, 10);
423 state = DONE;
425 } else if (tolower(*p) == 't') {
427 * XXX: B can't work: Sees the 'b' as a
428 * hex digit in "isxdigit". try "t" for
429 * bit field.
431 something = 2;
432 value = *value_p;
433 p++;
435 *fmt = 'b';
436 field_size = 1;
437 field_width = strtol(p, &p, 10);
438 state = DONE;
439 } else if (tolower(*p) == 's') {
440 /* Seek */
441 *fmt = 's';
442 p++;
443 if (tolower(*p) == 'v') {
444 p++;
445 something = 2;
446 value = *value_p;
447 } else {
448 something = 1;
449 value = strtol(p, &p, 0);
451 state = DONE;
452 } else {
453 fprintf(stderr, "Invalid starting "
454 "character: %c\n", *p);
455 is_error = 1;
456 state = DONE;
458 break;
460 case START_FIELD:
461 if (*p == ':') {
462 p++;
463 field_size = 1; /* Default to bits
464 when specified */
465 state = GET_FIELD;
466 } else
467 state = DONE;
468 break;
470 case GET_FIELD:
471 if (isdigit(*p)) {
472 *fmt = 'b';
473 field_size = 1;
474 field_width = strtol(p, &p, 10);
475 state = DONE;
476 } else if (*p == 'i') {
478 /* Integral (bytes) */
479 p++;
481 *fmt = 'i';
482 field_size = 8;
483 field_width = strtol(p, &p, 10);
484 state = DONE;
485 } else if (*p == 'b') {
487 /* Bits */
488 p++;
490 *fmt = 'b';
491 field_size = 1;
492 field_width = strtol(p, &p, 10);
493 state = DONE;
494 } else {
495 fprintf(stderr, "Invalid startfield %c "
496 "(%02x)\n", *p, *p);
497 is_error = 1;
498 state = DONE;
500 break;
502 case DONE:
503 break;
507 if (is_error) {
508 *error_p = 1;
509 return 0;
512 *error_p = 0;
513 *pp = p;
514 *width_p = field_width * field_size;
515 *value_p = value;
516 *suppress_p = suppress;
518 return (something);
521 static int
522 do_encode(u_char *buff, size_t vec_max, size_t *used,
523 int (*arg_get)(void *, char *), void *gethook, char *fmt, va_list ap)
525 int ind;
526 int shift;
527 u_char val;
528 int ret;
529 int width, value, error, suppress;
530 char c;
531 int encoded = 0;
532 char field_name[80];
534 ind = 0;
535 shift = 0;
536 val = 0;
538 while ((ret = next_field(&fmt, &c, &width, &value, field_name,
539 sizeof(field_name), &error, &suppress))) {
540 encoded++;
542 if (ret == 2) {
543 if (suppress)
544 value = 0;
545 else
546 value = arg_get ?
547 (*arg_get)(gethook, field_name) :
548 va_arg(ap, int);
551 #if 0
552 printf(
553 "do_encode: ret %d fmt %c width %d value %d name \"%s\" error %d suppress %d\n",
554 ret, c, width, value, field_name, error, suppress);
555 #endif
556 /* Absolute seek */
557 if (c == 's') {
558 ind = value;
559 continue;
562 /* A width of < 8 is a bit field. */
563 if (width < 8) {
565 /* This is a bit field. We start with the high bits
566 * so it reads the same as the SCSI spec.
569 shift += width;
571 val |= (value << (8 - shift));
573 if (shift == 8) {
574 if (ind < vec_max) {
575 buff[ind++] = val;
576 val = 0;
578 shift = 0;
580 } else {
581 if (shift) {
582 if (ind < vec_max) {
583 buff[ind++] = val;
584 val = 0;
586 shift = 0;
588 switch(width) {
589 case 8: /* 1 byte integer */
590 if (ind < vec_max)
591 buff[ind++] = value;
592 break;
594 case 16: /* 2 byte integer */
595 if (ind < vec_max - 2 + 1) {
596 buff[ind++] = value >> 8;
597 buff[ind++] = value;
599 break;
601 case 24: /* 3 byte integer */
602 if (ind < vec_max - 3 + 1) {
603 buff[ind++] = value >> 16;
604 buff[ind++] = value >> 8;
605 buff[ind++] = value;
607 break;
609 case 32: /* 4 byte integer */
610 if (ind < vec_max - 4 + 1) {
611 buff[ind++] = value >> 24;
612 buff[ind++] = value >> 16;
613 buff[ind++] = value >> 8;
614 buff[ind++] = value;
616 break;
618 default:
619 fprintf(stderr, "do_encode: Illegal width\n");
620 break;
625 /* Flush out any remaining bits
627 if (shift && ind < vec_max) {
628 buff[ind++] = val;
629 val = 0;
633 if (used)
634 *used = ind;
636 if (error)
637 return -1;
639 return encoded;
643 csio_decode(struct ccb_scsiio *csio, char *fmt, ...)
645 va_list ap;
647 va_start(ap, fmt);
649 return(do_buff_decode(csio->data_ptr, (size_t)csio->dxfer_len,
650 0, 0, fmt, ap));
654 csio_decode_visit(struct ccb_scsiio *csio, char *fmt,
655 void (*arg_put)(void *, int, void *, int, char *),
656 void *puthook)
658 va_list ap;
661 * We need some way to output things; we can't do it without
662 * the arg_put function.
664 if (arg_put == NULL)
665 return(-1);
667 bzero(&ap, sizeof(ap));
669 return(do_buff_decode(csio->data_ptr, (size_t)csio->dxfer_len,
670 arg_put, puthook, fmt, ap));
674 buff_decode(u_int8_t *buff, size_t len, char *fmt, ...)
676 va_list ap;
678 va_start(ap, fmt);
680 return(do_buff_decode(buff, len, 0, 0, fmt, ap));
684 buff_decode_visit(u_int8_t *buff, size_t len, char *fmt,
685 void (*arg_put)(void *, int, void *, int, char *),
686 void *puthook)
688 va_list ap;
691 * We need some way to output things; we can't do it without
692 * the arg_put function.
694 if (arg_put == NULL)
695 return(-1);
697 bzero(&ap, sizeof(ap));
699 return(do_buff_decode(buff, len, arg_put, puthook, fmt, ap));
703 * Build a SCSI CCB, given the command and data pointers and a format
704 * string describing the
707 csio_build(struct ccb_scsiio *csio, u_int8_t *data_ptr, u_int32_t dxfer_len,
708 u_int32_t flags, int retry_count, int timeout, char *cmd_spec, ...)
710 size_t cmdlen;
711 int retval;
712 va_list ap;
714 if (csio == NULL)
715 return(0);
717 bzero(csio, sizeof(struct ccb_scsiio));
719 va_start(ap, cmd_spec);
721 if ((retval = do_encode(csio->cdb_io.cdb_bytes, SCSI_MAX_CDBLEN,
722 &cmdlen, NULL, NULL, cmd_spec, ap)) == -1)
723 return(retval);
725 cam_fill_csio(csio,
726 /* retries */ retry_count,
727 /* cbfcnp */ NULL,
728 /* flags */ flags,
729 /* tag_action */ MSG_SIMPLE_Q_TAG,
730 /* data_ptr */ data_ptr,
731 /* dxfer_len */ dxfer_len,
732 /* sense_len */ SSD_FULL_SIZE,
733 /* cdb_len */ cmdlen,
734 /* timeout */ timeout ? timeout : 5000);
736 return(retval);
740 csio_build_visit(struct ccb_scsiio *csio, u_int8_t *data_ptr,
741 u_int32_t dxfer_len, u_int32_t flags, int retry_count,
742 int timeout, char *cmd_spec,
743 int (*arg_get)(void *hook, char *field_name), void *gethook)
745 va_list ap;
746 size_t cmdlen;
747 int retval;
749 if (csio == NULL)
750 return(0);
753 * We need something to encode, but we can't get it without the
754 * arg_get function.
756 if (arg_get == NULL)
757 return(-1);
759 bzero(&ap, sizeof(ap));
761 bzero(csio, sizeof(struct ccb_scsiio));
763 if ((retval = do_encode(csio->cdb_io.cdb_bytes, SCSI_MAX_CDBLEN,
764 &cmdlen, arg_get, gethook, cmd_spec, ap)) == -1)
765 return(retval);
767 cam_fill_csio(csio,
768 /* retries */ retry_count,
769 /* cbfcnp */ NULL,
770 /* flags */ flags,
771 /* tag_action */ MSG_SIMPLE_Q_TAG,
772 /* data_ptr */ data_ptr,
773 /* dxfer_len */ dxfer_len,
774 /* sense_len */ SSD_FULL_SIZE,
775 /* cdb_len */ cmdlen,
776 /* timeout */ timeout ? timeout : 5000);
778 return(retval);
782 csio_encode(struct ccb_scsiio *csio, char *fmt, ...)
784 va_list ap;
786 if (csio == NULL)
787 return(0);
789 va_start(ap, fmt);
791 return(do_encode(csio->data_ptr, csio->dxfer_len, 0, 0, 0, fmt, ap));
795 buff_encode_visit(u_int8_t *buff, size_t len, char *fmt,
796 int (*arg_get)(void *hook, char *field_name), void *gethook)
798 va_list ap;
801 * We need something to encode, but we can't get it without the
802 * arg_get function.
804 if (arg_get == NULL)
805 return(-1);
807 bzero(&ap, sizeof(ap));
809 return(do_encode(buff, len, 0, arg_get, gethook, fmt, ap));
813 csio_encode_visit(struct ccb_scsiio *csio, char *fmt,
814 int (*arg_get)(void *hook, char *field_name), void *gethook)
816 va_list ap;
819 * We need something to encode, but we can't get it without the
820 * arg_get function.
822 if (arg_get == NULL)
823 return(-1);
825 bzero(&ap, sizeof(ap));
827 return(do_encode(csio->data_ptr, csio->dxfer_len, 0, arg_get,
828 gethook, fmt, ap));