nrelease - fix/improve livecd
[dragonfly.git] / lib / libcam / scsi_cmdparse.c
CommitLineData
984263bc
MD
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.
7 *
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.
22 *
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 */
37#include <stdlib.h>
38#include <stdio.h>
39#include <ctype.h>
40#include <string.h>
41#include <sys/errno.h>
42#include <stdarg.h>
43#include <fcntl.h>
44
4842621b 45#include <bus/cam/cam.h>
46#include <bus/cam/cam_ccb.h>
47#include <bus/cam/scsi/scsi_message.h>
984263bc
MD
48#include "camlib.h"
49
50/*
51 * Decode: Decode the data section of a scsireq. This decodes
52 * trivial grammar:
53 *
54 * fields : field fields
55 * ;
56 *
57 * field : field_specifier
58 * | control
59 * ;
60 *
61 * control : 's' seek_value
62 * | 's' '+' seek_value
63 * ;
64 *
65 * seek_value : DECIMAL_NUMBER
66 * | 'v' // For indirect seek, i.e., value from the arg list
67 * ;
68 *
69 * field_specifier : type_specifier field_width
70 * | '{' NAME '}' type_specifier field_width
71 * ;
72 *
73 * field_width : DECIMAL_NUMBER
74 * ;
75 *
76 * type_specifier : 'i' // Integral types (i1, i2, i3, i4)
77 * | 'b' // Bits
78 * | 't' // Bits
79 * | 'c' // Character arrays
80 * | 'z' // Character arrays with zeroed trailing spaces
81 * ;
82 *
83 * Notes:
84 * 1. Integral types are swapped into host order.
85 * 2. Bit fields are allocated MSB to LSB to match the SCSI spec documentation.
86 * 3. 's' permits "seeking" in the string. "s+DECIMAL" seeks relative to
87 * DECIMAL; "sDECIMAL" seeks absolute to decimal.
88 * 4. 's' permits an indirect reference. "sv" or "s+v" will get the
89 * next integer value from the arg array.
90 * 5. Field names can be anything between the braces
91 *
92 * BUGS:
93 * i and b types are promoted to ints.
94 *
95 */
96
97static int
98do_buff_decode(u_int8_t *databuf, size_t len,
99 void (*arg_put)(void *, int , void *, int, char *),
5adc1304 100 void *puthook, const char *fmt, va_list ap)
984263bc
MD
101{
102 int assigned = 0;
103 int width;
104 int suppress;
105 int plus;
106 int done = 0;
107 static u_char mask[] = {0, 0x01, 0x03, 0x07, 0x0f,
108 0x1f, 0x3f, 0x7f, 0xff};
109 int value;
110 u_char *base = databuf;
5adc1304 111 char *intendp;
984263bc
MD
112 char letter;
113 char field_name[80];
114
115# define ARG_PUT(ARG) \
116 do \
117 { \
118 if (!suppress) \
119 { \
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++; \
128 } \
129 field_name[0] = 0; \
130 suppress = 0; \
131 } while (0)
132
133 u_char bits = 0; /* For bit fields */
134 int shift = 0; /* Bits already shifted out */
135 suppress = 0;
136 field_name[0] = 0;
137
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;
147
148 case '#': /* Comment */
149 while (*fmt && (*fmt != '\n'))
150 fmt++;
151 if (fmt)
152 fmt++; /* Skip '\n' */
153 break;
154
155 case '*': /* Suppress assignment */
156 fmt++;
157 suppress = 1;
158 break;
159
160 case '{': /* Field Name */
161 {
162 int i = 0;
163 fmt++; /* Skip '{' */
164 while (*fmt && (*fmt != '}')) {
165 if (i < sizeof(field_name))
166 field_name[i++] = *fmt;
167
168 fmt++;
169 }
170 if (fmt)
171 fmt++; /* Skip '}' */
172 field_name[i] = 0;
173 break;
174 }
175
176 case 't': /* Bit (field) */
177 case 'b': /* Bits */
178 fmt++;
5adc1304
PA
179 width = strtol(fmt, &intendp, 10);
180 fmt = intendp;
984263bc
MD
181 if (width > 8)
182 done = 1;
183 else {
184 if (shift <= 0) {
185 bits = *databuf++;
186 shift = 8;
187 }
188 value = (bits >> (shift - width)) &
189 mask[width];
190
191#if 0
192 printf("shift %2d bits %02x value %02x width %2d mask %02x\n",
193 shift, bits, value, width, mask[width]);
194#endif
195
196 ARG_PUT(value);
197
198 shift -= width;
199 }
200 break;
201
202 case 'i': /* Integral values */
203 shift = 0;
204 fmt++;
5adc1304
PA
205 width = strtol(fmt, &intendp, 10);
206 fmt = intendp;
984263bc
MD
207 switch(width) {
208 case 1:
209 ARG_PUT(*databuf);
210 databuf++;
211 break;
212
213 case 2:
214 ARG_PUT((*databuf) << 8 | *(databuf + 1));
215 databuf += 2;
216 break;
217
218 case 3:
219 ARG_PUT((*databuf) << 16 |
220 (*(databuf + 1)) << 8 | *(databuf + 2));
221 databuf += 3;
222 break;
223
224 case 4:
225 ARG_PUT((*databuf) << 24 |
226 (*(databuf + 1)) << 16 |
227 (*(databuf + 2)) << 8 |
228 *(databuf + 3));
229 databuf += 4;
230 break;
231
232 default:
233 done = 1;
234 break;
235 }
236
237 break;
238
239 case 'c': /* Characters (i.e., not swapped) */
240 case 'z': /* Characters with zeroed trailing
241 spaces */
242 shift = 0;
243 fmt++;
5adc1304
PA
244 width = strtol(fmt, &intendp, 10);
245 fmt = intendp;
984263bc
MD
246 if (!suppress) {
247 if (arg_put)
248 (*arg_put)(puthook,
249 (letter == 't' ? 'b' : letter),
250 databuf, width, field_name);
251 else {
252 char *dest;
253 dest = va_arg(ap, char *);
254 bcopy(databuf, dest, width);
255 if (letter == 'z') {
256 char *p;
257 for (p = dest + width - 1;
258 (p >= (char *)dest)
259 && (*p == ' '); p--)
260 *p = 0;
261 }
262 }
263 assigned++;
264 }
265 databuf += width;
266 field_name[0] = 0;
267 suppress = 0;
268 break;
269
270 case 's': /* Seek */
271 shift = 0;
272 fmt++;
273 if (*fmt == '+') {
274 plus = 1;
275 fmt++;
276 } else
277 plus = 0;
278
279 if (tolower(*fmt) == 'v') {
280 /*
281 * You can't suppress a seek value. You also
282 * can't have a variable seek when you are using
283 * "arg_put".
284 */
285 width = (arg_put) ? 0 : va_arg(ap, int);
286 fmt++;
5adc1304
PA
287 } else {
288 width = strtol(fmt, &intendp, 10);
289 fmt = intendp;
290 }
984263bc
MD
291
292 if (plus)
293 databuf += width; /* Relative seek */
294 else
295 databuf = base + width; /* Absolute seek */
296
297 break;
298
299 case 0:
300 done = 1;
301 break;
302
303 default:
304 fprintf(stderr, "Unknown letter in format: %c\n",
305 letter);
306 fmt++;
307 break;
308 }
309 }
310
311 return (assigned);
312}
313
314/* next_field: Return the next field in a command specifier. This
315 * builds up a SCSI command using this trivial grammar:
316 *
317 * fields : field fields
318 * ;
319 *
320 * field : value
321 * | value ':' field_width
322 * ;
323 *
324 * field_width : digit
325 * | 'i' digit // i2 = 2 byte integer, i3 = 3 byte integer etc.
326 * ;
327 *
328 * value : HEX_NUMBER
329 * | 'v' // For indirection.
330 * ;
331 *
332 * Notes:
333 * Bit fields are specified MSB first to match the SCSI spec.
334 *
335 * Examples:
336 * TUR: "0 0 0 0 0 0"
337 * WRITE BUFFER: "38 v:3 0:2 0:3 v v:i3 v:i3 0", mode, buffer_id, list_length
338 *
339 * The function returns the value:
340 * 0: For reached end, with error_p set if an error was found
341 * 1: For valid stuff setup
342 * 2: For "v" was entered as the value (implies use varargs)
343 *
344 */
345
346static int
5adc1304 347next_field(const char **pp, char *fmt, int *width_p, int *value_p, char *name,
984263bc
MD
348 int n_name, int *error_p, int *suppress_p)
349{
5adc1304
PA
350 const char *p = *pp;
351 char *intendp;
984263bc
MD
352
353 int something = 0;
354
355 enum {
356 BETWEEN_FIELDS,
357 START_FIELD,
358 GET_FIELD,
359 DONE,
360 } state;
361
362 int value = 0;
363 int field_size; /* Default to byte field type... */
364 int field_width; /* 1 byte wide */
365 int is_error = 0;
366 int suppress = 0;
367
368 field_size = 8; /* Default to byte field type... */
369 *fmt = 'i';
370 field_width = 1; /* 1 byte wide */
371 if (name)
372 *name = 0;
373
374 state = BETWEEN_FIELDS;
375
376 while (state != DONE) {
377 switch(state) {
378 case BETWEEN_FIELDS:
379 if (*p == 0)
380 state = DONE;
381 else if (isspace(*p))
382 p++;
383 else if (*p == '#') {
384 while (*p && *p != '\n')
385 p++;
386 if (p)
387 p++;
388 } else if (*p == '{') {
389 int i = 0;
390
391 p++;
392
393 while (*p && *p != '}') {
394 if(name && i < n_name) {
395 name[i] = *p;
396 i++;
397 }
398 p++;
399 }
400
401 if(name && i < n_name)
402 name[i] = 0;
403
404 if (*p == '}')
405 p++;
406 } else if (*p == '*') {
407 p++;
408 suppress = 1;
409 } else if (isxdigit(*p)) {
410 something = 1;
5adc1304
PA
411 value = strtol(p, &intendp, 16);
412 p = intendp;
984263bc
MD
413 state = START_FIELD;
414 } else if (tolower(*p) == 'v') {
415 p++;
416 something = 2;
417 value = *value_p;
418 state = START_FIELD;
419 } else if (tolower(*p) == 'i') {
420 /*
421 * Try to work without the "v".
422 */
423 something = 2;
424 value = *value_p;
425 p++;
426
427 *fmt = 'i';
428 field_size = 8;
5adc1304
PA
429 field_width = strtol(p, &intendp, 10);
430 p = intendp;
984263bc
MD
431 state = DONE;
432
433 } else if (tolower(*p) == 't') {
434 /*
435 * XXX: B can't work: Sees the 'b' as a
436 * hex digit in "isxdigit". try "t" for
437 * bit field.
438 */
439 something = 2;
440 value = *value_p;
441 p++;
442
443 *fmt = 'b';
444 field_size = 1;
5adc1304
PA
445 field_width = strtol(p, &intendp, 10);
446 p = intendp;
984263bc
MD
447 state = DONE;
448 } else if (tolower(*p) == 's') {
449 /* Seek */
450 *fmt = 's';
451 p++;
452 if (tolower(*p) == 'v') {
453 p++;
454 something = 2;
455 value = *value_p;
456 } else {
457 something = 1;
5adc1304
PA
458 value = strtol(p, &intendp, 0);
459 p = intendp;
984263bc
MD
460 }
461 state = DONE;
462 } else {
463 fprintf(stderr, "Invalid starting "
464 "character: %c\n", *p);
465 is_error = 1;
466 state = DONE;
467 }
468 break;
469
470 case START_FIELD:
471 if (*p == ':') {
472 p++;
473 field_size = 1; /* Default to bits
474 when specified */
475 state = GET_FIELD;
476 } else
477 state = DONE;
478 break;
479
480 case GET_FIELD:
481 if (isdigit(*p)) {
482 *fmt = 'b';
483 field_size = 1;
5adc1304
PA
484 field_width = strtol(p, &intendp, 10);
485 p = intendp;
984263bc
MD
486 state = DONE;
487 } else if (*p == 'i') {
488
489 /* Integral (bytes) */
490 p++;
491
492 *fmt = 'i';
493 field_size = 8;
5adc1304
PA
494 field_width = strtol(p, &intendp, 10);
495 p = intendp;
984263bc
MD
496 state = DONE;
497 } else if (*p == 'b') {
498
499 /* Bits */
500 p++;
501
502 *fmt = 'b';
503 field_size = 1;
5adc1304
PA
504 field_width = strtol(p, &intendp, 10);
505 p = intendp;
984263bc
MD
506 state = DONE;
507 } else {
508 fprintf(stderr, "Invalid startfield %c "
509 "(%02x)\n", *p, *p);
510 is_error = 1;
511 state = DONE;
512 }
513 break;
514
515 case DONE:
516 break;
517 }
518 }
519
520 if (is_error) {
521 *error_p = 1;
522 return 0;
523 }
524
525 *error_p = 0;
526 *pp = p;
527 *width_p = field_width * field_size;
528 *value_p = value;
529 *suppress_p = suppress;
530
531 return (something);
532}
533
534static int
535do_encode(u_char *buff, size_t vec_max, size_t *used,
5adc1304
PA
536 int (*arg_get)(void *, char *), void *gethook, const char *fmt,
537 va_list ap)
984263bc
MD
538{
539 int ind;
540 int shift;
541 u_char val;
542 int ret;
543 int width, value, error, suppress;
544 char c;
545 int encoded = 0;
546 char field_name[80];
547
548 ind = 0;
549 shift = 0;
550 val = 0;
551
552 while ((ret = next_field(&fmt, &c, &width, &value, field_name,
553 sizeof(field_name), &error, &suppress))) {
554 encoded++;
555
556 if (ret == 2) {
557 if (suppress)
558 value = 0;
559 else
560 value = arg_get ?
561 (*arg_get)(gethook, field_name) :
562 va_arg(ap, int);
563 }
564
565#if 0
566 printf(
567"do_encode: ret %d fmt %c width %d value %d name \"%s\" error %d suppress %d\n",
568 ret, c, width, value, field_name, error, suppress);
569#endif
570 /* Absolute seek */
571 if (c == 's') {
572 ind = value;
573 continue;
574 }
575
576 /* A width of < 8 is a bit field. */
577 if (width < 8) {
578
579 /* This is a bit field. We start with the high bits
580 * so it reads the same as the SCSI spec.
581 */
582
583 shift += width;
584
585 val |= (value << (8 - shift));
586
587 if (shift == 8) {
588 if (ind < vec_max) {
589 buff[ind++] = val;
590 val = 0;
591 }
592 shift = 0;
593 }
594 } else {
595 if (shift) {
596 if (ind < vec_max) {
597 buff[ind++] = val;
598 val = 0;
599 }
600 shift = 0;
601 }
602 switch(width) {
603 case 8: /* 1 byte integer */
604 if (ind < vec_max)
605 buff[ind++] = value;
606 break;
607
608 case 16: /* 2 byte integer */
609 if (ind < vec_max - 2 + 1) {
610 buff[ind++] = value >> 8;
611 buff[ind++] = value;
612 }
613 break;
614
615 case 24: /* 3 byte integer */
616 if (ind < vec_max - 3 + 1) {
617 buff[ind++] = value >> 16;
618 buff[ind++] = value >> 8;
619 buff[ind++] = value;
620 }
621 break;
622
623 case 32: /* 4 byte integer */
624 if (ind < vec_max - 4 + 1) {
625 buff[ind++] = value >> 24;
626 buff[ind++] = value >> 16;
627 buff[ind++] = value >> 8;
628 buff[ind++] = value;
629 }
630 break;
631
632 default:
633 fprintf(stderr, "do_encode: Illegal width\n");
634 break;
635 }
636 }
637 }
638
639 /* Flush out any remaining bits
640 */
b36bf52b 641 if (shift && ind < vec_max)
984263bc 642 buff[ind++] = val;
984263bc
MD
643
644 if (used)
645 *used = ind;
646
647 if (error)
648 return -1;
649
650 return encoded;
651}
652
653int
5adc1304 654csio_decode(struct ccb_scsiio *csio, const char *fmt, ...)
984263bc
MD
655{
656 va_list ap;
657
658 va_start(ap, fmt);
659
660 return(do_buff_decode(csio->data_ptr, (size_t)csio->dxfer_len,
661 0, 0, fmt, ap));
662}
663
664int
5adc1304 665csio_decode_visit(struct ccb_scsiio *csio, const char *fmt,
984263bc
MD
666 void (*arg_put)(void *, int, void *, int, char *),
667 void *puthook)
668{
669 va_list ap;
670
671 /*
672 * We need some way to output things; we can't do it without
673 * the arg_put function.
674 */
675 if (arg_put == NULL)
676 return(-1);
677
678 bzero(&ap, sizeof(ap));
679
680 return(do_buff_decode(csio->data_ptr, (size_t)csio->dxfer_len,
681 arg_put, puthook, fmt, ap));
682}
683
684int
5adc1304 685buff_decode(u_int8_t *buff, size_t len, const char *fmt, ...)
984263bc
MD
686{
687 va_list ap;
688
689 va_start(ap, fmt);
690
691 return(do_buff_decode(buff, len, 0, 0, fmt, ap));
692}
693
694int
5adc1304 695buff_decode_visit(u_int8_t *buff, size_t len, const char *fmt,
984263bc
MD
696 void (*arg_put)(void *, int, void *, int, char *),
697 void *puthook)
698{
699 va_list ap;
700
701 /*
702 * We need some way to output things; we can't do it without
703 * the arg_put function.
704 */
705 if (arg_put == NULL)
706 return(-1);
707
708 bzero(&ap, sizeof(ap));
709
710 return(do_buff_decode(buff, len, arg_put, puthook, fmt, ap));
711}
712
713/*
714 * Build a SCSI CCB, given the command and data pointers and a format
715 * string describing the
716 */
717int
718csio_build(struct ccb_scsiio *csio, u_int8_t *data_ptr, u_int32_t dxfer_len,
5adc1304
PA
719 u_int32_t flags, int retry_count, int timeout, const char *cmd_spec,
720 ...)
984263bc
MD
721{
722 size_t cmdlen;
723 int retval;
724 va_list ap;
725
726 if (csio == NULL)
727 return(0);
728
729 bzero(csio, sizeof(struct ccb_scsiio));
730
731 va_start(ap, cmd_spec);
732
733 if ((retval = do_encode(csio->cdb_io.cdb_bytes, SCSI_MAX_CDBLEN,
734 &cmdlen, NULL, NULL, cmd_spec, ap)) == -1)
735 return(retval);
736
737 cam_fill_csio(csio,
738 /* retries */ retry_count,
739 /* cbfcnp */ NULL,
740 /* flags */ flags,
741 /* tag_action */ MSG_SIMPLE_Q_TAG,
742 /* data_ptr */ data_ptr,
743 /* dxfer_len */ dxfer_len,
744 /* sense_len */ SSD_FULL_SIZE,
745 /* cdb_len */ cmdlen,
746 /* timeout */ timeout ? timeout : 5000);
747
748 return(retval);
749}
750
751int
752csio_build_visit(struct ccb_scsiio *csio, u_int8_t *data_ptr,
753 u_int32_t dxfer_len, u_int32_t flags, int retry_count,
5adc1304 754 int timeout, const char *cmd_spec,
984263bc
MD
755 int (*arg_get)(void *hook, char *field_name), void *gethook)
756{
757 va_list ap;
758 size_t cmdlen;
759 int retval;
760
761 if (csio == NULL)
762 return(0);
763
764 /*
765 * We need something to encode, but we can't get it without the
766 * arg_get function.
767 */
768 if (arg_get == NULL)
769 return(-1);
770
771 bzero(&ap, sizeof(ap));
772
773 bzero(csio, sizeof(struct ccb_scsiio));
774
775 if ((retval = do_encode(csio->cdb_io.cdb_bytes, SCSI_MAX_CDBLEN,
776 &cmdlen, arg_get, gethook, cmd_spec, ap)) == -1)
777 return(retval);
778
779 cam_fill_csio(csio,
780 /* retries */ retry_count,
781 /* cbfcnp */ NULL,
782 /* flags */ flags,
783 /* tag_action */ MSG_SIMPLE_Q_TAG,
784 /* data_ptr */ data_ptr,
785 /* dxfer_len */ dxfer_len,
786 /* sense_len */ SSD_FULL_SIZE,
787 /* cdb_len */ cmdlen,
788 /* timeout */ timeout ? timeout : 5000);
789
790 return(retval);
791}
792
793int
5adc1304 794csio_encode(struct ccb_scsiio *csio, const char *fmt, ...)
984263bc
MD
795{
796 va_list ap;
797
798 if (csio == NULL)
799 return(0);
800
801 va_start(ap, fmt);
802
803 return(do_encode(csio->data_ptr, csio->dxfer_len, 0, 0, 0, fmt, ap));
804}
805
806int
5adc1304 807buff_encode_visit(u_int8_t *buff, size_t len, const char *fmt,
984263bc
MD
808 int (*arg_get)(void *hook, char *field_name), void *gethook)
809{
810 va_list ap;
811
812 /*
813 * We need something to encode, but we can't get it without the
814 * arg_get function.
815 */
816 if (arg_get == NULL)
817 return(-1);
818
819 bzero(&ap, sizeof(ap));
820
821 return(do_encode(buff, len, 0, arg_get, gethook, fmt, ap));
822}
823
824int
5adc1304 825csio_encode_visit(struct ccb_scsiio *csio, const char *fmt,
984263bc
MD
826 int (*arg_get)(void *hook, char *field_name), void *gethook)
827{
828 va_list ap;
829
830 /*
831 * We need something to encode, but we can't get it without the
832 * arg_get function.
833 */
834 if (arg_get == NULL)
835 return(-1);
836
837 bzero(&ap, sizeof(ap));
838
839 return(do_encode(csio->data_ptr, csio->dxfer_len, 0, arg_get,
840 gethook, fmt, ap));
841}