| /* |
| * Copyright (c) 1999-2005 Douglas Gilbert. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * |
| */ |
| |
| /* |
| * CONTENTS |
| * Some SCSI commands are executed in many contexts and hence began |
| * to appear in several sg3_utils utilities. This files centralizes |
| * some of the low level command execution code. In most cases the |
| * interpretation of the command response is left to the each |
| * utility. |
| * One example is the SCSI INQUIRY command which is often required |
| * to identify and categorize (e.g. disk, tape or enclosure device) |
| * a SCSI target device. |
| * CHANGELOG |
| * v1.00 (20041018) |
| * fetch low level command execution code from other utilities |
| * v1.01 (20041026) |
| * fix "ll" read capacity calls, add sg_ll_report_luns |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <sys/ioctl.h> |
| #include "sg_include.h" |
| #include "sg_lib.h" |
| #include "sg_cmds.h" |
| |
| static char * version_str = "1.09 20050419"; |
| |
| |
| #define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */ |
| #define DEF_TIMEOUT 60000 /* 60,000 millisecs == 60 seconds */ |
| #define LONG_TIMEOUT 7200000 /* 7,200,000 millisecs == 120 minutes */ |
| #define EBUFF_SZ 256 |
| |
| #define INQUIRY_CMD 0x12 |
| #define INQUIRY_CMDLEN 6 |
| #define SYNCHRONIZE_CACHE_CMD 0x35 |
| #define SYNCHRONIZE_CACHE_CMDLEN 10 |
| #define SERVICE_ACTION_IN_16_CMD 0x9e |
| #define SERVICE_ACTION_IN_16_CMDLEN 16 |
| #define READ_CAPACITY_16_SA 0x10 |
| #define READ_CAPACITY_10_CMD 0x25 |
| #define READ_CAPACITY_10_CMDLEN 10 |
| #define MODE_SENSE6_CMD 0x1a |
| #define MODE_SENSE6_CMDLEN 6 |
| #define MODE_SENSE10_CMD 0x5a |
| #define MODE_SENSE10_CMDLEN 10 |
| #define MODE_SELECT6_CMD 0x15 |
| #define MODE_SELECT6_CMDLEN 6 |
| #define MODE_SELECT10_CMD 0x55 |
| #define MODE_SELECT10_CMDLEN 10 |
| #define REQUEST_SENSE_CMD 0x3 |
| #define REQUEST_SENSE_CMDLEN 6 |
| #define REPORT_LUNS_CMD 0xa0 |
| #define REPORT_LUNS_CMDLEN 12 |
| #define MAINTENANCE_IN_CMD 0xa3 |
| #define MAINTENANCE_IN_CMDLEN 12 |
| #define REPORT_TGT_PRT_GRP_SA 0x0a |
| #define LOG_SENSE_CMD 0x4d |
| #define LOG_SENSE_CMDLEN 10 |
| #define LOG_SELECT_CMD 0x4c |
| #define LOG_SELECT_CMDLEN 10 |
| #define TUR_CMD 0x0 |
| #define TUR_CMDLEN 6 |
| #define SEND_DIAGNOSTIC_CMD 0x1d |
| #define SEND_DIAGNOSTIC_CMDLEN 6 |
| #define RECEIVE_DIAGNOSTICS_CMD 0x1c |
| #define RECEIVE_DIAGNOSTICS_CMDLEN 6 |
| #define READ_DEFECT10_CMD 0x37 |
| #define READ_DEFECT10_CMDLEN 10 |
| #define SERVICE_ACTION_IN_12_CMD 0xab |
| #define SERVICE_ACTION_IN_12_CMDLEN 12 |
| #define READ_MEDIA_SERIAL_NUM_SA 0x1 |
| |
| #define MODE6_RESP_HDR_LEN 4 |
| #define MODE10_RESP_HDR_LEN 8 |
| #define MODE_RESP_ARB_LEN 1024 |
| |
| |
| const char * sg_cmds_version() |
| { |
| return version_str; |
| } |
| |
| /* Invokes a SCSI INQUIRY command and yields the response */ |
| /* Returns 0 when successful, -1 -> SG_IO ioctl failed, -2 -> bad response */ |
| int sg_ll_inquiry(int sg_fd, int cmddt, int evpd, int pg_op, |
| void * resp, int mx_resp_len, int noisy, int verbose) |
| { |
| int res, k; |
| unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0}; |
| unsigned char sense_b[SENSE_BUFF_LEN]; |
| struct sg_io_hdr io_hdr; |
| |
| if (cmddt) |
| inqCmdBlk[1] |= 2; |
| if (evpd) |
| inqCmdBlk[1] |= 1; |
| inqCmdBlk[2] = (unsigned char)pg_op; |
| /* 16 bit allocation length (was 8) is a recent SPC-3 addition */ |
| inqCmdBlk[3] = (unsigned char)((mx_resp_len >> 8) & 0xff); |
| inqCmdBlk[4] = (unsigned char)(mx_resp_len & 0xff); |
| if (NULL == sg_warnings_str) |
| sg_warnings_str = stderr; |
| if (verbose) { |
| fprintf(sg_warnings_str, " inquiry cdb: "); |
| for (k = 0; k < INQUIRY_CMDLEN; ++k) |
| fprintf(sg_warnings_str, "%02x ", inqCmdBlk[k]); |
| fprintf(sg_warnings_str, "\n"); |
| } |
| memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); |
| memset(sense_b, 0, sizeof(sense_b)); |
| io_hdr.interface_id = 'S'; |
| io_hdr.cmd_len = sizeof(inqCmdBlk); |
| io_hdr.mx_sb_len = sizeof(sense_b); |
| io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; |
| io_hdr.dxfer_len = mx_resp_len; |
| io_hdr.dxferp = resp; |
| io_hdr.cmdp = inqCmdBlk; |
| io_hdr.sbp = sense_b; |
| io_hdr.timeout = DEF_TIMEOUT; |
| |
| if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { |
| if (noisy || verbose) |
| fprintf(sg_warnings_str, "SG_IO (inquiry) error: %s\n", |
| safe_strerror(errno)); |
| return -1; |
| } |
| if (verbose > 2) |
| fprintf(sg_warnings_str, " duration=%u ms\n", io_hdr.duration); |
| res = sg_err_category3(&io_hdr); |
| switch (res) { |
| case SG_LIB_CAT_RECOVERED: |
| if (noisy || verbose) |
| sg_chk_n_print3("Inquiry", &io_hdr); |
| /* fall through */ |
| case SG_LIB_CAT_CLEAN: |
| if (verbose && io_hdr.resid) |
| fprintf(sg_warnings_str, " inquiry: resid=%d\n", io_hdr.resid); |
| return 0; |
| default: |
| if (noisy || verbose) { |
| char ebuff[EBUFF_SZ]; |
| |
| if (evpd) |
| snprintf(ebuff, EBUFF_SZ, "Inquiry error, VPD page=0x%x", |
| pg_op); |
| else if (cmddt) |
| snprintf(ebuff, EBUFF_SZ, "Inquiry error, CmdDt opcode=0x%x", |
| pg_op); |
| else |
| snprintf(ebuff, EBUFF_SZ, "Inquiry error, [standard]"); |
| sg_chk_n_print3(ebuff, &io_hdr); |
| } |
| return -2; |
| } |
| } |
| |
| /* Yields most of first 36 bytes of a standard INQUIRY (evpd==0) response. */ |
| /* Returns 0 when successful, -1 -> SG_IO ioctl failed, -2 -> bad response */ |
| int sg_simple_inquiry(int sg_fd, struct sg_simple_inquiry_resp * inq_data, |
| int noisy, int verbose) |
| { |
| int res, k; |
| unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0}; |
| unsigned char sense_b[SENSE_BUFF_LEN]; |
| struct sg_io_hdr io_hdr; |
| unsigned char inq_resp[36]; |
| |
| if (inq_data) { |
| memset(inq_data, 0, sizeof(* inq_data)); |
| inq_data->peripheral_qualifier = 0x3; |
| inq_data->peripheral_type = 0x1f; |
| } |
| inqCmdBlk[4] = (unsigned char)sizeof(inq_resp); |
| if (NULL == sg_warnings_str) |
| sg_warnings_str = stderr; |
| if (verbose) { |
| fprintf(sg_warnings_str, " inquiry cdb: "); |
| for (k = 0; k < INQUIRY_CMDLEN; ++k) |
| fprintf(sg_warnings_str, "%02x ", inqCmdBlk[k]); |
| fprintf(sg_warnings_str, "\n"); |
| } |
| memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); |
| memset(sense_b, 0, sizeof(sense_b)); |
| io_hdr.interface_id = 'S'; |
| io_hdr.cmd_len = sizeof(inqCmdBlk); |
| io_hdr.mx_sb_len = sizeof(sense_b); |
| io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; |
| io_hdr.dxfer_len = sizeof(inq_resp); |
| io_hdr.dxferp = inq_resp; |
| io_hdr.cmdp = inqCmdBlk; |
| io_hdr.sbp = sense_b; |
| io_hdr.timeout = DEF_TIMEOUT; |
| |
| if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { |
| if (noisy || verbose) |
| fprintf(sg_warnings_str, "SG_IO (inquiry) error: %s\n", |
| safe_strerror(errno)); |
| return -1; |
| } |
| if (verbose > 2) |
| fprintf(sg_warnings_str, " duration=%u ms\n", io_hdr.duration); |
| res = sg_err_category3(&io_hdr); |
| switch (res) { |
| case SG_LIB_CAT_RECOVERED: |
| if (noisy || verbose) |
| sg_chk_n_print3("Inquiry", &io_hdr); |
| /* fall through */ |
| case SG_LIB_CAT_CLEAN: |
| if (inq_data) { |
| inq_data->peripheral_qualifier = (inq_resp[0] >> 5) & 0x7; |
| inq_data->peripheral_type = inq_resp[0] & 0x1f; |
| inq_data->rmb = (inq_resp[1] & 0x80) ? 1 : 0; |
| inq_data->version = inq_resp[2]; |
| inq_data->byte_3 = inq_resp[3]; |
| inq_data->byte_5 = inq_resp[5]; |
| inq_data->byte_6 = inq_resp[6]; |
| inq_data->byte_7 = inq_resp[7]; |
| memcpy(inq_data->vendor, inq_resp + 8, 8); |
| memcpy(inq_data->product, inq_resp + 16, 16); |
| memcpy(inq_data->revision, inq_resp + 32, 4); |
| } |
| if (verbose && io_hdr.resid) |
| fprintf(sg_warnings_str, " inquiry: resid=%d\n", io_hdr.resid); |
| return 0; |
| default: |
| if (noisy) { |
| char ebuff[EBUFF_SZ]; |
| |
| snprintf(ebuff, EBUFF_SZ, "Inquiry error "); |
| sg_chk_n_print3(ebuff, &io_hdr); |
| } |
| return -2; |
| } |
| } |
| |
| /* Invokes a SCSI TEST UNIT READY command. |
| * 'pack_id' is just for diagnostics, safe to set to 0. |
| * Return of 0 -> success, -1 -> failure */ |
| int sg_ll_test_unit_ready(int sg_fd, int pack_id, int noisy, int verbose) |
| { |
| int res, k; |
| unsigned char turCmbBlk[TUR_CMDLEN] = {TUR_CMD, 0, 0, 0, 0, 0}; |
| unsigned char sense_b[SENSE_BUFF_LEN]; |
| struct sg_io_hdr io_hdr; |
| |
| if (NULL == sg_warnings_str) |
| sg_warnings_str = stderr; |
| if (verbose) { |
| fprintf(sg_warnings_str, " test unit ready cdb: "); |
| for (k = 0; k < TUR_CMDLEN; ++k) |
| fprintf(sg_warnings_str, "%02x ", turCmbBlk[k]); |
| fprintf(sg_warnings_str, "\n"); |
| } |
| memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); |
| memset(sense_b, 0, sizeof(sense_b)); |
| io_hdr.interface_id = 'S'; |
| io_hdr.cmd_len = sizeof(turCmbBlk); |
| io_hdr.mx_sb_len = sizeof(sense_b); |
| io_hdr.dxfer_direction = SG_DXFER_NONE; |
| io_hdr.dxfer_len = 0; |
| io_hdr.dxferp = NULL; |
| io_hdr.cmdp = turCmbBlk; |
| io_hdr.sbp = sense_b; |
| io_hdr.timeout = DEF_TIMEOUT; |
| io_hdr.pack_id = pack_id; /* diagnostic: safe to set to 0 */ |
| |
| if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { |
| if (noisy || verbose) |
| fprintf(sg_warnings_str, "test_unit_ready (SG_IO) error: %s\n", |
| safe_strerror(errno)); |
| return -1; |
| } |
| if (verbose > 2) |
| fprintf(sg_warnings_str, " duration=%u ms\n", io_hdr.duration); |
| res = sg_err_category3(&io_hdr); |
| switch (res) { |
| case SG_LIB_CAT_CLEAN: |
| return 0; |
| default: |
| if (noisy || verbose) |
| sg_chk_n_print3("test unit ready", &io_hdr); |
| return -1; |
| } |
| } |
| |
| /* Invokes a SCSI SYNCHRONIZE CACHE (10) command. Return of 0 -> success, |
| * -1 -> failure, SG_LIB_CAT_MEDIA_CHANGED -> repeat, SG_LIB_CAT_INVALID_OP |
| * -> cdb not supported, SG_LIB_CAT_IlLEGAL_REQ -> bad field in cdb */ |
| int sg_ll_sync_cache_10(int sg_fd, int sync_nv, int immed, int group, |
| unsigned int lba, unsigned int count, int noisy, |
| int verbose) |
| { |
| int res, k; |
| unsigned char scCmdBlk[SYNCHRONIZE_CACHE_CMDLEN] = |
| {SYNCHRONIZE_CACHE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
| unsigned char sense_b[SENSE_BUFF_LEN]; |
| struct sg_io_hdr io_hdr; |
| |
| if (sync_nv) |
| scCmdBlk[1] |= 4; |
| if (immed) |
| scCmdBlk[1] |= 2; |
| scCmdBlk[2] = (lba >> 24) & 0xff; |
| scCmdBlk[3] = (lba >> 16) & 0xff; |
| scCmdBlk[4] = (lba >> 8) & 0xff; |
| scCmdBlk[5] = lba & 0xff; |
| scCmdBlk[6] = group & 0x1f; |
| if (NULL == sg_warnings_str) |
| sg_warnings_str = stderr; |
| if (count > 0xffff) { |
| fprintf(sg_warnings_str, "count too big\n"); |
| return -1; |
| } |
| scCmdBlk[7] = (count >> 8) & 0xff; |
| scCmdBlk[8] = count & 0xff; |
| |
| if (verbose) { |
| fprintf(sg_warnings_str, " synchronize cache(10) cdb: "); |
| for (k = 0; k < SYNCHRONIZE_CACHE_CMDLEN; ++k) |
| fprintf(sg_warnings_str, "%02x ", scCmdBlk[k]); |
| fprintf(sg_warnings_str, "\n"); |
| } |
| memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); |
| memset(sense_b, 0, sizeof(sense_b)); |
| io_hdr.interface_id = 'S'; |
| io_hdr.cmd_len = sizeof(scCmdBlk); |
| io_hdr.mx_sb_len = sizeof(sense_b); |
| io_hdr.dxfer_direction = SG_DXFER_NONE; |
| io_hdr.dxfer_len = 0; |
| io_hdr.dxferp = NULL; |
| io_hdr.cmdp = scCmdBlk; |
| io_hdr.sbp = sense_b; |
| io_hdr.timeout = DEF_TIMEOUT; |
| |
| if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { |
| if (noisy || verbose) |
| fprintf(sg_warnings_str, "synchronize_cache (SG_IO) error: %s\n", |
| safe_strerror(errno)); |
| return -1; |
| } |
| if (verbose > 2) |
| fprintf(sg_warnings_str, " duration=%u ms\n", io_hdr.duration); |
| res = sg_err_category3(&io_hdr); |
| switch (res) { |
| case SG_LIB_CAT_CLEAN: |
| return 0; |
| case SG_LIB_CAT_MEDIA_CHANGED: |
| case SG_LIB_CAT_INVALID_OP: |
| case SG_LIB_CAT_ILLEGAL_REQ: |
| if (verbose > 1) |
| sg_chk_n_print3("synchronize cache", &io_hdr); |
| return res; |
| default: |
| if (noisy || verbose) |
| sg_chk_n_print3("synchronize cache", &io_hdr); |
| return -1; |
| } |
| return 0; |
| } |
| |
| |
| /* Invokes a SCSI READ CAPACITY (16) command. Returns 0 -> success, |
| * -1 -> failure, SG_LIB_CAT_MEDIA_CHANGED -> repeat, SG_LIB_CAT_INVALID_OP |
| * -> cdb not supported, SG_LIB_CAT_IlLEGAL_REQ -> bad field in cdb */ |
| int sg_ll_readcap_16(int sg_fd, int pmi, unsigned long long llba, |
| void * resp, int mx_resp_len, int verbose) |
| { |
| int k, res; |
| unsigned char rcCmdBlk[SERVICE_ACTION_IN_16_CMDLEN] = |
| {SERVICE_ACTION_IN_16_CMD, READ_CAPACITY_16_SA, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
| unsigned char sense_b[SENSE_BUFF_LEN]; |
| struct sg_io_hdr io_hdr; |
| |
| if (pmi) { /* lbs only valid when pmi set */ |
| rcCmdBlk[14] |= 1; |
| rcCmdBlk[2] = (llba >> 56) & 0xff; |
| rcCmdBlk[3] = (llba >> 48) & 0xff; |
| rcCmdBlk[4] = (llba >> 40) & 0xff; |
| rcCmdBlk[5] = (llba >> 32) & 0xff; |
| rcCmdBlk[6] = (llba >> 24) & 0xff; |
| rcCmdBlk[7] = (llba >> 16) & 0xff; |
| rcCmdBlk[8] = (llba >> 8) & 0xff; |
| rcCmdBlk[9] = llba & 0xff; |
| } |
| /* Allocation length, no guidance in SBC-2 rev 15b */ |
| rcCmdBlk[10] = (mx_resp_len >> 24) & 0xff; |
| rcCmdBlk[11] = (mx_resp_len >> 16) & 0xff; |
| rcCmdBlk[12] = (mx_resp_len >> 8) & 0xff; |
| rcCmdBlk[13] = mx_resp_len & 0xff; |
| if (NULL == sg_warnings_str) |
| sg_warnings_str = stderr; |
| if (verbose) { |
| fprintf(sg_warnings_str, " read capacity (16) cdb: "); |
| for (k = 0; k < SERVICE_ACTION_IN_16_CMDLEN; ++k) |
| fprintf(sg_warnings_str, "%02x ", rcCmdBlk[k]); |
| fprintf(sg_warnings_str, "\n"); |
| } |
| memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); |
| memset(sense_b, 0, sizeof(sense_b)); |
| io_hdr.interface_id = 'S'; |
| io_hdr.cmd_len = sizeof(rcCmdBlk); |
| io_hdr.mx_sb_len = sizeof(sense_b); |
| io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; |
| io_hdr.dxfer_len = mx_resp_len; |
| io_hdr.dxferp = resp; |
| io_hdr.cmdp = rcCmdBlk; |
| io_hdr.sbp = sense_b; |
| io_hdr.timeout = DEF_TIMEOUT; |
| |
| if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { |
| if (verbose) |
| fprintf(sg_warnings_str, "read_capacity16 (SG_IO) error: %s\n", |
| safe_strerror(errno)); |
| return -1; |
| } |
| if (verbose > 2) |
| fprintf(sg_warnings_str, " duration=%u ms\n", io_hdr.duration); |
| res = sg_err_category3(&io_hdr); |
| switch (res) { |
| case SG_LIB_CAT_RECOVERED: |
| if (verbose) |
| sg_chk_n_print3("Read capacity (16)", &io_hdr); |
| /* fall through */ |
| case SG_LIB_CAT_CLEAN: |
| if (verbose && io_hdr.resid) |
| fprintf(sg_warnings_str, " read_capacity16: resid=%d\n", |
| io_hdr.resid); |
| return 0; |
| case SG_LIB_CAT_INVALID_OP: |
| case SG_LIB_CAT_ILLEGAL_REQ: |
| case SG_LIB_CAT_MEDIA_CHANGED: |
| if (verbose > 1) |
| sg_chk_n_print3("READ CAPACITY 16 command error", &io_hdr); |
| return res; |
| default: |
| sg_chk_n_print3("READ CAPACITY 16 command error", &io_hdr); |
| return -1; |
| } |
| } |
| |
| /* Invokes a SCSI READ CAPACITY (10) command. Return of 0 -> success, |
| * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_MEDIA_CHANGED |
| * -> media changed, SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, |
| * -1 -> other failure */ |
| int sg_ll_readcap_10(int sg_fd, int pmi, unsigned int lba, |
| void * resp, int mx_resp_len, int verbose) |
| { |
| int k, res; |
| unsigned char rcCmdBlk[READ_CAPACITY_10_CMDLEN] = |
| {READ_CAPACITY_10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
| unsigned char sense_b[SENSE_BUFF_LEN]; |
| struct sg_io_hdr io_hdr; |
| |
| if (pmi) { /* lbs only valid when pmi set */ |
| rcCmdBlk[8] |= 1; |
| rcCmdBlk[2] = (lba >> 24) & 0xff; |
| rcCmdBlk[3] = (lba >> 16) & 0xff; |
| rcCmdBlk[4] = (lba >> 8) & 0xff; |
| rcCmdBlk[5] = lba & 0xff; |
| } |
| if (NULL == sg_warnings_str) |
| sg_warnings_str = stderr; |
| if (verbose) { |
| fprintf(sg_warnings_str, " read capacity (10) cdb: "); |
| for (k = 0; k < READ_CAPACITY_10_CMDLEN; ++k) |
| fprintf(sg_warnings_str, "%02x ", rcCmdBlk[k]); |
| fprintf(sg_warnings_str, "\n"); |
| } |
| memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); |
| memset(sense_b, 0, sizeof(sense_b)); |
| io_hdr.interface_id = 'S'; |
| io_hdr.cmd_len = sizeof(rcCmdBlk); |
| io_hdr.mx_sb_len = sizeof(sense_b); |
| io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; |
| io_hdr.dxfer_len = mx_resp_len; /* should be 8 */ |
| io_hdr.dxferp = resp; |
| io_hdr.cmdp = rcCmdBlk; |
| io_hdr.sbp = sense_b; |
| io_hdr.timeout = DEF_TIMEOUT; |
| |
| if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { |
| if (verbose) |
| fprintf(sg_warnings_str, "read_capacity (10) (SG_IO) error: %s\n", |
| safe_strerror(errno)); |
| return -1; |
| } |
| if (verbose > 2) |
| fprintf(sg_warnings_str, " duration=%u ms\n", io_hdr.duration); |
| res = sg_err_category3(&io_hdr); |
| switch (res) { |
| case SG_LIB_CAT_RECOVERED: |
| if (verbose) |
| sg_chk_n_print3("Read capacity (10)", &io_hdr); |
| /* fall through */ |
| case SG_LIB_CAT_CLEAN: |
| if (verbose && io_hdr.resid) |
| fprintf(sg_warnings_str, " read_capacity10: resid=%d\n", |
| io_hdr.resid); |
| return 0; |
| case SG_LIB_CAT_INVALID_OP: |
| case SG_LIB_CAT_ILLEGAL_REQ: |
| case SG_LIB_CAT_MEDIA_CHANGED: |
| if (verbose > 1) |
| sg_chk_n_print3("READ CAPACITY 10 command error", &io_hdr); |
| return res; |
| default: |
| sg_chk_n_print3("READ CAPACITY 10 command error", &io_hdr); |
| return -1; |
| } |
| } |
| |
| /* Invokes a SCSI MODE SENSE (6) command. Return of 0 -> success, |
| * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ -> |
| * bad field in cdb, -1 -> other failure */ |
| int sg_ll_mode_sense6(int sg_fd, int dbd, int pc, int pg_code, int sub_pg_code, |
| void * resp, int mx_resp_len, int noisy, int verbose) |
| { |
| int res, k; |
| unsigned char modesCmdBlk[MODE_SENSE6_CMDLEN] = |
| {MODE_SENSE6_CMD, 0, 0, 0, 0, 0}; |
| unsigned char sense_b[SENSE_BUFF_LEN]; |
| struct sg_io_hdr io_hdr; |
| |
| modesCmdBlk[1] = (unsigned char)(dbd ? 0x8 : 0); |
| modesCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); |
| modesCmdBlk[3] = (unsigned char)(sub_pg_code & 0xff); |
| modesCmdBlk[4] = (unsigned char)(mx_resp_len & 0xff); |
| if (NULL == sg_warnings_str) |
| sg_warnings_str = stderr; |
| if (mx_resp_len > 0xff) { |
| fprintf(sg_warnings_str, "mx_resp_len too big\n"); |
| return -1; |
| } |
| if (verbose) { |
| fprintf(sg_warnings_str, " mode sense (6) cdb: "); |
| for (k = 0; k < MODE_SENSE6_CMDLEN; ++k) |
| fprintf(sg_warnings_str, "%02x ", modesCmdBlk[k]); |
| fprintf(sg_warnings_str, "\n"); |
| } |
| |
| memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); |
| memset(sense_b, 0, sizeof(sense_b)); |
| io_hdr.interface_id = 'S'; |
| io_hdr.cmd_len = MODE_SENSE6_CMDLEN; |
| io_hdr.mx_sb_len = sizeof(sense_b); |
| io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; |
| io_hdr.dxfer_len = mx_resp_len; |
| io_hdr.dxferp = resp; |
| io_hdr.cmdp = modesCmdBlk; |
| io_hdr.sbp = sense_b; |
| io_hdr.timeout = DEF_TIMEOUT; |
| |
| if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { |
| if (noisy || verbose) |
| fprintf(sg_warnings_str, "mode sense (6) SG_IO error: %s\n", |
| safe_strerror(errno)); |
| return -1; |
| } |
| if (verbose > 2) |
| fprintf(sg_warnings_str, " duration=%u ms\n", io_hdr.duration); |
| res = sg_err_category3(&io_hdr); |
| switch (res) { |
| case SG_LIB_CAT_RECOVERED: |
| if (noisy || verbose) |
| sg_chk_n_print3("Mode sense (6)", &io_hdr); |
| /* fall through */ |
| case SG_LIB_CAT_CLEAN: |
| if (verbose && io_hdr.resid) |
| fprintf(sg_warnings_str, " mode sense (6): resid=%d\n", |
| io_hdr.resid); |
| if (verbose > 2) { |
| k = mx_resp_len - io_hdr.resid; |
| if (k > 0) { |
| fprintf(sg_warnings_str, " mode sense (6): response%s\n", |
| (k > 256 ? ", first 256 bytes" : "")); |
| dStrHex(resp, (k > 256 ? 256 : k), -1); |
| } |
| } |
| return 0; |
| case SG_LIB_CAT_INVALID_OP: |
| case SG_LIB_CAT_ILLEGAL_REQ: |
| if (verbose > 1) |
| sg_chk_n_print3("Mode sense (6) error", &io_hdr); |
| return res; |
| default: |
| if (noisy || verbose) { |
| char ebuff[EBUFF_SZ]; |
| |
| snprintf(ebuff, EBUFF_SZ, "Mode sense (6) error, dbd=%d " |
| "pc=%d page_code=%x sub_page_code=%x\n ", dbd, |
| pc, pg_code, sub_pg_code); |
| sg_chk_n_print3(ebuff, &io_hdr); |
| } |
| return -1; |
| } |
| } |
| |
| /* Invokes a SCSI MODE SENSE (10) command. Return of 0 -> success, |
| * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ -> |
| * bad field in cdb, -1 -> other failure */ |
| int sg_ll_mode_sense10(int sg_fd, int llbaa, int dbd, int pc, int pg_code, |
| int sub_pg_code, void * resp, int mx_resp_len, |
| int noisy, int verbose) |
| { |
| int res, k; |
| unsigned char modesCmdBlk[MODE_SENSE10_CMDLEN] = |
| {MODE_SENSE10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
| unsigned char sense_b[SENSE_BUFF_LEN]; |
| struct sg_io_hdr io_hdr; |
| |
| modesCmdBlk[1] = (unsigned char)((dbd ? 0x8 : 0) | (llbaa ? 0x10 : 0)); |
| modesCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); |
| modesCmdBlk[3] = (unsigned char)(sub_pg_code & 0xff); |
| modesCmdBlk[7] = (unsigned char)((mx_resp_len >> 8) & 0xff); |
| modesCmdBlk[8] = (unsigned char)(mx_resp_len & 0xff); |
| if (NULL == sg_warnings_str) |
| sg_warnings_str = stderr; |
| if (mx_resp_len > 0xffff) { |
| fprintf(sg_warnings_str, "mx_resp_len too big\n"); |
| return -1; |
| } |
| if (verbose) { |
| fprintf(sg_warnings_str, " mode sense (10) cdb: "); |
| for (k = 0; k < MODE_SENSE10_CMDLEN; ++k) |
| fprintf(sg_warnings_str, "%02x ", modesCmdBlk[k]); |
| fprintf(sg_warnings_str, "\n"); |
| } |
| |
| memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); |
| memset(sense_b, 0, sizeof(sense_b)); |
| io_hdr.interface_id = 'S'; |
| io_hdr.cmd_len = MODE_SENSE10_CMDLEN; |
| io_hdr.mx_sb_len = sizeof(sense_b); |
| io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; |
| io_hdr.dxfer_len = mx_resp_len; |
| io_hdr.dxferp = resp; |
| io_hdr.cmdp = modesCmdBlk; |
| io_hdr.sbp = sense_b; |
| io_hdr.timeout = DEF_TIMEOUT; |
| |
| if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { |
| if (noisy || verbose) |
| fprintf(sg_warnings_str, "mode sense (10) SG_IO error: %s\n", |
| safe_strerror(errno)); |
| return -1; |
| } |
| if (verbose > 2) |
| fprintf(sg_warnings_str, " duration=%u ms\n", io_hdr.duration); |
| res = sg_err_category3(&io_hdr); |
| switch (res) { |
| case SG_LIB_CAT_RECOVERED: |
| if (noisy || verbose) |
| sg_chk_n_print3("Mode sense (10)", &io_hdr); |
| /* fall through */ |
| case SG_LIB_CAT_CLEAN: |
| if (verbose && io_hdr.resid) |
| fprintf(sg_warnings_str, " mode sense (10): resid=%d\n", |
| io_hdr.resid); |
| if (verbose > 2) { |
| k = mx_resp_len - io_hdr.resid; |
| if (k > 0) { |
| fprintf(sg_warnings_str, " mode sense (10): response%s\n", |
| (k > 256 ? ", first 256 bytes" : "")); |
| dStrHex(resp, (k > 256 ? 256 : k), -1); |
| } |
| } |
| return 0; |
| case SG_LIB_CAT_INVALID_OP: |
| case SG_LIB_CAT_ILLEGAL_REQ: |
| if (verbose > 1) |
| sg_chk_n_print3("Mode sense (10) error", &io_hdr); |
| return res; |
| default: |
| if (noisy || verbose) { |
| char ebuff[EBUFF_SZ]; |
| |
| snprintf(ebuff, EBUFF_SZ, "Mode sense (10) error, dbd=%d " |
| "pc=%d page_code=%x sub_page_code=%x\n ", dbd, |
| pc, pg_code, sub_pg_code); |
| sg_chk_n_print3(ebuff, &io_hdr); |
| } |
| return -1; |
| } |
| } |
| |
| /* Invokes a SCSI MODE SELECT (6) command. Return of 0 -> success, |
| * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ -> |
| * bad field in cdb, -1 -> other failure */ |
| int sg_ll_mode_select6(int sg_fd, int pf, int sp, void * paramp, |
| int param_len, int noisy, int verbose) |
| { |
| int res, k; |
| unsigned char modesCmdBlk[MODE_SELECT6_CMDLEN] = |
| {MODE_SELECT6_CMD, 0, 0, 0, 0, 0}; |
| unsigned char sense_b[SENSE_BUFF_LEN]; |
| struct sg_io_hdr io_hdr; |
| |
| modesCmdBlk[1] = (unsigned char)(((pf << 4) & 0x10) | (sp & 0x1)); |
| modesCmdBlk[4] = (unsigned char)(param_len & 0xff); |
| if (NULL == sg_warnings_str) |
| sg_warnings_str = stderr; |
| if (param_len > 0xff) { |
| fprintf(sg_warnings_str, "mode select (6): param_len too big\n"); |
| return -1; |
| } |
| if (verbose) { |
| fprintf(sg_warnings_str, " mode select (6) cdb: "); |
| for (k = 0; k < MODE_SELECT6_CMDLEN; ++k) |
| fprintf(sg_warnings_str, "%02x ", modesCmdBlk[k]); |
| fprintf(sg_warnings_str, "\n"); |
| } |
| if (verbose > 1) { |
| fprintf(sg_warnings_str, " mode select (6) parameter block\n"); |
| dStrHex((const char *)paramp, param_len, -1); |
| } |
| |
| memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); |
| memset(sense_b, 0, sizeof(sense_b)); |
| io_hdr.interface_id = 'S'; |
| io_hdr.cmd_len = MODE_SELECT6_CMDLEN; |
| io_hdr.mx_sb_len = sizeof(sense_b); |
| io_hdr.dxfer_direction = SG_DXFER_TO_DEV; |
| io_hdr.dxfer_len = param_len; |
| io_hdr.dxferp = paramp; |
| io_hdr.cmdp = modesCmdBlk; |
| io_hdr.sbp = sense_b; |
| io_hdr.timeout = DEF_TIMEOUT; |
| |
| if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { |
| if (noisy || verbose) |
| fprintf(sg_warnings_str, "mode select (6) SG_IO error: %s\n", |
| safe_strerror(errno)); |
| return -1; |
| } |
| if (verbose > 2) |
| fprintf(sg_warnings_str, " duration=%u ms\n", io_hdr.duration); |
| res = sg_err_category3(&io_hdr); |
| switch (res) { |
| case SG_LIB_CAT_RECOVERED: |
| if (noisy || verbose) |
| sg_chk_n_print3("Mode select (6)", &io_hdr); |
| /* fall through */ |
| case SG_LIB_CAT_CLEAN: |
| return 0; |
| case SG_LIB_CAT_INVALID_OP: |
| case SG_LIB_CAT_ILLEGAL_REQ: |
| if (verbose > 1) |
| sg_chk_n_print3("Mode select (6) error", &io_hdr); |
| return res; |
| default: |
| if (noisy || verbose) { |
| char ebuff[EBUFF_SZ]; |
| |
| snprintf(ebuff, EBUFF_SZ, "Mode select (6) error, pf=%d " |
| "sp=%d\n ", pf, sp); |
| sg_chk_n_print3(ebuff, &io_hdr); |
| } |
| return -1; |
| } |
| } |
| |
| /* Invokes a SCSI MODE SELECT (10) command. Return of 0 -> success, |
| * SG_LIB_CAT_INVALID_OP -> invalid opcode, SG_LIB_CAT_ILLEGAL_REQ -> |
| * bad field in cdb, -1 -> other failure */ |
| int sg_ll_mode_select10(int sg_fd, int pf, int sp, void * paramp, |
| int param_len, int noisy, int verbose) |
| { |
| int res, k; |
| unsigned char modesCmdBlk[MODE_SELECT10_CMDLEN] = |
| {MODE_SELECT10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
| unsigned char sense_b[SENSE_BUFF_LEN]; |
| struct sg_io_hdr io_hdr; |
| |
| modesCmdBlk[1] = (unsigned char)(((pf << 4) & 0x10) | (sp & 0x1)); |
| modesCmdBlk[7] = (unsigned char)((param_len >> 8) & 0xff); |
| modesCmdBlk[8] = (unsigned char)(param_len & 0xff); |
| if (NULL == sg_warnings_str) |
| sg_warnings_str = stderr; |
| if (param_len > 0xffff) { |
| fprintf(sg_warnings_str, "mode select (10): param_len too big\n"); |
| return -1; |
| } |
| if (verbose) { |
| fprintf(sg_warnings_str, " mode select (10) cdb: "); |
| for (k = 0; k < MODE_SELECT10_CMDLEN; ++k) |
| fprintf(sg_warnings_str, "%02x ", modesCmdBlk[k]); |
| fprintf(sg_warnings_str, "\n"); |
| } |
| if (verbose > 1) { |
| fprintf(sg_warnings_str, " mode select (10) parameter block\n"); |
| dStrHex((const char *)paramp, param_len, -1); |
| } |
| |
| memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); |
| memset(sense_b, 0, sizeof(sense_b)); |
| io_hdr.interface_id = 'S'; |
| io_hdr.cmd_len = MODE_SELECT10_CMDLEN; |
| io_hdr.mx_sb_len = sizeof(sense_b); |
| io_hdr.dxfer_direction = SG_DXFER_TO_DEV; |
| io_hdr.dxfer_len = param_len; |
| io_hdr.dxferp = paramp; |
| io_hdr.cmdp = modesCmdBlk; |
| io_hdr.sbp = sense_b; |
| io_hdr.timeout = DEF_TIMEOUT; |
| |
| if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { |
| if (noisy || verbose) |
| fprintf(sg_warnings_str, "mode select (10) SG_IO error: %s\n", |
| safe_strerror(errno)); |
| return -1; |
| } |
| if (verbose > 2) |
| fprintf(sg_warnings_str, " duration=%u ms\n", io_hdr.duration); |
| res = sg_err_category3(&io_hdr); |
| switch (res) { |
| case SG_LIB_CAT_RECOVERED: |
| if (noisy || verbose) |
| sg_chk_n_print3("Mode select (10)", &io_hdr); |
| /* fall through */ |
| case SG_LIB_CAT_CLEAN: |
| return 0; |
| case SG_LIB_CAT_INVALID_OP: |
| case SG_LIB_CAT_ILLEGAL_REQ: |
| if (verbose > 1) |
| sg_chk_n_print3("Mode select (10) error", &io_hdr); |
| return res; |
| default: |
| if (noisy || verbose) { |
| char ebuff[EBUFF_SZ]; |
| |
| snprintf(ebuff, EBUFF_SZ, "Mode select (10) error, pf=%d " |
| "sp=%d\n ", pf, sp); |
| sg_chk_n_print3(ebuff, &io_hdr); |
| } |
| return -1; |
| } |
| } |
| |
| /* MODE SENSE commands yield a response that has block descriptors followed |
| * by mode pages. In most cases users are interested in the first mode page. |
| * This function returns the (byte) offset of the start of the first mode |
| * page. Set mode_sense_6 to 1 for MODE SENSE (6) and 0 for MODE SENSE (10). |
| * Returns >= 0 is successful or -1 if failure. If there is a failure |
| * a message is written to err_buff. */ |
| int sg_mode_page_offset(const unsigned char * resp, int resp_len, |
| int mode_sense_6, char * err_buff, int err_buff_len) |
| { |
| int bd_len; |
| int calc_len; |
| int offset; |
| |
| if ((NULL == resp) || (resp_len < 4) || |
| ((! mode_sense_6) && (resp_len < 8))) { |
| snprintf(err_buff, err_buff_len, "given response length too short: " |
| "%d\n", resp_len); |
| return -1; |
| } |
| if (mode_sense_6) { |
| calc_len = resp[0] + 1; |
| bd_len = resp[3]; |
| offset = bd_len + MODE6_RESP_HDR_LEN; |
| } else { |
| calc_len = (resp[0] << 8) + resp[1] + 2; |
| bd_len = (resp[6] << 8) + resp[7]; |
| /* LongLBA doesn't change this calculation */ |
| offset = bd_len + MODE10_RESP_HDR_LEN; |
| } |
| if ((offset + 2) > resp_len) { |
| snprintf(err_buff, err_buff_len, "given response length " |
| "too small, offset=%d given_len=%d bd_len=%d\n", |
| offset, resp_len, bd_len); |
| offset = -1; |
| } else if ((offset + 2) > calc_len) { |
| snprintf(err_buff, err_buff_len, "calculated response " |
| "length too small, offset=%d calc_len=%d bd_len=%d\n", |
| offset, calc_len, bd_len); |
| offset = -1; |
| } |
| return offset; |
| } |
| |
| /* Fetches current, changeable, default and/or saveable modes pages as |
| * indicated (by those "*_mp" arguments that are not NULL). |
| * Return of 0 -> overall success, SG_LIB_CAT_INVALID_OP -> invalid opcode, |
| * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure. |
| * If success_mask pointer is not NULL then zeroes it then sets bit 0 if |
| * current fetched ok, bit 1 if changeable fetched ok, bit 2 if default |
| * fetched ok and bit 3 if saved fetched ok. If error on current page |
| * then stops and returns that error; otherwise continues if an error is |
| * detected but returns the first error encountered. */ |
| int sg_get_mode_page_types(int sg_fd, int mode6, int pg_code, |
| int sub_pg_code, int mx_mpage_len, |
| int * success_mask, void * current_mp, |
| void * changeable_mp, void * default_mp, |
| void * saved_mp, int verbose) |
| { |
| int k, res, offset, calc_len; |
| unsigned char buff[MODE_RESP_ARB_LEN]; |
| char ebuff[EBUFF_SZ]; |
| void * pc_arr[4]; |
| int first_err = 0; |
| int mx_mode_resp_len; |
| |
| if (success_mask) |
| *success_mask = 0; |
| if (mx_mpage_len < 4) |
| return 0; |
| if (NULL == sg_warnings_str) |
| sg_warnings_str = stderr; |
| if (mode6) |
| mx_mode_resp_len = |
| (MODE_RESP_ARB_LEN < 252) ? MODE_RESP_ARB_LEN : 252; |
| else |
| mx_mode_resp_len = MODE_RESP_ARB_LEN; |
| memset(ebuff, 0, sizeof(ebuff)); |
| pc_arr[0] = current_mp; |
| pc_arr[1] = changeable_mp; |
| pc_arr[2] = default_mp; |
| pc_arr[3] = saved_mp; |
| for (k = 0; k < 4; ++k) { |
| if (NULL == pc_arr[k]) |
| continue; |
| if (mode6) |
| res = sg_ll_mode_sense6(sg_fd, 0 /* dbd */, k /* pc */, |
| pg_code, sub_pg_code, buff, |
| mx_mode_resp_len, 0, verbose); |
| else |
| res = sg_ll_mode_sense10(sg_fd, 0 /* llbaa */, 0 /* dbd */, |
| k /* pc */, pg_code, sub_pg_code, |
| buff, mx_mode_resp_len, 0, verbose); |
| if (0 != res) { |
| if (0 == first_err) |
| first_err = res; |
| if (0 == k) |
| break; /* if problem on current page, it won't improve */ |
| else |
| continue; |
| } |
| offset = sg_mode_page_offset(buff, mx_mode_resp_len, mode6, |
| ebuff, EBUFF_SZ); |
| if (verbose && (offset < 0)) { |
| if (('\0' != ebuff[0]) && (verbose > 0)) |
| fprintf(sg_warnings_str, "sg_get_mode_page_types: " |
| "pc=%d: %s\n", k, ebuff); |
| return offset; |
| } |
| calc_len = mode6 ? (buff[0] + 1) : ((buff[0] << 8) + buff[1] + 2); |
| calc_len = (calc_len < MODE_RESP_ARB_LEN) ? calc_len : |
| MODE_RESP_ARB_LEN; |
| calc_len -= offset; |
| calc_len = (calc_len < mx_mpage_len) ? calc_len : mx_mpage_len; |
| memcpy(pc_arr[k], buff + offset, calc_len); |
| if (success_mask) |
| *success_mask |= (1 << k); |
| } |
| return first_err; |
| } |
| |
| /* Invokes a SCSI REQUEST SENSE command. Return of 0 -> success, |
| * SG_LIB_CAT_INVALID_OP -> Request Sense not * supported??, |
| * SG_LIB_CAT_ILEGAL_REQ -> bad field in cdb, -1 -> other failure */ |
| int sg_ll_request_sense(int sg_fd, int desc, void * resp, int mx_resp_len, |
| int verbose) |
| { |
| int k, res; |
| unsigned char rsCmdBlk[REQUEST_SENSE_CMDLEN] = |
| {REQUEST_SENSE_CMD, 0, 0, 0, 0, 0}; |
| unsigned char sense_b[SENSE_BUFF_LEN]; |
| struct sg_io_hdr io_hdr; |
| |
| if (desc) |
| rsCmdBlk[1] |= 0x1; |
| if (NULL == sg_warnings_str) |
| sg_warnings_str = stderr; |
| if (mx_resp_len > 0xfc) { |
| fprintf(sg_warnings_str, "SPC-3 says request sense allocation " |
| "length should be <= 252\n"); |
| return -1; |
| } |
| rsCmdBlk[4] = mx_resp_len & 0xff; |
| if (verbose) { |
| fprintf(sg_warnings_str, " Request Sense cmd: "); |
| for (k = 0; k < REQUEST_SENSE_CMDLEN; ++k) |
| fprintf(sg_warnings_str, "%02x ", rsCmdBlk[k]); |
| fprintf(sg_warnings_str, "\n"); |
| } |
| |
| memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); |
| memset(sense_b, 0, sizeof(sense_b)); |
| io_hdr.interface_id = 'S'; |
| io_hdr.cmd_len = REQUEST_SENSE_CMDLEN; |
| io_hdr.mx_sb_len = sizeof(sense_b); |
| io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; |
| io_hdr.dxfer_len = mx_resp_len; |
| io_hdr.dxferp = resp; |
| io_hdr.cmdp = rsCmdBlk; |
| io_hdr.sbp = sense_b; |
| io_hdr.timeout = DEF_TIMEOUT; |
| |
| if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { |
| if (verbose) |
| fprintf(sg_warnings_str, "request sense SG_IO error: %s\n", |
| safe_strerror(errno)); |
| return -1; |
| } |
| |
| if (verbose > 2) |
| fprintf(sg_warnings_str, " duration=%u ms\n", io_hdr.duration); |
| /* shouldn't get errors on Request Sense but it is best to be safe */ |
| res = sg_err_category3(&io_hdr); |
| switch (res) { |
| case SG_LIB_CAT_RECOVERED: |
| if (verbose) |
| sg_chk_n_print3("Request sense", &io_hdr); |
| /* fall through */ |
| case SG_LIB_CAT_CLEAN: |
| if ((mx_resp_len >= 8) && (io_hdr.resid > (mx_resp_len - 8))) { |
| fprintf(sg_warnings_str, " request sense: resid=%d " |
| "indicates response too short\n", io_hdr.resid); |
| return -1; |
| } else if (verbose && io_hdr.resid) |
| fprintf(sg_warnings_str, " request sense: resid=%d\n", |
| io_hdr.resid); |
| return 0; |
| case SG_LIB_CAT_INVALID_OP: |
| case SG_LIB_CAT_ILLEGAL_REQ: |
| if (verbose > 1) |
| sg_chk_n_print3("REQUEST SENSE command problem", &io_hdr); |
| return res; |
| default: |
| sg_chk_n_print3("REQUEST SENSE command problem", &io_hdr); |
| return -1; |
| } |
| } |
| |
| /* Invokes a SCSI REPORT LUNS command. Return of 0 -> success, |
| * SG_LIB_CAT_INVALID_OP -> Report Luns not supported, |
| * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */ |
| int sg_ll_report_luns(int sg_fd, int select_report, void * resp, |
| int mx_resp_len, int noisy, int verbose) |
| { |
| int k, res; |
| unsigned char rlCmdBlk[REPORT_LUNS_CMDLEN] = |
| {REPORT_LUNS_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
| unsigned char sense_b[SENSE_BUFF_LEN]; |
| struct sg_io_hdr io_hdr; |
| |
| rlCmdBlk[2] = select_report & 0xff; |
| rlCmdBlk[6] = (mx_resp_len >> 24) & 0xff; |
| rlCmdBlk[7] = (mx_resp_len >> 16) & 0xff; |
| rlCmdBlk[8] = (mx_resp_len >> 8) & 0xff; |
| rlCmdBlk[9] = mx_resp_len & 0xff; |
| if (NULL == sg_warnings_str) |
| sg_warnings_str = stderr; |
| if (verbose) { |
| fprintf(sg_warnings_str, " report luns cdb: "); |
| for (k = 0; k < REPORT_LUNS_CMDLEN; ++k) |
| fprintf(sg_warnings_str, "%02x ", rlCmdBlk[k]); |
| fprintf(sg_warnings_str, "\n"); |
| } |
| memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); |
| memset(sense_b, 0, sizeof(sense_b)); |
| io_hdr.interface_id = 'S'; |
| io_hdr.cmd_len = sizeof(rlCmdBlk); |
| io_hdr.mx_sb_len = sizeof(sense_b); |
| io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; |
| io_hdr.dxfer_len = mx_resp_len; |
| io_hdr.dxferp = resp; |
| io_hdr.cmdp = rlCmdBlk; |
| io_hdr.sbp = sense_b; |
| io_hdr.timeout = DEF_TIMEOUT; |
| |
| if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { |
| if (noisy || verbose) |
| fprintf(sg_warnings_str, "report_luns (SG_IO) error: %s\n", |
| safe_strerror(errno)); |
| return -1; |
| } |
| if (verbose > 2) |
| fprintf(sg_warnings_str, " duration=%u ms\n", io_hdr.duration); |
| res = sg_err_category3(&io_hdr); |
| switch (res) { |
| case SG_LIB_CAT_RECOVERED: |
| if (noisy || verbose) |
| sg_chk_n_print3("Report luns", &io_hdr); |
| /* fall through */ |
| case SG_LIB_CAT_CLEAN: |
| if (verbose && io_hdr.resid) |
| fprintf(sg_warnings_str, " report_luns: resid=%d\n", |
| io_hdr.resid); |
| return 0; |
| case SG_LIB_CAT_INVALID_OP: |
| case SG_LIB_CAT_ILLEGAL_REQ: |
| if (verbose > 1) |
| sg_chk_n_print3("REPORTS LUNS command error", &io_hdr); |
| return res; |
| case SG_LIB_CAT_MEDIA_CHANGED: |
| return 2; |
| default: |
| if (noisy || verbose) |
| sg_chk_n_print3("REPORT LUNS command error", &io_hdr); |
| return -1; |
| } |
| } |
| |
| /* Invokes a SCSI LOG SENSE command. Return of 0 -> success, |
| * SG_LIB_CAT_INVALID_OP -> Log Sense not supported, |
| * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */ |
| int sg_ll_log_sense(int sg_fd, int ppc, int sp, int pc, int pg_code, |
| int paramp, unsigned char * resp, int mx_resp_len, |
| int noisy, int verbose) |
| { |
| int res, k; |
| unsigned char logsCmdBlk[LOG_SENSE_CMDLEN] = |
| {LOG_SENSE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
| unsigned char sense_b[SENSE_BUFF_LEN]; |
| struct sg_io_hdr io_hdr; |
| |
| if (NULL == sg_warnings_str) |
| sg_warnings_str = stderr; |
| if (mx_resp_len > 0xffff) { |
| fprintf(sg_warnings_str, "mx_resp_len too big\n"); |
| return -1; |
| } |
| logsCmdBlk[1] = (unsigned char)((ppc ? 2 : 0) | (sp ? 1 : 0)); |
| logsCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); |
| logsCmdBlk[5] = (unsigned char)((paramp >> 8) & 0xff); |
| logsCmdBlk[6] = (unsigned char)(paramp & 0xff); |
| logsCmdBlk[7] = (unsigned char)((mx_resp_len >> 8) & 0xff); |
| logsCmdBlk[8] = (unsigned char)(mx_resp_len & 0xff); |
| if (verbose) { |
| fprintf(sg_warnings_str, " log sense cdb: "); |
| for (k = 0; k < LOG_SENSE_CMDLEN; ++k) |
| fprintf(sg_warnings_str, "%02x ", logsCmdBlk[k]); |
| fprintf(sg_warnings_str, "\n"); |
| } |
| |
| memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); |
| memset(sense_b, 0, sizeof(sense_b)); |
| io_hdr.interface_id = 'S'; |
| io_hdr.cmd_len = sizeof(logsCmdBlk); |
| io_hdr.mx_sb_len = sizeof(sense_b); |
| io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; |
| io_hdr.dxfer_len = mx_resp_len; |
| io_hdr.dxferp = resp; |
| io_hdr.cmdp = logsCmdBlk; |
| io_hdr.sbp = sense_b; |
| io_hdr.timeout = DEF_TIMEOUT; |
| |
| if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { |
| if (noisy || verbose) |
| fprintf(sg_warnings_str, "log sense (SG_IO) error: %s\n", |
| safe_strerror(errno)); |
| return -1; |
| } |
| if (verbose > 2) |
| fprintf(sg_warnings_str, " duration=%u ms\n", io_hdr.duration); |
| res = sg_err_category3(&io_hdr); |
| switch (res) { |
| case SG_LIB_CAT_RECOVERED: |
| if (noisy || verbose) |
| sg_chk_n_print3("Log sense", &io_hdr); |
| /* fall through */ |
| case SG_LIB_CAT_CLEAN: |
| if (verbose && io_hdr.resid) |
| fprintf(sg_warnings_str, " log_sense: resid=%d\n", |
| io_hdr.resid); |
| return 0; |
| case SG_LIB_CAT_INVALID_OP: |
| case SG_LIB_CAT_ILLEGAL_REQ: |
| if (verbose > 1) |
| sg_chk_n_print3("log_sense error", &io_hdr); |
| return res; |
| default: |
| if (noisy || verbose) { |
| char ebuff[EBUFF_SZ]; |
| snprintf(ebuff, EBUFF_SZ, "log_sense: ppc=%d, sp=%d, " |
| "pc=%d, page_code=%x, paramp=%x\n ", ppc, sp, pc, |
| pg_code, paramp); |
| sg_chk_n_print3(ebuff, &io_hdr); |
| } |
| return -1; |
| } |
| } |
| |
| |
| /* Invokes a SCSI LOG SELECT command. Return of 0 -> success, |
| * SG_LIB_CAT_INVALID_OP -> Log Select not supported, |
| * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */ |
| int sg_ll_log_select(int sg_fd, int pcr, int sp, int pc, |
| unsigned char * paramp, int param_len, |
| int noisy, int verbose) |
| { |
| int res, k; |
| unsigned char logsCmdBlk[LOG_SELECT_CMDLEN] = |
| {LOG_SELECT_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
| unsigned char sense_b[SENSE_BUFF_LEN]; |
| struct sg_io_hdr io_hdr; |
| |
| if (NULL == sg_warnings_str) |
| sg_warnings_str = stderr; |
| if (param_len > 0xffff) { |
| fprintf(sg_warnings_str, "log select: param_len too big\n"); |
| return -1; |
| } |
| logsCmdBlk[1] = (unsigned char)((pcr ? 2 : 0) | (sp ? 1 : 0)); |
| logsCmdBlk[2] = (unsigned char)((pc << 6) & 0xc0); |
| logsCmdBlk[7] = (unsigned char)((param_len >> 8) & 0xff); |
| logsCmdBlk[8] = (unsigned char)(param_len & 0xff); |
| if (verbose) { |
| fprintf(sg_warnings_str, " log select cdb: "); |
| for (k = 0; k < LOG_SELECT_CMDLEN; ++k) |
| fprintf(sg_warnings_str, "%02x ", logsCmdBlk[k]); |
| fprintf(sg_warnings_str, "\n"); |
| } |
| if ((verbose > 1) && (param_len > 0)) { |
| fprintf(sg_warnings_str, " log select parameter block\n"); |
| dStrHex((const char *)paramp, param_len, -1); |
| } |
| |
| memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); |
| memset(sense_b, 0, sizeof(sense_b)); |
| io_hdr.interface_id = 'S'; |
| io_hdr.cmd_len = sizeof(logsCmdBlk); |
| io_hdr.mx_sb_len = sizeof(sense_b); |
| io_hdr.dxfer_direction = param_len ? SG_DXFER_TO_DEV : SG_DXFER_NONE; |
| io_hdr.dxfer_len = param_len; |
| io_hdr.dxferp = paramp; |
| io_hdr.cmdp = logsCmdBlk; |
| io_hdr.sbp = sense_b; |
| io_hdr.timeout = DEF_TIMEOUT; |
| |
| if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { |
| if (noisy || verbose) |
| fprintf(sg_warnings_str, "log select (SG_IO) error: %s\n", |
| safe_strerror(errno)); |
| return -1; |
| } |
| if (verbose > 2) |
| fprintf(sg_warnings_str, " duration=%u ms\n", io_hdr.duration); |
| res = sg_err_category3(&io_hdr); |
| switch (res) { |
| case SG_LIB_CAT_RECOVERED: |
| if (noisy || verbose) |
| sg_chk_n_print3("Log select", &io_hdr); |
| /* fall through */ |
| case SG_LIB_CAT_CLEAN: |
| if (verbose && io_hdr.resid) |
| fprintf(sg_warnings_str, " log_select: resid=%d\n", |
| io_hdr.resid); |
| return 0; |
| case SG_LIB_CAT_INVALID_OP: |
| case SG_LIB_CAT_ILLEGAL_REQ: |
| if (verbose > 1) |
| sg_chk_n_print3("log_select error", &io_hdr); |
| return res; |
| default: |
| if (noisy || verbose) { |
| char ebuff[EBUFF_SZ]; |
| snprintf(ebuff, EBUFF_SZ, "log_select: pcr=%d, sp=%d, " |
| "pc=%d\n ", pcr, sp, pc); |
| sg_chk_n_print3(ebuff, &io_hdr); |
| } |
| return -1; |
| } |
| } |
| |
| /* Invokes a SCSI REPORT TARGET PORT GROUPS command. Return of 0 -> success, |
| * SG_LIB_CAT_INVALID_OP -> Report Target Port Groups not supported, |
| * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */ |
| int sg_ll_report_tgt_prt_grp(int sg_fd, void * resp, |
| int mx_resp_len, int noisy, int verbose) |
| { |
| int k, res; |
| unsigned char rtpgCmdBlk[MAINTENANCE_IN_CMDLEN] = |
| {MAINTENANCE_IN_CMD, REPORT_TGT_PRT_GRP_SA, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
| unsigned char sense_b[SENSE_BUFF_LEN]; |
| struct sg_io_hdr io_hdr; |
| |
| rtpgCmdBlk[6] = (mx_resp_len >> 24) & 0xff; |
| rtpgCmdBlk[7] = (mx_resp_len >> 16) & 0xff; |
| rtpgCmdBlk[8] = (mx_resp_len >> 8) & 0xff; |
| rtpgCmdBlk[9] = mx_resp_len & 0xff; |
| if (NULL == sg_warnings_str) |
| sg_warnings_str = stderr; |
| if (verbose) { |
| fprintf(sg_warnings_str, " report target port groups cdb: "); |
| for (k = 0; k < MAINTENANCE_IN_CMDLEN; ++k) |
| fprintf(sg_warnings_str, "%02x ", rtpgCmdBlk[k]); |
| fprintf(sg_warnings_str, "\n"); |
| } |
| memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); |
| memset(sense_b, 0, sizeof(sense_b)); |
| io_hdr.interface_id = 'S'; |
| io_hdr.cmd_len = sizeof(rtpgCmdBlk); |
| io_hdr.mx_sb_len = sizeof(sense_b); |
| io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; |
| io_hdr.dxfer_len = mx_resp_len; |
| io_hdr.dxferp = resp; |
| io_hdr.cmdp = rtpgCmdBlk; |
| io_hdr.sbp = sense_b; |
| io_hdr.timeout = DEF_TIMEOUT; |
| |
| if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { |
| if (noisy || verbose) |
| fprintf(sg_warnings_str, "report_tgt_prt_grp (SG_IO) error: " |
| "%s\n", safe_strerror(errno)); |
| return -1; |
| } |
| if (verbose > 2) |
| fprintf(sg_warnings_str, " duration=%u ms\n", io_hdr.duration); |
| res = sg_err_category3(&io_hdr); |
| switch (res) { |
| case SG_LIB_CAT_RECOVERED: |
| if (noisy || verbose) |
| sg_chk_n_print3("Report target port groups", &io_hdr); |
| /* fall through */ |
| case SG_LIB_CAT_CLEAN: |
| if (verbose && io_hdr.resid) |
| fprintf(sg_warnings_str, " report_tgt_prt_grp: resid=%d\n", |
| io_hdr.resid); |
| return 0; |
| case SG_LIB_CAT_INVALID_OP: |
| case SG_LIB_CAT_ILLEGAL_REQ: |
| if (verbose > 1) |
| sg_chk_n_print3("REPORT TARGET PORT GROUPS", &io_hdr); |
| return res; |
| default: |
| if (noisy || verbose) |
| sg_chk_n_print3("REPORT TARGET PORT GROUPS command error", &io_hdr); |
| return -1; |
| } |
| } |
| |
| /* Invokes a SCSI SEND DIAGNOSTIC command. Foreground, extended self tests can |
| * take a long time, if so set long_duration flag. Return of 0 -> success, |
| * SG_LIB_CAT_INVALID_OP -> Send diagnostic not supported, |
| * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */ |
| int sg_ll_send_diag(int sg_fd, int sf_code, int pf_bit, int sf_bit, |
| int devofl_bit, int unitofl_bit, int long_duration, |
| void * paramp, int param_len, int noisy, |
| int verbose) |
| { |
| int k, res; |
| unsigned char senddiagCmdBlk[SEND_DIAGNOSTIC_CMDLEN] = |
| {SEND_DIAGNOSTIC_CMD, 0, 0, 0, 0, 0}; |
| unsigned char sense_b[SENSE_BUFF_LEN]; |
| struct sg_io_hdr io_hdr; |
| |
| senddiagCmdBlk[1] = (unsigned char)((sf_code << 5) | (pf_bit << 4) | |
| (sf_bit << 2) | (devofl_bit << 1) | unitofl_bit); |
| senddiagCmdBlk[3] = (unsigned char)((param_len >> 8) & 0xff); |
| senddiagCmdBlk[4] = (unsigned char)(param_len & 0xff); |
| |
| if (NULL == sg_warnings_str) |
| sg_warnings_str = stderr; |
| if (verbose) { |
| fprintf(sg_warnings_str, " Send diagnostic cmd: "); |
| for (k = 0; k < SEND_DIAGNOSTIC_CMDLEN; ++k) |
| fprintf(sg_warnings_str, "%02x ", senddiagCmdBlk[k]); |
| fprintf(sg_warnings_str, "\n"); |
| if ((verbose > 1) && paramp && param_len) { |
| fprintf(sg_warnings_str, " Send diagnostic parameter " |
| "block:\n"); |
| dStrHex(paramp, param_len, -1); |
| } |
| } |
| |
| memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); |
| io_hdr.interface_id = 'S'; |
| io_hdr.cmd_len = SEND_DIAGNOSTIC_CMDLEN; |
| io_hdr.mx_sb_len = sizeof(sense_b); |
| io_hdr.dxfer_direction = param_len ? SG_DXFER_TO_DEV : SG_DXFER_NONE; |
| io_hdr.dxfer_len = param_len; |
| io_hdr.dxferp = paramp; |
| io_hdr.cmdp = senddiagCmdBlk; |
| io_hdr.sbp = sense_b; |
| io_hdr.timeout = long_duration ? LONG_TIMEOUT : DEF_TIMEOUT; |
| |
| if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { |
| if (noisy || verbose) |
| fprintf(sg_warnings_str, "send diagnostic (SG_IO) error: %s\n", |
| safe_strerror(errno)); |
| return -1; |
| } |
| if (verbose > 2) |
| fprintf(sg_warnings_str, " duration=%u ms\n", io_hdr.duration); |
| res = sg_err_category3(&io_hdr); |
| switch (res) { |
| case SG_LIB_CAT_RECOVERED: |
| if (noisy || verbose) |
| sg_chk_n_print3("Send diagnostic, continuing", &io_hdr); |
| /* fall through */ |
| case SG_LIB_CAT_CLEAN: |
| return 0; |
| case SG_LIB_CAT_INVALID_OP: |
| case SG_LIB_CAT_ILLEGAL_REQ: |
| if (verbose > 1) |
| sg_chk_n_print3("SEND DIAGNOSTIC", &io_hdr); |
| return res; |
| default: |
| if (noisy) { |
| char ebuff[EBUFF_SZ]; |
| snprintf(ebuff, EBUFF_SZ, "Send diagnostic error, sf_code=0x%x, " |
| "pf_bit=%d, sf_bit=%d ", sf_code, pf_bit, sf_bit); |
| sg_chk_n_print3(ebuff, &io_hdr); |
| } |
| return -1; |
| } |
| } |
| |
| /* Invokes a SCSI RECEIVE DIAGNOSTICS RESULTS command. Return of 0 -> success, |
| * SG_LIB_CAT_INVALID_OP -> Receive diagnostics results not supported, |
| * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */ |
| int sg_ll_receive_diag(int sg_fd, int pcv, int pg_code, void * resp, |
| int mx_resp_len, int noisy, int verbose) |
| { |
| int k, res; |
| unsigned char rcvdiagCmdBlk[RECEIVE_DIAGNOSTICS_CMDLEN] = |
| {RECEIVE_DIAGNOSTICS_CMD, 0, 0, 0, 0, 0}; |
| unsigned char sense_b[SENSE_BUFF_LEN]; |
| struct sg_io_hdr io_hdr; |
| |
| rcvdiagCmdBlk[1] = (unsigned char)(pcv ? 0x1 : 0); |
| rcvdiagCmdBlk[2] = (unsigned char)(pg_code); |
| rcvdiagCmdBlk[3] = (unsigned char)((mx_resp_len >> 8) & 0xff); |
| rcvdiagCmdBlk[4] = (unsigned char)(mx_resp_len & 0xff); |
| |
| if (NULL == sg_warnings_str) |
| sg_warnings_str = stderr; |
| if (verbose) { |
| fprintf(sg_warnings_str, " Receive diagnostics results cmd: "); |
| for (k = 0; k < RECEIVE_DIAGNOSTICS_CMDLEN; ++k) |
| fprintf(sg_warnings_str, "%02x ", rcvdiagCmdBlk[k]); |
| fprintf(sg_warnings_str, "\n"); |
| } |
| |
| memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); |
| io_hdr.interface_id = 'S'; |
| io_hdr.cmd_len = RECEIVE_DIAGNOSTICS_CMDLEN; |
| io_hdr.mx_sb_len = sizeof(sense_b); |
| io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; |
| io_hdr.dxfer_len = mx_resp_len; |
| io_hdr.dxferp = resp; |
| io_hdr.cmdp = rcvdiagCmdBlk; |
| io_hdr.sbp = sense_b; |
| io_hdr.timeout = DEF_TIMEOUT; |
| |
| if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { |
| if (noisy || verbose) |
| fprintf(sg_warnings_str, "receive diagnostics results (SG_IO) " |
| "error: %s\n", safe_strerror(errno)); |
| return -1; |
| } |
| if (verbose > 2) |
| fprintf(sg_warnings_str, " duration=%u ms\n", io_hdr.duration); |
| res = sg_err_category3(&io_hdr); |
| switch (res) { |
| case SG_LIB_CAT_RECOVERED: |
| if (noisy || verbose) |
| sg_chk_n_print3("Receive diagnostics results, continuing", |
| &io_hdr); |
| /* fall through */ |
| case SG_LIB_CAT_CLEAN: |
| return 0; |
| case SG_LIB_CAT_INVALID_OP: |
| case SG_LIB_CAT_ILLEGAL_REQ: |
| if (verbose > 1) |
| sg_chk_n_print3("RECEIVE DIAGNOSTICS RESULTS", &io_hdr); |
| return res; |
| default: |
| if (noisy) { |
| char ebuff[EBUFF_SZ]; |
| snprintf(ebuff, EBUFF_SZ, "Receive diagnostics results error, " |
| "pcv=%d, page_code=%x ", pcv, pg_code); |
| sg_chk_n_print3(ebuff, &io_hdr); |
| } |
| return -1; |
| } |
| } |
| |
| /* Invokes a SCSI READ DEFECT DATA (10) command (SBC). Return of 0 -> |
| * success, SG_LIB_CAT_INVALID_OP -> invalid opcode, |
| * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */ |
| int sg_ll_read_defect10(int sg_fd, int req_plist, int req_glist, |
| int dl_format, void * resp, int mx_resp_len, |
| int noisy, int verbose) |
| { |
| int res, k; |
| unsigned char rdefCmdBlk[READ_DEFECT10_CMDLEN] = |
| {READ_DEFECT10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
| unsigned char sense_b[SENSE_BUFF_LEN]; |
| struct sg_io_hdr io_hdr; |
| |
| rdefCmdBlk[2] = (unsigned char)(((req_plist << 4) & 0x10) | |
| ((req_glist << 3) & 0x8) | (dl_format & 0x7)); |
| rdefCmdBlk[7] = (unsigned char)((mx_resp_len >> 8) & 0xff); |
| rdefCmdBlk[8] = (unsigned char)(mx_resp_len & 0xff); |
| if (NULL == sg_warnings_str) |
| sg_warnings_str = stderr; |
| if (mx_resp_len > 0xffff) { |
| fprintf(sg_warnings_str, "mx_resp_len too big\n"); |
| return -1; |
| } |
| if (verbose) { |
| fprintf(sg_warnings_str, " read defect (10) cdb: "); |
| for (k = 0; k < READ_DEFECT10_CMDLEN; ++k) |
| fprintf(sg_warnings_str, "%02x ", rdefCmdBlk[k]); |
| fprintf(sg_warnings_str, "\n"); |
| } |
| |
| memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); |
| memset(sense_b, 0, sizeof(sense_b)); |
| io_hdr.interface_id = 'S'; |
| io_hdr.cmd_len = READ_DEFECT10_CMDLEN; |
| io_hdr.mx_sb_len = sizeof(sense_b); |
| io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; |
| io_hdr.dxfer_len = mx_resp_len; |
| io_hdr.dxferp = resp; |
| io_hdr.cmdp = rdefCmdBlk; |
| io_hdr.sbp = sense_b; |
| io_hdr.timeout = DEF_TIMEOUT; |
| |
| if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { |
| if (noisy || verbose) |
| fprintf(sg_warnings_str, "read defect (10) SG_IO error: %s\n", |
| safe_strerror(errno)); |
| return -1; |
| } |
| if (verbose > 2) |
| fprintf(sg_warnings_str, " duration=%u ms\n", io_hdr.duration); |
| res = sg_err_category3(&io_hdr); |
| switch (res) { |
| case SG_LIB_CAT_RECOVERED: |
| if (noisy || verbose) |
| sg_chk_n_print3("Read defect (10)", &io_hdr); |
| /* fall through */ |
| case SG_LIB_CAT_CLEAN: |
| if (verbose && io_hdr.resid) |
| fprintf(sg_warnings_str, " read defect (10): resid=%d\n", |
| io_hdr.resid); |
| if (verbose > 2) { |
| k = mx_resp_len - io_hdr.resid; |
| if (k > 0) { |
| fprintf(sg_warnings_str, " read defect (10): response%s\n", |
| (k > 256 ? ", first 256 bytes" : "")); |
| dStrHex(resp, (k > 256 ? 256 : k), -1); |
| } |
| } |
| return 0; |
| case SG_LIB_CAT_INVALID_OP: |
| case SG_LIB_CAT_ILLEGAL_REQ: |
| if (verbose > 1) |
| sg_chk_n_print3("Read defect (10) error", &io_hdr); |
| return res; |
| default: |
| if (noisy || verbose) { |
| char ebuff[EBUFF_SZ]; |
| |
| snprintf(ebuff, EBUFF_SZ, "Read defect (10) error, req_plist=%d " |
| "req_glist=%d dl_format=%x\n ", req_plist, req_glist, |
| dl_format); |
| sg_chk_n_print3(ebuff, &io_hdr); |
| } |
| return -1; |
| } |
| } |
| |
| /* Invokes a SCSI READ MEDIA SERIAL NUMBER command. Return of 0 -> success, |
| * SG_LIB_CAT_INVALID_OP -> Read media serial number not supported, |
| * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, -1 -> other failure */ |
| int sg_ll_read_media_serial_num(int sg_fd, void * resp, |
| int mx_resp_len, int noisy, int verbose) |
| { |
| int k, res; |
| unsigned char rmsnCmdBlk[SERVICE_ACTION_IN_12_CMDLEN] = |
| {SERVICE_ACTION_IN_12_CMD, READ_MEDIA_SERIAL_NUM_SA, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
| unsigned char sense_b[SENSE_BUFF_LEN]; |
| struct sg_io_hdr io_hdr; |
| |
| rmsnCmdBlk[6] = (mx_resp_len >> 24) & 0xff; |
| rmsnCmdBlk[7] = (mx_resp_len >> 16) & 0xff; |
| rmsnCmdBlk[8] = (mx_resp_len >> 8) & 0xff; |
| rmsnCmdBlk[9] = mx_resp_len & 0xff; |
| if (NULL == sg_warnings_str) |
| sg_warnings_str = stderr; |
| if (verbose) { |
| fprintf(sg_warnings_str, " read media serial number cdb: "); |
| for (k = 0; k < SERVICE_ACTION_IN_12_CMDLEN; ++k) |
| fprintf(sg_warnings_str, "%02x ", rmsnCmdBlk[k]); |
| fprintf(sg_warnings_str, "\n"); |
| } |
| memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); |
| memset(sense_b, 0, sizeof(sense_b)); |
| io_hdr.interface_id = 'S'; |
| io_hdr.cmd_len = sizeof(rmsnCmdBlk); |
| io_hdr.mx_sb_len = sizeof(sense_b); |
| io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; |
| io_hdr.dxfer_len = mx_resp_len; |
| io_hdr.dxferp = resp; |
| io_hdr.cmdp = rmsnCmdBlk; |
| io_hdr.sbp = sense_b; |
| io_hdr.timeout = DEF_TIMEOUT; |
| |
| if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) { |
| if (noisy || verbose) |
| fprintf(sg_warnings_str, "read_media_serial_num (SG_IO) error:" |
| " %s\n", safe_strerror(errno)); |
| return -1; |
| } |
| if (verbose > 2) |
| fprintf(sg_warnings_str, " duration=%u ms\n", io_hdr.duration); |
| res = sg_err_category3(&io_hdr); |
| switch (res) { |
| case SG_LIB_CAT_RECOVERED: |
| if (noisy || verbose) |
| sg_chk_n_print3("Read media serial number", &io_hdr); |
| /* fall through */ |
| case SG_LIB_CAT_CLEAN: |
| if (verbose && io_hdr.resid) |
| fprintf(sg_warnings_str, " read_media_serial_num: resid=%d\n", |
| io_hdr.resid); |
| return 0; |
| case SG_LIB_CAT_INVALID_OP: |
| case SG_LIB_CAT_ILLEGAL_REQ: |
| if (verbose > 1) |
| sg_chk_n_print3("READ MEDIA SERIAL NUMBER", &io_hdr); |
| return res; |
| default: |
| if (noisy || verbose) |
| sg_chk_n_print3("READ MEDIA SERIAL NUMBER command error", |
| &io_hdr); |
| return -1; |
| } |
| } |