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.2 2003/06/17 04:26:48 dillon 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, 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 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, &fmt, 10); 180 if (width > 8) 181 done = 1; 182 else { 183 if (shift <= 0) { 184 bits = *databuf++; 185 shift = 8; 186 } 187 value = (bits >> (shift - width)) & 188 mask[width]; 189 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 194 195 ARG_PUT(value); 196 197 shift -= width; 198 } 199 break; 200 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; 210 211 case 2: 212 ARG_PUT((*databuf) << 8 | *(databuf + 1)); 213 databuf += 2; 214 break; 215 216 case 3: 217 ARG_PUT((*databuf) << 16 | 218 (*(databuf + 1)) << 8 | *(databuf + 2)); 219 databuf += 3; 220 break; 221 222 case 4: 223 ARG_PUT((*databuf) << 24 | 224 (*(databuf + 1)) << 16 | 225 (*(databuf + 2)) << 8 | 226 *(databuf + 3)); 227 databuf += 4; 228 break; 229 230 default: 231 done = 1; 232 break; 233 } 234 235 break; 236 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; 258 } 259 } 260 assigned++; 261 } 262 databuf += width; 263 field_name[0] = 0; 264 suppress = 0; 265 break; 266 267 case 's': /* Seek */ 268 shift = 0; 269 fmt++; 270 if (*fmt == '+') { 271 plus = 1; 272 fmt++; 273 } else 274 plus = 0; 275 276 if (tolower(*fmt) == 'v') { 277 /* 278 * You can't suppress a seek value. You also 279 * can't have a variable seek when you are using 280 * "arg_put". 281 */ 282 width = (arg_put) ? 0 : va_arg(ap, int); 283 fmt++; 284 } else 285 width = strtol(fmt, &fmt, 10); 286 287 if (plus) 288 databuf += width; /* Relative seek */ 289 else 290 databuf = base + width; /* Absolute seek */ 291 292 break; 293 294 case 0: 295 done = 1; 296 break; 297 298 default: 299 fprintf(stderr, "Unknown letter in format: %c\n", 300 letter); 301 fmt++; 302 break; 303 } 304 } 305 306 return (assigned); 307 } 308 309 /* next_field: Return the next field in a command specifier. This 310 * builds up a SCSI command using this trivial grammar: 311 * 312 * fields : field fields 313 * ; 314 * 315 * field : value 316 * | value ':' field_width 317 * ; 318 * 319 * field_width : digit 320 * | 'i' digit // i2 = 2 byte integer, i3 = 3 byte integer etc. 321 * ; 322 * 323 * value : HEX_NUMBER 324 * | 'v' // For indirection. 325 * ; 326 * 327 * Notes: 328 * Bit fields are specified MSB first to match the SCSI spec. 329 * 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 333 * 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) 338 * 339 */ 340 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) 344 { 345 char *p = *pp; 346 347 int something = 0; 348 349 enum { 350 BETWEEN_FIELDS, 351 START_FIELD, 352 GET_FIELD, 353 DONE, 354 } state; 355 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; 361 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; 367 368 state = BETWEEN_FIELDS; 369 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; 384 385 p++; 386 387 while (*p && *p != '}') { 388 if(name && i < n_name) { 389 name[i] = *p; 390 i++; 391 } 392 p++; 393 } 394 395 if(name && i < n_name) 396 name[i] = 0; 397 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') { 413 /* 414 * Try to work without the "v". 415 */ 416 something = 2; 417 value = *value_p; 418 p++; 419 420 *fmt = 'i'; 421 field_size = 8; 422 field_width = strtol(p, &p, 10); 423 state = DONE; 424 425 } else if (tolower(*p) == 't') { 426 /* 427 * XXX: B can't work: Sees the 'b' as a 428 * hex digit in "isxdigit". try "t" for 429 * bit field. 430 */ 431 something = 2; 432 value = *value_p; 433 p++; 434 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); 450 } 451 state = DONE; 452 } else { 453 fprintf(stderr, "Invalid starting " 454 "character: %c\n", *p); 455 is_error = 1; 456 state = DONE; 457 } 458 break; 459 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; 469 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') { 477 478 /* Integral (bytes) */ 479 p++; 480 481 *fmt = 'i'; 482 field_size = 8; 483 field_width = strtol(p, &p, 10); 484 state = DONE; 485 } else if (*p == 'b') { 486 487 /* Bits */ 488 p++; 489 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; 499 } 500 break; 501 502 case DONE: 503 break; 504 } 505 } 506 507 if (is_error) { 508 *error_p = 1; 509 return 0; 510 } 511 512 *error_p = 0; 513 *pp = p; 514 *width_p = field_width * field_size; 515 *value_p = value; 516 *suppress_p = suppress; 517 518 return (something); 519 } 520 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) 524 { 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]; 533 534 ind = 0; 535 shift = 0; 536 val = 0; 537 538 while ((ret = next_field(&fmt, &c, &width, &value, field_name, 539 sizeof(field_name), &error, &suppress))) { 540 encoded++; 541 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); 549 } 550 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; 560 } 561 562 /* A width of < 8 is a bit field. */ 563 if (width < 8) { 564 565 /* This is a bit field. We start with the high bits 566 * so it reads the same as the SCSI spec. 567 */ 568 569 shift += width; 570 571 val |= (value << (8 - shift)); 572 573 if (shift == 8) { 574 if (ind < vec_max) { 575 buff[ind++] = val; 576 val = 0; 577 } 578 shift = 0; 579 } 580 } else { 581 if (shift) { 582 if (ind < vec_max) { 583 buff[ind++] = val; 584 val = 0; 585 } 586 shift = 0; 587 } 588 switch(width) { 589 case 8: /* 1 byte integer */ 590 if (ind < vec_max) 591 buff[ind++] = value; 592 break; 593 594 case 16: /* 2 byte integer */ 595 if (ind < vec_max - 2 + 1) { 596 buff[ind++] = value >> 8; 597 buff[ind++] = value; 598 } 599 break; 600 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; 606 } 607 break; 608 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; 615 } 616 break; 617 618 default: 619 fprintf(stderr, "do_encode: Illegal width\n"); 620 break; 621 } 622 } 623 } 624 625 /* Flush out any remaining bits 626 */ 627 if (shift && ind < vec_max) { 628 buff[ind++] = val; 629 val = 0; 630 } 631 632 633 if (used) 634 *used = ind; 635 636 if (error) 637 return -1; 638 639 return encoded; 640 } 641 642 int 643 csio_decode(struct ccb_scsiio *csio, char *fmt, ...) 644 { 645 va_list ap; 646 647 va_start(ap, fmt); 648 649 return(do_buff_decode(csio->data_ptr, (size_t)csio->dxfer_len, 650 0, 0, fmt, ap)); 651 } 652 653 int 654 csio_decode_visit(struct ccb_scsiio *csio, char *fmt, 655 void (*arg_put)(void *, int, void *, int, char *), 656 void *puthook) 657 { 658 va_list ap; 659 660 /* 661 * We need some way to output things; we can't do it without 662 * the arg_put function. 663 */ 664 if (arg_put == NULL) 665 return(-1); 666 667 bzero(&ap, sizeof(ap)); 668 669 return(do_buff_decode(csio->data_ptr, (size_t)csio->dxfer_len, 670 arg_put, puthook, fmt, ap)); 671 } 672 673 int 674 buff_decode(u_int8_t *buff, size_t len, char *fmt, ...) 675 { 676 va_list ap; 677 678 va_start(ap, fmt); 679 680 return(do_buff_decode(buff, len, 0, 0, fmt, ap)); 681 } 682 683 int 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) 687 { 688 va_list ap; 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 bzero(&ap, sizeof(ap)); 698 699 return(do_buff_decode(buff, len, arg_put, puthook, fmt, ap)); 700 } 701 702 /* 703 * Build a SCSI CCB, given the command and data pointers and a format 704 * string describing the 705 */ 706 int 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, ...) 709 { 710 size_t cmdlen; 711 int retval; 712 va_list ap; 713 714 if (csio == NULL) 715 return(0); 716 717 bzero(csio, sizeof(struct ccb_scsiio)); 718 719 va_start(ap, cmd_spec); 720 721 if ((retval = do_encode(csio->cdb_io.cdb_bytes, SCSI_MAX_CDBLEN, 722 &cmdlen, NULL, NULL, cmd_spec, ap)) == -1) 723 return(retval); 724 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); 735 736 return(retval); 737 } 738 739 int 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) 744 { 745 va_list ap; 746 size_t cmdlen; 747 int retval; 748 749 if (csio == NULL) 750 return(0); 751 752 /* 753 * We need something to encode, but we can't get it without the 754 * arg_get function. 755 */ 756 if (arg_get == NULL) 757 return(-1); 758 759 bzero(&ap, sizeof(ap)); 760 761 bzero(csio, sizeof(struct ccb_scsiio)); 762 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); 766 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); 777 778 return(retval); 779 } 780 781 int 782 csio_encode(struct ccb_scsiio *csio, char *fmt, ...) 783 { 784 va_list ap; 785 786 if (csio == NULL) 787 return(0); 788 789 va_start(ap, fmt); 790 791 return(do_encode(csio->data_ptr, csio->dxfer_len, 0, 0, 0, fmt, ap)); 792 } 793 794 int 795 buff_encode_visit(u_int8_t *buff, size_t len, char *fmt, 796 int (*arg_get)(void *hook, char *field_name), void *gethook) 797 { 798 va_list ap; 799 800 /* 801 * We need something to encode, but we can't get it without the 802 * arg_get function. 803 */ 804 if (arg_get == NULL) 805 return(-1); 806 807 bzero(&ap, sizeof(ap)); 808 809 return(do_encode(buff, len, 0, arg_get, gethook, fmt, ap)); 810 } 811 812 int 813 csio_encode_visit(struct ccb_scsiio *csio, char *fmt, 814 int (*arg_get)(void *hook, char *field_name), void *gethook) 815 { 816 va_list ap; 817 818 /* 819 * We need something to encode, but we can't get it without the 820 * arg_get function. 821 */ 822 if (arg_get == NULL) 823 return(-1); 824 825 bzero(&ap, sizeof(ap)); 826 827 return(do_encode(csio->data_ptr, csio->dxfer_len, 0, arg_get, 828 gethook, fmt, ap)); 829 } 830