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