aboutsummaryrefslogtreecommitdiff
path: root/src/sg_opcodes.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sg_opcodes.c')
-rw-r--r--src/sg_opcodes.c1500
1 files changed, 1500 insertions, 0 deletions
diff --git a/src/sg_opcodes.c b/src/sg_opcodes.c
new file mode 100644
index 00000000..9a5e3b83
--- /dev/null
+++ b/src/sg_opcodes.c
@@ -0,0 +1,1500 @@
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ * Copyright (C) 2004-2022 D. Gilbert
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This program outputs information provided by a SCSI REPORT SUPPORTED
+ * OPERATION CODES [0xa3/0xc] (RSOC) and REPORT SUPPORTED TASK MANAGEMENT
+ * FUNCTIONS [0xa3/0xd] (RSTMF) commands.
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_extra.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+#include "sg_pt.h"
+
+static const char * version_str = "0.86 20221005"; /* spc6r06 */
+
+#define MY_NAME "sg_opcodes"
+
+#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
+#define DEF_TIMEOUT_SECS 60
+
+#define SG_MAINTENANCE_IN 0xa3
+#define RSOC_SA 0xc
+#define RSTMF_SA 0xd
+#define RSOC_CMD_LEN 12
+#define RSTMF_CMD_LEN 12
+#define MX_ALLOC_LEN 8192
+
+#define NAME_BUFF_SZ 128
+
+#define SEAGATE_READ_UDS_DATA_CMD 0xf7 /* may start reporting vendor cmds */
+
+static int peri_dtype = -1; /* ugly but not easy to pass to alpha compare */
+static bool no_final_msg = false;
+
+static struct option long_options[] = {
+ {"alpha", no_argument, 0, 'a'},
+ {"compact", no_argument, 0, 'c'},
+ {"enumerate", no_argument, 0, 'e'},
+ {"help", no_argument, 0, 'h'},
+ {"hex", no_argument, 0, 'H'},
+ {"inhex", required_argument, 0, 'i'},
+ {"in", required_argument, 0, 'i'},
+ {"json", optional_argument, 0, 'j'},
+ {"mask", no_argument, 0, 'm'},
+ {"mlu", no_argument, 0, 'M'}, /* added in spc5r20 */
+ {"no-inquiry", no_argument, 0, 'n'},
+ {"no_inquiry", no_argument, 0, 'n'},
+ {"new", no_argument, 0, 'N'},
+ {"opcode", required_argument, 0, 'o'},
+ {"old", no_argument, 0, 'O'},
+ {"pdt", required_argument, 0, 'p'},
+ {"raw", no_argument, 0, 'r'},
+ {"rctd", no_argument, 0, 'R'},
+ {"repd", no_argument, 0, 'q'},
+ {"sa", required_argument, 0, 's'},
+ {"tmf", no_argument, 0, 't'},
+ {"unsorted", no_argument, 0, 'u'},
+ {"verbose", no_argument, 0, 'v'},
+ {"version", no_argument, 0, 'V'},
+ {0, 0, 0, 0},
+};
+
+struct opts_t {
+ bool do_alpha;
+ bool do_compact;
+ bool do_enumerate;
+ bool no_inquiry;
+ bool do_mask;
+ bool do_mlu;
+ bool do_raw;
+ bool do_rctd; /* Return command timeout descriptor */
+ bool do_repd;
+ bool do_unsorted;
+ bool do_taskman;
+ bool opt_new;
+ bool verbose_given;
+ bool version_given;
+ int do_help;
+ int do_hex;
+ int opcode;
+ int servact;
+ int verbose;
+ const char * device_name;
+ const char * inhex_fn;
+ sgj_state json_st;
+};
+
+
+static void
+usage()
+{
+ pr2serr("Usage: sg_opcodes [--alpha] [--compact] [--enumerate] "
+ "[--help] [--hex]\n"
+ " [--inhex=FN] [--json[=JO]] [--mask] [--mlu] "
+ "[--no-inquiry]\n"
+ " [--opcode=OP[,SA]] [--pdt=DT] [--raw] "
+ "[--rctd]\n"
+ " [--repd] [--sa=SA] [--tmf] [--unsorted] "
+ "[--verbose]\n"
+ " [--version] DEVICE\n"
+ " where:\n"
+ " --alpha|-a output list of operation codes sorted "
+ "alphabetically\n"
+ " --compact|-c more compact output\n"
+ " --enumerate|-e use '--opcode=' and '--pdt=' to look up "
+ "name,\n"
+ " ignore DEVICE\n"
+ " --help|-h print usage message then exit\n"
+ " --hex|-H output response in hex, use -HHH for "
+ "hex\n"
+ " suitable for later use of --inhex= "
+ "option\n"
+ " --inhex=FN|-i FN contents of file FN treated as hex "
+ "and used\n"
+ " instead of DEVICE which is ignored\n"
+ " --json[=JO]|-jJO output in JSON instead of human "
+ "readable\n"
+ " test. Use --json=? for JSON help\n"
+ " --mask|-m show cdb usage data (a mask) when "
+ "all listed\n"
+ " --mlu|-M show MLU bit when all listed\n"
+ " --no-inquiry|-n don't output INQUIRY information\n"
+ " --opcode=OP[,SA]|-o OP[,SA] opcode (OP) and service "
+ "action (SA)\n"
+ " --pdt=DT|-p DT give peripheral device type for "
+ "'--no-inquiry'\n"
+ " '--enumerate'\n"
+ " --raw|-r output response in binary to stdout unless "
+ "--inhex=FN\n"
+ " is given then FN is parsed as binary "
+ "instead\n"
+ " --rctd|-R set RCTD (return command timeout "
+ "descriptor) bit\n"
+ " --repd|-q set Report Extended Parameter Data bit, "
+ "with --tmf\n"
+ " --sa=SA|-s SA service action in addition to opcode\n"
+ " --tmf|-t output list of supported task management "
+ "functions\n"
+ " --unsorted|-u output list of operation codes as is\n"
+ " (def: sort by opcode (then service "
+ "action))\n"
+ " --verbose|-v increase verbosity\n"
+ " --old|-O use old interface (use as first option)\n"
+ " --version|-V print version string then exit\n\n"
+ "Performs a SCSI REPORT SUPPORTED OPERATION CODES or a REPORT "
+ "SUPPORTED\nTASK MANAGEMENT FUNCTIONS command. All values are "
+ "in decimal by default,\nprefix with '0x' or add a trailing 'h' "
+ "for hex numbers.\n");
+}
+
+static void
+usage_old()
+{
+ pr2serr("Usage: sg_opcodes [-a] [-c] [-e] [-H] [-j] [-m] [-M] [-n] "
+ "[-o=OP]\n"
+ " [-p=DT] [-q] [-r] [-R] [-s=SA] [-t] [-u] "
+ "[-v] [-V]\n"
+ " DEVICE\n"
+ " where:\n"
+ " -a output list of operation codes sorted "
+ "alphabetically\n"
+ " -c more compact output\n"
+ " -e use '--opcode=' and '--pdt=' to look up name, "
+ "ignore DEVICE\n"
+ " -H print response in hex\n"
+ " -j print response in JSON\n"
+ " -m show cdb usage data (a mask) when all listed\n"
+ " -M show MLU bit when all listed\n"
+ " -n don't output INQUIRY information\n"
+ " -o=OP first byte of command to query (in hex)\n"
+ " -p=DT alternate source of pdt (normally obtained from "
+ "inquiry)\n"
+ " -q set REPD bit for tmf_s\n"
+ " -r output response in binary to stdout\n"
+ " -R set RCTD (return command timeout "
+ "descriptor) bit\n"
+ " -s=SA in addition to opcode (in hex)\n"
+ " -t output list of supported task management functions\n"
+ " -u output list of operation codes as is (unsorted)\n"
+ " -v verbose\n"
+ " -V output version string\n"
+ " -N|--new use new interface\n"
+ " -? output this usage message\n\n"
+ "Performs a SCSI REPORT SUPPORTED OPERATION CODES (or a REPORT "
+ "TASK MANAGEMENT\nFUNCTIONS) command\n");
+}
+
+static const char * const rsoc_s = "Report supported operation codes";
+
+static int
+do_rsoc(struct sg_pt_base * ptvp, bool rctd, int rep_opts, int rq_opcode,
+ int rq_servact, void * resp, int mx_resp_len, int * act_resp_lenp,
+ bool noisy, int verbose)
+{
+ int ret, res, sense_cat;
+ uint8_t rsoc_cdb[RSOC_CMD_LEN] = {SG_MAINTENANCE_IN, RSOC_SA, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
+
+ if (rctd)
+ rsoc_cdb[2] |= 0x80;
+ if (rep_opts)
+ rsoc_cdb[2] |= (rep_opts & 0x7);
+ if (rq_opcode > 0)
+ rsoc_cdb[3] = (rq_opcode & 0xff);
+ if (rq_servact > 0)
+ sg_put_unaligned_be16((uint16_t)rq_servact, rsoc_cdb + 4);
+ if (act_resp_lenp)
+ *act_resp_lenp = 0;
+ sg_put_unaligned_be32((uint32_t)mx_resp_len, rsoc_cdb + 6);
+
+ if (verbose) {
+ char b[128];
+
+ pr2serr(" %s cdb: %s\n", rsoc_s,
+ sg_get_command_str(rsoc_cdb, RSOC_CMD_LEN, false,
+ sizeof(b), b));
+ }
+ clear_scsi_pt_obj(ptvp);
+ set_scsi_pt_cdb(ptvp, rsoc_cdb, sizeof(rsoc_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, DEF_TIMEOUT_SECS, verbose);
+ ret = sg_cmds_process_resp(ptvp, rsoc_s, res, noisy, verbose, &sense_cat);
+ if (-1 == ret) {
+ if (get_scsi_pt_transport_err(ptvp))
+ ret = SG_LIB_TRANSPORT_ERROR;
+ else
+ 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 (act_resp_lenp)
+ *act_resp_lenp = ret;
+ if ((verbose > 2) && (ret > 0)) {
+ pr2serr("%s response:\n", rsoc_s);
+ hex2stderr((const uint8_t *)resp, ret, 1);
+ }
+ ret = 0;
+ }
+ return ret;
+}
+
+static const char * const rstmf_s = "Report supported task management "
+ "functions";
+
+static int
+do_rstmf(struct sg_pt_base * ptvp, bool repd, void * resp, int mx_resp_len,
+ int * act_resp_lenp, bool noisy, int verbose)
+{
+ int ret, res, sense_cat;
+ uint8_t rstmf_cdb[RSTMF_CMD_LEN] = {SG_MAINTENANCE_IN, RSTMF_SA,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ uint8_t sense_b[SENSE_BUFF_LEN] SG_C_CPP_ZERO_INIT;
+
+ if (repd)
+ rstmf_cdb[2] = 0x80;
+ if (act_resp_lenp)
+ *act_resp_lenp = 0;
+ sg_put_unaligned_be32((uint32_t)mx_resp_len, rstmf_cdb + 6);
+
+ if (verbose) {
+ char b[128];
+
+ pr2serr(" %s cdb: %s\n", rstmf_s,
+ sg_get_command_str(rstmf_cdb, RSTMF_CMD_LEN, false,
+ sizeof(b), b));
+ }
+ clear_scsi_pt_obj(ptvp);
+ set_scsi_pt_cdb(ptvp, rstmf_cdb, sizeof(rstmf_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, DEF_TIMEOUT_SECS, verbose);
+ ret = sg_cmds_process_resp(ptvp, rstmf_s, res, noisy, verbose,
+ &sense_cat);
+ if (-1 == ret) {
+ if (get_scsi_pt_transport_err(ptvp))
+ ret = SG_LIB_TRANSPORT_ERROR;
+ else
+ 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 (act_resp_lenp)
+ *act_resp_lenp = ret;
+ if ((verbose > 2) && (ret > 0)) {
+ pr2serr("%s response:\n", rstmf_s);
+ hex2stderr((const uint8_t *)resp, ret, 1);
+ }
+ ret = 0;
+ }
+ return ret;
+}
+
+static int
+new_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
+{
+ int c, n;
+ char * cp;
+ char b[32];
+
+ while (1) {
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "acehHi:j::mMnNo:Op:qrRs:tuvV",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'a':
+ op->do_alpha = true;
+ break;
+ case 'c':
+ op->do_compact = true;
+ break;
+ case 'e':
+ op->do_enumerate = true;
+ break;
+ case 'h':
+ case '?':
+ ++op->do_help;
+ break;
+ case 'H':
+ ++op->do_hex;
+ break;
+ case 'i':
+ op->inhex_fn = optarg;
+ break;
+ case 'j':
+ if (! sgj_init_state(&op->json_st, optarg)) {
+ int bad_char = op->json_st.first_bad_char;
+ char e[1500];
+
+ if (bad_char) {
+ pr2serr("bad argument to --json= option, unrecognized "
+ "character '%c'\n\n", bad_char);
+ }
+ sg_json_usage(0, e, sizeof(e));
+ pr2serr("%s", e);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'm':
+ op->do_mask = true;
+ break;
+ case 'M':
+ op->do_mlu = true;
+ break;
+ case 'n':
+ op->no_inquiry = true;
+ break;
+ case 'N':
+ break; /* ignore */
+ case 'o':
+ if (strlen(optarg) >= (sizeof(b) - 1)) {
+ pr2serr("argument to '--opcode' too long\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ cp = strchr(optarg, ',');
+ if (cp) {
+ memset(b, 0, sizeof(b));
+ strncpy(b, optarg, cp - optarg);
+ n = sg_get_num(b);
+ if ((n < 0) || (n > 255)) {
+ pr2serr("bad OP argument to '--opcode'\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ op->opcode = n;
+ n = sg_get_num(cp + 1);
+ if ((n < 0) || (n > 0xffff)) {
+ pr2serr("bad SA argument to '--opcode'\n");
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ op->servact = n;
+ } else {
+ n = sg_get_num(optarg);
+ if ((n < 0) || (n > 255)) {
+ pr2serr("bad argument to '--opcode'\n");
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ op->opcode = n;
+ }
+ break;
+ case 'O':
+ op->opt_new = false;
+ return 0;
+ case 'p':
+ n = -2;
+ if (isdigit((uint8_t)optarg[0]))
+ n = sg_get_num(optarg);
+ else if ((2 == strlen(optarg)) && (0 == strcmp("-1", optarg)))
+ n = -1;
+ if ((n < -1) || (n > PDT_MAX)) {
+ pr2serr("bad argument to '--pdt=DT', expect -1 to 31\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ peri_dtype = n;
+ break;
+ case 'q':
+ op->do_repd = true;
+ break;
+ case 'r':
+ op->do_raw = true;
+ break;
+ case 'R':
+ op->do_rctd = true;
+ break;
+ case 's':
+ n = sg_get_num(optarg);
+ if ((n < 0) || (n > 0xffff)) {
+ pr2serr("bad argument to '--sa'\n");
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ op->servact = n;
+ break;
+ case 't':
+ op->do_taskman = true;
+ break;
+ case 'u':
+ op->do_unsorted = true;
+ break;
+ case 'v':
+ op->verbose_given = true;
+ ++op->verbose;
+ break;
+ case 'V':
+ op->version_given = true;
+ break;
+ default:
+ pr2serr("unrecognised option code %c [0x%x]\n", c, c);
+ if (op->do_help)
+ break;
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+ if (optind < argc) {
+ if (NULL == op->device_name) {
+ op->device_name = argv[optind];
+ ++optind;
+ }
+ if (optind < argc) {
+ for (; optind < argc; ++optind)
+ pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+ return 0;
+}
+
+static int
+old_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
+{
+ bool jmp_out;
+ int k, plen, n, num;
+ const char * cp;
+
+ for (k = 1; k < argc; ++k) {
+ cp = argv[k];
+ plen = strlen(cp);
+ if (plen <= 0)
+ continue;
+ if ('-' == *cp) {
+ for (--plen, ++cp, jmp_out = false; plen > 0; --plen, ++cp) {
+ switch (*cp) {
+ case 'a':
+ op->do_alpha = true;
+ break;
+ case 'c':
+ op->do_compact = true;
+ break;
+ case 'e':
+ op->do_enumerate = true;
+ break;
+ case 'H':
+ ++op->do_hex;
+ break;
+ case 'j': /* don't accept argument with this old syntax */
+ sgj_init_state(&op->json_st, NULL);
+ break;
+ case 'm':
+ op->do_mask = true;
+ break;
+ case 'M':
+ op->do_mlu = true;
+ break;
+ case 'n':
+ op->no_inquiry = true;
+ break;
+ case 'N':
+ op->opt_new = true;
+ return 0;
+ case 'O':
+ break;
+ case 'q':
+ op->do_repd = true;
+ break;
+ case 'r':
+ op->do_raw = true;
+ break;
+ case 'R':
+ op->do_rctd = true;
+ break;
+ case 't':
+ op->do_taskman = true;
+ break;
+ case 'u':
+ op->do_unsorted = true;
+ break;
+ case 'v':
+ op->verbose_given = true;
+ ++op->verbose;
+ break;
+ case 'V':
+ op->version_given = true;
+ break;
+ case 'h':
+ case '?':
+ ++op->do_help;
+ break;
+ default:
+ jmp_out = true;
+ break;
+ }
+ if (jmp_out)
+ break;
+ }
+ if (plen <= 0)
+ continue;
+ if (0 == strncmp("i=", cp, 2))
+ op->inhex_fn = cp + 2;
+ else if (0 == strncmp("o=", cp, 2)) {
+ num = sscanf(cp + 2, "%x", (unsigned int *)&n);
+ if ((1 != num) || (n > 255)) {
+ pr2serr("Bad number after 'o=' option\n");
+ usage_old();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ op->opcode = n;
+ } else if (0 == strncmp("p=", cp, 2)) {
+ num = sscanf(cp + 2, "%d", &n);
+ if ((1 != num) || (n > PDT_MAX) || (n < -1)) {
+ pr2serr("Bad number after 'p=' option, expect -1 to "
+ "31\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ peri_dtype = n;
+ } else if (0 == strncmp("s=", cp, 2)) {
+ num = sscanf(cp + 2, "%x", (unsigned int *)&n);
+ if (1 != num) {
+ pr2serr("Bad number after 's=' option\n");
+ usage_old();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ op->servact = n;
+ } else if (0 == strncmp("-old", cp, 4))
+ ;
+ else if (jmp_out) {
+ pr2serr("Unrecognized option: %s\n", cp);
+ usage_old();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ } else if (NULL == op->device_name)
+ op->device_name = cp;
+ else {
+ pr2serr("too many arguments, got: %s, not expecting: %s\n",
+ op->device_name, cp);
+ usage_old();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+ return 0;
+}
+
+static int
+parse_cmd_line(struct opts_t * op, int argc, char * argv[])
+{
+ int res;
+ char * cp;
+
+ cp = getenv("SG3_UTILS_OLD_OPTS");
+ if (cp) {
+ op->opt_new = false;
+ res = old_parse_cmd_line(op, argc, argv);
+ if ((0 == res) && op->opt_new)
+ res = new_parse_cmd_line(op, argc, argv);
+ } else {
+ op->opt_new = true;
+ res = new_parse_cmd_line(op, argc, argv);
+ if ((0 == res) && (! op->opt_new))
+ res = old_parse_cmd_line(op, argc, argv);
+ }
+ return res;
+}
+
+static void
+dStrRaw(const char * str, int len)
+{
+ int k;
+
+ for (k = 0; k < len; ++k)
+ printf("%c", str[k]);
+}
+
+/* returns -1 when left < right, 0 when left == right, else returns 1 */
+static int
+opcode_num_compare(const void * left, const void * right)
+{
+ int l_serv_act = 0;
+ int r_serv_act = 0;
+ int l_opc, r_opc;
+ const uint8_t * ll = *(uint8_t **)left;
+ const uint8_t * rr = *(uint8_t **)right;
+
+ if (NULL == ll)
+ return -1;
+ if (NULL == rr)
+ return -1;
+ l_opc = ll[0];
+ if (ll[5] & 1)
+ l_serv_act = sg_get_unaligned_be16(ll + 2);
+ r_opc = rr[0];
+ if (rr[5] & 1)
+ r_serv_act = sg_get_unaligned_be16(rr + 2);
+ if (l_opc < r_opc)
+ return -1;
+ if (l_opc > r_opc)
+ return 1;
+ if (l_serv_act < r_serv_act)
+ return -1;
+ if (l_serv_act > r_serv_act)
+ return 1;
+ return 0;
+}
+
+/* returns -1 when left < right, 0 when left == right, else returns 1 */
+static int
+opcode_alpha_compare(const void * left, const void * right)
+{
+ const uint8_t * ll = *(uint8_t **)left;
+ const uint8_t * rr = *(uint8_t **)right;
+ int l_serv_act = 0;
+ int r_serv_act = 0;
+ char l_name_buff[NAME_BUFF_SZ];
+ char r_name_buff[NAME_BUFF_SZ];
+ int l_opc, r_opc;
+
+ if (NULL == ll)
+ return -1;
+ if (NULL == rr)
+ return -1;
+ l_opc = ll[0];
+ if (ll[5] & 1)
+ l_serv_act = sg_get_unaligned_be16(ll + 2);
+ l_name_buff[0] = '\0';
+ sg_get_opcode_sa_name(l_opc, l_serv_act, peri_dtype,
+ NAME_BUFF_SZ, l_name_buff);
+ r_opc = rr[0];
+ if (rr[5] & 1)
+ r_serv_act = sg_get_unaligned_be16(rr + 2);
+ r_name_buff[0] = '\0';
+ sg_get_opcode_sa_name(r_opc, r_serv_act, peri_dtype,
+ NAME_BUFF_SZ, r_name_buff);
+ return strncmp(l_name_buff, r_name_buff, NAME_BUFF_SZ);
+}
+
+/* For decoding a RSOC command's "All_commands" parameter data */
+static int
+list_all_codes(uint8_t * rsoc_buff, int rsoc_len, struct opts_t * op,
+ struct sg_pt_base * ptvp)
+{
+ bool sa_v;
+ int k, j, m, n, cd_len, serv_act, len, act_len, opcode, res;
+ uint8_t byt5;
+ unsigned int timeout;
+ uint8_t * bp;
+ uint8_t ** sort_arr = NULL;
+ sgj_state * jsp = &op->json_st;
+ sgj_opaque_p jap = NULL;
+ sgj_opaque_p jop = NULL;
+ char name_buff[NAME_BUFF_SZ];
+ char sa_buff[8];
+ char b[192];
+ const int blen = sizeof(b);
+
+ cd_len = sg_get_unaligned_be32(rsoc_buff + 0);
+ if (cd_len > (rsoc_len - 4)) {
+ sgj_pr_hr(jsp, "sg_opcodes: command data length=%d, allocation=%d; "
+ "truncate\n", cd_len, rsoc_len - 4);
+ cd_len = ((rsoc_len - 4) / 8) * 8;
+ }
+ if (0 == cd_len) {
+ sgj_pr_hr(jsp, "sg_opcodes: no commands to display\n");
+ return 0;
+ }
+ if (op->do_rctd) { /* Return command timeout descriptor */
+ if (op->do_compact) {
+ sgj_pr_hr(jsp, "\nOpcode,sa Nominal Recommended Name\n");
+ sgj_pr_hr(jsp, " (hex) timeout timeout(sec) \n");
+ sgj_pr_hr(jsp, "-----------------------------------------------"
+ "---------\n");
+ } else {
+ sgj_pr_hr(jsp, "\nOpcode Service CDB Nominal Recommended "
+ "Name\n");
+ sgj_pr_hr(jsp, "(hex) action(h) size timeout timeout(sec) "
+ " \n");
+ sgj_pr_hr(jsp, "-------------------------------------------------"
+ "---------------\n");
+ }
+ } else { /* RCTD clear in cdb */
+ if (op->do_compact) {
+ sgj_pr_hr(jsp, "\nOpcode,sa Name\n");
+ sgj_pr_hr(jsp, " (hex) \n");
+ sgj_pr_hr(jsp, "---------------------------------------\n");
+ } else if (op->do_mlu) {
+ sgj_pr_hr(jsp, "\nOpcode Service CDB MLU Name\n");
+ sgj_pr_hr(jsp, "(hex) action(h) size \n");
+ sgj_pr_hr(jsp, "-------------------------------------------"
+ "----\n");
+ } else {
+ sgj_pr_hr(jsp, "\nOpcode Service CDB RWCDLP, Name\n");
+ sgj_pr_hr(jsp, "(hex) action(h) size CDLP \n");
+ sgj_pr_hr(jsp, "-------------------------------------------"
+ "----\n");
+ }
+ }
+ /* SPC-4 does _not_ require any ordering of opcodes in the response */
+ if (! op->do_unsorted) {
+ sort_arr = (uint8_t **)calloc(cd_len, sizeof(uint8_t *));
+ if (NULL == sort_arr) {
+ pr2serr("sg_opcodes: no memory to sort operation codes, "
+ "try '-u'\n");
+ return sg_convert_errno(ENOMEM);
+ }
+ memset(sort_arr, 0, cd_len * sizeof(uint8_t *));
+ bp = rsoc_buff + 4;
+ for (k = 0, j = 0; k < cd_len; ++j, k += len, bp += len) {
+ sort_arr[j] = bp;
+ len = (bp[5] & 0x2) ? 20 : 8;
+ }
+ qsort(sort_arr, j, sizeof(uint8_t *),
+ (op->do_alpha ? opcode_alpha_compare : opcode_num_compare));
+ }
+
+ jap = sgj_named_subarray_r(jsp, jsp->basep, "all_command_descriptor");
+ for (k = 0, j = 0; k < cd_len; ++j, k += len) {
+ jop = sgj_new_unattached_object_r(jsp);
+
+ bp = op->do_unsorted ? (rsoc_buff + 4 + k) : sort_arr[j];
+ byt5 = bp[5];
+ len = (byt5 & 0x2) ? 20 : 8;
+ opcode = bp[0];
+ sa_v = !!(byt5 & 1); /* service action valid */
+ serv_act = 0;
+ name_buff[0] = '\0';
+ if (sa_v) {
+ serv_act = sg_get_unaligned_be16(bp + 2);
+ sg_get_opcode_sa_name(opcode, serv_act, peri_dtype, NAME_BUFF_SZ,
+ name_buff);
+ if (op->do_compact)
+ snprintf(sa_buff, sizeof(sa_buff), "%-4x", serv_act);
+ else
+ snprintf(sa_buff, sizeof(sa_buff), "%4x", serv_act);
+ } else {
+ sg_get_opcode_name(opcode, peri_dtype, NAME_BUFF_SZ, name_buff);
+ memset(sa_buff, ' ', sizeof(sa_buff));
+ }
+ if (op->do_rctd) {
+ n = 0;
+ if (byt5 & 0x2) { /* CTDP set */
+ /* don't show CDLP because it makes line too long */
+ if (op->do_compact)
+ n += sg_scnpr(b + n, blen - n, " %.2x%c%.4s", opcode,
+ (sa_v ? ',' : ' '), sa_buff);
+ else
+ n += sg_scnpr(b + n, blen - n, " %.2x %.4s %3d",
+ opcode,
+ sa_buff, sg_get_unaligned_be16(bp + 6));
+ timeout = sg_get_unaligned_be32(bp + 12);
+ if (0 == timeout)
+ n += sg_scnpr(b + n, blen - n, " -");
+ else
+ n += sg_scnpr(b + n, blen - n, " %8u", timeout);
+ timeout = sg_get_unaligned_be32(bp + 16);
+ if (0 == timeout)
+ n += sg_scnpr(b + n, blen - n, " -");
+ else
+ n += sg_scnpr(b + n, blen - n, " %8u", timeout);
+ sgj_pr_hr(jsp, "%s %s\n", b, name_buff);
+ } else /* CTDP clear */
+ if (op->do_compact)
+ sgj_pr_hr(jsp, " %.2x%c%.4s %s\n",
+ opcode, (sa_v ? ',' : ' '), sa_buff, name_buff);
+ else
+ sgj_pr_hr(jsp, " %.2x %.4s %3d "
+ " %s\n", opcode, sa_buff,
+ sg_get_unaligned_be16(bp + 6), name_buff);
+ } else { /* RCTD clear in cdb */
+ /* before version 0.69 treated RWCDLP (1 bit) and CDLP (2 bits),
+ * as a 3 bit field, now break them out separately */
+ int rwcdlp = (byt5 >> 2) & 0x3;
+ int cdlp = !!(0x40 & byt5);
+
+ if (op->do_compact)
+ sgj_pr_hr(jsp, " %.2x%c%.4s %s\n", bp[0],
+ (sa_v ? ',' : ' '), sa_buff, name_buff);
+ else if (op->do_mlu)
+ sgj_pr_hr(jsp, " %.2x %.4s %3d %3d %s\n",
+ bp[0], sa_buff, sg_get_unaligned_be16(bp + 6),
+ ((byt5 >> 4) & 0x3), name_buff);
+ else
+ sgj_pr_hr(jsp, " %.2x %.4s %3d %d,%d %s\n",
+ bp[0], sa_buff, sg_get_unaligned_be16(bp + 6),
+ rwcdlp, cdlp, name_buff);
+ }
+ if (jsp->pr_as_json) {
+ snprintf(b, blen, "0x%x", opcode);
+ sgj_js_nv_s(jsp, jop, "operation_code", b);
+ if (sa_v) {
+ snprintf(b, blen, "0x%x", serv_act);
+ sgj_js_nv_s(jsp, jop, "service_action", b);
+ }
+ if (name_buff[0])
+ sgj_js_nv_s(jsp, jop, "name", name_buff);
+ sgj_js_nv_i(jsp, jop, "rwcdlp", (byt5 >> 6) & 0x1);
+ sgj_js_nv_i(jsp, jop, "mlu", (byt5 >> 4) & 0x3);
+ sgj_js_nv_i(jsp, jop, "cdlp", (byt5 >> 2) & 0x3);
+ sgj_js_nv_i(jsp, jop, "ctdp", (byt5 >> 1) & 0x1);
+ sgj_js_nv_i(jsp, jop, "servactv", byt5 & 0x1);
+ sgj_js_nv_i(jsp, jop, "cdb_length",
+ sg_get_unaligned_be16(bp + 6));
+
+ sgj_js_nv_o(jsp, jap, NULL /* implies an array add */, jop);
+ }
+
+ if (op->do_mask && ptvp) {
+ int cdb_sz;
+ uint8_t d[64];
+
+ n = 0;
+ memset(d, 0, sizeof(d));
+ res = do_rsoc(ptvp, false, (sa_v ? 2 : 1), opcode, serv_act,
+ d, sizeof(d), &act_len, true, op->verbose);
+ if (0 == res) {
+ int nn;
+
+ cdb_sz = sg_get_unaligned_be16(d + 2);
+ cdb_sz = (cdb_sz < act_len) ? cdb_sz : act_len;
+ if ((cdb_sz > 0) && (cdb_sz <= 80)) {
+ if (op->do_compact)
+ n += sg_scnpr(b + n, blen - n,
+ " usage: ");
+ else
+ n += sg_scnpr(b + n, blen - n, " cdb usage: ");
+ nn = n;
+ for (m = 0; (m < cdb_sz) && ((4 + m) < (int)sizeof(d));
+ ++m)
+ n += sg_scnpr(b + n, blen - n, "%.2x ", d[4 + m]);
+ sgj_pr_hr(jsp, "%s\n", b);
+ if (jsp->pr_as_json) {
+ int l;
+ char *b2p = b + nn;
+ sgj_opaque_p jo2p = sgj_named_subobject_r(jsp, jop,
+ "one_command_descriptor");
+
+ l = strlen(b2p);
+ if ((l > 0) && (' ' == b2p[l - 1]))
+ b2p[l - 1] = '\0';
+ sgj_js_nv_i(jsp, jo2p, "cdb_size", cdb_sz);
+ sgj_js_nv_s(jsp, jo2p, "cdb_usage_data", b2p);
+ }
+ }
+ } else
+ goto err_out;
+ }
+ }
+ res = 0;
+err_out:
+ if (sort_arr)
+ free(sort_arr);
+ return res;
+}
+
+static void
+decode_cmd_timeout_desc(uint8_t * dp, int max_b_len, char * b,
+ struct opts_t * op)
+{
+ int len;
+ unsigned int timeout;
+ sgj_state * jsp = &op->json_st;
+
+ if ((max_b_len < 2) || (NULL == dp))
+ return;
+ b[max_b_len - 1] = '\0';
+ --max_b_len;
+ len = sg_get_unaligned_be16(dp + 0);
+ if (10 != len) {
+ snprintf(b, max_b_len, "command timeout descriptor length %d "
+ "(expect 10)", len);
+ return;
+ }
+ timeout = sg_get_unaligned_be32(dp + 4);
+ if (0 == timeout)
+ snprintf(b, max_b_len, "no nominal timeout, ");
+ else
+ snprintf(b, max_b_len, "nominal timeout: %u secs, ", timeout);
+ if (jsp->pr_as_json) {
+ sgj_js_nv_i(jsp, jsp->userp, "command_specific", dp[3]);
+ sgj_js_nv_i(jsp, jsp->userp, "nominal_command_processing_timeout",
+ timeout);
+ }
+ len = strlen(b);
+ max_b_len -= len;
+ b += len;
+ timeout = sg_get_unaligned_be32(dp + 8);
+ if (0 == timeout)
+ snprintf(b, max_b_len, "no recommended timeout");
+ else
+ snprintf(b, max_b_len, "recommended timeout: %u secs", timeout);
+ if (jsp->pr_as_json)
+ sgj_js_nv_i(jsp, jsp->userp, "recommended_command_timeout", timeout);
+ return;
+}
+
+/* For decoding a RSOC command's "One_command" parameter data which includes
+ * cdb usage data. */
+static void
+list_one(uint8_t * rsoc_buff, int cd_len, int rep_opts,
+ struct opts_t * op)
+{
+ bool valid = false;
+ int k, mlu, cdlp, rwcdlp, support, ctdp;
+ int n = 0;
+ uint8_t * bp;
+ const char * cp;
+ const char * dlp;
+ const char * mlu_p;
+ sgj_state * jsp = &op->json_st;
+ sgj_opaque_p jop = NULL;
+ char name_buff[NAME_BUFF_SZ];
+ char d[64];
+ char b[192];
+ const int blen = sizeof(b);
+
+
+ jop = sgj_named_subobject_r(jsp, jsp->basep, "one_command_descriptor");
+ n += sg_scnpr(b + n, blen - n, "\n Opcode=0x%.2x", op->opcode);
+ if (rep_opts > 1)
+ n += sg_scnpr(b + n, blen - n, " Service_action=0x%.4x", op->servact);
+ sgj_pr_hr(jsp, "%s\n", b);
+ sg_get_opcode_sa_name(((op->opcode > 0) ? op->opcode : 0),
+ ((op->servact > 0) ? op->servact : 0),
+ peri_dtype, NAME_BUFF_SZ, name_buff);
+ sgj_pr_hr(jsp, " Command_name: %s\n", name_buff);
+ ctdp = !!(0x80 & rsoc_buff[1]);
+ support = rsoc_buff[1] & 7;
+ switch(support) {
+ case 0:
+ cp = "not currently available";
+ break;
+ case 1:
+ cp = "NOT supported";
+ break;
+ case 3:
+ cp = "supported [conforming to SCSI standard]";
+ valid = true;
+ break;
+ case 5:
+ cp = "supported [in a vendor specific manner]";
+ valid = true;
+ break;
+ default:
+ snprintf(name_buff, NAME_BUFF_SZ, "support reserved [0x%x]",
+ rsoc_buff[1] & 7);
+ cp = name_buff;
+ break;
+ }
+ cdlp = 0x3 & (rsoc_buff[1] >> 3);
+ rwcdlp = rsoc_buff[0] & 1;
+ switch (cdlp) {
+ case 0:
+ if (rwcdlp)
+ dlp = "Reserved [RWCDLP=1, CDLP=0]";
+ else
+ dlp = "No command duration limit mode page";
+ break;
+ case 1:
+ if (rwcdlp)
+ dlp = "Command duration limit T2A mode page";
+ else
+ dlp = "Command duration limit A mode page";
+ break;
+ case 2:
+ if (rwcdlp)
+ dlp = "Command duration limit T2B mode page";
+ else
+ dlp = "Command duration limit B mode page";
+ break;
+ default:
+ dlp = "reserved [CDLP=3]";
+ break;
+ }
+ sgj_pr_hr(jsp, " Command is %s\n", cp);
+ sgj_pr_hr(jsp, " %s\n", dlp);
+ mlu = 0x3 & (rsoc_buff[1] >> 5);
+ switch (mlu) {
+ case 0:
+ mlu_p = "not reported";
+ break;
+ case 1:
+ mlu_p = "affects only this logical unit";
+ break;
+ case 2:
+ mlu_p = "affects more than 1, but not all LUs in this target";
+ break;
+ case 3:
+ mlu_p = "affects all LUs in this target";
+ break;
+ default:
+ snprintf(d, sizeof(d), "reserved [MLU=%d]", mlu);
+ mlu_p = d;
+ break;
+ }
+ sgj_pr_hr(jsp, " Multiple Logical Units (MLU): %s\n", mlu_p);
+ if (valid) {
+ n = 0;
+ n += sg_scnpr(b + n, blen - n, " Usage data: ");
+ bp = rsoc_buff + 4;
+ for (k = 0; k < cd_len; ++k)
+ n += sg_scnpr(b + n, blen - n, "%.2x ", bp[k]);
+ sgj_pr_hr(jsp, "%s\n", b);
+ }
+ if (jsp->pr_as_json) {
+ int l;
+
+ snprintf(b, blen, "0x%x", op->opcode);
+ sgj_js_nv_s(jsp, jop, "operation_code", b);
+ if (rep_opts > 1) {
+ snprintf(b, blen, "0x%x", op->servact);
+ sgj_js_nv_s(jsp, jop, "service_action", b);
+ }
+ sgj_js_nv_i(jsp, jop, "rwcdlp", rwcdlp);
+ sgj_js_nv_i(jsp, jop, "ctdp", ctdp);
+ sgj_js_nv_i(jsp, jop, "mlu", mlu);
+ sgj_js_nv_i(jsp, jop, "cdlp", cdlp);
+ sgj_js_nv_i(jsp, jop, "support", support);
+ sgj_js_nv_s(jsp, jop, "support_str", cp);
+ sgj_js_nv_i(jsp, jop, "cdb_size", cd_len);
+ n = 0;
+ for (k = 0; k < cd_len; ++k)
+ n += sg_scnpr(b + n, blen - n, "%.2x ", rsoc_buff[k + 4]);
+ l = strlen(b);
+ if ((l > 0) && (' ' == b[l - 1]))
+ b[l - 1] = '\0';
+ sgj_js_nv_s(jsp, jop, "cdb_usage_data", b);
+ }
+ if (ctdp) {
+ jsp->userp = sgj_named_subobject_r(jsp, jsp->basep,
+ "command_timeouts_descriptor");
+ bp = rsoc_buff + 4 + cd_len;
+ decode_cmd_timeout_desc(bp, NAME_BUFF_SZ, name_buff, op);
+ sgj_pr_hr(jsp, " %s\n", name_buff);
+ }
+}
+
+
+int
+main(int argc, char * argv[])
+{
+ bool as_json;
+ int cd_len, res, len, act_len, rq_len, in_len, vb;
+ int rep_opts = 0;
+ int sg_fd = -1;
+ const char * cp;
+ struct opts_t * op;
+ const char * op_name;
+ uint8_t * rsoc_buff = NULL;
+ uint8_t * free_rsoc_buff = NULL;
+ struct sg_pt_base * ptvp = NULL;
+ sgj_state * jsp;
+ sgj_opaque_p jop = NULL;
+ char buff[48];
+ char b[80];
+ struct sg_simple_inquiry_resp inq_resp;
+ struct opts_t opts;
+
+ op = &opts;
+ memset(op, 0, sizeof(opts));
+ op->opcode = -1;
+ op->servact = -1;
+ res = parse_cmd_line(op, argc, argv);
+ if (res)
+ return SG_LIB_SYNTAX_ERROR;
+ if (op->do_help) {
+ if (op->opt_new)
+ usage();
+ else
+ usage_old();
+ return 0;
+ }
+ jsp = &op->json_st;
+ as_json = jsp->pr_as_json;
+ if (as_json) {
+ jop = sgj_start_r(MY_NAME, version_str, argc, argv, jsp);
+ }
+#ifdef DEBUG
+ pr2serr("In DEBUG mode, ");
+ if (op->verbose_given && op->version_given) {
+ pr2serr("but override: '-vV' given, zero verbose and continue\n");
+ op->verbose_given = false;
+ op->version_given = false;
+ op->verbose = 0;
+ } else if (! op->verbose_given) {
+ pr2serr("set '-vv'\n");
+ op->verbose = 2;
+ } else
+ pr2serr("keep verbose=%d\n", op->verbose);
+#else
+ if (op->verbose_given && op->version_given)
+ pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
+#endif
+ if (op->version_given) {
+ pr2serr("Version string: %s\n", version_str);
+ goto fini;
+ }
+ vb = op->verbose;
+ if (op->do_enumerate) {
+ char name_buff[NAME_BUFF_SZ];
+
+ if (op->do_taskman)
+ printf("enumerate not supported with task management "
+ "functions\n");
+ else { /* SCSI command */
+ if (op->opcode < 0)
+ op->opcode = 0;
+ if (op->servact < 0)
+ op->servact = 0;
+ if (peri_dtype < 0)
+ peri_dtype = 0;
+ printf("SCSI command:");
+ if (vb)
+ printf(" [opcode=0x%x, sa=0x%x, pdt=0x%x]\n", op->opcode,
+ op->servact, peri_dtype);
+ else
+ printf("\n");
+ sg_get_opcode_sa_name(op->opcode, op->servact, peri_dtype,
+ NAME_BUFF_SZ, name_buff);
+ printf(" %s\n", name_buff);
+ }
+ goto fini;
+ } else if (op->inhex_fn) {
+ if (op->device_name) {
+ if (! as_json)
+ pr2serr("ignoring DEVICE, best to give DEVICE or "
+ "--inhex=FN, but not both\n");
+ op->device_name = NULL;
+ }
+ } else if (NULL == op->device_name) {
+ pr2serr("No DEVICE argument given\n\n");
+ if (op->opt_new)
+ usage();
+ else
+ usage_old();
+ res = SG_LIB_SYNTAX_ERROR;
+ goto err_out;
+ }
+ if ((-1 != op->servact) && (-1 == op->opcode)) {
+ pr2serr("When '-s' is chosen, so must '-o' be chosen\n");
+ if (op->opt_new)
+ usage();
+ else
+ usage_old();
+ res = SG_LIB_CONTRADICT;
+ goto err_out;
+ }
+ if (op->do_unsorted && op->do_alpha)
+ pr2serr("warning: unsorted ('-u') and alpha ('-a') options chosen, "
+ "ignoring alpha\n");
+ if (op->do_taskman && ((-1 != op->opcode) || op->do_alpha ||
+ op->do_unsorted)) {
+ pr2serr("warning: task management functions ('-t') chosen so alpha "
+ "('-a'),\n unsorted ('-u') and opcode ('-o') "
+ "options ignored\n");
+ }
+ op_name = op->do_taskman ? "Report supported task management functions" :
+ "Report supported operation codes";
+
+ rsoc_buff = (uint8_t *)sg_memalign(MX_ALLOC_LEN, 0, &free_rsoc_buff,
+ false);
+ if (NULL == rsoc_buff) {
+ pr2serr("Unable to allocate memory\n");
+ res = sg_convert_errno(ENOMEM);
+ no_final_msg = true;
+ goto err_out;
+ }
+
+ if (op->inhex_fn) {
+ if ((res = sg_f2hex_arr(op->inhex_fn, op->do_raw, false, rsoc_buff,
+ &in_len, MX_ALLOC_LEN))) {
+ if (SG_LIB_LBA_OUT_OF_RANGE == res)
+ pr2serr("decode buffer [%d] not large enough??\n",
+ MX_ALLOC_LEN);
+ goto err_out;
+ }
+ if (op->verbose > 2)
+ pr2serr("Read %d [0x%x] bytes of user supplied data\n",
+ in_len, in_len);
+ if (op->do_raw)
+ op->do_raw = false; /* can interfere on decode */
+ if (in_len < 4) {
+ pr2serr("--inhex=%s only decoded %d bytes (needs 4 at "
+ "least)\n", op->inhex_fn, in_len);
+ res = SG_LIB_SYNTAX_ERROR;
+ goto err_out;
+ }
+ res = 0;
+ act_len = in_len;
+ goto start_response;
+ }
+ if (op->opcode < 0) {
+ /* Try to open read-only */
+ if ((sg_fd = scsi_pt_open_device(op->device_name, true, vb)) < 0) {
+ int err = -sg_fd;
+
+ if (op->verbose)
+ pr2serr("sg_opcodes: error opening file (ro): %s: %s\n",
+ op->device_name, safe_strerror(err));
+#ifndef SG_LIB_WIN32
+ if (ENOENT == err) {
+ /* file or directory in the file's path doesn't exist, no
+ * point in retrying with read-write flag */
+ res = sg_convert_errno(err);
+ goto err_out;
+ }
+#endif
+ goto open_rw;
+ }
+ ptvp = construct_scsi_pt_obj_with_fd(sg_fd, op->verbose);
+ if (NULL == ptvp) {
+ pr2serr("Out of memory (ro)\n");
+ res = sg_convert_errno(ENOMEM);
+ no_final_msg = true;
+ goto err_out;
+ }
+ if (op->no_inquiry && (peri_dtype < 0))
+ pr2serr("--no-inquiry ignored because --pdt= not given\n");
+ if (op->no_inquiry && (peri_dtype >= 0))
+ ;
+ else if (0 == sg_simple_inquiry_pt(ptvp, &inq_resp, true, vb)) {
+ peri_dtype = inq_resp.peripheral_type;
+ if (! (as_json || op->do_raw || op->no_inquiry ||
+ (op->do_hex > 2))) {
+ printf(" %.8s %.16s %.4s\n", inq_resp.vendor,
+ inq_resp.product, inq_resp.revision);
+ cp = sg_get_pdt_str(peri_dtype, sizeof(buff), buff);
+ if (strlen(cp) > 0)
+ printf(" Peripheral device type: %s\n", cp);
+ else
+ printf(" Peripheral device type: 0x%x\n", peri_dtype);
+ }
+ } else {
+ pr2serr("sg_opcodes: %s doesn't respond to a SCSI INQUIRY\n",
+ op->device_name);
+ res = SG_LIB_CAT_OTHER;
+ no_final_msg = true;
+ goto err_out;
+ }
+ }
+
+open_rw: /* if not already open */
+ if (sg_fd < 0) {
+ sg_fd = scsi_pt_open_device(op->device_name, false /* RW */, vb);
+ if (sg_fd < 0) {
+ pr2serr("sg_opcodes: error opening file (rw): %s: %s\n",
+ op->device_name, safe_strerror(-sg_fd));
+ res = sg_convert_errno(-sg_fd);
+ no_final_msg = true;
+ goto err_out;
+ }
+ ptvp = construct_scsi_pt_obj_with_fd(sg_fd, op->verbose);
+ if (NULL == ptvp) {
+ pr2serr("Out of memory (rw)\n");
+ res = sg_convert_errno(ENOMEM);
+ no_final_msg = true;
+ goto err_out;
+ }
+ }
+ if (op->opcode >= 0)
+ rep_opts = ((op->servact >= 0) ? 2 : 1);
+ if (op->do_taskman) {
+ rq_len = (op->do_repd ? 16 : 4);
+ res = do_rstmf(ptvp, op->do_repd, rsoc_buff, rq_len, &act_len, true,
+ vb);
+ } else {
+ rq_len = MX_ALLOC_LEN;
+ res = do_rsoc(ptvp, op->do_rctd, rep_opts, op->opcode, op->servact,
+ rsoc_buff, rq_len, &act_len, true, vb);
+ }
+ if (res) {
+ sg_get_category_sense_str(res, sizeof(b), b, vb);
+ pr2serr("%s: %s\n", op_name, b);
+ no_final_msg = true;
+ if ((0 == op->servact) && (op->opcode >= 0))
+ pr2serr(" >> perhaps try again without a service action "
+ "[SA] of 0\n");
+ goto err_out;
+ }
+ act_len = (rq_len < act_len) ? rq_len : act_len;
+
+start_response:
+ if (act_len < 4) {
+ pr2serr("Actual length of response [%d] is too small\n", act_len);
+ res = SG_LIB_CAT_OTHER;
+ no_final_msg = true;
+ goto err_out;
+ }
+ if (op->do_taskman) {
+ if (op->do_raw) {
+ dStrRaw((const char *)rsoc_buff, act_len);
+ goto fini;
+ }
+ if (op->do_hex) {
+ if (op->do_hex > 2)
+ hex2stdout(rsoc_buff, act_len, -1);
+ else {
+ printf("\nTask Management Functions supported by device:\n");
+ if (2 == op->do_hex)
+ hex2stdout(rsoc_buff, act_len, 0);
+ else
+ hex2stdout(rsoc_buff, act_len, 1);
+ }
+ goto fini;
+ }
+ if (jsp->pr_as_json) {
+ sgj_js_nv_b(jsp, jop, "ats", rsoc_buff[0] & 0x80);
+ sgj_js_nv_b(jsp, jop, "atss", rsoc_buff[0] & 0x40);
+ sgj_js_nv_b(jsp, jop, "cacas", rsoc_buff[0] & 0x20);
+ sgj_js_nv_b(jsp, jop, "ctss", rsoc_buff[0] & 0x10);
+ sgj_js_nv_b(jsp, jop, "lurs", rsoc_buff[0] & 0x8);
+ sgj_js_nv_b(jsp, jop, "qts", rsoc_buff[0] & 0x4);
+ sgj_js_nv_b(jsp, jop, "trs", rsoc_buff[0] & 0x2);
+ sgj_js_nv_b(jsp, jop, "ws", rsoc_buff[0] & 0x1);
+ sgj_js_nv_b(jsp, jop, "qaes", rsoc_buff[1] & 0x4);
+ sgj_js_nv_b(jsp, jop, "qtss", rsoc_buff[1] & 0x2);
+ sgj_js_nv_b(jsp, jop, "itnrs", rsoc_buff[1] & 0x1);
+ if (! jsp->pr_out_hr)
+ goto fini;
+ }
+ sgj_pr_hr(jsp, "\nTask Management Functions supported by device:\n");
+ if (rsoc_buff[0] & 0x80)
+ sgj_pr_hr(jsp, " Abort task\n");
+ if (rsoc_buff[0] & 0x40)
+ sgj_pr_hr(jsp, " Abort task set\n");
+ if (rsoc_buff[0] & 0x20)
+ sgj_pr_hr(jsp, " Clear ACA\n");
+ if (rsoc_buff[0] & 0x10)
+ sgj_pr_hr(jsp, " Clear task set\n");
+ if (rsoc_buff[0] & 0x8)
+ sgj_pr_hr(jsp, " Logical unit reset\n");
+ if (rsoc_buff[0] & 0x4)
+ sgj_pr_hr(jsp, " Query task\n");
+ if (rsoc_buff[0] & 0x2)
+ sgj_pr_hr(jsp, " Target reset (obsolete)\n");
+ if (rsoc_buff[0] & 0x1)
+ sgj_pr_hr(jsp, " Wakeup (obsolete)\n");
+ if (rsoc_buff[1] & 0x4)
+ sgj_pr_hr(jsp, " Query asynchronous event\n");
+ if (rsoc_buff[1] & 0x2)
+ sgj_pr_hr(jsp, " Query task set\n");
+ if (rsoc_buff[1] & 0x1)
+ sgj_pr_hr(jsp, " I_T nexus reset\n");
+ if (op->do_repd) {
+ if (rsoc_buff[3] < 0xc) {
+ pr2serr("when REPD given, byte 3 of response should be >= "
+ "12\n");
+ res = SG_LIB_CAT_OTHER;
+ no_final_msg = true;
+ goto err_out;
+ } else
+ sgj_pr_hr(jsp, " Extended parameter data:\n");
+ sgj_pr_hr(jsp, " TMFTMOV=%d\n", !!(rsoc_buff[4] & 0x1));
+ sgj_pr_hr(jsp, " ATTS=%d\n", !!(rsoc_buff[6] & 0x80));
+ sgj_pr_hr(jsp, " ATSTS=%d\n", !!(rsoc_buff[6] & 0x40));
+ sgj_pr_hr(jsp, " CACATS=%d\n", !!(rsoc_buff[6] & 0x20));
+ sgj_pr_hr(jsp, " CTSTS=%d\n", !!(rsoc_buff[6] & 0x10));
+ sgj_pr_hr(jsp, " LURTS=%d\n", !!(rsoc_buff[6] & 0x8));
+ sgj_pr_hr(jsp, " QTTS=%d\n", !!(rsoc_buff[6] & 0x4));
+ sgj_pr_hr(jsp, " QAETS=%d\n", !!(rsoc_buff[7] & 0x4));
+ sgj_pr_hr(jsp, " QTSTS=%d\n", !!(rsoc_buff[7] & 0x2));
+ sgj_pr_hr(jsp, " ITNRTS=%d\n", !!(rsoc_buff[7] & 0x1));
+ sgj_pr_hr(jsp, " tmf long timeout: %u (100 ms units)\n",
+ sg_get_unaligned_be32(rsoc_buff + 8));
+ sgj_pr_hr(jsp, " tmf short timeout: %u (100 ms units)\n",
+ sg_get_unaligned_be32(rsoc_buff + 12));
+ }
+ } else if (0 == rep_opts) { /* list all supported operation codes */
+ len = sg_get_unaligned_be32(rsoc_buff + 0) + 4;
+ len = (len < act_len) ? len : act_len;
+ if (op->do_raw) {
+ dStrRaw((const char *)rsoc_buff, len);
+ goto fini;
+ }
+ if (op->do_hex) {
+ if (op->do_hex > 2)
+ hex2stdout(rsoc_buff, len, -1);
+ else if (2 == op->do_hex)
+ hex2stdout(rsoc_buff, len, 0);
+ else
+ hex2stdout(rsoc_buff, len, 1);
+ goto fini;
+ }
+ list_all_codes(rsoc_buff, len, op, ptvp);
+ } else { /* asked about specific command */
+ cd_len = sg_get_unaligned_be16(rsoc_buff + 2);
+ len = cd_len + 4;
+ len = (len < act_len) ? len : act_len;
+ cd_len = (cd_len < act_len) ? cd_len : act_len;
+ if (op->do_raw) {
+ dStrRaw((const char *)rsoc_buff, len);
+ goto fini;
+ }
+ if (op->do_hex) {
+ if (op->do_hex > 2)
+ hex2stdout(rsoc_buff, len, -1);
+ else if (2 == op->do_hex)
+ hex2stdout(rsoc_buff, len, 0);
+ else
+ hex2stdout(rsoc_buff, len, 1);
+ goto fini;
+ }
+ list_one(rsoc_buff, cd_len, rep_opts, op);
+ }
+fini:
+ res = 0;
+
+err_out:
+ if (free_rsoc_buff)
+ free(free_rsoc_buff);
+ if (! op->inhex_fn) {
+ if (ptvp)
+ destruct_scsi_pt_obj(ptvp);
+ if (sg_fd >= 0)
+ scsi_pt_close_device(sg_fd);
+ }
+ if ((0 == op->verbose) && (! no_final_msg)) {
+ if (! sg_if_can2stderr("sg_opcodes failed: ", res))
+ pr2serr("Some error occurred, try again with '-v' "
+ "or '-vv' for more information\n");
+ }
+ res = (res >= 0) ? res : SG_LIB_CAT_OTHER;
+ if (as_json) {
+ if (0 == op->do_hex)
+ sgj_js2file(jsp, NULL, res, stdout);
+ sgj_finish(jsp);
+ }
+ return res;
+}