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