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