/* Invokes a SCSI READ BUFFER(10) command (spc5r02). Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ static int sg_ll_read_buffer_10(int sg_fd, int rb_mode, int rb_mode_sp, int rb_id, uint32_t rb_offset, void * resp, int mx_resp_len, int * residp, bool noisy, int verbose) { int k, ret, res, sense_cat; uint8_t rb10_cb[SG_READ_BUFFER_10_CMDLEN] = {SG_READ_BUFFER_10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; rb10_cb[1] = (uint8_t)(rb_mode & 0x1f); if (rb_mode_sp) rb10_cb[1] |= (uint8_t)((rb_mode_sp & 0x7) << 5); rb10_cb[2] = (uint8_t)rb_id; sg_put_unaligned_be24(rb_offset, rb10_cb + 3); sg_put_unaligned_be24(mx_resp_len, rb10_cb + 6); if (verbose) { pr2serr(" Read buffer(10) cdb: "); for (k = 0; k < SG_READ_BUFFER_10_CMDLEN; ++k) pr2serr("%02x ", rb10_cb[k]); pr2serr("\n"); } ptvp = construct_scsi_pt_obj(); if (NULL == ptvp) { pr2serr("Read buffer(10): out of memory\n"); return -1; } set_scsi_pt_cdb(ptvp, rb10_cb, sizeof(rb10_cb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, "Read buffer(10)", res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else { if ((verbose > 2) && (ret > 0)) { pr2serr(" Read buffer(10): response%s\n", (ret > 256 ? ", first 256 bytes" : "")); hex2stderr((const uint8_t *)resp, (ret > 256 ? 256 : ret), -1); } ret = 0; } if (residp) *residp = get_scsi_pt_resid(ptvp); destruct_scsi_pt_obj(ptvp); return ret; }
/* Invokes a SCSI LOG SELECT command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_log_select(int sg_fd, int pcr, int sp, int pc, int pg_code, int subpg_code, unsigned char * paramp, int param_len, int noisy, int verbose) { static const char * const cdb_name_s = "log select"; int res, ret, k, sense_cat; unsigned char logs_cdb[LOG_SELECT_CMDLEN] = {LOG_SELECT_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (param_len > 0xffff) { pr2ws("%s: param_len too big\n", cdb_name_s); return -1; } logs_cdb[1] = (unsigned char)((pcr ? 2 : 0) | (sp ? 1 : 0)); logs_cdb[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); logs_cdb[3] = (unsigned char)(subpg_code & 0xff); sg_put_unaligned_be16((int16_t)param_len, logs_cdb + 7); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < LOG_SELECT_CMDLEN; ++k) pr2ws("%02x ", logs_cdb[k]); pr2ws("\n"); } if ((verbose > 1) && (param_len > 0)) { pr2ws(" %s parameter list\n", cdb_name_s); dStrHexErr((const char *)paramp, param_len, -1); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, logs_cdb, sizeof(logs_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, 0, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) ; else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; }
/* Invokes a SCSI MODE SELECT (10) command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors, * v2 adds rtd (revert to defaults) bit (spc5r11). */ int sg_ll_mode_select10_v2(int sg_fd, bool pf, bool rtd, bool sp, void * paramp, int param_len, bool noisy, int verbose) { static const char * const cdb_s = "mode select(10)"; int res, ret, k, sense_cat; uint8_t modes_cdb[MODE_SELECT10_CMDLEN] = {MODE_SELECT10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; modes_cdb[1] = (uint8_t)((pf ? 0x10 : 0x0) | (sp ? 0x1 : 0x0)); if (rtd) modes_cdb[1] |= 0x2; sg_put_unaligned_be16((int16_t)param_len, modes_cdb + 7); if (param_len > 0xffff) { pr2ws("%s: param_len too big\n", cdb_s); return -1; } if (verbose) { pr2ws(" %s cdb: ", cdb_s); for (k = 0; k < MODE_SELECT10_CMDLEN; ++k) pr2ws("%02x ", modes_cdb[k]); pr2ws("\n"); } if (verbose > 1) { pr2ws(" %s parameter list\n", cdb_s); hex2stderr((const uint8_t *)paramp, param_len, -1); } if (NULL == ((ptvp = create_pt_obj(cdb_s)))) return -1; set_scsi_pt_cdb(ptvp, modes_cdb, sizeof(modes_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (uint8_t *)paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat); if (-1 == ret) ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; }
static int ll_sync_cache_16(int sg_fd, int sync_nv, int immed, int group, uint64_t lba, unsigned int num_lb, int to_secs, int noisy, int verbose) { int res, ret, k, sense_cat; unsigned char sc_cdb[SYNCHRONIZE_CACHE16_CMDLEN] = { SYNCHRONIZE_CACHE16_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (sync_nv) sc_cdb[1] |= 4; /* obsolete in sbc3r35d */ if (immed) sc_cdb[1] |= 2; sg_put_unaligned_be64(lba, sc_cdb + 2); sc_cdb[14] = group & 0x1f; sg_put_unaligned_be32((uint32_t)num_lb, sc_cdb + 10); if (verbose) { pr2serr(" synchronize cache(16) cdb: "); for (k = 0; k < SYNCHRONIZE_CACHE16_CMDLEN; ++k) pr2serr("%02x ", sc_cdb[k]); pr2serr("\n"); } ptvp = construct_scsi_pt_obj(); if (NULL == ptvp) { pr2serr("synchronize cache(16): out of memory\n"); return -1; } set_scsi_pt_cdb(ptvp, sc_cdb, sizeof(sc_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); res = do_scsi_pt(ptvp, sg_fd, to_secs, verbose); ret = sg_cmds_process_resp(ptvp, "synchronize cache(16)", res, 0, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) ; else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; }
/* Invokes a SCSI SYNCHRONIZE CACHE (10) command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ 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) { static const char * const cdb_name_s = "synchronize cache(10)"; int res, ret, k, sense_cat; unsigned char sc_cdb[SYNCHRONIZE_CACHE_CMDLEN] = {SYNCHRONIZE_CACHE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (sync_nv) sc_cdb[1] |= 4; if (immed) sc_cdb[1] |= 2; sg_put_unaligned_be32((uint32_t)lba, sc_cdb + 2); sc_cdb[6] = group & 0x1f; if (count > 0xffff) { pr2ws("count too big\n"); return -1; } sg_put_unaligned_be16((int16_t)count, sc_cdb + 7); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < SYNCHRONIZE_CACHE_CMDLEN; ++k) pr2ws("%02x ", sc_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, sc_cdb, sizeof(sc_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, 0, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) ; else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; }
/* Invokes a SCSI MODE SELECT (6) command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_mode_select6(int sg_fd, int pf, int sp, void * paramp, int param_len, int noisy, int verbose) { static const char * const cdb_name_s = "mode select(6)"; int res, ret, k, sense_cat; unsigned char modes_cdb[MODE_SELECT6_CMDLEN] = {MODE_SELECT6_CMD, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; modes_cdb[1] = (unsigned char)(((pf << 4) & 0x10) | (sp & 0x1)); modes_cdb[4] = (unsigned char)(param_len & 0xff); if (param_len > 0xff) { pr2ws("%s: param_len too big\n", cdb_name_s); return -1; } if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < MODE_SELECT6_CMDLEN; ++k) pr2ws("%02x ", modes_cdb[k]); pr2ws("\n"); } if (verbose > 1) { pr2ws(" %s parameter list\n", cdb_name_s); dStrHexErr((const char *)paramp, param_len, -1); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, modes_cdb, sizeof(modes_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, 0, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) ; else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; }
/* Invokes a SCSI REPORT TIMESTAMP command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ static int sg_ll_rep_timestamp(int sg_fd, void * resp, int mx_resp_len, int * residp, int noisy, int verbose) { int k, ret, res, sense_cat; unsigned char rtCmdBlk[REP_TIMESTAMP_CMDLEN] = {SG_MAINTENANCE_IN, REP_TIMESTAMP_SA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; sg_put_unaligned_be32((uint32_t)mx_resp_len, rtCmdBlk + 6); if (verbose) { pr2serr(" Report timestamp cdb: "); for (k = 0; k < REP_TIMESTAMP_CMDLEN; ++k) pr2serr("%02x ", rtCmdBlk[k]); pr2serr("\n"); } ptvp = construct_scsi_pt_obj(); if (NULL == ptvp) { pr2serr("%s: out of memory\n", __func__); return -1; } set_scsi_pt_cdb(ptvp, rtCmdBlk, sizeof(rtCmdBlk)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, "report timestamp", res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) ; else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; k = get_scsi_pt_resid(ptvp); if (residp) *residp = k; if ((verbose > 2) && ((mx_resp_len - k) > 0)) { pr2serr("Parameter data returned:\n"); dStrHexErr((const char *)resp, mx_resp_len - k, ((verbose > 3) ? -1 : 1)); } destruct_scsi_pt_obj(ptvp); return ret; }
/* Invokes the SET TIMESTAMP command. Return of 0 -> success, various * SG_LIB_CAT_* positive values or -1 -> other errors */ static int sg_ll_set_timestamp(int sg_fd, void * paramp, int param_len, int noisy, int verbose) { int k, ret, res, sense_cat; unsigned char stCmdBlk[SET_TIMESTAMP_CMDLEN] = {SG_MAINTENANCE_OUT, SET_TIMESTAMP_SA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; sg_put_unaligned_be32(param_len, stCmdBlk + 6); if (verbose) { pr2serr(" Set timestamp cdb: "); for (k = 0; k < SET_TIMESTAMP_CMDLEN; ++k) pr2serr("%02x ", stCmdBlk[k]); pr2serr("\n"); if ((verbose > 1) && paramp && param_len) { pr2serr(" set timestamp parameter list:\n"); dStrHexErr((const char *)paramp, param_len, -1); } } ptvp = construct_scsi_pt_obj(); if (NULL == ptvp) { pr2serr("%s: out of memory\n", __func__); return -1; } set_scsi_pt_cdb(ptvp, stCmdBlk, sizeof(stCmdBlk)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (unsigned char *)paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, "set timestamp", res, 0, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) ; else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; }
/* Invokes a SCSI READ CAPACITY (16) command. Returns 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_readcap_16(int sg_fd, int pmi, uint64_t llba, void * resp, int mx_resp_len, int noisy, int verbose) { static const char * const cdb_name_s = "read capacity(16)"; int k, ret, res, sense_cat; unsigned char rc_cdb[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_pt_base * ptvp; if (pmi) { /* lbs only valid when pmi set */ rc_cdb[14] |= 1; sg_put_unaligned_be64(llba, rc_cdb + 2); } /* Allocation length, no guidance in SBC-2 rev 15b */ sg_put_unaligned_be32((uint32_t)mx_resp_len, rc_cdb + 10); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < SERVICE_ACTION_IN_16_CMDLEN; ++k) pr2ws("%02x ", rc_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, rc_cdb, sizeof(rc_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) ; else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; }
/* Invokes a SCSI BACKGROUND CONTROL command (SBC-4). Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ static int sg_ll_background_control(int sg_fd, unsigned int bo_ctl, unsigned int bo_time, bool noisy, int verbose) { int k, ret, res, sense_cat; uint8_t bcCDB[16] = {SG_SERVICE_ACTION_IN_16, BACKGROUND_CONTROL_SA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (bo_ctl) bcCDB[2] |= (bo_ctl & 0x3) << 6; if (bo_time) bcCDB[3] = bo_time; if (verbose) { pr2serr(" %s cdb: ", cmd_name); for (k = 0; k < (int)sizeof(bcCDB); ++k) pr2serr("%02x ", bcCDB[k]); pr2serr("\n"); } ptvp = construct_scsi_pt_obj(); if (NULL == ptvp) { pr2serr("%s: out of memory\n", cmd_name); return -1; } set_scsi_pt_cdb(ptvp, bcCDB, sizeof(bcCDB)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cmd_name, res, SG_NO_DATA_IN, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; }
/* Invokes a SCSI START STOP UNIT command (SBC + MMC). * Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors. * SBC-3 and MMC partially overlap on the power_condition_modifier(sbc) and * format_layer_number(mmc) fields. They also overlap on the noflush(sbc) * and fl(mmc) one bit field. This is the cause of the awkardly named * pc_mod__fl_num and noflush__fl arguments to this function. * */ int sg_ll_start_stop_unit_pt(struct sg_pt_base * ptvp, bool immed, int pc_mod__fl_num, int power_cond, bool noflush__fl, bool loej, bool start, bool noisy, int verbose) { static const char * const cdb_s = "start stop unit"; int k, res, ret, sense_cat; uint8_t ssuBlk[START_STOP_CMDLEN] = {START_STOP_CMD, 0, 0, 0, 0, 0}; uint8_t sense_b[SENSE_BUFF_LEN]; if (immed) ssuBlk[1] = 0x1; ssuBlk[3] = pc_mod__fl_num & 0xf; /* bits 2 and 3 are reserved in MMC */ ssuBlk[4] = ((power_cond & 0xf) << 4); if (noflush__fl) ssuBlk[4] |= 0x4; if (loej) ssuBlk[4] |= 0x2; if (start) ssuBlk[4] |= 0x1; if (verbose) { pr2ws(" %s command:", cdb_s); for (k = 0; k < (int)sizeof(ssuBlk); ++k) pr2ws(" %02x", ssuBlk[k]); pr2ws("\n"); } clear_scsi_pt_obj(ptvp); set_scsi_pt_cdb(ptvp, ssuBlk, sizeof(ssuBlk)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); res = do_scsi_pt(ptvp, -1, START_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat); if (-1 == ret) ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; return ret; }
/* Invokes a SCSI READ CAPACITY (10) command. Returns 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_readcap_10(int sg_fd, bool pmi, unsigned int lba, void * resp, int mx_resp_len, bool noisy, int verbose) { static const char * const cdb_s = "read capacity(10)"; int k, ret, res, sense_cat; uint8_t rc_cdb[READ_CAPACITY_10_CMDLEN] = {READ_CAPACITY_10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (pmi) { /* lbs only valid when pmi set */ rc_cdb[8] |= 1; sg_put_unaligned_be32((uint32_t)lba, rc_cdb + 2); } if (verbose) { pr2ws(" %s cdb: ", cdb_s); for (k = 0; k < READ_CAPACITY_10_CMDLEN; ++k) pr2ws("%02x ", rc_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_s)))) return -1; set_scsi_pt_cdb(ptvp, rc_cdb, sizeof(rc_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_s, res, noisy, verbose, &sense_cat); if (-1 == ret) ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; }
/* Invokes a SCSI PREVENT ALLOW MEDIUM REMOVAL command * [was in SPC-3 but displaced from SPC-4 into SBC-3, MMC-5, SSC-3] * prevent==0 allows removal, prevent==1 prevents removal ... * Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_prevent_allow(int sg_fd, int prevent, int noisy, int verbose) { static const char * const cdb_name_s = "prevent allow medium removal"; int k, res, ret, sense_cat; unsigned char p_cdb[PREVENT_ALLOW_CMDLEN] = {PREVENT_ALLOW_CMD, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if ((prevent < 0) || (prevent > 3)) { pr2ws("prevent argument should be 0, 1, 2 or 3\n"); return -1; } p_cdb[4] |= (prevent & 0x3); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < PREVENT_ALLOW_CMDLEN; ++k) pr2ws("%02x ", p_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, p_cdb, sizeof(p_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, 0, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) ; else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; }
/* Invokes a SCSI START STOP UNIT command (SBC + MMC). * Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors. * SBC-3 and MMC partially overlap on the power_condition_modifier(sbc) and * format_layer_number(mmc) fields. They also overlap on the noflush(sbc) * and fl(mmc) one bit field. This is the cause of the awkardly named * pc_mod__fl_num and noflush__fl arguments to this function. * */ int sg_ll_start_stop_unit(int sg_fd, int immed, int pc_mod__fl_num, int power_cond, int noflush__fl, int loej, int start, int noisy, int verbose) { static const char * const cdb_name_s = "start stop unit"; int k, res, ret, sense_cat; struct sg_pt_base * ptvp; unsigned char ssuBlk[START_STOP_CMDLEN] = {START_STOP_CMD, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; ssuBlk[1] = immed & 1; ssuBlk[3] = pc_mod__fl_num & 0xf; /* bits 2 and 3 are reserved in MMC */ ssuBlk[4] = ((power_cond & 0xf) << 4) | (noflush__fl ? 0x4 : 0) | (loej ? 0x2 : 0) | (start ? 0x1 : 0); if (verbose) { pr2ws(" %s command:", cdb_name_s); for (k = 0; k < (int)sizeof(ssuBlk); ++k) pr2ws(" %02x", ssuBlk[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, ssuBlk, sizeof(ssuBlk)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); res = do_scsi_pt(ptvp, sg_fd, START_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, 0, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) ; else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; }
/* Invokes a SCSI WRITE BUFFER command (SPC). Return of 0 -> * success, SG_LIB_CAT_INVALID_OP -> invalid opcode, * SG_LIB_CAT_ILLEGAL_REQ -> bad field in cdb, SG_LIB_CAT_UNIT_ATTENTION, * SG_LIB_CAT_NOT_READY -> device not ready, SG_LIB_CAT_ABORTED_COMMAND, * -1 -> other failure */ static int sg_ll_write_buffer_v2(int sg_fd, int mode, int m_specific, int buffer_id, uint32_t buffer_offset, void * paramp, uint32_t param_len, int to_secs, int noisy, int verbose) { int k, res, ret, sense_cat; uint8_t wbuf_cdb[WRITE_BUFFER_CMDLEN] = {WRITE_BUFFER_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (buffer_offset > 0xffffff) { pr2serr("%s: buffer_offset value too large for 24 bits\n", __func__); return -1; } if (param_len > 0xffffff) { pr2serr("%s: param_len value too large for 24 bits\n", __func__); return -1; } wbuf_cdb[1] = (uint8_t)(mode & 0x1f); wbuf_cdb[1] |= (uint8_t)((m_specific & 0x7) << 5); wbuf_cdb[2] = (uint8_t)(buffer_id & 0xff); sg_put_unaligned_be24(buffer_offset, wbuf_cdb + 3); sg_put_unaligned_be24(param_len, wbuf_cdb + 6); if (verbose) { pr2serr(" Write buffer cdb: "); for (k = 0; k < WRITE_BUFFER_CMDLEN; ++k) pr2serr("%02x ", wbuf_cdb[k]); pr2serr("\n"); if ((verbose > 1) && paramp && param_len) { pr2serr(" Write buffer parameter list%s:\n", ((param_len > 256) ? " (first 256 bytes)" : "")); dStrHexErr((const char *)paramp, ((param_len > 256) ? 256 : param_len), -1); } } ptvp = construct_scsi_pt_obj(); if (NULL == ptvp) { pr2serr("%s: out of memory\n", __func__); return -1; } set_scsi_pt_cdb(ptvp, wbuf_cdb, sizeof(wbuf_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (uint8_t *)paramp, param_len); res = do_scsi_pt(ptvp, sg_fd, to_secs, verbose); ret = sg_cmds_process_resp(ptvp, "Write buffer", res, 0, sense_b, noisy, verbose, &sense_cat); if (-1 == ret) ; else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; }
/* Invokes a SCSI LOG SENSE command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ int sg_ll_log_sense(int sg_fd, int ppc, int sp, int pc, int pg_code, int subpg_code, int paramp, unsigned char * resp, int mx_resp_len, int noisy, int verbose) { static const char * const cdb_name_s = "log sense"; int res, ret, k, sense_cat, resid; unsigned char logs_cdb[LOG_SENSE_CMDLEN] = {LOG_SENSE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; if (mx_resp_len > 0xffff) { pr2ws("mx_resp_len too big\n"); return -1; } logs_cdb[1] = (unsigned char)((ppc ? 2 : 0) | (sp ? 1 : 0)); logs_cdb[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); logs_cdb[3] = (unsigned char)(subpg_code & 0xff); sg_put_unaligned_be16((int16_t)paramp, logs_cdb + 5); sg_put_unaligned_be16((int16_t)mx_resp_len, logs_cdb + 7); if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < LOG_SENSE_CMDLEN; ++k) pr2ws("%02x ", logs_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, logs_cdb, sizeof(logs_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); resid = get_scsi_pt_resid(ptvp); destruct_scsi_pt_obj(ptvp); if (-1 == ret) ; else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else { if ((mx_resp_len > 3) && (ret < 4)) { /* resid indicates LOG SENSE response length bad, so zero it */ resp[2] = 0; resp[3] = 0; } ret = 0; } if (resid > 0) { if (resid > mx_resp_len) { pr2ws("%s: resid (%d) should never exceed requested len=%d\n", cdb_name_s, resid, mx_resp_len); return ret ? ret : SG_LIB_CAT_MALFORMED; } /* zero unfilled section of response buffer */ memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid); } return ret; }
/* Invokes a SCSI READ ATTRIBUTE command (SPC+SMC). Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ static int sg_ll_read_attr(int sg_fd, void * resp, int * residp, const struct opts_t * op) { int k, ret, res, sense_cat; int noisy = 1; unsigned char ra_cdb[SG_READ_ATTRIBUTE_CMDLEN] = {SG_READ_ATTRIBUTE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; ra_cdb[1] = 0x1f & op->sa; if (op->ea) sg_put_unaligned_be16(op->ea, ra_cdb + 2); if (op->lvn) ra_cdb[5] = 0xff & op->lvn; if (op->pn) ra_cdb[7] = 0xff & op->pn; if (op->fai) sg_put_unaligned_be16(op->fai, ra_cdb + 8); sg_put_unaligned_be32((uint32_t)op->maxlen, ra_cdb + 10); if (op->cache) ra_cdb[14] |= 0x1; if (op->verbose) { pr2serr(" Read attribute cdb: "); for (k = 0; k < SG_READ_ATTRIBUTE_CMDLEN; ++k) pr2serr("%02x ", ra_cdb[k]); pr2serr("\n"); } ptvp = construct_scsi_pt_obj(); if (NULL == ptvp) { pr2serr("%s: out of memory\n", __func__); return -1; } set_scsi_pt_cdb(ptvp, ra_cdb, sizeof(ra_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, op->maxlen); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, op->verbose); ret = sg_cmds_process_resp(ptvp, "read attribute", res, op->maxlen, sense_b, noisy, op->verbose, &sense_cat); if (-1 == ret) ; else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else ret = 0; if (residp) *residp = get_scsi_pt_resid(ptvp); destruct_scsi_pt_obj(ptvp); return ret; }
/* Invokes a SCSI WRITE AND VERIFY according with CDB. Returns 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ static int run_scsi_transaction(int sg_fd, const uint8_t *cdbp, int cdb_len, uint8_t *dop, int do_len, int timeout, bool noisy, int verbose) { int res, k, sense_cat, ret; struct sg_pt_base * ptvp; uint8_t sense_b[SENSE_BUFF_LEN]; char b[32]; snprintf(b, sizeof(b), "Write and verify(%d)", cdb_len); if (verbose) { pr2serr(" %s cdb: ", b); for (k = 0; k < cdb_len; ++k) pr2serr("%02x ", cdbp[k]); pr2serr("\n"); if ((verbose > 2) && dop && do_len) { pr2serr(" Data out buffer [%d bytes]:\n", do_len); hex2stderr(dop, do_len, -1); } } ptvp = construct_scsi_pt_obj(); if (NULL == ptvp) { pr2serr("%s: out of memory\n", b); return -1; } set_scsi_pt_cdb(ptvp, cdbp, cdb_len); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, dop, do_len); res = do_scsi_pt(ptvp, sg_fd, timeout, verbose); ret = sg_cmds_process_resp(ptvp, b, res, noisy, verbose, &sense_cat); if (-1 == ret) ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; case SG_LIB_CAT_MEDIUM_HARD: /* write or verify failed */ { bool valid; int slen; uint64_t ull = 0; slen = get_scsi_pt_sense_len(ptvp); valid = sg_get_sense_info_fld(sense_b, slen, &ull); if (valid) pr2serr("Medium or hardware error starting at lba=%" PRIu64 " [0x%" PRIx64 "]\n", ull, ull); } ret = sense_cat; break; case SG_LIB_CAT_PROTECTION: /* PI failure */ case SG_LIB_CAT_MISCOMPARE: /* only in bytchk=1 case */ default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; }
/* Invokes a SCSI MODE SENSE (10) command. Return of 0 -> success, * various SG_LIB_CAT_* positive values or -1 -> other errors */ 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) { static const char * const cdb_name_s = "mode sense(10)"; int res, ret, k, sense_cat, resid; unsigned char modes_cdb[MODE_SENSE10_CMDLEN] = {MODE_SENSE10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; modes_cdb[1] = (unsigned char)((dbd ? 0x8 : 0) | (llbaa ? 0x10 : 0)); modes_cdb[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f)); modes_cdb[3] = (unsigned char)(sub_pg_code & 0xff); sg_put_unaligned_be16((int16_t)mx_resp_len, modes_cdb + 7); if (mx_resp_len > 0xffff) { pr2ws("mx_resp_len too big\n"); return -1; } if (verbose) { pr2ws(" %s cdb: ", cdb_name_s); for (k = 0; k < MODE_SENSE10_CMDLEN; ++k) pr2ws("%02x ", modes_cdb[k]); pr2ws("\n"); } if (NULL == ((ptvp = create_pt_obj(cdb_name_s)))) return -1; set_scsi_pt_cdb(ptvp, modes_cdb, sizeof(modes_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (unsigned char *)resp, mx_resp_len); res = do_scsi_pt(ptvp, sg_fd, DEF_PT_TIMEOUT, verbose); ret = sg_cmds_process_resp(ptvp, cdb_name_s, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); resid = get_scsi_pt_resid(ptvp); destruct_scsi_pt_obj(ptvp); if (-1 == ret) ; else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else { if ((verbose > 2) && (ret > 0)) { pr2ws(" %s: response", cdb_name_s); if (3 == verbose) { pr2ws("%s:\n", (ret > 256 ? ", first 256 bytes" : "")); dStrHexErr((const char *)resp, (ret > 256 ? 256 : ret), -1); } else { pr2ws(":\n"); dStrHexErr((const char *)resp, ret, 0); } } ret = 0; } if (resid > 0) { if (resid > mx_resp_len) { pr2ws("%s: resid (%d) should never exceed requested len=%d\n", cdb_name_s, resid, mx_resp_len); return ret ? ret : SG_LIB_CAT_MALFORMED; } /* zero unfilled section of response buffer */ memset((unsigned char *)resp + (mx_resp_len - resid), 0, resid); } return ret; }
/* Returns number of TURs performed */ static int loop_turs(struct sg_pt_base * ptvp, struct loop_res_t * resp, struct opts_t * op) { int k, res; int vb = op->verbose; char b[80]; if (op->do_low) { int rs, n, sense_cat; uint8_t cdb[6]; uint8_t sense_b[32]; for (k = 0; k < op->do_number; ++k) { /* Might get Unit Attention on first invocation */ memset(cdb, 0, sizeof(cdb)); /* TUR's cdb is 6 zeros */ set_scsi_pt_cdb(ptvp, cdb, sizeof(cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); rs = do_scsi_pt(ptvp, -1, DEF_PT_TIMEOUT, vb); n = sg_cmds_process_resp(ptvp, "Test unit ready", rs, (0 == k), vb, &sense_cat); if (-1 == n) { resp->ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); return k; } else if (-2 == n) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: break; case SG_LIB_CAT_NOT_READY: ++resp->num_errs; if (1 == op->do_number) { resp->ret = sense_cat; printf("device not ready\n"); resp->reported = true; } break; case SG_LIB_CAT_UNIT_ATTENTION: ++resp->num_errs; if (vb) { pr2serr("Ignoring Unit attention (sense key)\n"); resp->reported = true; } break; default: ++resp->num_errs; if (1 == op->do_number) { resp->ret = sense_cat; sg_get_category_sense_str(sense_cat, sizeof(b), b, vb); printf("%s\n", b); resp->reported = true; return k; } break; } } clear_scsi_pt_obj(ptvp); } return k; } else { for (k = 0; k < op->do_number; ++k) { /* Might get Unit Attention on first invocation */ res = sg_ll_test_unit_ready_pt(ptvp, k, (0 == k), vb); if (res) { ++resp->num_errs; resp->ret = res; if (1 == op->do_number) { if (SG_LIB_CAT_NOT_READY == res) printf("device not ready\n"); else { sg_get_category_sense_str(res, sizeof(b), b, vb); printf("%s\n", b); } resp->reported = true; break; } } } return k; } }
static int do_write_same(int sg_fd, const struct opts_t * op, const void * dataoutp, int * act_cdb_lenp) { int k, ret, res, sense_cat, cdb_len; uint64_t llba; uint8_t ws_cdb[WRITE_SAME32_LEN]; uint8_t sense_b[SENSE_BUFF_LEN]; struct sg_pt_base * ptvp; cdb_len = op->pref_cdb_size; if (WRITE_SAME10_LEN == cdb_len) { llba = op->lba + op->numblocks; if ((op->numblocks > 0xffff) || (llba > UINT32_MAX) || op->ndob || (op->unmap && (! op->want_ws10))) { cdb_len = WRITE_SAME16_LEN; if (op->verbose) { const char * cp = "use WRITE SAME(16) instead of 10 byte " "cdb"; if (op->numblocks > 0xffff) pr2serr("%s since blocks exceed 65535\n", cp); else if (llba > UINT32_MAX) pr2serr("%s since LBA may exceed 32 bits\n", cp); else pr2serr("%s due to ndob or unmap settings\n", cp); } } } if (act_cdb_lenp) *act_cdb_lenp = cdb_len; memset(ws_cdb, 0, sizeof(ws_cdb)); switch (cdb_len) { case WRITE_SAME10_LEN: ws_cdb[0] = WRITE_SAME10_OP; ws_cdb[1] = ((op->wrprotect & 0x7) << 5); /* ANCHOR + UNMAP not allowed for WRITE_SAME10 in sbc3r24+r25 but * a proposal has been made to allow it. Anticipate approval. */ if (op->anchor) ws_cdb[1] |= 0x10; if (op->unmap) ws_cdb[1] |= 0x8; if (op->pbdata) ws_cdb[1] |= 0x4; if (op->lbdata) ws_cdb[1] |= 0x2; sg_put_unaligned_be32((uint32_t)op->lba, ws_cdb + 2); ws_cdb[6] = (op->grpnum & 0x1f); sg_put_unaligned_be16((uint16_t)op->numblocks, ws_cdb + 7); break; case WRITE_SAME16_LEN: ws_cdb[0] = WRITE_SAME16_OP; ws_cdb[1] = ((op->wrprotect & 0x7) << 5); if (op->anchor) ws_cdb[1] |= 0x10; if (op->unmap) ws_cdb[1] |= 0x8; if (op->pbdata) ws_cdb[1] |= 0x4; if (op->lbdata) ws_cdb[1] |= 0x2; if (op->ndob) ws_cdb[1] |= 0x1; sg_put_unaligned_be64(op->lba, ws_cdb + 2); sg_put_unaligned_be32((uint32_t)op->numblocks, ws_cdb + 10); ws_cdb[14] = (op->grpnum & 0x1f); break; case WRITE_SAME32_LEN: ws_cdb[0] = VARIABLE_LEN_OP; ws_cdb[6] = (op->grpnum & 0x1f); ws_cdb[7] = WRITE_SAME32_ADD; sg_put_unaligned_be16((uint16_t)WRITE_SAME32_SA, ws_cdb + 8); ws_cdb[10] = ((op->wrprotect & 0x7) << 5); if (op->anchor) ws_cdb[10] |= 0x10; if (op->unmap) ws_cdb[10] |= 0x8; if (op->pbdata) ws_cdb[10] |= 0x4; if (op->lbdata) ws_cdb[10] |= 0x2; if (op->ndob) ws_cdb[10] |= 0x1; sg_put_unaligned_be64(op->lba, ws_cdb + 12); sg_put_unaligned_be32((uint32_t)op->numblocks, ws_cdb + 28); break; default: pr2serr("do_write_same: bad cdb length %d\n", cdb_len); return -1; } if (op->verbose > 1) { pr2serr(" Write same(%d) cdb: ", cdb_len); for (k = 0; k < cdb_len; ++k) pr2serr("%02x ", ws_cdb[k]); pr2serr("\n Data-out buffer length=%d\n", op->xfer_len); } if ((op->verbose > 3) && (op->xfer_len > 0)) { pr2serr(" Data-out buffer contents:\n"); hex2stderr((const uint8_t *)dataoutp, op->xfer_len, 1); } ptvp = construct_scsi_pt_obj(); if (NULL == ptvp) { pr2serr("Write same(%d): out of memory\n", cdb_len); return -1; } set_scsi_pt_cdb(ptvp, ws_cdb, cdb_len); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_out(ptvp, (uint8_t *)dataoutp, op->xfer_len); res = do_scsi_pt(ptvp, sg_fd, op->timeout, op->verbose); ret = sg_cmds_process_resp(ptvp, "Write same", res, SG_NO_DATA_IN, sense_b, true /*noisy */, op->verbose, &sense_cat); if (-1 == ret) get_scsi_pt_os_err(ptvp); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; case SG_LIB_CAT_MEDIUM_HARD: { bool valid; int slen; uint64_t ull = 0; slen = get_scsi_pt_sense_len(ptvp); valid = sg_get_sense_info_fld(sense_b, slen, &ull); if (valid) pr2serr("Medium or hardware error starting at lba=%" PRIu64 " [0x%" PRIx64 "]\n", ull, ull); } ret = sense_cat; break; default: ret = sense_cat; break; } } else ret = 0; destruct_scsi_pt_obj(ptvp); return ret; }
/* Returns 0 on success, while positive values are SG_LIB_CAT_* errors * (e.g. SG_LIB_CAT_MALFORMED). If OS error, returns negated errno or -1. */ static int sg_ll_inquiry_com(struct sg_pt_base * ptvp, bool cmddt, bool evpd, int pg_op, void * resp, int mx_resp_len, int timeout_secs, int * residp, bool noisy, int verbose) { int res, ret, k, sense_cat, resid; uint8_t inq_cdb[INQUIRY_CMDLEN] = {INQUIRY_CMD, 0, 0, 0, 0, 0}; uint8_t sense_b[SENSE_BUFF_LEN]; uint8_t * up; if (cmddt) inq_cdb[1] |= 0x2; if (evpd) inq_cdb[1] |= 0x1; inq_cdb[2] = (uint8_t)pg_op; /* 16 bit allocation length (was 8, increased in spc3r09, 200209) */ sg_put_unaligned_be16((uint16_t)mx_resp_len, inq_cdb + 3); if (verbose) { pr2ws(" %s cdb: ", inquiry_s); for (k = 0; k < INQUIRY_CMDLEN; ++k) pr2ws("%02x ", inq_cdb[k]); pr2ws("\n"); } if (resp && (mx_resp_len > 0)) { up = (uint8_t *)resp; up[0] = 0x7f; /* defensive prefill */ if (mx_resp_len > 4) up[4] = 0; } if (timeout_secs <= 0) timeout_secs = DEF_PT_TIMEOUT; set_scsi_pt_cdb(ptvp, inq_cdb, sizeof(inq_cdb)); set_scsi_pt_sense(ptvp, sense_b, sizeof(sense_b)); set_scsi_pt_data_in(ptvp, (uint8_t *)resp, mx_resp_len); res = do_scsi_pt(ptvp, -1, timeout_secs, verbose); ret = sg_cmds_process_resp(ptvp, inquiry_s, res, mx_resp_len, sense_b, noisy, verbose, &sense_cat); resid = get_scsi_pt_resid(ptvp); if (residp) *residp = resid; if (-1 == ret) ret = sg_convert_errno(get_scsi_pt_os_err(ptvp)); else if (-2 == ret) { switch (sense_cat) { case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_NO_SENSE: ret = 0; break; default: ret = sense_cat; break; } } else if (ret < 4) { if (verbose) pr2ws("%s: got too few bytes (%d)\n", __func__, ret); ret = SG_LIB_CAT_MALFORMED; } else ret = 0; if (resid > 0) { if (resid > mx_resp_len) { pr2ws("%s resid (%d) should never exceed requested " "len=%d\n", inquiry_s, resid, mx_resp_len); return ret ? ret : SG_LIB_CAT_MALFORMED; } /* zero unfilled section of response buffer, based on resid */ memset((uint8_t *)resp + (mx_resp_len - resid), 0, resid); } return ret; }