diff options
author | Douglas Gilbert <dgilbert@interlog.com> | 2018-02-19 05:55:24 +0000 |
---|---|---|
committer | Douglas Gilbert <dgilbert@interlog.com> | 2018-02-19 05:55:24 +0000 |
commit | 39ea1b548f21c006c311ace897b221b100bfa88d (patch) | |
tree | 764ecee434b161b62c41182a6bc09ce950b85258 /src/sg_raw.c | |
parent | 5bdce507b09d32f452a390c22cb98ae7f840c151 (diff) | |
download | sg3_utils-39ea1b548f21c006c311ace897b221b100bfa88d.tar.gz |
sg_raw: add --raw option (for CF in binary); sg_lib: add sg_get_nvme_opcode_name() and sg_is_aligned(); sg_vpd: fully implement Device Constituents vpage
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@753 6180dd3e-e324-4e3e-922d-17de1ae2f315
Diffstat (limited to 'src/sg_raw.c')
-rw-r--r-- | src/sg_raw.c | 264 |
1 files changed, 176 insertions, 88 deletions
diff --git a/src/sg_raw.c b/src/sg_raw.c index 79cc6534..bea6ee69 100644 --- a/src/sg_raw.c +++ b/src/sg_raw.c @@ -33,16 +33,22 @@ #endif #include "sg_lib.h" #include "sg_pt.h" +#include "sg_pt_nvme.h" #include "sg_pr2serr.h" #include "sg_unaligned.h" -#define SG_RAW_VERSION "0.4.22 (2018-02-10)" +#define SG_RAW_VERSION "0.4.23 (2018-02-16)" #define DEFAULT_TIMEOUT 20 #define MIN_SCSI_CDBSZ 6 #define MAX_SCSI_CDBSZ 260 #define MAX_SCSI_DXLEN (64 * 1024) +#define NVME_ADDR_DATA_IN 0xfffffffffffffffe +#define NVME_ADDR_DATA_OUT 0xfffffffffffffffd +#define NVME_DATA_LEN_DATA_IN 0xfffffffe +#define NVME_DATA_LEN_DATA_OUT 0xfffffffd + static struct option long_options[] = { { "binary", no_argument, NULL, 'b' }, { "cmdfile", required_argument, NULL, 'c' }, @@ -52,6 +58,7 @@ static struct option long_options[] = { { "skip", required_argument, NULL, 'k' }, { "nosense", no_argument, NULL, 'n' }, { "outfile", required_argument, NULL, 'o' }, + { "raw", no_argument, NULL, 'w' }, { "request", required_argument, NULL, 'r' }, { "readonly", no_argument, NULL, 'R' }, { "send", required_argument, NULL, 's' }, @@ -74,6 +81,7 @@ struct opts_t { int datain_len; int dataout_len; int timeout; + int raw; int readonly; int verbose; off_t dataout_offset; @@ -119,6 +127,9 @@ usage() " --outfile=OFILE|-o OFILE Write binary data to OFILE (def: " "hexdump\n" " to stdout)\n" + " --raw|-w interpret CF (command file) as " + "binary (def:\n" + " interpret as ASCII hex)\n" " --readonly|-R Open DEVICE read-only (default: " "read-write)\n" " --request=RLEN|-r RLEN Request up to RLEN bytes of data " @@ -132,9 +143,9 @@ usage() " --version|-V Show version information and exit\n" "\n" "Between 6 and 260 command bytes (two hex digits each) can be " - "specified\nand will be sent to DEVICE. Lengths RLEN and SLEN " - "are decimal by\ndefault. Bidirectional commands accepted.\n\n" - "Simple example: Perform INQUIRY on /dev/sg0:\n" + "specified\nand will be sent to DEVICE. Lengths RLEN, SLEN and " + "KLEN are decimal by\ndefault. Bidirectional commands " + "accepted.\n\nSimple example: Perform INQUIRY on /dev/sg0:\n" " sg_raw -r 1k /dev/sg0 12 00 00 00 60 00\n"); } @@ -337,7 +348,7 @@ process_cl(struct opts_t * op, int argc, char *argv[]) while (1) { int c, n; - c = getopt_long(argc, argv, "bc:ehi:k:no:r:Rs:t:vV", long_options, + c = getopt_long(argc, argv, "bc:ehi:k:no:r:Rs:t:vVw", long_options, NULL); if (c == -1) break; @@ -417,6 +428,9 @@ process_cl(struct opts_t * op, int argc, char *argv[]) case 'V': op->do_version = true; break; + case 'w': /* -r and -R already in use, this is --raw */ + ++op->raw; + break; default: return SG_LIB_SYNTAX_ERROR; } @@ -449,22 +463,27 @@ process_cl(struct opts_t * op, int argc, char *argv[]) if (op->cmdfile_given) { bool ok; - ok = f2hex_arr(op->cmd_file, false /* as_binary */, + ok = f2hex_arr(op->cmd_file, (op->raw > 0) /* as_binary */, false /* no_space */, op->cdb, &op->cdb_length, MAX_SCSI_CDBSZ); if (! ok) return SG_LIB_SYNTAX_ERROR; + if (op->verbose > 2) { + pr2serr("Read %d from %s . They are in hex:\n", op->cdb_length, + op->cmd_file); + hex2stderr(op->cdb, op->cdb_length, -1); + } } if (op->cdb_length < MIN_SCSI_CDBSZ) { pr2serr("CDB too short (min. %d bytes)\n", MIN_SCSI_CDBSZ); return SG_LIB_SYNTAX_ERROR; } if (op->do_enumerate || (op->verbose > 1)) { - bool probable_scsi = sg_is_scsi_cdb(op->cdb, op->cdb_length); + bool is_scsi_cdb = sg_is_scsi_cdb(op->cdb, op->cdb_length); int sa; char b[80]; - if (probable_scsi) { + if (is_scsi_cdb) { if (op->cdb_length > 16) { sa = sg_get_unaligned_be16(op->cdb + 8); if ((0x7f != op->cdb[0]) && (0x7e != op->cdb[0])) @@ -475,7 +494,10 @@ process_cl(struct opts_t * op, int argc, char *argv[]) sa = op->cdb[1] & 0x1f; sg_get_opcode_sa_name(op->cdb[0], sa, 0, sizeof(b), b); printf("Attempt to decode cdb name: %s\n", b); - } + } else + printf(">>> Seems to be NVMe %s command\n", + sg_get_nvme_opcode_name(op->cdb[0], true /* admin */, + sizeof(b), b)); } return 0; } @@ -624,19 +646,23 @@ bail: int main(int argc, char *argv[]) { + bool is_scsi_cdb = true; int ret = 0; int err = 0; - int res_cat, status, slen, k, ret2; + int res_cat, status, s_len, k, ret2; int sg_fd = -1; + uint16_t sct_sc; + uint32_t result; struct sg_pt_base *ptvp = NULL; uint8_t sense_buffer[32]; - uint8_t * dxfer_buffer_in = NULL; - uint8_t * dxfer_buffer_out = NULL; + uint8_t * dinp = NULL; + uint8_t * doutp = NULL; uint8_t * free_buf_out = NULL; uint8_t * wrkBuf = NULL; struct opts_t opts; struct opts_t * op; char b[128]; + const int b_len = sizeof(b); op = &opts; memset(op, 0, sizeof(opts)); @@ -668,59 +694,114 @@ main(int argc, char *argv[]) ret = SG_LIB_CAT_OTHER; goto done; } - if (op->verbose) { - pr2serr(" cdb to send: "); - for (k = 0; k < op->cdb_length; ++k) - pr2serr("%02x ", op->cdb[k]); - pr2serr("\n"); - if (op->verbose > 1) { - sg_get_command_name(op->cdb, 0, sizeof(b) - 1, b); - b[sizeof(b) - 1] = '\0'; - pr2serr(" Command name: %s\n", b); - } - } - set_scsi_pt_cdb(ptvp, op->cdb, op->cdb_length); - if (op->verbose > 2) - pr2serr("sense_buffer=%p, length=%d\n", (void *)sense_buffer, - (int)sizeof(sense_buffer)); - set_scsi_pt_sense(ptvp, sense_buffer, sizeof(sense_buffer)); + is_scsi_cdb = sg_is_scsi_cdb(op->cdb, op->cdb_length); if (op->do_dataout) { - dxfer_buffer_out = fetch_dataout(op, &free_buf_out, &err); - if (dxfer_buffer_out == NULL) { + uint32_t dout_len; + + doutp = fetch_dataout(op, &free_buf_out, &err); + if (doutp == NULL) { ret = err; goto done; } + dout_len = op->dataout_len; if (op->verbose > 2) pr2serr("dxfer_buffer_out=%p, length=%d\n", - (void *)dxfer_buffer_out, op->dataout_len); - set_scsi_pt_data_out(ptvp, dxfer_buffer_out, op->dataout_len); + (void *)doutp, dout_len); + set_scsi_pt_data_out(ptvp, doutp, dout_len); + if (op->cmdfile_given) { + if (NVME_ADDR_DATA_OUT == + sg_get_unaligned_le64(op->cdb + SG_NVME_PT_ADDR)) + sg_put_unaligned_le64((uint64_t)(sg_uintptr_t)doutp, + op->cdb + SG_NVME_PT_ADDR); + if (NVME_DATA_LEN_DATA_OUT == + sg_get_unaligned_le32(op->cdb + SG_NVME_PT_DATA_LEN)) + sg_put_unaligned_le32(dout_len, + op->cdb + SG_NVME_PT_DATA_LEN); + } } if (op->do_datain) { - dxfer_buffer_in = sg_memalign(op->datain_len, 0 /* page_size */, - &wrkBuf, op->verbose > 3); - if (dxfer_buffer_in == NULL) { + uint32_t din_len = op->datain_len; + + dinp = sg_memalign(din_len, 0 /* page_size */, &wrkBuf, + op->verbose > 3); + if (dinp == NULL) { pr2serr("sg_memalign: failed to get %d bytes of memory\n", - op->datain_len); + din_len); ret = SG_LIB_OS_BASE_ERR + ENOMEM; goto done; } if (op->verbose > 2) - pr2serr("dxfer_buffer_in=%p, length=%d\n", - (void *)dxfer_buffer_in, op->datain_len); - set_scsi_pt_data_in(ptvp, dxfer_buffer_in, op->datain_len); + pr2serr("dxfer_buffer_in=%p, length=%d\n", (void *)dinp, din_len); + set_scsi_pt_data_in(ptvp, dinp, din_len); + if (op->cmdfile_given) { + if (NVME_ADDR_DATA_IN == + sg_get_unaligned_le64(op->cdb + SG_NVME_PT_ADDR)) + sg_put_unaligned_le64((uint64_t)(sg_uintptr_t)dinp, + op->cdb + SG_NVME_PT_ADDR); + if (NVME_DATA_LEN_DATA_IN == + sg_get_unaligned_le32(op->cdb + SG_NVME_PT_DATA_LEN)) + sg_put_unaligned_le32(din_len, + op->cdb + SG_NVME_PT_DATA_LEN); + } + } + if (op->verbose) { + pr2serr(" %s to send: ", is_scsi_cdb ? "cdb" : "cmd"); + if (is_scsi_cdb) { + for (k = 0; k < op->cdb_length; ++k) + pr2serr("%02x ", op->cdb[k]); + pr2serr("\n"); + if (op->verbose > 1) { + sg_get_command_name(op->cdb, 0, b_len - 1, b); + b[b_len - 1] = '\0'; + pr2serr(" Command name: %s\n", b); + } + } else { /* If not SCSI cdb then treat as NVMe command */ + pr2serr("\n"); + hex2stderr(op->cdb, op->cdb_length, -1); + if (op->verbose > 1) + pr2serr(" Command name: %s\n", + sg_get_nvme_opcode_name(op->cdb[0], true /* admin */, + b_len, b)); + } } + set_scsi_pt_cdb(ptvp, op->cdb, op->cdb_length); + if (op->verbose > 2) + pr2serr("sense_buffer=%p, length=%d\n", (void *)sense_buffer, + (int)sizeof(sense_buffer)); + set_scsi_pt_sense(ptvp, sense_buffer, sizeof(sense_buffer)); ret = do_scsi_pt(ptvp, sg_fd, op->timeout, op->verbose); if (ret > 0) { - if (SCSI_PT_DO_BAD_PARAMS == ret) { + switch (ret) { + case SCSI_PT_DO_BAD_PARAMS: pr2serr("do_scsi_pt: bad pass through setup\n"); ret = SG_LIB_CAT_OTHER; - } else if (SCSI_PT_DO_TIMEOUT == ret) { + break; + case SCSI_PT_DO_TIMEOUT: pr2serr("do_scsi_pt: timeout\n"); ret = SG_LIB_CAT_TIMEOUT; - } else + break; + case SCSI_PT_DO_NVME_STATUS: + sct_sc = (uint16_t)get_scsi_pt_status_response(ptvp); + pr2serr("NVMe Status: %s [0x%x]\n", + sg_get_nvme_cmd_status_str(sct_sc, b_len, b), sct_sc); + if (op->verbose) { + result = get_pt_result(ptvp); + pr2serr("NVMe Result=0x%x\n", result); + s_len = get_scsi_pt_sense_len(ptvp); + if ((op->verbose > 1) && (s_len > 0)) { + pr2serr("NVMe completion queue 4 DWords (as byte " + "string):\n"); + hex2stderr(sense_buffer, s_len, -1); + } + } + break; + default: + pr2serr("do_scsi_pt: unknown error: %d\n", ret); ret = SG_LIB_CAT_OTHER; + break; + } goto done; } else if (ret < 0) { int err; @@ -734,50 +815,57 @@ main(int argc, char *argv[]) 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); - pr2serr(">>> 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); - pr2serr(">>> os error: %s\n", b); - ret = SG_LIB_CAT_OTHER; - break; - default: - pr2serr(">>> unknown pass through result category (%d)\n", res_cat); - ret = SG_LIB_CAT_OTHER; - break; - } + s_len = get_scsi_pt_sense_len(ptvp); + if (is_scsi_cdb) { + res_cat = get_scsi_pt_result_category(ptvp); + switch (res_cat) { + case SCSI_PT_RESULT_GOOD: + ret = 0; + break; + case SCSI_PT_RESULT_SENSE: + ret = sg_err_category_sense(sense_buffer, s_len); + break; + case SCSI_PT_RESULT_TRANSPORT_ERR: + get_scsi_pt_transport_err_str(ptvp, b_len, b); + pr2serr(">>> transport error: %s\n", b); + ret = SG_LIB_CAT_OTHER; + break; + case SCSI_PT_RESULT_OS_ERR: + get_scsi_pt_os_err_str(ptvp, b_len, b); + pr2serr(">>> os error: %s\n", b); + ret = SG_LIB_CAT_OTHER; + break; + default: + pr2serr(">>> unknown pass through result category (%d)\n", + res_cat); + ret = SG_LIB_CAT_OTHER; + break; + } - status = get_scsi_pt_status_response(ptvp); - pr2serr("SCSI Status: "); - sg_print_scsi_status(status); - pr2serr("\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) - pr2serr(">>> Strange: status is CHECK CONDITION but no Sense " - "Information\n"); - else { - pr2serr("Sense Information:\n"); - sg_print_sense(NULL, sense_buffer, slen, (op->verbose > 0)); - pr2serr("\n"); + status = get_scsi_pt_status_response(ptvp); + pr2serr("SCSI Status: "); + sg_print_scsi_status(status); + pr2serr("\n\n"); + if ((SAM_STAT_CHECK_CONDITION == status) && (! op->no_sense)) { + if (0 == s_len) + pr2serr(">>> Strange: status is CHECK CONDITION but no Sense " + "Information\n"); + else { + pr2serr("Sense Information:\n"); + sg_print_sense(NULL, sense_buffer, s_len, (op->verbose > 0)); + pr2serr("\n"); + } + } + if (SAM_STAT_RESERVATION_CONFLICT == status) + ret = SG_LIB_CAT_RES_CONFLICT; + } else { /* NVMe command */ + result = get_pt_result(ptvp); + pr2serr("NVMe Result=0x%x\n", result); + if (op->verbose && (s_len > 0)) { + pr2serr("NVMe completion queue 4 DWords (as byte string):\n"); + hex2stderr(sense_buffer, s_len, -1); } } - 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); @@ -790,7 +878,7 @@ main(int argc, char *argv[]) } else { if (op->datain_file == NULL && !op->datain_binary) { pr2serr("Received %d bytes of data:\n", data_len); - hex2stderr(dxfer_buffer_in, data_len, 0); + hex2stderr(dinp, data_len, 0); } else { const char * cp = "stdout"; @@ -799,7 +887,7 @@ main(int argc, char *argv[]) ('-' == op->datain_file[0]))) cp = op->datain_file; pr2serr("Writing %d bytes of data to %s\n", data_len, cp); - ret2 = write_dataout(op->datain_file, dxfer_buffer_in, + ret2 = write_dataout(op->datain_file, dinp, data_len); if (0 != ret2) { if (0 == ret) @@ -811,8 +899,8 @@ main(int argc, char *argv[]) } done: - if (op->verbose) { - sg_get_category_sense_str(ret, sizeof(b), b, op->verbose - 1); + if (op->verbose && is_scsi_cdb) { + sg_get_category_sense_str(ret, b_len, b, op->verbose - 1); pr2serr("%s\n", b); } if (wrkBuf) @@ -823,5 +911,5 @@ done: destruct_scsi_pt_obj(ptvp); if (sg_fd >= 0) scsi_pt_close_device(sg_fd); - return ret; + return ret >= 0 ? ret : SG_LIB_CAT_OTHER; } |