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