| 531 | static int sg_io_cmnd_v4(int dev_fd, struct scsi_cmnd_io * iop, int report, |
| 532 | int unknown) |
| 533 | { |
| 534 | #ifndef SG_IO |
| 535 | ARGUSED(dev_fd); ARGUSED(iop); ARGUSED(report); |
| 536 | return -ENOTTY; |
| 537 | #else |
| 538 | struct sg_io_v4 io_hdr; |
| 539 | |
| 540 | if (report > 0) { |
| 541 | int k, j; |
| 542 | const unsigned char * ucp = iop->cmnd; |
| 543 | const char * np; |
| 544 | char buff[256]; |
| 545 | const int sz = (int)sizeof(buff); |
| 546 | |
| 547 | np = scsi_get_opcode_name(ucp[0]); |
| 548 | j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>"); |
| 549 | for (k = 0; k < (int)iop->cmnd_len; ++k) |
| 550 | j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]); |
| 551 | if ((report > 1) && |
| 552 | (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { |
| 553 | int trunc = (iop->dxfer_len > 256) ? 1 : 0; |
| 554 | |
| 555 | snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n Outgoing " |
| 556 | "data, len=%d%s:\n", (int)iop->dxfer_len, |
| 557 | (trunc ? " [only first 256 bytes shown]" : "")); |
| 558 | dStrHex((const char *)iop->dxferp, |
| 559 | (trunc ? 256 : iop->dxfer_len) , 1); |
| 560 | } |
| 561 | else |
| 562 | snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); |
| 563 | pout("%s", buff); |
| 564 | } |
| 565 | memset(&io_hdr, 0, sizeof(struct sg_io_v4)); |
| 566 | io_hdr.guard = 'Q'; |
| 567 | io_hdr.request_len = iop->cmnd_len; |
| 568 | io_hdr.request = __u64(iop->cmnd); |
| 569 | io_hdr.max_response_len = iop->max_sense_len; |
| 570 | io_hdr.response = __u64(iop->sensep); |
| 571 | /* sg_io_hdr interface timeout has millisecond units. Timeout of 0 |
| 572 | defaults to 60 seconds. */ |
| 573 | io_hdr.timeout = ((0 == iop->timeout) ? 60 : iop->timeout) * 1000; |
| 574 | switch (iop->dxfer_dir) { |
| 575 | case DXFER_NONE: |
| 576 | break; |
| 577 | case DXFER_FROM_DEVICE: |
| 578 | io_hdr.din_xfer_len = iop->dxfer_len; |
| 579 | io_hdr.din_xferp = __u64(iop->dxferp); |
| 580 | break; |
| 581 | case DXFER_TO_DEVICE: |
| 582 | io_hdr.dout_xfer_len = iop->dxfer_len; |
| 583 | io_hdr.dout_xferp = __u64(iop->dxferp); |
| 584 | break; |
| 585 | default: |
| 586 | pout("do_scsi_cmnd_v4: bad dxfer_dir\n"); |
| 587 | return -EINVAL; |
| 588 | } |
| 589 | iop->resp_sense_len = 0; |
| 590 | iop->scsi_status = 0; |
| 591 | iop->resid = 0; |
| 592 | if (ioctl(dev_fd, SG_IO, &io_hdr) < 0) { |
| 593 | if (report && (! unknown)) |
| 594 | pout(" SG_IO ioctl failed, errno=%d [%s]\n", errno, |
| 595 | strerror(errno)); |
| 596 | return -errno; |
| 597 | } |
| 598 | switch (iop->dxfer_dir) { |
| 599 | case DXFER_NONE: |
| 600 | iop->resid = 0; |
| 601 | break; |
| 602 | case DXFER_FROM_DEVICE: |
| 603 | iop->resid = io_hdr.din_resid; |
| 604 | break; |
| 605 | case DXFER_TO_DEVICE: |
| 606 | iop->resid = io_hdr.dout_resid; |
| 607 | break; |
| 608 | } |
| 609 | iop->scsi_status = io_hdr.device_status; |
| 610 | if (report > 0) { |
| 611 | pout(" scsi_status=0x%x, host_status=0x%x, driver_status=0x%x\n" |
| 612 | " info=0x%x duration=%d milliseconds resid=%d\n", io_hdr.device_status, |
| 613 | io_hdr.transport_status, io_hdr.driver_status, io_hdr.info, |
| 614 | io_hdr.duration, iop->resid); |
| 615 | if (report > 1) { |
| 616 | if (DXFER_FROM_DEVICE == iop->dxfer_dir) { |
| 617 | int trunc, len; |
| 618 | |
| 619 | len = iop->dxfer_len - iop->resid; |
| 620 | trunc = (len > 256) ? 1 : 0; |
| 621 | if (len > 0) { |
| 622 | pout(" Incoming data, len=%d%s:\n", len, |
| 623 | (trunc ? " [only first 256 bytes shown]" : "")); |
| 624 | dStrHex((const char*)iop->dxferp, (trunc ? 256 : len), |
| 625 | 1); |
| 626 | } else |
| 627 | pout(" Incoming data trimmed to nothing by resid\n"); |
| 628 | } |
| 629 | } |
| 630 | } |
| 631 | |
| 632 | if (io_hdr.info & SG_INFO_CHECK) { /* error or warning */ |
| 633 | int masked_driver_status = (LSCSI_DRIVER_MASK & io_hdr.driver_status); |
| 634 | |
| 635 | if (0 != io_hdr.transport_status) { |
| 636 | if ((LSCSI_DID_NO_CONNECT == io_hdr.transport_status) || |
| 637 | (LSCSI_DID_BUS_BUSY == io_hdr.transport_status) || |
| 638 | (LSCSI_DID_TIME_OUT == io_hdr.transport_status)) |
| 639 | return -ETIMEDOUT; |
| 640 | else |
| 641 | /* Check for DID_ERROR - workaround for aacraid driver quirk */ |
| 642 | if (LSCSI_DID_ERROR != io_hdr.transport_status) { |
| 643 | return -EIO; /* catch all if not DID_ERR */ |
| 644 | } |
| 645 | } |
| 646 | if (0 != masked_driver_status) { |
| 647 | if (LSCSI_DRIVER_TIMEOUT == masked_driver_status) |
| 648 | return -ETIMEDOUT; |
| 649 | else if (LSCSI_DRIVER_SENSE != masked_driver_status) |
| 650 | return -EIO; |
| 651 | } |
| 652 | if (LSCSI_DRIVER_SENSE == masked_driver_status) |
| 653 | iop->scsi_status = SCSI_STATUS_CHECK_CONDITION; |
| 654 | iop->resp_sense_len = io_hdr.response_len; |
| 655 | if ((SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) && |
| 656 | iop->sensep && (iop->resp_sense_len > 0)) { |
| 657 | if (report > 1) { |
| 658 | pout(" >>> Sense buffer, len=%d:\n", |
| 659 | (int)iop->resp_sense_len); |
| 660 | dStrHex((const char *)iop->sensep, iop->resp_sense_len , 1); |
| 661 | } |
| 662 | } |
| 663 | if (report) { |
| 664 | if (SCSI_STATUS_CHECK_CONDITION == iop->scsi_status && iop->sensep) { |
| 665 | if ((iop->sensep[0] & 0x7f) > 0x71) |
| 666 | pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n", |
| 667 | iop->scsi_status, iop->sensep[1] & 0xf, |
| 668 | iop->sensep[2], iop->sensep[3]); |
| 669 | else |
| 670 | pout(" status=%x: sense_key=%x asc=%x ascq=%x\n", |
| 671 | iop->scsi_status, iop->sensep[2] & 0xf, |
| 672 | iop->sensep[12], iop->sensep[13]); |
| 673 | } |
| 674 | else |
| 675 | pout(" status=0x%x\n", iop->scsi_status); |
| 676 | } |
| 677 | } |
| 678 | return 0; |
| 679 | #endif |
| 680 | } |
| 681 | |
| 682 | /* Preferred implementation for issuing SCSI commands in linux. This |
| 683 | * function uses the SG_IO ioctl. Return 0 if command issued successfully |
| 684 | * (various status values should still be checked). If the SCSI command |
| 685 | * cannot be issued then a negative errno value is returned. */ |