diff options
author | Douglas Gilbert <dgilbert@interlog.com> | 2022-08-07 02:22:53 +0000 |
---|---|---|
committer | Douglas Gilbert <dgilbert@interlog.com> | 2022-08-07 02:22:53 +0000 |
commit | 23beedb782b58779546c58a7e9ef18aeccf50485 (patch) | |
tree | 9a98707784d48a4fa055b81f11b8d2135cdcb435 /src | |
parent | 640f7a7eff809159d9ac4c3ffc3681a6a5c3bede (diff) | |
download | sg3_utils-23beedb782b58779546c58a7e9ef18aeccf50485.tar.gz |
sg_inq+sg_vpd: more JSON work (tpc)
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@964 6180dd3e-e324-4e3e-922d-17de1ae2f315
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.in | 1 | ||||
-rw-r--r-- | src/sg_decode_sense.c | 51 | ||||
-rw-r--r-- | src/sg_inq.c | 59 | ||||
-rw-r--r-- | src/sg_vpd.c | 561 | ||||
-rw-r--r-- | src/sg_vpd_common.c | 685 | ||||
-rw-r--r-- | src/sg_vpd_common.h | 10 |
6 files changed, 822 insertions, 545 deletions
diff --git a/src/Makefile.in b/src/Makefile.in index 9fb93bf7..c07b0124 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -547,6 +547,7 @@ EGREP = @EGREP@ ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ +FILECMD = @FILECMD@ GETOPT_O_FILES = @GETOPT_O_FILES@ GREP = @GREP@ INSTALL = @INSTALL@ diff --git a/src/sg_decode_sense.c b/src/sg_decode_sense.c index e902d0a6..db54e0b9 100644 --- a/src/sg_decode_sense.c +++ b/src/sg_decode_sense.c @@ -30,11 +30,11 @@ #include "sg_unaligned.h" -static const char * version_str = "1.31 20220729"; +static const char * version_str = "1.32 20220730"; #define MY_NAME "sg_decode_sense" -#define MAX_SENSE_LEN 4096 /* max descriptor format actually: 255+8 */ +#define MAX_SENSE_LEN 8192 /* max descriptor format actually: 255+8 */ static struct option long_options[] = { {"binary", required_argument, 0, 'b'}, @@ -46,8 +46,10 @@ static struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"hex", no_argument, 0, 'H'}, {"in", required_argument, 0, 'i'}, /* don't advertise */ - {"json", optional_argument, 0, 'j'}, {"inhex", required_argument, 0, 'i'}, /* same as --file */ + {"ignore-first", no_argument, 0, 'I'}, + {"ignore_first", no_argument, 0, 'I'}, + {"json", optional_argument, 0, 'j'}, {"nodecode", no_argument, 0, 'N'}, {"nospace", no_argument, 0, 'n'}, {"status", required_argument, 0, 's'}, @@ -68,6 +70,7 @@ struct opts_t { bool version_given; bool err_given; bool file_given; + bool ignore_first; const char * fname; int es_val; int hex_count; @@ -89,10 +92,11 @@ usage() pr2serr("Usage: sg_decode_sense [--binary=BFN] [--cdb] [--err=ES] " "[--file=HFN]\n" " [--help] [--hex] [--inhex=HFN] " - "[--json[=JO]]\n" - " [--nodecode] [--nospace] [--status=SS] " - "[--verbose]\n" - " [--version] [--write=WFN] H1 H2 H3 ...\n" + "[--ignore-first]\n" + " [--json[=JO]] [--nodecode] [--nospace] " + "[--status=SS]\n" + " [--verbose] [--version] [--write=WFN] " + "H1 H2 H3 ...\n" " where:\n" " --binary=BFN|-b BFN BFN is a file name to read sense " "data in\n" @@ -116,6 +120,10 @@ usage() " hex (used '-HH' or '-HHH' for different " "formats)\n" " --inhex=HFN|-i HFN same as action as --file=HFN\n" + " --ignore-first|-I when reading hex (e.g. with --file=HFN) " + "skip\n" + " the first hexadecimal value on each " + "line\n" " --json[=JO]|-j[JO] output in JSON instead of human " "readable text.\n" " Use --json=? for JSON help\n" @@ -149,7 +157,7 @@ parse_cmd_line(struct opts_t *op, int argc, char *argv[]) char *endptr; while (1) { - c = getopt_long(argc, argv, "b:ce:f:hHi:j::nNs:vVw:", long_options, + c = getopt_long(argc, argv, "b:ce:f:hHi:Ij::nNs:vVw:", long_options, NULL); if (c == -1) break; @@ -201,6 +209,9 @@ parse_cmd_line(struct opts_t *op, int argc, char *argv[]) op->file_given = true; op->fname = optarg; break; + case 'I': + op->ignore_first = true; + break; case 'j': if (! sgj_init_state(&op->json_st, optarg)) { int bad_char = op->json_st.first_bad_char; @@ -333,12 +344,17 @@ main(int argc, char *argv[]) const char * cp; sgj_state * jsp; sgj_opaque_p jop = NULL; + uint8_t * free_op_buff = NULL; char b[2048]; - struct opts_t opts; - op = &opts; + op = (struct opts_t *)sg_memalign(sizeof(*op), 0 /* page align */, + &free_op_buff, false); + if (NULL == op) { + pr2serr("Unable to allocate heap for options structure\n"); + ret = sg_convert_errno(ENOMEM); + goto clean_op; + } blen = sizeof(b); - memset(op, 0, sizeof(opts)); memset(b, 0, blen); ret = parse_cmd_line(op, argc, argv); @@ -360,14 +376,14 @@ main(int argc, char *argv[]) #endif if (op->version_given) { pr2serr("version: %s\n", version_str); - return 0; + goto clean_op; } if (ret != 0) { usage(); - return ret; + goto clean_op; } else if (op->do_help) { usage(); - return 0; + goto clean_op; } as_json = op->json_st.pr_as_json; jsp = &op->json_st; @@ -450,7 +466,9 @@ main(int argc, char *argv[]) op->sense_len = s; } else if (op->file_given) { ret = sg_f2hex_arr(op->fname, false, op->no_space, op->sense, - &op->sense_len, MAX_SENSE_LEN); + &op->sense_len, + (op->ignore_first ? -MAX_SENSE_LEN : + MAX_SENSE_LEN)); if (ret) { pr2serr("unable to decode ASCII hex from file: %s\n", op->fname); goto fini; @@ -522,5 +540,8 @@ fini: sgj_js2file(&op->json_st, NULL, ret, stdout); sgj_finish(jsp); } +clean_op: + if (free_op_buff) + free(free_op_buff); return ret; } diff --git a/src/sg_inq.c b/src/sg_inq.c index 001970f2..9745c0ec 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.26 20220729"; /* spc6r06 */ +static const char * version_str = "2.27 20220806"; /* spc6r06, sbc5r03 */ #define MY_NAME "sg_inq" @@ -328,8 +328,7 @@ usage() "Performs a SCSI INQUIRY command on DEVICE or decodes INQUIRY " "response\nheld in file FN. If no options given then does a " "'standard' INQUIRY.\nCan list VPD pages with '--vpd' or " - "'--page=PG' option. The sg_vpd\nand sdparm utilities decode " - "more VPD pages than this utility.\n"); + "'--page=PG' option.\n"); } #ifdef SG_SCSI_STRINGS @@ -3270,6 +3269,60 @@ vpd_decode(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int off) decode_dev_constit_vpd(rp, len, op, jap, recurse_vpd_decode); } break; + case VPD_3PARTY_COPY: /* 0x8f ["tpc"] */ + np = "Third party copy VPD page"; + if (!op->do_raw && (op->do_hex < 2)) + sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); + if (res) + break; + if (op->do_raw) + dStrRaw((const char *)rp, len); + else { + if (as_json) { + jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); + jap = sgj_named_subarray_r(jsp, jo2p, + "third_party_copy_descriptor_list"); + } + decode_3party_copy_vpd(rp, len, op, jap); + } + break; + case VPD_PROTO_LU: /* 0x90 ["pslu"] */ + np = "Protocol specific logical unit information VPD page"; + if (!op->do_raw && (op->do_hex < 2)) + sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); + if (res) + break; + if (op->do_raw) + dStrRaw((const char *)rp, len); + else { + if (as_json) { + jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); + jap = sgj_named_subarray_r(jsp, jo2p, + "logical_unit_information_descriptor_list"); + } + decode_proto_lu_vpd(rp, len, op, jap); + } + break; + case VPD_PROTO_PORT: /* 0x91 ["pspo"] */ + np = "Protocol specific port information VPD page"; + if (!op->do_raw && (op->do_hex < 2)) + sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np); + res = vpd_fetch_page_from_dev(sg_fd, rp, pn, op->maxlen, vb, &len); + if (res) + break; + if (op->do_raw) + dStrRaw((const char *)rp, len); + else { + if (as_json) { + jo2p = sg_vpd_js_hdr(jsp, jop, np, rp); + jap = sgj_named_subarray_r(jsp, jo2p, + "port_information_descriptor_list"); + } + decode_proto_port_vpd(rp, len, op, jap); + } + break; case VPD_SCSI_FEATURE_SETS: /* 0x92 ["sfs"] */ np = "SCSI Feature sets VPD page"; if (!op->do_raw && (op->do_hex < 2)) diff --git a/src/sg_vpd.c b/src/sg_vpd.c index a0bf5036..50f2c8a7 100644 --- a/src/sg_vpd.c +++ b/src/sg_vpd.c @@ -42,7 +42,7 @@ */ -static const char * version_str = "1.78 20220729"; /* spc6r06 + sbc5r01 */ +static const char * version_str = "1.79 20220806"; /* spc6r06 + sbc5r03 */ #define MY_NAME "sg_vpd" @@ -874,508 +874,6 @@ filter_dev_ids(const char * print_if_found, int num_leading, uint8_t * buff, return 0; } -/* This is xcopy(LID4) related: "ROD" == Representation Of Data - * Used by VPD_3PARTY_COPY */ -static void -decode_rod_descriptor(const uint8_t * buff, int len) -{ - const uint8_t * bp = buff; - int k, bump; - uint64_t ul; - - for (k = 0; k < len; k += bump, bp += bump) { - bump = sg_get_unaligned_be16(bp + 2) + 4; - switch (bp[0]) { - case 0: - /* Block ROD device type specific descriptor */ - printf(" Optimal block ROD length granularity: %d\n", - sg_get_unaligned_be16(bp + 6)); - printf(" Maximum Bytes in block ROD: %" PRIu64 "\n", - sg_get_unaligned_be64(bp + 8)); - ul = sg_get_unaligned_be64(bp + 16); - printf(" Optimal Bytes in block ROD transfer: "); - if (SG_LIB_UNBOUNDED_64BIT == ul) - printf("-1 [no limit]\n"); - else - printf("%" PRIu64 "\n", ul); - ul = sg_get_unaligned_be64(bp + 24); - printf(" Optimal Bytes to token per segment: "); - if (SG_LIB_UNBOUNDED_64BIT == ul) - printf("-1 [no limit]\n"); - else - printf("%" PRIu64 "\n", ul); - ul = sg_get_unaligned_be64(bp + 32); - printf(" Optimal Bytes from token per segment: "); - if (SG_LIB_UNBOUNDED_64BIT == ul) - printf("-1 [no limit]\n"); - else - printf("%" PRIu64 "\n", ul); - break; - case 1: - /* Stream ROD device type specific descriptor */ - printf(" Maximum Bytes in stream ROD: %" PRIu64 "\n", - sg_get_unaligned_be64(bp + 8)); - ul = sg_get_unaligned_be64(bp + 16); - printf(" Optimal Bytes in stream ROD transfer: "); - if (SG_LIB_UNBOUNDED_64BIT == ul) - printf("-1 [no limit]\n"); - else - printf("%" PRIu64 "\n", ul); - break; - case 3: - /* Copy manager ROD device type specific descriptor */ - printf(" Maximum Bytes in processor ROD: %" PRIu64 "\n", - sg_get_unaligned_be64(bp + 8)); - ul = sg_get_unaligned_be64(bp + 16); - printf(" Optimal Bytes in processor ROD transfer: "); - if (SG_LIB_UNBOUNDED_64BIT == ul) - printf("-1 [no limit]\n"); - else - printf("%" PRIu64 "\n", ul); - break; - default: - printf(" Unhandled descriptor (format %d, device type %d)\n", - bp[0] >> 5, bp[0] & 0x1F); - break; - } - } -} - -struct tpc_desc_type { - uint8_t code; - const char * name; -}; - -static struct tpc_desc_type tpc_desc_arr[] = { - {0x0, "block -> stream"}, - {0x1, "stream -> block"}, - {0x2, "block -> block"}, - {0x3, "stream -> stream"}, - {0x4, "inline -> stream"}, - {0x5, "embedded -> stream"}, - {0x6, "stream -> discard"}, - {0x7, "verify CSCD"}, - {0x8, "block<o> -> stream"}, - {0x9, "stream -> block<o>"}, - {0xa, "block<o> -> block<o>"}, - {0xb, "block -> stream & application_client"}, - {0xc, "stream -> block & application_client"}, - {0xd, "block -> block & application_client"}, - {0xe, "stream -> stream&application_client"}, - {0xf, "stream -> discard&application_client"}, - {0x10, "filemark -> tape"}, - {0x11, "space -> tape"}, /* obsolete: spc5r02 */ - {0x12, "locate -> tape"}, /* obsolete: spc5r02 */ - {0x13, "<i>tape -> <i>tape"}, - {0x14, "register persistent reservation key"}, - {0x15, "third party persistent reservation source I_T nexus"}, - {0x16, "<i>block -> <i>block"}, - {0x17, "positioning -> tape"}, /* this and next added spc5r02 */ - {0x18, "<loi>tape -> <loi>tape"}, /* loi: logical object identifier */ - {0xbe, "ROD <- block range(n)"}, - {0xbf, "ROD <- block range(1)"}, - {0xe0, "CSCD: FC N_Port_Name"}, - {0xe1, "CSCD: FC N_Port_ID"}, - {0xe2, "CSCD: FC N_Port_ID with N_Port_Name, checking"}, - {0xe3, "CSCD: Parallel interface: I_T"}, - {0xe4, "CSCD: Identification Descriptor"}, - {0xe5, "CSCD: IPv4"}, - {0xe6, "CSCD: Alias"}, - {0xe7, "CSCD: RDMA"}, - {0xe8, "CSCD: IEEE 1394 EUI-64"}, - {0xe9, "CSCD: SAS SSP"}, - {0xea, "CSCD: IPv6"}, - {0xeb, "CSCD: IP copy service"}, - {0xfe, "CSCD: ROD"}, - {0xff, "CSCD: extension"}, - {0x0, NULL}, -}; - -static const char * -get_tpc_desc_name(uint8_t code) -{ - const struct tpc_desc_type * dtp; - - for (dtp = tpc_desc_arr; dtp->name; ++dtp) { - if (code == dtp->code) - return dtp->name; - } - return ""; -} - -struct tpc_rod_type { - uint32_t type; - const char * name; -}; - -static struct tpc_rod_type tpc_rod_arr[] = { - {0x0, "copy manager internal"}, - {0x10000, "access upon reference"}, - {0x800000, "point in time copy - default"}, - {0x800001, "point in time copy - change vulnerable"}, - {0x800002, "point in time copy - persistent"}, - {0x80ffff, "point in time copy - any"}, - {0xffff0001, "block device zero"}, - {0x0, NULL}, -}; - -static const char * -get_tpc_rod_name(uint32_t rod_type) -{ - const struct tpc_rod_type * rtp; - - for (rtp = tpc_rod_arr; rtp->name; ++rtp) { - if (rod_type == rtp->type) - return rtp->name; - } - return ""; -} - -struct cscd_desc_id_t { - uint16_t id; - const char * name; -}; - -static struct cscd_desc_id_t cscd_desc_id_arr[] = { - /* only values higher than 0x7ff are listed */ - {0xc000, "copy src or dst null LU, pdt=0"}, - {0xc001, "copy src or dst null LU, pdt=1"}, - {0xf800, "copy src or dst in ROD token"}, - {0xffff, "copy src or dst is copy manager LU"}, - {0x0, NULL}, -}; - -static const char * -get_cscd_desc_id_name(uint16_t cscd_desc_id) -{ - const struct cscd_desc_id_t * cdip; - - for (cdip = cscd_desc_id_arr; cdip->name; ++cdip) { - if (cscd_desc_id == cdip->id) - return cdip->name; - } - return ""; -} - -/* VPD_3PARTY_COPY [3PC, third party copy] */ -static void -decode_3party_copy_vpd(uint8_t * buff, int len, int do_hex, int pdt, - int verbose) -{ - int j, k, m, bump, desc_type, desc_len, sa_len, blen; - unsigned int u; - const uint8_t * bp; - const char * cp; - uint64_t ull; - char b[120]; - - if (len < 4) { - pr2serr("Third-party Copy VPD page length too short=%d\n", len); - return; - } - if (3 == do_hex) { - hex2stdout(buff, len, -1); - return; - } - blen = sizeof(b); - len -= 4; - bp = buff + 4; - for (k = 0; k < len; k += bump, bp += bump) { - desc_type = sg_get_unaligned_be16(bp); - desc_len = sg_get_unaligned_be16(bp + 2); - if (verbose) - printf("Descriptor type=%d [0x%x] , len %d\n", desc_type, - desc_type, desc_len); - bump = 4 + desc_len; - if ((k + bump) > len) { - pr2serr("Third-party Copy VPD page, short descriptor length=%d, " - "left=%d\n", bump, (len - k)); - return; - } - if (0 == desc_len) - continue; - if (2 == do_hex) - hex2stdout(bp + 4, desc_len, 1); - else if (do_hex > 2) - hex2stdout(bp, bump, 1); - else { - int csll; - - switch (desc_type) { - case 0x0000: /* Required if POPULATE TOKEN (or friend) used */ - printf(" Block Device ROD Token Limits:\n"); - u = sg_get_unaligned_be16(bp + 10); - printf(" Maximum range descriptors: "); - if (0 == u) - printf("0 [not reported]\n"); - else - printf("%u\n", u); - u = sg_get_unaligned_be32(bp + 12); - printf(" Maximum inactivity timeout: "); - if (0 == u) - printf("0 [not reported]\n"); - else if (SG_LIB_UNBOUNDED_32BIT == u) - printf("-1 [no maximum given]\n"); - else - printf("%u seconds\n", u); - u = sg_get_unaligned_be32(bp + 16); - printf(" Default inactivity timeout: "); - if (0 == u) - printf("0 [not reported]\n"); - else - printf("%u seconds\n", u); - ull = sg_get_unaligned_be64(bp + 20); - printf(" Maximum token transfer size: "); - if (0 == ull) - printf("0 [not reported]\n"); - else - printf("%" PRIu64 "\n", ull); - ull = sg_get_unaligned_be64(bp + 28); - printf(" Optimal transfer count: "); - if (0 == ull) - printf("0 [not reported]\n"); - else - printf("%" PRIu64 "\n", ull); - break; - case 0x0001: /* Mandatory (SPC-4) */ - printf(" Supported commands:\n"); - j = 0; - csll = bp[4]; - if (csll >= desc_len) { - pr2serr("Command supported list length (%d) >= " - "descriptor length (%d), wrong so trim\n", - csll, desc_len); - csll = desc_len - 1; - } - while (j < csll) { - sa_len = bp[6 + j]; - for (m = 0; (m < sa_len) && ((j + m) < csll); ++m) { - sg_get_opcode_sa_name(bp[5 + j], bp[7 + j + m], - pdt, blen, b); - printf(" %s\n", b); - } - if (0 == sa_len) { - sg_get_opcode_name(bp[5 + j], pdt, blen, b); - printf(" %s\n", b); - } else if (m < sa_len) - pr2serr("Supported service actions list length (%d) " - "is too large\n", sa_len); - j += m + 2; - } - break; - case 0x0004: - printf(" Parameter data:\n"); - printf(" Maximum CSCD descriptor count: %d\n", - sg_get_unaligned_be16(bp + 8)); - printf(" Maximum segment descriptor count: %d\n", - sg_get_unaligned_be16(bp + 10)); - u = sg_get_unaligned_be32(bp + 12); - printf(" Maximum descriptor list length: %u\n", u); - u = sg_get_unaligned_be32(bp + 16); - printf(" Maximum inline data length: %u\n", u); - break; - case 0x0008: - printf(" Supported descriptors:\n"); - for (j = 0; j < bp[4]; j++) { - cp = get_tpc_desc_name(bp[5 + j]); - if (strlen(cp) > 0) - printf(" %s [0x%x]\n", cp, bp[5 + j]); - else - printf(" 0x%x\n", bp[5 + j]); - } - break; - case 0x000C: - printf(" Supported CSCD IDs (above 0x7ff):\n"); - for (j = 0; j < sg_get_unaligned_be16(bp + 4); j += 2) { - u = sg_get_unaligned_be16(bp + 6 + j); - cp = get_cscd_desc_id_name(u); - if (strlen(cp) > 0) - printf(" %s [0x%04x]\n", cp, u); - else - printf(" 0x%04x\n", u); - } - break; - case 0x000D: - printf(" Copy group identifier:\n"); - u = bp[4]; - sg_t10_uuid_desig2str(bp + 5, u, 1 /* c_set */, false, - false, NULL, blen, b); - printf("%s", b); - break; - case 0x0106: - printf(" ROD token features:\n"); - printf(" Remote tokens: %d\n", bp[4] & 0x0f); - u = sg_get_unaligned_be32(bp + 16); - printf(" Minimum token lifetime: %u seconds\n", u); - u = sg_get_unaligned_be32(bp + 20); - printf(" Maximum token lifetime: %u seconds\n", u); - u = sg_get_unaligned_be32(bp + 24); - printf(" Maximum token inactivity timeout: %u\n", u); - decode_rod_descriptor(bp + 48, - sg_get_unaligned_be16(bp + 46)); - break; - case 0x0108: - printf(" Supported ROD token and ROD types:\n"); - for (j = 0; j < sg_get_unaligned_be16(bp + 6); j+= 64) { - u = sg_get_unaligned_be32(bp + 8 + j); - cp = get_tpc_rod_name(u); - if (strlen(cp) > 0) - printf(" ROD type: %s [0x%x]\n", cp, u); - else - printf(" ROD type: 0x%x\n", u); - printf(" Internal: %s\n", - (bp[8 + j + 4] & 0x80) ? "yes" : "no"); - printf(" Token in: %s\n", - (bp[8 + j + 4] & 0x02) ? "yes" : "no"); - printf(" Token out: %s\n", - (bp[8 + j + 4] & 0x01) ? "yes" : "no"); - printf(" Preference: %d\n", - sg_get_unaligned_be16(bp + 8 + j + 6)); - } - break; - case 0x8001: /* Mandatory (SPC-4) */ - printf(" General copy operations:\n"); - u = sg_get_unaligned_be32(bp + 4); - printf(" Total concurrent copies: %u\n", u); - u = sg_get_unaligned_be32(bp + 8); - printf(" Maximum identified concurrent copies: %u\n", u); - u = sg_get_unaligned_be32(bp + 12); - printf(" Maximum segment length: %u\n", u); - printf(" Data segment granularity: "); - u = bp[16]; /* field is power of 2 */ - if (u < 64) - printf("%" PRIu64 "\n", (uint64_t)1 << u); - else - printf("too large [2^%u]\n", u); - printf(" Inline data granularity: "); - u = bp[17]; /* field is power of 2 */ - if (u < 64) - printf("%" PRIu64 "\n", (uint64_t)1 << u); - else - printf("too large [2^%u]\n", u); - break; - case 0x9101: - printf(" Stream copy operations:\n"); - u = sg_get_unaligned_be32(bp + 4); - printf(" Maximum stream device transfer size: %u\n", u); - break; - case 0xC001: - printf(" Held data:\n"); - u = sg_get_unaligned_be32(bp + 4); - printf(" Held data limit: %u\n", u); - ull = ((uint64_t)1 << bp[8]); - printf(" Held data granularity: %" PRIu64 "\n", ull); - break; - default: - pr2serr("Unexpected type=%d\n", desc_type); - hex2stderr(bp, bump, 1); - break; - } - } - } -} - -/* VPD_PROTO_LU */ -static void -decode_proto_lu_vpd(uint8_t * buff, int len, int do_hex) -{ - int k, bump, rel_port, desc_len, proto; - uint8_t * bp; - - if ((1 == do_hex) || (do_hex > 2)) { - hex2stdout(buff, len, (1 == do_hex) ? 1 : -1); - return; - } - if (len < 4) { - pr2serr("Protocol-specific logical unit information VPD page length " - "too short=%d\n", len); - return; - } - len -= 4; - bp = buff + 4; - for (k = 0; k < len; k += bump, bp += bump) { - rel_port = sg_get_unaligned_be16(bp); - printf(" Relative port=%d\n", rel_port); - proto = bp[2] & 0xf; - desc_len = sg_get_unaligned_be16(bp + 6); - bump = 8 + desc_len; - if ((k + bump) > len) { - pr2serr("Protocol-specific logical unit information VPD page, " - "short descriptor length=%d, left=%d\n", bump, (len - k)); - return; - } - if (0 == desc_len) - continue; - if (2 == do_hex) { - hex2stdout(bp + 8, desc_len, 1); - continue; - } - switch (proto) { - case TPROTO_SAS: - printf(" Protocol identifier: SAS\n"); - printf(" TLR control supported: %d\n", !!(bp[8] & 0x1)); - break; - default: - pr2serr("Unexpected proto=%d\n", proto); - hex2stderr(bp, bump, 1); - break; - } - } -} - -/* VPD_PROTO_PORT */ -static void -decode_proto_port_vpd(uint8_t * buff, int len, int do_hex) -{ - int k, j, bump, rel_port, desc_len, proto; - uint8_t * bp; - uint8_t * pidp; - - if ((1 == do_hex) || (do_hex > 2)) { - hex2stdout(buff, len, (1 == do_hex) ? 1 : -1); - return; - } - if (len < 4) { - pr2serr("Protocol-specific port information VPD page length too " - "short=%d\n", len); - return; - } - len -= 4; - bp = buff + 4; - for (k = 0; k < len; k += bump, bp += bump) { - rel_port = sg_get_unaligned_be16(bp); - printf(" Relative port=%d\n", rel_port); - proto = bp[2] & 0xf; - desc_len = sg_get_unaligned_be16(bp + 6); - bump = 8 + desc_len; - if ((k + bump) > len) { - pr2serr("Protocol-specific port VPD page, short descriptor " - "length=%d, left=%d\n", bump, (len - k)); - return; - } - if (0 == desc_len) - continue; - if (2 == do_hex) { - hex2stdout(bp + 8, desc_len, 1); - continue; - } - switch (proto) { - case TPROTO_SAS: /* page added in spl3r02 */ - printf(" power disable supported (pwr_d_s)=%d\n", - !!(bp[3] & 0x1)); /* added spl3r03 */ - pidp = bp + 8; - for (j = 0; j < desc_len; j += 4, pidp += 4) - printf(" phy id=%d, SSP persistent capable=%d\n", - pidp[1], (0x1 & pidp[2])); - break; - default: - pr2serr("Unexpected proto=%d\n", proto); - hex2stderr(bp, bump, 1); - break; - } - } -} - /* VPD_BLOCK_LIMITS sbc */ /* VPD_SA_DEV_CAP ssc */ /* VPD_OSD_INFO osd */ @@ -2343,60 +1841,73 @@ svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop, } break; case VPD_3PARTY_COPY: /* 0x8f */ - np = "Third party copy VPD page:"; + np = "Third party copy VPD page"; /* ["tpc"] */ if (allow_name) - printf("%s%s\n", pre, np); + sgj_pr_hr(jsp, "%s%s:\n", pre, np); res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { if (! allow_name && allow_if_found) - printf("%s%s\n", pre, np); + sgj_pr_hr(jsp, "%s%s\n", pre, np); if (op->do_raw) dStrRaw(rp, len); - else if (1 == op->do_hex) - hex2stdout(rp, len, 0); else { if (vb || long_notquiet) - printf(" [PQual=%d Peripheral device type: %s]\n", - pqual, pdt_str); - decode_3party_copy_vpd(rp, len, op->do_hex, pdt, vb); + 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 */ - np = "Protocol-specific logical unit information:"; + case VPD_PROTO_LU: /* 0x90 ["pslu"] */ + np = "Protocol-specific logical unit information VPD page"; if (allow_name) - printf("%s%s\n", pre, np); + sgj_pr_hr(jsp, "%s%s:\n", pre, np); res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { if (! allow_name && allow_if_found) - printf("%s%s\n", pre, np); + sgj_pr_hr(jsp, "%s%s:\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) - printf(" [PQual=%d Peripheral device type: %s]\n", - pqual, pdt_str); - decode_proto_lu_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, + "logical_unit_information_descriptor_list"); + } + decode_proto_lu_vpd(rp, len, op, jap); } return 0; } break; - case VPD_PROTO_PORT: /* 0x91 */ - np = "Protocol-specific port information:"; + case VPD_PROTO_PORT: /* 0x91 ["pspo"] */ + np = "Protocol-specific port VPD page"; if (allow_name) - printf("%s%s\n", pre, np); + sgj_pr_hr(jsp, "%s%s\n", pre, np); res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len); if (0 == res) { if (! allow_name && allow_if_found) - printf("%s%s\n", pre, np); + sgj_pr_hr(jsp, "%s%s:\n", pre, np); if (op->do_raw) dStrRaw(rp, len); else { if (vb || long_notquiet) - printf(" [PQual=%d Peripheral device type: %s]\n", - pqual, pdt_str); - decode_proto_port_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, + "port_information_descriptor_list"); + } + decode_proto_port_vpd(rp, len, op, jsp); } return 0; } diff --git a/src/sg_vpd_common.c b/src/sg_vpd_common.c index 9497de3d..5cc33f68 100644 --- a/src/sg_vpd_common.c +++ b/src/sg_vpd_common.c @@ -43,6 +43,8 @@ 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 y_s = "yes"; +static const char * const n_s = "no"; 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"; @@ -1865,7 +1867,7 @@ decode_format_presets_vpd(uint8_t * buff, int len, struct opts_t * op, 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)"); + "ForMaT Protection 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", @@ -1997,3 +1999,684 @@ decode_con_pos_range_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); } } + +/* This is xcopy(LID4) related: "ROD" == Representation Of Data + * Used by VPD_3PARTY_COPY 0x8f ["tpc"] */ +static void +decode_rod_descriptor(const uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jap) +{ + uint8_t pdt; + uint32_t u; + int k, bump; + uint64_t ull; + const uint8_t * bp = buff; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p; + static const char * ab_pdt = "abnormal use of 'pdt'"; + + for (k = 0; k < len; k += bump, bp += bump) { + jo2p = sgj_new_unattached_object_r(jsp); + bump = sg_get_unaligned_be16(bp + 2) + 4; + pdt = 0x1f & bp[0]; + u = (bp[0] >> 5) & 0x7; + sgj_js_nv_i(jsp, jo2p, "descriptor_format", u); + if (0 != u) { + sgj_pr_hr(jsp, " Unhandled descriptor (format %u, device type " + "%u)\n", u, pdt); + sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); + break; + } + switch (pdt) { + case 0: + /* Block ROD device type specific descriptor */ + sgj_js_nv_ihexstr_nex(jsp, jo2p, "peripheral_device_type", + pdt, false, NULL, "Block ROD device " + "type specific descriptor", ab_pdt); + sgj_hr_js_vi_nex(jsp, jo2p, 4, "Optimal block ROD length " + "granularity", SGJ_SEP_COLON_1_SPACE, + sg_get_unaligned_be16(bp + 6), true, "unit: LB"); + ull = sg_get_unaligned_be64(bp + 8); + sgj_hr_js_vi(jsp, jo2p, 4, "Maximum bytes in block ROD", + SGJ_SEP_COLON_1_SPACE, ull, true); + ull = sg_get_unaligned_be64(bp + 16); + sgj_hr_js_vistr(jsp, jo2p, 4, "Optimal Bytes in block ROD " + "transfer", SGJ_SEP_COLON_1_SPACE, ull, true, + (SG_LIB_UNBOUNDED_64BIT == ull) ? nl_s : NULL); + ull = sg_get_unaligned_be64(bp + 24); + sgj_hr_js_vistr(jsp, jo2p, 4, "Optimal Bytes to token per " + "segment", SGJ_SEP_COLON_1_SPACE, ull, true, + (SG_LIB_UNBOUNDED_64BIT == ull) ? nl_s : NULL); + ull = sg_get_unaligned_be64(bp + 32); + sgj_hr_js_vistr(jsp, jo2p, 4, "Optimal Bytes from token per " + "segment", SGJ_SEP_COLON_1_SPACE, ull, true, + (SG_LIB_UNBOUNDED_64BIT == ull) ? nl_s : NULL); + break; + case 1: + /* Stream ROD device type specific descriptor */ + sgj_js_nv_ihexstr_nex(jsp, jo2p, "peripheral_device_type", + pdt, false, NULL, "Stream ROD device " + "type specific descriptor", ab_pdt); + ull = sg_get_unaligned_be64(bp + 8); + sgj_hr_js_vi(jsp, jo2p, 4, "Maximum bytes in stream ROD", + SGJ_SEP_COLON_1_SPACE, ull, true); + ull = sg_get_unaligned_be64(bp + 16); + printf(" Optimal Bytes in stream ROD transfer: "); + if (SG_LIB_UNBOUNDED_64BIT == ull) + printf("-1 [no limit]\n"); + else + printf("%" PRIu64 "\n", ull); + break; + case 3: + /* Copy manager ROD device type specific descriptor */ + sgj_js_nv_ihexstr_nex(jsp, jo2p, "peripheral_device_type", + pdt, false, NULL, "Copy manager ROD " + "device type specific descriptor", + ab_pdt); + printf(" Maximum Bytes in processor ROD: %" PRIu64 "\n", + sg_get_unaligned_be64(bp + 8)); + ull = sg_get_unaligned_be64(bp + 16); + printf(" Optimal Bytes in processor ROD transfer: "); + if (SG_LIB_UNBOUNDED_64BIT == ull) + printf("-1 [no limit]\n"); + else + printf("%" PRIu64 "\n", ull); + break; + default: + sgj_js_nv_ihexstr(jsp, jo2p, "peripheral_device_type", + pdt, false, "unknown"); + break; + } + sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); + } +} + +struct tpc_desc_type { + uint8_t code; + const char * name; +}; + +static struct tpc_desc_type tpc_desc_arr[] = { + {0x0, "block -> stream"}, + {0x1, "stream -> block"}, + {0x2, "block -> block"}, + {0x3, "stream -> stream"}, + {0x4, "inline -> stream"}, + {0x5, "embedded -> stream"}, + {0x6, "stream -> discard"}, + {0x7, "verify CSCD"}, + {0x8, "block<o> -> stream"}, + {0x9, "stream -> block<o>"}, + {0xa, "block<o> -> block<o>"}, + {0xb, "block -> stream & application_client"}, + {0xc, "stream -> block & application_client"}, + {0xd, "block -> block & application_client"}, + {0xe, "stream -> stream&application_client"}, + {0xf, "stream -> discard&application_client"}, + {0x10, "filemark -> tape"}, + {0x11, "space -> tape"}, /* obsolete: spc5r02 */ + {0x12, "locate -> tape"}, /* obsolete: spc5r02 */ + {0x13, "<i>tape -> <i>tape"}, + {0x14, "register persistent reservation key"}, + {0x15, "third party persistent reservation source I_T nexus"}, + {0x16, "<i>block -> <i>block"}, + {0x17, "positioning -> tape"}, /* this and next added spc5r02 */ + {0x18, "<loi>tape -> <loi>tape"}, /* loi: logical object identifier */ + {0xbe, "ROD <- block range(n)"}, + {0xbf, "ROD <- block range(1)"}, + {0xe0, "CSCD: FC N_Port_Name"}, + {0xe1, "CSCD: FC N_Port_ID"}, + {0xe2, "CSCD: FC N_Port_ID with N_Port_Name, checking"}, + {0xe3, "CSCD: Parallel interface: I_T"}, + {0xe4, "CSCD: Identification Descriptor"}, + {0xe5, "CSCD: IPv4"}, + {0xe6, "CSCD: Alias"}, + {0xe7, "CSCD: RDMA"}, + {0xe8, "CSCD: IEEE 1394 EUI-64"}, + {0xe9, "CSCD: SAS SSP"}, + {0xea, "CSCD: IPv6"}, + {0xeb, "CSCD: IP copy service"}, + {0xfe, "CSCD: ROD"}, + {0xff, "CSCD: extension"}, + {0x0, NULL}, +}; + +static const char * +get_tpc_desc_name(uint8_t code) +{ + const struct tpc_desc_type * dtp; + + for (dtp = tpc_desc_arr; dtp->name; ++dtp) { + if (code == dtp->code) + return dtp->name; + } + return ""; +} + +struct tpc_rod_type { + uint32_t type; + const char * name; +}; + +static struct tpc_rod_type tpc_rod_arr[] = { + {0x0, "copy manager internal"}, + {0x10000, "access upon reference"}, + {0x800000, "point in time copy - default"}, + {0x800001, "point in time copy - change vulnerable"}, + {0x800002, "point in time copy - persistent"}, + {0x80ffff, "point in time copy - any"}, + {0xffff0001, "block device zero"}, + {0x0, NULL}, +}; + +static const char * +get_tpc_rod_name(uint32_t rod_type) +{ + const struct tpc_rod_type * rtp; + + for (rtp = tpc_rod_arr; rtp->name; ++rtp) { + if (rod_type == rtp->type) + return rtp->name; + } + return ""; +} + +struct cscd_desc_id_t { + uint16_t id; + const char * name; +}; + +static struct cscd_desc_id_t cscd_desc_id_arr[] = { + /* only values higher than 0x7ff are listed */ + {0xc000, "copy src or dst null LU, pdt=0"}, + {0xc001, "copy src or dst null LU, pdt=1"}, + {0xf800, "copy src or dst in ROD token"}, + {0xffff, "copy src or dst is copy manager LU"}, + {0x0, NULL}, +}; + +static const char * +get_cscd_desc_id_name(uint16_t cscd_desc_id) +{ + const struct cscd_desc_id_t * cdip; + + for (cdip = cscd_desc_id_arr; cdip->name; ++cdip) { + if (cscd_desc_id == cdip->id) + return cdip->name; + } + return ""; +} + +static const char * +get_tpc_desc_type_s(uint32_t desc_type) +{ + switch(desc_type) { + case 0: + return "Block Device ROD Limits"; + case 1: + return "Supported Commands"; + case 4: + return "Parameter Data"; + case 8: + return "Supported Descriptors"; + case 0xc: + return "Supported CSCD Descriptor IDs"; + case 0xd: + return "Copy Group Identifier"; + case 0x106: + return "ROD Token Features"; + case 0x108: + return "Supported ROD Token and ROD Types"; + case 0x8001: + return "General Copy Operations"; + case 0x9101: + return "Stream Copy Operations"; + case 0xC001: + return "Held Data"; + default: + if ((desc_type >= 0xE000) && (desc_type <= 0xEFFF)) + return "Restricted"; + else + return "Reserved"; + } +} + +/* VPD_3PARTY_COPY 3PC, third party copy 0x8f ["tpc"] */ +void +decode_3party_copy_vpd(uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jap) +{ + int j, k, m, bump, desc_type, desc_len, sa_len, pdt; + uint32_t u, v; + uint64_t ull; + const uint8_t * bp; + const char * cp; + const char * dtp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p = NULL; + sgj_opaque_p ja2p = NULL; + sgj_opaque_p jo3p = NULL; + char b[144]; + static const int blen = sizeof(b); + + if (len < 4) { + pr2serr("VPD page length too short=%d\n", len); + return; + } + if (3 == op->do_hex) { + hex2stdout(buff, len, -1); + return; + } + pdt = buff[0] & PDT_MASK; + len -= 4; + bp = buff + 4; + for (k = 0; k < len; k += bump, bp += bump) { + jo2p = sgj_new_unattached_object_r(jsp); + desc_type = sg_get_unaligned_be16(bp); + desc_len = sg_get_unaligned_be16(bp + 2); + if (op->verbose) + sgj_pr_hr(jsp, "Descriptor type=%d [0x%x] , len %d\n", desc_type, + desc_type, desc_len); + bump = 4 + desc_len; + if ((k + bump) > len) { + pr2serr("VPD page, short descriptor length=%d, left=%d\n", bump, + (len - k)); + break; + } + if (0 == desc_len) + goto skip; /* continue plus attach jo2p */ + if (2 == op->do_hex) + hex2stdout(bp + 4, desc_len, 1); + else if (op->do_hex > 2) + hex2stdout(bp, bump, 1); + else { + int csll; + + dtp = get_tpc_desc_type_s(desc_type); + sgj_js_nv_ihexstr(jsp, jo2p, "third_party_copy_descriptor_type", + desc_type, NULL, dtp); + sgj_js_nv_ihex(jsp, jo2p, "third_party_copy_descriptor_length", + desc_len); + + switch (desc_type) { + case 0x0000: /* Required if POPULATE TOKEN (or friend) used */ + sgj_pr_hr(jsp, " %s:\n", dtp); + u = sg_get_unaligned_be16(bp + 10); + sgj_hr_js_vistr(jsp, jo2p, 2, "Maximum range descriptors", + SGJ_SEP_COLON_1_SPACE, u, true, + (0 == u) ? nr_s : NULL); + u = sg_get_unaligned_be32(bp + 12); + if (0 == u) + cp = nr_s; + else if (SG_LIB_UNBOUNDED_32BIT == u) + cp = "No maximum given"; + else + cp = NULL; + sgj_hr_js_vistr_nex(jsp, jo2p, 2, "Maximum inactivity " + "timeout", SGJ_SEP_COLON_1_SPACE, u, true, + cp, "unit: second"); + u = sg_get_unaligned_be32(bp + 16); + sgj_hr_js_vistr_nex(jsp, jo2p, 2, "Default inactivity " + "timeout", SGJ_SEP_COLON_1_SPACE, u, true, + (0 == u) ? nr_s : NULL, "unit: second"); + ull = sg_get_unaligned_be64(bp + 20); + sgj_hr_js_vistr_nex(jsp, jo2p, 2, "Maximum token transfer " + "size", SGJ_SEP_COLON_1_SPACE, ull, true, + (0 == ull) ? nr_s : NULL, "unit: LB"); + ull = sg_get_unaligned_be64(bp + 28); + sgj_hr_js_vistr_nex(jsp, jo2p, 2, "Optimal transfer count", + SGJ_SEP_COLON_1_SPACE, ull, true, + (0 == ull) ? nr_s : NULL, "unit: LB"); + break; + case 0x0001: /* Mandatory (SPC-4) */ + sgj_pr_hr(jsp, " %s:\n", "Commands supported list"); + ja2p = sgj_named_subarray_r(jsp, jo2p, + "commands_supported_list"); + j = 0; + csll = bp[4]; + if (csll >= desc_len) { + pr2serr("Command supported list length (%d) >= " + "descriptor length (%d), wrong so trim\n", + csll, desc_len); + csll = desc_len - 1; + } + while (j < csll) { + uint8_t opc, sa; + static const char * soc = "supported_operation_code"; + static const char * ssa = "supported_service_action"; + + jo3p = NULL; + opc = bp[5 + j]; + sa_len = bp[6 + j]; + for (m = 0; (m < sa_len) && ((j + m) < csll); ++m) { + jo3p = sgj_new_unattached_object_r(jsp); + sa = bp[7 + j + m]; + sg_get_opcode_sa_name(opc, sa, pdt, blen, b); + sgj_pr_hr(jsp, " %s\n", b); + sgj_js_nv_s(jsp, jo3p, "name", b); + sgj_js_nv_ihex(jsp, jo3p, soc, opc); + sgj_js_nv_ihex(jsp, jo3p, ssa, sa); + sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p); + } + if (0 == sa_len) { + jo3p = sgj_new_unattached_object_r(jsp); + sg_get_opcode_name(opc, pdt, blen, b); + sgj_pr_hr(jsp, " %s\n", b); + sgj_js_nv_s(jsp, jo3p, "name", b); + sgj_js_nv_ihex(jsp, jo3p, soc, opc); + sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p); + } else if (m < sa_len) + pr2serr("Supported service actions list length (%d) " + "is too large\n", sa_len); + j += m + 2; + } + break; + case 0x0004: + sgj_pr_hr(jsp, " %s:\n", dtp); + sgj_hr_js_vi(jsp, jo2p, 2, "Maximum CSCD descriptor count", + SGJ_SEP_COLON_1_SPACE, + sg_get_unaligned_be16(bp + 8), true); + sgj_hr_js_vi(jsp, jo2p, 2, "Maximum segment descriptor count", + SGJ_SEP_COLON_1_SPACE, + sg_get_unaligned_be16(bp + 10), true); + sgj_hr_js_vi(jsp, jo2p, 2, "Maximum descriptor list length", + SGJ_SEP_COLON_1_SPACE, + sg_get_unaligned_be32(bp + 12), true); + sgj_hr_js_vi(jsp, jo2p, 2, "Maximum inline data length", + SGJ_SEP_COLON_1_SPACE, + sg_get_unaligned_be32(bp + 17), true); + break; + case 0x0008: + sgj_pr_hr(jsp, " Supported descriptors:\n"); + ja2p = sgj_named_subarray_r(jsp, jo2p, + "supported_descriptor_list"); + for (j = 0; j < bp[4]; j++) { + bool found_name; + + jo3p = sgj_new_unattached_object_r(jsp); + u = bp[5 + j]; + cp = get_tpc_desc_name(u); + found_name = (strlen(cp) > 0); + if (found_name) + sgj_pr_hr(jsp, " %s [0x%x]\n", cp, u); + else + sgj_pr_hr(jsp, " 0x%x\n", u); + sgj_js_nv_s(jsp, jo3p, "name", found_name ? cp : nr_s); + sgj_js_nv_ihex(jsp, jo3p, "code", u); + sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p); + } + break; + case 0x000C: + sgj_pr_hr(jsp, " Supported CSCD IDs (above 0x7ff):\n"); + ja2p = sgj_named_subarray_r(jsp, jo2p, "supported_cscd_" + "descriptor_id_list"); + v = sg_get_unaligned_be16(bp + 4); + for (j = 0; j < (int)v; j += 2) { + bool found_name; + + jo3p = sgj_new_unattached_object_r(jsp); + u = sg_get_unaligned_be16(bp + 6 + j); + cp = get_cscd_desc_id_name(u); + found_name = (strlen(cp) > 0); + if (found_name) + sgj_pr_hr(jsp, " %s [0x%04x]\n", cp, u); + else + sgj_pr_hr(jsp, " 0x%04x\n", u); + sgj_js_nv_s(jsp, jo3p, "name", found_name ? cp : nr_s); + sgj_js_nv_ihex(jsp, jo3p, "id", u); + sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p); + } + break; + case 0x000D: + sgj_pr_hr(jsp, " Copy group identifier:\n"); + u = bp[4]; + sg_t10_uuid_desig2str(bp + 5, u, 1 /* c_set */, false, + true, NULL, blen, b); + sgj_pr_hr(jsp, " Locally assigned UUID: %s", b); + sgj_js_nv_s(jsp, jo2p, "locally_assigned_uuid", b); + break; + case 0x0106: + sgj_pr_hr(jsp, " ROD token features:\n"); + sgj_hr_js_vi(jsp, jo2p, 2, "Remote tokens", + SGJ_SEP_COLON_1_SPACE, bp[4] & 0x0f, true); + u = sg_get_unaligned_be32(bp + 16); + sgj_pr_hr(jsp, " Minimum token lifetime: %u seconds\n", u); + sgj_js_nv_ihex_nex(jsp, jo2p, "minimum_token_lifetime", u, + true, "unit: second"); + u = sg_get_unaligned_be32(bp + 20); + sgj_pr_hr(jsp, " Maximum token lifetime: %u seconds\n", u); + sgj_js_nv_ihex_nex(jsp, jo2p, "maximum_token_lifetime", u, + true, "unit: second"); + u = sg_get_unaligned_be32(bp + 24); + sgj_hr_js_vi_nex(jsp, jo2p, 2, "Maximum token inactivity " + "timeout", SGJ_SEP_COLON_1_SPACE, u, + true, "unit: second"); + u = sg_get_unaligned_be16(bp + 46); + ja2p = sgj_named_subarray_r(jsp, jo2p, + "rod_device_type_specific_features_descriptor_list"); + decode_rod_descriptor(bp + 48, u, op, ja2p); + break; + case 0x0108: + sgj_pr_hr(jsp, " Supported ROD token and ROD types:\n"); + ja2p = sgj_named_subarray_r(jsp, jo2p, "rod_type_" + "descriptor_list"); + for (j = 0; j < sg_get_unaligned_be16(bp + 6); j+= 64) { + bool found_name; + + jo3p = sgj_new_unattached_object_r(jsp); + u = sg_get_unaligned_be32(bp + 8 + j); + cp = get_tpc_rod_name(u); + found_name = (strlen(cp) > 0); + if (found_name > 0) + sgj_pr_hr(jsp, " ROD type: %s [0x%x]\n", cp, u); + else + sgj_pr_hr(jsp, " ROD type: 0x%x\n", u); + sgj_js_nv_ihexstr(jsp, jo3p, "rod_type", u, NULL, + found_name ? cp : NULL); + u = bp[8 + j + 4]; + sgj_pr_hr(jsp, " ECPY_INT: %s\n", + (u & 0x80) ? y_s : n_s); + sgj_js_nv_ihex_nex(jsp, jo3p, "ecpy_int", !!(0x80 & u), + false, "Extended CoPY INTernal rods"); + sgj_pr_hr(jsp, " Token in: %s\n", + (u & 0x2) ? y_s : n_s); + sgj_js_nv_i(jsp, jo3p, "token_in", !!(0x2 & u)); + sgj_pr_hr(jsp, " Token out: %s\n", + (u & 0x1) ? y_s : n_s); + sgj_js_nv_i(jsp, jo3p, "token_out", !!(0x2 & u)); + u = sg_get_unaligned_be16(bp + 8 + j + 6); + sgj_hr_js_vi(jsp, jo3p, 4, "Preference indicator", + SGJ_SEP_COLON_1_SPACE, u, true); + sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p); + } + break; + case 0x8001: /* Mandatory (SPC-4) */ + sgj_pr_hr(jsp, " General copy operations:\n"); + u = sg_get_unaligned_be32(bp + 4); + sgj_hr_js_vi(jsp, jo2p, 2, "Total concurrent copies", + SGJ_SEP_COLON_1_SPACE, u, true); + u = sg_get_unaligned_be32(bp + 8); + sgj_hr_js_vi(jsp, jo2p, 2, "Maximum identified concurrent " + "copies", SGJ_SEP_COLON_1_SPACE, u, true); + u = sg_get_unaligned_be32(bp + 12); + sgj_hr_js_vi_nex(jsp, jo2p, 2, "Maximum segment length", + SGJ_SEP_COLON_1_SPACE, u, true, + "unit: byte"); + u = bp[16]; /* field is power of 2 */ + sgj_hr_js_vi_nex(jsp, jo2p, 2, "Data segment granularity", + SGJ_SEP_COLON_1_SPACE, u, true, + "unit: 2^val LB"); + u = bp[17]; /* field is power of 2 */ + sgj_hr_js_vi_nex(jsp, jo2p, 2, "Inline data granularity", + SGJ_SEP_COLON_1_SPACE, u, true, + "unit: 2^val LB"); + break; + case 0x9101: + sgj_pr_hr(jsp, " Stream copy operations:\n"); + u = sg_get_unaligned_be32(bp + 4); + sgj_hr_js_vi_nex(jsp, jo2p, 2, "Maximum stream device " + "transfer size", SGJ_SEP_COLON_1_SPACE, u, + true, "unit: byte"); + break; + case 0xC001: + sgj_pr_hr(jsp, " Held data:\n"); + u = sg_get_unaligned_be32(bp + 4); + sgj_hr_js_vi_nex(jsp, jo2p, 2, "Held data limit", + SGJ_SEP_COLON_1_SPACE, u, true, + "unit: byte; (lower limit: minimum)"); + sgj_hr_js_vi_nex(jsp, jo2p, 2, "Held data granularity", + SGJ_SEP_COLON_1_SPACE, bp[8], true, + "unit: 2^val byte"); + break; + default: + pr2serr("Unexpected type=%d\n", desc_type); + hex2stderr(bp, bump, 1); + break; + } + } +skip: + sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); + jo2p = NULL; + } + if (jo2p) + sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); +} + +/* VPD_PROTO_LU 0x90 ["pslu"] */ +void +decode_proto_lu_vpd(uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jap) +{ + int k, bump, rel_port, desc_len, proto; + uint8_t * bp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p = NULL; + char b[128]; + static const int blen = sizeof(b); + + if ((1 == op->do_hex) || (op->do_hex > 2)) { + hex2stdout(buff, len, (1 == op->do_hex) ? 1 : -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 += bump, bp += bump) { + jo2p = sgj_new_unattached_object_r(jsp); + rel_port = sg_get_unaligned_be16(bp); + sgj_hr_js_vi(jsp, jo2p, 2, "Relative port", + SGJ_SEP_COLON_1_SPACE, rel_port, true); + proto = bp[2] & 0xf; + sg_get_trans_proto_str(proto, blen, b); + sgj_hr_js_vistr(jsp, jo2p, 4, "Protocol identifier", + SGJ_SEP_COLON_1_SPACE, proto, false, b); + desc_len = sg_get_unaligned_be16(bp + 6); + bump = 8 + desc_len; + if ((k + bump) > len) { + pr2serr("Protocol-specific logical unit information VPD page, " + "short descriptor length=%d, left=%d\n", bump, (len - k)); + sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); + return; + } + if (0 == desc_len) + goto again;; + if (2 == op->do_hex) { + hex2stdout(bp + 8, desc_len, 1); + goto again;; + } + switch (proto) { + case TPROTO_SAS: + sgj_hr_js_vi(jsp, jo2p, 2, "TLR control supported", + SGJ_SEP_COLON_1_SPACE, !!(bp[8] & 0x1), false); + break; + default: + pr2serr("Unexpected proto=%d\n", proto); + hex2stderr(bp, bump, 1); + break; + } +again: + sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); + } +} + +/* VPD_PROTO_PORT 0x91 ["pspo"] */ +void +decode_proto_port_vpd(uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jap) +{ + bool pds, ssp_pers; + int k, j, bump, rel_port, desc_len, proto, phy; + uint8_t * bp; + uint8_t * pidp; + sgj_state * jsp = &op->json_st; + sgj_opaque_p jo2p = NULL; + sgj_opaque_p ja2p = NULL; + sgj_opaque_p jo3p = NULL; + char b[128]; + static const int blen = sizeof(b); + + if ((1 == op->do_hex) || (op->do_hex > 2)) { + hex2stdout(buff, len, (1 == op->do_hex) ? 1 : -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 += bump, bp += bump) { + jo2p = sgj_new_unattached_object_r(jsp); + rel_port = sg_get_unaligned_be16(bp); + sgj_hr_js_vi(jsp, jo2p, 2, "Relative port", + SGJ_SEP_COLON_1_SPACE, rel_port, true); + proto = bp[2] & 0xf; + sg_get_trans_proto_str(proto, blen, b); + sgj_hr_js_vistr(jsp, jo2p, 4, "Protocol identifier", + SGJ_SEP_COLON_1_SPACE, proto, false, b); + desc_len = sg_get_unaligned_be16(bp + 6); + bump = 8 + desc_len; + if ((k + bump) > len) { + pr2serr("VPD page, short descriptor length=%d, left=%d\n", + bump, (len - k)); + sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); + return; + } + if (0 == desc_len) + goto again; + if (2 == op->do_hex) { + hex2stdout(bp + 8, desc_len, 1); + goto again; + } + switch (proto) { + case TPROTO_SAS: /* page added in spl3r02 */ + pds = !!(bp[3] & 0x1); + sgj_pr_hr(jsp, " power disable supported (pwr_d_s)=%d\n", pds); + sgj_js_nv_ihex_nex(jsp, jo2p, "pwr_d_s", pds, false, + "PoWeR Disable Supported"); + ja2p = sgj_named_subarray_r(jsp, jo2p, + "sas_phy_information_descriptor_list"); + pidp = bp + 8; + for (j = 0; j < desc_len; j += 4, pidp += 4) { + jo3p = sgj_new_unattached_object_r(jsp); + phy = pidp[1]; + ssp_pers = !!(0x1 & pidp[2]); + sgj_pr_hr(jsp, " phy id=%d, SSP persistent capable=%d\n", + phy, ssp_pers); + sgj_js_nv_ihex(jsp, jo3p, "phy_identifier", phy); + sgj_js_nv_i(jsp, jo3p, "ssp_persistent_capable", ssp_pers); + sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p); + } + break; + default: + pr2serr("Unexpected proto=%d\n", proto); + hex2stderr(bp, bump, 1); + break; + } +again: + sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p); + } +} diff --git a/src/sg_vpd_common.h b/src/sg_vpd_common.h index 73233524..bc2bbfac 100644 --- a/src/sg_vpd_common.h +++ b/src/sg_vpd_common.h @@ -42,7 +42,7 @@ extern "C" { #define VPD_DEVICE_CONSTITUENTS 0x8b #define VPD_CFA_PROFILE_INFO 0x8c #define VPD_POWER_CONSUMPTION 0x8d -#define VPD_3PARTY_COPY 0x8f /* 3PC, XCOPY, SPC-4, SBC-3 */ +#define VPD_3PARTY_COPY 0x8f /* 3PC, XCOPY, SPC-5, SBC-4 */ #define VPD_PROTO_LU 0x90 #define VPD_PROTO_PORT 0x91 #define VPD_SCSI_FEATURE_SETS 0x92 /* spc5r11 */ @@ -174,6 +174,14 @@ void decode_format_presets_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jap); void decode_con_pos_range_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jap); +void decode_3party_copy_vpd(uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jap); +void +decode_proto_lu_vpd(uint8_t * buff, int len, struct opts_t * op, + sgj_opaque_p jap); +void +decode_proto_port_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); |