diff options
author | Douglas Gilbert <dgilbert@interlog.com> | 2022-07-15 04:28:12 +0000 |
---|---|---|
committer | Douglas Gilbert <dgilbert@interlog.com> | 2022-07-15 04:28:12 +0000 |
commit | f1c4468bc5353fa361029e790b3ab370d22d5101 (patch) | |
tree | a54fd4eff8a43d3aa764fd3de14897e5f2517e19 /src | |
parent | a51fa77e373d1e4a1be10e8e4ef9a87547d26e86 (diff) | |
download | sg3_utils-f1c4468bc5353fa361029e790b3ab370d22d5101.tar.gz |
sg_inq+sg_vpd: JSON work continues
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@960 6180dd3e-e324-4e3e-922d-17de1ae2f315
Diffstat (limited to 'src')
-rw-r--r-- | src/sg_inq.c | 698 | ||||
-rw-r--r-- | src/sg_vpd.c | 526 | ||||
-rw-r--r-- | src/sg_vpd_common.c | 414 | ||||
-rw-r--r-- | src/sg_vpd_common.h | 63 |
4 files changed, 867 insertions, 834 deletions
diff --git a/src/sg_inq.c b/src/sg_inq.c index 8e5b48d7..98f505d6 100644 --- a/src/sg_inq.c +++ b/src/sg_inq.c @@ -53,7 +53,7 @@ #include "sg_vpd_common.h" /* for shared VPD page processing with sg_vpd */ -static const char * version_str = "2.18 20220511"; /* spc6r06 */ +static const char * version_str = "2.21 20220714"; /* spc6r06 */ #define MY_NAME "sg_inq" @@ -126,7 +126,10 @@ static const char * find_version_descriptor_str(int value); static void decode_dev_ids(const char * leadin, uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jop); static int vpd_decode(int sg_fd, struct opts_t * op, sgj_opaque_p jop, - int inhex_len); + int off); + +// Testing <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +#undef SG_SCSI_STRINGS #if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS) && \ defined(HDIO_GET_IDENTITY) @@ -136,17 +139,6 @@ struct opts_t; static void prepare_ata_identify(const struct opts_t * op, int inhex_len); #endif -#if 0 -struct svpd_values_name_t { - int value; - int subvalue; - int pdt; /* peripheral device type id, -1 is the default */ - /* (all or not applicable) value */ - int vendor; /* vendor flag */ - const char * acron; - const char * name; -}; -#endif /* Note that this table is sorted by acronym */ static struct svpd_values_name_t t10_vpd_pg[] = { @@ -159,6 +151,7 @@ static struct svpd_values_name_t t10_vpd_pg[] = { {VPD_BLOCK_LIMITS_EXT, 0, 0, "ble", "Block limits extension (SBC)"}, {VPD_CON_POS_RANGE, 0, 0, "cpr", "Concurrent positioning ranges " "(SBC)"}, + {VPD_DEVICE_CONSTITUENTS, 0, -1, "dc", "Device constituents"}, {VPD_DEVICE_ID, 0, -1, "di", "Device identification"}, #if 0 /* following found in sg_vpd */ {VPD_DEVICE_ID, VPD_DI_SEL_AS_IS, -1, "di_asis", "Like 'di' " @@ -178,25 +171,27 @@ static struct svpd_values_name_t t10_vpd_pg[] = { {VPD_MODE_PG_POLICY, 0, -1, "mpp", "Mode page policy"}, {VPD_POWER_CONDITION, 0, -1, "po", "Power condition"}, {VPD_POWER_CONSUMPTION, 0, -1, "psm", "Power consumption"}, - {VPD_PROTO_LU, 0, 0x0, "pslu", "Protocol-specific logical unit " + {VPD_PROTO_LU, 0, -1, "pslu", "Protocol-specific logical unit " "information"}, - {VPD_PROTO_PORT, 0, 0x0, "pspo", "Protocol-specific port information"}, + {VPD_PROTO_PORT, 0, -1, "pspo", "Protocol-specific port information"}, {VPD_REFERRALS, 0, 0, "ref", "Referrals (SBC)"}, {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 response"}, + {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_SUPPORTED_VPDS, 0, -1, "sv", "Supported VPD pages"}, {VPD_3PARTY_COPY, 0, -1, "tpc", "Third party copy"}, - {VPD_ZBC_DEV_CHARS, 0, -1, "zbdch", "Zoned block device " + {VPD_ZBC_DEV_CHARS, 0, 0, "zbdch", "Zoned block device " "characteristics"}, {0, 0, 0, NULL, NULL}, }; +/* Some alternate acronyms for T10 VPD pages (compatibility with sg_vpd) */ static struct svpd_values_name_t alt_t10_vpd_pg[] = { + {VPD_NOPE_WANT_STD_INQ, 0, -1, "stdinq", "Standard inquiry data format"}, {VPD_POWER_CONDITION, 0, -1, "pc", "Power condition"}, {0, 0, 0, NULL, NULL}, }; @@ -965,84 +960,60 @@ encode_string(char *out, const uint8_t *in, int inlen) return j; } -struct vpd_name { - int number; - int peri_type; - const char * name; -}; - -/* In numerical order */ -static struct vpd_name vpd_name_arr[] = { - {VPD_SUPPORTED_VPDS, 0, "Supported VPD pages"}, /* 0x0 */ - {VPD_UNIT_SERIAL_NUM, 0, "Unit serial number"}, /* 0x80 */ - {0x81, 0, "Implemented operating definitions (obsolete)"}, - {0x82, 0, "ASCII implemented operating definition (obsolete)"}, - {VPD_DEVICE_ID, 0, "Device identification"}, - {VPD_SOFTW_INF_ID, 0, "Software interface identification"}, - {VPD_MAN_NET_ADDR, 0, "Management network addresses"}, - {VPD_EXT_INQ, 0, "Extended INQUIRY data"}, - {VPD_MODE_PG_POLICY, 0, "Mode page policy"}, - {VPD_SCSI_PORTS, 0, "SCSI ports"}, - {VPD_ATA_INFO, 0, "ATA information"}, - {VPD_POWER_CONDITION, 0, "Power condition"}, - {VPD_DEVICE_CONSTITUENTS, 0, "Device constituents"}, - {VPD_CFA_PROFILE_INFO, 0, "CFA profile information"}, /* 0x8c */ - {VPD_POWER_CONSUMPTION, 0, "Power consumption"}, /* 0x8d */ - {VPD_3PARTY_COPY, 0, "Third party copy"}, /* 0x8f */ - {VPD_PROTO_LU, 0, "Protocol-specific logical unit information"}, /* 0x90 */ - {VPD_PROTO_PORT, 0, "Protocol-specific port information"}, /* 0x91 */ - {VPD_SCSI_FEATURE_SETS, 0, "SCSI Feature sets"}, /* 0x92 */ - /* 0xb0 to 0xbf are per peripheral device type */ - {VPD_BLOCK_LIMITS, 0, "Block limits (sbc2)"}, /* 0xb0 */ - {VPD_BLOCK_DEV_CHARS, 0, "Block device characteristics (sbc3)"}, - {VPD_LB_PROVISIONING, 0, "Logical block provisioning (sbc3)"}, - {VPD_REFERRALS, 0, "Referrals (sbc3)"}, - {0xb0, PDT_TAPE, "Sequential access device capabilities (ssc3)"}, - {0xb2, PDT_TAPE, "TapeAlert supported flags (ssc3)"}, - {0xb0, PDT_OSD, "OSD information (osd)"}, - {0xb1, PDT_OSD, "Security token (osd)"}, - /* 0xc0 to 0xff are vendor specific */ - {0xc0, 0, "vendor: Firmware numbers (seagate); Unit path report (EMC)"}, - {0xc1, 0, "vendor: Date code (seagate)"}, - {0xc2, 0, "vendor: Jumper settings (seagate); Software version (RDAC)"}, - {0xc3, 0, "vendor: Device behavior (seagate)"}, - {0xc9, 0, "Volume Access Control (RDAC)"}, -}; - -static const char * -get_vpd_page_str(int vpd_page_num, int scsi_ptype) +static const struct svpd_values_name_t * +get_vpd_page_info(int vpd_page_num, int dev_pdt) { - int k; - int vpd_name_arr_sz = (int)SG_ARRAY_SIZE(vpd_name_arr); + int decay_pdt; + const struct svpd_values_name_t * vnp; + const struct svpd_values_name_t * prev_vnp; - if ((vpd_page_num >= 0xb0) && (vpd_page_num < 0xc0)) { - /* peripheral device type relevant for 0xb0..0xbf range */ - for (k = 0; k < vpd_name_arr_sz; ++k) { - if ((vpd_name_arr[k].number == vpd_page_num) && - (vpd_name_arr[k].peri_type == scsi_ptype)) + if (vpd_page_num < 0xb0) { /* take T10 first match */ + for (vnp = t10_vpd_pg; vnp->acron; ++vnp) { + if (vnp->value == vpd_page_num) + return vnp; + } + return NULL; + } else if (vpd_page_num < 0xc0) { + for (vnp = t10_vpd_pg; vnp->acron; ++vnp) { + if (vnp->value == vpd_page_num) break; } - if (k < vpd_name_arr_sz) - return vpd_name_arr[k].name; - for (k = 0; k < vpd_name_arr_sz; ++k) { - if ((vpd_name_arr[k].number == vpd_page_num) && - (vpd_name_arr[k].peri_type == 0)) + if (NULL == vnp->acron) + return NULL; + if (vnp->pdt == dev_pdt) /* exact match */ + return vnp; + prev_vnp = vnp; + + for (++vnp; vnp->acron; ++vnp) { + if (vnp->value == vpd_page_num) break; } - if (k < vpd_name_arr_sz) - return vpd_name_arr[k].name; - else + decay_pdt = sg_lib_pdt_decay(dev_pdt); + if (NULL == vnp->acron) { + if (decay_pdt == prev_vnp->pdt) + return prev_vnp; return NULL; - } else { - /* rest of 0x0..0xff range doesn't depend on peripheral type */ - for (k = 0; k < vpd_name_arr_sz; ++k) { - if (vpd_name_arr[k].number == vpd_page_num) + } + if ((vnp->pdt == dev_pdt) || (vnp->pdt == decay_pdt)) + return vnp; + if (decay_pdt == prev_vnp->pdt) + return prev_vnp; + + for (++vnp; vnp->acron; ++vnp) { + if (vnp->value == vpd_page_num) break; } - if (k < vpd_name_arr_sz) - return vpd_name_arr[k].name; - else + if (NULL == vnp->acron) return NULL; + if ((vnp->pdt == dev_pdt) || (vnp->pdt == decay_pdt)) + return vnp; + return NULL; /* give up */ + } else { /* vendor specific: vpd >= 0xc0 */ + for (vnp = vs_vpd_pg; vnp->acron; ++vnp) { + if (vnp->pdt == dev_pdt) + return vnp; + } + return NULL; } } @@ -1062,7 +1033,7 @@ svpd_inhex_decode_all(struct opts_t * op, sgj_opaque_p jop) res = 0; if (op->page_given && (VPD_NOPE_WANT_STD_INQ == op->vpd_pn)) - return vpd_decode(-1, op, jop, in_len); + return vpd_decode(-1, op, jop, 0); for (k = 0, off = 0; off < in_len; ++k, off += bump) { rp = rsp_buff + off; @@ -1091,8 +1062,7 @@ svpd_inhex_decode_all(struct opts_t * op, sgj_opaque_p jop) if (op->do_long) printf("[0x%x] ", pn); - op->inhex_off = off; - res = vpd_decode(-1, op, jop, in_len); + res = vpd_decode(-1, op, jop, off); if (SG_LIB_CAT_OTHER == res) { ; // xxxxx } @@ -1102,13 +1072,17 @@ svpd_inhex_decode_all(struct opts_t * op, sgj_opaque_p jop) static void -decode_supported_vpd(uint8_t * buff, int len, int do_hex) +decode_supported_vpd(uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jap) { int vpd, k, rlen, pdt; - const char * cp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p; + const struct svpd_values_name_t * vnp; + char b[64]; - if (do_hex) { - hex2stdout(buff, len, (1 == do_hex) ? 0 : -1); + if (op->do_hex) { + hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1); return; } if (len < 4) { @@ -1122,14 +1096,23 @@ decode_supported_vpd(uint8_t * buff, int len, int do_hex) "%d\n", rlen, len); else len = rlen; - printf(" Supported VPD pages:\n"); + sgj_pr_hr(jsp, " Supported VPD pages:\n"); for (k = 0; k < len - 4; ++k) { vpd = buff[4 + k]; - cp = get_vpd_page_str(vpd, pdt); - if (cp) - printf(" 0x%x\t%s\n", vpd, cp); + snprintf(b, sizeof(b), "0x%x", vpd); + vnp = get_vpd_page_info(vpd, pdt); + if (jsp->pr_as_json && jap) { + jo2p = sgj_new_unattached_object(jsp); + sgj_add_nv_i(jsp, jo2p, "i", vpd); + sgj_add_nv_s(jsp, jo2p, "hex", b + 2); + sgj_add_nv_s(jsp, jo2p, "name", vnp ? vnp->name : "unknown"); + sgj_add_nv_s(jsp, jo2p, "acronym", vnp ? vnp->acron : "unknown"); + sgj_add_nv_o(jsp, jap, NULL /* name */, jo2p); + } + if (vnp) + sgj_pr_hr(jsp, " %s\t%s\n", b, vnp->name); else - printf(" 0x%x\n", vpd); + sgj_pr_hr(jsp, " %s\n", b); } } @@ -1212,7 +1195,7 @@ decode_id_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jap) decode_dev_ids("Device identification", buff + 4, len - 4, op, jap); } -/* VPD_SCSI_PORTS */ +/* VPD_SCSI_PORTS 0x88 ["sp"] */ static void decode_scsi_ports_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jap) @@ -1954,112 +1937,6 @@ export_dev_ids(uint8_t * buff, int len, int verbose) "around offset=%d\n", off); } -/* VPD_ATA_INFO [0x89] */ -static void -decode_ata_info_vpd(uint8_t * buff, int len, int do_hex) -{ - char b[80]; - int is_be, num; - - if (len < 36) { - pr2serr("ATA information VPD page length too short=%d\n", len); - return; - } - if (do_hex && (2 != do_hex)) { - hex2stdout(buff, len, (3 == do_hex) ? 0 : -1); - return; - } - memcpy(b, buff + 8, 8); - b[8] = '\0'; - printf(" SAT Vendor identification: %s\n", b); - memcpy(b, buff + 16, 16); - b[16] = '\0'; - printf(" SAT Product identification: %s\n", b); - memcpy(b, buff + 32, 4); - b[4] = '\0'; - printf(" SAT Product revision level: %s\n", b); - if (len < 56) - return; - printf(" Signature (Device to host FIS):\n"); - hex2stdout(buff + 36, 20, 1); - if (len < 60) - return; - is_be = sg_is_big_endian(); - if ((0xec == buff[56]) || (0xa1 == buff[56])) { - printf(" ATA command IDENTIFY %sDEVICE response summary:\n", - ((0xa1 == buff[56]) ? "PACKET " : "")); - num = sg_ata_get_chars((const unsigned short *)(buff + 60), 27, 20, - is_be, b); - b[num] = '\0'; - printf(" model: %s\n", b); - num = sg_ata_get_chars((const unsigned short *)(buff + 60), 10, 10, - is_be, b); - b[num] = '\0'; - printf(" serial number: %s\n", b); - num = sg_ata_get_chars((const unsigned short *)(buff + 60), 23, 4, - is_be, b); - b[num] = '\0'; - printf(" firmware revision: %s\n", b); - printf(" response in hex:\n"); - } else - printf(" ATA command 0x%x got following response:\n", - (unsigned int)buff[56]); - if (len < 572) - return; - if (2 == do_hex) - hex2stdout(buff + 60, 512, 0); - else - dWordHex((const unsigned short *)(buff + 60), 256, 0, - sg_is_big_endian()); -} - -/* VPD_SCSI_FEATURE_SETS [0x92] (sfs) */ -static void -decode_feature_sets_vpd(uint8_t * buff, int len, - const struct opts_t * op) -{ - int k, bump; - uint16_t sf_code; - bool found; - uint8_t * bp; - char b[64]; - - if ((1 == op->do_hex) || (op->do_hex > 2)) { - hex2stdout(buff, len, (1 == op->do_hex) ? 1 : -1); - return; - } - if (len < 4) { - pr2serr("SCSI Feature sets VPD page length too short=%d\n", len); - return; - } - len -= 8; - bp = buff + 8; - for (k = 0; k < len; k += bump, bp += bump) { - sf_code = sg_get_unaligned_be16(bp); - bump = 2; - if ((k + bump) > len) { - pr2serr("SCSI Feature sets, short descriptor length=%d, " - "left=%d\n", bump, (len - k)); - return; - } - if (2 == op->do_hex) - hex2stdout(bp + 8, 2, 1); - else if (op->do_hex > 2) - hex2stdout(bp, 2, 1); - else { - printf(" %s", sg_get_sfs_str(sf_code, -2, sizeof(b), b, - &found, op->verbose)); - if (op->verbose == 1) - printf(" [0x%x]\n", (unsigned int)sf_code); - else if (op->verbose > 1) - printf(" [0x%x] found=%s\n", (unsigned int)sf_code, - found ? "true" : "false"); - else - printf("\n"); - } - } -} - /* VPD_BLOCK_LIMITS sbc */ /* Sequential access device characteristics, ssc+smc */ /* OSD information, osd */ @@ -2655,99 +2532,108 @@ decode_rdac_vpd_c9(uint8_t * buff, int len, int do_hex) extern const char * sg_ansi_version_arr[]; static const char * -get_ansi_version_str(int version, char * buff, int buff_len) +get_ansi_version_str(int version, char * b, int blen) { version &= 0xf; - buff[buff_len - 1] = '\0'; - strncpy(buff, sg_ansi_version_arr[version], buff_len - 1); - return buff; + b[blen - 1] = '\0'; + strncpy(b, sg_ansi_version_arr[version], blen - 1); + return b; } static void -std_inq_decode(const struct opts_t * op, int act_len) +std_inq_decode(struct opts_t * op, sgj_opaque_p jop, int off) { - int len, pqual, peri_type, ansi_version, k, j; + int len, pqual, pdt, ansi_version, k, j; + sgj_state * jsp = &op->json_st; + bool as_json = jsp->pr_as_json; const char * cp; - int vdesc_arr[8]; - char buff[48]; const uint8_t * rp; + int vdesc_arr[8]; + char b[128]; + static const int blen = sizeof(b); - rp = rsp_buff + (op->inhex_fn ? op->inhex_off : 0); + rp = rsp_buff + off; memset(vdesc_arr, 0, sizeof(vdesc_arr)); if (op->do_raw) { - dStrRaw((const char *)rp, act_len); + dStrRaw((const char *)rp, op->maxlen); return; } else if (op->do_hex) { /* with -H, print with address, -HH without */ - hex2stdout(rp, act_len, ((1 == op->do_hex) ? 0 : -1)); + hex2stdout(rp, op->maxlen, ((1 == op->do_hex) ? 0 : -1)); return; } pqual = (rp[0] & 0xe0) >> 5; if (! op->do_raw && ! op->do_export) { - printf("standard INQUIRY:"); + snprintf(b, blen, "standard INQUIRY:"); if (0 == pqual) - printf("\n"); + sgj_pr_hr(jsp, "%s\n", b); else if (1 == pqual) - printf(" [PQ indicates LU temporarily unavailable]\n"); + sgj_pr_hr(jsp, "%s [PQ indicates LU temporarily unavailable]\n", + b); else if (3 == pqual) - printf(" [PQ indicates LU not accessible via this port]\n"); + sgj_pr_hr(jsp, "%s [PQ indicates LU not accessible via this " + "port]\n", b); else - printf(" [reserved or vendor specific qualifier [%d]]\n", pqual); + sgj_pr_hr(jsp, "%s [reserved or vendor specific qualifier " + "[%d]]\n", b, pqual); } len = rp[4] + 5; /* N.B. rp[2] full byte is 'version' in SPC-2,3,4 but in SPC * [spc-r11a (1997)] bits 6,7: ISO/IEC version; bits 3-5: ECMA * version; bits 0-2: SCSI version */ ansi_version = rp[2] & 0x7; /* Only take SCSI version */ - peri_type = rp[0] & PDT_MASK; + pdt = rp[0] & PDT_MASK; if (op->do_export) { printf("SCSI_TPGS=%d\n", (rp[5] & 0x30) >> 4); - cp = sg_get_pdt_str(peri_type, sizeof(buff), buff); + cp = sg_get_pdt_str(pdt, blen, b); if (strlen(cp) > 0) printf("SCSI_TYPE=%s\n", cp); } else { - printf(" PQual=%d PDT=%d RMB=%d LU_CONG=%d hot_pluggable=%d " - "version=0x%02x ", pqual, peri_type, !!(rp[1] & 0x80), - !!(rp[1] & 0x40), (rp[1] >> 4) & 0x3, (unsigned int)rp[2]); - printf(" [%s]\n", get_ansi_version_str(ansi_version, buff, - sizeof(buff))); - printf(" [AERC=%d] [TrmTsk=%d] NormACA=%d HiSUP=%d " - " Resp_data_format=%d\n SCCS=%d ", !!(rp[3] & 0x80), - !!(rp[3] & 0x40), !!(rp[3] & 0x20), !!(rp[3] & 0x10), - rp[3] & 0x0f, !!(rp[5] & 0x80)); - printf("ACC=%d TPGS=%d 3PC=%d Protect=%d ", !!(rp[5] & 0x40), - ((rp[5] & 0x30) >> 4), !!(rp[5] & 0x08), !!(rp[5] & 0x01)); - printf(" [BQue=%d]\n EncServ=%d ", !!(rp[6] & 0x80), - !!(rp[6] & 0x40)); + sgj_pr_hr(jsp, " PQual=%d PDT=%d RMB=%d LU_CONG=%d " + "hot_pluggable=%d version=0x%02x ", pqual, pdt, + !!(rp[1] & 0x80), !!(rp[1] & 0x40), (rp[1] >> 4) & 0x3, + (unsigned int)rp[2]); + sgj_pr_hr(jsp, " [%s]\n", get_ansi_version_str(ansi_version, b, + blen)); + sgj_pr_hr(jsp, " [AERC=%d] [TrmTsk=%d] NormACA=%d HiSUP=%d " + " Resp_data_format=%d\n SCCS=%d ", !!(rp[3] & 0x80), + !!(rp[3] & 0x40), !!(rp[3] & 0x20), !!(rp[3] & 0x10), + rp[3] & 0x0f, !!(rp[5] & 0x80)); + sgj_pr_hr(jsp, "ACC=%d TPGS=%d 3PC=%d Protect=%d ", + !!(rp[5] & 0x40), ((rp[5] & 0x30) >> 4), !!(rp[5] & 0x08), + !!(rp[5] & 0x01)); + sgj_pr_hr(jsp, " [BQue=%d]\n EncServ=%d ", !!(rp[6] & 0x80), + !!(rp[6] & 0x40)); if (rp[6] & 0x10) - printf("MultiP=1 (VS=%d) ", !!(rp[6] & 0x20)); + sgj_pr_hr(jsp, "MultiP=1 (VS=%d) ", !!(rp[6] & 0x20)); else - printf("MultiP=0 "); - printf("[MChngr=%d] [ACKREQQ=%d] Addr16=%d\n [RelAdr=%d] ", - !!(rp[6] & 0x08), !!(rp[6] & 0x04), !!(rp[6] & 0x01), - !!(rp[7] & 0x80)); - printf("WBus16=%d Sync=%d [Linked=%d] [TranDis=%d] ", - !!(rp[7] & 0x20), !!(rp[7] & 0x10), !!(rp[7] & 0x08), - !!(rp[7] & 0x04)); - printf("CmdQue=%d\n", !!(rp[7] & 0x02)); - if (act_len > 56) - printf(" [SPI: Clocking=0x%x QAS=%d IUS=%d]\n", - (rp[56] & 0x0c) >> 2, !!(rp[56] & 0x2), !!(rp[56] & 0x1)); - if (act_len >= len) - printf(" length=%d (0x%x)", len, len); + sgj_pr_hr(jsp, "MultiP=0 "); + sgj_pr_hr(jsp, "[MChngr=%d] [ACKREQQ=%d] Addr16=%d\n " + "[RelAdr=%d] ", !!(rp[6] & 0x08), !!(rp[6] & 0x04), + !!(rp[6] & 0x01), !!(rp[7] & 0x80)); + sgj_pr_hr(jsp, "WBus16=%d Sync=%d [Linked=%d] [TranDis=%d] ", + !!(rp[7] & 0x20), !!(rp[7] & 0x10), !!(rp[7] & 0x08), + !!(rp[7] & 0x04)); + sgj_pr_hr(jsp, "CmdQue=%d\n", !!(rp[7] & 0x02)); + if (op->maxlen > 56) + sgj_pr_hr(jsp, " [SPI: Clocking=0x%x QAS=%d IUS=%d]\n", + (rp[56] & 0x0c) >> 2, !!(rp[56] & 0x2), + !!(rp[56] & 0x1)); + if (op->maxlen >= len) + sgj_pr_hr(jsp, " length=%d (0x%x)", len, len); else - printf(" length=%d (0x%x), but only fetched %d bytes", len, - len, act_len); + sgj_pr_hr(jsp, " length=%d (0x%x), but only fetched %d bytes", + len, len, op->maxlen); if ((ansi_version >= 2) && (len < SAFE_STD_INQ_RESP_LEN)) - printf("\n [for SCSI>=2, len>=36 is expected]"); - cp = sg_get_pdt_str(peri_type, sizeof(buff), buff); + sgj_pr_hr(jsp, "\n [for SCSI>=2, len>=36 is expected]"); + cp = sg_get_pdt_str(pdt, blen, b); if (strlen(cp) > 0) - printf(" Peripheral device type: %s\n", cp); + sgj_pr_hr(jsp, " Peripheral device type: %s\n", cp); } - if (act_len <= 8) { + if (op->maxlen <= 8) { if (! op->do_export) - printf(" Inquiry response length=%d, no vendor, product or " - "revision data\n", act_len); + sgj_pr_hr(jsp, " Inquiry response length=%d, no vendor, product " + "or revision data\n", op->maxlen); } else { int i; @@ -2765,10 +2651,10 @@ std_inq_decode(const struct opts_t * op, int act_len) printf("SCSI_VENDOR_ENC=%s\n", xtra_buff); } } else - printf(" Vendor identification: %s\n", xtra_buff); - if (act_len <= 16) { + sgj_pr_hr(jsp, " Vendor identification: %s\n", xtra_buff); + if (op->maxlen <= 16) { if (! op->do_export) - printf(" Product identification: <none>\n"); + sgj_pr_hr(jsp, " Product identification: <none>\n"); } else { memcpy(xtra_buff, &rp[16], 16); xtra_buff[16] = '\0'; @@ -2780,11 +2666,11 @@ std_inq_decode(const struct opts_t * op, int act_len) printf("SCSI_MODEL_ENC=%s\n", xtra_buff); } } else - printf(" Product identification: %s\n", xtra_buff); + sgj_pr_hr(jsp, " Product identification: %s\n", xtra_buff); } - if (act_len <= 32) { + if (op->maxlen <= 32) { if (! op->do_export) - printf(" Product revision level: <none>\n"); + sgj_pr_hr(jsp, " Product revision level: <none>\n"); } else { memcpy(xtra_buff, &rp[32], 4); xtra_buff[4] = '\0'; @@ -2793,35 +2679,35 @@ std_inq_decode(const struct opts_t * op, int act_len) if (len > 0) printf("SCSI_REVISION=%s\n", xtra_buff); } else - printf(" Product revision level: %s\n", xtra_buff); + sgj_pr_hr(jsp, " Product revision level: %s\n", xtra_buff); } - if (op->do_vendor && (act_len > 36) && ('\0' != rp[36]) && + if (op->do_vendor && (op->maxlen > 36) && ('\0' != rp[36]) && (' ' != rp[36])) { - memcpy(xtra_buff, &rp[36], act_len < 56 ? act_len - 36 : + memcpy(xtra_buff, &rp[36], op->maxlen < 56 ? op->maxlen - 36 : 20); if (op->do_export) { len = encode_whitespaces((uint8_t *)xtra_buff, 20); if (len > 0) printf("VENDOR_SPECIFIC=%s\n", xtra_buff); } else - printf(" Vendor specific: %s\n", xtra_buff); + sgj_pr_hr(jsp, " Vendor specific: %s\n", xtra_buff); } if (op->do_descriptors) { - for (j = 0, k = 58; ((j < 8) && ((k + 1) < act_len)); + for (j = 0, k = 58; ((j < 8) && ((k + 1) < op->maxlen)); k +=2, ++j) vdesc_arr[j] = sg_get_unaligned_be16(rp + k); } - if ((op->do_vendor > 1) && (act_len > 96)) { - memcpy(xtra_buff, &rp[96], act_len - 96); + if ((op->do_vendor > 1) && (op->maxlen > 96)) { + memcpy(xtra_buff, &rp[96], op->maxlen - 96); if (op->do_export) { len = encode_whitespaces((uint8_t *)xtra_buff, - act_len - 96); + op->maxlen - 96); if (len > 0) printf("VENDOR_SPECIFIC=%s\n", xtra_buff); } else - printf(" Vendor specific: %s\n", xtra_buff); + sgj_pr_hr(jsp, " Vendor specific: %s\n", xtra_buff); } - if (op->do_vendor && (act_len > 243) && + if (op->do_vendor && (op->maxlen > 243) && (0 == strncmp("OPEN-V", (const char *)&rp[16], 6))) { memcpy(xtra_buff, &rp[212], 32); if (op->do_export) { @@ -2829,26 +2715,41 @@ std_inq_decode(const struct opts_t * op, int act_len) if (len > 0) printf("VENDOR_SPECIFIC_OPEN-V_LDEV_NAME=%s\n", xtra_buff); } else - printf(" Vendor specific OPEN-V LDEV Name: %s\n", xtra_buff); + sgj_pr_hr(jsp, " Vendor specific OPEN-V LDEV Name: %s\n", + xtra_buff); } } if (! op->do_export) { + sgj_opaque_p jo2p = NULL; + + if (as_json) + jo2p = std_inq_decode_js(rp, op->maxlen, op, jop); if ((0 == op->maxlen) && usn_buff[0]) - printf(" Unit serial number: %s\n", usn_buff); + sgj_pr_hr(jsp, " Unit serial number: %s\n", usn_buff); if (op->do_descriptors) { - if (0 == vdesc_arr[0]) - printf("\n No version descriptors available\n"); - else { - printf("\n Version descriptors:\n"); + sgj_opaque_p jap = sgj_new_named_array(jsp, jo2p, + "version_descriptor_list"); + if (0 == vdesc_arr[0]) { + sgj_pr_hr(jsp, "\n"); + sgj_pr_hr(jsp, " No version descriptors available\n"); + } else { + sgj_pr_hr(jsp, "\n"); + sgj_pr_hr(jsp, " Version descriptors:\n"); for (k = 0; k < 8; ++k) { - if (0 == vdesc_arr[k]) + sgj_opaque_p jo3p = sgj_new_unattached_object(jsp); + int vdv = vdesc_arr[k]; + + if (0 == vdv) break; - cp = find_version_descriptor_str(vdesc_arr[k]); + cp = find_version_descriptor_str(vdv); if (cp) - printf(" %s\n", cp); + sgj_pr_hr(jsp, " %s\n", cp); else - printf(" [unrecognised version descriptor " - "code: 0x%x]\n", vdesc_arr[k]); + sgj_pr_hr(jsp, " [unrecognised version descriptor " + "code: 0x%x]\n", vdv); + sgj_add_nv_ihexstr(jsp, jo3p, "version_descriptor", vdv, + NULL, cp ? cp : "unknown"); + sgj_add_nv_o(jsp, jap, NULL /* name */, jo3p); } } } @@ -2993,16 +2894,17 @@ fini: } -/* Process a standard INQUIRY response. Returns 0 if successful */ +/* Process a standard INQUIRY data format (response). + * Returns 0 if successful */ static int -std_inq_process(int sg_fd, const struct opts_t * op, int inhex_len) +std_inq_process(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int off) { int res, len, rlen, act_len; - char buff[48]; int vb, resid; + char buff[48]; if (sg_fd < 0) { /* assume --inhex=FD usage */ - std_inq_decode(op, inhex_len); + std_inq_decode(op, jop, off); return 0; } rlen = (op->maxlen > 0) ? op->maxlen : SAFE_STD_INQ_RESP_LEN; @@ -3043,7 +2945,8 @@ std_inq_process(int sg_fd, const struct opts_t * op, int inhex_len) if (fetch_unit_serial_num(sg_fd, usn_buff, sizeof(usn_buff), vb)) usn_buff[0] = '\0'; } - std_inq_decode(op, act_len); + op->maxlen = act_len; + std_inq_decode(op, jop, 0); return 0; } else if (res < 0) { /* could be an ATA device */ #if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS) && \ @@ -3082,7 +2985,7 @@ std_inq_process(int sg_fd, const struct opts_t * op, int inhex_len) static int cmddt_process(int sg_fd, const struct opts_t * op) { - int k, j, num, len, peri_type, reserved_cmddt, support_num, res; + int k, j, num, len, pdt, reserved_cmddt, support_num, res; char op_name[128]; memset(rsp_buff, 0, rsp_buff_sz); @@ -3092,7 +2995,7 @@ cmddt_process(int sg_fd, const struct opts_t * op) res = sg_ll_inquiry(sg_fd, true /* cmddt */, false, k, rsp_buff, DEF_ALLOC_LEN, true, op->verbose); if (0 == res) { - peri_type = rsp_buff[0] & PDT_MASK; + pdt = rsp_buff[0] & PDT_MASK; support_num = rsp_buff[1] & 7; reserved_cmddt = rsp_buff[4]; if ((3 == support_num) || (5 == support_num)) { @@ -3101,7 +3004,7 @@ cmddt_process(int sg_fd, const struct opts_t * op) printf(" %.2x", (int)rsp_buff[6 + j]); if (5 == support_num) printf(" [vendor specific manner (5)]"); - sg_get_opcode_name((uint8_t)k, peri_type, + sg_get_opcode_name((uint8_t)k, pdt, sizeof(op_name) - 1, op_name); op_name[sizeof(op_name) - 1] = '\0'; printf(" %s\n", op_name); @@ -3125,10 +3028,10 @@ cmddt_process(int sg_fd, const struct opts_t * op) res = sg_ll_inquiry(sg_fd, true /* cmddt */, false, op->vpd_pn, rsp_buff, DEF_ALLOC_LEN, true, op->verbose); if (0 == res) { - peri_type = rsp_buff[0] & PDT_MASK; + pdt = rsp_buff[0] & PDT_MASK; if (! op->do_raw) { printf("CmdDt INQUIRY, opcode=0x%.2x: [", op->vpd_pn); - sg_get_opcode_name((uint8_t)op->vpd_pn, peri_type, + sg_get_opcode_name((uint8_t)op->vpd_pn, pdt, sizeof(op_name) - 1, op_name); op_name[sizeof(op_name) - 1] = '\0'; printf("%s]\n", op_name); @@ -3204,21 +3107,18 @@ cmddt_process(int sg_fd, const struct opts_t * op) /* Returns 0 if successful */ static int -vpd_mainly_hex(int sg_fd, const struct opts_t * op, int inhex_len) +vpd_mainly_hex(int sg_fd, struct opts_t * op, sgj_opaque_p jap, int off) { int res, len; char b[128]; const char * cp; uint8_t * rp; - rp = rsp_buff + (op->inhex_fn ? op->inhex_off : 0); + rp = rsp_buff + off; if ((! op->do_raw) && (op->do_hex < 2)) printf("VPD INQUIRY, page code=0x%.2x:\n", op->vpd_pn); if (sg_fd < 0) { len = sg_get_unaligned_be16(rp + 2) + 4; - if (op->verbose && (len > inhex_len)) - pr2serr("warning: VPD page's length (%d) > bytes in --inhex=FN " - "file (%d)\n", len , inhex_len); res = 0; } else { memset(rp, 0, DEF_ALLOC_LEN); @@ -3230,7 +3130,7 @@ vpd_mainly_hex(int sg_fd, const struct opts_t * op, int inhex_len) dStrRaw((const char *)rp, len); else { if (0 == op->vpd_pn) - decode_supported_vpd(rp, len, op->do_hex); + decode_supported_vpd(rp, len, op, jap); else { if (op->verbose) { cp = sg_get_pdt_str(rp[0] & PDT_MASK, sizeof(b), b); @@ -3252,12 +3152,18 @@ vpd_mainly_hex(int sg_fd, const struct opts_t * op, int inhex_len) return res; } +static int +recurse_vpd_decode(struct opts_t * op, sgj_opaque_p jop, int off) +{ + return vpd_decode(-1, op, jop, off); +} + /* Returns 0 if successful */ static int -vpd_decode(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int inhex_len) +vpd_decode(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int off) { bool bad = false; - int len, pdt, pn, vb, mxlen /*, pqual */; + int len, pdt, pn, vb /*, pqual */; int res = 0; sgj_state * jsp = &op->json_st; bool as_json = jsp->pr_as_json; @@ -3268,16 +3174,15 @@ vpd_decode(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int inhex_len) uint8_t * rp; // char d[80]; - pn = op->vpd_pn; - rp = rsp_buff + (op->inhex_fn ? op->inhex_off : 0); + rp = rsp_buff + off; vb = op->verbose; - if (sg_fd >= 0) - mxlen = op->maxlen; + if ((off > 0) && (VPD_NOPE_WANT_STD_INQ != op->vpd_pn)) + pn = rp[1]; else - mxlen = inhex_len; + pn = op->vpd_pn; if (sg_fd != -1 && !op->do_force && pn != VPD_SUPPORTED_VPDS) { - res = vpd_fetch_page_from_dev(sg_fd, rp, VPD_SUPPORTED_VPDS, mxlen, - vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, VPD_SUPPORTED_VPDS, + op->maxlen, vb, &len); if (res) goto out; if (vpd_page_not_supported(rp, len, pn, vb)) { @@ -3289,23 +3194,31 @@ vpd_decode(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int inhex_len) } } switch (pn) { - case VPD_SUPPORTED_VPDS: + case VPD_SUPPORTED_VPDS: /* 0x0 ["sv"] */ + np = "Supported VPD pages VPD page"; if (!op->do_raw && (op->do_hex < 2)) - printf("VPD INQUIRY: Supported VPD pages page\n"); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len); + sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (res) break; if (op->do_raw) dStrRaw((const char *)rp, len); else if (op->do_hex) hex2stdout(rp, len, (1 == op->do_hex) ? 0 : -1); - else - decode_supported_vpd(rp, len, op->do_hex); + else { + if (as_json) { + jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); + jap = sgj_new_named_array(jsp, jo2p, + "supported_vpd_page_list"); + } + decode_supported_vpd(rp, len, op, jap); + } break; - case VPD_UNIT_SERIAL_NUM: + case VPD_UNIT_SERIAL_NUM: /* 0x80 ["sn"] */ + np = "Unit serial number VPD page"; if (! op->do_raw && ! op->do_export && (op->do_hex < 2)) - printf("VPD INQUIRY: Unit serial number page\n"); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len); + sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (res) break; if (op->do_raw) @@ -3338,17 +3251,21 @@ vpd_decode(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int inhex_len) printf("\n"); } } else { + if (as_json) + jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); k = encode_unicode((uint8_t *)obuff, len); - if (k > 0) - printf(" Unit serial number: %s\n", obuff); + if (k > 0) { + sgj_pr_hr(jsp, " Unit serial number: %s\n", obuff); + sgj_add_nv_s(jsp, jo2p, "unit_serial_number", obuff); + } } } break; - case VPD_DEVICE_ID: + case VPD_DEVICE_ID: /* 0x83 ["di"] */ np = "Device Identification VPD page"; if (! op->do_raw && ! op->do_export && (op->do_hex < 3)) sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (res) break; if (op->do_raw) @@ -3369,8 +3286,8 @@ vpd_decode(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int inhex_len) case VPD_SOFTW_INF_ID: /* 0x84 ["sii"] */ np = "Software interface identification VPD page"; if (! op->do_raw && (op->do_hex < 2)) - printf("VPD INQUIRY: %s\n", np); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len); + sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (res) break; if (op->do_raw) @@ -3384,11 +3301,11 @@ vpd_decode(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int inhex_len) decode_softw_inf_id(rp, len, op, jap); } break; - case VPD_MAN_NET_ADDR: /* 0x86 ["mna"] */ + case VPD_MAN_NET_ADDR: /* 0x85 ["mna"] */ np = "Management network addresses page"; if (!op->do_raw && (op->do_hex < 2)) sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (res) break; if (op->do_raw) @@ -3405,11 +3322,26 @@ vpd_decode(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int inhex_len) decode_net_man_vpd(rp, len, op, jap); } break; - case VPD_MODE_PG_POLICY: + case VPD_EXT_INQ: /* 0x86 ["ei"] */ + np = "Extended INQUIRY data"; + if (!op->do_raw && (op->do_hex < 2)) + sgj_pr_hr(jsp, "VPD INQUIRY: %s page\n", np); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); + if (res) + break; + if (op->do_raw) + dStrRaw((const char *)rp, len); + else { + if (as_json) + jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); + decode_x_inq_vpd(rp, len, false /* protect */, op, jo2p); + } + break; + case VPD_MODE_PG_POLICY: /* 0x87 ["mpp"] */ np = "Mode page policy"; if (!op->do_raw && (op->do_hex < 2)) sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (res) break; if (op->do_raw) @@ -3423,25 +3355,29 @@ vpd_decode(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int inhex_len) decode_mode_policy_vpd(rp, len, op, jap); } break; - case VPD_EXT_INQ: - np = "Extended INQUIRY data"; + case VPD_SCSI_PORTS: /* 0x88 ["sp"] */ + np = "SCSI Ports VPD page"; if (!op->do_raw && (op->do_hex < 2)) - sgj_pr_hr(jsp, "VPD INQUIRY: %s page\n", np); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len); + sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (res) break; if (op->do_raw) dStrRaw((const char *)rp, len); else { - if (as_json) + if (as_json) { jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); - decode_x_inq_vpd(rp, len, false /* protect */, op, jo2p); + jap = sgj_new_named_array(jsp, jo2p, + "scsi_ports_descriptor_list"); + } + decode_scsi_ports_vpd(rp, len, op, jap); } break; - case VPD_ATA_INFO: + case VPD_ATA_INFO: /* 0x89 ["ai"] */ + np = "ATA information VPD page"; if (!op->do_raw && (op->do_hex < 2)) - printf("VPD INQUIRY: ATA information page\n"); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len); + sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (res) break; /* format output for 'hdparm --Istdin' with '-rr' or '-HHH' */ @@ -3450,14 +3386,19 @@ vpd_decode(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int inhex_len) sg_is_big_endian()); else if (op->do_raw) dStrRaw((const char *)rp, len); - else - decode_ata_info_vpd(rp, len, op->do_hex); + else { + if (as_json) + jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); + else + op->do_long = true; + decode_ata_info_vpd(rp, len, op, jo2p); + } break; case VPD_POWER_CONDITION: /* 0x8a ["pc"] */ np = "Power condition page VPD page"; if (!op->do_raw && (op->do_hex < 2)) sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (res) break; if (op->do_raw) @@ -3468,19 +3409,44 @@ vpd_decode(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int inhex_len) decode_power_condition(rp, len, op, jo2p); } break; - case VPD_SCSI_FEATURE_SETS: /* 0x92 */ + case VPD_DEVICE_CONSTITUENTS: /* 0x8b ["dc"] */ + np = "Device constituents page VPD page"; if (!op->do_raw && (op->do_hex < 2)) - printf("VPD INQUIRY: SCSI Feature sets\n"); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len); + sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (res) break; if (op->do_raw) dStrRaw((const char *)rp, len); - else - decode_feature_sets_vpd(rp, len, op); + else { + if (as_json) { + jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); + jap = sgj_new_named_array(jsp, jo2p, + "constituent_descriptor_list"); + } + decode_dev_constit_vpd(rp, len, op, jap, recurse_vpd_decode); + } + break; + case VPD_SCSI_FEATURE_SETS: /* 0x92 ["sfs"] */ + np = "SCSI Feature sets VPD page"; + if (!op->do_raw && (op->do_hex < 2)) + sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); + if (res) + break; + if (op->do_raw) + dStrRaw((const char *)rp, len); + else { + if (as_json) { + jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); + jap = sgj_new_named_array(jsp, jo2p, + "feature_set_code_list"); + } + decode_feature_sets_vpd(rp, len, op, jap); + } break; case 0xb0: /* VPD pages in B0h to BFh range depend on pdt */ - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { pdt = rp[0] & PDT_MASK; if (! op->do_raw && (op->do_hex < 2)) { @@ -3508,7 +3474,7 @@ vpd_decode(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int inhex_len) pr2serr("VPD INQUIRY: page=0xb0\n"); break; case 0xb1: /* VPD pages in B0h to BFh range depend on pdt */ - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { pdt = rp[0] & PDT_MASK; if (! op->do_raw && (op->do_hex < 2)) { @@ -3544,9 +3510,9 @@ vpd_decode(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int inhex_len) if (!op->do_raw && (op->do_hex < 2)) pr2serr(" Only hex output supported. The sg_vpd utility decodes " "the B2h page.\n"); - return vpd_mainly_hex(sg_fd, op, inhex_len); + return vpd_mainly_hex(sg_fd, op, NULL, off); case 0xb3: /* VPD pages in B0h to BFh range depend on pdt */ - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { pdt = rp[0] & PDT_MASK; if (! op->do_raw && (op->do_hex < 2)) { @@ -3609,24 +3575,6 @@ vpd_decode(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int inhex_len) else decode_rdac_vpd_c9(rp, len, op->do_hex); break; - case VPD_SCSI_PORTS: - np = "SCSI Ports VPD page"; - if (!op->do_raw && (op->do_hex < 2)) - sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len); - if (res) - break; - if (op->do_raw) - dStrRaw((const char *)rp, len); - else { - if (as_json) { - jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); - jap = sgj_new_named_array(jsp, jo2p, - "scsi_ports_descriptor_list"); - } - decode_scsi_ports_vpd(rp, len, op, jap); - } - break; default: bad = true; break; @@ -3636,7 +3584,7 @@ vpd_decode(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int inhex_len) if (!op->do_raw && (op->do_hex < 2)) printf("VPD INQUIRY: ASCII information page, FRU code=0x%x\n", pn); - res = vpd_fetch_page_from_dev(sg_fd, rp, pn, mxlen, vb, &len); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { if (op->do_raw) dStrRaw((const char *)rp, len); @@ -3647,7 +3595,7 @@ vpd_decode(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int inhex_len) if (op->do_hex < 2) pr2serr(" Only hex output supported. The sg_vpd and sdparm " "utilities decode more VPD pages.\n"); - return vpd_mainly_hex(sg_fd, op, inhex_len); + return vpd_mainly_hex(sg_fd, op, NULL, off); } } out: @@ -4188,7 +4136,6 @@ main(int argc, char * argv[]) goto err_out; } op->do_raw = 0; /* don't want raw on output with --inhex= */ - op->inhex_off = 0; if (-1 == op->vpd_pn) { /* may be able to deduce VPD page */ if (op->page_pdt < 0) op->page_pdt = PDT_MASK & rsp_buff[0]; @@ -4316,9 +4263,9 @@ main(int argc, char * argv[]) if (op->inhex_fn) { if (op->do_vpd) { if (op->do_decode) - ret = vpd_decode(-1, op, jop, inhex_len); + ret = vpd_decode(-1, op, jop, 0); else - ret = vpd_mainly_hex(-1, op, inhex_len); + ret = vpd_mainly_hex(-1, op, NULL, 0); goto err_out; } #if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS) && \ @@ -4330,7 +4277,8 @@ main(int argc, char * argv[]) } #endif else { - ret = std_inq_process(-1, op, inhex_len); + op->maxlen = inhex_len; + ret = std_inq_process(-1, op, jop, 0); goto err_out; } } @@ -4403,7 +4351,7 @@ main(int argc, char * argv[]) if ((! op->do_cmddt) && (! op->do_vpd)) { /* So it's a standard INQUIRY, try ATA IDENTIFY if that fails */ - ret = std_inq_process(sg_fd, op, -1); + ret = std_inq_process(sg_fd, op, jop, 0); if (ret) goto err_out; } else if (op->do_cmddt) { @@ -4414,11 +4362,11 @@ main(int argc, char * argv[]) goto err_out; } else if (op->do_vpd) { if (op->do_decode) { - ret = vpd_decode(sg_fd, op, jop, -1); + ret = vpd_decode(sg_fd, op, jop, 0); if (ret) goto err_out; } else { - ret = vpd_mainly_hex(sg_fd, op, -1); + ret = vpd_mainly_hex(sg_fd, op, NULL, 0); if (ret) goto err_out; } diff --git a/src/sg_vpd.c b/src/sg_vpd.c index 122111d2..206fe91d 100644 --- a/src/sg_vpd.c +++ b/src/sg_vpd.c @@ -31,7 +31,7 @@ #include "sg_unaligned.h" #include "sg_pr2serr.h" -#include "sg_vpd_common.h" /* shared with sg_inq */ +#include "sg_vpd_common.h" /* shared with sg_inq */ /* This utility program was originally written for the Linux OS SCSI subsystem. @@ -42,7 +42,7 @@ */ -static const char * version_str = "1.74 20220711"; /* spc6r06 + sbc5r01 */ +static const char * version_str = "1.75 20220714"; /* spc6r06 + sbc5r01 */ #define MY_NAME "sg_vpd" @@ -409,46 +409,12 @@ static const char * sg_ansi_version_arr[16] = "reserved [Fh]", }; -static const char * vpd_p_s = "VPD page"; - -static const char * -hot_pluggable_str(int hp) -{ - switch (hp) { - case 0: - return "No information"; - case 1: - return "target device designed to be removed from SCSI domain"; - case 2: - return "target device not designed to be removed from SCSI domain"; - default: - return "value reserved by T10"; - } -} - -static const char * -tpgs_str(int tpgs) -{ - switch (tpgs) { - case 1: - return "only implicit asymmetric logical unit access"; - case 2: - return "only explicit asymmetric logical unit access"; - case 3: - return "both explicit and implicit asymmetric logical unit access"; - case 0: - default: - return "not supported"; - } -} - 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, tpgs, j, n; + int pqual, pdt, hp, j, n; sgj_state * jsp = &op->json_st; - sgj_opaque_p jo2p; const char * cp; char c[256]; static const int clen = sizeof(c); @@ -504,73 +470,13 @@ std_inq_decode(uint8_t * b, int len, struct opts_t * op, sgj_opaque_p jop) !!(b[7] & 0x02)); if (len < 36) goto skip1; - sgj_pr_hr(jsp, " Vendor_identification: %.8s\n", b + 8); - sgj_pr_hr(jsp, " Product_identification: %.16s\n", b + 16); - sgj_pr_hr(jsp, " Product_revision_level: %.4s\n", b + 32); + 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; - jo2p = sgj_new_snake_named_object(jsp, jop, np); - sgj_add_nv_ihexstr(jsp, jo2p, "peripheral_qualifier", pqual, NULL, - pqual_str(pqual)); - sgj_add_nv_ihexstr(jsp, jo2p, "peripheral_device_type", pdt, NULL, - sg_get_pdt_str(pdt, clen, c)); - sgj_add_nv_ihex_nex(jsp, jo2p, "rmb", !!(b[1] & 0x80), false, - "Removable Medium Bit"); - sgj_add_nv_ihex_nex(jsp, jo2p, "lu_cong", !!(b[1] & 0x40), false, - "Logical Unit Conglomerate"); - sgj_add_nv_ihexstr(jsp, jo2p, "hot_pluggable", hp, NULL, - hot_pluggable_str(hp)); - snprintf(c, clen, "%s", (ver > 0xf) ? "old or reserved version code" : - sg_ansi_version_arr[ver]); - sgj_add_nv_ihexstr(jsp, jo2p, "version", ver, NULL, c); - sgj_add_nv_ihex_nex(jsp, jo2p, "aerc", !!(b[3] & 0x80), false, - "Asynchronous Event Reporting Capability (obsolete " - "SPC-3)"); - sgj_add_nv_ihex_nex(jsp, jo2p, "trmtsk", !!(b[3] & 0x40), false, - "Terminate Task (obsolete SPC-2)"); - sgj_add_nv_ihex_nex(jsp, jo2p, "normaca", !!(b[3] & 0x20), false, - "Normal ACA (Auto Contingent Allegiance)"); - sgj_add_nv_ihex_nex(jsp, jo2p, "hisup", !!(b[3] & 0x10), false, - "Hierarchial Support"); - sgj_add_nv_ihex(jsp, jo2p, "response_data_format", b[3] & 0xf); - sgj_add_nv_ihex_nex(jsp, jo2p, "sccs", !!(b[5] & 0x80), false, - "SCC (SCSI Storage Commands) Supported"); - sgj_add_nv_ihex_nex(jsp, jo2p, "acc", !!(b[5] & 0x40), false, - "Access Commands Coordinator (obsolete SPC-5)"); - tpgs = (b[5] >> 4) & 0x3; - sgj_add_nv_ihexstr_nex(jsp, jo2p, "tpgs", tpgs, false, NULL, - tpgs_str(tpgs), "Target Port Group Support"); - sgj_add_nv_ihex_nex(jsp, jo2p, "3pc", !!(b[5] & 0x8), false, - "Third Party Copy"); - sgj_add_nv_ihex(jsp, jo2p, "protect", !!(b[5] & 0x1)); - /* Skip SPI specific flags which have been obsolete for a while) */ - sgj_add_nv_ihex_nex(jsp, jo2p, "bque", !!(b[6] & 0x80), false, - "Basic task management model (obsolete SPC-4)"); - sgj_add_nv_ihex_nex(jsp, jo2p, "encserv", !!(b[6] & 0x40), false, - "Enclousure Services supported"); - sgj_add_nv_ihex_nex(jsp, jo2p, "multip", !!(b[6] & 0x10), false, - "Multiple SCSI port"); - sgj_add_nv_ihex_nex(jsp, jo2p, "mchngr", !!(b[6] & 0x8), false, - "Medium changer (obsolete SPC-4)"); - sgj_add_nv_ihex_nex(jsp, jo2p, "reladr", !!(b[7] & 0x80), false, - "Relative Addressing (obsolete in SPC-4)"); - sgj_add_nv_ihex_nex(jsp, jo2p, "linked", !!(b[7] & 0x8), false, - "Linked Commands (obsolete in SPC-4)"); - sgj_add_nv_ihex_nex(jsp, jo2p, "cmdque", !!(b[7] & 0x2), false, - "Command Management Model (command queuing)"); - if (len < 16) - return; - snprintf(c, clen, "%.8s", b + 8); - sgj_add_nv_s(jsp, jo2p, "t10_vendor_identification", c); - if (len < 32) - return; - snprintf(c, clen, "%.16s", b + 16); - sgj_add_nv_s(jsp, jo2p, "product_identification", c); - if (len < 36) - return; - snprintf(c, clen, "%.4s", b + 32); - sgj_add_nv_s(jsp, jo2p, "product_revision_level", c); + std_inq_decode_js(b, len, op, jop); } /* VPD_DEVICE_ID 0x83 ["di, di_asis, di_lu, di_port, di_target"] */ @@ -650,10 +556,10 @@ decode_scsi_ports_vpd(uint8_t * buff, int len, struct opts_t * op, } else { char b[1024]; - sg_decode_transportid_str(" ", bp + 8, ip_tid_len, - true, sizeof(b), b); - if (jsp->pr_as_json) - sgj_add_nv_s(jsp, jo2p, "initiator_port_transport_id", b); + sg_decode_transportid_str(" ", bp + 8, ip_tid_len, + true, sizeof(b), b); + if (jsp->pr_as_json) + sgj_add_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)); @@ -963,202 +869,6 @@ filter_dev_ids(const char * print_if_found, int num_leading, uint8_t * buff, return 0; } -/* VPD_ATA_INFO 0x89 ['ai"] */ -static void -decode_ata_info_vpd(const uint8_t * buff, int len, struct opts_t * op, - sgj_opaque_p jop) -{ - bool do_long_nq = op->do_long && (! op->do_quiet); - int num, is_be, cc, n; - sgj_state * jsp = &op->json_st; - const char * cp; - const char * ata_transp; - char b[144]; - char d[80]; - static const int blen = sizeof(b); - static const int dlen = sizeof(d); - static const char * sat_vip = "SAT Vendor identification"; - static const char * sat_pip = "SAT Product identification"; - static const char * sat_prlp = "SAT Product revision level"; - - if (len < 36) { - pr2serr("ATA information VPD page length too short=%d\n", len); - return; - } - if (op->do_hex && (2 != op->do_hex)) { - hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1); - return; - } - memcpy(b, buff + 8, 8); - b[8] = '\0'; - sgj_pr_hr(jsp, " %s: %s\n", sat_vip, b); - memcpy(b, buff + 16, 16); - b[16] = '\0'; - sgj_pr_hr(jsp, " %s: %s\n", sat_pip, b); - memcpy(b, buff + 32, 4); - b[4] = '\0'; - sgj_pr_hr(jsp, " %s: %s\n", sat_prlp, b); - if (len < 56) - return; - ata_transp = (0x34 == buff[36]) ? "SATA" : "PATA"; - if (do_long_nq) { - sgj_pr_hr(jsp, " Device signature [%s] (in hex):\n", ata_transp); - hex2stdout(buff + 36, 20, 0); - } else - sgj_pr_hr(jsp, " Device signature indicates %s transport\n", - ata_transp); - cc = buff[56]; /* 0xec for IDENTIFY DEVICE and 0xa1 for IDENTIFY - * PACKET DEVICE (obsolete) */ - n = snprintf(b, blen, " Command code: 0x%x\n", cc); - if (len < 60) - return; - if (0xec == cc) - cp = ""; - else if (0xa1 == cc) - cp = "PACKET "; - else - cp = NULL; - is_be = sg_is_big_endian(); - if (cp) { - n += sg_scnpr(b + n, blen - n, " ATA command IDENTIFY %sDEVICE " - "response summary:\n", cp); - num = sg_ata_get_chars((const unsigned short *)(buff + 60), 27, 20, - is_be, d); - d[num] = '\0'; - n += sg_scnpr(b + n, blen - n, " model: %s\n", d); - num = sg_ata_get_chars((const unsigned short *)(buff + 60), 10, 10, - is_be, d); - d[num] = '\0'; - n += sg_scnpr(b + n, blen - n, " serial number: %s\n", d); - num = sg_ata_get_chars((const unsigned short *)(buff + 60), 23, 4, - is_be, d); - d[num] = '\0'; - n += sg_scnpr(b + n, blen - n, " firmware revision: %s\n", d); - sgj_pr_hr(jsp, "%s", b); - if (do_long_nq) - sgj_pr_hr(jsp, " ATA command IDENTIFY %sDEVICE response in " - "hex:\n", cp); - } else if (do_long_nq) - sgj_pr_hr(jsp, " ATA command 0x%x got following response:\n", - (unsigned int)cc); - if (jsp->pr_as_json) { - sgj_convert_to_snake_name(sat_vip, d, dlen); - sgj_add_nv_s_len(jsp, jop, d, (const char *)(buff + 8), 8); - sgj_convert_to_snake_name(sat_pip, d, dlen); - sgj_add_nv_s_len(jsp, jop, d, (const char *)(buff + 16), 16); - sgj_convert_to_snake_name(sat_prlp, d, dlen); - sgj_add_nv_s_len(jsp, jop, d, (const char *)(buff + 32), 4); - sgj_add_nv_hex_bytes(jsp, jop, "ata_device_signature", buff + 36, 20); - sgj_add_nv_ihex(jsp, jop, "command_code", buff[56]); - sgj_add_nv_s(jsp, jop, "ata_identify_device_data_example", - "sg_vpd -p ai -HHH /dev/sdc | hdparm --Istdin"); - } - if (len < 572) - return; - if (2 == op->do_hex) - hex2stdout((buff + 60), 512, 0); - else if (do_long_nq) - dWordHex((const unsigned short *)(buff + 60), 256, 0, is_be); -} - -static const char * constituent_type_arr[] = { - "Reserved", - "Virtual tape library", - "Virtual tape drive", - "Direct access block device", -}; - -/* VPD_DEVICE_CONSTITUENTS 0x8b */ -static void -decode_dev_constit_vpd(const uint8_t * buff, int len, struct opts_t * op, - sgj_opaque_p jop) -{ - int k, j, res, bump, csd_len; - uint16_t constit_type; - const uint8_t * bp; - char b[64]; - static const char * dcp = "Device constituents VPD page"; - - if ((1 == op->do_hex) || (op->do_hex > 2)) { - hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1); - return; - } - if (len < 4) { - pr2serr("%s length too short=%d\n", dcp, len); - return; - } - len -= 4; - bp = buff + 4; - for (k = 0, j = 0; k < len; k += bump, bp += bump, ++j) { - if (j > 0) - printf("\n"); - printf(" Constituent descriptor %d:\n", j + 1); - if ((k + 36) > len) { - pr2serr("%s, short descriptor length=36, left=%d\n", dcp, - (len - k)); - return; - } - constit_type = sg_get_unaligned_be16(bp + 0); - if (constit_type >= SG_ARRAY_SIZE(constituent_type_arr)) - printf(" Constituent type: unknown [0x%x]\n", constit_type); - else - printf(" Constituent type: %s [0x%x]\n", - constituent_type_arr[constit_type], constit_type); - printf(" Constituent device type: "); - if (0xff == bp[2]) - printf("Unknown [0xff]\n"); - else if (bp[2] >= 0x20) - printf("Reserved [0x%x]\n", bp[2]); - else - printf("%s [0x%x]\n", - sg_get_pdt_str(PDT_MASK & bp[2], sizeof(b), b), bp[2]); - printf(" Vendor_identification: %.8s\n", bp + 4); - printf(" Product_identification: %.16s\n", bp + 12); - printf(" Product_revision_level: %.4s\n", bp + 28); - csd_len = sg_get_unaligned_be16(bp + 34); - bump = 36 + csd_len; - if ((k + bump) > len) { - pr2serr("%s, short descriptor length=%d, left=%d\n", dcp, bump, - (len - k)); - return; - } - if (csd_len > 0) { - int m, q, cs_bump; - uint8_t cs_type; - uint8_t cs_len; - const uint8_t * cs_bp; - - printf(" Constituent specific descriptors:\n"); - for (m = 0, q = 0, cs_bp = bp + 36; m < csd_len; - m += cs_bump, ++q, cs_bp += cs_bump) { - cs_type = cs_bp[0]; - cs_len = sg_get_unaligned_be16(cs_bp + 2); - cs_bump = cs_len + 4; - if (1 == cs_type) { /* VPD page */ - int off = cs_bp + 4 - buff; - - printf(" Constituent VPD page %d:\n", q + 1); - /* SPC-5 says these shall _not_ themselves be Device - * Constituent VPD pages. So no infinite recursion. */ - res = svpd_decode_t10(-1, op, jop, 0, off, NULL); - if (SG_LIB_CAT_OTHER == res) { - res = svpd_decode_vendor(-1, op, off); - if (SG_LIB_CAT_OTHER == res) - svpd_unable_to_decode(-1, op, 0, off); - } - } else { - if (0xff == cs_type) - printf(" Vendor specific data (in hex):\n"); - else - printf(" Reserved [0x%x] specific data (in " - "hex):\n", cs_type); - hex2stdout(cs_bp + 4, cs_len, 0 /* plus ASCII */); - } - } /* end of Constituent specific descriptor loop */ - } - } /* end Constituent descriptor loop */ -} - static const char * power_unit_arr[] = { "Gigawatts", @@ -1715,53 +1425,6 @@ decode_proto_port_vpd(uint8_t * buff, int len, int do_hex) } } -/* VPD_SCSI_FEATURE_SETS [0x92] (sfs) */ -static void -decode_feature_sets_vpd(uint8_t * buff, int len, const struct opts_t * op) -{ - int k, bump; - uint16_t sf_code; - bool found; - uint8_t * bp; - char b[64]; - - if ((1 == op->do_hex) || (op->do_hex > 2)) { - hex2stdout(buff, len, (1 == op->do_hex) ? 1 : -1); - return; - } - if (len < 4) { - pr2serr("SCSI Feature sets VPD page length too short=%d\n", len); - return; - } - len -= 8; - bp = buff + 8; - for (k = 0; k < len; k += bump, bp += bump) { - sf_code = sg_get_unaligned_be16(bp); - bump = 2; - if ((k + bump) > len) { - pr2serr("SCSI Feature sets, short descriptor length=%d, " - "left=%d\n", bump, (len - k)); - return; - } - if (2 == op->do_hex) - hex2stdout(bp + 8, 2, 1); - else if (op->do_hex > 2) - hex2stdout(bp, 2, 1); - else { - printf(" %s", sg_get_sfs_str(sf_code, -2, sizeof(b), b, - &found, op->verbose)); - if (op->verbose == 1) - printf(" [0x%x]\n", (unsigned int)sf_code); - else if (op->verbose > 1) - printf(" [0x%x] found=%s\n", (unsigned int)sf_code, - found ? "true" : "false"); - else - printf("\n"); - } - } -} - - /* VPD_BLOCK_LIMITS sbc */ /* VPD_SA_DEV_CAP ssc */ /* VPD_OSD_INFO osd */ @@ -2025,17 +1688,18 @@ static const char * prov_type_arr[8] = { /* VPD_LB_PROVISIONING 0xb2 */ static int -decode_block_lb_prov_vpd(uint8_t * b, int len, const struct opts_t * op) +decode_block_lb_prov_vpd(uint8_t * b, int len, struct opts_t * op) { int dp, pt; unsigned int u; + sgj_state * jsp = &op->json_st; if (len < 4) { pr2serr("Logical block provisioning page too short=%d\n", len); return SG_LIB_CAT_MALFORMED; } pt = b[6] & 0x7; - printf(" Unmap command supported (LBPU): %d\n", !!(0x80 & b[5])); + sgj_pr_hr(jsp, " Unmap command supported (LBPU): %d\n", !!(0x80 & b[5])); printf(" Write same (16) with unmap bit supported (LBPWS): %d\n", !!(0x40 & b[5])); printf(" Write same (10) with unmap bit supported (LBPWS10): %d\n", @@ -2235,7 +1899,7 @@ decode_tapealert_supported_vpd(uint8_t * b, int len) /* VPD_LB_PROVISIONING sbc */ /* VPD_TA_SUPPORTED ssc */ static void -decode_b2_vpd(uint8_t * buff, int len, int pdt, const struct opts_t * op) +decode_b2_vpd(uint8_t * buff, int len, int pdt, struct opts_t * op) { if (op->do_hex) { hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1); @@ -2255,16 +1919,19 @@ decode_b2_vpd(uint8_t * buff, int len, int pdt, const struct opts_t * op) } } -/* VPD_REFERRALS sbc */ -/* VPD_AUTOMATION_DEV_SN ssc */ +/* VPD_REFERRALS sbc 0xb3 ["ref"] */ +/* VPD_AUTOMATION_DEV_SN ssc 0xb3 ["adsn"] */ static void -decode_b3_vpd(uint8_t * b, int len, int do_hex, int pdt) +decode_b3_vpd(uint8_t * b, int len, int pdt, struct opts_t * op, + sgj_opaque_p jop) { char obuff[DEF_ALLOC_LEN]; + sgj_state * jsp = &op->json_st; unsigned int u; + char d[64]; - if (do_hex) { - hex2stdout(b, len, (1 == do_hex) ? 0 : -1); + if (op->do_hex) { + hex2stdout(b, len, (1 == op->do_hex) ? 0 : -1); return; } switch (pdt) { @@ -2274,13 +1941,15 @@ decode_b3_vpd(uint8_t * b, int len, int do_hex, int pdt) break; } u = sg_get_unaligned_be32(b + 8); - printf(" User data segment size: "); + snprintf(d, sizeof(d), " User data segment size: "); if (0 == u) - printf("0 [per sense descriptor]\n"); + sgj_pr_hr(jsp, "%s0 [per sense descriptor]\n", d); else - printf("%u\n", u); + sgj_pr_hr(jsp, "%s%u\n", d, u); + sgj_add_nv_ihex(jsp, jop, "user_data_segment_size", u); u = sg_get_unaligned_be32(b + 12); - printf(" User data segment multiplier: %u\n", u); + sgj_pr_hr_js_vi(jsp, jop, 2, "User data segment multiplier", + SGJ_SEP_COLON_1_SPACE, u); break; case PDT_TAPE: case PDT_MCHANGER: memset(obuff, 0, sizeof(obuff)); @@ -2621,6 +2290,19 @@ svpd_unable_to_decode(int sg_fd, struct opts_t * op, int subvalue, int off) 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, 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 @@ -2658,8 +2340,10 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, allow_if_found = (op->examine > 0) && (! op->do_quiet); rp = rsp_buff + off; pn = op->vpd_pn; - if (inhex_active && (VPD_NOPE_WANT_STD_INQ != 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 && 0 == op->examine && pn != VPD_NOPE_WANT_STD_INQ && pn != VPD_SUPPORTED_VPDS) { @@ -2722,13 +2406,13 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, } break; case VPD_SUPPORTED_VPDS: /* 0x0 ["sv"] */ - np = "Supported VPD pages"; + np = "Supported VPD pages VPD page"; if (allow_name) - sgj_pr_hr(jsp, "%s%s %s:\n", pre, np, vpd_p_s); + 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 %s:\n", pre, np, vpd_p_s); + sgj_pr_hr(jsp, "%s%s:\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else if (op->do_hex) @@ -2788,13 +2472,13 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, } break; case VPD_UNIT_SERIAL_NUM: /* 0x80 ["sn"] */ - np = "Unit serial number"; + np = "Unit serial number VPD page"; if (allow_name && not_json) - sgj_pr_hr(jsp, "%s%s VPD page:\n", pre, np); + 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 %s:\n", pre, np, vpd_p_s); + sgj_pr_hr(jsp, "%s%s:\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else if (op->do_hex) @@ -2816,13 +2500,13 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, } break; case VPD_DEVICE_ID: /* 0x83 ["di, di_asis, di_lu, di_port, di_target"] */ - np = "Device Identification"; + np = "Device Identification VPD page"; if (allow_name) - sgj_pr_hr(jsp, "%s%s %s:\n", pre, np, vpd_p_s); + 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 %s:\n", pre, np, vpd_p_s); + sgj_pr_hr(jsp, "%s%s:\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else if (op->do_hex) @@ -2842,13 +2526,13 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, } break; case VPD_SOFTW_INF_ID: /* 0x84 ["sii"] */ - np = "Software interface identification"; + np = "Software interface identification VPD page"; if (allow_name) - sgj_pr_hr(jsp, "%s%s %s:\n", pre, np, vpd_p_s); + 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 %s:\n", pre, np, vpd_p_s); + sgj_pr_hr(jsp, "%s%s:\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else { @@ -2866,13 +2550,13 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, } break; case VPD_MAN_NET_ADDR: /* 0x85 ["mna"] */ - np= "Management network addresses"; + np= "Management network addresses VPD page"; if (allow_name) - sgj_pr_hr(jsp, "%s%s %s:\n", pre, np, vpd_p_s); + 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 %s:\n", pre, np, vpd_p_s); + sgj_pr_hr(jsp, "%s%s:\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else { @@ -2887,13 +2571,13 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, } break; case VPD_EXT_INQ: /* 0x86 ["ei"] */ - np = "extended INQUIRY data"; + np = "extended INQUIRY data VPD page"; if (allow_name) - sgj_pr_hr(jsp, "%s%s %s:\n", pre, np, vpd_p_s); + 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 %s:\n", pre, np, vpd_p_s); + sgj_pr_hr(jsp, "%s%s:\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else { @@ -2920,14 +2604,13 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, } break; case VPD_MODE_PG_POLICY: /* 0x87 */ - np = "Mode page policy"; + np = "Mode page policy VPD page"; if (allow_name) - sgj_pr_hr(jsp, "%s%s %s:\n", pre, np, vpd_p_s); + 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 %s:\n", (prefix ? prefix : ""), np, - vpd_p_s); + sgj_pr_hr(jsp, "%s%s:\n", (prefix ? prefix : ""), np); if (op->do_raw) dStrRaw(rp, len); else { @@ -2945,13 +2628,13 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, } break; case VPD_SCSI_PORTS: /* 0x88 ["sp"] */ - np = "SCSI Ports"; + np = "SCSI Ports VPD page"; if (allow_name) - sgj_pr_hr(jsp, "%s%s %s:\n", pre, np, vpd_p_s); + 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 %s:\n", pre, np, vpd_p_s); + sgj_pr_hr(jsp, "%s%s:\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else { @@ -2969,15 +2652,14 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, } break; case VPD_ATA_INFO: /* 0x89 ['ai"] */ - np = "ATA information"; + np = "ATA information VPD page"; if (allow_name) - sgj_pr_hr(jsp, "%s%s %s:\n", pre, np, vpd_p_s); + 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 %s:\n", (prefix ? prefix : ""), np, - vpd_p_s); + 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 " @@ -2999,7 +2681,7 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, return 0; } break; - case VPD_POWER_CONDITION: /* 0x8a ["pc"\ */ + case VPD_POWER_CONDITION: /* 0x8a ["pc"] */ np = "Power condition VPD page:"; if (allow_name) sgj_pr_hr(jsp, "%s%s\n", pre, np); @@ -3013,23 +2695,34 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); - decode_power_condition(rp, len, op, jop); + 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 */ - np = "Device constituents VPD page:"; + case VPD_DEVICE_CONSTITUENTS: /* 0x8b ["dc"] */ + np = "Device constituents VPD page"; if (allow_name) - printf("%s%s\n", pre, np); + 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) - printf("%s%s\n", pre, np); + sgj_pr_hr(jsp, "%s%s:\n", pre, np); if (op->do_raw) dStrRaw(rp, len); - else - decode_dev_constit_vpd(rp, len, op, jop); + 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_new_named_array(jsp, jo2p, + "constituent_descriptor_list"); + } + decode_dev_constit_vpd(rp, len, op, jap, recurse_vpd_decode); + } return 0; } break; @@ -3111,10 +2804,10 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, return 0; } break; - case VPD_SCSI_FEATURE_SETS: /* 0x92 */ - np = "SCSI Feature sets:"; + case VPD_SCSI_FEATURE_SETS: /* 0x92 ["sfs"] */ + np = "SCSI Feature sets VPD page"; if (allow_name) - printf("%s%s\n", pre, np); + 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) @@ -3123,9 +2816,14 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, dStrRaw(rp, len); else { if (vb || long_notquiet) - printf(" [PQual=%d Peripheral device type: %s]\n", - pqual, pdt_str); - decode_feature_sets_vpd(rp, len, op); + 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_new_named_array(jsp, jo2p, + "feature_set_code_list"); + } + decode_feature_sets_vpd(rp, len, op, jap); } return 0; } @@ -3216,15 +2914,15 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, break; } if (NULL == np) - printf("VPD page=0x%x, pdt=0x%x:\n", pn, pdt); + sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt); else if (allow_name || allow_if_found) - printf("%s%s\n", pre, np); + sgj_pr_hr(jsp, "%s%s\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) - printf(" [PQual=%d Peripheral device type: %s]\n", - pqual, pdt_str); + sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " + "%s]\n", pqual, pdt_str); decode_b2_vpd(rp, len, pdt, op); } return 0; @@ -3248,16 +2946,18 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, break; } if (NULL == np) - printf("VPD page=0x%x, pdt=0x%x:\n", pn, pdt); + sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt); else if (allow_name || allow_if_found) - printf("%s%s\n", pre, np); + sgj_pr_hr(jsp, "%s%s\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) - printf(" [PQual=%d Peripheral device type: %s]\n", - pqual, pdt_str); - decode_b3_vpd(rp, len, op->do_hex, pdt); + 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_b3_vpd(rp, len, pdt, op, jo2p); } return 0; } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) && diff --git a/src/sg_vpd_common.c b/src/sg_vpd_common.c index 49d18670..7f168c5b 100644 --- a/src/sg_vpd_common.c +++ b/src/sg_vpd_common.c @@ -36,6 +36,13 @@ /* This file holds common code for sg_inq and sg_vpd as both those utilities * decode SCSI VPD pages. */ +const char * t10_vendor_id_hr = "T10_vendor_identification"; +const char * t10_vendor_id_js = "t10_vendor_identification"; +const char * product_id_hr = "Product_identification"; +const char * product_id_js = "product_identification"; +const char * product_rev_lev_hr = "Product_revision_level"; +const char * product_rev_lev_js = "product_revision_level"; + sgj_opaque_p sg_vpd_js_hdr(sgj_state * jsp, sgj_opaque_p jop, const char * name, const uint8_t * vpd_hdrp) @@ -556,3 +563,410 @@ filter_json_dev_ids(uint8_t * buff, int len, int m_assoc, struct opts_t * op, } return 0; } + +/* VPD_ATA_INFO 0x89 ['ai"] */ +void +decode_ata_info_vpd(const uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jop) +{ + bool do_long_nq = op->do_long && (! op->do_quiet); + int num, is_be, cc, n; + sgj_state * jsp = &op->json_st; + const char * cp; + const char * ata_transp; + char b[512]; + char d[80]; + static const int blen = sizeof(b); + static const int dlen = sizeof(d); + static const char * sat_vip = "SAT Vendor identification"; + static const char * sat_pip = "SAT Product identification"; + static const char * sat_prlp = "SAT Product revision level"; + + if (len < 36) { + pr2serr("ATA information VPD page length too short=%d\n", len); + return; + } + if (op->do_hex && (2 != op->do_hex)) { + hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1); + return; + } + memcpy(b, buff + 8, 8); + b[8] = '\0'; + sgj_pr_hr(jsp, " %s: %s\n", sat_vip, b); + memcpy(b, buff + 16, 16); + b[16] = '\0'; + sgj_pr_hr(jsp, " %s: %s\n", sat_pip, b); + memcpy(b, buff + 32, 4); + b[4] = '\0'; + sgj_pr_hr(jsp, " %s: %s\n", sat_prlp, b); + if (len < 56) + return; + ata_transp = (0x34 == buff[36]) ? "SATA" : "PATA"; + if (do_long_nq) { + sgj_pr_hr(jsp, " Device signature [%s] (in hex):\n", ata_transp); + hex2stdout(buff + 36, 20, 0); + } else + sgj_pr_hr(jsp, " Device signature indicates %s transport\n", + ata_transp); + cc = buff[56]; /* 0xec for IDENTIFY DEVICE and 0xa1 for IDENTIFY + * PACKET DEVICE (obsolete) */ + n = snprintf(b, blen, " Command code: 0x%x\n", cc); + if (len < 60) + return; + if (0xec == cc) + cp = ""; + else if (0xa1 == cc) + cp = "PACKET "; + else + cp = NULL; + is_be = sg_is_big_endian(); + if (cp) { + n += sg_scnpr(b + n, blen - n, " ATA command IDENTIFY %sDEVICE " + "response summary:\n", cp); + num = sg_ata_get_chars((const unsigned short *)(buff + 60), 27, 20, + is_be, d); + d[num] = '\0'; + n += sg_scnpr(b + n, blen - n, " model: %s\n", d); + num = sg_ata_get_chars((const unsigned short *)(buff + 60), 10, 10, + is_be, d); + d[num] = '\0'; + n += sg_scnpr(b + n, blen - n, " serial number: %s\n", d); + num = sg_ata_get_chars((const unsigned short *)(buff + 60), 23, 4, + is_be, d); + d[num] = '\0'; + n += sg_scnpr(b + n, blen - n, " firmware revision: %s\n", d); + sgj_pr_hr(jsp, "%s", b); + if (do_long_nq) + sgj_pr_hr(jsp, " ATA command IDENTIFY %sDEVICE response in " + "hex:\n", cp); + } else if (do_long_nq) + sgj_pr_hr(jsp, " ATA command 0x%x got following response:\n", + (unsigned int)cc); + if (jsp->pr_as_json) { + sgj_convert_to_snake_name(sat_vip, d, dlen); + sgj_add_nv_s_len(jsp, jop, d, (const char *)(buff + 8), 8); + sgj_convert_to_snake_name(sat_pip, d, dlen); + sgj_add_nv_s_len(jsp, jop, d, (const char *)(buff + 16), 16); + sgj_convert_to_snake_name(sat_prlp, d, dlen); + sgj_add_nv_s_len(jsp, jop, d, (const char *)(buff + 32), 4); + sgj_add_nv_hex_bytes(jsp, jop, "ata_device_signature", buff + 36, 20); + sgj_add_nv_ihex(jsp, jop, "command_code", buff[56]); + sgj_add_nv_s(jsp, jop, "ata_identify_device_data_example", + "sg_vpd -p ai -HHH /dev/sdc | hdparm --Istdin"); + } + if (len < 572) + return; + if (2 == op->do_hex) + hex2stdout((buff + 60), 512, 0); + else if (do_long_nq) + dWordHex((const unsigned short *)(buff + 60), 256, 0, is_be); +} + +/* VPD_SCSI_FEATURE_SETS 0x92 ["sfs"] */ +void +decode_feature_sets_vpd(uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jap) +{ + int k, bump; + uint16_t sf_code; + bool found; + uint8_t * bp; + sgj_opaque_p jo2p; + sgj_state * jsp = &op->json_st; + char b[256]; + char d[80]; + + if ((1 == op->do_hex) || (op->do_hex > 2)) { + hex2stdout(buff, len, (1 == op->do_hex) ? 1 : -1); + return; + } + if (len < 4) { + pr2serr("SCSI Feature sets VPD page length too short=%d\n", len); + return; + } + len -= 8; + bp = buff + 8; + for (k = 0; k < len; k += bump, bp += bump) { + jo2p = sgj_new_unattached_object(jsp); + sf_code = sg_get_unaligned_be16(bp); + bump = 2; + if ((k + bump) > len) { + pr2serr("SCSI Feature sets, short descriptor length=%d, " + "left=%d\n", bump, (len - k)); + return; + } + if (2 == op->do_hex) + hex2stdout(bp + 8, 2, 1); + else if (op->do_hex > 2) + hex2stdout(bp, 2, 1); + else { + sg_scnpr(b, sizeof(b), " %s", + sg_get_sfs_str(sf_code, -2, sizeof(d), d, &found, + op->verbose)); + if (op->verbose == 1) + sgj_pr_hr(jsp, "%s [0x%x]\n", b, (unsigned int)sf_code); + else if (op->verbose > 1) + sgj_pr_hr(jsp, "%s [0x%x] found=%s\n", b, + (unsigned int)sf_code, found ? "true" : "false"); + else + sgj_pr_hr(jsp, "%s\n", b); + sgj_add_nv_ihexstr(jsp, jo2p, "feature_set_code", sf_code, NULL, + d); + if (jsp->verbose) + sgj_add_nv_b(jsp, jo2p, "meaning_is_match", found); + } + sgj_add_nv_o(jsp, jap, NULL, jo2p); + } +} + +static const char * constituent_type_arr[] = { + "Reserved", + "Virtual tape library", + "Virtual tape drive", + "Direct access block device", +}; + +/* VPD_DEVICE_CONSTITUENTS 0x8b ["dc"] */ +void +decode_dev_constit_vpd(const uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jap, recurse_vpd_decodep fp) +{ + uint16_t constit_type; + int k, j, res, bump, csd_len; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p, jo3p, ja2p; + const uint8_t * bp; + char b[256]; + char d[64]; + static const int blen = sizeof(b); + static const int dlen = sizeof(d); + + if ((1 == op->do_hex) || (op->do_hex > 2)) { + hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1); + return; + } + if (len < 4) { + pr2serr("page length too short=%d\n", len); + return; + } + len -= 4; + bp = buff + 4; + for (k = 0, j = 0; k < len; k += bump, bp += bump, ++j) { + jo2p = sgj_new_unattached_object(jsp); + if (j > 0) + sgj_pr_hr(jsp, "\n"); + sgj_pr_hr(jsp, " Constituent descriptor %d:\n", j + 1); + if ((k + 36) > len) { + pr2serr("short descriptor length=36, left=%d\n", (len - k)); + sgj_add_nv_o(jsp, jap, NULL, jo2p); + return; + } + constit_type = sg_get_unaligned_be16(bp + 0); + if (constit_type >= SG_ARRAY_SIZE(constituent_type_arr)) + sgj_pr_hr(jsp," Constituent type: unknown [0x%x]\n", + constit_type); + else + sgj_pr_hr(jsp, " Constituent type: %s [0x%x]\n", + constituent_type_arr[constit_type], constit_type); + sg_scnpr(b, blen, " Constituent device type: "); + if (0xff == bp[2]) + sgj_pr_hr(jsp, "%sUnknown [0xff]\n", b); + else if (bp[2] >= 0x20) + sgj_pr_hr(jsp, "%sReserved [0x%x]\n", b, bp[2]); + else + sgj_pr_hr(jsp, "%s%s [0x%x]\n", b, + sg_get_pdt_str(PDT_MASK & bp[2], dlen, d), bp[2]); + snprintf(b, blen, "%.8s", bp + 4); + sgj_pr_hr(jsp, " %s: %s\n", t10_vendor_id_hr, b); + sgj_add_nv_s(jsp, jo2p, t10_vendor_id_js, b); + snprintf(b, blen, "%.16s", bp + 12); + sgj_pr_hr(jsp, " %s: %s\n", product_id_hr, b); + sgj_add_nv_s(jsp, jo2p, product_id_js, b); + snprintf(b, blen, "%.4s", bp + 28); + sgj_pr_hr(jsp, " %s: %s\n", product_rev_lev_hr, b); + sgj_add_nv_s(jsp, jo2p, product_rev_lev_js, b); + csd_len = sg_get_unaligned_be16(bp + 34); + bump = 36 + csd_len; + if ((k + bump) > len) { + pr2serr("short descriptor length=%d, left=%d\n", bump, (len - k)); + sgj_add_nv_o(jsp, jap, NULL, jo2p); + return; + } + if (csd_len > 0) { + int m, q, cs_bump; + uint8_t cs_type; + uint8_t cs_len; + const uint8_t * cs_bp; + + sgj_pr_hr(jsp, " Constituent specific descriptors:\n"); + ja2p = sgj_new_named_array(jsp, jo2p, + "constituent_specific_descriptor_list"); + for (m = 0, q = 0, cs_bp = bp + 36; m < csd_len; + m += cs_bump, ++q, cs_bp += cs_bump) { + jo3p = sgj_new_unattached_object(jsp); + cs_type = cs_bp[0]; + cs_len = sg_get_unaligned_be16(cs_bp + 2); + cs_bump = cs_len + 4; + sgj_add_nv_ihex(jsp, jo3p, "constituent_specific_type", + cs_type); + if (1 == cs_type) { /* VPD page */ + int off = cs_bp + 4 - buff; + + sgj_pr_hr(jsp, " Constituent specific VPD page " + "%d:\n", q + 1); + /* SPC-5 says these shall _not_ themselves be Device + * Constituent VPD pages. So no infinite recursion. */ + res = (*fp)(op, jo3p, off); + if (res) + pr2serr("%s: recurse_vpd_decode() failed, res=%d\n", + __func__, res); + } else { + if (0xff == cs_type) + sgj_pr_hr(jsp, " Vendor specific data (in " + "hex):\n"); + else + sgj_pr_hr(jsp, " Reserved [0x%x] specific " + "data (in hex):\n", cs_type); + if (jsp->pr_as_json) + sgj_add_nv_hex_bytes(jsp, jo3p, + "constituent_specific_data_hex", + cs_bp + 4, cs_len); + else + hex2stdout(cs_bp + 4, cs_len, 0 /* plus ASCII */); + } + sgj_add_nv_o(jsp, ja2p, NULL, jo3p); + } /* end of Constituent specific descriptor loop */ + } + sgj_add_nv_o(jsp, jap, NULL, jo2p); + } /* end Constituent descriptor loop */ +} + +/* 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 const char * +hot_pluggable_str(int hp) +{ + switch (hp) { + case 0: + return "No information"; + case 1: + return "target device designed to be removed from SCSI domain"; + case 2: + return "target device not designed to be removed from SCSI domain"; + default: + return "value reserved by T10"; + } +} + +static const char * +tpgs_str(int tpgs) +{ + switch (tpgs) { + case 1: + return "only implicit asymmetric logical unit access"; + case 2: + return "only explicit asymmetric logical unit access"; + case 3: + return "both explicit and implicit asymmetric logical unit access"; + case 0: + default: + return "not supported"; + } +} + +sgj_opaque_p +std_inq_decode_js(const uint8_t * b, int len, struct opts_t * op, + sgj_opaque_p jop) +{ + int tpgs; + int pqual = (b[0] & 0xe0) >> 5; + int pdt = b[0] & PDT_MASK; + int hp = (b[1] >> 4) & 0x3; + int ver = b[2]; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p = NULL; + char c[256]; + static const int clen = sizeof(c); + + jo2p = sgj_new_named_object(jsp, jop, "standard_inquiry_data_format"); + sgj_add_nv_ihexstr(jsp, jo2p, "peripheral_qualifier", pqual, NULL, + pqual_str(pqual)); + sgj_add_nv_ihexstr(jsp, jo2p, "peripheral_device_type", pdt, NULL, + sg_get_pdt_str(pdt, clen, c)); + sgj_add_nv_ihex_nex(jsp, jo2p, "rmb", !!(b[1] & 0x80), false, + "Removable Medium Bit"); + sgj_add_nv_ihex_nex(jsp, jo2p, "lu_cong", !!(b[1] & 0x40), false, + "Logical Unit Conglomerate"); + sgj_add_nv_ihexstr(jsp, jo2p, "hot_pluggable", hp, NULL, + hot_pluggable_str(hp)); + snprintf(c, clen, "%s", (ver > 0xf) ? "old or reserved version code" : + sg_ansi_version_arr[ver]); + sgj_add_nv_ihexstr(jsp, jo2p, "version", ver, NULL, c); + sgj_add_nv_ihex_nex(jsp, jo2p, "aerc", !!(b[3] & 0x80), false, + "Asynchronous Event Reporting Capability (obsolete " + "SPC-3)"); + sgj_add_nv_ihex_nex(jsp, jo2p, "trmtsk", !!(b[3] & 0x40), false, + "Terminate Task (obsolete SPC-2)"); + sgj_add_nv_ihex_nex(jsp, jo2p, "normaca", !!(b[3] & 0x20), false, + "Normal ACA (Auto Contingent Allegiance)"); + sgj_add_nv_ihex_nex(jsp, jo2p, "hisup", !!(b[3] & 0x10), false, + "Hierarchial Support"); + sgj_add_nv_ihex(jsp, jo2p, "response_data_format", b[3] & 0xf); + sgj_add_nv_ihex_nex(jsp, jo2p, "sccs", !!(b[5] & 0x80), false, + "SCC (SCSI Storage Commands) Supported"); + sgj_add_nv_ihex_nex(jsp, jo2p, "acc", !!(b[5] & 0x40), false, + "Access Commands Coordinator (obsolete SPC-5)"); + tpgs = (b[5] >> 4) & 0x3; + sgj_add_nv_ihexstr_nex(jsp, jo2p, "tpgs", tpgs, false, NULL, + tpgs_str(tpgs), "Target Port Group Support"); + sgj_add_nv_ihex_nex(jsp, jo2p, "3pc", !!(b[5] & 0x8), false, + "Third Party Copy"); + sgj_add_nv_ihex(jsp, jo2p, "protect", !!(b[5] & 0x1)); + /* Skip SPI specific flags which have been obsolete for a while) */ + sgj_add_nv_ihex_nex(jsp, jo2p, "bque", !!(b[6] & 0x80), false, + "Basic task management model (obsolete SPC-4)"); + sgj_add_nv_ihex_nex(jsp, jo2p, "encserv", !!(b[6] & 0x40), false, + "Enclousure Services supported"); + sgj_add_nv_ihex_nex(jsp, jo2p, "multip", !!(b[6] & 0x10), false, + "Multiple SCSI port"); + sgj_add_nv_ihex_nex(jsp, jo2p, "mchngr", !!(b[6] & 0x8), false, + "Medium changer (obsolete SPC-4)"); + sgj_add_nv_ihex_nex(jsp, jo2p, "reladr", !!(b[7] & 0x80), false, + "Relative Addressing (obsolete in SPC-4)"); + sgj_add_nv_ihex_nex(jsp, jo2p, "linked", !!(b[7] & 0x8), false, + "Linked Commands (obsolete in SPC-4)"); + sgj_add_nv_ihex_nex(jsp, jo2p, "cmdque", !!(b[7] & 0x2), false, + "Command Management Model (command queuing)"); + if (len < 16) + return jo2p; + snprintf(c, clen, "%.8s", b + 8); + sgj_add_nv_s(jsp, jo2p, t10_vendor_id_js, c); + if (len < 32) + return jo2p; + snprintf(c, clen, "%.16s", b + 16); + sgj_add_nv_s(jsp, jo2p, product_id_js, c); + if (len < 36) + return jo2p; + snprintf(c, clen, "%.4s", b + 32); + sgj_add_nv_s(jsp, jo2p, product_rev_lev_js, c); + return jo2p; +} diff --git a/src/sg_vpd_common.h b/src/sg_vpd_common.h index 485418f3..7cb56293 100644 --- a/src/sg_vpd_common.h +++ b/src/sg_vpd_common.h @@ -99,7 +99,6 @@ struct opts_t { int do_raw; /* sg_inq + sg_vpd */ int do_vendor; /* sg_inq */ int examine; /* sg_vpd */ - int inhex_off; /* sg_inq (for decoding multiple VPD pages) */ int maxlen; /* sg_inq[was: resp_len] + sg_vpd */ int num_pages; /* sg_inq */ int page_pdt; /* sg_inq */ @@ -113,40 +112,6 @@ struct opts_t { sgj_state json_st; }; -#if 0 -struct opts_t { - bool do_ata; - bool do_decode; - bool do_descriptors; - bool do_export; - bool do_force; - bool do_only; /* --only after standard inq don't fetch VPD page 0x80 */ - bool verbose_given; - bool version_given; - bool do_vpd; - bool page_given; - bool possible_nvme; - int do_block; - int do_cmddt; - int do_help; - int do_hex; - int do_long; - int do_raw; - int do_vendor; - int verbose; - int resp_len; - int page_num; - int page_pdt; - int num_pages; - const char * page_arg; - const char * device_name; - const char * inhex_fn; -#ifdef SG_SCSI_STRINGS - bool opt_new; -#endif -}; -#endif - struct svpd_values_name_t { int value; /* VPD page number */ int subvalue; /* to differentiate if value+pdt are not unique */ @@ -156,17 +121,8 @@ struct svpd_values_name_t { const char * name; }; -#if 0 -struct svpd_values_name_t { - int value; - int subvalue; - int pdt; /* peripheral device type id, -1 is the default */ - /* (all or not applicable) value */ - int vendor; /* vendor flag */ - const char * acron; - const char * name; -}; -#endif +typedef int (*recurse_vpd_decodep)(struct opts_t *, sgj_opaque_p jop, int off); + sgj_opaque_p sg_vpd_js_hdr(sgj_state * jsp, sgj_opaque_p jop, const char * name, const uint8_t * vpd_hdrp); @@ -182,6 +138,15 @@ void decode_power_condition(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jop); int filter_json_dev_ids(uint8_t * buff, int len, int m_assoc, struct opts_t * op, sgj_opaque_p jap); +void decode_ata_info_vpd(const uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jop); +void decode_feature_sets_vpd(uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jap); +void decode_dev_constit_vpd(const uint8_t * buff, int len, + struct opts_t * op, sgj_opaque_p jap, + recurse_vpd_decodep fp); +sgj_opaque_p std_inq_decode_js(const uint8_t * b, int len, + struct opts_t * op, sgj_opaque_p jop); const char * pqual_str(int pqual); void svpd_enumerate_vendor(int vend_prod_num); @@ -196,6 +161,12 @@ int vpd_fetch_page(int sg_fd, uint8_t * rp, int page, int mxlen, void dup_sanity_chk(int sz_opts_t, int sz_values_name_t); extern uint8_t * rsp_buff; +extern const char * t10_vendor_id_hr; +extern const char * t10_vendor_id_js; +extern const char * product_id_hr; +extern const char * product_id_js; +extern const char * product_rev_lev_hr; +extern const char * product_rev_lev_js; #ifdef __cplusplus |