static int sg_cmds_process_helper(const char * leadin, int mx_di_len, int resid, const uint8_t * sbp, int slen, bool noisy, int verbose, int * o_sense_cat) { int scat, got; bool n = false; bool check_data_in = false; char b[512]; scat = sg_err_category_sense(sbp, slen); switch (scat) { case SG_LIB_CAT_NOT_READY: case SG_LIB_CAT_INVALID_OP: case SG_LIB_CAT_ILLEGAL_REQ: case SG_LIB_LBA_OUT_OF_RANGE: case SG_LIB_CAT_ABORTED_COMMAND: case SG_LIB_CAT_COPY_ABORTED: case SG_LIB_CAT_DATA_PROTECT: case SG_LIB_CAT_PROTECTION: case SG_LIB_CAT_NO_SENSE: case SG_LIB_CAT_MISCOMPARE: n = false; break; case SG_LIB_CAT_RECOVERED: case SG_LIB_CAT_MEDIUM_HARD: check_data_in = true; #if defined(__GNUC__) #if (__GNUC__ >= 7) __attribute__((fallthrough)); /* FALL THROUGH */ #endif #endif case SG_LIB_CAT_UNIT_ATTENTION: case SG_LIB_CAT_SENSE: default: n = noisy; break; } if (verbose || n) { if (leadin && (strlen(leadin) > 0)) pr2ws("%s:\n", leadin); sg_get_sense_str(NULL, sbp, slen, (verbose > 1), sizeof(b), b); pr2ws("%s", b); if ((mx_di_len > 0) && (resid > 0)) { got = mx_di_len - resid; if ((verbose > 2) || check_data_in || (got > 0)) pr2ws(" %s requested %d bytes (data-in) but got %d " "bytes\n", pass_through_s, mx_di_len, got); } } if (o_sense_cat) *o_sense_cat = scat; return -2; }
int sg_err_category_new(int scsi_status, int host_status, int driver_status, const unsigned char * sense_buffer, int sb_len) { int masked_driver_status = (SG_LIB_DRIVER_MASK & driver_status); scsi_status &= 0x7e; if ((0 == scsi_status) && (0 == host_status) && (0 == masked_driver_status)) return SG_LIB_CAT_CLEAN; if ((SAM_STAT_CHECK_CONDITION == scsi_status) || (SAM_STAT_COMMAND_TERMINATED == scsi_status) || (SG_LIB_DRIVER_SENSE == masked_driver_status)) return sg_err_category_sense(sense_buffer, sb_len); if (0 != host_status) { if ((SG_LIB_DID_NO_CONNECT == host_status) || (SG_LIB_DID_BUS_BUSY == host_status) || (SG_LIB_DID_TIME_OUT == host_status)) return SG_LIB_CAT_TIMEOUT; } if (SG_LIB_DRIVER_TIMEOUT == masked_driver_status) return SG_LIB_CAT_TIMEOUT; return SG_LIB_CAT_OTHER; }
/* This is a helper function used by sg_cmds_* implementations after the * call to the pass-through. pt_res is returned from do_scsi_pt(). If valid * sense data is found it is decoded and output to sg_warnings_strm (def: * stderr); depending on the 'noisy' and 'verbose' settings. Returns -2 for * "sense" category (may not be fatal), -1 for failed, 0, or a positive * number. If 'mx_di_len > 0' then asks pass-through for resid and returns * (mx_di_len - resid); otherwise returns 0. So for data-in it should return * the actual number of bytes received. For data-out (to device) or no data * call with 'mx_di_len' set to 0 or less. If -2 returned then sense category * output via 'o_sense_cat' pointer (if not NULL). Note that several sense * categories also have data in bytes received; -2 is still returned. */ int sg_cmds_process_resp(struct sg_pt_base * ptvp, const char * leadin, int pt_res, int mx_di_len, const uint8_t * sbp, bool noisy, int verbose, int * o_sense_cat) { int got, cat, duration, slen, resid, resp_code, sstat; bool transport_sense; char b[1024]; if (NULL == leadin) leadin = ""; if (pt_res < 0) { #ifdef SG_LIB_LINUX if (verbose) pr2ws("%s: %s os error: %s\n", leadin, pass_through_s, safe_strerror(-pt_res)); if ((-ENXIO == pt_res) && o_sense_cat) { if (verbose > 2) pr2ws("map ENXIO to SG_LIB_CAT_NOT_READY\n"); *o_sense_cat = SG_LIB_CAT_NOT_READY; return -2; } else if (noisy && (0 == verbose)) pr2ws("%s: %s os error: %s\n", leadin, pass_through_s, safe_strerror(-pt_res)); #else if (noisy || verbose) pr2ws("%s: %s os error: %s\n", leadin, pass_through_s, safe_strerror(-pt_res)); #endif return -1; } else if (SCSI_PT_DO_BAD_PARAMS == pt_res) { pr2ws("%s: bad %s setup\n", leadin, pass_through_s); return -1; } else if (SCSI_PT_DO_TIMEOUT == pt_res) { pr2ws("%s: %s timeout\n", leadin, pass_through_s); return -1; } if ((verbose > 2) && ((duration = get_scsi_pt_duration_ms(ptvp)) >= 0)) pr2ws(" duration=%d ms\n", duration); resid = (mx_di_len > 0) ? get_scsi_pt_resid(ptvp) : 0; slen = get_scsi_pt_sense_len(ptvp); switch ((cat = get_scsi_pt_result_category(ptvp))) { case SCSI_PT_RESULT_GOOD: if (sbp && (slen > 7)) { resp_code = sbp[0] & 0x7f; /* SBC referrals can have status=GOOD and sense_key=COMPLETED */ if (resp_code >= 0x70) { if (resp_code < 0x72) { if (SPC_SK_NO_SENSE != (0xf & sbp[2])) sg_err_category_sense(sbp, slen); } else if (resp_code < 0x74) { if (SPC_SK_NO_SENSE != (0xf & sbp[1])) sg_err_category_sense(sbp, slen); } } } if (mx_di_len > 0) { got = mx_di_len - resid; if ((verbose > 1) && (resid != 0)) pr2ws(" %s: %s requested %d bytes (data-in) but got %d " "bytes\n", leadin, pass_through_s, mx_di_len, got); if (got >= 0) return got; else { if (verbose) pr2ws(" %s: %s can't get negative bytes, say it got " "none\n", leadin, pass_through_s); return 0; } } else return 0; case SCSI_PT_RESULT_STATUS: /* other than GOOD and CHECK CONDITION */ sstat = get_scsi_pt_status_response(ptvp); if (o_sense_cat) { switch (sstat) { case SAM_STAT_RESERVATION_CONFLICT: *o_sense_cat = SG_LIB_CAT_RES_CONFLICT; return -2; case SAM_STAT_CONDITION_MET: *o_sense_cat = SG_LIB_CAT_CONDITION_MET; return -2; case SAM_STAT_BUSY: *o_sense_cat = SG_LIB_CAT_BUSY; return -2; case SAM_STAT_TASK_SET_FULL: *o_sense_cat = SG_LIB_CAT_TS_FULL; return -2; case SAM_STAT_ACA_ACTIVE: *o_sense_cat = SG_LIB_CAT_ACA_ACTIVE; return -2; case SAM_STAT_TASK_ABORTED: *o_sense_cat = SG_LIB_CAT_TASK_ABORTED; return -2; default: break; } } if (verbose || noisy) { sg_get_scsi_status_str(sstat, sizeof(b), b); pr2ws("%s: scsi status: %s\n", leadin, b); } return -1; case SCSI_PT_RESULT_SENSE: return sg_cmds_process_helper(leadin, mx_di_len, resid, sbp, slen, noisy, verbose, o_sense_cat); case SCSI_PT_RESULT_TRANSPORT_ERR: if (verbose || noisy) { get_scsi_pt_transport_err_str(ptvp, sizeof(b), b); pr2ws("%s: transport: %s\n", leadin, b); } #ifdef SG_LIB_LINUX transport_sense = (slen > 0); #else transport_sense = ((SAM_STAT_CHECK_CONDITION == get_scsi_pt_status_response(ptvp)) && (slen > 0)); #endif if (transport_sense) return sg_cmds_process_helper(leadin, mx_di_len, resid, sbp, slen, noisy, verbose, o_sense_cat); else return -1; case SCSI_PT_RESULT_OS_ERR: if (verbose || noisy) { get_scsi_pt_os_err_str(ptvp, sizeof(b), b); pr2ws("%s: os: %s\n", leadin, b); } return -1; default: pr2ws("%s: unknown %s result category (%d)\n", leadin, pass_through_s, cat); return -1; } }
int main(int argc, char * argv[]) { int bsg_fd, k, ok; unsigned char inqCmdBlk[INQ_CMD_LEN] = {0x12, 0, 0, 0, INQ_REPLY_LEN, 0}; unsigned char sdiagCmdBlk[SDIAG_CMD_LEN] = {0x1d, 0, 0, 0, 0, 0}; unsigned char inqBuff[16][INQ_REPLY_LEN]; struct sg_io_v4 io_hdr[16]; struct sg_io_v4 rio_hdr; char * file_name = 0; char ebuff[EBUFF_SZ]; unsigned char sense_buffer[16][SENSE_BUFFER_LEN]; int q_at_tail = 0; for (k = 1; k < argc; ++k) { if (0 == memcmp("-t", argv[k], 2)) ++q_at_tail; else if (*argv[k] == '-') { printf("Unrecognized switch: %s\n", argv[k]); file_name = 0; break; } else if (0 == file_name) file_name = argv[k]; else { printf("too many arguments\n"); file_name = 0; break; } } if (0 == file_name) { printf("Usage: 'bsg_queue_tst [-t] <bsg_device>'\n" "where:\n -t queue_at_tail (def: q_at_head)\n"); return 1; } /* An access mode of O_RDWR is required for write()/read() interface */ if ((bsg_fd = open(file_name, O_RDWR)) < 0) { snprintf(ebuff, EBUFF_SZ, "bsg_queue_tst: error opening file: %s", file_name); perror(ebuff); return 1; } for (k = 0; k < 16; ++k) { /* Prepare INQUIRY command */ memset(&io_hdr[k], 0, sizeof(struct sg_io_v4)); io_hdr[k].guard = 'Q'; /* io_hdr[k].iovec_count = 0; */ /* memset takes care of this */ if (0 == (k % 3)) { io_hdr[k].request_len = sizeof(sdiagCmdBlk); io_hdr[k].request = (uint64_t)(long)sdiagCmdBlk; } else { io_hdr[k].request_len = sizeof(inqCmdBlk); io_hdr[k].request = (uint64_t)(long)inqCmdBlk; io_hdr[k].din_xfer_len = INQ_REPLY_LEN; io_hdr[k].din_xferp = (uint64_t)(long)inqBuff[k]; } io_hdr[k].response = (uint64_t)(long)sense_buffer[k]; io_hdr[k].max_response_len = SENSE_BUFFER_LEN; io_hdr[k].timeout = 20000; /* 20000 millisecs == 20 seconds */ io_hdr[k].usr_ptr = k; /* default is to queue at head (in SCSI mid level) */ if (q_at_tail) io_hdr[k].flags |= BSG_FLAG_Q_AT_TAIL; else io_hdr[k].flags |= BSG_FLAG_Q_AT_HEAD; if (write(bsg_fd, &io_hdr[k], sizeof(struct sg_io_v4)) < 0) { perror("bsg_queue_tst: bsg write error"); close(bsg_fd); return 1; } } /* sleep(3); */ for (k = 0; k < 16; ++k) { memset(&rio_hdr, 0, sizeof(struct sg_io_v4)); rio_hdr.guard = 'Q'; if (read(bsg_fd, &rio_hdr, sizeof(struct sg_io_v4)) < 0) { perror("bsg_queue_tst: bsg read error"); close(bsg_fd); return 1; } /* now for the error processing */ ok = 0; if (0 == rio_hdr.device_status) ok = 1; else { switch (sg_err_category_sense((unsigned char *)(long) rio_hdr.response, rio_hdr.response_len)) { case SG_LIB_CAT_CLEAN: ok = 1; break; case SG_LIB_CAT_RECOVERED: printf("Recovered error, continuing\n"); ok = 1; break; default: /* won't bother decoding other categories */ fprintf(stderr, "command error:\n"); sg_print_sense(NULL, (unsigned char *)(long)rio_hdr.response, rio_hdr.response_len, 1); break; } } if (ok) { /* output result if it is available */ /* if (0 == rio_hdr.pack_id) */ if (0 == (rio_hdr.usr_ptr % 3)) printf("SEND DIAGNOSTIC %d duration=%u\n", (int)rio_hdr.usr_ptr, rio_hdr.duration); else printf("INQUIRY %d duration=%u\n", (int)rio_hdr.usr_ptr, rio_hdr.duration); } } close(bsg_fd); return 0; }
int main(int argc, char *argv[]) { int ret = 0; int res_cat, status, slen, k, ret2; int sg_fd = -1; struct sg_pt_base *ptvp = NULL; unsigned char sense_buffer[32]; unsigned char * dxfer_buffer_in = NULL; unsigned char * dxfer_buffer_out = NULL; unsigned char *wrkBuf = NULL; struct opts_t opts; struct opts_t * op; char b[128]; op = &opts; memset(op, 0, sizeof(opts)); op->timeout = DEFAULT_TIMEOUT; ret = process_cl(op, argc, argv); if (ret != 0) { usage(); goto done; } else if (op->do_help) { usage(); goto done; } else if (op->do_version) { version(); goto done; } sg_fd = scsi_pt_open_device(op->device_name, op->readonly, op->do_verbose); if (sg_fd < 0) { fprintf(stderr, "%s: %s\n", op->device_name, safe_strerror(-sg_fd)); ret = SG_LIB_FILE_ERROR; goto done; } ptvp = construct_scsi_pt_obj(); if (ptvp == NULL) { fprintf(stderr, "out of memory\n"); ret = SG_LIB_CAT_OTHER; goto done; } if (op->do_verbose) { fprintf(stderr, " cdb to send: "); for (k = 0; k < op->cdb_length; ++k) fprintf(stderr, "%02x ", op->cdb[k]); fprintf(stderr, "\n"); if (op->do_verbose > 2) { sg_get_command_name(op->cdb, 0, sizeof(b) - 1, b); b[sizeof(b) - 1] = '\0'; fprintf(stderr, " Command name: %s\n", b); } } set_scsi_pt_cdb(ptvp, op->cdb, op->cdb_length); set_scsi_pt_sense(ptvp, sense_buffer, sizeof(sense_buffer)); if (op->do_dataout) { dxfer_buffer_out = fetch_dataout(op); if (dxfer_buffer_out == NULL) { ret = SG_LIB_CAT_OTHER; goto done; } set_scsi_pt_data_out(ptvp, dxfer_buffer_out, op->dataout_len); } if (op->do_datain) { dxfer_buffer_in = my_memalign(op->datain_len, &wrkBuf); if (dxfer_buffer_in == NULL) { perror("malloc"); ret = SG_LIB_CAT_OTHER; goto done; } set_scsi_pt_data_in(ptvp, dxfer_buffer_in, op->datain_len); } ret = do_scsi_pt(ptvp, sg_fd, op->timeout, op->do_verbose); if (ret > 0) { if (SCSI_PT_DO_BAD_PARAMS == ret) { fprintf(stderr, "do_scsi_pt: bad pass through setup\n"); ret = SG_LIB_CAT_OTHER; } else if (SCSI_PT_DO_TIMEOUT == ret) { fprintf(stderr, "do_scsi_pt: timeout\n"); ret = SG_LIB_CAT_TIMEOUT; } else ret = SG_LIB_CAT_OTHER; goto done; } else if (ret < 0) { fprintf(stderr, "do_scsi_pt: %s\n", safe_strerror(-ret)); ret = SG_LIB_CAT_OTHER; goto done; } slen = 0; res_cat = get_scsi_pt_result_category(ptvp); switch (res_cat) { case SCSI_PT_RESULT_GOOD: ret = 0; break; case SCSI_PT_RESULT_SENSE: slen = get_scsi_pt_sense_len(ptvp); ret = sg_err_category_sense(sense_buffer, slen); break; case SCSI_PT_RESULT_TRANSPORT_ERR: get_scsi_pt_transport_err_str(ptvp, sizeof(b), b); fprintf(stderr, ">>> transport error: %s\n", b); ret = SG_LIB_CAT_OTHER; break; case SCSI_PT_RESULT_OS_ERR: get_scsi_pt_os_err_str(ptvp, sizeof(b), b); fprintf(stderr, ">>> os error: %s\n", b); ret = SG_LIB_CAT_OTHER; break; default: fprintf(stderr, ">>> unknown pass through result category (%d)\n", res_cat); ret = SG_LIB_CAT_OTHER; break; } status = get_scsi_pt_status_response(ptvp); fprintf(stderr, "SCSI Status: "); sg_print_scsi_status(status); fprintf(stderr, "\n\n"); if ((SAM_STAT_CHECK_CONDITION == status) && (! op->no_sense)) { if (SCSI_PT_RESULT_SENSE != res_cat) slen = get_scsi_pt_sense_len(ptvp); if (0 == slen) fprintf(stderr, ">>> Strange: status is CHECK CONDITION but no " "Sense Information\n"); else { fprintf(stderr, "Sense Information:\n"); sg_print_sense(NULL, sense_buffer, slen, (op->do_verbose > 0)); fprintf(stderr, "\n"); } } if (SAM_STAT_RESERVATION_CONFLICT == status) ret = SG_LIB_CAT_RES_CONFLICT; if (op->do_datain) { int data_len = op->datain_len - get_scsi_pt_resid(ptvp); if (ret && !(SG_LIB_CAT_RECOVERED == ret || SG_LIB_CAT_NO_SENSE == ret)) fprintf(stderr, "Error %d occurred, no data received\n", ret); else if (data_len == 0) { fprintf(stderr, "No data received\n"); } else { if (op->datain_file == NULL && !op->datain_binary) { fprintf(stderr, "Received %d bytes of data:\n", data_len); dStrHexErr((const char *)dxfer_buffer_in, data_len, 0); } else { const char * cp = "stdout"; if (op->datain_file && ! ((1 == strlen(op->datain_file)) && ('-' == op->datain_file[0]))) cp = op->datain_file; fprintf(stderr, "Writing %d bytes of data to %s\n", data_len, cp); ret2 = write_dataout(op->datain_file, dxfer_buffer_in, data_len); if (0 != ret2) { if (0 == ret) ret = ret2; goto done; } } } } done: if (op->do_verbose) { sg_get_category_sense_str(ret, sizeof(b), b, op->do_verbose - 1); fprintf(stderr, "%s\n", b); } if (wrkBuf) free(wrkBuf); if (ptvp) destruct_scsi_pt_obj(ptvp); if (sg_fd >= 0) scsi_pt_close_device(sg_fd); return ret; }