ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/src/trunk/lib/libcam/scsi_cmdparse.c
Revision: 10880
Committed: Wed Jun 13 23:59:33 2018 UTC (5 years, 10 months ago) by laffer1
Content type: text/plain
File size: 17972 byte(s)
Log Message:
tag

File Contents

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

Properties

Name Value
svn:keywords MidnightBSD=%H