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