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