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