diff options
author | Douglas Gilbert <dgilbert@interlog.com> | 2022-07-27 19:58:23 +0000 |
---|---|---|
committer | Douglas Gilbert <dgilbert@interlog.com> | 2022-07-27 19:58:23 +0000 |
commit | aac31a343f55c0b40b7d46ba453201fff3430acb (patch) | |
tree | c9e35ef76fc2f3130e116a9a06142b8866aace6b /src | |
parent | 270cd7304dd379ee490df57120d281641c292398 (diff) | |
download | sg3_utils-aac31a343f55c0b40b7d46ba453201fff3430acb.tar.gz |
rescan-scsi-bus.sh: fix handling of '-I <secs>' option ; sg_inq+sg_vpd: more JSON work
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@962 6180dd3e-e324-4e3e-922d-17de1ae2f315
Diffstat (limited to 'src')
-rw-r--r-- | src/sg_inq.c | 453 | ||||
-rw-r--r-- | src/sg_vpd.c | 556 | ||||
-rw-r--r-- | src/sg_vpd_common.c | 763 | ||||
-rw-r--r-- | src/sg_vpd_common.h | 18 |
4 files changed, 1246 insertions, 544 deletions
diff --git a/src/sg_inq.c b/src/sg_inq.c index cf7c4064..c35e9343 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.22 20220718"; /* spc6r06 */ +static const char * version_str = "2.24 20220727"; /* spc6r06 */ #define MY_NAME "sg_inq" @@ -232,6 +232,8 @@ static struct option long_options[] = { {"only", no_argument, 0, 'o'}, {"page", required_argument, 0, 'p'}, {"raw", no_argument, 0, 'r'}, + {"sinq_inraw", required_argument, 0, 'Q'}, + {"sinq-inraw", required_argument, 0, 'Q'}, {"vendor", no_argument, 0, 's'}, {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, @@ -252,9 +254,9 @@ usage() "[--inhex=FN]\n" " [--json[=JO]] [--len=LEN] [--long] " "[--maxlen=LEN]\n" - " [--only] [--page=PG] [--raw] [--vendor] " - "[--verbose]\n" - " [--version] [--vpd] DEVICE\n" + " [--only] [--page=PG] [--raw] [--sinq_inraw=RFN] " + "[--vendor]\n" + " [--verbose] [--version] [--vpd] DEVICE\n" " where:\n" " --ata|-a treat DEVICE as (directly attached) ATA " "device\n"); @@ -265,9 +267,9 @@ usage() "[--inhex=FN]\n" " [--json[=JO]] [--len=LEN] [--long] " "[--maxlen=LEN]\n" - " [--only] [--page=PG] [--raw] [--verbose] " - "[--version]\n" - " [--vpd] DEVICE\n" + " [--only] [--page=PG] [--raw] [--sinq_inraw=RFN] " + "[--verbose]\n" + " [--version] [--vpd] DEVICE\n" " where:\n"); #endif pr2serr(" --block=0|1 0-> open(non-blocking); 1-> " @@ -314,6 +316,9 @@ usage() " abbreviation (opcode number if " "'--cmddt' given)\n" " --raw|-r output response in binary (to stdout)\n" + " --sinq_inraw=RFN|-Q RFN read raw (binary) standard " + "INQUIRY\n" + " response from the RFN filename\n" " --vendor|-s show vendor specific fields in std " "inquiry\n" " --verbose|-v increase verbosity\n" @@ -420,18 +425,18 @@ new_parse_cmd_line(struct opts_t * op, int argc, char * argv[]) #ifdef SG_LIB_LINUX #ifdef SG_SCSI_STRINGS - c = getopt_long(argc, argv, "aB:cdeEfhHiI:j::l:Lm:NoOp:rsuvVx", + c = getopt_long(argc, argv, "aB:cdeEfhHiI:j::l:Lm:NoOp:Q:rsuvVx", long_options, &option_index); #else - c = getopt_long(argc, argv, "B:cdeEfhHiI:j::l:Lm:op:rsuvVx", + c = getopt_long(argc, argv, "B:cdeEfhHiI:j::l:Lm:op:Q:rsuvVx", long_options, &option_index); #endif /* SG_SCSI_STRINGS */ #else /* SG_LIB_LINUX */ #ifdef SG_SCSI_STRINGS - c = getopt_long(argc, argv, "B:cdeEfhHiI:j::l:Lm:NoOp:rsuvVx", + c = getopt_long(argc, argv, "B:cdeEfhHiI:j::l:Lm:NoOp:Q:rsuvVx", long_options, &option_index); #else - c = getopt_long(argc, argv, "B:cdeEfhHiI:j::l:Lm:op:rsuvVx", + c = getopt_long(argc, argv, "B:cdeEfhHiI:j::l:Lm:op:Q:rsuvVx", long_options, &option_index); #endif /* SG_SCSI_STRINGS */ #endif /* SG_LIB_LINUX */ @@ -541,6 +546,9 @@ new_parse_cmd_line(struct opts_t * op, int argc, char * argv[]) op->page_str = optarg; op->page_given = true; break; + case 'Q': + op->sinq_inraw_fn = optarg; + break; case 'r': ++op->do_raw; break; @@ -1966,20 +1974,12 @@ decode_b0_vpd(uint8_t * buff, int len, int do_hex) } } -static const char * zoned_strs[] = { - "", - " [host-aware]", - " [host-managed]", - "", -}; - -/* VPD_BLOCK_DEV_CHARS sbc */ +/* VPD_BLOCK_DEV_CHARS sbc 0xb1 ["bdc"] */ /* VPD_MAN_ASS_SN ssc */ static void decode_b1_vpd(uint8_t * buff, int len, int do_hex) { - int pdt, zoned; - unsigned int u; + int pdt; if (do_hex) { hex2stdout(buff, len, (1 == do_hex) ? 0 : -1); @@ -1988,55 +1988,7 @@ decode_b1_vpd(uint8_t * buff, int len, int do_hex) pdt = PDT_MASK & buff[0]; switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: - if (len < 64) { - pr2serr("Block device characteristics VPD page length too " - "short=%d\n", len); - return; - } - u = sg_get_unaligned_be16(buff + 4); - if (0 == u) - printf(" Medium rotation rate is not reported\n"); - else if (1 == u) - printf(" Non-rotating medium (e.g. solid state)\n"); - else if ((u < 0x401) || (0xffff == u)) - printf(" Reserved [0x%x]\n", u); - else - printf(" Nominal rotation rate: %d rpm\n", u); - printf(" Product type=%d\n", buff[6]); - printf(" WABEREQ=%d\n", (buff[7] >> 6) & 0x3); - printf(" WACEREQ=%d\n", (buff[7] >> 4) & 0x3); - u = buff[7] & 0xf; - printf(" Nominal form factor "); - switch(u) { - case 0: - printf("is not reported\n"); - break; - case 1: - printf("5.25 inches\n"); - break; - case 2: - printf("3.5 inches\n"); - break; - case 3: - printf("2.5 inches\n"); - break; - case 4: - printf("1.8 inches\n"); - break; - case 5: - printf("less then 1.8 inches\n"); - break; - default: - printf("reserved [%u]\n", u); - break; - } - zoned = (buff[8] >> 4) & 0x3; /* added sbc4r04 */ - printf(" ZONED=%d%s\n", zoned, zoned_strs[zoned]); - /* ZONED field made obsolete in sbc5r01 */ - printf(" FUAB=%d\n", buff[8] & 0x2); - printf(" VBULS=%d\n", buff[8] & 0x1); - printf(" DEPOPULATION_TIME=%u (seconds)\n", - sg_get_unaligned_be32(buff + 12)); /* added sbc4r14 */ + /* now done by decode_block_dev_ch_vpd() in sg_vpd_common.c */ break; case PDT_TAPE: case PDT_MCHANGER: case PDT_ADC: printf(" Manufacturer-assigned serial number: %.*s\n", @@ -2054,7 +2006,6 @@ static void decode_b3_vpd(uint8_t * buff, int len, int do_hex) { int pdt; - unsigned int s, m; if (do_hex) { hex2stdout(buff, len, (1 == do_hex) ? 0 : -1); @@ -2063,19 +2014,7 @@ decode_b3_vpd(uint8_t * buff, int len, int do_hex) pdt = PDT_MASK & buff[0]; switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: - if (len < 0x10) { - pr2serr("Referrals VPD page length too short=%d\n", len); - return; - } - s = sg_get_unaligned_be32(buff + 8); - m = sg_get_unaligned_be32(buff + 12); - if (0 == s) - printf(" Single user data segment\n"); - else if (0 == m) - printf(" Segment size specified by user data segment " - "descriptor\n"); - else - printf(" Segment size: %u, segment multiplier: %u\n", s, m); + /* now done in decode)referrals_vpd() in sg_vpd_common.c */ break; default: printf(" Unable to decode pdt=0x%x, in hex:\n", pdt); @@ -3057,6 +2996,7 @@ vpd_decode(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int off) sgj_opaque_p jo2p = NULL; sgj_opaque_p jap = NULL; const char * np; + const char * ep = ""; // const char * pdt_str; uint8_t * rp; // char d[80]; @@ -3402,68 +3342,323 @@ vpd_decode(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int off) case 0xb1: /* VPD pages in B0h to BFh range depend on pdt */ res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { + bool bdc = false; + static const char * masn = + "Manufactured-assigned serial number VPD page"; + + if (op->do_raw) { + dStrRaw((const char *)rp, len); + break; + } pdt = rp[0] & PDT_MASK; - if (! op->do_raw && (op->do_hex < 2)) { - switch (pdt) { - case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: - printf("VPD INQUIRY: Block device characteristics page " - "(SBC)\n"); - break; - case PDT_TAPE: case PDT_MCHANGER: - printf("Manufactured assigned serial number VPD page " - "(SSC):\n"); - break; - case PDT_OSD: - printf("Security token VPD page (OSD):\n"); - break; - case PDT_ADC: - printf("Manufactured assigned serial number VPD page " - "(ADC):\n"); - break; - default: - printf("VPD INQUIRY: page=0x%x, pdt=0x%x\n", 0xb1, pdt); - break; - } + 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; + printf("VPD INQUIRY: page=0x%x, pdt=0x%x\n", 0xb1, pdt); + break; } - if (op->do_raw) - dStrRaw((const char *)rp, len); + if (op->do_hex < 2) { + if (NULL == np) + sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt); + else + sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep); + } + 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->do_hex); } else if (! op->do_raw) pr2serr("VPD INQUIRY: page=0xb1\n"); break; case 0xb2: /* VPD pages in B0h to BFh range depend on pdt */ + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); + if (0 == res) { + bool lbpv = false; + + if (op->do_raw) { + dStrRaw((const char *)rp, len); + break; + } + pdt = rp[0] & PDT_MASK; + 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)"; + break; + default: + np = NULL; + break; + } + if (op->do_hex < 2) { + if (NULL == np) + sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt); + else + sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep); + } + if (as_json) + jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); + if (lbpv) + return decode_block_lb_prov_vpd(rp, len, op, jo2p); + else + return vpd_mainly_hex(sg_fd, op, NULL, off); + } else if (! op->do_raw) + pr2serr("VPD INQUIRY: page=0xb2\n"); + break; +#if 0 +xxxxx 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, NULL, off); - case 0xb3: /* VPD pages in B0h to BFh range depend on pdt */ +#endif + case 0xb3: res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); if (0 == res) { + bool ref = false; + + if (op->do_raw) { + dStrRaw((const char *)rp, len); + break; + } pdt = rp[0] & PDT_MASK; - if (! op->do_raw && (op->do_hex < 2)) { - switch (pdt) { - case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: - printf("VPD INQUIRY: Referrals VPD page (SBC)\n"); - break; - default: - printf("VPD INQUIRY: page=0x%x, pdt=0x%x\n", 0xb3, pdt); - break; - } + switch (pdt) { + case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: + np = "Referrals VPD page"; + ep = "(SBC)"; + ref = true; + break; + default: + np = NULL; + break; } - if (op->do_raw) - dStrRaw((const char *)rp, len); + if (op->do_hex < 2) { + if (NULL == np) + sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt); + else + sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep); + } + 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->do_hex); + return 0; } else if (! op->do_raw) pr2serr("VPD INQUIRY: page=0xb3\n"); break; case 0xb4: + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); + if (0 == res) { + bool sbl = false; + + if (op->do_raw) { + dStrRaw((const char *)rp, len); + break; + } + 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; + default: + np = NULL; + break; + } + if (op->do_hex < 2) { + if (NULL == np) + sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt); + else + sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep); + } + if (as_json) { + jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); + jap = sgj_named_subarray_r(jsp, jo2p, "logical_block_" + "length_and_protection_types_descriptor_list"); + } + if (sbl) + decode_sup_block_lens_vpd(rp, len, op, jap); + else + return vpd_mainly_hex(sg_fd, op, NULL, off); + return 0; + } else if (! op->do_raw) + pr2serr("VPD INQUIRY: page=0xb4\n"); + break; case 0xb5: + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); + if (0 == res) { + bool bdce = false; + + if (op->do_raw) { + dStrRaw((const char *)rp, len); + break; + } + pdt = rp[0] & PDT_MASK; + switch (pdt) { + case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: + np = "Block device characteristics VPD page"; + ep = "(SBC)"; + bdce = true; + break; + default: + np = NULL; + break; + } + if (op->do_hex < 2) { + if (NULL == np) + sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt); + else + sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep); + } + 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 + return vpd_mainly_hex(sg_fd, op, NULL, off); + return 0; + } else if (! op->do_raw) + pr2serr("VPD INQUIRY: page=0xb5\n"); + break; case 0xb6: + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); + if (0 == res) { + bool zbdch = false; + + if (op->do_raw) { + dStrRaw((const char *)rp, len); + break; + } + 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 (op->do_hex < 2) { + if (NULL == np) + sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt); + else + sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep); + } + if (as_json) + jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); + if (zbdch) + decode_zbdch_vpd(rp, len, op, jo2p); + else + return vpd_mainly_hex(sg_fd, op, NULL, off); + return 0; + } else if (! op->do_raw) + pr2serr("VPD INQUIRY: page=0xb6\n"); + break; case 0xb7: + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); + if (0 == res) { + bool ble = false; + + if (op->do_raw) { + dStrRaw((const char *)rp, len); + break; + } + 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 (op->do_hex < 2) { + if (NULL == np) + sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt); + else + sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep); + } + 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 vpd_mainly_hex(sg_fd, op, NULL, off); + return 0; + } else if (! op->do_raw) + pr2serr("VPD INQUIRY: page=0xb7\n"); + break; case 0xb8: + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); + if (0 == res) { + bool fp = false; + + if (op->do_raw) { + dStrRaw((const char *)rp, len); + break; + } + 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 (op->do_hex < 2) { + if (NULL == np) + sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt); + else + sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep); + } + 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 vpd_mainly_hex(sg_fd, op, NULL, off); + return 0; + } else if (! op->do_raw) + pr2serr("VPD INQUIRY: page=0xb8\n"); + break; case 0xb9: +// yyyyyyyy bad = true; pr2serr("Please try the sg_vpd utility which decodes more VPD " "pages\n\n"); @@ -3918,6 +4113,7 @@ main(int argc, char * argv[]) int sg_fd = -1; int ret = 0; int inhex_len = 0; + int inraw_len = 0; const struct svpd_values_name_t * vnp; sgj_state * jsp; sgj_opaque_p jop = NULL; @@ -4042,6 +4238,25 @@ main(int argc, char * argv[]) pr2serr("Unable to allocate %d bytes on heap\n", rsp_buff_sz); return sg_convert_errno(ENOMEM); } + if (op->sinq_inraw_fn) { + if (op->do_cmddt) { + pr2serr("Don't support --cmddt with --sinq-inraw= option\n"); + ret = SG_LIB_CONTRADICT; + goto err_out; + } + 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"); diff --git a/src/sg_vpd.c b/src/sg_vpd.c index 8cb231d7..74a2bd95 100644 --- a/src/sg_vpd.c +++ b/src/sg_vpd.c @@ -42,7 +42,7 @@ */ -static const char * version_str = "1.76 20220717"; /* spc6r06 + sbc5r01 */ +static const char * version_str = "1.78 20220727"; /* spc6r06 + sbc5r01 */ #define MY_NAME "sg_vpd" @@ -1517,104 +1517,20 @@ decode_b0_vpd(uint8_t * buff, int len, int do_hex, int pdt) } } -static const char * product_type_arr[] = -{ - "Not specified", - "CFast", - "CompactFlash", - "MemoryStick", - "MultiMediaCard", - "Secure Digital Card (SD)", - "XQD", - "Universal Flash Storage Card (UFS)", -}; - -/* ZONED field here replaced by ZONED BLOCK DEVICE EXTENSION field in the - * Zoned Block Device Characteristics VPD page. The new field includes - * Zone Domains and Realms (see ZBC-2) */ -static const char * bdc_zoned_strs[] = { - "", - " [host-aware]", - " [host-managed]", - "", -}; - -/* VPD_BLOCK_DEV_CHARS sbc */ +/* 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, int do_hex, int pdt) { - int zoned; - unsigned int u, k; - if (do_hex) { hex2stdout(buff, len, (1 == do_hex) ? 0 : -1); return; } switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: - if (len < 64) { - pr2serr("Block device characteristics VPD page length too " - "short=%d\n", len); - return; - } - u = sg_get_unaligned_be16(buff + 4); - if (0 == u) - printf(" Medium rotation rate is not reported\n"); - else if (1 == u) - printf(" Non-rotating medium (e.g. solid state)\n"); - else if ((u < 0x401) || (0xffff == u)) - printf(" Reserved [0x%x]\n", u); - else - printf(" Nominal rotation rate: %u rpm\n", u); - u = buff[6]; - k = SG_ARRAY_SIZE(product_type_arr); - printf(" Product type: "); - if (u < k) - printf("%s\n", product_type_arr[u]); - else if (u < 0xf0) - printf("Reserved [0x%x]\n", u); - else - printf("Vendor specific [0x%x]\n", u); - printf(" WABEREQ=%d\n", (buff[7] >> 6) & 0x3); - printf(" WACEREQ=%d\n", (buff[7] >> 4) & 0x3); - u = buff[7] & 0xf; - printf(" Nominal form factor"); - switch (u) { - case 0: - printf(" not reported\n"); - break; - case 1: - printf(": 5.25 inch\n"); - break; - case 2: - printf(": 3.5 inch\n"); - break; - case 3: - printf(": 2.5 inch\n"); - break; - case 4: - printf(": 1.8 inch\n"); - break; - case 5: - printf(": less then 1.8 inch\n"); - break; - default: - printf(": reserved\n"); - break; - } - printf(" MACT=%d\n", !!(buff[8] & 0x40)); /* added sbc5r01 */ - zoned = (buff[8] >> 4) & 0x3; /* added sbc4r04, obsolete sbc5r01 */ - printf(" ZONED=%d%s\n", zoned, bdc_zoned_strs[zoned]); - printf(" RBWZ=%d\n", !!(buff[8] & 0x8)); /* sbc4r12 */ - printf(" BOCS=%d\n", !!(buff[8] & 0x4)); /* sbc4r07 */ - printf(" FUAB=%d\n", !!(buff[8] & 0x2)); - printf(" VBULS=%d\n", !!(buff[8] & 0x1)); - printf(" DEPOPULATION_TIME=%u (seconds)\n", - sg_get_unaligned_be32(buff + 12)); /* added sbc4r14 */ - break; + /* now done by decode_block_dev_ch_vpd() in sg_vpd_common.c */ case PDT_TAPE: case PDT_MCHANGER: case PDT_ADC: printf(" Manufacturer-assigned serial number: %.*s\n", len - 4, buff + 4); @@ -1626,108 +1542,7 @@ decode_b1_vpd(uint8_t * buff, int len, int do_hex, int pdt) } } -static const char * prov_type_arr[8] = { - "not known or fully provisioned", - "resource provisioned", - "thin provisioned", - "reserved [0x3]", - "reserved [0x4]", - "reserved [0x5]", - "reserved [0x6]", - "reserved [0x7]", -}; - -/* VPD_LB_PROVISIONING 0xb2 */ -static int -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; - 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", - !!(0x20 & b[5])); - printf(" Logical block provisioning read zeros (LBPRZ): %d\n", - (0x7 & (b[5] >> 2))); /* expanded from 1 to 3 bits in sbc4r07 */ - printf(" Anchored LBAs supported (ANC_SUP): %d\n", !!(0x2 & b[5])); - dp = !!(b[5] & 0x1); - u = b[4]; - printf(" Threshold exponent: "); - if (0 == u) - printf("0 [threshold sets not supported]\n"); - else - printf("%u\n", u); - printf(" Descriptor present (DP): %d\n", dp); - printf(" Minimum percentage: "); - u = 0x1f & (b[6] >> 3); - if (0 == u) - printf("0 [not reported]\n"); - else - printf("%d\n", u); - printf(" Provisioning type: %d (%s)\n", pt, prov_type_arr[pt]); - printf(" Threshold percentage: "); - if (0 == b[7]) - printf("0 [percentages not supported]\n"); - else - printf("%u\n", b[7]); - if (dp && (len > 11)) { - int i_len; - const uint8_t * bp; - char bb[1024]; - - bp = b + 8; - i_len = bp[3]; - if (0 == i_len) { - pr2serr("LB provisioning page provisioning group descriptor too " - "short=%d\n", i_len); - return 0; - } - printf(" Provisioning group descriptor:\n"); - sg_get_designation_descriptor_str(" ", bp, i_len + 4, 0, - op->do_long, sizeof(bb), bb); - printf("%s", bb); - } - return 0; -} - -/* VPD_SUP_BLOCK_LENS 0xb4 (added sbc4r01) */ -static void -decode_sup_block_lens_vpd(uint8_t * buff, int len) -{ - int k; - unsigned int u; - uint8_t * bp; - - if (len < 4) { - pr2serr("Supported block lengths and protection types VPD page " - "length too short=%d\n", len); - return; - } - len -= 4; - bp = buff + 4; - for (k = 0; k < len; k += 8, bp += 8) { - u = sg_get_unaligned_be32(bp); - printf(" Logical block length: %u\n", u); - printf(" P_I_I_SUP: %d\n", !!(bp[4] & 0x40)); - printf(" NO_PI_CHK: %d\n", !!(bp[4] & 0x8)); /* sbc4r05 */ - printf(" GRD_CHK: %d\n", !!(bp[4] & 0x4)); - printf(" APP_CHK: %d\n", !!(bp[4] & 0x2)); - printf(" REF_CHK: %d\n", !!(bp[4] & 0x1)); - printf(" T3PS: %d\n", !!(bp[5] & 0x8)); - printf(" T2PS: %d\n", !!(bp[5] & 0x4)); - printf(" T1PS: %d\n", !!(bp[5] & 0x2)); - printf(" T0PS: %d\n", !!(bp[5] & 0x1)); - } -} - +#if 0 /* VPD_BLOCK_DEV_C_EXTENS 0xb5 (added sbc4r02) */ static void decode_block_dev_char_ext_vpd(uint8_t * b, int len) @@ -1791,6 +1606,7 @@ decode_block_dev_char_ext_vpd(uint8_t * b, int len) printf(" Utilization B: %u\n", sg_get_unaligned_be32(b + 8)); printf(" Utilization A: %u\n", sg_get_unaligned_be32(b + 12)); } +#endif /* VPD_LB_PROTECTION 0xb5 (SSC) [added in ssc5r02a] */ static void @@ -1858,7 +1674,8 @@ decode_b2_vpd(uint8_t * buff, int len, int pdt, struct opts_t * op) } switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: - decode_block_lb_prov_vpd(buff, len, op); + /* decode_block_lb_prov_vpd() is now in sg_vpd_common.c */ + // decode_block_lb_prov_vpd(buff, len, op); break; case PDT_TAPE: case PDT_MCHANGER: decode_tapealert_supported_vpd(buff, len); @@ -1873,13 +1690,9 @@ decode_b2_vpd(uint8_t * buff, int len, int pdt, struct opts_t * op) /* VPD_REFERRALS sbc 0xb3 ["ref"] */ /* VPD_AUTOMATION_DEV_SN ssc 0xb3 ["adsn"] */ static void -decode_b3_vpd(uint8_t * b, int len, int pdt, struct opts_t * op, - sgj_opaque_p jop) +decode_b3_vpd(uint8_t * b, int len, int pdt, struct opts_t * op) { char obuff[DEF_ALLOC_LEN]; - sgj_state * jsp = &op->json_st; - unsigned int u; - char d[64]; if (op->do_hex) { hex2stdout(b, len, (1 == op->do_hex) ? 0 : -1); @@ -1887,20 +1700,7 @@ decode_b3_vpd(uint8_t * b, int len, int pdt, struct opts_t * op, } switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: - if (len < 16) { - pr2serr("Referrals VPD page length too short=%d\n", len); - break; - } - u = sg_get_unaligned_be32(b + 8); - snprintf(d, sizeof(d), " User data segment size: "); - if (0 == u) - sgj_pr_hr(jsp, "%s0 [per sense descriptor]\n", d); - else - sgj_pr_hr(jsp, "%s%u\n", d, u); - sgj_js_nv_ihex(jsp, jop, "user_data_segment_size", u); - u = sg_get_unaligned_be32(b + 12); - sgj_hr_js_vi(jsp, jop, 2, "User data segment multiplier", - SGJ_SEP_COLON_1_SPACE, u, true); + /* now done in decode_referrals_vpd() in sg_vpd_common.c */ break; case PDT_TAPE: case PDT_MCHANGER: memset(obuff, 0, sizeof(obuff)); @@ -1917,7 +1717,7 @@ decode_b3_vpd(uint8_t * b, int len, int pdt, struct opts_t * op, } } -/* VPD_SUP_BLOCK_LENS sbc */ +/* VPD_SUP_BLOCK_LENS sbc ["sbl"] */ /* VPD_DTDE_ADDRESS ssc */ static void decode_b4_vpd(uint8_t * b, int len, int do_hex, int pdt) @@ -1930,7 +1730,7 @@ decode_b4_vpd(uint8_t * b, int len, int do_hex, int pdt) } switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: - decode_sup_block_lens_vpd(b, len); + /* now done by decode_sup_block_lens_vpd() in sg_vpd_common.c */ break; case PDT_TAPE: case PDT_MCHANGER: printf(" Data transfer device element address: 0x"); @@ -1955,7 +1755,7 @@ decode_b5_vpd(uint8_t * b, int len, int do_hex, int pdt) } switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: - decode_block_dev_char_ext_vpd(b, len); + /* now done by decode_block_dev_char_ext_vpd() in sg_vpd_common.c */ break; case PDT_TAPE: case PDT_MCHANGER: decode_lb_protection_vpd(b, len, do_hex); @@ -1967,138 +1767,7 @@ decode_b5_vpd(uint8_t * b, int len, int do_hex, int pdt) } } -/* VPD_ZBC_DEV_CHARS 0xb6 sbc or zbc [zbc2r04] */ -static void -decode_zbdch_vpd(uint8_t * b, int len, int do_hex) -{ - uint32_t u; - char d[32]; - - if (do_hex) { - hex2stdout(b, len, (1 == do_hex) ? 0 : -1); - return; - } - if (len < 64) { - pr2serr("Zoned block device characteristics VPD page length too " - "short=%d\n", len); - return; - } - printf(" Peripheral device type: %s\n", - sg_get_pdt_str(PDT_MASK & b[0], sizeof(d), d)); - printf(" Zoned block device extension: "); - switch ((b[4] >> 4) & 0xf) { - case 0: - if (PDT_ZBC == (PDT_MASK & b[0])) - printf("host managed zoned block device [0, pdt=0x14]\n"); - else - printf("not reported [0]\n"); - break; - case 1: - printf("host aware zoned block device model [1]\n"); - break; - case 2: - printf("Domains and realms zoned block device model [2]\n"); - break; - default: - printf("Unknown [0x%x]\n", (b[4] >> 4) & 0xf); - break; - } - /* activation aligned on realm boundaries */ - printf(" AAORB: %d\n", !!(b[4] & 0x2)); - printf(" URSWRZ: %d\n", !!(b[4] & 0x1)); - u = sg_get_unaligned_be32(b + 8); - printf(" Optimal number of open sequential write preferred zones: "); - if (SG_LIB_UNBOUNDED_32BIT == u) - printf("not reported\n"); - else - printf("%" PRIu32 "\n", u); - u = sg_get_unaligned_be32(b + 12); - printf(" Optimal number of non-sequentially written sequential write " - "preferred zones: "); - if (SG_LIB_UNBOUNDED_32BIT == u) - printf("not reported\n"); - else - printf("%" PRIu32 "\n", u); - u = sg_get_unaligned_be32(b + 16); - printf(" Maximum number of open sequential write required zones: "); - if (SG_LIB_UNBOUNDED_32BIT == u) - printf("no limit\n"); - else - printf("%" PRIu32 "\n", u); - printf(" Zone alignment method: "); /* zbc2r11,zbc2r12 */ - switch (b[23] & 0xf) { - case 0: - printf("not reported [0]\n"); - break; - case 1: - printf("use constant zone lengths\n"); - break; - case 0x8: - printf("zone length given by REPORT ZONES\n"); - break; - default: - printf("Unknown [0x%x]\n", (b[23] & 0xf)); - break; - } - printf(" Zone starting LBA granularity: 0x%" PRIx64 "\n", - sg_get_unaligned_be64(b + 24)); -} - -/* VPD_BLOCK_LIMITS_EXT [0xb7] sbc */ -static void -decode_b7_vpd(uint8_t * buff, int len, int do_hex, int pdt) -{ - unsigned int u; - - if (do_hex) { - hex2stdout(buff, len, (1 == do_hex) ? 0 : -1); - return; - } - switch (pdt) { - case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: - if (len < 12) { - pr2serr("Block limits extension VPD page length too short=%d\n", - len); - return; - } - u = sg_get_unaligned_be16(buff + 6); - printf(" Maximum number of streams: "); - if (0 == u) - printf("0 [Stream control not supported]\n"); - else - printf("%u\n", u); - u = sg_get_unaligned_be16(buff + 8); - printf(" Optimal stream write size: %u blocks\n", u); - u = sg_get_unaligned_be32(buff + 10); - printf(" Stream granularity size: %u\n", u); - if (len > 27) { - u = sg_get_unaligned_be32(buff + 16); - printf(" Maximum scattered LBA range transfer length: "); - if (0 == u) - printf("0 blocks [not reported]\n"); - else - printf("%u blocks\n", u); - u = sg_get_unaligned_be16(buff + 22); - printf(" Maximum scattered LBA range descriptor count: "); - if (0 == u) - printf("0 [not reported]\n"); - else - printf("%u\n", u); - u = sg_get_unaligned_be32(buff + 24); - printf(" Maximum scattered transfer length: "); - if (0 == u) - printf("0 blocks [not reported]\n"); - else - printf("%u blocks\n", u); - } - break; - default: - pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt); - hex2stderr(buff, len, 0); - break; - } -} - +#if 0 /* VPD_FORMAT_PRESETS 0xb8 (added sbc4r18) */ static void decode_format_presets_vpd(uint8_t * buff, int len, int do_hex) @@ -2167,6 +1836,7 @@ decode_format_presets_vpd(uint8_t * buff, int len, int do_hex) } } } +#endif /* VPD_CON_POS_RANGE 0xb9 (added sbc5r01) */ static void @@ -2841,34 +2511,48 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, 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 (SBC):"; + np = "Block device characteristics VPD page"; + ep = "(SBC)"; + bdc = true; break; case PDT_TAPE: case PDT_MCHANGER: - np = "Manufactured-assigned serial number VPD page (SSC):"; + np = masn; + ep = "(SSC)"; break; case PDT_OSD: - np = "Security token VPD page (OSD):"; + np = "Security token VPD page"; + ep = "(OSD)"; break; case PDT_ADC: - np = "Manufactured-assigned serial number VPD page (ADC):"; + np = masn; + ep = "(ADC)"; break; default: np = NULL; 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 %s\n", pre, np, ep ? ep : ""); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) - printf(" [PQual=%d Peripheral device type: %s]\n", - pqual, pdt_str); - decode_b1_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); + if (bdc) + decode_block_dev_ch_vpd(rp, len, op, jo2p); + else + decode_b1_vpd(rp, len, op->do_hex, pdt); } return 0; } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) && @@ -2878,12 +2562,17 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, 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; + switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: - np = "Logical block provisioning VPD page (SBC):"; + np = "Logical block provisioning VPD page"; + ep = "(SBC)"; + lbpv = true; break; case PDT_TAPE: case PDT_MCHANGER: - np = "TapeAlert supported flags VPD page (SSC):"; + np = "TapeAlert supported flags VPD page"; + ep = "(SSC)"; break; default: np = NULL; @@ -2892,14 +2581,19 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, 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\n", pre, np); + 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); - decode_b2_vpd(rp, len, pdt, op); + "%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 + decode_b2_vpd(rp, len, pdt, op); } return 0; } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) && @@ -2909,13 +2603,18 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, 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 (SBC):"; + np = "Referrals VPD page"; + ep = "(SBC)"; + ref = true; break; case PDT_TAPE: case PDT_MCHANGER: - np = "Automation device serial number VPD page SSC):"; + np = "Automation device serial number VPD page"; + ep = "(SSC)"; break; default: np = NULL; @@ -2924,7 +2623,7 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, 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\n", pre, np); + sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : ""); if (op->do_raw) dStrRaw(rp, len); else { @@ -2933,7 +2632,10 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, "%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); + if (ref) + decode_referrals_vpd(rp, len, op, jo2p); + else + decode_b3_vpd(rp, len, pdt, op); } return 0; } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) && @@ -2943,29 +2645,42 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, 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 " - "(SBC):"; + 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 (SSC):"; + np = "Data transfer device element address"; + ep = "(SSC)"; break; default: np = NULL; 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 %s\n", pre, np, ep ? ep : ""); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) - printf(" [PQual=%d Peripheral device type: %s]\n", - pqual, pdt_str); - decode_b4_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); + jap = sgj_named_subarray_r(jsp, jo2p, "logical_block_" + "length_and_protection_types_descriptor_list"); + } + if (sbl) + decode_sup_block_lens_vpd(rp, len, op, jap); + else + decode_b4_vpd(rp, len, op->do_hex, pdt); } return 0; } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) && @@ -2975,85 +2690,115 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, 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; + + 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 (SBC):"; + np = "Block device characteristics extension VPD page"; + ep = "(SBC)"; + bdce = true; break; case PDT_TAPE: case PDT_MCHANGER: - np = "Logical block protection VPD page (SSC):"; + np = "Logical block protection VPD page"; + ep = "(SSC)"; break; default: np = NULL; 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 %s\n", pre, np, ep ? ep : ""); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) - printf(" [PQual=%d Peripheral device type: %s]\n", - pqual, pdt_str); - decode_b5_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); + if (bdce) + decode_block_dev_char_ext_vpd(rp, len, op, jo2p); + else + decode_b5_vpd(rp, len, op->do_hex, pdt); } return 0; } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) && (0 == op->examine)) - printf("%sVPD page=0xb4\n", pre); + printf("%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 (SBC, " - "ZBC):"; + np = "Zoned block device characteristics VPD page"; + ep = "(SBC, ZBC)"; + zbdch = true; break; default: np = NULL; 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 %s\n", pre, np, ep ? ep : ""); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) - printf(" [PQual=%d Peripheral device type: %s]\n", - pqual, pdt_str); - decode_zbdch_vpd(rp, len, op->do_hex); + 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) && (0 == op->examine)) - printf("%sVPD page=0xb5\n", pre); + printf("%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 (SBC):"; + np = "Block limits extension VPD page"; + ep = "(SBC)"; + ble = true; break; default: np = NULL; 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 %s:\n", pre, np, ep ? ep : ""); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) - printf(" [PQual=%d Peripheral device type: %s]\n", - pqual, pdt_str); - decode_b7_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); + 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) && @@ -3063,30 +2808,43 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, 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 (SBC):"; + np = "Format presets VPD page"; + ep = "(SBC)"; + fp = true; break; default: np = NULL; 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 %s:\n", pre, np, ep ? ep : ""); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) - printf(" [PQual=%d Peripheral device type: %s]\n", - pqual, pdt_str); - decode_format_presets_vpd(rp, len, op->do_hex); + 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) && (0 == op->examine)) - printf("%sVPD page=0xb7\n", pre); + printf("%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); diff --git a/src/sg_vpd_common.c b/src/sg_vpd_common.c index 111185a1..c7943f45 100644 --- a/src/sg_vpd_common.c +++ b/src/sg_vpd_common.c @@ -42,6 +42,14 @@ 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"; +static const char * const nl_s = "no limit"; +static const char * const nlr_s = "no limit reported"; +static const char * const nr_s = "not reported"; +static const char * const ns_s = "not supported"; +static const char * const rsv_s = "Reserved"; +static const char * const vs_s = "Vendor specific"; +static const char * const null_s = ""; +static const char * const mn_s = "meaning"; sgj_opaque_p sg_vpd_js_hdr(sgj_state * jsp, sgj_opaque_p jop, const char * name, @@ -286,12 +294,12 @@ decode_x_inq_vpd(uint8_t * b, int len, bool protect, struct opts_t * op, cp = "Logical unit group"; break; default: - cp = "reserved"; + cp = rsv_s; break; } jo2p = sgj_hr_js_subo_r(jsp, jop, 2, np, SGJ_SEP_EQUAL_NO_SPACE, - n); - sgj_js_nv_s(jsp, jo2p, "meaning", cp); + n, false); + sgj_js_nv_s(jsp, jo2p, mn_s, cp); if (jsp->pr_name_ex) sgj_js_nv_s(jsp, jo2p, "abbreviated_name_expansion", nex_p); } else @@ -479,7 +487,7 @@ decode_mode_policy_vpd(uint8_t * buff, int len, struct opts_t * op, n = 0; ppc = (bp[0] & 0x3f); pspc = bp[1]; - snprintf(b + n, blen - n, " Policy page code: 0x%x", ppc); + n = snprintf(b + n, blen - n, " Policy page code: 0x%x", ppc); if (pspc) n += snprintf(b + n, blen - n, ", subpage code: 0x%x", pspc); sgj_pr_hr(jsp, "%s\n", b); @@ -626,7 +634,7 @@ decode_ata_info_vpd(const uint8_t * buff, int len, struct opts_t * op, if (len < 60) return; if (0xec == cc) - cp = ""; + cp = null_s; else if (0xa1 == cc) cp = "PACKET "; else @@ -784,7 +792,7 @@ decode_dev_constit_vpd(const uint8_t * buff, int len, struct opts_t * op, 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]); + sgj_pr_hr(jsp, "%s%s [0x%x]\n", b, rsv_s, 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]); @@ -837,8 +845,8 @@ decode_dev_constit_vpd(const uint8_t * buff, int len, struct opts_t * op, 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); + sgj_pr_hr(jsp, " %s [0x%x] specific data (in " + "hex):\n", rsv_s, cs_type); if (jsp->pr_as_json) sgj_js_nv_hex_bytes(jsp, jo3p, "constituent_specific_data_hex", @@ -901,7 +909,7 @@ tpgs_str(int tpgs) return "both explicit and implicit asymmetric logical unit access"; case 0: default: - return "not supported"; + return ns_s; } } @@ -1057,6 +1065,7 @@ decode_power_consumption(uint8_t * buff, int len, struct opts_t * op, } } + /* VPD_BLOCK_LIMITS 0xb0 ["bl"] */ void decode_block_limits_vpd(const uint8_t * buff, int len, struct opts_t * op, @@ -1071,7 +1080,6 @@ decode_block_limits_vpd(const uint8_t * buff, int len, struct opts_t * op, static const char * mcawl = "Maximum compare and write length"; static const char * otlg = "Optimal transfer length granularity"; static const char * cni = "command not implemented"; - static const char * nr = "not reported"; static const char * ul = "unlimited"; static const char * mtl = "Maximum transfer length"; static const char * otl = "Optimal transfer length"; @@ -1108,36 +1116,36 @@ decode_block_limits_vpd(const uint8_t * buff, int len, struct opts_t * op, u = sg_get_unaligned_be16(buff + 6); if (0 == u) { - sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", otlg, nr); + sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", otlg, nr_s); sgj_convert_to_snake_name(otlg, b, blen); - sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr); + sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s); } else sgj_hr_js_vi_nex(jsp, jop, 2, otlg, SGJ_SEP_COLON_1_SPACE, u, true, "unit: LB"); u = sg_get_unaligned_be32(buff + 8); if (0 == u) { - sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", mtl, nr); + sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", mtl, nr_s); sgj_convert_to_snake_name(mtl, b, blen); - sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr); + sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s); } else sgj_hr_js_vi_nex(jsp, jop, 2, mtl, SGJ_SEP_COLON_1_SPACE, u, true, "unit: LB"); u = sg_get_unaligned_be32(buff + 12); if (0 == u) { - sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", otl, nr); + sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", otl, nr_s); sgj_convert_to_snake_name(otl, b, blen); - sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr); + sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s); } else sgj_hr_js_vi_nex(jsp, jop, 2, otl, SGJ_SEP_COLON_1_SPACE, u, true, "unit: LB"); if (len > 19) { /* added in sbc3r09 */ u = sg_get_unaligned_be32(buff + 16); if (0 == u) { - sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", mpl, nr); + sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", mpl, nr_s); sgj_convert_to_snake_name(mpl, b, blen); - sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr); + sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s); } else sgj_hr_js_vi_nex(jsp, jop, 2, mpl, SGJ_SEP_COLON_1_SPACE, u, true, "unit: LB"); @@ -1170,9 +1178,9 @@ decode_block_limits_vpd(const uint8_t * buff, int len, struct opts_t * op, if (len > 35) { /* added in sbc3r19 */ u = sg_get_unaligned_be32(buff + 28); if (0 == u) { - sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", oug, nr); + sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", oug, nr_s); sgj_convert_to_snake_name(oug, b, blen); - sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr); + sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s); } else sgj_hr_js_vi_nex(jsp, jop, 2, oug, SGJ_SEP_COLON_1_SPACE, u, true, "unit: LB"); @@ -1189,9 +1197,9 @@ decode_block_limits_vpd(const uint8_t * buff, int len, struct opts_t * op, if (len > 43) { /* added in sbc3r26 */ ull = sg_get_unaligned_be64(buff + 36); if (0 == ull) { - sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", mwsl, nr); + sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", mwsl, nr_s); sgj_convert_to_snake_name(mwsl, b, blen); - sgj_js_nv_ihexstr(jsp, jop, b, ull, NULL, nr); + sgj_js_nv_ihexstr(jsp, jop, b, ull, NULL, nr_s); } else sgj_hr_js_vi_nex(jsp, jop, 2, mwsl, SGJ_SEP_COLON_1_SPACE, ull, true, "unit: LB"); @@ -1199,9 +1207,9 @@ decode_block_limits_vpd(const uint8_t * buff, int len, struct opts_t * op, if (len > 47) { /* added in sbc4r02 */ u = sg_get_unaligned_be32(buff + 44); if (0 == u) { - sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", matl, nr); + sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", matl, nr_s); sgj_convert_to_snake_name(matl, b, blen); - sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr); + sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s); } else sgj_hr_js_vi_nex(jsp, jop, 2, matl, SGJ_SEP_COLON_1_SPACE, u, true, "unit: LB"); @@ -1231,9 +1239,9 @@ decode_block_limits_vpd(const uint8_t * buff, int len, struct opts_t * op, if (len > 56) { u = sg_get_unaligned_be32(buff + 56); if (0 == u) { - sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", matlwab, nr); + sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", matlwab, nr_s); sgj_convert_to_snake_name(matlwab, b, blen); - sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr); + sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s); } else sgj_hr_js_vi_nex(jsp, jop, 2, matlwab, SGJ_SEP_COLON_1_SPACE, u, true, "unit: LB"); @@ -1250,3 +1258,706 @@ decode_block_limits_vpd(const uint8_t * buff, int len, struct opts_t * op, u, true, "unit: LB"); } } + +static const char * product_type_arr[] = +{ + "Not specified", + "CFast", + "CompactFlash", + "MemoryStick", + "MultiMediaCard", + "Secure Digital Card (SD)", + "XQD", + "Universal Flash Storage Card (UFS)", +}; + +/* ZONED field here replaced by ZONED BLOCK DEVICE EXTENSION field in the + * Zoned Block Device Characteristics VPD page. The new field includes + * Zone Domains and Realms (see ZBC-2) */ +static const char * bdc_zoned_strs[] = { + nr_s, + "host-aware", + "host-managed", + rsv_s, +}; + +/* VPD_BLOCK_DEV_CHARS 0xb1 ["bdc"] */ +void +decode_block_dev_ch_vpd(const uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jop) +{ + int zoned; + unsigned int u, k; + sgj_state * jsp = &op->json_st; + const char * cp; + char b[144]; + static const int blen = sizeof(b); + static const char * mrr_j = "medium_rotation_rate"; + static const char * mrr_h = "Medium rotation rate"; + static const char * nrm = "Non-rotating medium (e.g. solid state)"; + static const char * pt_j = "product_type"; + + if (len < 64) { + pr2serr("page length too short=%d\n", len); + return; + } + u = sg_get_unaligned_be16(buff + 4); + if (0 == u) { + sgj_pr_hr(jsp, " %s is %s\n", mrr_h, nr_s); + sgj_js_nv_ihexstr(jsp, jop, mrr_j, 0, NULL, nr_s); + } else if (1 == u) { + sgj_pr_hr(jsp, " %s\n", nrm); + sgj_js_nv_ihexstr(jsp, jop, mrr_j, 1, NULL, nrm); + } else if ((u < 0x401) || (0xffff == u)) { + sgj_pr_hr(jsp, " %s [0x%x]\n", rsv_s, u); + sgj_js_nv_ihexstr(jsp, jop, mrr_j, u, NULL, rsv_s); + } else { + sgj_js_nv_ihex_nex(jsp, jop, mrr_j, u, true, + "unit: rpm; nominal rotation rate"); + } + u = buff[6]; + k = SG_ARRAY_SIZE(product_type_arr); + if (u < k) { + sgj_pr_hr(jsp, " %s: %s\n", "Product type", product_type_arr[u]); + sgj_js_nv_ihexstr(jsp, jop, pt_j, u, NULL, product_type_arr[u]); + } else { + sgj_pr_hr(jsp, " %s: %s [0x%x]\n", "Product type", + (u < 0xf0) ? rsv_s : vs_s, u); + sgj_js_nv_ihexstr(jsp, jop, pt_j, u, NULL, (u < 0xf0) ? rsv_s : vs_s); + } + sgj_hr_js_vi_nex(jsp, jop, 2, "WABEREQ", SGJ_SEP_EQUAL_NO_SPACE, + (buff[7] >> 6) & 0x3, false, + "Write After Block Erase REQuired"); + sgj_hr_js_vi_nex(jsp, jop, 2, "WACEREQ", SGJ_SEP_EQUAL_NO_SPACE, + (buff[7] >> 4) & 0x3, false, + "Write After Cryptographic Erase REQuired"); + u = buff[7] & 0xf; + switch (u) { + case 0: + snprintf(b, blen, nr_s); + break; + case 1: + snprintf(b, blen, "5.25 inch"); + break; + case 2: + snprintf(b, blen, "3.5 inch"); + break; + case 3: + snprintf(b, blen, "2.5 inch"); + break; + case 4: + snprintf(b, blen, "1.8 inch"); + break; + case 5: + snprintf(b, blen, "less then 1.8 inch"); + break; + default: + snprintf(b, blen, rsv_s); + break; + } + sgj_pr_hr(jsp, " Nominal form factor: %s\n", b); + sgj_js_nv_ihexstr(jsp, jop, "nominal_forn_factor", u, NULL, b); + sgj_hr_js_vi_nex(jsp, jop, 2, "MACT", SGJ_SEP_EQUAL_NO_SPACE, + !!(buff[8] & 0x40), false, "Multiple ACTuator"); + printf(" MACT=%d\n", !!(buff[8] & 0x40)); /* added sbc5r01 */ + zoned = (buff[8] >> 4) & 0x3; /* added sbc4r04, obsolete sbc5r01 */ + cp = bdc_zoned_strs[zoned]; + sgj_pr_hr(jsp, " ZONED=%d [%s]\n", zoned, cp); + sgj_js_nv_ihexstr_nex(jsp, jop, "zoned", zoned, false, NULL, + cp, "Added in SBC-4, obsolete in SBC-5"); + sgj_hr_js_vi_nex(jsp, jop, 2, "RBWZ", SGJ_SEP_EQUAL_NO_SPACE, + !!(buff[8] & 0x4), false, + "Background Operation Control Supported"); + sgj_hr_js_vi_nex(jsp, jop, 2, "FUAB", SGJ_SEP_EQUAL_NO_SPACE, + !!(buff[8] & 0x2), false, + "Force Unit Access Behaviour"); + sgj_hr_js_vi_nex(jsp, jop, 2, "VBULS", SGJ_SEP_EQUAL_NO_SPACE, + !!(buff[8] & 0x1), false, + "Verify Byte check Unmapped Lba Supported"); + u = sg_get_unaligned_be32(buff + 12); + sgj_hr_js_vi_nex(jsp, jop, 2, "DEPOPULATION TIME", SGJ_SEP_COLON_1_SPACE, + u, true, "unit: second"); +} + +static const char * prov_type_arr[8] = { + "not known or fully provisioned", + "resource provisioned", + "thin provisioned", + rsv_s, + rsv_s, + rsv_s, + rsv_s, + rsv_s, +}; + +/* VPD_LB_PROVISIONING 0xb2 ["lbpv"] */ +int +decode_block_lb_prov_vpd(uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jop) +{ + unsigned int u, dp, pt, t_exp; + sgj_state * jsp = &op->json_st; + const char * cp; + char b[1024]; + static const int blen = sizeof(b); + static const char * mp = "Minimum percentage"; + static const char * tp = "Threshold percentage"; + static const char * pgd = "Provisioning group descriptor"; + + if (len < 4) { + pr2serr("page too short=%d\n", len); + return SG_LIB_CAT_MALFORMED; + } + t_exp = buff[4]; + sgj_js_nv_ihexstr(jsp, jop, "threshold_exponent", t_exp, NULL, + (0 == t_exp) ? ns_s : NULL); + sgj_hr_js_vi_nex(jsp, jop, 2, "LBPU", SGJ_SEP_EQUAL_NO_SPACE, + !!(buff[5] & 0x80), false, + "Logical Block Provisioning Unmap command supported"); + sgj_hr_js_vi_nex(jsp, jop, 2, "LBPWS", SGJ_SEP_EQUAL_NO_SPACE, + !!(buff[5] & 0x40), false, "Logical Block Provisioning " + "Write Same (16) command supported"); + sgj_hr_js_vi_nex(jsp, jop, 2, "LBPWS10", SGJ_SEP_EQUAL_NO_SPACE, + !!(buff[5] & 0x20), false, + "Logical Block Provisioning Write Same (10) command " + "supported"); + sgj_hr_js_vi_nex(jsp, jop, 2, "LBPRZ", SGJ_SEP_EQUAL_NO_SPACE, + (0x7 & (buff[5] >> 2)), true, + "Logical Block Provisioning Read Zero"); + sgj_hr_js_vi_nex(jsp, jop, 2, "ANC_SUP", SGJ_SEP_EQUAL_NO_SPACE, + !!(buff[5] & 0x2), false, + "ANChor SUPported"); + dp = !!(buff[5] & 0x1); + sgj_hr_js_vi_nex(jsp, jop, 2, "DP", SGJ_SEP_EQUAL_NO_SPACE, + dp, false, "Descriptor Present"); + u = 0x1f & (buff[6] >> 3); /* minimum percentage */ + if (0 == u) + sgj_pr_hr(jsp, " %s: 0 [%s]\n", mp, nr_s); + else + sgj_pr_hr(jsp, " %s: %u\n", mp, u); + sgj_convert_to_snake_name(mp, b, blen); + sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, (0 == u) ? nr_s : NULL); + pt = buff[6] & 0x7; + cp = prov_type_arr[pt]; + if (pt > 2) + snprintf(b, blen, " [%u]]", u); + else + b[0] = '\0'; + sgj_pr_hr(jsp, " Provisioning type: %s%s\n", cp, b); + sgj_js_nv_ihexstr(jsp, jop, "provisioning_type", pt, NULL, cp); + u = buff[7]; /* threshold percentage */ + snprintf(b, blen, "%s ", tp); + if (0 == u) + sgj_pr_hr(jsp, " %s: 0 [percentages %s]\n", b, ns_s); + else + sgj_pr_hr(jsp, " %s: %u", b, u); + sgj_convert_to_snake_name(tp, b, blen); + sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, (0 == u) ? ns_s : NULL); + if (dp && (len > 11)) { + int i_len; + const uint8_t * bp; + sgj_opaque_p jo2p; + + bp = buff + 8; + i_len = bp[3]; + if (0 == i_len) { + pr2serr("%s too short=%d\n", pgd, i_len); + return 0; + } + if (jsp->pr_as_json) { + jo2p = sgj_snake_named_subobject_r(jsp, jop, pgd); + sgj_js_designation_descriptor(jsp, jo2p, bp, i_len + 4); + } + sgj_pr_hr(jsp, " %s:\n", pgd); + sg_get_designation_descriptor_str(" ", bp, i_len + 4, true, + op->do_long, blen, b); + if (jsp->pr_as_json && jsp->pr_out_hr) + sgj_js_str_out(jsp, b, strlen(b)); + else + printf("%s", b); + } + return 0; +} + +/* VPD_REFERRALS 0xb3 ["ref"] */ +void +decode_referrals_vpd(uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jop) +{ + uint32_t u; + sgj_state * jsp = &op->json_st; + char b[64]; + + if (len < 16) { + pr2serr("Referrals VPD page length too short=%d\n", len); + return; + } + u = sg_get_unaligned_be32(buff + 8); + snprintf(b, sizeof(b), " User data segment size: "); + if (0 == u) + sgj_pr_hr(jsp, "%s0 [per sense descriptor]\n", b); + else + sgj_pr_hr(jsp, "%s%u\n", b, u); + sgj_js_nv_ihex(jsp, jop, "user_data_segment_size", u); + u = sg_get_unaligned_be32(buff + 12); + sgj_hr_js_vi(jsp, jop, 2, "User data segment multiplier", + SGJ_SEP_COLON_1_SPACE, u, true); +} + +/* VPD_SUP_BLOCK_LENS 0xb4 ["sbl"] (added sbc4r01) */ +void +decode_sup_block_lens_vpd(uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jap) +{ + int k; + unsigned int u; + uint8_t * bp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p = NULL; + + if (len < 4) { + pr2serr("page length too short=%d\n", len); + return; + } + len -= 4; + bp = buff + 4; + for (k = 0; k < len; k += 8, bp += 8) { + if (jsp->pr_as_json) + jo2p = sgj_new_unattached_object_r(jsp); + u = sg_get_unaligned_be32(bp); + sgj_hr_js_vi(jsp, jo2p, 2, "Logical block length", + SGJ_SEP_COLON_1_SPACE, u, true); + sgj_hr_js_vi_nex(jsp, jo2p, 4, "P_I_I_SUP", + SGJ_SEP_COLON_1_SPACE, !!(bp[4] & 0x40), false, + "Protection Information Interval SUPported"); + sgj_hr_js_vi_nex(jsp, jo2p, 4, "NO_PI_CHK", + SGJ_SEP_COLON_1_SPACE, !!(bp[4] & 0x8), false, + "NO Protection Information CHecKing"); + sgj_hr_js_vi_nex(jsp, jo2p, 4, "GRD_CHK", + SGJ_SEP_COLON_1_SPACE, !!(bp[4] & 0x4), false, + "GuaRD CHecK"); + sgj_hr_js_vi_nex(jsp, jo2p, 4, "APP_CHK", + SGJ_SEP_COLON_1_SPACE, !!(bp[4] & 0x2), false, + "APPlication tag CHecK"); + sgj_hr_js_vi_nex(jsp, jo2p, 4, "REF_CHK", + SGJ_SEP_COLON_1_SPACE, !!(bp[4] & 0x1), false, + "REFerence tag CHecK"); + sgj_hr_js_vi_nex(jsp, jo2p, 4, "T3PS", + SGJ_SEP_COLON_1_SPACE, !!(bp[5] & 0x8), false, + "Type 3 Protection Supported"); + sgj_hr_js_vi_nex(jsp, jo2p, 4, "T2PS", + SGJ_SEP_COLON_1_SPACE, !!(bp[5] & 0x4), false, + "Type 2 Protection Supported"); + sgj_hr_js_vi_nex(jsp, jo2p, 4, "T1PS", + SGJ_SEP_COLON_1_SPACE, !!(bp[5] & 0x2), false, + "Type 1 Protection Supported"); + sgj_hr_js_vi_nex(jsp, jo2p, 4, "T0PS", + SGJ_SEP_COLON_1_SPACE, !!(bp[5] & 0x1), false, + "Type 0 Protection Supported"); + sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); + } +} + +/* VPD_BLOCK_DEV_C_EXTENS 0xb5 ["bdce"] (added sbc4r02) */ +void +decode_block_dev_char_ext_vpd(uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jop) +{ + bool b_active = false; + bool combined = false; + int n; + uint32_t u; + sgj_state * jsp = &op->json_st; + const char * utp = null_s; + const char * uup = null_s; + const char * uip = null_s; + char b[128]; + static const int blen = sizeof(b); + + if (len < 16) { + pr2serr("page length too short=%d\n", len); + return; + } + switch (buff[5]) { + case 1: + utp = "Combined writes and reads"; + combined = true; + break; + case 2: + utp = "Writes only"; + break; + case 3: + utp = "Separate writes and reads"; + b_active = true; + break; + default: + utp = rsv_s; + break; + } + sgj_hr_js_vistr(jsp, jop, 2, "Utilization type", SGJ_SEP_COLON_1_SPACE, + buff[5], true, utp); + switch (buff[6]) { + case 2: + uup = "megabytes"; + break; + case 3: + uup = "gigabytes"; + break; + case 4: + uup = "terabytes"; + break; + case 5: + uup = "petabytes"; + break; + case 6: + uup = "exabytes"; + break; + default: + uup = rsv_s; + break; + } + sgj_hr_js_vistr(jsp, jop, 2, "Utilization units", SGJ_SEP_COLON_1_SPACE, + buff[6], true, uup); + switch (buff[7]) { + case 0xa: + uip = "per day"; + break; + case 0xe: + uip = "per year"; + break; + default: + uip = rsv_s; + break; + } + sgj_hr_js_vistr(jsp, jop, 2, "Utilization interval", + SGJ_SEP_COLON_1_SPACE, buff[7], true, uip); + u = sg_get_unaligned_be32(buff + 8); + sgj_hr_js_vistr(jsp, jop, 2, "Utilization B", SGJ_SEP_COLON_1_SPACE, + u, true, (b_active ? NULL : rsv_s)); + n = sg_scnpr(b, blen, "%s: ", "Designed utilization"); + if (b_active) + n += sg_scnpr(b + n, blen - n, "%u %s for reads and ", u, uup); + u = sg_get_unaligned_be32(buff + 12); + sgj_hr_js_vi(jsp, jop, 2, "Utilization A", SGJ_SEP_COLON_1_SPACE, u, true); + n += sg_scnpr(b + n, blen - n, "%u %s for %swrites, %s", u, uup, + combined ? "reads and " : null_s, uip); + sgj_pr_hr(jsp, " %s\n", b); + if (jsp->pr_string) + sgj_js_nv_s(jsp, jop, "summary", b); +} + +/* VPD_ZBC_DEV_CHARS 0xb6 ["zdbch"] sbc or zbc [zbc2r04] */ +void +decode_zbdch_vpd(uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jop) +{ + uint32_t u, pdt; + sgj_state * jsp = &op->json_st; + char b[128]; + static const int blen = sizeof(b); + + if (op->do_hex) { + hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1); + return; + } + if (len < 64) { + pr2serr("Zoned block device characteristics VPD page length too " + "short=%d\n", len); + return; + } + pdt = PDT_MASK & buff[0]; + sgj_pr_hr(jsp, " Peripheral device type: %s\n", + sg_get_pdt_str(pdt, blen, b)); + + printf(" Zoned block device extension: "); + u = (buff[4] >> 4) & 0xf; + switch (u) { + case 0: + if (PDT_ZBC == (PDT_MASK & buff[0])) + snprintf(b, blen, "host managed zoned block device"); + else + snprintf(b, blen, "%s", nr_s); + break; + case 1: + snprintf(b, blen, "host aware zoned block device model"); + break; + case 2: + snprintf(b, blen, "Domains and realms zoned block device model"); + break; + default: + snprintf(b, blen, "%s", rsv_s); + break; + } + sgj_hr_js_vistr(jsp, jop, 2, "Zoned block device extension", + SGJ_SEP_COLON_1_SPACE, u, true, b); + sgj_hr_js_vi_nex(jsp, jop, 2, "AAORB", SGJ_SEP_COLON_1_SPACE, + !!(buff[4] & 0x2), false, + "Activation Aligned On Realm Boundaries"); + sgj_hr_js_vi_nex(jsp, jop, 2, "URSWRZ", SGJ_SEP_COLON_1_SPACE, + !!(buff[4] & 0x1), false, + "Unrestricted Read in Sequential Write Required Zone"); + u = sg_get_unaligned_be32(buff + 8); + sgj_hr_js_vistr(jsp, jop, 2, "Optimal number of open sequential write " + "preferred zones", SGJ_SEP_COLON_1_SPACE, u, true, + (SG_LIB_UNBOUNDED_32BIT == u) ? nr_s : NULL); + u = sg_get_unaligned_be32(buff + 12); + sgj_hr_js_vistr(jsp, jop, 2, "Optimal number of non-sequentially " + "written sequential write preferred zones", + SGJ_SEP_COLON_1_SPACE, u, true, + (SG_LIB_UNBOUNDED_32BIT == u) ? nr_s : NULL); + u = sg_get_unaligned_be32(buff + 16); + sgj_hr_js_vistr(jsp, jop, 2, "Maximum number of open sequential write " + "required zones", SGJ_SEP_COLON_1_SPACE, u, true, + (SG_LIB_UNBOUNDED_32BIT == u) ? nl_s : NULL); + u = buff[23] & 0xf; + switch (u) { + case 0: + snprintf(b, blen, "not reported\n"); + break; + case 1: + snprintf(b, blen, "Zoned starting LBAs aligned using constant zone " + "lengths"); + break; + case 0x8: + snprintf(b, blen, "Zoned starting LBAs potentially non-constant (as " + "reported by REPORT ZONES)"); + break; + default: + snprintf(b, blen, "%s", rsv_s); + break; + } + sgj_hr_js_vistr(jsp, jop, 2, "Zoned alignment method", + SGJ_SEP_COLON_1_SPACE, u, true, b); + sgj_hr_js_vi(jsp, jop, 2, "Zone starting LBA granularity", + SGJ_SEP_COLON_1_SPACE, sg_get_unaligned_be64(buff + 24), + true); +} + +/* VPD_BLOCK_LIMITS_EXT 0xb7 ["ble"] SBC */ +void +decode_block_limits_ext_vpd(uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jop) +{ + uint32_t u; + sgj_state * jsp = &op->json_st; + + if (op->do_hex) { + hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1); + return; + } + if (len < 12) { + pr2serr("page length too short=%d\n", len); + return; + } + u = sg_get_unaligned_be16(buff + 6); + sgj_hr_js_vistr(jsp, jop, 2, "Maximum number of streams", + SGJ_SEP_COLON_1_SPACE, u, true, + (0 == u) ? "Stream control not supported" : NULL); + u = sg_get_unaligned_be16(buff + 8); + sgj_hr_js_vi_nex(jsp, jop, 2, "Optimal stream write size", + SGJ_SEP_COLON_1_SPACE, u, true, "unit: LB"); + u = sg_get_unaligned_be32(buff + 10); + sgj_hr_js_vi_nex(jsp, jop, 2, "Stream granularity size", + SGJ_SEP_COLON_1_SPACE, u, true, + "unit: number of optimal stream write size blocks"); + if (len < 28) + return; + u = sg_get_unaligned_be32(buff + 16); + sgj_hr_js_vistr_nex(jsp, jop, 2, "Maximum scattered LBA range transfer " + "length", SGJ_SEP_COLON_1_SPACE, u, true, + (0 == u ? nlr_s : NULL), + "unit: LB (in a single LBA range descriptor)"); + u = sg_get_unaligned_be16(buff + 22); + sgj_hr_js_vistr(jsp, jop, 2, "Maximum scattered LBA range descriptor " + "count", SGJ_SEP_COLON_1_SPACE, u, true, + (0 == u ? nlr_s : NULL)); + u = sg_get_unaligned_be32(buff + 24); + sgj_hr_js_vistr_nex(jsp, jop, 2, "Maximum scattered transfer length", + SGJ_SEP_COLON_1_SPACE, u, true, + (0 == u ? nlr_s : NULL), + "unit: LB (per single Write Scattered command)"); +} + +static const char * sch_type_arr[8] = { + rsv_s, + "non-zoned", + "host aware zoned", + "host managed zoned", + "zone domain and realms zoned", + rsv_s, + rsv_s, + rsv_s, +}; + +static char * +get_zone_align_method(uint8_t val, char * b, int blen) +{ + switch (val) { + case 0: + snprintf(b, blen, "%s", nr_s); + break; + case 1: + snprintf(b, blen, "%s", "using constant zone lengths"); + break; + case 8: + snprintf(b, blen, "%s", "taking gap zones into account"); + break; + default: + snprintf(b, blen, "%s", rsv_s); + break; + } + return b; +} + +/* VPD_FORMAT_PRESETS 0xb8 ["fp"] (added sbc4r18) */ +void +decode_format_presets_vpd(uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jap) +{ + uint8_t sch_type; + int k; + uint32_t u; + uint64_t ul; + sgj_state * jsp = &op->json_st; + uint8_t * bp; + sgj_opaque_p jo2p, jo3p; + const char * cp; + char b[128]; + char d[64]; + static const int blen = sizeof(b); + static const int dlen = sizeof(d); + static const char * llczp = "Low LBA conventional zones percentage"; + static const char * hlczp = "High LBA conventional zones percentage"; + static const char * ztzd = "Zone type for zone domain"; + + if (op->do_hex) { + hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1); + return; + } + if (len < 4) { + pr2serr("VPD page length too short=%d\n", len); + return; + } + len -= 4; + bp = buff + 4; + for (k = 0; k < len; k += 64, bp += 64) { + jo2p = sgj_new_unattached_object_r(jsp); + sgj_hr_js_vi(jsp, jo2p, 2, "Preset identifier", SGJ_SEP_COLON_1_SPACE, + sg_get_unaligned_be64(bp + 0), true); + sch_type = bp[4]; + if (sch_type < 8) { + cp = sch_type_arr[sch_type]; + if (rsv_s != cp) + snprintf(b, blen, "%s block device", cp); + else + snprintf(b, blen, "%s", cp); + } else + snprintf(b, blen, "%s", rsv_s); + sgj_hr_js_vistr(jsp, jo2p, 4, "Schema type", SGJ_SEP_COLON_1_SPACE, + sch_type, true, b); + sgj_hr_js_vi(jsp, jo2p, 4, "Logical blocks per physical block " + "exponent", SGJ_SEP_COLON_1_SPACE, + 0xf & bp[7], true); + sgj_hr_js_vi_nex(jsp, jo2p, 4, "Logical block length", + SGJ_SEP_COLON_1_SPACE, sg_get_unaligned_be32(bp + 8), + true, "unit: byte"); + sgj_hr_js_vi(jsp, jo2p, 4, "Designed last Logical Block Address", + SGJ_SEP_COLON_1_SPACE, + sg_get_unaligned_be64(bp + 16), true); + sgj_hr_js_vi_nex(jsp, jo2p, 4, "FMTPINFO", SGJ_SEP_COLON_1_SPACE, + (bp[38] >> 6) & 0x3, false, + "ForMaT Protecion INFOrmation (see Format Unit)"); + sgj_hr_js_vi(jsp, jo2p, 4, "Protection field usage", + SGJ_SEP_COLON_1_SPACE, bp[38] & 0x7, false); + sgj_hr_js_vi(jsp, jo2p, 4, "Protection interval exponent", + SGJ_SEP_COLON_1_SPACE, bp[39] & 0xf, true); + jo3p = sgj_named_subobject_r(jsp, jo2p, + "schema_type_specific_information"); + switch (sch_type) { + case 2: + sgj_pr_hr(jsp, " Defines zones for host aware device:\n"); + u = bp[40 + 0]; + sgj_pr_hr(jsp, " %s: %u.%u %%\n", llczp, u / 10, u % 10); + sgj_convert_to_snake_name(llczp, b, blen); + sgj_js_nv_ihex_nex(jsp, jo3p, b, u, true, "unit: 1/10 of a " + "percent"); + u = bp[40 + 1]; + sgj_pr_hr(jsp, " %s: %u.%u %%\n", hlczp, u / 10, u % 10); + sgj_convert_to_snake_name(hlczp, b, blen); + sgj_js_nv_ihex_nex(jsp, jo3p, b, u, true, "unit: 1/10 of a " + "percent"); + u = sg_get_unaligned_be32(bp + 40 + 12); + sgj_hr_js_vistr(jsp, jo3p, 6, "Logical blocks per zone", + SGJ_SEP_COLON_1_SPACE, u, true, + (0 == u ? rsv_s : NULL)); + break; + case 3: + sgj_pr_hr(jsp, " Defines zones for host managed device:\n"); + u = bp[40 + 0]; + sgj_pr_hr(jsp, " %s: %u.%u %%\n", llczp, u / 10, u % 10); + sgj_convert_to_snake_name(llczp, b, blen); + sgj_js_nv_ihex_nex(jsp, jo3p, b, u, true, "unit: 1/10 of a " + "percent"); + u = bp[40 + 1]; + sgj_pr_hr(jsp, " %s: %u.%u %%\n", hlczp, u / 10, u % 10); + sgj_convert_to_snake_name(hlczp, b, blen); + sgj_js_nv_ihex_nex(jsp, jo3p, b, u, true, "unit: 1/10 of a " + "percent"); + u = bp[40 + 3] & 0x7; + sgj_hr_js_vistr(jsp, jo3p, 6, "Designed zone alignment method", + SGJ_SEP_COLON_1_SPACE, u, true, + get_zone_align_method(u, b, blen)); + ul = sg_get_unaligned_be64(bp + 40 + 4); + sgj_hr_js_vi_nex(jsp, jo3p, 6, "Designed zone starting LBA " + "granularity", SGJ_SEP_COLON_1_SPACE, ul, true, + "unit: LB"); + u = sg_get_unaligned_be32(bp + 40 + 12); + sgj_hr_js_vistr(jsp, jo3p, 6, "Logical blocks per zone", + SGJ_SEP_COLON_1_SPACE, u, true, + (0 == u ? rsv_s : NULL)); + break; + case 4: + sgj_pr_hr(jsp, " Defines zones for zone domains and realms " + "device:\n"); + snprintf(b, blen, "%s 0", ztzd); + u = bp[40 + 0]; + sg_get_zone_type_str((u >> 4) & 0xf, dlen, d); + sgj_hr_js_vistr(jsp, jo3p, 6, b, SGJ_SEP_COLON_1_SPACE, u, true, + d); + snprintf(b, blen, "%s 1", ztzd); + sg_get_zone_type_str(u & 0xf, dlen, d); + sgj_hr_js_vistr(jsp, jo3p, 6, b, SGJ_SEP_COLON_1_SPACE, u, true, + d); + + snprintf(b, blen, "%s 2", ztzd); + u = bp[40 + 1]; + sg_get_zone_type_str((u >> 4) & 0xf, dlen, d); + sgj_hr_js_vistr(jsp, jo3p, 6, b, SGJ_SEP_COLON_1_SPACE, u, true, + d); + snprintf(b, blen, "%s 3", ztzd); + sg_get_zone_type_str(u & 0xf, dlen, d); + sgj_hr_js_vistr(jsp, jo3p, 6, b, SGJ_SEP_COLON_1_SPACE, u, true, + d); + u = bp[40 + 3] & 0x7; + sgj_hr_js_vistr(jsp, jo3p, 6, "Designed zone alignment method", + SGJ_SEP_COLON_1_SPACE, u, true, + get_zone_align_method(u, d, dlen)); + ul = sg_get_unaligned_be64(bp + 40 + 4); + sgj_hr_js_vi_nex(jsp, jo3p, 6, "Designed zone starting LBA " + "granularity", SGJ_SEP_COLON_1_SPACE, ul, true, + "unit: LB"); + u = sg_get_unaligned_be32(bp + 40 + 12); + sgj_hr_js_vistr(jsp, jo3p, 6, "Logical blocks per zone", + SGJ_SEP_COLON_1_SPACE, u, true, + (0 == u ? rsv_s : NULL)); + ul = sg_get_unaligned_be64(bp + 40 + 16); + sgj_hr_js_vi_nex(jsp, jo3p, 6, "Designed zone maximum address", + SGJ_SEP_COLON_1_SPACE, ul, true, "unit: LBA"); + break; + default: + sgj_pr_hr(jsp, " No schema type specific information\n"); + break; + } + sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); + } +} diff --git a/src/sg_vpd_common.h b/src/sg_vpd_common.h index 3f182664..0571ece1 100644 --- a/src/sg_vpd_common.h +++ b/src/sg_vpd_common.h @@ -10,6 +10,8 @@ * SPDX-License-Identifier: BSD-2-Clause */ +/* This is a common header file for the sg_inq and sg_vpd utilities */ + #include <stdint.h> #include <stdbool.h> @@ -154,6 +156,22 @@ void decode_power_consumption(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jap); void decode_block_limits_vpd(const uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jop); +void decode_block_dev_ch_vpd(const uint8_t * buff, int len, + struct opts_t * op, sgj_opaque_p jop); +int decode_block_lb_prov_vpd(uint8_t * buff, int len, + struct opts_t * op, sgj_opaque_p jop); +void decode_referrals_vpd(uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jop); +void decode_sup_block_lens_vpd(uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jap); +void decode_block_dev_char_ext_vpd(uint8_t * buff, int len, + struct opts_t * op, sgj_opaque_p jop); +void decode_zbdch_vpd(uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jop); +void decode_block_limits_ext_vpd(uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jop); +void decode_format_presets_vpd(uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jap); const char * pqual_str(int pqual); void svpd_enumerate_vendor(int vend_prod_num); |