aboutsummaryrefslogtreecommitdiff
path: root/src/sg_vpd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sg_vpd.c')
-rw-r--r--src/sg_vpd.c2770
1 files changed, 2770 insertions, 0 deletions
diff --git a/src/sg_vpd.c b/src/sg_vpd.c
new file mode 100644
index 00000000..c8900fb7
--- /dev/null
+++ b/src/sg_vpd.c
@@ -0,0 +1,2770 @@
+/*
+ * Copyright (c) 2006-2022 Douglas Gilbert.
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the BSD_LICENSE file.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+#include "sg_vpd_common.h" /* shared with sg_inq */
+
+/* This utility program was originally written for the Linux OS SCSI subsystem.
+
+ This program fetches Vital Product Data (VPD) pages from the given
+ device and outputs it as directed. VPD pages are obtained via a
+ SCSI INQUIRY command. Most of the data in this program is obtained
+ from the SCSI SPC-4 document at https://www.t10.org .
+
+*/
+
+static const char * version_str = "1.83 20220915"; /* spc6r06 + sbc5r03 */
+
+#define MY_NAME "sg_vpd"
+
+/* Device identification VPD page associations */
+#define VPD_ASSOC_LU 0
+#define VPD_ASSOC_TPORT 1
+#define VPD_ASSOC_TDEVICE 2
+
+/* values for selection one or more associations (2**vpd_assoc),
+ except _AS_IS */
+#define VPD_DI_SEL_LU 1
+#define VPD_DI_SEL_TPORT 2
+#define VPD_DI_SEL_TARGET 4
+#define VPD_DI_SEL_AS_IS 32
+
+#define DEF_ALLOC_LEN 252
+#define MIN_MAXLEN 16
+#define MX_ALLOC_LEN (0xc000 + 0x80)
+#define VPD_ATA_INFO_LEN 572
+
+#define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
+#define INQUIRY_CMD 0x12
+#define INQUIRY_CMDLEN 6
+#define DEF_PT_TIMEOUT 60 /* 60 seconds */
+
+
+uint8_t * rsp_buff;
+
+static int svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop,
+ int subvalue, int off, const char * prefix);
+static int svpd_unable_to_decode(int sg_fd, struct opts_t * op, int subvalue,
+ int off);
+
+static int filter_dev_ids(const char * print_if_found, int num_leading,
+ uint8_t * buff, int len, int m_assoc,
+ struct opts_t * op, sgj_opaque_p jop);
+
+static const int rsp_buff_sz = MX_ALLOC_LEN + 2;
+
+static uint8_t * free_rsp_buff;
+
+static struct option long_options[] = {
+ {"all", no_argument, 0, 'a'},
+ {"enumerate", no_argument, 0, 'e'},
+ {"examine", no_argument, 0, 'E'},
+ {"force", no_argument, 0, 'f'},
+ {"help", no_argument, 0, 'h'},
+ {"hex", no_argument, 0, 'H'},
+ {"ident", no_argument, 0, 'i'},
+ {"inhex", required_argument, 0, 'I'},
+ {"json", optional_argument, 0, 'j'},
+ {"long", no_argument, 0, 'l'},
+ {"maxlen", required_argument, 0, 'm'},
+ {"page", required_argument, 0, 'p'},
+ {"quiet", no_argument, 0, 'q'},
+ {"raw", no_argument, 0, 'r'},
+ {"sinq_inraw", required_argument, 0, 'Q'},
+ {"sinq-inraw", required_argument, 0, 'Q'},
+ {"vendor", required_argument, 0, 'M'},
+ {"verbose", no_argument, 0, 'v'},
+ {"version", no_argument, 0, 'V'},
+ {0, 0, 0, 0},
+};
+
+
+/* arranged in alphabetical order by acronym */
+static struct svpd_values_name_t standard_vpd_pg[] = {
+ {VPD_AUTOMATION_DEV_SN, 0, 1, "adsn", "Automation device serial "
+ "number (SSC)"},
+ {VPD_ATA_INFO, 0, -1, "ai", "ATA information (SAT)"},
+ {VPD_ASCII_OP_DEF, 0, -1, "aod",
+ "ASCII implemented operating definition (obsolete)"},
+ {VPD_BLOCK_DEV_CHARS, 0, 0, "bdc", "Block device characteristics "
+ "(SBC)"},
+ {VPD_BLOCK_DEV_C_EXTENS, 0, 0, "bdce", "Block device characteristics "
+ "extension (SBC)"},
+ {VPD_BLOCK_LIMITS, 0, 0, "bl", "Block limits (SBC)"},
+ {VPD_BLOCK_LIMITS_EXT, 0, 0, "ble", "Block limits extension (SBC)"},
+ {VPD_CFA_PROFILE_INFO, 0, 0, "cfa", "CFA profile information"},
+ {VPD_CON_POS_RANGE, 0, 0, "cpr", "Concurrent positioning ranges"},
+ {VPD_DEVICE_CONSTITUENTS, 0, -1, "dc", "Device constituents"},
+ {VPD_DEVICE_ID, 0, -1, "di", "Device identification"},
+ {VPD_DEVICE_ID, VPD_DI_SEL_AS_IS, -1, "di_asis", "Like 'di' "
+ "but designators ordered as found"},
+ {VPD_DEVICE_ID, VPD_DI_SEL_LU, -1, "di_lu", "Device identification, "
+ "lu only"},
+ {VPD_DEVICE_ID, VPD_DI_SEL_TPORT, -1, "di_port", "Device "
+ "identification, target port only"},
+ {VPD_DEVICE_ID, VPD_DI_SEL_TARGET, -1, "di_target", "Device "
+ "identification, target device only"},
+ {VPD_DTDE_ADDRESS, 0, 1, "dtde",
+ "Data transfer device element address (SSC)"},
+ {VPD_EXT_INQ, 0, -1, "ei", "Extended inquiry data"},
+ {VPD_FORMAT_PRESETS, 0, 0, "fp", "Format presets"},
+ {VPD_IMP_OP_DEF, 0, -1, "iod",
+ "Implemented operating definition (obsolete)"},
+ {VPD_LB_PROTECTION, 0, 0, "lbpro", "Logical block protection (SSC)"},
+ {VPD_LB_PROVISIONING, 0, 0, "lbpv", "Logical block provisioning (SBC)"},
+ {VPD_MAN_ASS_SN, 0, 1, "mas", "Manufacturer assigned serial number (SSC)"},
+ {VPD_MAN_ASS_SN, 0, 0x12, "masa",
+ "Manufacturer assigned serial number (ADC)"},
+ {VPD_MAN_NET_ADDR, 0, -1, "mna", "Management network addresses"},
+ {VPD_MODE_PG_POLICY, 0, -1, "mpp", "Mode page policy"},
+ {VPD_OSD_INFO, 0, 0x11, "oi", "OSD information"},
+ {VPD_POWER_CONDITION, 0, -1, "pc", "Power condition"},/* "po" in sg_inq */
+ {VPD_POWER_CONSUMPTION, 0, -1, "psm", "Power consumption"},
+ {VPD_PROTO_LU, 0, -1, "pslu", "Protocol-specific logical unit "
+ "information"},
+ {VPD_PROTO_PORT, 0, -1, "pspo", "Protocol-specific port information"},
+ {VPD_REFERRALS, 0, 0, "ref", "Referrals (SBC)"},
+ {VPD_SA_DEV_CAP, 0, 1, "sad",
+ "Sequential access device capabilities (SSC)"},
+ {VPD_SUP_BLOCK_LENS, 0, 0, "sbl", "Supported block lengths and "
+ "protection types (SBC)"},
+ {VPD_SCSI_FEATURE_SETS, 0, -1, "sfs", "SCSI feature sets"},
+ {VPD_SOFTW_INF_ID, 0, -1, "sii", "Software interface identification"},
+ {VPD_NOPE_WANT_STD_INQ, 0, -1, "sinq", "Standard inquiry data format"},
+ {VPD_UNIT_SERIAL_NUM, 0, -1, "sn", "Unit serial number"},
+ {VPD_SCSI_PORTS, 0, -1, "sp", "SCSI ports"},
+ {VPD_SECURITY_TOKEN, 0, 0x11, "st", "Security token (OSD)"},
+ {VPD_SUPPORTED_VPDS, 0, -1, "sv", "Supported VPD pages"},
+ {VPD_TA_SUPPORTED, 0, 1, "tas", "TapeAlert supported flags (SSC)"},
+ {VPD_3PARTY_COPY, 0, -1, "tpc", "Third party copy"},
+ {VPD_ZBC_DEV_CHARS, 0, -1, "zbdch", "Zoned block device characteristics"},
+ /* Use pdt of -1 since this page both for pdt=0 and pdt=0x14 */
+ {0, 0, 0, NULL, NULL},
+};
+
+
+static void
+usage()
+{
+ pr2serr("Usage: sg_vpd [--all] [--enumerate] [--examine] [--force] "
+ "[--help] [--hex]\n"
+ " [--ident] [--inhex=FN] [--long] [--maxlen=LEN] "
+ "[--page=PG]\n"
+ " [--quiet] [--raw] [--sinq_inraw=RFN] "
+ "[--vendor=VP] [--verbose]\n"
+ " [--version] DEVICE\n");
+ pr2serr(" where:\n"
+ " --all|-a output all pages listed in the supported "
+ "pages VPD\n"
+ " page\n"
+ " --enumerate|-e enumerate known VPD pages names (ignore "
+ "DEVICE),\n"
+ " can be used with --page=num to search\n"
+ " --examine|-E starting at 0x80 scan pages code to 0xff\n"
+ " --force|-f skip VPD page 0 (supported VPD pages) "
+ "checking\n"
+ " --help|-h output this usage message then exit\n"
+ " --hex|-H output page in ASCII hexadecimal\n"
+ " --ident|-i output device identification VPD page, "
+ "twice for\n"
+ " short logical unit designator (equiv: "
+ "'-qp di_lu')\n"
+ " --inhex=FN|-I FN read ASCII hex from file FN instead of "
+ "DEVICE;\n"
+ " if used with --raw then read binary "
+ "from FN\n"
+ " --json[=JO]|-j[JO] output in JSON instead of human "
+ "readable text.\n"
+ " Use --json=? for JSON help\n"
+ " --long|-l perform extra decoding\n"
+ " --maxlen=LEN|-m LEN max response length (allocation "
+ "length in cdb)\n"
+ " (def: 0 -> 252 bytes)\n"
+ " --page=PG|-p PG fetch VPD page where PG is an "
+ "acronym, or a decimal\n"
+ " number unless hex indicator "
+ "is given (e.g. '0x83');\n"
+ " can also take PG,VP as an "
+ "operand\n"
+ " --quiet|-q suppress some output when decoding\n"
+ " --raw|-r output page in binary; if --inhex=FN is "
+ "also\n"
+ " given, FN is in binary (else FN is in "
+ "hex)\n"
+ " --sinq_inraw=RFN|-Q RFN read raw (binary) standard "
+ "INQUIRY\n"
+ " response from the RFN filename\n"
+ " --vendor=VP|-M VP vendor/product abbreviation [or "
+ "number]\n"
+ " --verbose|-v increase verbosity\n"
+ " --version|-V print version string and exit\n\n"
+ "Fetch Vital Product Data (VPD) page using SCSI INQUIRY or "
+ "decodes VPD\npage response held in file FN. To list available "
+ "pages use '-e'. Also\n'-p -1' or '-p sinq' yields the standard "
+ "INQUIRY response.\n");
+}
+
+static const struct svpd_values_name_t *
+sdp_get_vpd_detail(int page_num, int subvalue, int pdt)
+{
+ const struct svpd_values_name_t * vnp;
+ int sv, ty;
+
+ sv = (subvalue < 0) ? 1 : 0;
+ ty = (pdt < 0) ? 1 : 0;
+ for (vnp = standard_vpd_pg; vnp->acron; ++vnp) {
+ if ((page_num == vnp->value) &&
+ (sv || (subvalue == vnp->subvalue)) &&
+ (ty || (pdt == vnp->pdt)))
+ return vnp;
+ }
+ if (! ty)
+ return sdp_get_vpd_detail(page_num, subvalue, -1);
+ if (! sv)
+ return sdp_get_vpd_detail(page_num, -1, -1);
+ return NULL;
+}
+
+static const struct svpd_values_name_t *
+sdp_find_vpd_by_acron(const char * ap)
+{
+ const struct svpd_values_name_t * vnp;
+
+ for (vnp = standard_vpd_pg; vnp->acron; ++vnp) {
+ if (0 == strcmp(vnp->acron, ap))
+ return vnp;
+ }
+ return NULL;
+}
+
+static void
+enumerate_vpds(int standard, int vendor)
+{
+ const struct svpd_values_name_t * vnp;
+
+ if (standard) {
+ for (vnp = standard_vpd_pg; vnp->acron; ++vnp) {
+ if (vnp->name) {
+ if (vnp->value < 0)
+ printf(" %-10s -1 %s\n", vnp->acron, vnp->name);
+ else
+ printf(" %-10s 0x%02x %s\n", vnp->acron, vnp->value,
+ vnp->name);
+ }
+ }
+ }
+ if (vendor)
+ svpd_enumerate_vendor(-2);
+}
+
+static int
+count_standard_vpds(int vpd_pn)
+{
+ const struct svpd_values_name_t * vnp;
+ int matches = 0;
+
+ for (vnp = standard_vpd_pg; vnp->acron; ++vnp) {
+ if ((vpd_pn == vnp->value) && vnp->name) {
+ if (0 == matches)
+ printf("Matching standard VPD pages:\n");
+ ++matches;
+ if (vnp->value < 0)
+ printf(" %-10s -1 %s\n", vnp->acron, vnp->name);
+ else
+ printf(" %-10s 0x%02x %s\n", vnp->acron, vnp->value,
+ vnp->name);
+ }
+ }
+ return matches;
+}
+
+static void
+dStrRaw(const uint8_t * str, int len)
+{
+ int k;
+
+ for (k = 0; k < len; ++k)
+ printf("%c", str[k]);
+}
+
+/* Assume index is less than 16 */
+static const char * sg_ansi_version_arr[16] =
+{
+ "no conformance claimed",
+ "SCSI-1", /* obsolete, ANSI X3.131-1986 */
+ "SCSI-2", /* obsolete, ANSI X3.131-1994 */
+ "SPC", /* withdrawn, ANSI INCITS 301-1997 */
+ "SPC-2", /* ANSI INCITS 351-2001, ISO/IEC 14776-452 */
+ "SPC-3", /* ANSI INCITS 408-2005, ISO/IEC 14776-453 */
+ "SPC-4", /* ANSI INCITS 513-2015 */
+ "SPC-5", /* ANSI INCITS 502-2020 */
+ "ecma=1, [8h]",
+ "ecma=1, [9h]",
+ "ecma=1, [Ah]",
+ "ecma=1, [Bh]",
+ "reserved [Ch]",
+ "reserved [Dh]",
+ "reserved [Eh]",
+ "reserved [Fh]",
+};
+
+static void
+std_inq_decode(uint8_t * b, int len, struct opts_t * op, sgj_opaque_p jop)
+{
+ uint8_t ver;
+ int pqual, pdt, hp, j, n;
+ sgj_state * jsp = &op->json_st;
+ const char * cp;
+ char c[256];
+ static const int clen = sizeof(c);
+ static const char * np = "Standard INQUIRY data format:";
+
+ if (len < 4) {
+ pr2serr("%s: len [%d] too short\n", __func__, len);
+ return;
+ }
+ pqual = (b[0] & 0xe0) >> 5;
+ pdt = b[0] & PDT_MASK;
+ hp = (b[1] >> 4) & 0x3;
+ ver = b[2];
+ sgj_pr_hr(jsp, "%s", np);
+ if (0 == pqual)
+ sgj_pr_hr(jsp, "\n");
+ else {
+ cp = pqual_str(pqual);
+
+ if (pqual < 3)
+ sgj_pr_hr(jsp, " [PQ indicates %s]\n", cp);
+ else
+ sgj_pr_hr(jsp, " [PQ indicates %s [0x%x] ]\n", cp, pqual);
+ }
+ sgj_pr_hr(jsp, " PQual=%d PDT=%d RMB=%d LU_CONG=%d hot_pluggable="
+ "%d version=0x%02x [%s]\n", pqual, pdt, !!(b[1] & 0x80),
+ !!(b[1] & 0x40), hp, ver, sg_ansi_version_arr[ver & 0xf]);
+ sgj_pr_hr(jsp, " [AERC=%d] [TrmTsk=%d] NormACA=%d HiSUP=%d "
+ " Resp_data_format=%d\n",
+ !!(b[3] & 0x80), !!(b[3] & 0x40), !!(b[3] & 0x20),
+ !!(b[3] & 0x10), b[3] & 0x0f);
+ if (len < 5)
+ goto skip1;
+ j = b[4] + 5;
+ if (op->verbose > 2)
+ pr2serr(">> requested %d bytes, %d bytes available\n", len, j);
+ sgj_pr_hr(jsp, " SCCS=%d ACC=%d TPGS=%d 3PC=%d Protect=%d "
+ "[BQue=%d]\n", !!(b[5] & 0x80), !!(b[5] & 0x40),
+ ((b[5] & 0x30) >> 4), !!(b[5] & 0x08), !!(b[5] & 0x01),
+ !!(b[6] & 0x80));
+ n = 0;
+ n += sg_scnpr(c + n, clen - n, "EncServ=%d ", !!(b[6] & 0x40));
+ if (b[6] & 0x10)
+ n += sg_scnpr(c + n, clen - n, "MultiP=1 (VS=%d) ", !!(b[6] & 0x20));
+ else
+ n += sg_scnpr(c + n, clen - n, "MultiP=0 ");
+ n += sg_scnpr(c + n, clen - n, "[MChngr=%d] [ACKREQQ=%d] Addr16=%d",
+ !!(b[6] & 0x08), !!(b[6] & 0x04), !!(b[6] & 0x01));
+ sgj_pr_hr(jsp, " %s\n", c);
+ sgj_pr_hr(jsp, " [RelAdr=%d] WBus16=%d Sync=%d [Linked=%d] "
+ "[TranDis=%d] CmdQue=%d\n", !!(b[7] & 0x80), !!(b[7] & 0x20),
+ !!(b[7] & 0x10), !!(b[7] & 0x08), !!(b[7] & 0x04),
+ !!(b[7] & 0x02));
+ if (len < 36)
+ goto skip1;
+ sgj_pr_hr(jsp, " %s: %.8s\n", t10_vendor_id_hr, b + 8);
+ sgj_pr_hr(jsp, " %s: %.16s\n", product_id_hr, b + 16);
+ sgj_pr_hr(jsp, " %s: %.4s\n", product_rev_lev_hr, b + 32);
+skip1:
+ if (! jsp->pr_as_json || (len < 8))
+ return;
+ std_inq_decode_js(b, len, op, jop);
+}
+
+/* VPD_DEVICE_ID 0x83 ["di, di_asis, di_lu, di_port, di_target"] */
+static void
+device_id_vpd_variants(uint8_t * buff, int len, int subvalue,
+ struct opts_t * op, sgj_opaque_p jap)
+{
+ int m_a, blen;
+ uint8_t * b;
+
+ if (len < 4) {
+ pr2serr("Device identification VPD page length too short=%d\n", len);
+ return;
+ }
+ blen = len - 4;
+ b = buff + 4;
+ m_a = -1;
+ if (0 == subvalue) {
+ filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_LU), 0, b, blen,
+ VPD_ASSOC_LU, op, jap);
+ filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TPORT), 0, b, blen,
+ VPD_ASSOC_TPORT, op, jap);
+ filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TDEVICE), 0, b, blen,
+ VPD_ASSOC_TDEVICE, op, jap);
+ } else if (VPD_DI_SEL_AS_IS == subvalue)
+ filter_dev_ids(NULL, 0, b, blen, m_a, op, jap);
+ else {
+ if (VPD_DI_SEL_LU & subvalue)
+ filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_LU), 0, b, blen,
+ VPD_ASSOC_LU, op, jap);
+ if (VPD_DI_SEL_TPORT & subvalue)
+ filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TPORT), 0, b,
+ blen, VPD_ASSOC_TPORT, op, jap);
+ if (VPD_DI_SEL_TARGET & subvalue)
+ filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TDEVICE), 0,
+ b, blen, VPD_ASSOC_TDEVICE, op, jap);
+ }
+}
+
+static void /* VPD_SUPPORTED_VPDS ["sv"] */
+decode_supported_vpd_4vpd(uint8_t * buff, int len, struct opts_t * op,
+ sgj_opaque_p jap)
+{
+ uint8_t pn;
+ int k, rlen, pdt;
+ sgj_state * jsp = &op->json_st;
+ sgj_opaque_p jo2p;
+ const struct svpd_values_name_t * vnp;
+ uint8_t * bp;
+ char b[144];
+ static const int blen = sizeof(b);
+ static const char * svps = "Supported VPD pages";
+
+ if ((1 == op->do_hex) || (op->do_hex > 2)) {
+ hex2stdout(buff, len, no_ascii_4hex(op));
+ return;
+ }
+ pdt = PDT_MASK & buff[0];
+ rlen = buff[3] + 4;
+ if (rlen > len)
+ pr2serr("%s VPD page truncated, indicates %d, got %d\n", svps, rlen,
+ len);
+ else
+ len = rlen;
+ if (len < 4) {
+ pr2serr("%s VPD page length too short=%d\n", svps, len);
+ return;
+ }
+ len -= 4;
+ bp = buff + 4;
+
+ for (k = 0; k < len; ++k) {
+ pn = bp[k];
+ snprintf(b, blen, "0x%02x", pn);
+ vnp = sdp_get_vpd_detail(pn, -1, pdt);
+ if (vnp) {
+ if (op->do_long)
+ sgj_pr_hr(jsp, " %s %s [%s]\n", b, vnp->name, vnp->acron);
+ else
+ sgj_pr_hr(jsp, " %s [%s]\n", vnp->name, vnp->acron);
+ } else if (op->vend_prod_num >= 0) {
+ vnp = svpd_find_vendor_by_num(pn, op->vend_prod_num);
+ if (vnp) {
+ if (op->do_long)
+ sgj_pr_hr(jsp, " %s %s [%s]\n", b, vnp->name,
+ vnp->acron);
+ else
+ sgj_pr_hr(jsp, " %s [%s]\n", vnp->name, vnp->acron);
+ } else
+ sgj_pr_hr(jsp, " %s\n", b);
+ } else
+ sgj_pr_hr(jsp, " %s\n", b);
+ if (jsp->pr_as_json) {
+ jo2p = sgj_new_unattached_object_r(jsp);
+ sgj_js_nv_i(jsp, jo2p, "i", pn);
+ sgj_js_nv_s(jsp, jo2p, "hex", b + 2);
+ if (vnp) {
+ sgj_js_nv_s(jsp, jo2p, "name", vnp->name);
+ sgj_js_nv_s(jsp, jo2p, "acronym", vnp->acron);
+ } else {
+ sgj_js_nv_s(jsp, jo2p, "name", "unknown");
+ sgj_js_nv_s(jsp, jo2p, "acronym", "unknown");
+ }
+ sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
+ }
+ }
+}
+
+/* VPD_SCSI_PORTS 0x88 ["sp"] */
+static void
+decode_scsi_ports_vpd_4vpd(uint8_t * buff, int len, struct opts_t * op,
+ sgj_opaque_p jap)
+{
+ int k, bump, rel_port, ip_tid_len, tpd_len;
+ sgj_state * jsp = &op->json_st;
+ sgj_opaque_p jo2p = NULL;
+ sgj_opaque_p ja2p = NULL;
+ uint8_t * bp;
+
+ if ((1 == op->do_hex) || (op->do_hex > 2)) {
+ hex2stdout(buff, len, no_ascii_4hex(op));
+ return;
+ }
+ if (len < 4) {
+ pr2serr("SCSI Ports VPD page length too short=%d\n", len);
+ return;
+ }
+ len -= 4;
+ bp = buff + 4;
+ for (k = 0; k < len; k += bump, bp += bump) {
+ rel_port = sg_get_unaligned_be16(bp + 2);
+ sgj_pr_hr(jsp, " Relative port=%d\n", rel_port);
+ jo2p = sgj_new_unattached_object_r(jsp);
+ sgj_js_nv_i(jsp, jo2p, "relative_port", rel_port);
+ ip_tid_len = sg_get_unaligned_be16(bp + 6);
+ bump = 8 + ip_tid_len;
+ if ((k + bump) > len) {
+ pr2serr("SCSI Ports VPD page, short descriptor "
+ "length=%d, left=%d\n", bump, (len - k));
+ return;
+ }
+ if (ip_tid_len > 0) {
+ if (op->do_hex > 1) {
+ sgj_pr_hr(jsp, " Initiator port transport id:\n");
+ hex2stdout((bp + 8), ip_tid_len, 1);
+ } else {
+ char b[1024];
+
+ sg_decode_transportid_str(" ", bp + 8, ip_tid_len,
+ true, sizeof(b), b);
+ if (jsp->pr_as_json)
+ sgj_js_nv_s(jsp, jo2p, "initiator_port_transport_id", b);
+ sgj_pr_hr(jsp, "%s",
+ sg_decode_transportid_str(" ", bp + 8,
+ ip_tid_len, true, sizeof(b), b));
+ }
+ }
+ tpd_len = sg_get_unaligned_be16(bp + bump + 2);
+ if ((k + bump + tpd_len + 4) > len) {
+ pr2serr("SCSI Ports VPD page, short descriptor(tgt) "
+ "length=%d, left=%d\n", bump, (len - k));
+ return;
+ }
+ if (tpd_len > 0) {
+ if (op->do_hex > 1) {
+ sgj_pr_hr(jsp, " Target port descriptor(s):\n");
+ hex2stdout(bp + bump + 4, tpd_len, 1);
+ } else {
+ if ((0 == op->do_quiet) || (ip_tid_len > 0))
+ sgj_pr_hr(jsp, " Target port descriptor(s):\n");
+ if (jsp->pr_as_json) {
+ sgj_opaque_p jo3p = sgj_named_subobject_r(jsp, jo2p,
+ "target_port");
+
+ ja2p = sgj_named_subarray_r(jsp, jo3p,
+ "designation_descriptor_list");
+ }
+ filter_dev_ids("", 2 /* leading spaces */, bp + bump + 4,
+ tpd_len, VPD_ASSOC_TPORT, op, ja2p);
+ }
+ }
+ bump += tpd_len + 4;
+ sgj_js_nv_o(jsp, jap, NULL, jo2p);
+ }
+}
+
+/* Prints outs an abridged set of device identification designators
+ selected by association, designator type and/or code set. Not used
+ for JSON output. */
+static int
+filter_dev_ids_quiet(uint8_t * buff, int len, int m_assoc)
+{
+ int k, m, p_id, c_set, piv, desig_type, i_len, naa, off, u;
+ int assoc, is_sas, rtp;
+ const uint8_t * bp;
+ const uint8_t * ip;
+ uint8_t sas_tport_addr[8];
+
+ rtp = 0;
+ memset(sas_tport_addr, 0, sizeof(sas_tport_addr));
+ for (k = 0, off = -1; true; ++k) {
+ if ((0 == k) && (0 != buff[2])) {
+ /* first already in buff */
+ if (m_assoc != VPD_ASSOC_LU)
+ return 0;
+ ip = buff;
+ c_set = 1;
+ assoc = VPD_ASSOC_LU;
+ is_sas = 0;
+ desig_type = 3;
+ i_len = 16;
+ } else {
+ u = sg_vpd_dev_id_iter(buff, len, &off, m_assoc, -1, -1);
+ if (0 != u)
+ break;
+ bp = buff + off;
+ i_len = bp[3];
+ if ((off + i_len + 4) > len) {
+ pr2serr(" VPD page error: designator length longer than\n"
+ " remaining response length=%d\n", (len - off));
+ return SG_LIB_CAT_MALFORMED;
+ }
+ ip = bp + 4;
+ p_id = ((bp[0] >> 4) & 0xf);
+ c_set = (bp[0] & 0xf);
+ piv = ((bp[1] & 0x80) ? 1 : 0);
+ is_sas = (piv && (6 == p_id)) ? 1 : 0;
+ assoc = ((bp[1] >> 4) & 0x3);
+ desig_type = (bp[1] & 0xf);
+ }
+ switch (desig_type) {
+ case 0: /* vendor specific */
+ break;
+ case 1: /* T10 vendor identification */
+ break;
+ case 2: /* EUI-64 based */
+ if ((8 != i_len) && (12 != i_len) && (16 != i_len))
+ pr2serr(" << expect 8, 12 and 16 byte "
+ "EUI, got %d>>\n", i_len);
+ printf(" 0x");
+ for (m = 0; m < i_len; ++m)
+ printf("%02x", (unsigned int)ip[m]);
+ printf("\n");
+ break;
+ case 3: /* NAA */
+ naa = (ip[0] >> 4) & 0xff;
+ if (1 != c_set) {
+ pr2serr(" << expected binary code_set (1), got %d for "
+ "NAA=%d>>\n", c_set, naa);
+ hex2stderr(ip, i_len, 0);
+ break;
+ }
+ switch (naa) {
+ case 2: /* NAA IEEE extended */
+ if (8 != i_len) {
+ pr2serr(" << unexpected NAA 2 identifier "
+ "length: 0x%x>>\n", i_len);
+ hex2stderr(ip, i_len, 0);
+ break;
+ }
+ printf(" 0x");
+ for (m = 0; m < 8; ++m)
+ printf("%02x", (unsigned int)ip[m]);
+ printf("\n");
+ break;
+ case 3: /* Locally assigned */
+ case 5: /* IEEE Registered */
+ if (8 != i_len) {
+ pr2serr(" << unexpected NAA 3 or 5 "
+ "identifier length: 0x%x>>\n", i_len);
+ hex2stderr(ip, i_len, 0);
+ break;
+ }
+ if ((0 == is_sas) || (1 != assoc)) {
+ printf(" 0x");
+ for (m = 0; m < 8; ++m)
+ printf("%02x", (unsigned int)ip[m]);
+ printf("\n");
+ } else if (rtp) {
+ printf(" 0x");
+ for (m = 0; m < 8; ++m)
+ printf("%02x", (unsigned int)ip[m]);
+ printf(",0x%x\n", rtp);
+ rtp = 0;
+ } else {
+ if (sas_tport_addr[0]) {
+ printf(" 0x");
+ for (m = 0; m < 8; ++m)
+ printf("%02x", (unsigned int)sas_tport_addr[m]);
+ printf("\n");
+ }
+ memcpy(sas_tport_addr, ip, sizeof(sas_tport_addr));
+ }
+ break;
+ case 6: /* NAA IEEE registered extended */
+ if (16 != i_len) {
+ pr2serr(" << unexpected NAA 6 identifier length: "
+ "0x%x>>\n", i_len);
+ hex2stderr(ip, i_len, 0);
+ break;
+ }
+ printf(" 0x");
+ for (m = 0; m < 16; ++m)
+ printf("%02x", (unsigned int)ip[m]);
+ printf("\n");
+ break;
+ default:
+ pr2serr(" << bad NAA nibble, expected 2, 3, 5 or 6, got "
+ "%d>>\n", naa);
+ hex2stderr(ip, i_len, 0);
+ break;
+ }
+ break;
+ case 4: /* Relative target port */
+ if ((0 == is_sas) || (1 != c_set) || (1 != assoc) || (4 != i_len))
+ break;
+ rtp = sg_get_unaligned_be16(ip + 2);
+ if (sas_tport_addr[0]) {
+ printf(" 0x");
+ for (m = 0; m < 8; ++m)
+ printf("%02x", (unsigned int)sas_tport_addr[m]);
+ printf(",0x%x\n", rtp);
+ memset(sas_tport_addr, 0, sizeof(sas_tport_addr));
+ rtp = 0;
+ }
+ break;
+ case 5: /* (primary) Target port group */
+ break;
+ case 6: /* Logical unit group */
+ break;
+ case 7: /* MD5 logical unit identifier */
+ break;
+ case 8: /* SCSI name string */
+ if (c_set < 2) { /* quietly accept ASCII for UTF-8 */
+ pr2serr(" << expected UTF-8 code_set>>\n");
+ hex2stderr(ip, i_len, 0);
+ break;
+ }
+ if (! (strncmp((const char *)ip, "eui.", 4) ||
+ strncmp((const char *)ip, "EUI.", 4) ||
+ strncmp((const char *)ip, "naa.", 4) ||
+ strncmp((const char *)ip, "NAA.", 4) ||
+ strncmp((const char *)ip, "iqn.", 4))) {
+ pr2serr(" << expected name string prefix>>\n");
+ hex2stderr(ip, i_len, -1);
+ break;
+ }
+ /* does %s print out UTF-8 ok??
+ * Seems to depend on the locale. Looks ok here with my
+ * locale setting: en_AU.UTF-8
+ */
+ printf(" %.*s\n", i_len, (const char *)ip);
+ break;
+ case 9: /* Protocol specific port identifier */
+ break;
+ case 0xa: /* UUID identifier [spc5r08] RFC 4122 */
+ if ((1 != c_set) || (18 != i_len) || (1 != ((ip[0] >> 4) & 0xf)))
+ break;
+ for (m = 0; m < 16; ++m) {
+ if ((4 == m) || (6 == m) || (8 == m) || (10 == m))
+ printf("-");
+ printf("%02x", (unsigned int)ip[2 + m]);
+ }
+ printf("\n");
+ break;
+ default: /* reserved */
+ break;
+ }
+ }
+ if (sas_tport_addr[0]) {
+ printf(" 0x");
+ for (m = 0; m < 8; ++m)
+ printf("%02x", (unsigned int)sas_tport_addr[m]);
+ printf("\n");
+ }
+ if (-2 == u) {
+ pr2serr("VPD page error: short designator around offset %d\n", off);
+ return SG_LIB_CAT_MALFORMED;
+ }
+ return 0;
+}
+
+/* Prints outs designation descriptors (dd_s) selected by association,
+ designator type and/or code set. VPD_DEVICE_ID and VPD_SCSI_PORTS */
+static int
+filter_dev_ids(const char * print_if_found, int num_leading, uint8_t * buff,
+ int len, int m_assoc, struct opts_t * op, sgj_opaque_p jap)
+{
+ bool printed, sgj_out_hr;
+ int assoc, off, u, i_len;
+ const uint8_t * bp;
+ sgj_state * jsp = &op->json_st;
+ char b[1024];
+ char sp[82];
+ static const int blen = sizeof(b);
+
+ if (op->do_quiet && (! jsp->pr_as_json))
+ return filter_dev_ids_quiet(buff, len, m_assoc);
+ sgj_out_hr = false;
+ if (jsp->pr_as_json) {
+ int ret = filter_json_dev_ids(buff, len, m_assoc, op, jap);
+
+ if (ret || (! jsp->pr_out_hr))
+ return ret;
+ sgj_out_hr = true;
+ }
+ if (num_leading > (int)(sizeof(sp) - 2))
+ num_leading = sizeof(sp) - 2;
+ if (num_leading > 0)
+ snprintf(sp, sizeof(sp), "%*c", num_leading, ' ');
+ else
+ sp[0] = '\0';
+ if (buff[2] != 0) { /* all valid dd_s should have 0 in this byte */
+ if (op->verbose)
+ pr2serr("%s: designation descriptors byte 2 should be 0\n"
+ "perhaps this is a standard inquiry response, ignore\n",
+ __func__);
+ return 0;
+ }
+ off = -1;
+ printed = false;
+ while ((u = sg_vpd_dev_id_iter(buff, len, &off, m_assoc, -1, -1)) == 0) {
+ bp = buff + off;
+ i_len = bp[3];
+ if ((off + i_len + 4) > len) {
+ pr2serr(" VPD page error: designator length longer than\n"
+ " remaining response length=%d\n", (len - off));
+ return SG_LIB_CAT_MALFORMED;
+ }
+ assoc = ((bp[1] >> 4) & 0x3);
+ if (print_if_found && (! printed)) {
+ printed = true;
+ if (strlen(print_if_found) > 0) {
+ snprintf(b, blen, " %s:", print_if_found);
+ if (sgj_out_hr)
+ sgj_js_str_out(jsp, b, strlen(b));
+ else
+ printf("%s\n", b);
+ }
+ }
+ if (NULL == print_if_found) {
+ snprintf(b, blen, " %s%s:", sp, sg_get_desig_assoc_str(assoc));
+ if (sgj_out_hr)
+ sgj_js_str_out(jsp, b, strlen(b));
+ else
+ printf("%s\n", b);
+ }
+ sg_get_designation_descriptor_str(sp, bp, i_len + 4, false,
+ op->do_long, blen, b);
+ if (sgj_out_hr)
+ sgj_js_str_out(jsp, b, strlen(b));
+ else
+ printf("%s", b);
+ }
+ if (-2 == u) {
+ pr2serr("VPD page error: short designator around offset %d\n", off);
+ return SG_LIB_CAT_MALFORMED;
+ }
+ return 0;
+}
+
+/* VPD_BLOCK_LIMITS sbc */
+/* VPD_SA_DEV_CAP ssc */
+/* VPD_OSD_INFO osd */
+static void
+decode_b0_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jop)
+{
+ int pdt = PDT_MASK & buff[0];
+ sgj_state * jsp = &op->json_st;
+
+ if (op->do_hex) {
+ hex2stdout(buff, len, no_ascii_4hex(op));
+ return;
+ }
+ switch (pdt) {
+ case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+ /* now done by decode_block_limits_vpd() in sg_vpd_common.c */
+ break;
+ case PDT_TAPE: case PDT_MCHANGER:
+ sgj_haj_vi_nex(jsp, jop, 2, "TSMC", SGJ_SEP_EQUAL_NO_SPACE,
+ !!(buff[4] & 0x2), false, "Tape Stream Mirror "
+ "Capable");
+ sgj_haj_vi_nex(jsp, jop, 2, "WORM", SGJ_SEP_EQUAL_NO_SPACE,
+ !!(buff[4] & 0x1), false, "Write Once Read Multiple "
+ "supported");
+ break;
+ case PDT_OSD:
+ default:
+ pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt);
+ hex2stderr(buff, len, 0);
+ break;
+ }
+}
+
+/* VPD_BLOCK_DEV_CHARS sbc 0xb1 ["bdc"] */
+/* VPD_MAN_ASS_SN ssc */
+/* VPD_SECURITY_TOKEN osd */
+/* VPD_ES_DEV_CHARS ses-4 */
+static void
+decode_b1_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jop)
+{
+ int pdt;
+ sgj_state * jsp = &op->json_st;
+
+ pdt = buff[0] & PDT_MASK;
+ if (op->do_hex) {
+ hex2stdout(buff, len, no_ascii_4hex(op));
+ return;
+ }
+ switch (pdt) {
+ case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+ /* now done by decode_block_dev_ch_vpd() in sg_vpd_common.c */
+ case PDT_TAPE: case PDT_MCHANGER: case PDT_ADC:
+ sgj_pr_hr(jsp, " Manufacturer-assigned serial number: %.*s\n",
+ len - 4, buff + 4);
+ sgj_js_nv_s_len(jsp, jop, "manufacturer_assigned_serial_number",
+ (const char *)buff + 4, len - 4);
+ break;
+ default:
+ pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt);
+ hex2stderr(buff, len, 0);
+ break;
+ }
+}
+
+/* VPD_LB_PROVISIONING sbc */
+/* VPD_TA_SUPPORTED ssc */
+static void
+decode_b2_vpd(uint8_t * buff, int len, int pdt, struct opts_t * op)
+{
+ if (op->do_hex) {
+ hex2stdout(buff, len, no_ascii_4hex(op));
+ return;
+ }
+ switch (pdt) {
+ case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+ /* decode_block_lb_prov_vpd() is now in sg_vpd_common.c */
+ break;
+ case PDT_TAPE: case PDT_MCHANGER:
+ /* decode_tapealert_supported_vpd() is now in sg_vpd_common.c */
+ break;
+ default:
+ pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt);
+ hex2stderr(buff, len, 0);
+ break;
+ }
+}
+
+/* VPD_REFERRALS sbc 0xb3 ["ref"] */
+/* VPD_AUTOMATION_DEV_SN ssc 0xb3 ["adsn"] */
+static void
+decode_b3_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jop)
+{
+ int pdt;
+ sgj_state * jsp = &op->json_st;
+
+ if (op->do_hex) {
+ hex2stdout(buff, len, no_ascii_4hex(op));
+ return;
+ }
+ pdt = buff[0] & PDT_MASK;
+ switch (pdt) {
+ case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+ /* now done in decode_referrals_vpd() in sg_vpd_common.c */
+ break;
+ case PDT_TAPE: case PDT_MCHANGER:
+ sgj_pr_hr(jsp, " Automation device serial number: %.*s\n",
+ len - 4, buff + 4);
+ sgj_js_nv_s_len(jsp, jop, "automation_device_serial_number",
+ (const char *)buff + 4, len - 4);
+ break;
+ default:
+ pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt);
+ hex2stderr(buff, len, 0);
+ break;
+ }
+}
+
+/* VPD_SUP_BLOCK_LENS sbc ["sbl"] */
+/* VPD_DTDE_ADDRESS ssc */
+static void
+decode_b4_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jop)
+{
+ int pdt = buff[0] & PDT_MASK;
+ sgj_state * jsp = &op->json_st;
+
+ if (op->do_hex) {
+ hex2stdout(buff, len, no_ascii_4hex(op));
+ return;
+ }
+ switch (pdt) {
+ case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+ /* now done by decode_sup_block_lens_vpd() in sg_vpd_common.c */
+ break;
+ case PDT_TAPE: case PDT_MCHANGER:
+ sgj_pr_hr(jsp, " Device transfer data element:\n");
+ if (! jsp->pr_as_json)
+ hex2stdout(buff + 4, len - 4, 1);
+ sgj_js_nv_hex_bytes(jsp, jop, "device_transfer_data_element",
+ buff + 4, len - 4);
+ break;
+ default:
+ pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt);
+ hex2stderr(buff, len, 0);
+ break;
+ }
+}
+
+/* VPD_BLOCK_DEV_C_EXTENS sbc */
+/* VPD_LB_PROTECTION 0xb5 ["lbpro"] ssc */
+static void
+decode_b5_vpd(uint8_t * b, int len, int do_hex, int pdt)
+{
+ if (do_hex) {
+ hex2stdout(b, len, (1 == do_hex) ? 0 : -1);
+ return;
+ }
+ switch (pdt) {
+ case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+ /* now done by decode_block_dev_char_ext_vpd() in sg_vpd_common.c */
+ break;
+ case PDT_TAPE: case PDT_MCHANGER:
+ /* now done by decode_lb_protection_vpd() in sg_vpd_common.c */
+ break;
+ default:
+ pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt);
+ hex2stderr(b, len, 0);
+ break;
+ }
+}
+
+/* Returns 0 if successful */
+static int
+svpd_unable_to_decode(int sg_fd, struct opts_t * op, int subvalue, int off)
+{
+ bool as_json, json_o_hr, hex0;
+ int res, len, n;
+ sgj_state * jsp = &op->json_st;
+ uint8_t * rp;
+
+ as_json = jsp->pr_as_json;
+ json_o_hr = as_json && jsp->pr_out_hr;
+ hex0 = (0 == op->do_hex);
+ rp = rsp_buff + off;
+ if (hex0 && (! op->do_raw) && (! op->examine_given))
+ sgj_pr_hr(jsp, "Only hex output supported\n");
+ if ((!op->do_raw) && (op->do_hex < 2) && (! op->examine_given)) {
+ if (subvalue) {
+ if (hex0)
+ sgj_pr_hr(jsp, "VPD page code=0x%.2x, subvalue=0x%.2x:\n",
+ op->vpd_pn, subvalue);
+ else
+ printf("VPD page code=0x%.2x, subvalue=0x%.2x:\n", op->vpd_pn,
+ subvalue);
+ } else if (op->vpd_pn >= 0) {
+ if (hex0)
+ sgj_pr_hr(jsp, "VPD page code=0x%.2x:\n", op->vpd_pn);
+ else
+ printf("VPD page code=0x%.2x:\n", op->vpd_pn);
+ } else {
+ if (hex0)
+ sgj_pr_hr(jsp, "VPD page code=%d:\n", op->vpd_pn);
+ else
+ printf("VPD page code=%d:\n", op->vpd_pn);
+ }
+ }
+
+ res = vpd_fetch_page(sg_fd, rp, op->vpd_pn, op->maxlen, op->do_quiet,
+ op->verbose, &len);
+ if (0 == res) {
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (json_o_hr && hex0 && (len > 0) && (len < UINT16_MAX)) {
+ char * p;
+
+ n = len * 4;
+ p = malloc(n);
+ if (p) {
+ n = hex2str(rp, len, NULL, 1, n - 1, p);
+ sgj_js_str_out(jsp, p, n);
+ }
+ } else
+ hex2stdout(rp, len, no_ascii_4hex(op));
+ }
+ } else if ((! op->do_quiet) && (! op->examine_given)) {
+ if (op->vpd_pn >= 0)
+ pr2serr("fetching VPD page code=0x%.2x: failed\n", op->vpd_pn);
+ else
+ pr2serr("fetching VPD page code=%d: failed\n", op->vpd_pn);
+ }
+ return res;
+}
+
+static int
+recurse_vpd_decode(struct opts_t * op, sgj_opaque_p jop, int off)
+{
+ int res = svpd_decode_t10(-1, op, jop, 0, off, NULL);
+
+ if (SG_LIB_CAT_OTHER == res) {
+ res = svpd_decode_vendor(-1, op, jop, off);
+ if (SG_LIB_CAT_OTHER == res)
+ svpd_unable_to_decode(-1, op, 0, off);
+ }
+ return res;
+}
+
+/* Returns 0 if successful. If don't know how to decode, returns
+ * SG_LIB_CAT_OTHER else see sg_ll_inquiry(). */
+static int
+svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop,
+ int subvalue, int off, const char * prefix)
+{
+ bool allow_name, allow_if_found, long_notquiet, qt;
+ bool vpd_supported = false;
+ bool inhex_active = (-1 == sg_fd);
+ bool exam_not_given = ! op->examine_given;
+ int len, pdt, pqual, num, k, resid, alloc_len, pn, vb;
+ int res = 0;
+ sgj_state * jsp = &op->json_st;
+ uint8_t * rp;
+ sgj_opaque_p jap = NULL;
+ sgj_opaque_p jo2p = NULL;
+ const char * np;
+ const char * ep;
+ const char * pre = (prefix ? prefix : "");
+ const char * pdt_str;
+ bool as_json = jsp->pr_as_json;
+ bool not_json = ! as_json;
+ char obuff[DEF_ALLOC_LEN];
+ char d[48];
+
+ vb = op->verbose;
+ qt = op->do_quiet;
+ long_notquiet = op->do_long && (! op->do_quiet);
+ if (op->do_raw || (op->do_quiet && (! op->do_long) && (! op->do_all)) ||
+ (op->do_hex >= 3) || op->examine_given)
+ allow_name = false;
+ else
+ allow_name = true;
+ allow_if_found = op->examine_given && (! op->do_quiet);
+ rp = rsp_buff + off;
+ pn = op->vpd_pn;
+ if ((off > 0) && (VPD_NOPE_WANT_STD_INQ != op->vpd_pn))
+ pn = rp[1];
+ else
+ pn = op->vpd_pn;
+ if (!inhex_active && !op->do_force && exam_not_given &&
+ pn != VPD_NOPE_WANT_STD_INQ &&
+ pn != VPD_SUPPORTED_VPDS) {
+ res = vpd_fetch_page(sg_fd, rp, VPD_SUPPORTED_VPDS, op->maxlen, qt,
+ vb, &len);
+ if (res)
+ return res;
+
+ num = rp[3];
+ if (num > (len - 4))
+ num = (len - 4);
+ if (vb > 1) {
+ pr2serr("Supported VPD pages, hex list: ");
+ hex2stderr(rp + 4, num, -1);
+ }
+ for (k = 0; k < num; ++k) {
+ if (pn == rp[4 + k]) {
+ vpd_supported = true;
+ break;
+ }
+ }
+ if (! vpd_supported) { /* get creative, was SG_LIB_CAT_ILLEGAL_REQ */
+ if (vb)
+ pr2serr("Given VPD page not in supported list, use --force "
+ "to override this check\n");
+ return sg_convert_errno(EDOM);
+ }
+ }
+ pdt = rp[0] & PDT_MASK;
+ pdt_str = sg_get_pdt_str(pdt, sizeof(d), d);
+ pqual = (rp[0] & 0xe0) >> 5;
+
+ switch(pn) {
+ case VPD_NOPE_WANT_STD_INQ: /* -2 (want standard inquiry response) */
+ if (!inhex_active) {
+ if (op->maxlen > 0)
+ alloc_len = op->maxlen;
+ else if (op->do_long)
+ alloc_len = DEF_ALLOC_LEN;
+ else
+ alloc_len = 36;
+ res = sg_ll_inquiry_v2(sg_fd, false, 0, rp, alloc_len,
+ DEF_PT_TIMEOUT, &resid, ! op->do_quiet, vb);
+ } else {
+ alloc_len = op->maxlen;
+ resid = 0;
+ res = 0;
+ }
+ if (0 == res) {
+ alloc_len -= resid;
+ if (op->do_raw)
+ dStrRaw(rp, alloc_len);
+ else if (op->do_hex) {
+ if (! op->do_quiet && (op->do_hex < 3))
+ sgj_pr_hr(jsp, "Standard Inquiry data format:\n");
+ hex2stdout(rp, alloc_len, (1 == op->do_hex) ? 0 : -1);
+ } else
+ std_inq_decode(rp, alloc_len, op, jop);
+ return 0;
+ }
+ break;
+ case VPD_SUPPORTED_VPDS: /* 0x0 ["sv"] */
+ np = "Supported VPD pages VPD page";
+ if (allow_name)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ if (! allow_name && allow_if_found)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else if (op->do_hex)
+ hex2stdout(rp, len, no_ascii_4hex(op));
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ num = rp[3];
+ if (num > (len - 4))
+ num = (len - 4);
+ if (as_json) {
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ jap = sgj_named_subarray_r(jsp, jo2p,
+ "supported_vpd_page_list");
+ }
+ decode_supported_vpd_4vpd(rp, len, op, jap);
+ }
+ return 0;
+ }
+ break;
+ case VPD_UNIT_SERIAL_NUM: /* 0x80 ["sn"] */
+ np = "Unit serial number VPD page";
+ if (allow_name && not_json)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ if (! allow_name && allow_if_found)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else if (op->do_hex)
+ hex2stdout(rp, len, no_ascii_4hex(op));
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ memset(obuff, 0, sizeof(obuff));
+ len -= 4;
+ if (len >= (int)sizeof(obuff))
+ len = sizeof(obuff) - 1;
+ memcpy(obuff, rp + 4, len);
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ sgj_haj_vs(jsp, jo2p, 2, np, SGJ_SEP_COLON_1_SPACE, obuff);
+ }
+ return 0;
+ }
+ break;
+ case VPD_DEVICE_ID: /* 0x83 ["di, di_asis, di_lu, di_port, di_target"] */
+ np = "Device Identification VPD page";
+ if (allow_name)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ if (! allow_name && allow_if_found)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else if (op->do_hex)
+ hex2stdout(rp, len, no_ascii_4hex(op));
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json) {
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ jap = sgj_named_subarray_r(jsp, jo2p,
+ "designation_descriptor_list");
+ }
+ device_id_vpd_variants(rp, len, subvalue, op, jap);
+ }
+ return 0;
+ }
+ break;
+ case VPD_SOFTW_INF_ID: /* 0x84 ["sii"] */
+ np = "Software interface identification VPD page";
+ if (allow_name)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ if (! allow_name && allow_if_found)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json) {
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ jap = sgj_named_subarray_r(jsp, jo2p,
+ "software_interface_identifier_list");
+ }
+ decode_softw_inf_id(rp, len, op, jap);
+ }
+ return 0;
+ }
+ break;
+ case VPD_MAN_NET_ADDR: /* 0x85 ["mna"] */
+ np= "Management network addresses VPD page";
+ if (allow_name)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ if (! allow_name && allow_if_found)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (as_json) {
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ jap = sgj_named_subarray_r(jsp, jo2p,
+ "network_services_descriptor_list");
+ }
+ decode_net_man_vpd(rp, len, op, jap);
+ }
+ return 0;
+ }
+ break;
+ case VPD_EXT_INQ: /* 0x86 ["ei"] */
+ np = "extended INQUIRY data VPD page";
+ if (allow_name)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ if (! allow_name && allow_if_found)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ bool protect = false;
+
+ op->protect_not_sure = false;
+ if (op->std_inq_a_valid)
+ protect = !! (0x1 & op->std_inq_a[5]);
+ else if ((sg_fd >= 0) && (! op->do_force)) {
+ struct sg_simple_inquiry_resp sir;
+
+ res = sg_simple_inquiry(sg_fd, &sir, false, vb);
+ if (res) {
+ if (op->verbose)
+ pr2serr("%s: sg_simple_inquiry() failed, "
+ "res=%d\n", __func__, res);
+ op->protect_not_sure = true;
+ } else
+ protect = !!(sir.byte_5 & 0x1); /* SPC-3 and later */
+ } else
+ op->protect_not_sure = true;
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp," [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json)
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ decode_x_inq_vpd(rp, len, protect, op, jo2p);
+ }
+ return 0;
+ }
+ break;
+ case VPD_MODE_PG_POLICY: /* 0x87 */
+ np = "Mode page policy VPD page";
+ if (allow_name)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ if (! allow_name && allow_if_found)
+ sgj_pr_hr(jsp, "%s%s:\n", (prefix ? prefix : ""), np);
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json) {
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ jap = sgj_named_subarray_r(jsp, jo2p,
+ "mode_page_policy_descriptor_list");
+ }
+ decode_mode_policy_vpd(rp, len, op, jap);
+ }
+ return 0;
+ }
+ break;
+ case VPD_SCSI_PORTS: /* 0x88 ["sp"] */
+ np = "SCSI Ports VPD page";
+ if (allow_name)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ if (! allow_name && allow_if_found)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json) {
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ jap = sgj_named_subarray_r(jsp, jo2p,
+ "scsi_ports_descriptor_list");
+ }
+ decode_scsi_ports_vpd_4vpd(rp, len, op, jap);
+ }
+ return 0;
+ }
+ break;
+ case VPD_ATA_INFO: /* 0x89 ['ai"] */
+ np = "ATA information VPD page";
+ if (allow_name)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ alloc_len = op->maxlen ? op->maxlen : VPD_ATA_INFO_LEN;
+ res = vpd_fetch_page(sg_fd, rp, pn, alloc_len, qt, vb, &len);
+ if (0 == res) {
+ if (! allow_name && allow_if_found)
+ sgj_pr_hr(jsp, "%s%s:\n", (prefix ? prefix : ""), np);
+ if ((2 == op->do_raw) || (3 == op->do_hex)) { /* for hdparm */
+ if (len < (60 + 512))
+ pr2serr("ATA_INFO VPD page len (%d) less than expected "
+ "572\n", len);
+ else
+ dWordHex((const unsigned short *)(rp + 60), 256, -2,
+ sg_is_big_endian());
+ }
+ else if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json)
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ decode_ata_info_vpd(rp, len, op, jo2p);
+ }
+ return 0;
+ }
+ break;
+ case VPD_POWER_CONDITION: /* 0x8a ["pc"] */
+ np = "Power condition VPD page:";
+ if (allow_name)
+ sgj_pr_hr(jsp, "%s%s\n", pre, np);
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ if (! allow_name && allow_if_found)
+ sgj_pr_hr(jsp, "%s%s\n", pre, np);
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json)
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ decode_power_condition(rp, len, op, jo2p);
+ }
+ return 0;
+ }
+ break;
+ case VPD_DEVICE_CONSTITUENTS: /* 0x8b ["dc"] */
+ np = "Device constituents VPD page";
+ if (allow_name)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ if (! allow_name && allow_if_found)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json) {
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ jap = sgj_named_subarray_r(jsp, jo2p,
+ "constituent_descriptor_list");
+ }
+ decode_dev_constit_vpd(rp, len, op, jap, recurse_vpd_decode);
+ }
+ return 0;
+ }
+ break;
+ case VPD_CFA_PROFILE_INFO: /* 0x8c ["cfa"] */
+ np = "CFA profile information VPD page";
+ if (allow_name)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ if (! allow_name && allow_if_found)
+ sgj_pr_hr(jsp, "%s%s\n", pre, np);
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json) {
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ jap = sgj_named_subarray_r(jsp, jo2p,
+ "cfa_profile_descriptor_list");
+ }
+ decode_cga_profile_vpd(rp, len, op, jap);
+ }
+ return 0;
+ }
+ break;
+ case VPD_POWER_CONSUMPTION: /* 0x8d ["psm"] */
+ np = "Power consumption VPD page";
+ if (allow_name)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ if (! allow_name && allow_if_found)
+ sgj_pr_hr(jsp, "%s%s\n", pre, np);
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json) {
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ jap = sgj_named_subarray_r(jsp, jo2p,
+ "power_consumption_descriptor_list");
+ }
+ decode_power_consumption(rp, len, op, jap);
+ }
+ return 0;
+ }
+ break;
+ case VPD_3PARTY_COPY: /* 0x8f */
+ np = "Third party copy VPD page"; /* ["tpc"] */
+ if (allow_name)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ if (! allow_name && allow_if_found)
+ sgj_pr_hr(jsp, "%s%s\n", pre, np);
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json) {
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ jap = sgj_named_subarray_r(jsp, jo2p,
+ "third_party_copy_descriptors");
+ }
+ decode_3party_copy_vpd(rp, len, op, jap);
+ }
+ return 0;
+ }
+ break;
+ case VPD_PROTO_LU: /* 0x90 ["pslu"] */
+ np = "Protocol-specific logical unit information VPD page";
+ if (allow_name)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ if (! allow_name && allow_if_found)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json) {
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ jap = sgj_named_subarray_r(jsp, jo2p,
+ "logical_unit_information_descriptor_list");
+ }
+ decode_proto_lu_vpd(rp, len, op, jap);
+ }
+ return 0;
+ }
+ break;
+ case VPD_PROTO_PORT: /* 0x91 ["pspo"] */
+ np = "Protocol-specific port VPD page";
+ if (allow_name)
+ sgj_pr_hr(jsp, "%s%s\n", pre, np);
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ if (! allow_name && allow_if_found)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json) {
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ jap = sgj_named_subarray_r(jsp, jo2p,
+ "port_information_descriptor_list");
+ }
+ decode_proto_port_vpd(rp, len, op, jap);
+ }
+ return 0;
+ }
+ break;
+ case VPD_SCSI_FEATURE_SETS: /* 0x92 ["sfs"] */
+ np = "SCSI Feature sets VPD page";
+ if (allow_name)
+ sgj_pr_hr(jsp, "%s%s:\n", pre, np);
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ if (! allow_name && allow_if_found)
+ sgj_pr_hr(jsp, "%s%s\n", pre, np);
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json) {
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ jap = sgj_named_subarray_r(jsp, jo2p,
+ "feature_set_code_list");
+ }
+ decode_feature_sets_vpd(rp, len, op, jap);
+ }
+ return 0;
+ }
+ break;
+ case 0xb0: /* depends on pdt */
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ bool bl = false;
+ bool sad = false;
+ bool oi = false;
+
+ switch (pdt) {
+ case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+ np = "Block limits VPD page";
+ ep = "(SBC)";
+ bl = true;
+ break;
+ case PDT_TAPE: case PDT_MCHANGER:
+ np = "Sequential-access device capabilities VPD page";
+ ep = "(SSC)";
+ sad = true;
+ break;
+ case PDT_OSD:
+ np = "OSD information VPD page";
+ ep = "(OSD)";
+ oi = true;
+ break;
+ default:
+ np = NULL;
+ break;
+ }
+ if (NULL == np)
+ sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
+ else if (allow_name || allow_if_found)
+ sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : "");
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json)
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ if (bl)
+ decode_block_limits_vpd(rp, len, op, jo2p);
+ else if (sad) {
+ decode_b0_vpd(rp, len, op, jop);
+ } else if (oi) {
+ decode_b0_vpd(rp, len, op, jop);
+ } else {
+
+ }
+ }
+ return 0;
+ } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
+ exam_not_given)
+ sgj_pr_hr(jsp, "%sVPD page=0xb0\n", pre);
+ break;
+ case 0xb1: /* depends on pdt */
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ bool bdc = false;
+ static const char * masn =
+ "Manufactured-assigned serial number VPD page";
+
+ switch (pdt) {
+ case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+ np = "Block device characteristics VPD page";
+ ep = "(SBC)";
+ bdc = true;
+ break;
+ case PDT_TAPE: case PDT_MCHANGER:
+ np = masn;
+ ep = "(SSC)";
+ break;
+ case PDT_OSD:
+ np = "Security token VPD page";
+ ep = "(OSD)";
+ break;
+ case PDT_ADC:
+ np = masn;
+ ep = "(ADC)";
+ break;
+ default:
+ np = NULL;
+ break;
+ }
+ if (NULL == np)
+ sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
+ else if (allow_name || allow_if_found)
+ sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : "");
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json)
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ if (bdc)
+ decode_block_dev_ch_vpd(rp, len, op, jo2p);
+ else
+ decode_b1_vpd(rp, len, op, jo2p);
+ }
+ return 0;
+ } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
+ exam_not_given)
+ sgj_pr_hr(jsp, "%sVPD page=0xb1\n", pre);
+ break;
+ case 0xb2: /* VPD page depends on pdt */
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ bool lbpv = false;
+ bool tas = false;
+
+ switch (pdt) {
+ case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+ np = "Logical block provisioning VPD page";
+ ep = "(SBC)";
+ lbpv = true;
+ break;
+ case PDT_TAPE: case PDT_MCHANGER:
+ np = "TapeAlert supported flags VPD page";
+ ep = "(SSC)";
+ tas = true;
+ break;
+ default:
+ np = NULL;
+ break;
+ }
+ if (NULL == np)
+ sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
+ else if (allow_name || allow_if_found)
+ sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : "");
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json)
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ if (lbpv)
+ decode_block_lb_prov_vpd(rp, len, op, jo2p);
+ else if (tas)
+ decode_tapealert_supported_vpd(rp, len, op, jo2p);
+ else
+ decode_b2_vpd(rp, len, pdt, op);
+ }
+ return 0;
+ } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
+ exam_not_given)
+ sgj_pr_hr(jsp, "%sVPD page=0xb2\n", pre);
+ break;
+ case 0xb3: /* VPD page depends on pdt */
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ bool ref = false;
+
+ pdt = rp[0] & PDT_MASK;
+ switch (pdt) {
+ case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+ np = "Referrals VPD page";
+ ep = "(SBC)";
+ ref = true;
+ break;
+ case PDT_TAPE: case PDT_MCHANGER:
+ np = "Automation device serial number VPD page";
+ ep = "(SSC)";
+ break;
+ default:
+ np = NULL;
+ break;
+ }
+ if (NULL == np)
+ sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
+ else if (allow_name || allow_if_found)
+ sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : "");
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json)
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ if (ref)
+ decode_referrals_vpd(rp, len, op, jo2p);
+ else
+ decode_b3_vpd(rp, len, op, jo2p);
+ }
+ return 0;
+ } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
+ exam_not_given)
+ sgj_pr_hr(jsp, "%sVPD page=0xb3\n", pre);
+ break;
+ case 0xb4: /* VPD page depends on pdt */
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ bool sbl = false;
+
+ pdt = rp[0] & PDT_MASK;
+ switch (pdt) {
+ case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+ np = "Supported block lengths and protection types VPD page";
+ ep = "(SBC)";
+ sbl = true;
+ break;
+ case PDT_TAPE: case PDT_MCHANGER:
+ np = "Data transfer device element address";
+ ep = "(SSC)";
+ break;
+ default:
+ np = NULL;
+ break;
+ }
+ if (NULL == np)
+ sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
+ else if (allow_name || allow_if_found)
+ sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : "");
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json)
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ if (sbl) {
+ if (as_json)
+ jap = sgj_named_subarray_r(jsp, jo2p, "logical_block_"
+ "length_and_protection_types_descriptor_list");
+ decode_sup_block_lens_vpd(rp, len, op, jap);
+ } else
+ decode_b4_vpd(rp, len, op, jo2p);
+ }
+ return 0;
+ } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
+ exam_not_given)
+ sgj_pr_hr(jsp, "%sVPD page=0xb4\n", pre);
+ break;
+ case 0xb5: /* VPD page depends on pdt */
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ bool bdce = false;
+ bool lbp = false;
+
+ pdt = rp[0] & PDT_MASK;
+ switch (pdt) {
+ case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+ np = "Block device characteristics extension VPD page";
+ ep = "(SBC)";
+ bdce = true;
+ break;
+ case PDT_TAPE: case PDT_MCHANGER:
+ np = "Logical block protection VPD page";
+ ep = "(SSC)";
+ lbp = true;
+ break;
+ default:
+ np = NULL;
+ break;
+ }
+ if (NULL == np)
+ sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
+ else if (allow_name || allow_if_found)
+ sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : "");
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json)
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ if (bdce)
+ decode_block_dev_char_ext_vpd(rp, len, op, jo2p);
+ else if (lbp) {
+ if (as_json)
+ jap = sgj_named_subarray_r(jsp, jo2p,
+ "logical_block_protection_method_descriptor_list");
+ decode_lb_protection_vpd(rp, len, op, jap);
+ } else
+ decode_b5_vpd(rp, len, op->do_hex, pdt);
+ }
+ return 0;
+ } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
+ exam_not_given)
+ sgj_pr_hr(jsp, "%sVPD page=0xb5\n", pre);
+ break;
+ case VPD_ZBC_DEV_CHARS: /* 0xb6 for both pdt=0 and pdt=0x14 */
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ bool zbdch = false;
+
+ pdt = rp[0] & PDT_MASK;
+ switch (pdt) {
+ case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+ np = "Zoned block device characteristics VPD page";
+ ep = "(SBC, ZBC)";
+ zbdch = true;
+ break;
+ default:
+ np = NULL;
+ break;
+ }
+ if (NULL == np)
+ sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
+ else if (allow_name || allow_if_found)
+ sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : "");
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json)
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ if (zbdch)
+ decode_zbdch_vpd(rp, len, op, jo2p);
+ else
+ return SG_LIB_CAT_OTHER;
+ }
+ return 0;
+ } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
+ exam_not_given)
+ sgj_pr_hr(jsp, "%sVPD page=0xb6\n", pre);
+ break;
+ case 0xb7:
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ bool ble = false;
+
+ pdt = rp[0] & PDT_MASK;
+ switch (pdt) {
+ case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+ np = "Block limits extension VPD page";
+ ep = "(SBC)";
+ ble = true;
+ break;
+ default:
+ np = NULL;
+ break;
+ }
+ if (NULL == np)
+ sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
+ else if (allow_name || allow_if_found)
+ sgj_pr_hr(jsp, "%s%s %s:\n", pre, np, ep ? ep : "");
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json)
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ if (ble)
+ decode_block_limits_ext_vpd(rp, len, op, jo2p);
+ else
+ return SG_LIB_CAT_OTHER;
+ }
+ return 0;
+ } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
+ exam_not_given)
+ sgj_pr_hr(jsp, "%sVPD page=0xb7\n", pre);
+ break;
+ case 0xb8: /* VPD_FORMAT_PRESETS */
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ bool fp = false;
+
+ pdt = rp[0] & PDT_MASK;
+ switch (pdt) {
+ case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+ np = "Format presets VPD page";
+ ep = "(SBC)";
+ fp = true;
+ break;
+ default:
+ np = NULL;
+ break;
+ }
+ if (NULL == np)
+ sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
+ else if (allow_name || allow_if_found)
+ sgj_pr_hr(jsp, "%s%s %s:\n", pre, np, ep ? ep : "");
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json) {
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ jap = sgj_named_subarray_r(jsp, jo2p, "format_preset_"
+ "descriptor_list");
+ }
+ if (fp)
+ decode_format_presets_vpd(rp, len, op, jap);
+ else
+ return SG_LIB_CAT_OTHER;
+ }
+ return 0;
+ } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
+ exam_not_given)
+ sgj_pr_hr(jsp, "%sVPD page=0xb8\n", pre);
+ break;
+ case 0xb9: /* VPD_CON_POS_RANGE */
+ res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
+ if (0 == res) {
+ bool cpr = false; /* ["cpr"] */
+
+ pdt = rp[0] & PDT_MASK;
+ switch (pdt) {
+ case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
+ np = "Concurrent positioning ranges VPD page";
+ ep = "(SBC)";
+ cpr = true;
+ break;
+ default:
+ np = NULL;
+ break;
+ }
+ if (NULL == np)
+ sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
+ else if (allow_name || allow_if_found)
+ sgj_pr_hr(jsp, "%s%s %s:\n", pre, np, ep ? ep : "");
+ if (op->do_raw)
+ dStrRaw(rp, len);
+ else {
+ if (vb || long_notquiet)
+ sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
+ "%s]\n", pqual, pdt_str);
+ if (as_json) {
+ jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
+ jap = sgj_named_subarray_r(jsp, jo2p, "lba_range_"
+ "descriptor_list");
+ }
+ if (cpr)
+ decode_con_pos_range_vpd(rp, len, op, jap);
+ else
+ return SG_LIB_CAT_OTHER;
+ }
+ return 0;
+ } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
+ exam_not_given)
+ sgj_pr_hr(jsp, "%sVPD page=0xb8\n", pre);
+ break;
+ default:
+ return SG_LIB_CAT_OTHER;
+ }
+ return res;
+}
+
+static int
+svpd_decode_all(int sg_fd, struct opts_t * op, sgj_opaque_p jop)
+{
+ int k, res, rlen, n, pn;
+ int max_pn = 255;
+ int any_err = 0;
+ sgj_state * jsp = &op->json_st;
+ uint8_t vpd0_buff[512];
+ uint8_t * rp = vpd0_buff;
+
+ if (op->vpd_pn > 0)
+ max_pn = op->vpd_pn;
+ if (sg_fd >= 0) { /* have valid open file descriptor (handle) */
+ res = vpd_fetch_page(sg_fd, rp, VPD_SUPPORTED_VPDS, op->maxlen,
+ op->do_quiet, op->verbose, &rlen);
+ if (res) {
+ if (! op->do_quiet) {
+ if (SG_LIB_CAT_ABORTED_COMMAND == res)
+ pr2serr("%s: VPD page 0, aborted command\n", __func__);
+ else if (res) {
+ char b[80];
+
+ sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
+ pr2serr("%s: fetching VPD page 0 failed: %s\n", __func__,
+ b);
+ }
+ }
+ return res;
+ }
+ n = sg_get_unaligned_be16(rp + 2);
+ if (n > (rlen - 4)) {
+ if (op->verbose)
+ pr2serr("%s: rlen=%d > page0 size=%d\n", __func__, rlen,
+ n + 4);
+ n = (rlen - 4);
+ }
+ for (k = 0; k < n; ++k) {
+ pn = rp[4 + k];
+ if (pn > max_pn)
+ continue;
+ op->vpd_pn = pn;
+ if (k > 0)
+ sgj_pr_hr(jsp, "\n");
+ if (op->do_long) {
+ if (jsp->pr_as_json)
+ sgj_pr_hr(jsp, "[0x%x]:\n", pn);
+ else
+ printf("[0x%x] ", pn);
+ }
+
+ res = svpd_decode_t10(sg_fd, op, jop, 0, 0, NULL);
+ if (SG_LIB_CAT_OTHER == res) {
+ res = svpd_decode_vendor(sg_fd, op, jop, 0);
+ if (SG_LIB_CAT_OTHER == res)
+ res = svpd_unable_to_decode(sg_fd, op, 0, 0);
+ }
+ if (! op->do_quiet) {
+ if (SG_LIB_CAT_ABORTED_COMMAND == res)
+ pr2serr("fetching VPD page failed, aborted command\n");
+ else if (res) {
+ char b[80];
+
+ sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
+ pr2serr("fetching VPD page failed: %s\n", b);
+ }
+ }
+ if (res)
+ any_err = res;
+ }
+ res = any_err;
+ } else { /* input is coming from --inhex=FN */
+ int bump, off;
+ int in_len = op->maxlen;
+ int prev_pn = -1;
+
+ res = 0;
+ if (op->page_given && (VPD_NOPE_WANT_STD_INQ == op->vpd_pn))
+ return svpd_decode_t10(-1, op, jop, 0, 0, NULL);
+
+ for (k = 0, off = 0; off < in_len; ++k, off += bump) {
+ rp = rsp_buff + off;
+ pn = rp[1];
+ bump = sg_get_unaligned_be16(rp + 2) + 4;
+ if ((off + bump) > in_len) {
+ pr2serr("%s: page 0x%x size (%d) exceeds buffer\n", __func__,
+ pn, bump);
+ bump = in_len - off;
+ }
+ if (op->page_given && (pn != op->vpd_pn))
+ continue;
+ if (pn <= prev_pn) {
+ pr2serr("%s: prev_pn=0x%x, this pn=0x%x, not ascending so "
+ "exit\n", __func__, prev_pn, pn);
+ break;
+ }
+ prev_pn = pn;
+ op->vpd_pn = pn;
+ if (pn > max_pn) {
+ if (op->verbose > 2)
+ pr2serr("%s: skipping as this pn=0x%x exceeds "
+ "max_pn=0x%x\n", __func__, pn, max_pn);
+ continue;
+ }
+ if (op->do_long) {
+ if (jsp->pr_as_json)
+ sgj_pr_hr(jsp, "[0x%x]:\n", pn);
+ else
+ printf("[0x%x] ", pn);
+ }
+
+ res = svpd_decode_t10(-1, op, jop, 0, off, NULL);
+ if (SG_LIB_CAT_OTHER == res) {
+ res = svpd_decode_vendor(-1, op, jop, off);
+ if (SG_LIB_CAT_OTHER == res)
+ res = svpd_unable_to_decode(-1, op, 0, off);
+ }
+ }
+ }
+ return res;
+}
+
+static int
+svpd_examine_all(int sg_fd, struct opts_t * op, sgj_opaque_p jop)
+{
+ bool first = true;
+ bool got_one = false;
+ int k, res, start;
+ int max_pn;
+ int any_err = 0;
+ sgj_state * jsp = &op->json_st;
+ char b[80];
+
+ max_pn = (op->page_given ? op->vpd_pn : 0xff);
+ switch (op->examine) {
+ case 1:
+ start = 0x80;
+ break;
+ case 2:
+ start = 0x0;
+ break;
+ default:
+ start = 0xc0;
+ break;
+ }
+ if (start > max_pn) { /* swap them around */
+ k = start;
+ start = max_pn;
+ max_pn = k;
+ }
+ for (k = start; k <= max_pn; ++k) {
+ op->vpd_pn = k;
+ if (first)
+ first = false;
+ else if (got_one) {
+ sgj_pr_hr(jsp, "\n");
+ got_one = false;
+ }
+ if (op->do_long)
+ snprintf(b, sizeof(b), "[0x%x] ", k);
+ else
+ b[0] = '\0';
+ res = svpd_decode_t10(sg_fd, op, jop, 0, 0, b);
+ if (SG_LIB_CAT_OTHER == res) {
+ res = svpd_decode_vendor(sg_fd, op, jop, 0);
+ if (SG_LIB_CAT_OTHER == res)
+ res = svpd_unable_to_decode(sg_fd, op, 0, 0);
+ }
+ if (! op->do_quiet) {
+ if (SG_LIB_CAT_ABORTED_COMMAND == res)
+ pr2serr("fetching VPD page failed, aborted command\n");
+ else if (res && (SG_LIB_CAT_ILLEGAL_REQ != res)) {
+ /* SG_LIB_CAT_ILLEGAL_REQ expected as well examine all */
+ sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
+ pr2serr("fetching VPD page failed: %s\n", b);
+ }
+ }
+ if (res && (SG_LIB_CAT_ILLEGAL_REQ != res))
+ any_err = res;
+ if (0 == res)
+ got_one = true;
+ }
+ return any_err;
+}
+
+
+int
+main(int argc, char * argv[])
+{
+ bool as_json;
+ int c, res, matches;
+ int sg_fd = -1;
+ int inhex_len = 0;
+ int inraw_len = 0;
+ int ret = 0;
+ int subvalue = 0;
+ const char * cp;
+ sgj_state * jsp;
+ sgj_opaque_p jop = NULL;
+ const struct svpd_values_name_t * vnp;
+ struct opts_t opts SG_C_CPP_ZERO_INIT;
+ struct opts_t * op = &opts;
+
+ op->invoker = SG_VPD_INV_SG_VPD;
+ dup_sanity_chk((int)sizeof(opts), (int)sizeof(*vnp));
+ op->vend_prod_num = -1;
+ while (1) {
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "aeEfhHiI:j::lm:M:p:qQ:rvV", long_options,
+ &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'a':
+ op->do_all = true;
+ break;
+ case 'e':
+ op->do_enum = true;
+ break;
+ case 'E':
+ ++op->examine;
+ op->examine_given = true;
+ break;
+ case 'f':
+ op->do_force = true;
+ break;
+ case 'h':
+ case '?':
+ usage();
+ return 0;
+ case 'H':
+ ++op->do_hex;
+ break;
+ case 'i':
+ ++op->do_ident;
+ break;
+ case 'I':
+ if (op->inhex_fn) {
+ pr2serr("only one '--inhex=' option permitted\n");
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ } else
+ 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 'l':
+ op->do_long = true;
+ break;
+ case 'm':
+ op->maxlen = sg_get_num(optarg);
+ if ((op->maxlen < 0) || (op->maxlen > MX_ALLOC_LEN)) {
+ pr2serr("argument to '--maxlen' should be %d or less\n",
+ MX_ALLOC_LEN);
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ if ((op->maxlen > 0) && (op->maxlen < MIN_MAXLEN)) {
+ pr2serr("Warning: overriding '--maxlen' < %d, using "
+ "default\n", MIN_MAXLEN);
+ op->maxlen = 0;
+ }
+ break;
+ case 'M':
+ if (op->vend_prod) {
+ pr2serr("only one '--vendor=' option permitted\n");
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ } else
+ op->vend_prod = optarg;
+ break;
+ case 'p':
+ if (op->page_str) {
+ pr2serr("only one '--page=' option permitted\n");
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ } else
+ op->page_str = optarg;
+ op->page_given = true;
+ break;
+ case 'q':
+ op->do_quiet = true;
+ break;
+ case 'Q':
+ op->sinq_inraw_fn = optarg;
+ break;
+ case 'r':
+ ++op->do_raw;
+ break;
+ case 'v':
+ op->verbose_given = true;
+ ++op->verbose;
+ break;
+ case 'V':
+ op->version_given = true;
+ break;
+ default:
+ pr2serr("unrecognised option code 0x%x ??\n", c);
+ 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;
+ }
+ }
+
+#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: %s\n", version_str);
+ return 0;
+ }
+
+ jsp = &op->json_st;
+ if (op->do_enum) {
+ if (op->device_name)
+ pr2serr("Device name %s ignored when --enumerate given\n",
+ op->device_name);
+ if (op->vend_prod) {
+ if (isdigit((uint8_t)op->vend_prod[0])) {
+ op->vend_prod_num = sg_get_num_nomult(op->vend_prod);
+ if ((op->vend_prod_num < 0) || (op->vend_prod_num > 10)) {
+ pr2serr("Bad vendor/product number after '--vendor=' "
+ "option\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ } else {
+ op->vend_prod_num = svpd_find_vp_num_by_acron(op->vend_prod);
+ if (op->vend_prod_num < 0) {
+ pr2serr("Bad vendor/product acronym after '--vendor=' "
+ "option\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+ svpd_enumerate_vendor(op->vend_prod_num);
+ return 0;
+ }
+ if (op->page_str) {
+ if ((0 == strcmp("-1", op->page_str)) ||
+ (0 == strcmp("-2", op->page_str)))
+ op->vpd_pn = VPD_NOPE_WANT_STD_INQ;
+ else if (isdigit((uint8_t)op->page_str[0])) {
+ op->vpd_pn = sg_get_num_nomult(op->page_str);
+ if ((op->vpd_pn < 0) || (op->vpd_pn > 255)) {
+ pr2serr("Bad page code value after '-p' option\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ } else {
+ pr2serr("with --enumerate only search using VPD page "
+ "numbers\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ matches = count_standard_vpds(op->vpd_pn);
+ if (0 == matches)
+ matches = svpd_count_vendor_vpds(op->vpd_pn,
+ op->vend_prod_num);
+ if (0 == matches)
+ sgj_pr_hr(jsp, "No matches found for VPD page number 0x%x\n",
+ op->vpd_pn);
+ } else { /* enumerate standard then vendor VPD pages */
+ sgj_pr_hr(jsp, "Standard VPD pages:\n");
+ enumerate_vpds(1, 1);
+ }
+ return 0;
+ }
+
+ as_json = jsp->pr_as_json;
+ if (as_json)
+ jop = sgj_start_r(MY_NAME, version_str, argc, argv, jsp);
+
+ if (op->page_str) {
+ if ('-' == op->page_str[0])
+ op->vpd_pn = VPD_NOPE_WANT_STD_INQ;
+ else if (isalpha((uint8_t)op->page_str[0])) {
+ vnp = sdp_find_vpd_by_acron(op->page_str);
+ if (NULL == vnp) {
+ vnp = svpd_find_vendor_by_acron(op->page_str);
+ if (NULL == vnp) {
+ if (0 == strcmp("stdinq", op->page_str)) {
+ vnp = sdp_find_vpd_by_acron("sinq");
+ } else {
+ pr2serr("abbreviation doesn't match a VPD page\n");
+ sgj_pr_hr(jsp, "Available standard VPD pages:\n");
+ enumerate_vpds(1, 1);
+ ret = SG_LIB_SYNTAX_ERROR;
+ goto fini;
+ }
+ }
+ }
+ op->vpd_pn = vnp->value;
+ subvalue = vnp->subvalue;
+ op->vend_prod_num = subvalue;
+ } else {
+ cp = strchr(op->page_str, ',');
+ if (cp && op->vend_prod) {
+ pr2serr("the --page=pg,vp and the --vendor=vp forms overlap, "
+ "choose one or the other\n");
+ ret = SG_LIB_SYNTAX_ERROR;
+ goto fini;
+ }
+ op->vpd_pn = sg_get_num_nomult(op->page_str);
+ if ((op->vpd_pn < 0) || (op->vpd_pn > 255)) {
+ pr2serr("Bad page code value after '-p' option\n");
+ sgj_pr_hr(jsp, "Available standard VPD pages:\n");
+ enumerate_vpds(1, 1);
+ ret = SG_LIB_SYNTAX_ERROR;
+ goto fini;
+ }
+ if (cp) {
+ if (isdigit((uint8_t)*(cp + 1)))
+ op->vend_prod_num = sg_get_num_nomult(cp + 1);
+ else
+ op->vend_prod_num = svpd_find_vp_num_by_acron(cp + 1);
+ if ((op->vend_prod_num < 0) || (op->vend_prod_num > 255)) {
+ pr2serr("Bad vendor/product acronym after comma in '-p' "
+ "option\n");
+ if (op->vend_prod_num < 0)
+ svpd_enumerate_vendor(-1);
+ ret = SG_LIB_SYNTAX_ERROR;
+ goto fini;
+ }
+ subvalue = op->vend_prod_num;
+ } else if (op->vend_prod) {
+ if (isdigit((uint8_t)op->vend_prod[0]))
+ op->vend_prod_num = sg_get_num_nomult(op->vend_prod);
+ else
+ op->vend_prod_num =
+ svpd_find_vp_num_by_acron(op->vend_prod);
+ if ((op->vend_prod_num < 0) || (op->vend_prod_num > 255)) {
+ pr2serr("Bad vendor/product acronym after '--vendor=' "
+ "option\n");
+ svpd_enumerate_vendor(-1);
+ ret = SG_LIB_SYNTAX_ERROR;
+ goto fini;
+ }
+ subvalue = op->vend_prod_num;
+ }
+ }
+ if (op->verbose > 3)
+ pr2serr("'--page=' matched pn=%d [0x%x], subvalue=%d\n",
+ op->vpd_pn, op->vpd_pn, subvalue);
+ } else if (op->vend_prod) {
+ if (isdigit((uint8_t)op->vend_prod[0]))
+ op->vend_prod_num = sg_get_num_nomult(op->vend_prod);
+ else
+ op->vend_prod_num = svpd_find_vp_num_by_acron(op->vend_prod);
+ if ((op->vend_prod_num < 0) || (op->vend_prod_num > 255)) {
+ pr2serr("Bad vendor/product acronym after '--vendor=' "
+ "option\n");
+ svpd_enumerate_vendor(-1);
+ ret = SG_LIB_SYNTAX_ERROR;
+ goto fini;
+ }
+ subvalue = op->vend_prod_num;
+ }
+
+ rsp_buff = sg_memalign(rsp_buff_sz, 0 /* page align */, &free_rsp_buff,
+ false);
+ if (NULL == rsp_buff) {
+ pr2serr("Unable to allocate %d bytes on heap\n", rsp_buff_sz);
+ ret = sg_convert_errno(ENOMEM);
+ goto fini;
+ }
+ if (op->sinq_inraw_fn) {
+ if ((ret = sg_f2hex_arr(op->sinq_inraw_fn, true, false, rsp_buff,
+ &inraw_len, rsp_buff_sz))) {
+ goto err_out;
+ }
+ if (inraw_len < 36) {
+ pr2serr("Unable to read 36 or more bytes from %s\n",
+ op->sinq_inraw_fn);
+ ret = SG_LIB_FILE_ERROR;
+ goto err_out;
+ }
+ memcpy(op->std_inq_a, rsp_buff, 36);
+ op->std_inq_a_valid = true;
+ }
+ if (op->inhex_fn) {
+ if (op->device_name) {
+ pr2serr("Cannot have both a DEVICE and --inhex= option\n");
+ ret = SG_LIB_SYNTAX_ERROR;
+ goto err_out;
+ }
+ if ((ret = sg_f2hex_arr(op->inhex_fn, !!op->do_raw, false, rsp_buff,
+ &inhex_len, rsp_buff_sz))) {
+ goto err_out;
+ }
+ if (op->verbose > 2)
+ pr2serr("Read %d [0x%x] bytes of user supplied data\n", inhex_len,
+ inhex_len);
+ if (op->verbose > 3)
+ hex2stderr(rsp_buff, inhex_len, 0);
+ op->do_raw = 0; /* don't want raw on output with --inhex= */
+ if ((NULL == op->page_str) && (! op->do_all)) {
+ /* may be able to deduce VPD page */
+ if ((0x2 == (0xf & rsp_buff[3])) && (rsp_buff[2] > 2)) {
+ if (op->verbose)
+ pr2serr("Guessing from --inhex= this is a standard "
+ "INQUIRY\n");
+ } else if (rsp_buff[2] <= 2) {
+ if (op->verbose)
+ pr2serr("Guessing from --inhex this is VPD page 0x%x\n",
+ rsp_buff[1]);
+ op->vpd_pn = rsp_buff[1];
+ } else {
+ if (op->vpd_pn > 0x80) {
+ op->vpd_pn = rsp_buff[1];
+ if (op->verbose)
+ pr2serr("Guessing from --inhex this is VPD page "
+ "0x%x\n", rsp_buff[1]);
+ } else {
+ op->vpd_pn = VPD_NOPE_WANT_STD_INQ;
+ if (op->verbose)
+ pr2serr("page number unclear from --inhex, hope "
+ "it's a standard INQUIRY response\n");
+ }
+ }
+ }
+ } else if ((NULL == op->device_name) && (! op->std_inq_a_valid)) {
+ pr2serr("No DEVICE argument given\n\n");
+ usage();
+ ret = SG_LIB_SYNTAX_ERROR;
+ goto err_out;
+ }
+
+ if (op->do_raw && op->do_hex) {
+ pr2serr("Can't do hex and raw at the same time\n");
+ usage();
+ ret = SG_LIB_SYNTAX_ERROR;
+ goto err_out;
+ }
+ if (op->do_ident) {
+ op->vpd_pn = VPD_DEVICE_ID;
+ if (op->do_ident > 1) {
+ if (! op->do_long)
+ op->do_quiet = true;
+ subvalue = VPD_DI_SEL_LU;
+ }
+ }
+ if (op->do_raw) {
+ if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+ perror("sg_set_binary_mode");
+ ret = SG_LIB_FILE_ERROR;
+ goto err_out;
+ }
+ }
+
+ if (op->inhex_fn) {
+ if ((0 == op->maxlen) || (inhex_len < op->maxlen))
+ op->maxlen = inhex_len;
+ if (op->do_all || op->page_given)
+ res = svpd_decode_all(-1, op, jop);
+ else {
+ res = svpd_decode_t10(-1, op, jop, subvalue, 0, NULL);
+ if (SG_LIB_CAT_OTHER == res) {
+ res = svpd_decode_vendor(-1, op, jop, 0);
+ if (SG_LIB_CAT_OTHER == res)
+ res = svpd_unable_to_decode(-1, op, subvalue, 0);
+ }
+ }
+ ret = res;
+ goto err_out;
+ } else if (op->std_inq_a_valid && (NULL == op->device_name)) {
+ /* nothing else to do ... */
+ /* --sinq_inraw=RFN contents still in rsp_buff */
+ if (op->do_raw)
+ dStrRaw(rsp_buff, inraw_len);
+ else if (op->do_hex) {
+ if (! op->do_quiet && (op->do_hex < 3))
+ sgj_pr_hr(jsp, "Standard Inquiry data format:\n");
+ hex2stdout(rsp_buff, inraw_len, (1 == op->do_hex) ? 0 : -1);
+ } else
+ std_inq_decode(rsp_buff, inraw_len, op, jop);
+ ret = 0;
+ goto fini;
+ }
+
+ if ((sg_fd = sg_cmds_open_device(op->device_name, true /* ro */,
+ op->verbose)) < 0) {
+ if (op->verbose > 0)
+ pr2serr("error opening file: %s: %s\n", op->device_name,
+ safe_strerror(-sg_fd));
+ ret = sg_convert_errno(-sg_fd);
+ if (ret < 0)
+ ret = SG_LIB_FILE_ERROR;
+ goto err_out;
+ }
+
+ if (op->examine_given) {
+ ret = svpd_examine_all(sg_fd, op, jop);
+ } else if (op->do_all)
+ ret = svpd_decode_all(sg_fd, op, jop);
+ else {
+ memset(rsp_buff, 0, rsp_buff_sz);
+
+ res = svpd_decode_t10(sg_fd, op, jop, subvalue, 0, NULL);
+ if (SG_LIB_CAT_OTHER == res) {
+ res = svpd_decode_vendor(sg_fd, op, jop, 0);
+ if (SG_LIB_CAT_OTHER == res)
+ res = svpd_unable_to_decode(sg_fd, op, subvalue, 0);
+ }
+ if (! op->do_quiet) {
+ if (SG_LIB_CAT_ABORTED_COMMAND == res)
+ pr2serr("fetching VPD page failed, aborted command\n");
+ else if (res) {
+ char b[80];
+
+ sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
+ pr2serr("fetching VPD page failed: %s\n", b);
+ }
+ }
+ ret = res;
+ }
+err_out:
+ if (free_rsp_buff)
+ free(free_rsp_buff);
+ if ((0 == op->verbose) && (! op->do_quiet)) {
+ if (! sg_if_can2stderr("sg_vpd failed: ", ret))
+ pr2serr("Some error occurred, try again with '-v' or '-vv' for "
+ "more information\n");
+ }
+fini:
+ res = (sg_fd >= 0) ? sg_cmds_close_device(sg_fd) : 0;
+
+ if (res < 0) {
+ pr2serr("close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ ret = sg_convert_errno(-res);
+ }
+ ret = (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+ if (as_json) {
+ if (0 == op->do_hex)
+ sgj_js2file(jsp, NULL, ret, stdout);
+ sgj_finish(jsp);
+ }
+ return ret;
+}