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 45 #include <bus/cam/cam.h> 46 #include <bus/cam/cam_ccb.h> 47 #include <bus/cam/scsi/scsi_message.h> 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 97 static int 98 do_buff_decode(u_int8_t *databuf, size_t len, 99 void (*arg_put)(void *, int , void *, int, char *), 100 void *puthook, const char *fmt, va_list ap) 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; 111 char *intendp; 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++; 179 width = strtol(fmt, &intendp, 10); 180 fmt = intendp; 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++; 205 width = strtol(fmt, &intendp, 10); 206 fmt = intendp; 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++; 244 width = strtol(fmt, &intendp, 10); 245 fmt = intendp; 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++; 287 } else { 288 width = strtol(fmt, &intendp, 10); 289 fmt = intendp; 290 } 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 346 static int 347 next_field(const char **pp, char *fmt, int *width_p, int *value_p, char *name, 348 int n_name, int *error_p, int *suppress_p) 349 { 350 const char *p = *pp; 351 char *intendp; 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; 411 value = strtol(p, &intendp, 16); 412 p = intendp; 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; 429 field_width = strtol(p, &intendp, 10); 430 p = intendp; 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; 445 field_width = strtol(p, &intendp, 10); 446 p = intendp; 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; 458 value = strtol(p, &intendp, 0); 459 p = intendp; 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; 484 field_width = strtol(p, &intendp, 10); 485 p = intendp; 486 state = DONE; 487 } else if (*p == 'i') { 488 489 /* Integral (bytes) */ 490 p++; 491 492 *fmt = 'i'; 493 field_size = 8; 494 field_width = strtol(p, &intendp, 10); 495 p = intendp; 496 state = DONE; 497 } else if (*p == 'b') { 498 499 /* Bits */ 500 p++; 501 502 *fmt = 'b'; 503 field_size = 1; 504 field_width = strtol(p, &intendp, 10); 505 p = intendp; 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 534 static int 535 do_encode(u_char *buff, size_t vec_max, size_t *used, 536 int (*arg_get)(void *, char *), void *gethook, const char *fmt, 537 va_list ap) 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 */ 641 if (shift && ind < vec_max) 642 buff[ind++] = val; 643 644 if (used) 645 *used = ind; 646 647 if (error) 648 return -1; 649 650 return encoded; 651 } 652 653 int 654 csio_decode(struct ccb_scsiio *csio, const char *fmt, ...) 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 664 int 665 csio_decode_visit(struct ccb_scsiio *csio, const char *fmt, 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 684 int 685 buff_decode(u_int8_t *buff, size_t len, const char *fmt, ...) 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 694 int 695 buff_decode_visit(u_int8_t *buff, size_t len, const char *fmt, 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 */ 717 int 718 csio_build(struct ccb_scsiio *csio, u_int8_t *data_ptr, u_int32_t dxfer_len, 719 u_int32_t flags, int retry_count, int timeout, const char *cmd_spec, 720 ...) 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 751 int 752 csio_build_visit(struct ccb_scsiio *csio, u_int8_t *data_ptr, 753 u_int32_t dxfer_len, u_int32_t flags, int retry_count, 754 int timeout, const char *cmd_spec, 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 793 int 794 csio_encode(struct ccb_scsiio *csio, const char *fmt, ...) 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 806 int 807 buff_encode_visit(u_int8_t *buff, size_t len, const char *fmt, 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 824 int 825 csio_encode_visit(struct ccb_scsiio *csio, const char *fmt, 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 } 842