1 /* $OpenBSD: libscsi.c,v 1.6 2008/01/13 20:23:34 chl Exp $ */ 2 3 /* Copyright (c) 1994 HD Associates 4 * (contact: dufault@hda.com) 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by HD Associates 18 * 4. Neither the name of the HD Associaates nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL HD ASSOCIATES OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * $FreeBSD: scsi.c,v 1.6 1995/05/30 05:47:26 rgrimes Exp $ 35 */ 36 #include <stdlib.h> 37 #include <stdio.h> 38 #include <ctype.h> 39 #include <string.h> 40 #include <sys/scsiio.h> 41 #include <errno.h> 42 #include <stdarg.h> 43 #include <fcntl.h> 44 45 #include "libscsi.h" 46 47 static struct { 48 FILE *db_f; 49 int db_level; 50 int db_trunc; 51 } behave; 52 53 /* scsireq_reset: Reset a scsireq structure. 54 */ 55 scsireq_t * 56 scsireq_reset(scsireq_t *scsireq) 57 { 58 if (scsireq == 0) 59 return scsireq; 60 61 scsireq->flags = 0; /* info about the request status and type */ 62 scsireq->timeout = 2000; /* 2 seconds */ 63 bzero(scsireq->cmd, sizeof(scsireq->cmd)); 64 scsireq->cmdlen = 0; 65 /* Leave scsireq->databuf alone */ 66 /* Leave scsireq->datalen alone */ 67 scsireq->datalen_used = 0; 68 bzero(scsireq->sense, sizeof(scsireq->sense)); 69 scsireq->senselen = sizeof(scsireq->sense); 70 scsireq->senselen_used = 0; 71 scsireq->status = 0; 72 scsireq->retsts = 0; 73 scsireq->error = 0; 74 75 return scsireq; 76 } 77 78 /* scsireq_new: Allocate and initialize a new scsireq. 79 */ 80 scsireq_t * 81 scsireq_new(void) 82 { 83 scsireq_t *p = (scsireq_t *)malloc(sizeof(scsireq_t)); 84 85 if (p) 86 scsireq_reset(p); 87 88 return p; 89 } 90 91 /* 92 * Decode: Decode the data section of a scsireq. This decodes 93 * trivial grammar: 94 * 95 * fields : field fields 96 * ; 97 * 98 * field : field_specifier 99 * | control 100 * ; 101 * 102 * control : 's' seek_value 103 * | 's' '+' seek_value 104 * ; 105 * 106 * seek_value : DECIMAL_NUMBER 107 * | 'v' // For indirect seek, i.e., value from the arg list 108 * ; 109 * 110 * field_specifier : type_specifier field_width 111 * | '{' NAME '}' type_specifier field_width 112 * ; 113 * 114 * field_width : DECIMAL_NUMBER 115 * ; 116 * 117 * type_specifier : 'i' // Integral types (i1, i2, i3, i4) 118 * | 'b' // Bits 119 * | 't' // Bits 120 * | 'c' // Character arrays 121 * | 'z' // Character arrays with zeroed trailing spaces 122 * ; 123 * 124 * Notes: 125 * 1. Integral types are swapped into host order. 126 * 2. Bit fields are allocated MSB to LSB to match the SCSI spec documentation. 127 * 3. 's' permits "seeking" in the string. "s+DECIMAL" seeks relative to 128 * DECIMAL; "sDECIMAL" seeks absolute to decimal. 129 * 4. 's' permits an indirect reference. "sv" or "s+v" will get the 130 * next integer value from the arg array. 131 * 5. Field names can be anything between the braces 132 * 133 * BUGS: 134 * i and b types are promoted to ints. 135 * 136 */ 137 static int 138 do_buff_decode(u_char *databuf, size_t len, 139 void (*arg_put)(void *, int , void *, int, char *), 140 void *puthook, char *fmt, va_list ap) 141 { 142 int assigned = 0; 143 int width; 144 int suppress; 145 int plus; 146 int done = 0; 147 static u_char mask[] = {0, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; 148 int value; 149 u_char *base = databuf; 150 char letter; 151 char field_name[80]; 152 153 # define ARG_PUT(ARG) \ 154 do \ 155 { \ 156 if (!suppress) { \ 157 if (arg_put) \ 158 (*arg_put)(puthook, (letter == 't' ? 'b' : letter), \ 159 (void *)((long)(ARG)), 1, field_name); \ 160 else \ 161 *(va_arg(ap, int *)) = (ARG); \ 162 assigned++; \ 163 } \ 164 field_name[0] = 0; \ 165 suppress = 0; \ 166 } while (0) 167 168 u_char bits = 0; /* For bit fields */ 169 int shift = 0; /* Bits already shifted out */ 170 suppress = 0; 171 field_name[0] = 0; 172 173 while (!done) { 174 switch (letter = *fmt) { 175 case ' ': /* White space */ 176 case '\t': 177 case '\r': 178 case '\n': 179 case '\f': 180 fmt++; 181 break; 182 183 case '#': /* Comment */ 184 while (*fmt && (*fmt != '\n')) 185 fmt++; 186 if (fmt) 187 fmt++; /* Skip '\n' */ 188 break; 189 190 case '*': /* Suppress assignment */ 191 fmt++; 192 suppress = 1; 193 break; 194 195 case '{': /* Field Name */ 196 { 197 int i = 0; 198 fmt++; /* Skip '{' */ 199 while (*fmt && (*fmt != '}')) { 200 if (i < sizeof(field_name)) 201 field_name[i++] = *fmt; 202 203 fmt++; 204 } 205 if (fmt) 206 fmt++; /* Skip '}' */ 207 field_name[i] = 0; 208 } 209 break; 210 211 case 't': /* Bit (field) */ 212 case 'b': /* Bits */ 213 fmt++; 214 width = strtol(fmt, &fmt, 10); 215 if (width > 8) 216 done = 1; 217 else { 218 if (shift <= 0) { 219 bits = *databuf++; 220 shift = 8; 221 } 222 value = (bits >> (shift - width)) & mask[width]; 223 224 #if 0 225 printf("shift %2d bits %02x value %02x width %2d mask %02x\n", 226 shift, bits, value, width, mask[width]); 227 #endif 228 229 ARG_PUT(value); 230 231 shift -= width; 232 } 233 234 break; 235 236 case 'i': /* Integral values */ 237 shift = 0; 238 fmt++; 239 width = strtol(fmt, &fmt, 10); 240 switch (width) { 241 case 1: 242 ARG_PUT(*databuf); 243 databuf++; 244 break; 245 246 case 2: 247 ARG_PUT((*databuf) << 8 | *(databuf + 1)); 248 databuf += 2; 249 break; 250 251 case 3: 252 ARG_PUT( 253 (*databuf) << 16 | 254 (*(databuf + 1)) << 8 | 255 *(databuf + 2)); 256 databuf += 3; 257 break; 258 259 case 4: 260 ARG_PUT( 261 (*databuf) << 24 | 262 (*(databuf + 1)) << 16 | 263 (*(databuf + 2)) << 8 | 264 *(databuf + 3)); 265 databuf += 4; 266 break; 267 268 default: 269 done = 1; 270 } 271 272 break; 273 274 case 'c': /* Characters (i.e., not swapped) */ 275 case 'z': /* Characters with zeroed trailing spaces */ 276 shift = 0; 277 fmt++; 278 width = strtol(fmt, &fmt, 10); 279 if (!suppress) { 280 if (arg_put) 281 (*arg_put)(puthook, (letter == 't' ? 'b' : letter), 282 databuf, width, field_name); 283 else { 284 char *dest; 285 dest = va_arg(ap, char *); 286 bcopy(databuf, dest, width); 287 if (letter == 'z') { 288 char *p; 289 for (p = dest + width - 1; 290 (p >= (char *)dest) && (*p == ' '); p--) 291 *p = 0; 292 } 293 } 294 assigned++; 295 } 296 databuf += width; 297 field_name[0] = 0; 298 suppress = 0; 299 break; 300 301 case 's': /* Seek */ 302 shift = 0; 303 fmt++; 304 if (*fmt == '+') { 305 plus = 1; 306 fmt++; 307 } else 308 plus = 0; 309 310 if (tolower(*fmt) == 'v') { 311 /* You can't suppress a seek value. You also 312 * can't have a variable seek when you are using 313 * "arg_put". 314 */ 315 width = (arg_put) ? 0 : va_arg(ap, int); 316 fmt++; 317 } else 318 width = strtol(fmt, &fmt, 10); 319 320 if (plus) 321 databuf += width; /* Relative seek */ 322 else 323 databuf = base + width; /* Absolute seek */ 324 325 break; 326 327 case 0: 328 done = 1; 329 break; 330 331 default: 332 fprintf(stderr, "Unknown letter in format: %c\n", letter); 333 fmt++; 334 } 335 } 336 337 return assigned; 338 } 339 340 int 341 scsireq_decode_visit(scsireq_t *scsireq, char *fmt, 342 void (*arg_put)(void *, int , void *, int, char *), void *puthook) 343 { 344 va_list ap; 345 int ret; 346 347 ret = do_buff_decode(scsireq->databuf, (size_t)scsireq->datalen, 348 arg_put, puthook, fmt, ap); 349 va_end (ap); 350 return (ret); 351 } 352 353 int 354 scsireq_buff_decode_visit(u_char *buff, size_t len, char *fmt, 355 void (*arg_put)(void *, int, void *, int, char *), void *puthook) 356 { 357 va_list ap; 358 359 /* XXX */ 360 return do_buff_decode(buff, len, arg_put, puthook, fmt, ap); 361 } 362 363 /* next_field: Return the next field in a command specifier. This 364 * builds up a SCSI command using this trivial grammar: 365 * 366 * fields : field fields 367 * ; 368 * 369 * field : value 370 * | value ':' field_width 371 * ; 372 * 373 * field_width : digit 374 * | 'i' digit // i2 = 2 byte integer, i3 = 3 byte integer etc. 375 * ; 376 * 377 * value : HEX_NUMBER 378 * | 'v' // For indirection. 379 * ; 380 * 381 * Notes: 382 * Bit fields are specified MSB first to match the SCSI spec. 383 * 384 * Examples: 385 * TUR: "0 0 0 0 0 0" 386 * WRITE BUFFER: "38 v:3 0:2 0:3 v v:i3 v:i3 0", mode, buffer_id, list_length 387 * 388 * The function returns the value: 389 * 0: For reached end, with error_p set if an error was found 390 * 1: For valid stuff setup 391 * 2: For "v" was entered as the value (implies use varargs) 392 * 393 */ 394 static int 395 next_field(char **pp, char *fmt, int *width_p, int *value_p, char *name, 396 int n_name, int *error_p, int *suppress_p) 397 { 398 char *p = *pp; 399 400 int something = 0; 401 402 enum { BETWEEN_FIELDS, START_FIELD, GET_FIELD, DONE } state; 403 404 int value = 0; 405 int field_size; /* Default to byte field type... */ 406 int field_width; /* 1 byte wide */ 407 int is_error = 0; 408 int suppress = 0; 409 410 field_size = 8; /* Default to byte field type... */ 411 *fmt = 'i'; 412 field_width = 1; /* 1 byte wide */ 413 if (name) 414 *name = 0; 415 416 state = BETWEEN_FIELDS; 417 418 while (state != DONE) 419 switch (state) 420 { 421 case BETWEEN_FIELDS: 422 if (*p == 0) 423 state = DONE; 424 else if (isspace(*p)) 425 p++; 426 else if (*p == '#') { 427 while (*p && *p != '\n') 428 p++; 429 430 if (p) 431 p++; 432 } else if (*p == '{') { 433 int i = 0; 434 435 p++; 436 437 while (*p && *p != '}') { 438 if (name && i < n_name) { 439 name[i] = *p; 440 i++; 441 } 442 p++; 443 } 444 445 if (name && i < n_name) 446 name[i] = 0; 447 448 if (*p == '}') 449 p++; 450 } else if (*p == '*') { 451 p++; 452 suppress = 1; 453 } else if (isxdigit(*p)) { 454 something = 1; 455 value = strtol(p, &p, 16); 456 state = START_FIELD; 457 } else if (tolower(*p) == 'v') { 458 p++; 459 something = 2; 460 value = *value_p; 461 state = START_FIELD; 462 } 463 /* try to work without the 'v' */ 464 else if (tolower(*p) == 'i') { 465 something = 2; 466 value = *value_p; 467 p++; 468 469 *fmt = 'i'; 470 field_size = 8; 471 field_width = strtol(p, &p, 10); 472 state = DONE; 473 } else if (tolower(*p) == 't') { 474 /* XXX: B can't work: Sees the 'b' 475 * as a hex digit in "isxdigit". 476 * try "t" for bit field. 477 */ 478 something = 2; 479 value = *value_p; 480 p++; 481 482 *fmt = 'b'; 483 field_size = 1; 484 field_width = strtol(p, &p, 10); 485 state = DONE; 486 } else if (tolower(*p) == 's') { /* Seek */ 487 *fmt = 's'; 488 p++; 489 if (tolower(*p) == 'v') { 490 p++; 491 something = 2; 492 value = *value_p; 493 } else { 494 something = 1; 495 value = strtol(p, &p, 0); 496 } 497 state = DONE; 498 } else { 499 fprintf(stderr, "Invalid starting character: %c\n", *p); 500 is_error = 1; 501 state = DONE; 502 } 503 break; 504 505 case START_FIELD: 506 if (*p == ':') { 507 p++; 508 field_size = 1; /* Default to bits when specified */ 509 state = GET_FIELD; 510 } else 511 state = DONE; 512 break; 513 514 case GET_FIELD: 515 if (isdigit(*p)) { 516 *fmt = 'b'; 517 field_size = 1; 518 field_width = strtol(p, &p, 10); 519 state = DONE; 520 } else if (*p == 'i') { /* Integral (bytes) */ 521 p++; 522 523 *fmt = 'i'; 524 field_size = 8; 525 field_width = strtol(p, &p, 10); 526 state = DONE; 527 } else if (*p == 'b') { /* Bits */ 528 p++; 529 530 *fmt = 'b'; 531 field_size = 1; 532 field_width = strtol(p, &p, 10); 533 state = DONE; 534 } else { 535 fprintf(stderr, "Invalid startfield %c (%02x)\n", 536 *p, *p); 537 is_error = 1; 538 state = DONE; 539 } 540 break; 541 542 case DONE: 543 break; 544 } 545 546 if (is_error) { 547 *error_p = 1; 548 return 0; 549 } 550 551 *error_p = 0; 552 *pp = p; 553 *width_p = field_width * field_size; 554 *value_p = value; 555 *suppress_p = suppress; 556 557 return something; 558 } 559 560 static int 561 do_encode(u_char *buff, size_t vec_max, size_t *used, 562 int (*arg_get)(void *, char *), 563 void *gethook, char *fmt, va_list ap) 564 { 565 int ind; 566 int shift; 567 u_char val; 568 int ret; 569 int width, value, error, suppress; 570 char c; 571 int encoded = 0; 572 char field_name[80]; 573 574 ind = 0; 575 shift = 0; 576 val = 0; 577 578 while ((ret = next_field(&fmt, &c, &width, &value, field_name, 579 sizeof(field_name), &error, &suppress))) 580 { 581 encoded++; 582 583 if (ret == 2) { 584 if (suppress) 585 value = 0; 586 else 587 value = arg_get ? (*arg_get)(gethook, field_name) : va_arg(ap, int); 588 } 589 590 #if 0 591 printf( 592 "do_encode: ret %d fmt %c width %d value %d name \"%s\"" 593 "error %d suppress %d\n", 594 ret, c, width, value, field_name, error, suppress); 595 #endif 596 597 if (c == 's') /* Absolute seek */ 598 { 599 ind = value; 600 continue; 601 } 602 603 if (width < 8) /* A width of < 8 is a bit field. */ 604 { 605 606 /* This is a bit field. We start with the high bits 607 * so it reads the same as the SCSI spec. 608 */ 609 610 shift += width; 611 612 val |= (value << (8 - shift)); 613 614 if (shift == 8) { 615 if (ind < vec_max) { 616 buff[ind++] = val; 617 val = 0; 618 } 619 shift = 0; 620 } 621 } else { 622 if (shift) { 623 if (ind < vec_max) { 624 buff[ind++] = val; 625 val = 0; 626 } 627 shift = 0; 628 } 629 switch (width) 630 { 631 case 8: /* 1 byte integer */ 632 if (ind < vec_max) 633 buff[ind++] = value; 634 break; 635 636 case 16: /* 2 byte integer */ 637 if (ind < vec_max - 2 + 1) { 638 buff[ind++] = value >> 8; 639 buff[ind++] = value; 640 } 641 break; 642 643 case 24: /* 3 byte integer */ 644 if (ind < vec_max - 3 + 1) { 645 buff[ind++] = value >> 16; 646 buff[ind++] = value >> 8; 647 buff[ind++] = value; 648 } 649 break; 650 651 case 32: /* 4 byte integer */ 652 if (ind < vec_max - 4 + 1) { 653 buff[ind++] = value >> 24; 654 buff[ind++] = value >> 16; 655 buff[ind++] = value >> 8; 656 buff[ind++] = value; 657 } 658 break; 659 660 default: 661 fprintf(stderr, "do_encode: Illegal width\n"); 662 break; 663 } 664 } 665 } 666 667 /* Flush out any remaining bits */ 668 if (shift && ind < vec_max) { 669 buff[ind++] = val; 670 val = 0; 671 } 672 673 674 if (used) 675 *used = ind; 676 677 if (error) 678 return -1; 679 680 return encoded; 681 } 682 683 /* XXX: Should be a constant in scsiio.h 684 */ 685 #define CMD_BUFLEN 16 686 687 scsireq_t * 688 scsireq_build(scsireq_t *scsireq, u_long datalen, caddr_t databuf, 689 u_long flags, char *cmd_spec, ...) 690 { 691 size_t cmdlen; 692 va_list ap; 693 694 if (scsireq == 0) 695 return 0; 696 697 scsireq_reset(scsireq); 698 699 if (databuf) { 700 scsireq->databuf = databuf; 701 scsireq->datalen = datalen; 702 scsireq->flags = flags; 703 } 704 else if (datalen) { 705 /* XXX: Good way to get a memory leak. Perhaps this should be 706 * removed. 707 */ 708 if ( (scsireq->databuf = malloc(datalen)) == 0) 709 return 0; 710 711 scsireq->datalen = datalen; 712 scsireq->flags = flags; 713 } 714 715 va_start(ap, cmd_spec); 716 717 if (do_encode(scsireq->cmd, CMD_BUFLEN, &cmdlen, 0, 0, cmd_spec, ap) == -1) 718 return 0; 719 va_end (ap); 720 721 scsireq->cmdlen = cmdlen; 722 return scsireq; 723 } 724 725 scsireq_t 726 *scsireq_build_visit(scsireq_t *scsireq, u_long datalen, caddr_t databuf, 727 u_long flags, char *cmd_spec, 728 int (*arg_get)(void *hook, char *field_name), void *gethook) 729 { 730 size_t cmdlen; 731 va_list ap; 732 733 if (scsireq == 0) 734 return 0; 735 736 scsireq_reset(scsireq); 737 738 if (databuf) { 739 scsireq->databuf = databuf; 740 scsireq->datalen = datalen; 741 scsireq->flags = flags; 742 } else if (datalen) { 743 /* XXX: Good way to get a memory leak. Perhaps this should be 744 * removed. 745 */ 746 if ( (scsireq->databuf = malloc(datalen)) == 0) 747 return 0; 748 749 scsireq->datalen = datalen; 750 scsireq->flags = flags; 751 } 752 753 if (do_encode(scsireq->cmd, CMD_BUFLEN, &cmdlen, arg_get, gethook, 754 cmd_spec, ap) == -1) 755 return 0; 756 757 scsireq->cmdlen = cmdlen; 758 759 return scsireq; 760 } 761 762 int 763 scsireq_buff_encode_visit(u_char *buff, size_t len, char *fmt, 764 int (*arg_get)(void *hook, char *field_name), void *gethook) 765 { 766 va_list ap; 767 return do_encode(buff, len, 0, arg_get, gethook, fmt, ap); 768 } 769 770 int 771 scsireq_encode_visit(scsireq_t *scsireq, char *fmt, 772 int (*arg_get)(void *hook, char *field_name), void *gethook) 773 { 774 va_list ap; 775 return do_encode(scsireq->databuf, scsireq->datalen, 0, 776 arg_get, gethook, fmt, ap); 777 } 778 779 FILE * 780 scsi_debug_output(char *s) 781 { 782 if (s == 0) 783 behave.db_f = 0; 784 else { 785 behave.db_f = fopen(s, "w"); 786 787 if (behave.db_f == 0) 788 behave.db_f = stderr; 789 } 790 791 return behave.db_f; 792 } 793 794 #define SCSI_TRUNCATE -1 795 796 typedef struct scsi_assoc { 797 int code; 798 char *text; 799 } scsi_assoc_t; 800 801 static scsi_assoc_t retsts[] = 802 { 803 { SCCMD_OK, "No error" }, 804 { SCCMD_TIMEOUT, "Command Timeout" }, 805 { SCCMD_BUSY, "Busy" }, 806 { SCCMD_SENSE, "Sense Returned" }, 807 { SCCMD_UNKNOWN, "Unknown return status" }, 808 809 { 0, 0 } 810 }; 811 812 static char * 813 scsi_assoc_text(int code, scsi_assoc_t *tab) 814 { 815 while (tab->text) { 816 if (tab->code == code) 817 return tab->text; 818 819 tab++; 820 } 821 822 return "Unknown code"; 823 } 824 825 void 826 scsi_dump(FILE *f, char *text, u_char *p, int req, int got, int dump_print) 827 { 828 int i; 829 int trunc = 0; 830 831 if (f == 0 || req == 0) 832 return; 833 834 fprintf(f, "%s (%d of %d):\n", text, got, req); 835 836 if (behave.db_trunc != -1 && got > behave.db_trunc) { 837 trunc = 1; 838 got = behave.db_trunc; 839 } 840 841 for (i = 0; i < got; i++) { 842 fprintf(f, "%02x", p[i]); 843 844 putc(' ', f); 845 846 if ((i % 16) == 15 || i == got - 1) { 847 int j; 848 if (dump_print) { 849 fprintf(f, " # "); 850 for (j = i - 15; j <= i; j++) 851 putc((isprint(p[j]) ? p[j] : '.'), f); 852 853 putc('\n', f); 854 } else 855 putc('\n', f); 856 } 857 } 858 859 fprintf(f, "%s", (trunc) ? "(truncated)...\n" : "\n"); 860 } 861 862 /* XXX: sense_7x_dump and scsi_sense dump was just sort of 863 * grabbed out of the old ds 864 * library and not really merged in carefully. It should use the 865 * new buffer decoding stuff. 866 */ 867 868 /* Get unsigned long. 869 */ 870 static u_long 871 g_u_long(u_char *s) 872 { 873 return (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; 874 } 875 876 /* In the old software you could patch in a special error table: 877 */ 878 static scsi_assoc_t *error_table = 0; 879 880 static void 881 sense_7x_dump(FILE *f, scsireq_t *scsireq) 882 { 883 int code; 884 u_char *s = (u_char *)scsireq->sense; 885 int valid = (*s) & 0x80; 886 u_long val; 887 888 static scsi_assoc_t sense[] = { 889 { 0, "No sense" }, 890 { 1, "Recovered error" }, 891 { 2, "Not Ready" }, 892 { 3, "Medium error" }, 893 { 4, "Hardware error" }, 894 { 5, "Illegal request" }, 895 { 6, "Unit attention" }, 896 { 7, "Data protect" }, 897 { 8, "Blank check" }, 898 { 9, "Vendor specific" }, 899 { 0xa, "Copy aborted" }, 900 { 0xb, "Aborted Command" }, 901 { 0xc, "Equal" }, 902 { 0xd, "Volume overflow" }, 903 { 0xe, "Miscompare" }, 904 { 0, 0 }, 905 }; 906 907 static scsi_assoc_t code_tab[] = { 908 {0x70, "current errors"}, 909 {0x71, "deferred errors"}, 910 }; 911 912 fprintf(f, "Error code is \"%s\"\n", scsi_assoc_text(s[0]&0x7F, code_tab)); 913 fprintf(f, "Segment number is %02x\n", s[1]); 914 915 if (s[2] & 0x20) 916 fprintf(f, "Incorrect Length Indicator is set.\n"); 917 918 fprintf(f, "Sense key is \"%s\"\n", scsi_assoc_text(s[2] & 0x7, sense)); 919 920 val = g_u_long(s + 3); 921 fprintf(f, "The Information field is%s %08lx (%ld).\n", 922 valid ? "" : " not valid but contains", (long)val, (long)val); 923 924 val = g_u_long(s + 8); 925 fprintf(f, "The Command Specific Information field is %08lx (%ld).\n", 926 (long)val, (long)val); 927 928 fprintf(f, "Additional sense code: %02x\n", s[12]); 929 fprintf(f, "Additional sense code qualifier: %02x\n", s[13]); 930 931 code = (s[12] << 8) | s[13]; 932 933 if (error_table) 934 fprintf(f, "%s\n", scsi_assoc_text(code, error_table)); 935 936 if (s[15] & 0x80) { 937 if ((s[2] & 0x7) == 0x05) /* Illegal request */ 938 { 939 int byte; 940 u_char value, bit; 941 int bad_par = ((s[15] & 0x40) == 0); 942 fprintf(f, "Illegal value in the %s.\n", 943 (bad_par ? "parameter list" : "command descriptor block")); 944 byte = ((s[16] << 8) | s[17]); 945 value = bad_par ? (u_char)scsireq->databuf[byte] : (u_char)scsireq->cmd[byte]; 946 bit = s[15] & 0x7; 947 if (s[15] & 0x08) 948 fprintf(f, "Bit %d of byte %d (value %02x) is illegal.\n", 949 bit, byte, value); 950 else 951 fprintf(f, "Byte %d (value %02x) is illegal.\n", byte, value); 952 } else { 953 fprintf(f, "Sense Key Specific (valid but not illegal request):\n"); 954 fprintf(f, "%02x %02x %02x\n", s[15] & 0x7f, s[16], s[17]); 955 } 956 } 957 } 958 959 /* scsi_sense_dump: Dump the sense portion of the scsireq structure. 960 */ 961 static void 962 scsi_sense_dump(FILE *f, scsireq_t *scsireq) 963 { 964 u_char *s = (u_char *)scsireq->sense; 965 int code = (*s) & 0x7f; 966 967 if (scsireq->senselen_used == 0) { 968 fprintf(f, "No sense sent.\n"); 969 return; 970 } 971 972 #if 0 973 if (!valid) 974 fprintf(f, "The sense data is not valid.\n"); 975 #endif 976 977 switch (code) { 978 case 0x70: 979 case 0x71: 980 sense_7x_dump(f, scsireq); 981 break; 982 983 default: 984 fprintf(f, "No sense dump for error code %02x.\n", code); 985 } 986 scsi_dump(f, "sense", s, scsireq->senselen, scsireq->senselen_used, 0); 987 } 988 989 static void 990 scsi_retsts_dump(FILE *f, scsireq_t *scsireq) 991 { 992 if (scsireq->retsts == 0) 993 return; 994 995 fprintf(f, "return status %d (%s)", 996 scsireq->retsts, scsi_assoc_text(scsireq->retsts, retsts)); 997 998 switch (scsireq->retsts) { 999 case SCCMD_TIMEOUT: 1000 fprintf(f, " after %ld ms", scsireq->timeout); 1001 break; 1002 1003 default: 1004 break; 1005 } 1006 } 1007 1008 int 1009 scsi_debug(FILE *f, int ret, scsireq_t *scsireq) 1010 { 1011 char *d; 1012 if (f == 0) 1013 return 0; 1014 1015 fprintf(f, "SCIOCCOMMAND ioctl"); 1016 1017 if (ret == 0) 1018 fprintf(f, ": Command accepted."); 1019 else { 1020 if (ret != -1) 1021 fprintf(f, ", return value %d?", ret); 1022 1023 if (errno) { 1024 fprintf(f, ": %s", strerror(errno)); 1025 errno = 0; 1026 } 1027 } 1028 1029 fputc('\n', f); 1030 1031 if (ret == 0 && (scsireq->status || scsireq->retsts || behave.db_level)) 1032 { 1033 scsi_retsts_dump(f, scsireq); 1034 1035 if (scsireq->status) 1036 fprintf(f, " host adapter status %d\n", scsireq->status); 1037 1038 if (scsireq->flags & SCCMD_READ) 1039 d = "Data in"; 1040 else if (scsireq->flags & SCCMD_WRITE) 1041 d = "Data out"; 1042 else 1043 d = "No data transfer?"; 1044 1045 if (scsireq->cmdlen == 0) 1046 fprintf(f, "Zero length command????\n"); 1047 1048 scsi_dump(f, "Command out", 1049 (u_char *)scsireq->cmd, scsireq->cmdlen, scsireq->cmdlen, 0); 1050 scsi_dump(f, d, 1051 (u_char *)scsireq->databuf, scsireq->datalen, 1052 scsireq->datalen_used, 1); 1053 scsi_sense_dump(f, scsireq); 1054 } 1055 1056 fflush(f); 1057 1058 return ret; 1059 } 1060 1061 static char *debug_output; 1062 1063 int 1064 scsi_open(const char *path, int flags) 1065 { 1066 int fd = open(path, flags); 1067 1068 if (fd != -1) { 1069 char *p; 1070 debug_output = getenv("SU_DEBUG_OUTPUT"); 1071 (void)scsi_debug_output(debug_output); 1072 1073 if ((p = getenv("SU_DEBUG_LEVEL"))) 1074 sscanf(p, "%d", &behave.db_level); 1075 1076 if ((p = getenv("SU_DEBUG_TRUNCATE"))) 1077 sscanf(p, "%d", &behave.db_trunc); 1078 else 1079 behave.db_trunc = SCSI_TRUNCATE; 1080 } 1081 1082 return fd; 1083 } 1084 1085 int 1086 scsireq_enter(int fid, scsireq_t *scsireq) 1087 { 1088 int ret; 1089 1090 ret = ioctl(fid, SCIOCCOMMAND, (void *)scsireq); 1091 1092 if (behave.db_f) 1093 scsi_debug(behave.db_f, ret, scsireq); 1094 1095 return ret; 1096 } 1097