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