aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2022-08-07 02:22:53 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2022-08-07 02:22:53 +0000
commit23beedb782b58779546c58a7e9ef18aeccf50485 (patch)
tree9a98707784d48a4fa055b81f11b8d2135cdcb435 /src
parent640f7a7eff809159d9ac4c3ffc3681a6a5c3bede (diff)
downloadsg3_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.in1
-rw-r--r--src/sg_decode_sense.c51
-rw-r--r--src/sg_inq.c59
-rw-r--r--src/sg_vpd.c561
-rw-r--r--src/sg_vpd_common.c685
-rw-r--r--src/sg_vpd_common.h10
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);