/
githubmirror
/
sg3_utils
Обзор
Документация
Войти
/
githubmirror
/
sg3_utils
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/sg_vpd.c
3 129 строк
110 KB
Douglas Gilbert
Various small changes
02 июл 2026, 07:13
02 июл 2026, 07:13
8bdcae2
Код
Авторство
О чём код?
/* * Copyright (c) 2006-2026 Douglas Gilbert. * All rights reserved. * * SPDX-License-Identifier: BSD-2-Clause */ #include <unistd.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <stdbool.h> #include <string.h> #include <ctype.h> #include <getopt.h> #define __STDC_FORMAT_MACROS 1 #include <inttypes.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sg_lib.h" #include "sg_lib_names.h" #include "sg_cmds_basic.h" #include "sg_pt.h" #include "sg_unaligned.h" #include "sg_pr2serr.h" #include "sg_vpd_common.h" /* shared with sg_inq */ /* This utility program was originally written for the Linux OS SCSI subsystem. This program fetches Vital Product Data (VPD) pages from the given device and outputs it as directed. VPD pages are obtained via a SCSI INQUIRY command. Most of the data in this program is obtained from the SCSI SPC-4 document at https://www.t10.org . */ static const char * version_str = "2.09 20260630"; /* spc7r05 + sbc6r02 */ #define MY_NAME "sg_vpd" /* Device identification VPD page associations */ #define VPD_ASSOC_LU 0 #define VPD_ASSOC_TPORT 1 #define VPD_ASSOC_TDEVICE 2 /* values for selection one or more associations (2**vpd_assoc), except _AS_IS */ #define VPD_DI_SEL_LU 1 #define VPD_DI_SEL_TPORT 2 #define VPD_DI_SEL_TARGET 4 #define VPD_DI_SEL_AS_IS 32 #define DEF_ALLOC_LEN 252 #define MIN_MAXLEN 16 #define MX_ALLOC_LEN (0xc000 + 0x80) #define VPD_ATA_INFO_LEN 572 #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */ #define INQUIRY_CMD 0x12 #define INQUIRY_CMDLEN 6 #define DEF_PT_TIMEOUT 60 /* 60 seconds */ uint8_t * rsp_buff; static int svpd_decode_t10(struct sg_pt_base * ptvp, struct opts_t * op, sgj_opaque_p jop, int subvalue, int off, const char * prefix); static int svpd_desig_descs(const char * print_if_found, int leadin_sp, const uint8_t * buff, int blen, bool pr_assoc, int m_assoc, struct opts_t * op, sgj_opaque_p jop); static const int rsp_buff_sz = MX_ALLOC_LEN + 2; static uint8_t * free_rsp_buff; static const struct option long_options[] = { {"all", no_argument, 0, 'a'}, {"debug", no_argument, 0, 'D'}, {"descriptors", no_argument, 0, 'd'}, {"desc", no_argument, 0, 'd'}, {"enumerate", no_argument, 0, 'e'}, {"examine", no_argument, 0, 'E'}, {"export", no_argument, 0, 'Z'}, /* error message says to try sg_inq */ {"force", no_argument, 0, 'f'}, {"help", no_argument, 0, 'h'}, {"hex", no_argument, 0, 'H'}, {"ident", no_argument, 0, 'i'}, {"inhex", required_argument, 0, 'I'}, {"json", optional_argument, 0, '^'}, /* short option is '-j' */ {"js-file", required_argument, 0, 'J'}, {"js_file", required_argument, 0, 'J'}, {"long", no_argument, 0, 'l'}, {"maxlen", required_argument, 0, 'm'}, {"page", required_argument, 0, 'p'}, {"quiet", no_argument, 0, 'q'}, {"raw", no_argument, 0, 'r'}, {"sinq_inraw", required_argument, 0, 'Q'}, {"sinq-inraw", required_argument, 0, 'Q'}, {"vendor", required_argument, 0, 'M'}, {"verbose", no_argument, 0, 'v'}, {"version", no_argument, 0, 'V'}, {0, 0, 0, 0}, }; /* arranged in alphabetical order by acronym */ static const struct svpd_values_name_t standard_vpd_pg[] = { {VPD_AUTOMATION_DEV_SN, 0, 1, "adsn", "Automation device serial " "number (SSC)"}, {VPD_ATA_INFO, 0, -1, "ai", "ATA information (SAT)"}, {VPD_ASCII_OP_DEF, 0, -1, "aod", "ASCII implemented operating definition (obsolete)"}, {VPD_BLOCK_DEV_CHARS, 0, 0, "bdc", "Block device characteristics " "(SBC)"}, {VPD_BLOCK_DEV_C_EXTENS, 0, 0, "bdce", "Block device characteristics " "extension (SBC)"}, {VPD_BLOCK_LIMITS, 0, 0, "bl", "Block limits (SBC)"}, {VPD_BLOCK_LIMITS_EXT, 0, 0, "ble", "Block limits extension (SBC)"}, {VPD_CAP_PROD_ID, 0, 0, "cap", "Capacity/Product identification mapping"}, {VPD_CFA_PROFILE_INFO, 0, 0, "cfa", "CFA profile information"}, {VPD_CON_POS_RANGE, 0, 0, "cpr", "Concurrent positioning ranges"}, {VPD_DEVICE_CONSTITUENTS, 0, -1, "dc", "Device constituents"}, {VPD_DEVICE_ID, 0, -1, "di", "Device identification"}, {VPD_DEVICE_ID, VPD_DI_SEL_AS_IS, -1, "di_asis", "Like 'di' " "but designators ordered as found"}, {VPD_DEVICE_ID, VPD_DI_SEL_LU, -1, "di_lu", "Device identification, " "lu only"}, {VPD_DEVICE_ID, VPD_DI_SEL_TPORT, -1, "di_port", "Device " "identification, target port only"}, {VPD_DEVICE_ID, VPD_DI_SEL_TARGET, -1, "di_target", "Device " "identification, target device only"}, {VPD_DTDE_ADDRESS, 0, 1, "dtde", "Data transfer device element address (SSC)"}, {VPD_EXT_INQ, 0, -1, "ei", "Extended inquiry data"}, {VPD_FORMAT_PRESETS, 0, 0, "fp", "Format presets"}, {VPD_IMP_OP_DEF, 0, -1, "iod", "Implemented operating definition (obsolete)"}, {VPD_LB_PROTECTION, 0, 0, "lbpro", "Logical block protection (SSC)"}, {VPD_LB_PROVISIONING, 0, 0, "lbpv", "Logical block provisioning (SBC)"}, {VPD_MAN_ASS_SN, 0, 1, "mas", "Manufacturer assigned serial number (SSC)"}, {VPD_MAN_ASS_SN, 0, 0x12, "masa", "Manufacturer assigned serial number (ADC)"}, {VPD_MAN_NET_ADDR, 0, -1, "mna", "Management network addresses"}, {VPD_MODE_PG_POLICY, 0, -1, "mpp", "Mode page policy"}, {SG_NVME_VPD_NICR, 0, -1, "nicr", "NVMe Identify Controller Response (sg3_utils for SNT)"}, {VPD_NVME_INFO, 0, -1, "nvmei", "NVMe information (SNT)"}, {VPD_OSD_INFO, 0, 0x11, "oi", "OSD information"}, {VPD_POWER_CONDITION, 0, -1, "pc", "Power condition"},/* "po" in sg_inq */ {VPD_POWER_CONSUMPTION, 0, -1, "psm", "Power consumption"}, {VPD_PROTO_LU, 0, -1, "pslu", "Protocol-specific logical unit " "information"}, {VPD_PROTO_PORT, 0, -1, "pspo", "Protocol-specific port information"}, {VPD_REFERRALS, 0, 0, "ref", "Referrals (SBC)"}, {VPD_SA_DEV_CAP, 0, 1, "sad", "Sequential access device capabilities (SSC)"}, {VPD_SUP_BLOCK_LENS, 0, 0, "sbl", "Supported block lengths and " "protection types (SBC)"}, {VPD_SCSI_FEATURE_SETS, 0, -1, "sfs", "SCSI feature sets"}, {VPD_SOFTW_INF_ID, 0, -1, "sii", "Software interface identification"}, {VPD_NOPE_WANT_STD_INQ, 0, -1, "sinq", "Standard inquiry data format"}, {VPD_UNIT_SERIAL_NUM, 0, -1, "sn", "Unit serial number"}, {VPD_SCSI_PORTS, 0, -1, "sp", "SCSI ports"}, {VPD_SECURITY_TOKEN, 0, 0x11, "st", "Security token (OSD)"}, {VPD_SUPPORTED_VPDS, 0, -1, "sv", "Supported VPD pages"}, {VPD_TA_SUPPORTED, 0, 1, "tas", "TapeAlert supported flags (SSC)"}, {VPD_3PARTY_COPY, 0, -1, "tpc", "Third party copy"}, {VPD_ZBC_DEV_CHARS, 0, -1, "zbdc", "Zoned block device characteristics"}, /* Use pdt of -1 since this page both for pdt=0 and pdt=0x14 */ {0, 0, 0, NULL, NULL}, }; static void usage() { pr2serr( "Usage: sg_vpd [--all] [--descriptors] [--enumerate] " "[--examine]\n" " [--force] [--help] [--hex] [--ident] [--inhex=FN]\n" " [--json[=JO]] [--js-file=JFN] [--long] " "[--maxlen=LEN]\n" " [--page=PG] [--quiet] [--raw] " "[--sinq_inraw=RFN]\n" " [--vendor=VP] [--verbose] [--version] DEVICE\n"); pr2serr( " where:\n" " --all|-a output all pages listed in the supported " "pages VPD\n" " page\n" " --descriptors|-d display standard inquiry version " "descriptors\n" " --enumerate|-e enumerate known VPD pages names (ignore " "DEVICE),\n" " can be used with --page=num to search\n" " --examine|-E starting at 0x80 scan pages code to 0xff\n" " --force|-f skip VPD page 0 (supported VPD pages) " "checking\n" " --help|-h output this usage message then exit\n" " --hex|-H output page in ASCII hexadecimal\n" " --ident|-i output device identification VPD page, " "twice for\n" " short logical unit designator (equiv: " "'-qp di_lu')\n" " --inhex=FN|-I FN read ASCII hex from file FN instead of " "DEVICE;\n" " if used with --raw then read binary " "from FN\n" " --json[=JO]|-j[=JO] output in JSON instead of plain " "text\n" " Use --json=? for JSON help\n" " --js-file=JFN|-J JFN JFN is a filename to which JSON " "output is\n" " written (def: stdout); truncates " "then writes\n" " --long|-l perform extra decoding\n" " --maxlen=LEN|-m LEN max response length (allocation " "length in cdb)\n" " (def: 0 -> 252 bytes)\n" " --page=PG|-p PG fetch VPD page where PG is an " "acronym, or a decimal\n" " number unless hex indicator " "is given (e.g. '0x83');\n" " can also take PG,VP as an " "operand\n" " --quiet|-q suppress some decoding and error output\n" " --raw|-r output page in binary; if --inhex=FN is " "also\n" " given, FN is in binary (else FN is in " "hex)\n" " --sinq_inraw=RFN|-Q RFN read raw (binary) standard " "INQUIRY\n" " response from the RFN filename\n" " --vendor=VP|-M VP vendor/product abbreviation [or " "number]\n" " --verbose|-v increase verbosity\n" " --version|-V print version string and exit\n\n" "Fetch Vital Product Data (VPD) page using SCSI INQUIRY or " "decodes VPD\npage response held in file FN. To list available " "pages use '-e'. Also\n'-p -1' or '-p sinq' yields the standard " "INQUIRY response.\n"); } static const struct svpd_values_name_t * sdp_get_vpd_detail(int page_num, int subvalue, int pdt) { const struct svpd_values_name_t * vnp; int sv, ty; sv = (subvalue < 0) ? 1 : 0; ty = (pdt < 0) ? 1 : 0; for (vnp = standard_vpd_pg; vnp->acron; ++vnp) { if ((page_num == vnp->value) && (sv || (subvalue == vnp->subvalue)) && (ty || (pdt == vnp->pdt))) return vnp; } if (! ty) return sdp_get_vpd_detail(page_num, subvalue, -1); if (! sv) return sdp_get_vpd_detail(page_num, -1, -1); return NULL; } static const struct svpd_values_name_t * sdp_find_vpd_by_acron(const char * ap) { const struct svpd_values_name_t * vnp; for (vnp = standard_vpd_pg; vnp->acron; ++vnp) { if (0 == strcmp(vnp->acron, ap)) return vnp; } return NULL; } static void enumerate_vpds(int standard, int vendor) { const struct svpd_values_name_t * vnp; if (standard) { for (vnp = standard_vpd_pg; vnp->acron; ++vnp) { if (vnp->name) { if (vnp->value < 0) printf(" %-10s -1 %s\n", vnp->acron, vnp->name); else printf(" %-10s 0x%02x %s\n", vnp->acron, vnp->value, vnp->name); } } } if (vendor) svpd_enumerate_vendor(-2); } static int count_standard_vpds(int vpd_pn) { const struct svpd_values_name_t * vnp; int matches = 0; for (vnp = standard_vpd_pg; vnp->acron; ++vnp) { if ((vpd_pn == vnp->value) && vnp->name) { if (0 == matches) printf("Matching standard %ss:\n", vpd_pg_s); ++matches; if (vnp->value < 0) printf(" %-10s -1 %s\n", vnp->acron, vnp->name); else printf(" %-10s 0x%02x %s\n", vnp->acron, vnp->value, vnp->name); } } return matches; } static void dStrRaw(const uint8_t * str, int len) { int k; for (k = 0; k < len; ++k) printf("%c", str[k]); } static const char * find_version_descriptor_str(int value) { #ifdef SG_SCSI_STRINGS int k; const struct sg_lib_simple_value_name_t * vdp; for (k = 0; ((vdp = sg_lib_version_descriptor_arr + k) && vdp->name); ++k) { if (value == vdp->value) return vdp->name; if (value < vdp->value) break; } return NULL; #else if (value) { } /* defeat warning */ return " "; #endif } static void std_inq_decode(uint8_t * b, int len, struct opts_t * op, sgj_opaque_p jop) { bool vd_active = false; uint8_t ver; int pqual, pdt, hp, j, k, n; sgj_state * jsp = &op->json_st; const char * cp; char c[256]; char d[32]; int vdesc_arr[8]; static const int clen = sizeof(c); static const int dlen = sizeof(d); static const char * np = "Standard INQUIRY data format:"; if (len < 4) { pr2serr("%s: len [%d] too short\n", __func__, len); return; } pqual = (b[0] & 0xe0) >> 5; pdt = b[0] & PDT_MASK; hp = (b[1] >> 4) & 0x3; ver = b[2]; sgj_pr_hr(jsp, "%s", np); if (0 == pqual) sgj_pr_hr(jsp, "\n"); else { cp = pqual_str(pqual); if (pqual < 3) sgj_pr_hr(jsp, " [PQ indicates %s]\n", cp); else sgj_pr_hr(jsp, " [PQ indicates %s [0x%x] ]\n", cp, pqual); } sgj_pr_hr(jsp, " PQual=%d PDT=%d RMB=%d LU_CONG=%d hot_pluggable=" "%d version=0x%02x [%s]\n", pqual, pdt, !!(b[1] & 0x80), !!(b[1] & 0x40), hp, ver, sg_get_scsi_ansi_version_str(ver & 0xf, dlen, d)); sgj_pr_hr(jsp, " [AERC=%d] [TrmTsk=%d] NormACA=%d HiSUP=%d " " Resp_data_format=%d\n", !!(b[3] & 0x80), !!(b[3] & 0x40), !!(b[3] & 0x20), !!(b[3] & 0x10), b[3] & 0x0f); if (len < 5) goto skip1; j = b[4] + 5; if (op->verbose > 2) pr2serr(">> requested %d bytes, %d bytes available\n", len, j); sgj_pr_hr(jsp, " SCCS=%d ACC=%d TPGS=%d 3PC=%d Protect=%d " "[BQue=%d]\n", !!(b[5] & 0x80), !!(b[5] & 0x40), ((b[5] & 0x30) >> 4), !!(b[5] & 0x08), !!(b[5] & 0x01), !!(b[6] & 0x80)); n = sg_scnpr(c, clen, "EncServ=%d ", !!(b[6] & 0x40)); if (b[6] & 0x10) n += sg_scn3pr(c, clen, n, "MultiP=1 (VS=%d) ", !!(b[6] & 0x20)); else n += sg_scn3pr(c, clen, n, "MultiP=0 "); sg_scn3pr(c, clen, n, "[MChngr=%d] [ACKREQQ=%d] Addr16=%d", !!(b[6] & 0x08), !!(b[6] & 0x04), !!(b[6] & 0x01)); sgj_pr_hr(jsp, " %s\n", c); sgj_pr_hr(jsp, " [RelAdr=%d] WBus16=%d Sync=%d [Linked=%d] " "[TranDis=%d] CmdQue=%d\n", !!(b[7] & 0x80), !!(b[7] & 0x20), !!(b[7] & 0x10), !!(b[7] & 0x08), !!(b[7] & 0x04), !!(b[7] & 0x02)); if (len < SINQ_COMMON_RESP_LEN) goto skip1; sgj_pr_hr(jsp, " %s: %.8s\n", t10_vendor_id_hr, b + 8); sgj_pr_hr(jsp, " %s: %.16s\n", product_id_hr, b + 16); sgj_pr_hr(jsp, " %s: %.4s\n", product_rev_lev_hr, b + 32); vd_active = op->do_descriptors && (len > 73); if (vd_active) { for (j = 0, k = 58; ((j < 8) && ((k + 1) < len)); k +=2, ++j) vdesc_arr[j] = sg_get_unaligned_be16(b + k); } skip1: if (jsp->pr_as_json && (len > 7)) std_inq_decode_js(b, len, op, jop); if (vd_active) { sgj_opaque_p jap = sgj_named_subarray_r(jsp, jop, "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"); #ifdef SG_SCSI_STRINGS sgj_pr_hr(jsp, " Version descriptors:\n"); #else sgj_pr_hr(jsp, " Version descriptors [--disable-scsistrings " "active]:\n"); #endif for (k = 0; k < 8; ++k) { sgj_opaque_p jo2p = sgj_new_unattached_object_r(jsp); int vdv = vdesc_arr[k]; if (0 == vdv) break; cp = find_version_descriptor_str(vdv); if (cp) { if (' ' == *cp) sgj_pr_hr(jsp, " code: 0x%x\n", vdv); else sgj_pr_hr(jsp, " %s\n", cp); } else sgj_pr_hr(jsp, " [unrecognised version descriptor " "code: 0x%x]\n", vdv); sgj_js_nv_ihexstr(jsp, jo2p, "version_descriptor", vdv, NULL, cp ? cp : "unknown"); sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); } } } } /* VPD_DEVICE_ID 0x83 ["di, di_asis, di_lu, di_port, di_target"] */ static void device_id_vpd_variants(uint8_t * buff, int len, int subvalue, struct opts_t * op, sgj_opaque_p jap) { int m_a, blen; uint8_t * b; if (len < 4) { pr2serr("%s %s %s=%d\n", dev_id_s, vpd_pg_s, lts_s, len); return; } blen = len - 4; b = buff + 4; m_a = -1; /* association mask; -1 => any/all */ if (0 == subvalue) { svpd_desig_descs(sg_get_desig_assoc_str(VPD_ASSOC_LU), 0, b, blen, false, VPD_ASSOC_LU, op, jap); svpd_desig_descs(sg_get_desig_assoc_str(VPD_ASSOC_TPORT), 0, b, blen, false, VPD_ASSOC_TPORT, op, jap); svpd_desig_descs(sg_get_desig_assoc_str(VPD_ASSOC_TDEVICE), 0, b, blen, false, VPD_ASSOC_TDEVICE, op, jap); } else if (VPD_DI_SEL_AS_IS == subvalue) svpd_desig_descs(NULL, 0, b, blen, true, m_a, op, jap); else { if (VPD_DI_SEL_LU & subvalue) svpd_desig_descs(sg_get_desig_assoc_str(VPD_ASSOC_LU), 0, b, blen, false, VPD_ASSOC_LU, op, jap); if (VPD_DI_SEL_TPORT & subvalue) svpd_desig_descs(sg_get_desig_assoc_str(VPD_ASSOC_TPORT), 0, b, blen, false, VPD_ASSOC_TPORT, op, jap); if (VPD_DI_SEL_TARGET & subvalue) svpd_desig_descs(sg_get_desig_assoc_str(VPD_ASSOC_TDEVICE), 0, b, blen, false, VPD_ASSOC_TDEVICE, op, jap); } } static void /* VPD_SUPPORTED_VPDS ["sv"] */ decode_supported_vpd_4vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jap) { uint8_t pn; int k, rlen, pdt; sgj_state * jsp = &op->json_st; sgj_opaque_p jo2p; const struct svpd_values_name_t * vnp; uint8_t * bp; char b[144]; static const int blen = sizeof(b); if (op->do_hex > 0) { if (op->do_hex > 2) named_hhh_output(svp_vpdp, buff, len, op); else hex2stdout(buff, len, no_ascii_4hex(op)); return; } pdt = PDT_MASK & buff[0]; rlen = buff[3] + 4; if (rlen > len) pr2serr("%s truncated, indicates %d, got %d\n", svp_vpdp, rlen, len); else len = rlen; if (len < 4) { pr2serr("%s %s=%d\n", svp_vpdp, lts_s, len); return; } len -= 4; bp = buff + 4; for (k = 0; k < len; ++k) { pn = bp[k]; snprintf(b, blen, "0x%02x", pn); vnp = sdp_get_vpd_detail(pn, -1, pdt); if (vnp) { if (op->do_long) sgj_pr_hr(jsp, " %s %s [%s]\n", b, vnp->name, vnp->acron); else sgj_pr_hr(jsp, " %s [%s]\n", vnp->name, vnp->acron); } else if (op->vend_prod_num >= 0) { vnp = svpd_find_vendor_by_num(pn, op->vend_prod_num); if (vnp) { if (op->do_long) sgj_pr_hr(jsp, " %s %s [%s]\n", b, vnp->name, vnp->acron); else sgj_pr_hr(jsp, " %s [%s]\n", vnp->name, vnp->acron); } else sgj_pr_hr(jsp, " %s\n", b); } else sgj_pr_hr(jsp, " %s\n", b); if (jsp->pr_as_json) { jo2p = sgj_new_unattached_object_r(jsp); sgj_js_nv_i(jsp, jo2p, "i", pn); sgj_js_nv_s(jsp, jo2p, "hex", b + 2); if (vnp) { sgj_js_nv_s(jsp, jo2p, "name", vnp->name); sgj_js_nv_s(jsp, jo2p, "acronym", vnp->acron); } else { sgj_js_nv_s(jsp, jo2p, "name", "unknown"); sgj_js_nv_s(jsp, jo2p, "acronym", "unknown"); } sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); } } } /* VPD_SCSI_PORTS 0x88 ["sp"] */ static void decode_scsi_ports_vpd_4vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jap) { int k, bump, rel_port, ip_tid_len, tpd_len, dhex; sgj_state * jsp = &op->json_st; sgj_opaque_p jo2p = NULL; sgj_opaque_p ja2p = NULL; uint8_t * bp; dhex = op->do_hex; if (dhex > 0) { if (dhex > 2) named_hhh_output(sp_vpdp, buff, len, op); else hex2stdout(buff, len, no_ascii_4hex(op)); return; } else if (dhex < 0) dhex = -dhex; if (len < 4) { pr2serr("%s %s=%d\n", sp_vpdp, lts_s, len); return; } len -= 4; bp = buff + 4; for (k = 0; k < len; k += bump, bp += bump) { rel_port = sg_get_unaligned_be16(bp + 2); sgj_pr_hr(jsp, " Relative port=%d\n", rel_port); jo2p = sgj_new_unattached_object_r(jsp); sgj_js_nv_i(jsp, jo2p, "relative_port", rel_port); ip_tid_len = sg_get_unaligned_be16(bp + 6); bump = 8 + ip_tid_len; if ((k + bump) > len) { pr2serr("%s, short descriptor length=%d, left=%d\n", sp_vpdp, bump, (len - k)); return; } if (ip_tid_len > 0) { if (dhex > 1) { sgj_pr_hr(jsp, " Initiator port transport id:\n"); hex2stdout((bp + 8), ip_tid_len, 1); } else { char b[1024]; sg_decode_transportid_str(" ", bp + 8, ip_tid_len, true, sizeof(b), b); if (jsp->pr_as_json) sgj_js_nv_s(jsp, jo2p, "initiator_port_transport_id", b); sgj_pr_hr(jsp, "%s", sg_decode_transportid_str(" ", bp + 8, ip_tid_len, true, sizeof(b), b)); } } tpd_len = sg_get_unaligned_be16(bp + bump + 2); if ((k + bump + tpd_len + 4) > len) { pr2serr("%s, short descriptor(tgt) length=%d, left=%d\n", sp_vpdp, bump, (len - k)); return; } if (tpd_len > 0) { if (dhex > 1) { sgj_pr_hr(jsp, " Target port descriptor(s):\n"); hex2stdout(bp + bump + 4, tpd_len, 1); } else { if ((0 == op->do_quiet) || (ip_tid_len > 0)) sgj_pr_hr(jsp, " Target port descriptor(s):\n"); if (jsp->pr_as_json) { sgj_opaque_p jo3p = sgj_named_subobject_r(jsp, jo2p, "target_port"); ja2p = sgj_named_subarray_r(jsp, jo3p, "designation_descriptor_list"); } svpd_desig_descs(NULL, 2 /* leading spaces */, bp + bump + 4, tpd_len, true, VPD_ASSOC_TPORT, op, ja2p); } } bump += tpd_len + 4; sgj_js_nv_o(jsp, jap, NULL, jo2p); } } /* Prints outs an abridged set of device identification designators selected by association, designator type and/or code set. Not used for JSON output. */ static int svpd_quiet_desig_descs(const char * print_if_found, int leadin_sp, const uint8_t * buff, int blen, int m_assoc) { bool found = false; int k, m, p_id, c_set, piv, desig_type, i_len, naa, off, u; int assoc, is_sas, rtp; const uint8_t * bp; const uint8_t * ip; uint8_t sas_tport_addr[8]; rtp = 0; memset(sas_tport_addr, 0, sizeof(sas_tport_addr)); for (k = 0, off = -1; true; ++k) { if ((0 == k) && (0 != buff[2])) { /* first already in buff */ if (m_assoc != VPD_ASSOC_LU) return 0; if (print_if_found && (! found)) { found = true; printf("%*s %s:\n", leadin_sp, "", print_if_found); } ip = buff; c_set = 1; assoc = VPD_ASSOC_LU; is_sas = 0; desig_type = 3; i_len = 16; } else { u = sg_vpd_dev_id_iter(buff, blen, &off, m_assoc, -1, -1); if (0 != u) break; if (print_if_found && (! found)) { found = true; printf("%*s %s:\n", leadin_sp, "", print_if_found); } bp = buff + off; i_len = bp[3]; if ((off + i_len + 4) > blen) { pr2serr(" %s error: designator length longer than\n" " remaining response length=%d\n", vpd_pg_s, (blen - off)); return SG_LIB_CAT_MALFORMED; } ip = bp + 4; p_id = ((bp[0] >> 4) & 0xf); c_set = (bp[0] & 0xf); piv = ((bp[1] & 0x80) ? 1 : 0); is_sas = (piv && (6 == p_id)) ? 1 : 0; assoc = ((bp[1] >> 4) & 0x3); desig_type = (bp[1] & 0xf); } switch (desig_type) { case 0: /* vendor specific */ break; /* ignore */ case 1: /* T10 vendor identification */ break; case 2: /* EUI-64 based */ if ((8 != i_len) && (12 != i_len) && (16 != i_len)) pr2serr(" << expect 8, 12 and 16 byte " "EUI, got %d>>\n", i_len); printf(" 0x"); for (m = 0; m < i_len; ++m) printf("%02x", (unsigned int)ip[m]); printf("\n"); break; case 3: /* NAA */ naa = (ip[0] >> 4) & 0xff; if (1 != c_set) { pr2serr(" << expected binary code_set (1), got %d for " "NAA=%d>>\n", c_set, naa); hex2stderr(ip, i_len, 0); break; } switch (naa) { case 2: /* NAA IEEE extended */ if (8 != i_len) { pr2serr(" << unexpected NAA 2 identifier " "length: 0x%x>>\n", i_len); hex2stderr(ip, i_len, 0); break; } printf(" 0x"); for (m = 0; m < 8; ++m) printf("%02x", (unsigned int)ip[m]); printf("\n"); break; case 3: /* Locally assigned */ case 5: /* IEEE Registered */ if (8 != i_len) { pr2serr(" << unexpected NAA 3 or 5 " "identifier length: 0x%x>>\n", i_len); hex2stderr(ip, i_len, 0); break; } if ((0 == is_sas) || (1 != assoc)) { printf(" 0x"); for (m = 0; m < 8; ++m) printf("%02x", (unsigned int)ip[m]); printf("\n"); } else if (rtp) { printf(" 0x"); for (m = 0; m < 8; ++m) printf("%02x", (unsigned int)ip[m]); printf(",0x%x\n", rtp); rtp = 0; } else { if (sas_tport_addr[0]) { printf(" 0x"); for (m = 0; m < 8; ++m) printf("%02x", (unsigned int)sas_tport_addr[m]); printf("\n"); } memcpy(sas_tport_addr, ip, sizeof(sas_tport_addr)); } break; case 6: /* NAA IEEE registered extended */ if (16 != i_len) { pr2serr(" << unexpected NAA 6 identifier length: " "0x%x>>\n", i_len); hex2stderr(ip, i_len, 0); break; } printf(" 0x"); for (m = 0; m < 16; ++m) printf("%02x", (unsigned int)ip[m]); printf("\n"); break; default: pr2serr(" << bad NAA nibble, expected 2, 3, 5 or 6, got " "%d>>\n", naa); hex2stderr(ip, i_len, 0); break; } break; case 4: /* Relative target port */ if ((0 == is_sas) || (1 != c_set) || (1 != assoc) || (4 != i_len)) break; rtp = sg_get_unaligned_be16(ip + 2); if (sas_tport_addr[0]) { printf(" 0x"); for (m = 0; m < 8; ++m) printf("%02x", (unsigned int)sas_tport_addr[m]); printf(",0x%x\n", rtp); memset(sas_tport_addr, 0, sizeof(sas_tport_addr)); rtp = 0; } break; case 5: /* (primary) Target port group */ break; case 6: /* Logical unit group */ break; case 7: /* MD5 logical unit identifier */ break; case 8: /* SCSI name string */ if (c_set < 2) { /* quietly accept ASCII for UTF-8 */ pr2serr(" << expected UTF-8 code_set>>\n"); hex2stderr(ip, i_len, 0); break; } if (! (strncmp((const char *)ip, "eui.", 4) || strncmp((const char *)ip, "EUI.", 4) || strncmp((const char *)ip, "naa.", 4) || strncmp((const char *)ip, "NAA.", 4) || strncmp((const char *)ip, "iqn.", 4))) { pr2serr(" << expected name string prefix>>\n"); hex2stderr(ip, i_len, -1); break; } /* does %s print out UTF-8 ok?? * Seems to depend on the locale. Looks ok here with my * locale setting: en_AU.UTF-8 */ printf(" %.*s\n", i_len, (const char *)ip); break; case 9: /* Protocol specific port identifier */ break; case 0xa: /* UUID identifier [spc5r08] RFC 4122 */ if ((1 != c_set) || (18 != i_len) || (1 != ((ip[0] >> 4) & 0xf))) break; for (m = 0; m < 16; ++m) { if ((4 == m) || (6 == m) || (8 == m) || (10 == m)) printf("-"); printf("%02x", (unsigned int)ip[2 + m]); } printf("\n"); break; default: /* reserved */ break; } } if (sas_tport_addr[0]) { printf(" 0x"); for (m = 0; m < 8; ++m) printf("%02x", (unsigned int)sas_tport_addr[m]); printf("\n"); } if (-2 == u) { pr2serr("%s error: short designator around offset %d\n", vpd_pg_s, off); return SG_LIB_CAT_MALFORMED; } return 0; } /* Prints outs designation descriptors (dd_s) selected by association, designator type and/or code set. For VPD_DEVICE_ID and VPD_SCSI_PORTS */ static int svpd_desig_descs(const char * print_if_found, int leadin_sp, const uint8_t * buff, int blen, bool pr_assoc, int m_assoc, struct opts_t * op, sgj_opaque_p jap) { sgj_state * jsp = &op->json_st; if (op->do_quiet && (! jsp->pr_as_json)) return svpd_quiet_desig_descs(print_if_found, leadin_sp, buff, blen, m_assoc); return filter_process_desig_descs(print_if_found, leadin_sp, buff, blen, pr_assoc, m_assoc, op, jap); } /* VPD_BLOCK_LIMITS sbc */ /* VPD_SA_DEV_CAP ssc */ /* VPD_OSD_INFO osd */ static void decode_b0_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jop) { int pdt = PDT_MASK & buff[0]; int dhex = op->do_hex; const char * vpd_pp = NULL; sgj_state * jsp = &op->json_st; if (dhex < 0) dhex = -dhex; if (dhex < 3) { hex2stdout(buff, len, no_ascii_4hex(op)); return; } switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: /* now done by decode_block_limits_vpd() in sg_vpd_common.c */ break; case PDT_TAPE: case PDT_MCHANGER: if (dhex > 2) { vpd_pp = sad_vpdp; break; } sgj_haj_vi_nex(jsp, jop, 2, "TSMC", SGJ_SEP_EQUAL_NO_SPACE, !!(buff[4] & 0x2), false, "Tape Stream Mirror " "Capable"); sgj_haj_vi_nex(jsp, jop, 2, "WORM", SGJ_SEP_EQUAL_NO_SPACE, !!(buff[4] & 0x1), false, "Write Once Read Multiple " "supported"); break; case PDT_OSD: if (dhex > 2) { vpd_pp = osdi_vpdp; break; } /* fall-through */ default: pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt); hex2stderr(buff, len, no_ascii_4hex(op)); break; } if (vpd_pp) named_hhh_output(vpd_pp, buff, len, op); } /* VPD_BLOCK_DEV_CHARS sbc 0xb1 ["bdc"] */ /* VPD_MAN_ASS_SN ssc */ /* VPD_SECURITY_TOKEN osd */ /* VPD_ES_DEV_CHARS ses-4 */ static void decode_b1_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jop) { int pdt; sgj_state * jsp = &op->json_st; pdt = buff[0] & PDT_MASK; if (op->do_hex > 0) { hex2stdout(buff, len, no_ascii_4hex(op)); return; } switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: /* now done by decode_block_dev_ch_vpd() in sg_vpd_common.c */ case PDT_TAPE: case PDT_MCHANGER: case PDT_ADC: sgj_pr_hr(jsp, " Manufacturer-assigned serial number: %.*s\n", len - 4, buff + 4); sgj_js_nv_s_len(jsp, jop, "manufacturer_assigned_serial_number", (const char *)buff + 4, len - 4); break; default: pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt); hex2stderr(buff, len, 0); break; } } /* VPD_LB_PROVISIONING sbc */ /* VPD_TA_SUPPORTED ssc */ static void decode_b2_vpd(uint8_t * buff, int len, int pdt, struct opts_t * op) { if (op->do_hex > 0) { hex2stdout(buff, len, no_ascii_4hex(op)); return; } switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: /* decode_block_lb_prov_vpd() is now in sg_vpd_common.c */ break; case PDT_TAPE: case PDT_MCHANGER: /* decode_tapealert_supported_vpd() is now in sg_vpd_common.c */ break; default: pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt); hex2stderr(buff, len, 0); break; } } /* VPD_REFERRALS sbc 0xb3 ["ref"] */ /* VPD_AUTOMATION_DEV_SN ssc 0xb3 ["adsn"] */ static void decode_b3_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jop) { int pdt; sgj_state * jsp = &op->json_st; if (op->do_hex > 0) { hex2stdout(buff, len, no_ascii_4hex(op)); return; } pdt = buff[0] & PDT_MASK; switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: /* now done in decode_referrals_vpd() in sg_vpd_common.c */ break; case PDT_TAPE: case PDT_MCHANGER: sgj_pr_hr(jsp, " Automation device serial number: %.*s\n", len - 4, buff + 4); sgj_js_nv_s_len(jsp, jop, "automation_device_serial_number", (const char *)buff + 4, len - 4); break; default: pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt); hex2stderr(buff, len, 0); break; } } /* VPD_SUP_BLOCK_LENS sbc ["sbl"] */ /* VPD_DTDE_ADDRESS ssc */ static void decode_b4_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jop) { int pdt = buff[0] & PDT_MASK; sgj_state * jsp = &op->json_st; if (op->do_hex > 0) { hex2stdout(buff, len, no_ascii_4hex(op)); return; } switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: /* now done by decode_sup_block_lens_vpd() in sg_vpd_common.c */ break; case PDT_TAPE: case PDT_MCHANGER: sgj_pr_hr(jsp, " Device transfer data element:\n"); if (! jsp->pr_as_json) hex2stdout(buff + 4, len - 4, 1); sgj_js_nv_hex_bytes(jsp, jop, "device_transfer_data_element", buff + 4, len - 4); break; default: pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt); hex2stderr(buff, len, 0); break; } } /* VPD_BLOCK_DEV_C_EXTENS sbc */ /* VPD_LB_PROTECTION 0xb5 ["lbpro"] ssc */ static void decode_b5_vpd(uint8_t * b, int len, int pdt, struct opts_t * op) { if (op->do_hex > 0) { hex2stdout(b, len, no_ascii_4hex(op)); return; } switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: /* now done by decode_block_dev_char_ext_vpd() in sg_vpd_common.c */ break; case PDT_TAPE: case PDT_MCHANGER: /* now done by decode_lb_protection_vpd() in sg_vpd_common.c */ break; default: pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt); hex2stderr(b, len, 0); break; } } /* Returns 0 if successful */ static int svpd_unable_to_decode(struct sg_pt_base * ptvp, struct opts_t * op, sgj_opaque_p jop, int subvalue, int off) { bool as_json, json_o_hr, hex0; int res, len, n, pg_c, dhex; uint8_t * rp; sgj_state * jsp = &op->json_st; sgj_opaque_p jo2p = NULL; char b[128]; static const int blen = sizeof(b); as_json = jsp->pr_as_json; json_o_hr = as_json && jsp->pr_out_hr; dhex = op->do_hex; if (dhex < 0) dhex = -dhex; hex0 = (0 == dhex); b[0] = '\0'; pg_c = op->vpd_pn; rp = rsp_buff + off; if (hex0 && (! op->do_raw) && (! op->examine_given)) sgj_pr_hr(jsp, "Only hex output supported\n"); if (subvalue) snprintf(b, blen, "%s code=0x%.2x, subvalue=0x%.2x", vpd_pg_s, pg_c, subvalue); else if (op->vpd_pn >= 0) snprintf(b, blen, "%s code=0x%.2x", vpd_pg_s, pg_c); else snprintf(b, blen, "%s code=%d", vpd_pg_s, pg_c); if ((!op->do_raw) && (dhex < 2) && (! op->examine_given)) sgj_pr_hr(jsp, "%s:\n", b); res = vpd_fetch_page(ptvp, rp, pg_c, op->maxlen, op->do_quiet, op->verbose, &len); if (0 == res) { int rlen; char * p; if (op->do_raw) { dStrRaw(rp, len); return 0; } else if (dhex > 0) { if (dhex > 2) named_hhh_output(b, rp, len, op); else hex2stdout(rp, len, no_ascii_4hex(op)); return 0; } if (len < 4) { pr2serr("%s: response too short (< 4 bytes)\n", __func__); return SG_LIB_CAT_MALFORMED; } rlen = sg_get_unaligned_be16(rp + 2) + 4; if (len > rlen) len = rlen; n = len * 4; p = (char *)malloc(n); if (p) { hex2str(rp, len, NULL, 1, n - 1, p); if (json_o_hr) sgj_hr_str_out(jsp, p, strlen(p)); else sgj_pr_hr(jsp, "%s\n", p); free(p); } if (as_json) { const char * ccp; snprintf(b, blen, "vpd_page_%02x", pg_c); jo2p = sg_vpd_js_hdr(jsp, jop, b, rp); snprintf(b, blen, "%d bytes long when 4 byte header included", rlen); sgj_js_nv_ihexstr(jsp, jo2p, "page_length", rlen - 4, NULL, b); if (pg_c <= 0x80) ccp = "unimplemented"; else if (pg_c <= 0x82) ccp = "obsolete"; else if (pg_c <= 0x8f) ccp = "unimplemented"; else if (pg_c <= 0xbf) ccp = "restricted"; else ccp = "vendor_specific"; sgj_js_nv_s(jsp, jo2p, "vpd_category", ccp); sgjv_js_hex_long(jsp, jo2p, rp, len); } } else if ((! op->do_quiet) && (! op->examine_given)) { if (pg_c >= 0) pr2serr("fetching %s code=0x%.2x: failed\n", vpd_pg_s, pg_c); else pr2serr("fetching %s code=%d: failed\n", vpd_pg_s, pg_c); } return res; } static int recurse_vpd_decode(struct opts_t * op, sgj_opaque_p jop, int off) { int res = svpd_decode_t10(NULL, op, jop, 0, off, NULL); if (SG_LIB_CAT_OTHER == res) { res = svpd_decode_vendor(NULL, op, jop, off); if (SG_LIB_CAT_OTHER == res) svpd_unable_to_decode(NULL, op, jop, 0, off); } return res; } /* Returns 0 if successful. If don't know how to decode, returns * SG_LIB_CAT_OTHER else see sg_ll_inquiry(). */ static int svpd_decode_t10(struct sg_pt_base * ptvp, struct opts_t * op, sgj_opaque_p jop, int subvalue, int off, const char * prefix) { bool allow_name, allow_if_found, long_notquiet, qt; bool vpd_supported = false; bool inhex_active = (NULL == ptvp); bool exam_not_given = ! op->examine_given; int len, pdt, pqual, num, k, resid, alloc_len, pn, dhex, vb; int res = 0; sgj_state * jsp = &op->json_st; uint8_t * rp; sgj_opaque_p jap = NULL; sgj_opaque_p jo2p = NULL; const char * np; const char * ep; const char * pre = (prefix ? prefix : ""); const char * pdt_str; bool as_json = jsp->pr_as_json; bool not_json = ! as_json; char obuff[DEF_ALLOC_LEN]; char d[48]; vb = op->verbose; qt = op->do_quiet; dhex = op->do_hex; if (dhex < 0) dhex = -dhex; long_notquiet = op->do_long && (! op->do_quiet); if (op->do_raw || (op->do_quiet && (! op->do_long) && (! op->do_all)) || (dhex >= 3) || op->examine_given) allow_name = false; else allow_name = true; allow_if_found = op->examine_given && (! op->do_quiet); rp = rsp_buff + off; if ((off > 0) && (VPD_NOPE_WANT_STD_INQ != op->vpd_pn)) pn = rp[1]; else pn = op->vpd_pn; if (!inhex_active && !op->do_force && exam_not_given && pn != VPD_NOPE_WANT_STD_INQ && pn != VPD_SUPPORTED_VPDS) { res = vpd_fetch_page(ptvp, rp, VPD_SUPPORTED_VPDS, op->maxlen, qt, vb, &len); if (res) return res; num = rp[3]; if (num > (len - 4)) num = (len - 4); if (vb > 1) { pr2serr("Supported %ss, hex list: ", vpd_pg_s); hex2stderr(rp + 4, num, -1); } for (k = 0; k < num; ++k) { if (pn == rp[4 + k]) { vpd_supported = true; break; } } if (! vpd_supported) { /* get creative, was SG_LIB_CAT_ILLEGAL_REQ */ if (vb) pr2serr("Given %s not in supported list, use --force to " "override this check\n", vpd_pg_s); return sg_convert_errno(EDOM); } } pdt = rp[0] & PDT_MASK; pdt_str = sg_get_pdt_str(pdt, sizeof(d), d); pqual = (rp[0] & 0xe0) >> 5; switch(pn) { case VPD_NOPE_WANT_STD_INQ: /* -2 (want standard inquiry response) */ np = "Standard Inquiry data format"; if (!inhex_active) { if (op->maxlen > 0) alloc_len = op->maxlen; else if (op->do_long) alloc_len = DEF_ALLOC_LEN; else if (op->do_descriptors) alloc_len = 74; else alloc_len = SINQ_COMMON_RESP_LEN; res = sg_ll_inquiry_pt(ptvp, false, 0, rp, alloc_len, DEF_PT_TIMEOUT, &resid, ! op->do_quiet, vb); } else { alloc_len = op->maxlen; resid = 0; res = 0; } if (0 == res) { alloc_len -= resid; if (op->do_raw) dStrRaw(rp, alloc_len); else if (dhex > 0) { if (! op->do_quiet && (dhex < 3)) sgj_pr_hr(jsp, "%s:\n", np); if (dhex > 2) named_hhh_output(np, rp, alloc_len, op); else hex2stdout(rp, alloc_len, (1 == dhex) ? 0 : -1); } else std_inq_decode(rp, alloc_len, op, jop); return 0; } break; case VPD_SUPPORTED_VPDS: /* 0x0 ["sv"] */ np = svp_vpdp; if (allow_name) sgj_pr_hr(jsp, "%s%s:\n", pre, np); res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { if (! allow_name && allow_if_found) sgj_pr_hr(jsp, "%s%s:\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else if (dhex) { if (dhex > 2) named_hhh_output(np, rp, len, op); else hex2stdout(rp, len, no_ascii_4hex(op)); } else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) { jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); jap = sgj_named_subarray_r(jsp, jo2p, "supported_vpd_page_list"); } decode_supported_vpd_4vpd(rp, len, op, jap); } return 0; } break; case VPD_UNIT_SERIAL_NUM: /* 0x80 ["sn"] */ np = usn_vpdp; if (allow_name && not_json) sgj_pr_hr(jsp, "%s%s:\n", pre, np); res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { if (! allow_name && allow_if_found) sgj_pr_hr(jsp, "%s%s:\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else if (dhex > 0) { if (dhex > 2) named_hhh_output(np, rp, len, op); else hex2stdout(rp, len, no_ascii_4hex(op)); } else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); memset(obuff, 0, sizeof(obuff)); len -= 4; if (len >= (int)sizeof(obuff)) len = sizeof(obuff) - 1; memcpy(obuff, rp + 4, len); jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); sgj_haj_vs(jsp, jo2p, 2, "Product serial number", SGJ_SEP_COLON_1_SPACE, obuff); } return 0; } break; case VPD_DEVICE_ID: /* 0x83 ["di, di_asis, di_lu, di_port, di_target"] */ np = "Device Identification VPD page"; if (allow_name) sgj_pr_hr(jsp, "%s%s:\n", pre, np); res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { if (! allow_name && allow_if_found) sgj_pr_hr(jsp, "%s%s:\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else if (dhex > 0) { if (dhex > 2) named_hhh_output(np, rp, len, op); else hex2stdout(rp, len, no_ascii_4hex(op)); } else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) { jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); jap = sgj_named_subarray_r(jsp, jo2p, "designation_descriptor_list"); } device_id_vpd_variants(rp, len, subvalue, op, jap); } return 0; } break; case VPD_SOFTW_INF_ID: /* 0x84 ["sii"] */ np = "Software interface identification VPD page"; if (allow_name) sgj_pr_hr(jsp, "%s%s:\n", pre, np); res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { if (! allow_name && allow_if_found) sgj_pr_hr(jsp, "%s%s:\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else if (dhex > 0) { if (dhex > 2) named_hhh_output(np, rp, len, op); else hex2stdout(rp, len, no_ascii_4hex(op)); } else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) { jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); jap = sgj_named_subarray_r(jsp, jo2p, "software_interface_identifier_list"); } decode_softw_inf_id(rp, len, op, jap); } return 0; } break; case VPD_MAN_NET_ADDR: /* 0x85 ["mna"] */ np= mna_vpdp; if (allow_name) sgj_pr_hr(jsp, "%s%s:\n", pre, np); res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { if (! allow_name && allow_if_found) sgj_pr_hr(jsp, "%s%s:\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else if (dhex > 0) { if (dhex > 2) named_hhh_output(np, rp, len, op); else hex2stdout(rp, len, no_ascii_4hex(op)); } else { if (as_json) { jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); jap = sgj_named_subarray_r(jsp, jo2p, "network_services_descriptor_list"); } decode_man_net_vpd(rp, len, op, jap); } return 0; } break; case VPD_EXT_INQ: /* 0x86 ["ei"] */ np = eid_vpdp; if (allow_name) sgj_pr_hr(jsp, "%s%s:\n", pre, np); res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { if (! allow_name && allow_if_found) sgj_pr_hr(jsp, "%s%s:\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else if (dhex > 0) { if (dhex > 2) named_hhh_output(np, rp, len, op); else hex2stdout(rp, len, no_ascii_4hex(op)); } else { bool protect = false; op->protect_not_sure = false; if (op->std_inq_a_valid) protect = !! (0x1 & op->std_inq_a[5]); else if (ptvp && (! op->do_force)) { struct sg_simple_inquiry_resp sir; res = sg_simple_inquiry(get_pt_file_handle(ptvp), &sir, false, vb); if (res) { if (op->verbose) pr2serr("%s: sg_simple_inquiry() failed, " "res=%d\n", __func__, res); op->protect_not_sure = true; } else protect = !!(sir.byte_5 & 0x1); /* SPC-3 and later */ } else op->protect_not_sure = true; if (vb || long_notquiet) sgj_pr_hr(jsp," [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); decode_x_inq_vpd(rp, len, protect, op, jo2p); } return 0; } break; case VPD_MODE_PG_POLICY: /* 0x87 */ np = mpp_vpdp; if (allow_name) sgj_pr_hr(jsp, "%s%s:\n", pre, np); res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { if (! allow_name && allow_if_found) sgj_pr_hr(jsp, "%s%s:\n", (prefix ? prefix : ""), np); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) { jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); jap = sgj_named_subarray_r(jsp, jo2p, "mode_page_policy_descriptor_list"); } decode_mode_policy_vpd(rp, len, op, jap); } return 0; } break; case VPD_SCSI_PORTS: /* 0x88 ["sp"] */ np = sp_vpdp; if (allow_name) sgj_pr_hr(jsp, "%s%s:\n", pre, np); res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { if (! allow_name && allow_if_found) sgj_pr_hr(jsp, "%s%s:\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) { jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); jap = sgj_named_subarray_r(jsp, jo2p, "scsi_ports_descriptor_list"); } decode_scsi_ports_vpd_4vpd(rp, len, op, jap); } return 0; } break; case VPD_ATA_INFO: /* 0x89 ['ai"] */ np = ai_vpdp; if (allow_name) sgj_pr_hr(jsp, "%s%s:\n", pre, np); alloc_len = op->maxlen ? op->maxlen : VPD_ATA_INFO_LEN; res = vpd_fetch_page(ptvp, rp, pn, alloc_len, qt, vb, &len); if (0 == res) { if (! allow_name && allow_if_found) sgj_pr_hr(jsp, "%s%s:\n", (prefix ? prefix : ""), np); if ((2 == op->do_raw) || (3 == dhex)) { /* for hdparm */ if (len < (60 + 512)) pr2serr("%s len (%d) less than expected 572\n", np, len); else dWordHex((const unsigned short *)(rp + 60), 256, -2, sg_is_big_endian()); } else if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); decode_ata_info_vpd(rp, len, op, jo2p); } return 0; } break; case VPD_POWER_CONDITION: /* 0x8a ["pc"] */ np = pc_vpdp; if (allow_name) sgj_pr_hr(jsp, "%s%s\n", pre, np); res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { if (! allow_name && allow_if_found) sgj_pr_hr(jsp, "%s%s\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); decode_power_condition(rp, len, op, jo2p); } return 0; } break; case VPD_DEVICE_CONSTITUENTS: /* 0x8b ["dc"] */ np = dc_vpdp; if (allow_name) sgj_pr_hr(jsp, "%s%s:\n", pre, np); res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { if (! allow_name && allow_if_found) sgj_pr_hr(jsp, "%s%s:\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) { jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); jap = sgj_named_subarray_r(jsp, jo2p, "constituent_descriptor_list"); } decode_dev_constit_vpd(rp, len, op, jap, recurse_vpd_decode); } return 0; } break; case VPD_CFA_PROFILE_INFO: /* 0x8c ["cfa"] */ np = cpi_vpdp; if (allow_name) sgj_pr_hr(jsp, "%s%s:\n", pre, np); res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { if (! allow_name && allow_if_found) sgj_pr_hr(jsp, "%s%s\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) { jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); jap = sgj_named_subarray_r(jsp, jo2p, "cfa_profile_descriptor_list"); } decode_cga_profile_vpd(rp, len, op, jap); } return 0; } break; case VPD_POWER_CONSUMPTION: /* 0x8d ["psm"] */ np = psm_vpdp; if (allow_name) sgj_pr_hr(jsp, "%s%s:\n", pre, np); res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { if (! allow_name && allow_if_found) sgj_pr_hr(jsp, "%s%s\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) { jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); jap = sgj_named_subarray_r(jsp, jo2p, "power_consumption_descriptor_list"); } decode_power_consumption(rp, len, op, jap); } return 0; } break; case VPD_NVME_INFO: /* 0x8e ["nvmei"] */ np = psm_vpdp; if (allow_name) sgj_pr_hr(jsp, "%s%s\n", pre, np); res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { if (! allow_name && allow_if_found) sgj_pr_hr(jsp, "%s%s\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); decode_nvme_info_vpd(rp, len, op, jo2p); } return 0; } break; case VPD_3PARTY_COPY: /* 0x8f */ np = tpc_vpdp; /* ["tpc"] */ if (allow_name) sgj_pr_hr(jsp, "%s%s:\n", pre, np); res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { if (! allow_name && allow_if_found) sgj_pr_hr(jsp, "%s%s\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) { jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); jap = sgj_named_subarray_r(jsp, jo2p, "third_party_copy_descriptors"); } decode_3party_copy_vpd(rp, len, op, jap); } return 0; } break; case VPD_PROTO_LU: /* 0x90 ["pslu"] */ np = pslu_vpdp; if (allow_name) sgj_pr_hr(jsp, "%s%s:\n", pre, np); res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { if (! allow_name && allow_if_found) sgj_pr_hr(jsp, "%s%s:\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) { jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); jap = sgj_named_subarray_r(jsp, jo2p, "logical_unit_information_descriptor_list"); } decode_proto_lu_vpd(rp, len, op, jap); } return 0; } break; case VPD_PROTO_PORT: /* 0x91 ["pspo"] */ np = pspo_vpdp; if (allow_name) sgj_pr_hr(jsp, "%s%s\n", pre, np); res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { if (! allow_name && allow_if_found) sgj_pr_hr(jsp, "%s%s:\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) { jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); jap = sgj_named_subarray_r(jsp, jo2p, "port_information_descriptor_list"); } decode_proto_port_vpd(rp, len, op, jap); } return 0; } break; case VPD_SCSI_FEATURE_SETS: /* 0x92 ["sfs"] */ np = sfs_vpdp; if (allow_name) sgj_pr_hr(jsp, "%s%s:\n", pre, np); res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { if (! allow_name && allow_if_found) sgj_pr_hr(jsp, "%s%s\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) { jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); jap = sgj_named_subarray_r(jsp, jo2p, "feature_set_code_list"); } decode_feature_sets_vpd(rp, len, op, jap); } return 0; } break; case 0xb0: /* depends on pdt */ res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { bool bl = false; bool sad = false; bool oi = false; switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: np = bl_vpdp; ep = "(SBC)"; bl = true; break; case PDT_TAPE: case PDT_MCHANGER: np = sad_vpdp; ep = "(SSC)"; sad = true; break; case PDT_OSD: np = "OSD information VPD page"; ep = "(OSD)"; oi = true; break; default: np = NULL; break; } if (NULL == np) sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt); else if (allow_name || allow_if_found) sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : ""); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); if (bl) decode_block_limits_vpd(rp, len, op, jo2p); else if (sad) { decode_b0_vpd(rp, len, op, jo2p); } else if (oi) { decode_b0_vpd(rp, len, op, jo2p); } else { } } return 0; } else if ((! op->do_raw) && (! op->do_quiet) && (dhex < 3) && exam_not_given) sgj_pr_hr(jsp, "%sVPD page=0xb0\n", pre); break; case 0xb1: /* depends on pdt */ res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { bool bdc = false; switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: np = bdc_vpdp; ep = "(SBC)"; bdc = true; break; case PDT_TAPE: case PDT_MCHANGER: np = masn_vpdp; ep = "(SSC)"; break; case PDT_OSD: np = st_vpdp; ep = "(OSD)"; break; case PDT_ADC: np = masn_vpdp; ep = "(ADC)"; break; default: np = NULL; break; } if (NULL == np) sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt); else if (allow_name || allow_if_found) sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : ""); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); if (bdc) decode_block_dev_ch_vpd(rp, len, op, jo2p); else decode_b1_vpd(rp, len, op, jo2p); } return 0; } else if ((! op->do_raw) && (! op->do_quiet) && (dhex < 3) && exam_not_given) sgj_pr_hr(jsp, "%sVPD page=0xb1\n", pre); break; case 0xb2: /* VPD page depends on pdt */ res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { bool lbpv = false; bool tas = false; switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: np = lbpv_vpdp; ep = "(SBC)"; lbpv = true; break; case PDT_TAPE: case PDT_MCHANGER: np = lbpro_vpdp; ep = "(SSC)"; tas = true; break; default: np = NULL; break; } if (NULL == np) sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt); else if (allow_name || allow_if_found) sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : ""); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); if (lbpv) decode_block_lb_prov_vpd(rp, len, op, jo2p); else if (tas) decode_tapealert_supported_vpd(rp, len, op, jo2p); else decode_b2_vpd(rp, len, pdt, op); } return 0; } else if ((! op->do_raw) && (! op->do_quiet) && (dhex < 3) && exam_not_given) sgj_pr_hr(jsp, "%sVPD page=0xb2\n", pre); break; case 0xb3: /* VPD page depends on pdt */ res = vpd_fetch_page(ptvp, 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 = ref_vpdp; ep = "(SBC)"; ref = true; break; case PDT_TAPE: case PDT_MCHANGER: np = adsn_vpdp; ep = "(SSC)"; break; default: np = NULL; break; } if (NULL == np) sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt); else if (allow_name || allow_if_found) sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : ""); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); if (ref) decode_referrals_vpd(rp, len, op, jo2p); else decode_b3_vpd(rp, len, op, jo2p); } return 0; } else if ((! op->do_raw) && (! op->do_quiet) && (dhex < 3) && exam_not_given) sgj_pr_hr(jsp, "%sVPD page=0xb3\n", pre); break; case 0xb4: /* VPD page depends on pdt */ res = vpd_fetch_page(ptvp, 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 = sbl_vpdp; ep = "(SBC)"; sbl = true; break; case PDT_TAPE: case PDT_MCHANGER: np = dtde_vpdp; ep = "(SSC)"; break; default: np = NULL; break; } if (NULL == np) sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt); else if (allow_name || allow_if_found) sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : ""); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); if (sbl) { if (as_json) jap = sgj_named_subarray_r(jsp, jo2p, "logical_block_" "length_and_protection_types_descriptor_list"); decode_sup_block_lens_vpd(rp, len, op, jap); } else decode_b4_vpd(rp, len, op, jo2p); } return 0; } else if ((! op->do_raw) && (! op->do_quiet) && (dhex < 3) && exam_not_given) sgj_pr_hr(jsp, "%sVPD page=0xb4\n", pre); break; case 0xb5: /* VPD page depends on pdt */ res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { bool bdce = false; bool lbp = false; pdt = rp[0] & PDT_MASK; switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: np = bdce_vpdp; ep = "(SBC)"; bdce = true; break; case PDT_TAPE: case PDT_MCHANGER: np = lbpro_vpdp; ep = "(SSC)"; lbp = true; break; default: np = NULL; break; } if (NULL == np) sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt); else if (allow_name || allow_if_found) sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : ""); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); if (bdce) decode_block_dev_char_ext_vpd(rp, len, op, jo2p); else if (lbp) { if (as_json) jap = sgj_named_subarray_r(jsp, jo2p, "logical_block_protection_method_descriptor_list"); decode_lb_protection_vpd(rp, len, op, jap); } else decode_b5_vpd(rp, len, pdt, op); } return 0; } else if ((! op->do_raw) && (! op->do_quiet) && (dhex < 3) && exam_not_given) sgj_pr_hr(jsp, "%sVPD page=0xb5\n", pre); break; case VPD_ZBC_DEV_CHARS: /* 0xb6 for both pdt=0 and pdt=0x14 */ res = vpd_fetch_page(ptvp, 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 = zbdc_vpdp; ep = "(SBC, ZBC)"; zbdch = true; break; default: np = NULL; break; } if (NULL == np) sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt); else if (allow_name || allow_if_found) sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : ""); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); if (zbdch) decode_zbdch_vpd(rp, len, op, jo2p); else return SG_LIB_CAT_OTHER; } return 0; } else if ((! op->do_raw) && (! op->do_quiet) && (dhex < 3) && exam_not_given) sgj_pr_hr(jsp, "%sVPD page=0xb6\n", pre); break; case 0xb7: res = vpd_fetch_page(ptvp, 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 = ble_vpdp; ep = "(SBC)"; ble = true; break; default: np = NULL; break; } if (NULL == np) sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt); else if (allow_name || allow_if_found) sgj_pr_hr(jsp, "%s%s %s:\n", pre, np, ep ? ep : ""); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); if (ble) decode_block_limits_ext_vpd(rp, len, op, jo2p); else return SG_LIB_CAT_OTHER; } return 0; } else if ((! op->do_raw) && (! op->do_quiet) && (dhex < 3) && exam_not_given) sgj_pr_hr(jsp, "%sVPD page=0xb7\n", pre); break; case 0xb8: /* VPD_FORMAT_PRESETS */ res = vpd_fetch_page(ptvp, 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 = fp_vpdp; ep = "(SBC)"; fp = true; break; default: np = NULL; break; } if (NULL == np) sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt); else if (allow_name || allow_if_found) sgj_pr_hr(jsp, "%s%s %s:\n", pre, np, ep ? ep : ""); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) { jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); jap = sgj_named_subarray_r(jsp, jo2p, "format_preset_" "descriptor_list"); } if (fp) decode_format_presets_vpd(rp, len, op, jap); else return SG_LIB_CAT_OTHER; } return 0; } else if ((! op->do_raw) && (! op->do_quiet) && (dhex < 3) && exam_not_given) sgj_pr_hr(jsp, "%sVPD page=0xb8\n", pre); break; case 0xb9: /* VPD_CON_POS_RANGE */ res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { bool cpr = false; /* ["cpr"] */ pdt = rp[0] & PDT_MASK; switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: np = cpr_vpdp; ep = "(SBC)"; cpr = true; break; default: np = NULL; break; } if (NULL == np) sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt); else if (allow_name || allow_if_found) sgj_pr_hr(jsp, "%s%s %s:\n", pre, np, ep ? ep : ""); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) { jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); jap = sgj_named_subarray_r(jsp, jo2p, "lba_range_" "descriptor_list"); } if (cpr) decode_con_pos_range_vpd(rp, len, op, jap); else return SG_LIB_CAT_OTHER; } return 0; } else if ((! op->do_raw) && (! op->do_quiet) && (dhex < 3) && exam_not_given) sgj_pr_hr(jsp, "%sVPD page=0xb9\n", pre); break; case 0xba: /* VPD_CAP_PROD_ID */ res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { bool cap = false; /* ["cap"] */ pdt = rp[0] & PDT_MASK; switch (pdt) { case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC: np = cap_vpdp; ep = "(SBC)"; cap = true; break; default: np = NULL; break; } if (NULL == np) sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt); else if (allow_name || allow_if_found) sgj_pr_hr(jsp, "%s%s %s:\n", pre, np, ep ? ep : ""); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) { jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); jap = sgj_named_subarray_r(jsp, jo2p, "capacity_product_identification_" "descriptors_list"); } if (cap) decode_cap_prod_id_vpd(rp, len, op, jap); else return SG_LIB_CAT_OTHER; } return 0; } else if ((! op->do_raw) && (! op->do_quiet) && (dhex < 3) && exam_not_given) sgj_pr_hr(jsp, "%sVPD page=0xba\n", pre); break; case SG_NVME_VPD_NICR: /* 0xde, model on ATA Info VPD page [0x89] */ np = "NVME Identity Controller response VPD page"; if (allow_name) sgj_pr_hr(jsp, "%s%s\n", pre, np); res = vpd_fetch_page(ptvp, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { if (! allow_name && allow_if_found) sgj_pr_hr(jsp, "%s%s:\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: " "%s]\n", pqual, pdt_str); if (as_json) jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); decode_nicr_vpd(rp, len, op, jo2p); } } break; default: return SG_LIB_CAT_OTHER; } return res; } static int svpd_decode_all(struct sg_pt_base * ptvp, struct opts_t * op, sgj_opaque_p jop) { int k, res, rlen, n, pn, subvalue; int max_pn = 255; int any_err = 0; sgj_state * jsp = &op->json_st; uint8_t vpd0_buff[512]; uint8_t * rp = vpd0_buff; if (op->vpd_pn > 0) max_pn = op->vpd_pn; if (ptvp) { /* have valid open file descriptor (handle) */ res = vpd_fetch_page(ptvp, rp, VPD_SUPPORTED_VPDS, op->maxlen, op->do_quiet, op->verbose, &rlen); if (res) { if (! op->do_quiet) { if (SG_LIB_CAT_ABORTED_COMMAND == res) pr2serr("%s: VPD page 0, aborted command\n", __func__); else if (res) { char b[80]; sg_get_category_sense_str(res, sizeof(b), b, op->verbose); pr2serr("%s: fetching VPD page 0 failed: %s\n", __func__, b); } } return res; } n = sg_get_unaligned_be16(rp + 2); if (n > (rlen - 4)) { if (op->verbose) pr2serr("%s: rlen=%d > page0 size=%d\n", __func__, rlen, n + 4); n = (rlen - 4); } for (k = 0; k < n; ++k) { pn = rp[4 + k]; if (pn > max_pn) continue; op->vpd_pn = pn; if (k > 0) sgj_pr_hr(jsp, "\n"); if (op->do_long) { if (jsp->pr_as_json) sgj_pr_hr(jsp, "[0x%x]:\n", pn); else printf("[0x%x] ", pn); } res = svpd_decode_t10(ptvp, op, jop, 0, 0, NULL); if (SG_LIB_CAT_OTHER == res) { res = svpd_decode_vendor(ptvp, op, jop, 0); if (SG_LIB_CAT_OTHER == res) res = svpd_unable_to_decode(ptvp, op, jop, 0, 0); } if (! op->do_quiet) { if (SG_LIB_CAT_ABORTED_COMMAND == res) pr2serr("fetching VPD page failed, aborted command\n"); else if (res) { char b[80]; sg_get_category_sense_str(res, sizeof(b), b, op->verbose); pr2serr("fetching VPD page failed: %s\n", b); } } if (res) any_err = res; } res = any_err; } else { /* input is coming from --inhex=FN */ int bump, off; int in_len = op->maxlen; int prev_pn = -1; res = 0; if (op->page_given && (VPD_NOPE_WANT_STD_INQ == op->vpd_pn)) return svpd_decode_t10(NULL, op, jop, 0, 0, NULL); for (off = 0; off < in_len; off += bump) { rp = rsp_buff + off; pn = rp[1]; bump = sg_get_unaligned_be16(rp + 2) + 4; if ((off + bump) > in_len) { pr2serr("%s: page 0x%x size (%d) exceeds buffer\n", __func__, pn, bump); bump = in_len - off; } if (op->page_given && (pn != op->vpd_pn)) continue; if (pn <= prev_pn) { pr2serr("%s: prev_pn=0x%x, this pn=0x%x, not ascending so " "exit\n", __func__, prev_pn, pn); break; } prev_pn = pn; op->vpd_pn = pn; if (pn > max_pn) { if (op->verbose > 2) pr2serr("%s: skipping as this pn=0x%x exceeds " "max_pn=0x%x\n", __func__, pn, max_pn); continue; } if (op->do_long) { if (jsp->pr_as_json) sgj_pr_hr(jsp, "[0x%x]:\n", pn); else sgj_pr_hr(jsp, "[0x%x] ", pn); } subvalue = 0; if ((pn == op->orig_vpd_pn) && (op->vend_prod_num > 0)) subvalue = op->vend_prod_num; res = svpd_decode_t10(NULL, op, jop, subvalue, off, NULL); if (SG_LIB_CAT_OTHER == res) { res = svpd_decode_vendor(NULL, op, jop, off); if (SG_LIB_CAT_OTHER == res) res = svpd_unable_to_decode(NULL, op, jop, 0, off); } } } return res; } static int svpd_examine_all(struct sg_pt_base * ptvp, struct opts_t * op, sgj_opaque_p jop) { bool first = true; bool got_one = false; int k, res, start; int max_pn; int any_err = 0; sgj_state * jsp = &op->json_st; char b[80]; max_pn = (op->page_given ? op->vpd_pn : 0xff); switch (op->examine) { case 1: start = 0x80; break; case 2: start = 0x0; break; default: start = 0xc0; break; } if (start > max_pn) { /* swap them around */ k = start; start = max_pn; max_pn = k; } for (k = start; k <= max_pn; ++k) { op->vpd_pn = k; if (first) first = false; else if (got_one) { sgj_pr_hr(jsp, "\n"); got_one = false; } if (op->do_long) snprintf(b, sizeof(b), "[0x%x] ", k); else b[0] = '\0'; res = svpd_decode_t10(ptvp, op, jop, 0, 0, b); if (SG_LIB_CAT_OTHER == res) { res = svpd_decode_vendor(ptvp, op, jop, 0); if (SG_LIB_CAT_OTHER == res) res = svpd_unable_to_decode(ptvp, op, jop, 0, 0); } if (! op->do_quiet) { if (SG_LIB_CAT_ABORTED_COMMAND == res) pr2serr("fetching VPD page failed, aborted command\n"); else if (res && (SG_LIB_CAT_ILLEGAL_REQ != res)) { /* SG_LIB_CAT_ILLEGAL_REQ expected as well examine all */ sg_get_category_sense_str(res, sizeof(b), b, op->verbose); pr2serr("fetching VPD page failed: %s\n", b); } } if (res && (SG_LIB_CAT_ILLEGAL_REQ != res)) any_err = res; if (0 == res) got_one = true; } return any_err; } /* Handles short options after '-j' including a sequence of short options * that include one 'j' (for JSON). Want optional argument to '-j' to be * prefixed by '='. Return 0 for good, SG_LIB_SYNTAX_ERROR for syntax error * and SG_LIB_OK_FALSE for exit with no error. */ static int chk_short_opts(const char sopt_ch, struct opts_t * op) { /* only need to process short, non-argument options */ switch (sopt_ch) { case 'a': op->do_all = true; break; case 'd': op->do_descriptors = true; break; case 'D': op->do_debug = true; break; case 'e': op->do_enum = true; break; case 'E': ++op->examine; op->examine_given = true; break; case 'f': op->do_force = true; break; case 'h': case '?': usage(); return SG_LIB_OK_FALSE; case 'H': ++op->do_hex; break; case 'i': ++op->do_ident; break; case 'j': break; /* simply ignore second 'j' (e.g. '-jxj') */ case 'l': op->do_long = true; break; case 'q': op->do_quiet = true; break; case 'r': ++op->do_raw; break; case 'v': op->verbose_given = true; ++op->verbose; break; case 'V': op->version_given = true; break; default: pr2serr("unrecognised option code 0x%x ??\n", sopt_ch); if (op->do_help) break; usage(); return SG_LIB_SYNTAX_ERROR; } return 0; } int main(int argc, char * argv[]) { bool as_json = false; int c, k, n, q, res, matches, vb; int sg_fd = -1; int inhex_len = 0; int inraw_len = 0; int ret = 0; int subvalue = 0; const char * cp; struct sg_pt_base * ptvp = NULL; sgj_state * jsp; sgj_opaque_p jop = NULL; const struct svpd_values_name_t * vnp; struct opts_t opts SG_C_CPP_ZERO_INIT; struct opts_t * op = &opts; if (getenv("SG3_UTILS_INVOCATION")) sg_rep_invocation(MY_NAME, version_str, argc, argv, stderr); op->vend_prod_num = -1; op->cns = -1; while (1) { int option_index = 0; c = getopt_long(argc, argv, "^adDeEfhHiI:j::J:lm:M:p:qQ:rvVZ", long_options, &option_index); if (c == -1) break; switch (c) { case 'a': op->do_all = true; break; case 'd': op->do_descriptors = true; break; case 'D': op->do_debug = true; break; case 'e': op->do_enum = true; break; case 'E': ++op->examine; op->examine_given = true; break; case 'f': op->do_force = true; break; case 'h': case '?': usage(); return 0; case 'H': ++op->do_hex; break; case 'i': ++op->do_ident; break; case 'I': if (op->inhex_fn) { pr2serr("only one '--inhex=' option permitted\n"); usage(); return SG_LIB_SYNTAX_ERROR; } else op->inhex_fn = optarg; break; case 'j': /* for: -j[=JO] */ case '^': /* for: --json[=JO] */ op->do_json = true; /* Now want '=' to precede all JSON optional arguments */ if (optarg) { if ('^' == c) { op->json_arg = optarg; break; } else if ('=' == *optarg) { op->json_arg = optarg + 1; break; } n = strlen(optarg); for (k = 0; k < n; ++k) { q = chk_short_opts(*(optarg + k), op); if (SG_LIB_SYNTAX_ERROR == q) return SG_LIB_SYNTAX_ERROR; if (SG_LIB_OK_FALSE == q) return 0; } } else op->json_arg = NULL; break; case 'J': op->do_json = true; op->js_file = optarg; break; case 'l': op->do_long = true; break; case 'm': op->maxlen = sg_get_num(optarg); if ((op->maxlen < 0) || (op->maxlen > MX_ALLOC_LEN)) { pr2serr("argument to '--maxlen' should be %d or less\n", MX_ALLOC_LEN); return SG_LIB_SYNTAX_ERROR; } if ((op->maxlen > 0) && (op->maxlen < MIN_MAXLEN)) { pr2serr("Warning: overriding '--maxlen' < %d, using " "default\n", MIN_MAXLEN); op->maxlen = 0; } break; case 'M': if (op->vend_prod_arg) { pr2serr("only one '--vendor=' option permitted\n"); usage(); return SG_LIB_SYNTAX_ERROR; } else op->vend_prod_arg = optarg; break; case 'p': if (op->page_str) { pr2serr("only one '--page=' option permitted\n"); usage(); return SG_LIB_SYNTAX_ERROR; } else op->page_str = optarg; op->page_given = true; break; case 'q': op->do_quiet = true; break; case 'Q': op->sinq_inraw_fn = optarg; break; case 'r': ++op->do_raw; break; case 'v': op->verbose_given = true; ++op->verbose; break; case 'V': op->version_given = true; break; case 'Z': pr2serr("--export NOT support by sg_vpd, try sg_inq instead\n"); /* treat like --version for quick exit */ op->version_given = true; break; default: pr2serr("unrecognised option code 0x%x ??\n", c); usage(); return SG_LIB_SYNTAX_ERROR; } } if (optind < argc) { if (NULL == op->device_name) { op->device_name = argv[optind]; ++optind; } if (optind < argc) { for (; optind < argc; ++optind) pr2serr("Unexpected extra argument: %s\n", argv[optind]); usage(); return SG_LIB_SYNTAX_ERROR; } } #ifdef DEBUG pr2serr("In DEBUG mode, "); if (op->verbose_given && op->version_given) { pr2serr("but override: '-vV' given, zero verbose and continue\n"); op->verbose_given = false; op->version_given = false; op->verbose = 0; } else if (! op->verbose_given) { pr2serr("set '-vv'\n"); op->verbose = 2; } else pr2serr("keep verbose=%d\n", op->verbose); #else if (op->verbose_given && op->version_given) pr2serr("Not in DEBUG mode, so '-vV' has no special action\n"); #endif if (op->version_given) { pr2serr("version: %s\n", version_str); return 0; } vb = op->verbose; if (op->do_enum) { if (op->device_name) pr2serr("Device name %s ignored when --enumerate given\n", op->device_name); if (op->vend_prod_arg) { if (isdigit((uint8_t)op->vend_prod_arg[0])) { op->vend_prod_num = sg_get_num_nomult(op->vend_prod_arg); if ((op->vend_prod_num < 0) || (op->vend_prod_num > 10)) { pr2serr("Bad vendor/product number after '--vendor=' " "option\n"); return SG_LIB_SYNTAX_ERROR; } } else { op->vend_prod_num = svpd_find_vp_num_by_acron(op->vend_prod_arg); if (op->vend_prod_num < 0) { pr2serr("Bad vendor/product acronym after '--vendor=' " "option\n"); return SG_LIB_SYNTAX_ERROR; } } svpd_enumerate_vendor(op->vend_prod_num); return 0; } if (op->page_str) { if ((0 == strcmp("-1", op->page_str)) || (0 == strcmp("-2", op->page_str))) op->vpd_pn = VPD_NOPE_WANT_STD_INQ; else if (isdigit((uint8_t)op->page_str[0])) { op->vpd_pn = sg_get_num_nomult(op->page_str); if ((op->vpd_pn < 0) || (op->vpd_pn > 255)) { pr2serr("Bad page code value after '-p' option\n"); return SG_LIB_SYNTAX_ERROR; } } else { pr2serr("with --enumerate only search using VPD page " "numbers\n"); return SG_LIB_SYNTAX_ERROR; } matches = count_standard_vpds(op->vpd_pn); if (0 == matches) matches = svpd_count_vendor_vpds(op->vpd_pn, op->vend_prod_num); if (0 == matches) printf("No matches found for VPD page number 0x%x\n", op->vpd_pn); } else { /* enumerate standard then vendor VPD pages */ printf("Standard VPD pages:\n"); enumerate_vpds(1, 1); } return 0; } if (op->do_debug) { if (vb > 0) pr2serr("debug option set: changes meaning of some --hex " "options\n"); if (op->do_hex > 0) op->do_hex = -op->do_hex; } jsp = &op->json_st; if (op->do_json) { if (! sgj_init_state(jsp, op->json_arg)) { int bad_char = jsp->first_bad_char; char e[1500]; if (bad_char) { pr2serr("bad argument to --json= option, unrecognized " "character '%c'\n\n", bad_char); } sg_json_usage(0, e, sizeof(e)); pr2serr("%s", e); ret = SG_LIB_SYNTAX_ERROR; goto err_out; } jop = sgj_start_r(MY_NAME, version_str, argc, argv, jsp); if ((op->verbose > 0) && (0 == jsp->verbose)) jsp->verbose = op->verbose; } if (op->do_long > 0) jsp->z_counter = op->do_long; else if (! op->do_quiet) jsp->z_counter = 1; as_json = jsp->pr_as_json; if (op->page_str) { if ('-' == op->page_str[0]) op->vpd_pn = VPD_NOPE_WANT_STD_INQ; else if (isalpha((uint8_t)op->page_str[0])) { vnp = sdp_find_vpd_by_acron(op->page_str); if (NULL == vnp) { vnp = svpd_find_vendor_by_acron(op->page_str); if (NULL == vnp) { if (0 == strcmp("stdinq", op->page_str)) { vnp = sdp_find_vpd_by_acron("sinq"); } else { pr2serr("abbreviation doesn't match a VPD page\n"); sgj_pr_hr(jsp, "Available standard VPD pages:\n"); enumerate_vpds(1, 1); ret = SG_LIB_SYNTAX_ERROR; goto fini; } } } op->vpd_pn = vnp->value; op->orig_vpd_pn = op->vpd_pn; subvalue = vnp->subvalue; op->vend_prod_num = subvalue; } else { cp = strchr(op->page_str, ','); if (cp && op->vend_prod_arg) { pr2serr("the --page=pg,vp and the --vendor=vp forms overlap, " "choose one or the other\n"); ret = SG_LIB_SYNTAX_ERROR; goto fini; } op->vpd_pn = sg_get_num_nomult(op->page_str); if ((op->vpd_pn < 0) || (op->vpd_pn > 255)) { pr2serr("Bad page code value after '-p' option\n"); sgj_pr_hr(jsp, "Available standard VPD pages:\n"); enumerate_vpds(1, 1); ret = SG_LIB_SYNTAX_ERROR; goto fini; } op->orig_vpd_pn = op->vpd_pn; if (cp) { if (isdigit((uint8_t)*(cp + 1))) op->vend_prod_num = sg_get_num_nomult(cp + 1); else op->vend_prod_num = svpd_find_vp_num_by_acron(cp + 1); if ((op->vend_prod_num < 0) || (op->vend_prod_num > 255)) { pr2serr("Bad vendor/product acronym after comma in '-p' " "option\n"); if (op->vend_prod_num < 0) svpd_enumerate_vendor(-1); ret = SG_LIB_SYNTAX_ERROR; goto fini; } subvalue = op->vend_prod_num; } else if (op->vend_prod_arg) { if (isdigit((uint8_t)op->vend_prod_arg[0])) op->vend_prod_num = sg_get_num_nomult(op->vend_prod_arg); else op->vend_prod_num = svpd_find_vp_num_by_acron(op->vend_prod_arg); if ((op->vend_prod_num < 0) || (op->vend_prod_num > 255)) { pr2serr("Bad vendor/product acronym after '--vendor=' " "option\n"); svpd_enumerate_vendor(-1); ret = SG_LIB_SYNTAX_ERROR; goto fini; } subvalue = op->vend_prod_num; } } if (vb > 3) pr2serr("'--page=' matched pn=%d [0x%x], subvalue=%d\n", op->vpd_pn, op->vpd_pn, subvalue); } else if (op->vend_prod_arg) { if (isdigit((uint8_t)op->vend_prod_arg[0])) op->vend_prod_num = sg_get_num_nomult(op->vend_prod_arg); else op->vend_prod_num = svpd_find_vp_num_by_acron(op->vend_prod_arg); if ((op->vend_prod_num < 0) || (op->vend_prod_num > 255)) { pr2serr("Bad vendor/product acronym after '--vendor=' " "option\n"); svpd_enumerate_vendor(-1); ret = SG_LIB_SYNTAX_ERROR; goto fini; } subvalue = op->vend_prod_num; } if (op->do_descriptors) { if (op->vend_prod_arg) { pr2serr("--vendor= and --desc contradict, pick one\n"); ret = SG_LIB_CONTRADICT; goto fini; } if (op->page_given) { if (VPD_NOPE_WANT_STD_INQ != op->vpd_pn) { pr2serr("--desc only valid with --page=sinq\n"); ret = SG_LIB_CONTRADICT; goto fini; } } else if (op->do_ident) { pr2serr("--desc and --ident are not compatible\n"); ret = SG_LIB_CONTRADICT; goto fini; } else op->vpd_pn = VPD_NOPE_WANT_STD_INQ; } rsp_buff = sg_memalign(rsp_buff_sz, 0 /* page align */, &free_rsp_buff, false); if (NULL == rsp_buff) { pr2serr("Unable to allocate %d bytes on heap\n", rsp_buff_sz); ret = sg_convert_errno(ENOMEM); goto fini; } if (op->sinq_inraw_fn) { /* Note: want to support both --sinq_inraw= and --inhex= options */ if ((ret = sg_f2hex_arr(op->sinq_inraw_fn, true, false, rsp_buff, &inraw_len, rsp_buff_sz))) { goto err_out; } if (inraw_len < SINQ_COMMON_RESP_LEN) { pr2serr("Unable to read 36 or more bytes from %s\n", op->sinq_inraw_fn); ret = SG_LIB_FILE_ERROR; goto err_out; } if (inraw_len > SINQ_VER_DESC_RESP_LEN) inraw_len = SINQ_VER_DESC_RESP_LEN; memcpy(op->std_inq_a, rsp_buff, inraw_len); op->std_inq_a_valid = true; } if (op->inhex_fn) { if (op->device_name) { pr2serr("Cannot have both a DEVICE and --inhex= option\n"); ret = SG_LIB_SYNTAX_ERROR; goto err_out; } if ((ret = sg_f2hex_arr(op->inhex_fn, !!op->do_raw, false, rsp_buff, &inhex_len, rsp_buff_sz))) { goto err_out; } if (vb > 2) pr2serr("Read %d [0x%x] bytes of user supplied data\n", inhex_len, inhex_len); if (vb > 3) hex2stderr(rsp_buff, inhex_len, 0); op->do_raw = 0; /* don't want raw on output with --inhex= */ if ((NULL == op->page_str) && (! op->do_all)) { /* may be able to deduce VPD page */ if ((0x2 == (0xf & rsp_buff[3])) && (rsp_buff[2] > 2)) { if (vb) pr2serr("Guessing from --inhex= this is a standard " "INQUIRY\n"); } else if (rsp_buff[2] <= 2) { if (vb) pr2serr("Guessing from --inhex this is VPD page 0x%x\n", rsp_buff[1]); op->vpd_pn = rsp_buff[1]; } else { if (op->vpd_pn > 0x80) { op->vpd_pn = rsp_buff[1]; if (vb) pr2serr("Guessing from --inhex this is VPD page " "0x%x\n", rsp_buff[1]); } else { op->vpd_pn = VPD_NOPE_WANT_STD_INQ; if (vb) pr2serr("page number unclear from --inhex, hope " "it's a standard INQUIRY response\n"); } } } } else if ((NULL == op->device_name) && (! op->std_inq_a_valid)) { pr2serr("No DEVICE argument given\n\n"); pr2serr("Use '-h' or '--help' option for usage summary\n"); ret = SG_LIB_SYNTAX_ERROR; goto err_out; } if (op->do_raw && op->do_hex) { pr2serr("Can't do hex and raw at the same time\n"); usage(); ret = SG_LIB_SYNTAX_ERROR; goto err_out; } if (op->do_ident) { op->vpd_pn = VPD_DEVICE_ID; op->orig_vpd_pn = op->vpd_pn; if (op->do_ident > 1) { if (! op->do_long) op->do_quiet = true; subvalue = VPD_DI_SEL_LU; } } if (op->do_raw) { if (sg_set_binary_mode(STDOUT_FILENO) < 0) { perror("sg_set_binary_mode"); ret = SG_LIB_FILE_ERROR; goto err_out; } } if (op->inhex_fn) { if ((0 == op->maxlen) || (inhex_len < op->maxlen)) op->maxlen = inhex_len; if (op->do_all || op->page_given) res = svpd_decode_all(NULL, op, jop); else { res = svpd_decode_t10(NULL, op, jop, subvalue, 0, NULL); if (SG_LIB_CAT_OTHER == res) { res = svpd_decode_vendor(NULL, op, jop, 0); if (SG_LIB_CAT_OTHER == res) res = svpd_unable_to_decode(NULL, op, jop, subvalue, 0); } } ret = res; goto err_out; } else if (op->std_inq_a_valid && (NULL == op->device_name)) { /* nothing else to do ... */ /* --sinq_inraw=RFN contents still in rsp_buff */ if (op->do_raw) dStrRaw(rsp_buff, inraw_len); else if (op->do_hex) { if (! op->do_quiet && (op->do_hex < 3)) sgj_pr_hr(jsp, "Standard Inquiry data format:\n"); hex2stdout(rsp_buff, inraw_len, (1 == op->do_hex) ? 0 : -1); } else std_inq_decode(rsp_buff, inraw_len, op, jop); ret = 0; goto fini; } if ((sg_fd = sg_cmds_open_device(op->device_name, true /* ro */, vb)) < 0) { pr2serr("error opening file: %s: %s\n", op->device_name, safe_strerror(-sg_fd)); ret = sg_convert_errno(-sg_fd); if (ret < 0) ret = SG_LIB_FILE_ERROR; goto err_out; } ptvp = construct_scsi_pt_obj_with_fd(sg_fd, vb); if (NULL == ptvp) { pr2serr("memory problem from construct_scsi_pt_obj_with_fd()\n"); ret = sg_convert_errno(ENOMEM); goto err_out; } if (op->examine_given) { ret = svpd_examine_all(ptvp, op, jop); } else if (op->do_all) ret = svpd_decode_all(ptvp, op, jop); else { memset(rsp_buff, 0, rsp_buff_sz); res = svpd_decode_t10(ptvp, op, jop, subvalue, 0, NULL); if (SG_LIB_CAT_OTHER == res) { res = svpd_decode_vendor(ptvp, op, jop, 0); if (SG_LIB_CAT_OTHER == res) res = svpd_unable_to_decode(ptvp, op, jop, subvalue, 0); } if (! op->do_quiet) { if (SG_LIB_CAT_ABORTED_COMMAND == res) pr2serr("fetching VPD page failed, aborted command\n"); else if (res) { char b[80]; sg_get_category_sense_str(res, sizeof(b), b, vb); pr2serr("fetching VPD page failed: %s\n", b); } } ret = res; } err_out: if (free_rsp_buff) free(free_rsp_buff); if ((0 == vb) && (! op->do_quiet)) { if (! sg_if_can2stderr("sg_vpd failed: ", ret)) pr2serr("Some error occurred, try again with '-v' or '-vv' for " "more information\n"); } fini: if (ptvp) destruct_scsi_pt_obj(ptvp); res = (sg_fd >= 0) ? sg_cmds_close_device(sg_fd) : 0; if (res < 0) { pr2serr("close error: %s\n", safe_strerror(-res)); if (0 == ret) ret = sg_convert_errno(-res); } ret = (ret >= 0) ? ret : SG_LIB_CAT_OTHER; if (as_json && jop) { FILE * fp = stdout; if (op->js_file) { if ((1 != strlen(op->js_file)) || ('-' != op->js_file[0])) { fp = fopen(op->js_file, "w"); /* truncate if exists */ if (NULL == fp) { int e = errno; pr2serr("unable to open file: %s [%s]\n", op->js_file, safe_strerror(e)); ret = sg_convert_errno(e); } } /* '--js-file=-' will send JSON output to stdout */ } if (fp) sgj_js2file(jsp, NULL, ret, fp); if (op->js_file && fp && (stdout != fp)) fclose(fp); sgj_finish(jsp); } return ret; }