1 /*
2 * Copyright (c) 2006-2022 Douglas Gilbert.
3 * All rights reserved.
4 * Use of this source code is governed by a BSD-style
5 * license that can be found in the BSD_LICENSE file.
6 *
7 * SPDX-License-Identifier: BSD-2-Clause
8 */
9
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stdarg.h>
15 #include <stdbool.h>
16 #include <string.h>
17 #include <ctype.h>
18 #include <assert.h>
19 #include <getopt.h>
20 #define __STDC_FORMAT_MACROS 1
21 #include <inttypes.h>
22 #include <errno.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include "sg_lib.h"
31 #include "sg_lib_data.h"
32 #include "sg_cmds_basic.h"
33 #include "sg_unaligned.h"
34 #include "sg_pr2serr.h"
35
36 #include "sg_vpd_common.h"
37
38 /* This file holds common code for sg_inq and sg_vpd as both those utilities
39 * decode SCSI VPD pages. */
40
41 const char * t10_vendor_id_hr = "T10_vendor_identification";
42 const char * t10_vendor_id_js = "t10_vendor_identification";
43 const char * product_id_hr = "Product_identification";
44 const char * product_id_js = "product_identification";
45 const char * product_rev_lev_hr = "Product_revision_level";
46 const char * product_rev_lev_js = "product_revision_level";
47 static const char * const y_s = "yes";
48 static const char * const n_s = "no";
49 static const char * const nl_s = "no limit";
50 static const char * const nlr_s = "no limit reported";
51 /* Earlier gcc compilers (e.g. 6.4) don't accept this first form when it is
52 * used in another array of strings initialization (e.g. bdc_zoned_strs) */
53 // static const char * const nr_s = "not reported";
54 static char nr_s[] = "not reported";
55 static const char * const ns_s = "not supported";
56 // static const char * const rsv_s = "Reserved";
57 static char rsv_s[] = "Reserved";
58 static const char * const vs_s = "Vendor specific";
59 static const char * const null_s = "";
60 static const char * const mn_s = "meaning";
61
62 /* Supported vendor specific VPD pages */
63 /* Arrange in alphabetical order by acronym */
64 struct svpd_vp_name_t vp_arr[] = {
65 {VPD_VP_DDS, "dds", "DDS tape family from IBM"},
66 {VPD_VP_EMC, "emc", "EMC (company)"},
67 {VPD_VP_WDC_HITACHI, "hit", "WDC/Hitachi disk"},
68 {VPD_VP_HP3PAR, "hp3par", "3PAR array (HP was Left Hand)"},
69 {VPD_VP_HP_LTO, "hp_lto", "HP LTO tape/systems"},
70 {VPD_VP_IBM_LTO, "ibm_lto", "IBM LTO tape/systems"},
71 {VPD_VP_NVME, "nvme", "NVMe related"},
72 {VPD_VP_RDAC, "rdac", "RDAC array (NetApp E-Series)"},
73 {VPD_VP_SEAGATE, "sea", "Seagate disk"},
74 {VPD_VP_SG, "sg", "sg3_utils extensions"},
75 {VPD_VP_WDC_HITACHI, "wdc", "WDC/Hitachi disk"},
76 {0, NULL, NULL},
77 };
78
79 /* Supported vendor specific VPD pages */
80 /* 'subvalue' holds vendor/product number to disambiguate */
81 /* Arrange in alphabetical order by acronym */
82 struct svpd_values_name_t vendor_vpd_pg[] = {
83 {VPD_V_ACI_LTO, VPD_VP_HP_LTO, 1, "aci", "ACI revision level (HP LTO)"},
84 {VPD_V_DATC_SEA, VPD_VP_SEAGATE, 0, "datc", "Date code (Seagate)"},
85 {VPD_V_DCRL_LTO, VPD_VP_IBM_LTO, 1, "dcrl", "Drive component revision "
86 "levels (IBM LTO)"},
87 {VPD_V_FVER_DDS, VPD_VP_DDS, 1, "ddsver", "Firmware revision (DDS)"},
88 {VPD_V_DEV_BEH_SEA, VPD_VP_SEAGATE, 0, "devb", "Device behavior "
89 "(Seagate)"},
90 {VPD_V_DSN_LTO, VPD_VP_IBM_LTO, 1, "dsn", "Drive serial numbers (IBM "
91 "LTO)"},
92 {VPD_V_DUCD_LTO, VPD_VP_IBM_LTO, 1, "ducd", "Device unique "
93 "configuration data (IBM LTO)"},
94 {VPD_V_EDID_RDAC, VPD_VP_RDAC, 0, "edid", "Extended device "
95 "identification (RDAC)"},
96 {VPD_V_FIRM_SEA, VPD_VP_SEAGATE, 0, "firm", "Firmware numbers "
97 "(Seagate)"},
98 {VPD_V_FVER_LTO, VPD_VP_HP_LTO, 0, "frl", "Firmware revision level "
99 "(HP LTO)"},
100 {VPD_V_FVER_RDAC, VPD_VP_RDAC, 0, "fwr4", "Firmware version (RDAC)"},
101 {VPD_V_HEAD_LTO, VPD_VP_HP_LTO, 1, "head", "Head Assy revision level "
102 "(HP LTO)"},
103 {VPD_V_HP3PAR, VPD_VP_HP3PAR, 0, "hp3par", "Volume information "
104 "(HP/3PAR)"},
105 {VPD_V_HVER_LTO, VPD_VP_HP_LTO, 1, "hrl", "Hardware revision level "
106 "(HP LTO)"},
107 {VPD_V_HVER_RDAC, VPD_VP_RDAC, 0, "hwr4", "Hardware version (RDAC)"},
108 {VPD_V_JUMP_SEA, VPD_VP_SEAGATE, 0, "jump", "Jump setting (Seagate)"},
109 {VPD_V_MECH_LTO, VPD_VP_HP_LTO, 1, "mech", "Mechanism revision level "
110 "(HP LTO)"},
111 {VPD_V_MPDS_LTO, VPD_VP_IBM_LTO, 1, "mpds", "Mode parameter default "
112 "settings (IBM LTO)"},
113 {SG_NVME_VPD_NICR, VPD_VP_SG, 0, "nicr",
114 "NVMe Identify Controller Response (sg3_utils)"},
115 {VPD_V_PCA_LTO, VPD_VP_HP_LTO, 1, "pca", "PCA revision level (HP LTO)"},
116 {VPD_V_FEAT_RDAC, VPD_VP_RDAC, 0, "prm4", "Feature Parameters (RDAC)"},
117 {VPD_V_RVSI_RDAC, VPD_VP_RDAC, 0, "rvsi", "Replicated volume source "
118 "identifier (RDAC)"},
119 {VPD_V_SAID_RDAC, VPD_VP_RDAC, 0, "said", "Storage array world wide "
120 "name (RDAC)"},
121 {VPD_V_SUBS_RDAC, VPD_VP_RDAC, 0, "subs", "Subsystem identifier (RDAC)"},
122 {VPD_V_SVER_RDAC, VPD_VP_RDAC, 0, "swr4", "Software version (RDAC)"},
123 {VPD_V_UPR_EMC, VPD_VP_EMC, 0, "upr", "Unit path report (EMC)"},
124 {VPD_V_VAC_RDAC, VPD_VP_RDAC, 0, "vac1", "Volume access control (RDAC)"},
125 {VPD_V_HIT_PG3, VPD_VP_WDC_HITACHI, 0, "wp3", "Page 0x3 (WDC/Hitachi)"},
126 {VPD_V_HIT_PG_D1, VPD_VP_WDC_HITACHI, 0, "wpd1",
127 "Page 0xd1 (WDC/Hitachi)"},
128 {VPD_V_HIT_PG_D2, VPD_VP_WDC_HITACHI, 0, "wpd2",
129 "Page 0xd2 (WDC/Hitachi)"},
130 {0, 0, 0, NULL, NULL},
131 };
132
133
134 int
no_ascii_4hex(const struct opts_t * op)135 no_ascii_4hex(const struct opts_t * op)
136 {
137 if (op->do_hex < 2)
138 return 1;
139 else if (2 == op->do_hex)
140 return 0;
141 else
142 return -1;
143 }
144
145 int
svpd_find_vp_num_by_acron(const char * vp_ap)146 svpd_find_vp_num_by_acron(const char * vp_ap)
147 {
148 size_t len;
149 const struct svpd_vp_name_t * vpp;
150
151 for (vpp = vp_arr; vpp->acron; ++vpp) {
152 len = strlen(vpp->acron);
153 if (0 == strncmp(vpp->acron, vp_ap, len))
154 return vpp->vend_prod_num;
155 }
156 return -1;
157 }
158
159 /* if vend_prod_num < -1 then list vendor_product ids + vendor pages, =-1
160 * list only vendor_product ids, else list pages for that vend_prod_num */
161 void
svpd_enumerate_vendor(int vend_prod_num)162 svpd_enumerate_vendor(int vend_prod_num)
163 {
164 bool seen;
165 const struct svpd_vp_name_t * vpp;
166 const struct svpd_values_name_t * vnp;
167
168 if (vend_prod_num < 0) {
169 for (seen = false, vpp = vp_arr; vpp->acron; ++vpp) {
170 if (vpp->name) {
171 if (! seen) {
172 printf("\nVendor/product identifiers:\n");
173 seen = true;
174 }
175 printf(" %-10s %d %s\n", vpp->acron,
176 vpp->vend_prod_num, vpp->name);
177 }
178 }
179 }
180 if (-1 == vend_prod_num)
181 return;
182 for (seen = false, vnp = vendor_vpd_pg; vnp->acron; ++vnp) {
183 if ((vend_prod_num >= 0) && (vend_prod_num != vnp->subvalue))
184 continue;
185 if (vnp->name) {
186 if (! seen) {
187 printf("\nVendor specific VPD pages:\n");
188 seen = true;
189 }
190 printf(" %-10s 0x%02x,%d %s\n", vnp->acron,
191 vnp->value, vnp->subvalue, vnp->name);
192 }
193 }
194 }
195
196 /* mxlen is command line --maxlen=LEN option (def: 0) or -1 for a VPD page
197 * with a short length (1 byte). Returns 0 for success. */
198 int /* global: use by sg_vpd_vendor.c */
vpd_fetch_page(int sg_fd,uint8_t * rp,int page,int mxlen,bool qt,int vb,int * rlenp)199 vpd_fetch_page(int sg_fd, uint8_t * rp, int page, int mxlen, bool qt,
200 int vb, int * rlenp)
201 {
202 int res, resid, rlen, len, n;
203
204 if (sg_fd < 0) {
205 len = sg_get_unaligned_be16(rp + 2) + 4;
206 if (vb && (len > mxlen))
207 pr2serr("warning: VPD page's length (%d) > bytes in --inhex=FN "
208 "file (%d)\n", len , mxlen);
209 if (rlenp)
210 *rlenp = (len < mxlen) ? len : mxlen;
211 return 0;
212 }
213 if (mxlen > MX_ALLOC_LEN) {
214 pr2serr("--maxlen=LEN too long: %d > %d\n", mxlen, MX_ALLOC_LEN);
215 return SG_LIB_SYNTAX_ERROR;
216 }
217 n = (mxlen > 0) ? mxlen : DEF_ALLOC_LEN;
218 res = sg_ll_inquiry_v2(sg_fd, true, page, rp, n, DEF_PT_TIMEOUT, &resid,
219 ! qt, vb);
220 if (res)
221 return res;
222 rlen = n - resid;
223 if (rlen < 4) {
224 pr2serr("VPD response too short (len=%d)\n", rlen);
225 return SG_LIB_CAT_MALFORMED;
226 }
227 if (page != rp[1]) {
228 pr2serr("invalid VPD response; probably a STANDARD INQUIRY "
229 "response\n");
230 n = (rlen < 32) ? rlen : 32;
231 if (vb) {
232 pr2serr("First %d bytes of bad response\n", n);
233 hex2stderr(rp, n, 0);
234 }
235 return SG_LIB_CAT_MALFORMED;
236 } else if ((0x80 == page) && (0x2 == rp[2]) && (0x2 == rp[3])) {
237 /* could be a Unit Serial number VPD page with a very long
238 * length of 4+514 bytes; more likely standard response for
239 * SCSI-2, RMB=1 and a response_data_format of 0x2. */
240 pr2serr("invalid Unit Serial Number VPD response; probably a "
241 "STANDARD INQUIRY response\n");
242 return SG_LIB_CAT_MALFORMED;
243 }
244 if (mxlen < 0)
245 len = rp[3] + 4;
246 else
247 len = sg_get_unaligned_be16(rp + 2) + 4;
248 if (len <= rlen) {
249 if (rlenp)
250 *rlenp = len;
251 return 0;
252 } else if (mxlen) {
253 if (rlenp)
254 *rlenp = rlen;
255 return 0;
256 }
257 if (len > MX_ALLOC_LEN) {
258 pr2serr("response length too long: %d > %d\n", len, MX_ALLOC_LEN);
259 return SG_LIB_CAT_MALFORMED;
260 } else {
261 res = sg_ll_inquiry_v2(sg_fd, true, page, rp, len, DEF_PT_TIMEOUT,
262 &resid, ! qt, vb);
263 if (res)
264 return res;
265 rlen = len - resid;
266 /* assume it is well behaved: hence page and len still same */
267 if (rlenp)
268 *rlenp = rlen;
269 return 0;
270 }
271 }
272
273 sgj_opaque_p
sg_vpd_js_hdr(sgj_state * jsp,sgj_opaque_p jop,const char * name,const uint8_t * vpd_hdrp)274 sg_vpd_js_hdr(sgj_state * jsp, sgj_opaque_p jop, const char * name,
275 const uint8_t * vpd_hdrp)
276 {
277 int pdt = vpd_hdrp[0] & PDT_MASK;
278 int pqual = (vpd_hdrp[0] & 0xe0) >> 5;
279 int pn = vpd_hdrp[1];
280 const char * pdt_str;
281 sgj_opaque_p jo2p = sgj_snake_named_subobject_r(jsp, jop, name);
282 char d[64];
283
284 pdt_str = sg_get_pdt_str(pdt, sizeof(d), d);
285 sgj_js_nv_ihexstr(jsp, jo2p, "peripheral_qualifier",
286 pqual, NULL, pqual_str(pqual));
287 sgj_js_nv_ihexstr(jsp, jo2p, "peripheral_device_type",
288 pdt, NULL, pdt_str);
289 sgj_js_nv_ihex(jsp, jo2p, "page_code", pn);
290 return jo2p;
291 }
292
293 const char *
pqual_str(int pqual)294 pqual_str(int pqual)
295 {
296 switch (pqual) {
297 case 0:
298 return "LU accessible";
299 case 1:
300 return "LU temporarily unavailable";
301 case 3:
302 return "LU not accessible via this port";
303 default:
304 return "value reserved by T10";
305 }
306 }
307
308 static const char * network_service_type_arr[] =
309 {
310 "unspecified",
311 "storage configuration service",
312 "diagnostics",
313 "status",
314 "logging",
315 "code download",
316 "copy service",
317 "administrative configuration service",
318 "reserved[0x8]", "reserved[0x9]",
319 "reserved[0xa]", "reserved[0xb]", "reserved[0xc]", "reserved[0xd]",
320 "reserved[0xe]", "reserved[0xf]", "reserved[0x10]", "reserved[0x11]",
321 "reserved[0x12]", "reserved[0x13]", "reserved[0x14]", "reserved[0x15]",
322 "reserved[0x16]", "reserved[0x17]", "reserved[0x18]", "reserved[0x19]",
323 "reserved[0x1a]", "reserved[0x1b]", "reserved[0x1c]", "reserved[0x1d]",
324 "reserved[0x1e]", "reserved[0x1f]",
325 };
326
327 /* VPD_MAN_NET_ADDR 0x85 ["mna"] */
328 void
decode_net_man_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)329 decode_net_man_vpd(const uint8_t * buff, int len, struct opts_t * op,
330 sgj_opaque_p jap)
331 {
332 int k, bump, na_len, assoc, nst;
333 sgj_state * jsp = &op->json_st;
334 sgj_opaque_p jo2p;
335 const uint8_t * bp;
336 const char * assoc_str;
337 const char * nst_str;
338
339 if ((1 == op->do_hex) || (op->do_hex > 2)) {
340 hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
341 return;
342 }
343 if (len < 4) {
344 pr2serr("Management network addresses VPD page length too short=%d\n",
345 len);
346 return;
347 }
348 len -= 4;
349 bp = buff + 4;
350 for (k = 0; k < len; k += bump, bp += bump) {
351 assoc = (bp[0] >> 5) & 0x3;
352 assoc_str = sg_get_desig_assoc_str(assoc);
353 nst = bp[0] & 0x1f;
354 nst_str = network_service_type_arr[nst];
355 sgj_pr_hr(jsp, " %s, Service type: %s\n", assoc_str, nst_str);
356 na_len = sg_get_unaligned_be16(bp + 2);
357 if (jsp->pr_as_json) {
358 jo2p = sgj_new_unattached_object_r(jsp);
359 sgj_js_nv_ihexstr(jsp, jo2p, "association", assoc, NULL,
360 assoc_str);
361 sgj_js_nv_ihexstr(jsp, jo2p, "service_type", nst, NULL,
362 nst_str);
363 sgj_js_nv_s_len(jsp, jo2p, "network_address",
364 (const char *)(bp + 4), na_len);
365 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
366 }
367 if (na_len > 0) {
368 if (op->do_hex > 1) {
369 sgj_pr_hr(jsp, " Network address:\n");
370 hex2stdout((bp + 4), na_len, 0);
371 } else
372 sgj_pr_hr(jsp, " %s\n", bp + 4);
373 }
374 bump = 4 + na_len;
375 if ((k + bump) > len) {
376 pr2serr("Management network addresses VPD page, short "
377 "descriptor length=%d, left=%d\n", bump, (len - k));
378 return;
379 }
380 }
381 }
382
383 /* VPD_EXT_INQ Extended Inquiry VPD ["ei"] */
384 void
decode_x_inq_vpd(const uint8_t * b,int len,bool protect,struct opts_t * op,sgj_opaque_p jop)385 decode_x_inq_vpd(const uint8_t * b, int len, bool protect, struct opts_t * op,
386 sgj_opaque_p jop)
387 {
388 bool do_long_nq = op->do_long && (! op->do_quiet);
389 int n;
390 sgj_state * jsp = &op->json_st;
391 sgj_opaque_p jo2p;
392 const char * cp;
393 const char * np;
394 const char * nex_p;
395 char d[128];
396 static const int dlen = sizeof(d);
397
398 if (len < 7) {
399 pr2serr("Extended INQUIRY data VPD page length too short=%d\n", len);
400 return;
401 }
402 if (op->do_hex) {
403 hex2stdout(b, len, (1 == op->do_hex) ? 0 : -1);
404 return;
405 }
406 if (do_long_nq || jsp->pr_as_json) {
407 n = (b[4] >> 6) & 0x3;
408 if (1 == n)
409 cp = "before final WRITE BUFFER";
410 else if (2 == n)
411 cp = "after power on or hard reset";
412 else {
413 cp = "none";
414 d[0] = '\0';
415 }
416 if (cp[0])
417 snprintf(d, dlen, " [%s]", cp);
418 sgj_pr_hr(jsp, " ACTIVATE_MICROCODE=%d%s\n", n, d);
419 sgj_js_nv_ihexstr(jsp, jop, "activate_microcode", n, NULL, cp);
420 n = (b[4] >> 3) & 0x7;
421 if (protect) {
422 switch (n)
423 {
424 case 0:
425 cp = "protection type 1 supported";
426 break;
427 case 1:
428 cp = "protection types 1 and 2 supported";
429 break;
430 case 2:
431 cp = "protection type 2 supported";
432 break;
433 case 3:
434 cp = "protection types 1 and 3 supported";
435 break;
436 case 4:
437 cp = "protection type 3 supported";
438 break;
439 case 5:
440 cp = "protection types 2 and 3 supported";
441 break;
442 case 6:
443 cp = "see Supported block lengths and protection types "
444 "VPD page";
445 break;
446 case 7:
447 cp = "protection types 1, 2 and 3 supported";
448 break;
449 }
450 } else if (op->protect_not_sure) {
451 cp = "Unsure because unable to read PROTECT bit in standard "
452 "INQUIRY response";
453 d[0] = '\0';
454 } else {
455 cp = "none";
456 d[0] = '\0';
457 }
458 if (cp[0])
459 snprintf(d, dlen, " [%s]", cp);
460 sgj_pr_hr(jsp, " SPT=%d%s\n", n, d);
461 sgj_js_nv_ihexstr_nex(jsp, jop, "spt", n, false, NULL,
462 cp, "Supported Protection Type");
463 sgj_haj_vi_nex(jsp, jop, 2, "GRD_CHK", SGJ_SEP_EQUAL_NO_SPACE,
464 !!(b[4] & 0x4), false, "guard check");
465 sgj_haj_vi_nex(jsp, jop, 2, "APP_CHK", SGJ_SEP_EQUAL_NO_SPACE,
466 !!(b[4] & 0x2), false, "application tag check");
467 sgj_haj_vi_nex(jsp, jop, 2, "REF_CHK", SGJ_SEP_EQUAL_NO_SPACE,
468 !!(b[4] & 0x1), false, "reference tag check");
469 sgj_haj_vi_nex(jsp, jop, 2, "UASK_SUP", SGJ_SEP_EQUAL_NO_SPACE,
470 !!(b[5] & 0x20), false, "Unit Attention "
471 "condition Sense Key specific data Supported");
472 sgj_haj_vi_nex(jsp, jop, 2, "GROUP_SUP", SGJ_SEP_EQUAL_NO_SPACE,
473 !!(b[5] & 0x10), false, "grouping function supported");
474 sgj_haj_vi_nex(jsp, jop, 2, "PRIOR_SUP", SGJ_SEP_EQUAL_NO_SPACE,
475 !!(b[5] & 0x8), false, "priority supported");
476 sgj_haj_vi_nex(jsp, jop, 2, "HEADSUP", SGJ_SEP_EQUAL_NO_SPACE,
477 !!(b[5] & 0x4), false, "head of queue supported");
478 sgj_haj_vi_nex(jsp, jop, 2, "ORDSUP", SGJ_SEP_EQUAL_NO_SPACE,
479 !!(b[5] & 0x2), false, "ordered (task attribute) "
480 "supported");
481 sgj_haj_vi_nex(jsp, jop, 2, "SIMPSUP", SGJ_SEP_EQUAL_NO_SPACE,
482 !!(b[5] & 0x1), false, "simple (task attribute) "
483 "supported");
484 sgj_haj_vi_nex(jsp, jop, 2, "WU_SUP", SGJ_SEP_EQUAL_NO_SPACE,
485 !!(b[6] & 0x8), false, "Write uncorrectable "
486 "supported");
487 sgj_haj_vi_nex(jsp, jop, 2, "CRD_SUP", SGJ_SEP_EQUAL_NO_SPACE,
488 !!(b[6] & 0x4), false, "Correction disable "
489 "supported (obsolete SPC-5)");
490 sgj_haj_vi_nex(jsp, jop, 2, "NV_SUP", SGJ_SEP_EQUAL_NO_SPACE,
491 !!(b[6] & 0x2), false, "Nonvolatile cache "
492 "supported");
493 sgj_haj_vi_nex(jsp, jop, 2, "V_SUP", SGJ_SEP_EQUAL_NO_SPACE,
494 !!(b[6] & 0x1), false, "Volatile cache supported");
495 sgj_haj_vi_nex(jsp, jop, 2, "NO_PI_CHK", SGJ_SEP_EQUAL_NO_SPACE,
496 !!(b[7] & 0x20), false, "No protection "
497 "information checking"); /* spc5r02 */
498 sgj_haj_vi_nex(jsp, jop, 2, "P_I_I_SUP", SGJ_SEP_EQUAL_NO_SPACE,
499 !!(b[7] & 0x10), false, "Protection information "
500 "interval supported");
501 sgj_haj_vi_nex(jsp, jop, 2, "LUICLR", SGJ_SEP_EQUAL_NO_SPACE,
502 !!(b[7] & 0x1), false, "Logical unit I_T nexus clear");
503 np = "LU_COLL_TYPE";
504 n = (b[8] >> 5) & 0x7;
505 nex_p = "Logical unit collection type";
506 if (jsp && (jsp->pr_string)) {
507 switch (n) {
508 case 0:
509 cp = "not reported";
510 break;
511 case 1:
512 cp = "Conglomerate";
513 break;
514 case 2:
515 cp = "Logical unit group";
516 break;
517 default:
518 cp = rsv_s;
519 break;
520 }
521 jo2p = sgj_haj_subo_r(jsp, jop, 2, np, SGJ_SEP_EQUAL_NO_SPACE,
522 n, false);
523 sgj_js_nv_s(jsp, jo2p, mn_s, cp);
524 if (jsp->pr_name_ex)
525 sgj_js_nv_s(jsp, jo2p, "abbreviated_name_expansion", nex_p);
526 } else
527 sgj_haj_vi_nex(jsp, jop, 2, np, SGJ_SEP_EQUAL_NO_SPACE, n,
528 true, nex_p);
529
530 sgj_haj_vi_nex(jsp, jop, 2, "R_SUP", SGJ_SEP_EQUAL_NO_SPACE,
531 !!(b[8] & 0x10), false, "Referrals supported");
532 sgj_haj_vi_nex(jsp, jop, 2, "RTD_SUP", SGJ_SEP_EQUAL_NO_SPACE,
533 !!(b[8] & 0x8), false,
534 "Revert to defaults supported");
535 sgj_haj_vi_nex(jsp, jop, 2, "HSSRELEF", SGJ_SEP_EQUAL_NO_SPACE,
536 !!(b[8] & 0x2), false,
537 "History snapshots release effects");
538 sgj_haj_vi_nex(jsp, jop, 2, "CBCS", SGJ_SEP_EQUAL_NO_SPACE,
539 !!(b[8] & 0x1), false, "Capability-based command "
540 "security (obsolete SPC-5)");
541 sgj_haj_vi(jsp, jop, 2, "Multi I_T nexus microcode download",
542 SGJ_SEP_EQUAL_NO_SPACE, b[9] & 0xf, true);
543 sgj_haj_vi(jsp, jop, 2, "Extended self-test completion minutes",
544 SGJ_SEP_EQUAL_NO_SPACE,
545 sg_get_unaligned_be16(b + 10), true);
546 sgj_haj_vi_nex(jsp, jop, 2, "POA_SUP", SGJ_SEP_EQUAL_NO_SPACE,
547 !!(b[12] & 0x80), false,
548 "Power on activation supported");
549 sgj_haj_vi_nex(jsp, jop, 2, "HRA_SUP", SGJ_SEP_EQUAL_NO_SPACE,
550 !!(b[12] & 0x40), false,
551 "Hard reset activation supported");
552 sgj_haj_vi_nex(jsp, jop, 2, "VSA_SUP", SGJ_SEP_EQUAL_NO_SPACE,
553 !!(b[12] & 0x20), false,
554 "Vendor specific activation supported");
555 sgj_haj_vi_nex(jsp, jop, 2, "DMS_VALID", SGJ_SEP_EQUAL_NO_SPACE,
556 !!(b[12] & 0x10), false,
557 "Download microcode support byte valid");
558 sgj_haj_vi(jsp, jop, 2, "Maximum supported sense data length",
559 SGJ_SEP_EQUAL_NO_SPACE, b[13], true);
560 sgj_haj_vi_nex(jsp, jop, 2, "IBS", SGJ_SEP_EQUAL_NO_SPACE,
561 !!(b[14] & 0x80), false, "Implicit bind supported");
562 sgj_haj_vi_nex(jsp, jop, 2, "IAS", SGJ_SEP_EQUAL_NO_SPACE,
563 !!(b[14] & 0x40), false,
564 "Implicit affiliation supported");
565 sgj_haj_vi_nex(jsp, jop, 2, "SAC", SGJ_SEP_EQUAL_NO_SPACE,
566 !!(b[14] & 0x4), false,
567 "Set affiliation command supported");
568 sgj_haj_vi_nex(jsp, jop, 2, "NRD1", SGJ_SEP_EQUAL_NO_SPACE,
569 !!(b[14] & 0x2), false,
570 "No redirect one supported (BIND)");
571 sgj_haj_vi_nex(jsp, jop, 2, "NRD0", SGJ_SEP_EQUAL_NO_SPACE,
572 !!(b[14] & 0x1), false,
573 "No redirect zero supported (BIND)");
574 sgj_haj_vi(jsp, jop, 2, "Maximum inquiry change logs",
575 SGJ_SEP_EQUAL_NO_SPACE,
576 sg_get_unaligned_be16(b + 15), true);
577 sgj_haj_vi(jsp, jop, 2, "Maximum mode page change logs",
578 SGJ_SEP_EQUAL_NO_SPACE,
579 sg_get_unaligned_be16(b + 17), true);
580 sgj_haj_vi_nex(jsp, jop, 2, "DM_MD_4", SGJ_SEP_EQUAL_NO_SPACE,
581 !!(b[19] & 0x80), false,
582 "Download microcode mode 4 supported");
583 sgj_haj_vi_nex(jsp, jop, 2, "DM_MD_5", SGJ_SEP_EQUAL_NO_SPACE,
584 !!(b[19] & 0x40), false,
585 "Download microcode mode 5 supported");
586 sgj_haj_vi_nex(jsp, jop, 2, "DM_MD_6", SGJ_SEP_EQUAL_NO_SPACE,
587 !!(b[19] & 0x20), false,
588 "Download microcode mode 6 supported");
589 sgj_haj_vi_nex(jsp, jop, 2, "DM_MD_7", SGJ_SEP_EQUAL_NO_SPACE,
590 !!(b[19] & 0x10), false,
591 "Download microcode mode 7 supported");
592 sgj_haj_vi_nex(jsp, jop, 2, "DM_MD_D", SGJ_SEP_EQUAL_NO_SPACE,
593 !!(b[19] & 0x8), false,
594 "Download microcode mode 0xd supported");
595 sgj_haj_vi_nex(jsp, jop, 2, "DM_MD_E", SGJ_SEP_EQUAL_NO_SPACE,
596 !!(b[19] & 0x4), false,
597 "Download microcode mode 0xe supported");
598 sgj_haj_vi_nex(jsp, jop, 2, "DM_MD_F", SGJ_SEP_EQUAL_NO_SPACE,
599 !!(b[19] & 0x2), false,
600 "Download microcode mode 0xf supported");
601 if (do_long_nq || (! jsp->pr_out_hr))
602 return;
603 }
604 sgj_pr_hr(jsp, " ACTIVATE_MICROCODE=%d SPT=%d GRD_CHK=%d APP_CHK=%d "
605 "REF_CHK=%d\n", ((b[4] >> 6) & 0x3), ((b[4] >> 3) & 0x7),
606 !!(b[4] & 0x4), !!(b[4] & 0x2), !!(b[4] & 0x1));
607 sgj_pr_hr(jsp, " UASK_SUP=%d GROUP_SUP=%d PRIOR_SUP=%d HEADSUP=%d "
608 "ORDSUP=%d SIMPSUP=%d\n", !!(b[5] & 0x20), !!(b[5] & 0x10),
609 !!(b[5] & 0x8), !!(b[5] & 0x4), !!(b[5] & 0x2), !!(b[5] & 0x1));
610 sgj_pr_hr(jsp, " WU_SUP=%d [CRD_SUP=%d] NV_SUP=%d V_SUP=%d\n",
611 !!(b[6] & 0x8), !!(b[6] & 0x4), !!(b[6] & 0x2), !!(b[6] & 0x1));
612 sgj_pr_hr(jsp, " NO_PI_CHK=%d P_I_I_SUP=%d LUICLR=%d\n", !!(b[7] & 0x20),
613 !!(b[7] & 0x10), !!(b[7] & 0x1));
614 /* RTD_SUP added in spc5r11, LU_COLL_TYPE added in spc5r09,
615 * HSSRELEF added in spc5r02; CBCS obsolete in spc5r01 */
616 sgj_pr_hr(jsp, " LU_COLL_TYPE=%d R_SUP=%d RTD_SUP=%d HSSRELEF=%d "
617 "[CBCS=%d]\n", (b[8] >> 5) & 0x7, !!(b[8] & 0x10),
618 !!(b[8] & 0x8), !!(b[8] & 0x2), !!(b[8] & 0x1));
619 sgj_pr_hr(jsp, " Multi I_T nexus microcode download=%d\n", b[9] & 0xf);
620 sgj_pr_hr(jsp, " Extended self-test completion minutes=%d\n",
621 sg_get_unaligned_be16(b + 10)); /* spc4r27 */
622 sgj_pr_hr(jsp, " POA_SUP=%d HRA_SUP=%d VSA_SUP=%d DMS_VALID=%d\n",
623 !!(b[12] & 0x80), !!(b[12] & 0x40), !!(b[12] & 0x20),
624 !!(b[12] & 0x10)); /* spc5r20 */
625 sgj_pr_hr(jsp, " Maximum supported sense data length=%d\n",
626 b[13]); /* spc4r34 */
627 sgj_pr_hr(jsp, " IBS=%d IAS=%d SAC=%d NRD1=%d NRD0=%d\n",
628 !!(b[14] & 0x80), !!(b[14] & 0x40), !!(b[14] & 0x4),
629 !!(b[14] & 0x2), !!(b[14] & 0x1)); /* added in spc5r09 */
630 sgj_pr_hr(jsp, " Maximum inquiry change logs=%u\n",
631 sg_get_unaligned_be16(b + 15)); /* spc5r17 */
632 sgj_pr_hr(jsp, " Maximum mode page change logs=%u\n",
633 sg_get_unaligned_be16(b + 17)); /* spc5r17 */
634 sgj_pr_hr(jsp, " DM_MD_4=%d DM_MD_5=%d DM_MD_6=%d DM_MD_7=%d\n",
635 !!(b[19] & 0x80), !!(b[19] & 0x40), !!(b[19] & 0x20),
636 !!(b[19] & 0x10)); /* spc5r20 */
637 sgj_pr_hr(jsp, " DM_MD_D=%d DM_MD_E=%d DM_MD_F=%d\n",
638 !!(b[19] & 0x8), !!(b[19] & 0x4), !!(b[19] & 0x2));
639 }
640
641 /* VPD_SOFTW_INF_ID 0x84 */
642 void
decode_softw_inf_id(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)643 decode_softw_inf_id(const uint8_t * buff, int len, struct opts_t * op,
644 sgj_opaque_p jap)
645 {
646 sgj_state * jsp = &op->json_st;
647 sgj_opaque_p jop;
648 uint64_t ieee_id;
649
650 if (op->do_hex) {
651 hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
652 return;
653 }
654 len -= 4;
655 buff += 4;
656 for ( ; len > 5; len -= 6, buff += 6) {
657 ieee_id = sg_get_unaligned_be48(buff + 0);
658 sgj_pr_hr(jsp, " IEEE identifier: 0x%" PRIx64 "\n", ieee_id);
659 if (jsp->pr_as_json) {
660 jop = sgj_new_unattached_object_r(jsp);
661 sgj_js_nv_ihex(jsp, jop, "ieee_identifier", ieee_id);
662 sgj_js_nv_o(jsp, jap, NULL /* name */, jop);
663 }
664 }
665 }
666
667 static const char * mode_page_policy_arr[] =
668 {
669 "shared",
670 "per target port",
671 "per initiator port",
672 "per I_T nexus",
673 };
674
675 /* VPD_MODE_PG_POLICY 0x87 ["mpp"] */
676 void
decode_mode_policy_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)677 decode_mode_policy_vpd(const uint8_t * buff, int len, struct opts_t * op,
678 sgj_opaque_p jap)
679 {
680 int k, n, bump, ppc, pspc;
681 sgj_state * jsp = &op->json_st;
682 sgj_opaque_p jo2p;
683 const uint8_t * bp;
684 char b[128];
685 static const int blen = sizeof(b);
686
687 if ((1 == op->do_hex) || (op->do_hex > 2)) {
688 hex2stdout(buff, len, (1 == op->do_hex) ? 1 : -1);
689 return;
690 }
691 if (len < 4) {
692 pr2serr("Mode page policy VPD page length too short=%d\n", len);
693 return;
694 }
695 len -= 4;
696 bp = buff + 4;
697 for (k = 0; k < len; k += bump, bp += bump) {
698 bump = 4;
699 if ((k + bump) > len) {
700 pr2serr("Mode page policy VPD page, short "
701 "descriptor length=%d, left=%d\n", bump, (len - k));
702 return;
703 }
704 if (op->do_hex > 1)
705 hex2stdout(bp, 4, 1);
706 else {
707 n = 0;
708 ppc = (bp[0] & 0x3f);
709 pspc = bp[1];
710 n = sg_scnpr(b + n, blen - n, " Policy page code: 0x%x", ppc);
711 if (pspc)
712 n += sg_scnpr(b + n, blen - n, ", subpage code: 0x%x", pspc);
713 sgj_pr_hr(jsp, "%s\n", b);
714 if ((0 == k) && (0x3f == (0x3f & bp[0])) && (0xff == bp[1]))
715 sgj_pr_hr(jsp, " therefore the policy applies to all modes "
716 "pages and subpages\n");
717 sgj_pr_hr(jsp, " MLUS=%d, Policy: %s\n", !!(bp[2] & 0x80),
718 mode_page_policy_arr[bp[2] & 0x3]);
719 if (jsp->pr_as_json) {
720 jo2p = sgj_new_unattached_object_r(jsp);
721 sgj_js_nv_ihex(jsp, jo2p, "policy_page_code", ppc);
722 sgj_js_nv_ihex(jsp, jo2p, "policy_subpage_code", pspc);
723 sgj_js_nv_ihex_nex(jsp, jo2p, "mlus", !!(bp[2] & 0x80), false,
724 "Multiple logical units share");
725 sgj_js_nv_ihexstr(jsp, jo2p, "mode_page_policy", bp[2] & 0x3,
726 NULL, mode_page_policy_arr[bp[2] & 0x3]);
727 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
728 }
729 }
730 }
731 }
732
733 /* VPD_POWER_CONDITION 0x8a ["pc"] */
734 void
decode_power_condition(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)735 decode_power_condition(const uint8_t * buff, int len, struct opts_t * op,
736 sgj_opaque_p jop)
737 {
738 sgj_state * jsp = &op->json_st;
739
740 if (len < 18) {
741 pr2serr("Power condition VPD page length too short=%d\n", len);
742 return;
743 }
744 if (op->do_hex) {
745 hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
746 return;
747 }
748 sgj_pr_hr(jsp, " Standby_y=%d Standby_z=%d Idle_c=%d Idle_b=%d "
749 "Idle_a=%d\n", !!(buff[4] & 0x2), !!(buff[4] & 0x1),
750 !!(buff[5] & 0x4), !!(buff[5] & 0x2), !!(buff[5] & 0x1));
751 if (jsp->pr_as_json) {
752 sgj_js_nv_ihex(jsp, jop, "standby_y", !!(buff[4] & 0x2));
753 sgj_js_nv_ihex(jsp, jop, "standby_z", !!(buff[4] & 0x1));
754 sgj_js_nv_ihex(jsp, jop, "idle_c", !!(buff[5] & 0x4));
755 sgj_js_nv_ihex(jsp, jop, "idle_b", !!(buff[5] & 0x2));
756 sgj_js_nv_ihex(jsp, jop, "idle_a", !!(buff[5] & 0x1));
757 }
758 sgj_haj_vi_nex(jsp, jop, 2, "Stopped condition recovery time",
759 SGJ_SEP_SPACE_1, sg_get_unaligned_be16(buff + 6),
760 true, "unit: millisecond");
761 sgj_haj_vi_nex(jsp, jop, 2, "Standby_z condition recovery time",
762 SGJ_SEP_SPACE_1, sg_get_unaligned_be16(buff + 8),
763 true, "unit: millisecond");
764 sgj_haj_vi_nex(jsp, jop, 2, "Standby_y condition recovery time",
765 SGJ_SEP_SPACE_1, sg_get_unaligned_be16(buff + 10),
766 true, "unit: millisecond");
767 sgj_haj_vi_nex(jsp, jop, 2, "Idle_a condition recovery time",
768 SGJ_SEP_SPACE_1, sg_get_unaligned_be16(buff + 12),
769 true, "unit: millisecond");
770 sgj_haj_vi_nex(jsp, jop, 2, "Idle_b condition recovery time",
771 SGJ_SEP_SPACE_1, sg_get_unaligned_be16(buff + 14),
772 true, "unit: millisecond");
773 sgj_haj_vi_nex(jsp, jop, 2, "Idle_c condition recovery time",
774 SGJ_SEP_SPACE_1, sg_get_unaligned_be16(buff + 16),
775 true, "unit: millisecond");
776 }
777
778 int
filter_json_dev_ids(uint8_t * buff,int len,int m_assoc,struct opts_t * op,sgj_opaque_p jap)779 filter_json_dev_ids(uint8_t * buff, int len, int m_assoc, struct opts_t * op,
780 sgj_opaque_p jap)
781 {
782 int u, off, i_len;
783 sgj_opaque_p jo2p;
784 const uint8_t * bp;
785 sgj_state * jsp = &op->json_st;
786
787 off = -1;
788 while ((u = sg_vpd_dev_id_iter(buff, len, &off, m_assoc, -1, -1)) == 0) {
789 bp = buff + off;
790 i_len = bp[3];
791 if ((off + i_len + 4) > len) {
792 pr2serr(" VPD page error: designator length longer than\n"
793 " remaining response length=%d\n", (len - off));
794 return SG_LIB_CAT_MALFORMED;
795 }
796 jo2p = sgj_new_unattached_object_r(jsp);
797 sgj_js_designation_descriptor(jsp, jo2p, bp, i_len + 4);
798 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
799 }
800 if (-2 == u) {
801 pr2serr("VPD page error: short designator around offset %d\n", off);
802 return SG_LIB_CAT_MALFORMED;
803 }
804 return 0;
805 }
806
807 /* VPD_ATA_INFO 0x89 ["ai"] */
808 void
decode_ata_info_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)809 decode_ata_info_vpd(const uint8_t * buff, int len, struct opts_t * op,
810 sgj_opaque_p jop)
811 {
812 bool do_long_nq = op->do_long && (! op->do_quiet);
813 int num, is_be, cc, n;
814 sgj_state * jsp = &op->json_st;
815 const char * cp;
816 const char * ata_transp;
817 char b[512];
818 char d[80];
819 static const int blen = sizeof(b);
820 static const int dlen = sizeof(d);
821 static const char * sat_vip = "SAT Vendor identification";
822 static const char * sat_pip = "SAT Product identification";
823 static const char * sat_prlp = "SAT Product revision level";
824
825 if (len < 36) {
826 pr2serr("ATA information VPD page length too short=%d\n", len);
827 return;
828 }
829 if (op->do_hex && (2 != op->do_hex)) {
830 hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
831 return;
832 }
833 memcpy(b, buff + 8, 8);
834 b[8] = '\0';
835 sgj_pr_hr(jsp, " %s: %s\n", sat_vip, b);
836 memcpy(b, buff + 16, 16);
837 b[16] = '\0';
838 sgj_pr_hr(jsp, " %s: %s\n", sat_pip, b);
839 memcpy(b, buff + 32, 4);
840 b[4] = '\0';
841 sgj_pr_hr(jsp, " %s: %s\n", sat_prlp, b);
842 if (len < 56)
843 return;
844 ata_transp = (0x34 == buff[36]) ? "SATA" : "PATA";
845 if (do_long_nq) {
846 sgj_pr_hr(jsp, " Device signature [%s] (in hex):\n", ata_transp);
847 hex2stdout(buff + 36, 20, 0);
848 } else
849 sgj_pr_hr(jsp, " Device signature indicates %s transport\n",
850 ata_transp);
851 cc = buff[56]; /* 0xec for IDENTIFY DEVICE and 0xa1 for IDENTIFY
852 * PACKET DEVICE (obsolete) */
853 n = sg_scnpr(b, blen, " Command code: 0x%x\n", cc);
854 if (len < 60)
855 return;
856 if (0xec == cc)
857 cp = null_s;
858 else if (0xa1 == cc)
859 cp = "PACKET ";
860 else
861 cp = NULL;
862 is_be = sg_is_big_endian();
863 if (cp) {
864 n += sg_scnpr(b + n, blen - n, " ATA command IDENTIFY %sDEVICE "
865 "response summary:\n", cp);
866 num = sg_ata_get_chars((const unsigned short *)(buff + 60), 27, 20,
867 is_be, d);
868 d[num] = '\0';
869 n += sg_scnpr(b + n, blen - n, " model: %s\n", d);
870 num = sg_ata_get_chars((const unsigned short *)(buff + 60), 10, 10,
871 is_be, d);
872 d[num] = '\0';
873 n += sg_scnpr(b + n, blen - n, " serial number: %s\n", d);
874 num = sg_ata_get_chars((const unsigned short *)(buff + 60), 23, 4,
875 is_be, d);
876 d[num] = '\0';
877 n += sg_scnpr(b + n, blen - n, " firmware revision: %s\n", d);
878 sgj_pr_hr(jsp, "%s", b);
879 if (do_long_nq)
880 sgj_pr_hr(jsp, " ATA command IDENTIFY %sDEVICE response in "
881 "hex:\n", cp);
882 } else if (do_long_nq)
883 sgj_pr_hr(jsp, " ATA command 0x%x got following response:\n",
884 (unsigned int)cc);
885 if (jsp->pr_as_json) {
886 sgj_convert_to_snake_name(sat_vip, d, dlen);
887 sgj_js_nv_s_len(jsp, jop, d, (const char *)(buff + 8), 8);
888 sgj_convert_to_snake_name(sat_pip, d, dlen);
889 sgj_js_nv_s_len(jsp, jop, d, (const char *)(buff + 16), 16);
890 sgj_convert_to_snake_name(sat_prlp, d, dlen);
891 sgj_js_nv_s_len(jsp, jop, d, (const char *)(buff + 32), 4);
892 sgj_js_nv_hex_bytes(jsp, jop, "ata_device_signature", buff + 36, 20);
893 sgj_js_nv_ihex(jsp, jop, "command_code", buff[56]);
894 sgj_js_nv_s(jsp, jop, "ata_identify_device_data_example",
895 "sg_vpd -p ai -HHH /dev/sdc | hdparm --Istdin");
896 }
897 if (len < 572)
898 return;
899 if (2 == op->do_hex)
900 hex2stdout((buff + 60), 512, 0);
901 else if (do_long_nq)
902 dWordHex((const unsigned short *)(buff + 60), 256, 0, is_be);
903 }
904
905 /* VPD_SCSI_FEATURE_SETS 0x92 ["sfs"] */
906 void
decode_feature_sets_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)907 decode_feature_sets_vpd(const uint8_t * buff, int len, struct opts_t * op,
908 sgj_opaque_p jap)
909 {
910 int k, bump;
911 uint16_t sf_code;
912 bool found;
913 const uint8_t * bp;
914 sgj_opaque_p jo2p;
915 sgj_state * jsp = &op->json_st;
916 char b[256];
917 char d[80];
918
919 if ((1 == op->do_hex) || (op->do_hex > 2)) {
920 hex2stdout(buff, len, (1 == op->do_hex) ? 1 : -1);
921 return;
922 }
923 if (len < 4) {
924 pr2serr("SCSI Feature sets VPD page length too short=%d\n", len);
925 return;
926 }
927 len -= 8;
928 bp = buff + 8;
929 for (k = 0; k < len; k += bump, bp += bump) {
930 jo2p = sgj_new_unattached_object_r(jsp);
931 sf_code = sg_get_unaligned_be16(bp);
932 bump = 2;
933 if ((k + bump) > len) {
934 pr2serr("SCSI Feature sets, short descriptor length=%d, "
935 "left=%d\n", bump, (len - k));
936 return;
937 }
938 if (2 == op->do_hex)
939 hex2stdout(bp + 8, 2, 1);
940 else if (op->do_hex > 2)
941 hex2stdout(bp, 2, 1);
942 else {
943 sg_scnpr(b, sizeof(b), " %s",
944 sg_get_sfs_str(sf_code, -2, sizeof(d), d, &found,
945 op->verbose));
946 if (op->verbose == 1)
947 sgj_pr_hr(jsp, "%s [0x%x]\n", b, (unsigned int)sf_code);
948 else if (op->verbose > 1)
949 sgj_pr_hr(jsp, "%s [0x%x] found=%s\n", b,
950 (unsigned int)sf_code, found ? "true" : "false");
951 else
952 sgj_pr_hr(jsp, "%s\n", b);
953 sgj_js_nv_ihexstr(jsp, jo2p, "feature_set_code", sf_code, NULL,
954 d);
955 if (jsp->verbose)
956 sgj_js_nv_b(jsp, jo2p, "meaning_is_match", found);
957 }
958 sgj_js_nv_o(jsp, jap, NULL, jo2p);
959 }
960 }
961
962 static const char * constituent_type_arr[] = {
963 "Reserved",
964 "Virtual tape library",
965 "Virtual tape drive",
966 "Direct access block device",
967 };
968
969 /* VPD_DEVICE_CONSTITUENTS 0x8b ["dc"] */
970 void
decode_dev_constit_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap,recurse_vpd_decodep fp)971 decode_dev_constit_vpd(const uint8_t * buff, int len, struct opts_t * op,
972 sgj_opaque_p jap, recurse_vpd_decodep fp)
973 {
974 uint16_t constit_type;
975 int k, j, res, bump, csd_len;
976 sgj_state * jsp = &op->json_st;
977 sgj_opaque_p jo2p, jo3p, ja2p;
978 const uint8_t * bp;
979 char b[256];
980 char d[64];
981 static const int blen = sizeof(b);
982 static const int dlen = sizeof(d);
983
984 if ((1 == op->do_hex) || (op->do_hex > 2)) {
985 hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
986 return;
987 }
988 if (len < 4) {
989 pr2serr("page length too short=%d\n", len);
990 return;
991 }
992 len -= 4;
993 bp = buff + 4;
994 for (k = 0, j = 0; k < len; k += bump, bp += bump, ++j) {
995 jo2p = sgj_new_unattached_object_r(jsp);
996 if (j > 0)
997 sgj_pr_hr(jsp, "\n");
998 sgj_pr_hr(jsp, " Constituent descriptor %d:\n", j + 1);
999 if ((k + 36) > len) {
1000 pr2serr("short descriptor length=36, left=%d\n", (len - k));
1001 sgj_js_nv_o(jsp, jap, NULL, jo2p);
1002 return;
1003 }
1004 constit_type = sg_get_unaligned_be16(bp + 0);
1005 if (constit_type >= SG_ARRAY_SIZE(constituent_type_arr))
1006 sgj_pr_hr(jsp," Constituent type: unknown [0x%x]\n",
1007 constit_type);
1008 else
1009 sgj_pr_hr(jsp, " Constituent type: %s [0x%x]\n",
1010 constituent_type_arr[constit_type], constit_type);
1011 sg_scnpr(b, blen, " Constituent device type: ");
1012 if (0xff == bp[2])
1013 sgj_pr_hr(jsp, "%sUnknown [0xff]\n", b);
1014 else if (bp[2] >= 0x20)
1015 sgj_pr_hr(jsp, "%s%s [0x%x]\n", b, rsv_s, bp[2]);
1016 else
1017 sgj_pr_hr(jsp, "%s%s [0x%x]\n", b,
1018 sg_get_pdt_str(PDT_MASK & bp[2], dlen, d), bp[2]);
1019 snprintf(b, blen, "%.8s", bp + 4);
1020 sgj_pr_hr(jsp, " %s: %s\n", t10_vendor_id_hr, b);
1021 sgj_js_nv_s(jsp, jo2p, t10_vendor_id_js, b);
1022 snprintf(b, blen, "%.16s", bp + 12);
1023 sgj_pr_hr(jsp, " %s: %s\n", product_id_hr, b);
1024 sgj_js_nv_s(jsp, jo2p, product_id_js, b);
1025 snprintf(b, blen, "%.4s", bp + 28);
1026 sgj_pr_hr(jsp, " %s: %s\n", product_rev_lev_hr, b);
1027 sgj_js_nv_s(jsp, jo2p, product_rev_lev_js, b);
1028 csd_len = sg_get_unaligned_be16(bp + 34);
1029 bump = 36 + csd_len;
1030 if ((k + bump) > len) {
1031 pr2serr("short descriptor length=%d, left=%d\n", bump, (len - k));
1032 sgj_js_nv_o(jsp, jap, NULL, jo2p);
1033 return;
1034 }
1035 if (csd_len > 0) {
1036 int m, q, cs_bump;
1037 uint8_t cs_type;
1038 uint8_t cs_len;
1039 const uint8_t * cs_bp;
1040
1041 sgj_pr_hr(jsp, " Constituent specific descriptors:\n");
1042 ja2p = sgj_named_subarray_r(jsp, jo2p,
1043 "constituent_specific_descriptor_list");
1044 for (m = 0, q = 0, cs_bp = bp + 36; m < csd_len;
1045 m += cs_bump, ++q, cs_bp += cs_bump) {
1046 jo3p = sgj_new_unattached_object_r(jsp);
1047 cs_type = cs_bp[0];
1048 cs_len = sg_get_unaligned_be16(cs_bp + 2);
1049 cs_bump = cs_len + 4;
1050 sgj_js_nv_ihex(jsp, jo3p, "constituent_specific_type",
1051 cs_type);
1052 if (1 == cs_type) { /* VPD page */
1053 int off = cs_bp + 4 - buff;
1054
1055 sgj_pr_hr(jsp, " Constituent specific VPD page "
1056 "%d:\n", q + 1);
1057 /* SPC-5 says these shall _not_ themselves be Device
1058 * Constituent VPD pages. So no infinite recursion. */
1059 res = (*fp)(op, jo3p, off);
1060 if (res)
1061 pr2serr("%s: recurse_vpd_decode() failed, res=%d\n",
1062 __func__, res);
1063 } else {
1064 if (0xff == cs_type)
1065 sgj_pr_hr(jsp, " Vendor specific data (in "
1066 "hex):\n");
1067 else
1068 sgj_pr_hr(jsp, " %s [0x%x] specific data (in "
1069 "hex):\n", rsv_s, cs_type);
1070 if (jsp->pr_as_json)
1071 sgj_js_nv_hex_bytes(jsp, jo3p,
1072 "constituent_specific_data_hex",
1073 cs_bp + 4, cs_len);
1074 else
1075 hex2stdout(cs_bp + 4, cs_len, 0 /* plus ASCII */);
1076 }
1077 sgj_js_nv_o(jsp, ja2p, NULL, jo3p);
1078 } /* end of Constituent specific descriptor loop */
1079 }
1080 sgj_js_nv_o(jsp, jap, NULL, jo2p);
1081 } /* end Constituent descriptor loop */
1082 }
1083
1084 /* VPD_CFA_PROFILE_INFO 0x8c ["cfa"] */
1085 void
decode_cga_profile_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)1086 decode_cga_profile_vpd(const uint8_t * buff, int len, struct opts_t * op,
1087 sgj_opaque_p jap)
1088 {
1089 int k;
1090 uint32_t u;
1091 sgj_state * jsp = &op->json_st;
1092 const uint8_t * bp;
1093 sgj_opaque_p jo2p;
1094
1095 if (op->do_hex) {
1096 hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
1097 return;
1098 }
1099 if (len < 4) {
1100 pr2serr("VPD page length too short=%d\n", len);
1101 return;
1102 }
1103 len -= 4;
1104 bp = buff + 4;
1105 for (k = 0; k < len; k += 4, bp += 4) {
1106 jo2p = sgj_new_unattached_object_r(jsp);
1107 sgj_haj_vi(jsp, jo2p, 0, "CGA profile supported",
1108 SGJ_SEP_COLON_1_SPACE, bp[0], true);
1109 u = sg_get_unaligned_be16(bp + 2);
1110 sgj_haj_vi_nex(jsp, jo2p, 2, "Sequential write data size",
1111 SGJ_SEP_COLON_1_SPACE, u, true, "unit: LB");
1112 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
1113 }
1114 }
1115
1116 /* Assume index is less than 16 */
1117 static const char * sg_ansi_version_arr[16] =
1118 {
1119 "no conformance claimed",
1120 "SCSI-1", /* obsolete, ANSI X3.131-1986 */
1121 "SCSI-2", /* obsolete, ANSI X3.131-1994 */
1122 "SPC", /* withdrawn, ANSI INCITS 301-1997 */
1123 "SPC-2", /* ANSI INCITS 351-2001, ISO/IEC 14776-452 */
1124 "SPC-3", /* ANSI INCITS 408-2005, ISO/IEC 14776-453 */
1125 "SPC-4", /* ANSI INCITS 513-2015 */
1126 "SPC-5", /* ANSI INCITS 502-2020 */
1127 "ecma=1, [8h]",
1128 "ecma=1, [9h]",
1129 "ecma=1, [Ah]",
1130 "ecma=1, [Bh]",
1131 "reserved [Ch]",
1132 "reserved [Dh]",
1133 "reserved [Eh]",
1134 "reserved [Fh]",
1135 };
1136
1137 static const char *
hot_pluggable_str(int hp)1138 hot_pluggable_str(int hp)
1139 {
1140 switch (hp) {
1141 case 0:
1142 return "No information";
1143 case 1:
1144 return "target device designed to be removed from SCSI domain";
1145 case 2:
1146 return "target device not designed to be removed from SCSI domain";
1147 default:
1148 return "value reserved by T10";
1149 }
1150 }
1151
1152 static const char *
tpgs_str(int tpgs)1153 tpgs_str(int tpgs)
1154 {
1155 switch (tpgs) {
1156 case 1:
1157 return "only implicit asymmetric logical unit access";
1158 case 2:
1159 return "only explicit asymmetric logical unit access";
1160 case 3:
1161 return "both explicit and implicit asymmetric logical unit access";
1162 case 0:
1163 default:
1164 return ns_s;
1165 }
1166 }
1167
1168 sgj_opaque_p
std_inq_decode_js(const uint8_t * b,int len,struct opts_t * op,sgj_opaque_p jop)1169 std_inq_decode_js(const uint8_t * b, int len, struct opts_t * op,
1170 sgj_opaque_p jop)
1171 {
1172 int tpgs;
1173 int pqual = (b[0] & 0xe0) >> 5;
1174 int pdt = b[0] & PDT_MASK;
1175 int hp = (b[1] >> 4) & 0x3;
1176 int ver = b[2];
1177 sgj_state * jsp = &op->json_st;
1178 sgj_opaque_p jo2p = NULL;
1179 char c[256];
1180 static const int clen = sizeof(c);
1181
1182 jo2p = sgj_named_subobject_r(jsp, jop, "standard_inquiry_data_format");
1183 sgj_js_nv_ihexstr(jsp, jo2p, "peripheral_qualifier", pqual, NULL,
1184 pqual_str(pqual));
1185 sgj_js_nv_ihexstr(jsp, jo2p, "peripheral_device_type", pdt, NULL,
1186 sg_get_pdt_str(pdt, clen, c));
1187 sgj_js_nv_ihex_nex(jsp, jo2p, "rmb", !!(b[1] & 0x80), false,
1188 "Removable Medium Bit");
1189 sgj_js_nv_ihex_nex(jsp, jo2p, "lu_cong", !!(b[1] & 0x40), false,
1190 "Logical Unit Conglomerate");
1191 sgj_js_nv_ihexstr(jsp, jo2p, "hot_pluggable", hp, NULL,
1192 hot_pluggable_str(hp));
1193 snprintf(c, clen, "%s", (ver > 0xf) ? "old or reserved version code" :
1194 sg_ansi_version_arr[ver]);
1195 sgj_js_nv_ihexstr(jsp, jo2p, "version", ver, NULL, c);
1196 sgj_js_nv_ihex_nex(jsp, jo2p, "aerc", !!(b[3] & 0x80), false,
1197 "Asynchronous Event Reporting Capability (obsolete "
1198 "SPC-3)");
1199 sgj_js_nv_ihex_nex(jsp, jo2p, "trmtsk", !!(b[3] & 0x40), false,
1200 "Terminate Task (obsolete SPC-2)");
1201 sgj_js_nv_ihex_nex(jsp, jo2p, "normaca", !!(b[3] & 0x20), false,
1202 "Normal ACA (Auto Contingent Allegiance)");
1203 sgj_js_nv_ihex_nex(jsp, jo2p, "hisup", !!(b[3] & 0x10), false,
1204 "Hierarchial Support");
1205 sgj_js_nv_ihex(jsp, jo2p, "response_data_format", b[3] & 0xf);
1206 sgj_js_nv_ihex_nex(jsp, jo2p, "sccs", !!(b[5] & 0x80), false,
1207 "SCC (SCSI Storage Commands) Supported");
1208 sgj_js_nv_ihex_nex(jsp, jo2p, "acc", !!(b[5] & 0x40), false,
1209 "Access Commands Coordinator (obsolete SPC-5)");
1210 tpgs = (b[5] >> 4) & 0x3;
1211 sgj_js_nv_ihexstr_nex(jsp, jo2p, "tpgs", tpgs, false, NULL,
1212 tpgs_str(tpgs), "Target Port Group Support");
1213 sgj_js_nv_ihex_nex(jsp, jo2p, "3pc", !!(b[5] & 0x8), false,
1214 "Third Party Copy");
1215 sgj_js_nv_ihex(jsp, jo2p, "protect", !!(b[5] & 0x1));
1216 /* Skip SPI specific flags which have been obsolete for a while) */
1217 sgj_js_nv_ihex_nex(jsp, jo2p, "bque", !!(b[6] & 0x80), false,
1218 "Basic task management model (obsolete SPC-4)");
1219 sgj_js_nv_ihex_nex(jsp, jo2p, "encserv", !!(b[6] & 0x40), false,
1220 "Enclousure Services supported");
1221 sgj_js_nv_ihex_nex(jsp, jo2p, "multip", !!(b[6] & 0x10), false,
1222 "Multiple SCSI port");
1223 sgj_js_nv_ihex_nex(jsp, jo2p, "mchngr", !!(b[6] & 0x8), false,
1224 "Medium changer (obsolete SPC-4)");
1225 sgj_js_nv_ihex_nex(jsp, jo2p, "reladr", !!(b[7] & 0x80), false,
1226 "Relative Addressing (obsolete in SPC-4)");
1227 sgj_js_nv_ihex_nex(jsp, jo2p, "linked", !!(b[7] & 0x8), false,
1228 "Linked Commands (obsolete in SPC-4)");
1229 sgj_js_nv_ihex_nex(jsp, jo2p, "cmdque", !!(b[7] & 0x2), false,
1230 "Command Management Model (command queuing)");
1231 if (len < 16)
1232 return jo2p;
1233 snprintf(c, clen, "%.8s", b + 8);
1234 sgj_js_nv_s(jsp, jo2p, t10_vendor_id_js, c);
1235 if (len < 32)
1236 return jo2p;
1237 snprintf(c, clen, "%.16s", b + 16);
1238 sgj_js_nv_s(jsp, jo2p, product_id_js, c);
1239 if (len < 36)
1240 return jo2p;
1241 snprintf(c, clen, "%.4s", b + 32);
1242 sgj_js_nv_s(jsp, jo2p, product_rev_lev_js, c);
1243 return jo2p;
1244 }
1245
1246 static const char * power_unit_arr[] =
1247 {
1248 "Gigawatts",
1249 "Megawatts",
1250 "Kilowatts",
1251 "Watts",
1252 "Milliwatts",
1253 "Microwatts",
1254 "Unit reserved",
1255 "Unit reserved",
1256 };
1257
1258 /* VPD_POWER_CONSUMPTION 0x8d ["psm"] */
1259 void
decode_power_consumption(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)1260 decode_power_consumption(const uint8_t * buff, int len, struct opts_t * op,
1261 sgj_opaque_p jap)
1262 {
1263 int k, bump, pcmp_id, pcmp_unit;
1264 unsigned int pcmp_val;
1265 sgj_state * jsp = &op->json_st;
1266 sgj_opaque_p jo2p;
1267 const uint8_t * bp;
1268 char b[128];
1269 static const int blen = sizeof(b);
1270 static const char * pcmp = "power_consumption";
1271 static const char * pci = "Power consumption identifier";
1272 static const char * mpc = "Maximum power consumption";
1273
1274 if ((1 == op->do_hex) || (op->do_hex > 2)) {
1275 hex2stdout(buff, len, (1 == op->do_hex) ? 1 : -1);
1276 return;
1277 }
1278 if (len < 4) {
1279 pr2serr("length too short=%d\n", len);
1280 return;
1281 }
1282 len -= 4;
1283 bp = buff + 4;
1284 for (k = 0; k < len; k += bump, bp += bump) {
1285 bump = 4;
1286 if ((k + bump) > len) {
1287 pr2serr("short descriptor length=%d, left=%d\n", bump,
1288 (len - k));
1289 return;
1290 }
1291 if (op->do_hex > 1)
1292 hex2stdout(bp, 4, 1);
1293 else {
1294 jo2p = sgj_new_unattached_object_r(jsp);
1295 pcmp_id = bp[0];
1296 pcmp_unit = 0x7 & bp[1];
1297 pcmp_val = sg_get_unaligned_be16(bp + 2);
1298 if (jsp->pr_as_json) {
1299 sgj_convert_to_snake_name(pci, b, blen);
1300 sgj_js_nv_ihex(jsp, jo2p, b, pcmp_id);
1301 snprintf(b, blen, "%s_units", pcmp);
1302 sgj_js_nv_ihexstr(jsp, jo2p, b, pcmp_unit, NULL,
1303 power_unit_arr[pcmp_unit]);
1304 snprintf(b, blen, "%s_value", pcmp);
1305 sgj_js_nv_ihex(jsp, jo2p, b, pcmp_val);
1306 }
1307 snprintf(b, blen, " %s: 0x%x", pci, pcmp_id);
1308 if (pcmp_val >= 1000 && pcmp_unit > 0)
1309 sgj_pr_hr(jsp, "%s %s: %d.%03d %s\n", b, mpc,
1310 pcmp_val / 1000, pcmp_val % 1000,
1311 power_unit_arr[pcmp_unit - 1]); /* up one unit */
1312 else
1313 sgj_pr_hr(jsp, "%s %s: %u %s\n", b, mpc, pcmp_val,
1314 power_unit_arr[pcmp_unit]);
1315 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
1316 }
1317 }
1318 }
1319
1320
1321 /* VPD_BLOCK_LIMITS 0xb0 ["bl"] */
1322 void
decode_block_limits_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)1323 decode_block_limits_vpd(const uint8_t * buff, int len, struct opts_t * op,
1324 sgj_opaque_p jop)
1325 {
1326 int wsnz, ugavalid;
1327 uint32_t u;
1328 uint64_t ull;
1329 sgj_state * jsp = &op->json_st;
1330 char b[144];
1331 static const int blen = sizeof(b);
1332 static const char * mcawl = "Maximum compare and write length";
1333 static const char * otlg = "Optimal transfer length granularity";
1334 static const char * cni = "command not implemented";
1335 static const char * ul = "unlimited";
1336 static const char * mtl = "Maximum transfer length";
1337 static const char * otl = "Optimal transfer length";
1338 static const char * mpl = "Maximum prefetch length";
1339 static const char * mulc = "Maximum unmap LBA count";
1340 static const char * mubdc = "Maximum unmap block descriptor count";
1341 static const char * oug = "Optimal unmap granularity";
1342 static const char * ugav = "Unmap granularity alignment valid";
1343 static const char * uga = "Unmap granularity alignment";
1344 static const char * mwsl = "Maximum write same length";
1345 static const char * matl = "Maximum atomic transfer length";
1346 static const char * aa = "Atomic alignment";
1347 static const char * atlg = "Atomic transfer length granularity";
1348 static const char * matlwab = "Maximum atomic transfer length with "
1349 "atomic boundary";
1350 static const char * mabs = "Maximum atomic boundary size";
1351
1352 if (len < 16) {
1353 pr2serr("page length too short=%d\n", len);
1354 return;
1355 }
1356 wsnz = !!(buff[4] & 0x1);
1357 sgj_pr_hr(jsp, " Write same non-zero (WSNZ): %d\n", wsnz);
1358 sgj_js_nv_ihex_nex(jsp, jop, "wsnz", wsnz, false,
1359 "Write Same Non-Zero (number of LBs must be > 0)");
1360 u = buff[5];
1361 if (0 == u) {
1362 sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", mcawl, cni);
1363 sgj_convert_to_snake_name(mcawl, b, blen);
1364 sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, cni);
1365 } else
1366 sgj_haj_vi_nex(jsp, jop, 2, mcawl, SGJ_SEP_COLON_1_SPACE, u,
1367 true, "unit: LB");
1368
1369 u = sg_get_unaligned_be16(buff + 6);
1370 if (0 == u) {
1371 sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", otlg, nr_s);
1372 sgj_convert_to_snake_name(otlg, b, blen);
1373 sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s);
1374 } else
1375 sgj_haj_vi_nex(jsp, jop, 2, otlg, SGJ_SEP_COLON_1_SPACE, u,
1376 true, "unit: LB");
1377
1378 u = sg_get_unaligned_be32(buff + 8);
1379 if (0 == u) {
1380 sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", mtl, nr_s);
1381 sgj_convert_to_snake_name(mtl, b, blen);
1382 sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s);
1383 } else
1384 sgj_haj_vi_nex(jsp, jop, 2, mtl, SGJ_SEP_COLON_1_SPACE, u,
1385 true, "unit: LB");
1386
1387 u = sg_get_unaligned_be32(buff + 12);
1388 if (0 == u) {
1389 sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", otl, nr_s);
1390 sgj_convert_to_snake_name(otl, b, blen);
1391 sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s);
1392 } else
1393 sgj_haj_vi_nex(jsp, jop, 2, otl, SGJ_SEP_COLON_1_SPACE, u,
1394 true, "unit: LB");
1395 if (len > 19) { /* added in sbc3r09 */
1396 u = sg_get_unaligned_be32(buff + 16);
1397 if (0 == u) {
1398 sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", mpl, nr_s);
1399 sgj_convert_to_snake_name(mpl, b, blen);
1400 sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s);
1401 } else
1402 sgj_haj_vi_nex(jsp, jop, 2, mpl, SGJ_SEP_COLON_1_SPACE, u,
1403 true, "unit: LB");
1404 }
1405 if (len > 27) { /* added in sbc3r18 */
1406 u = sg_get_unaligned_be32(buff + 20);
1407 sgj_convert_to_snake_name(mulc, b, blen);
1408 if (0 == u) {
1409 sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", mulc, cni);
1410 sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, cni);
1411 } else if (0xffffffff == u) {
1412 sgj_pr_hr(jsp, " %s: %s blocks\n", ul, mulc);
1413 sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, ul);
1414 } else
1415 sgj_haj_vi_nex(jsp, jop, 2, mulc, SGJ_SEP_COLON_1_SPACE, u,
1416 true, "unit: LB");
1417
1418 u = sg_get_unaligned_be32(buff + 24);
1419 sgj_convert_to_snake_name(mulc, b, blen);
1420 if (0 == u) {
1421 sgj_pr_hr(jsp, " %s: 0 block descriptors [%s]\n", mubdc, cni);
1422 sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, cni);
1423 } else if (0xffffffff == u) {
1424 sgj_pr_hr(jsp, " %s: %s block descriptors\n", ul, mubdc);
1425 sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, ul);
1426 } else
1427 sgj_haj_vi(jsp, jop, 2, mubdc, SGJ_SEP_COLON_1_SPACE,
1428 u, true);
1429 }
1430 if (len > 35) { /* added in sbc3r19 */
1431 u = sg_get_unaligned_be32(buff + 28);
1432 if (0 == u) {
1433 sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", oug, nr_s);
1434 sgj_convert_to_snake_name(oug, b, blen);
1435 sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s);
1436 } else
1437 sgj_haj_vi_nex(jsp, jop, 2, oug, SGJ_SEP_COLON_1_SPACE, u,
1438 true, "unit: LB");
1439
1440 ugavalid = !!(buff[32] & 0x80);
1441 sgj_pr_hr(jsp, " %s: %s\n", ugav, ugavalid ? "true" : "false");
1442 sgj_js_nv_i(jsp, jop, ugav, ugavalid);
1443 if (ugavalid) {
1444 u = 0x7fffffff & sg_get_unaligned_be32(buff + 32);
1445 sgj_haj_vi_nex(jsp, jop, 2, uga, SGJ_SEP_COLON_1_SPACE, u,
1446 true, "unit: LB");
1447 }
1448 }
1449 if (len > 43) { /* added in sbc3r26 */
1450 ull = sg_get_unaligned_be64(buff + 36);
1451 if (0 == ull) {
1452 sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", mwsl, nr_s);
1453 sgj_convert_to_snake_name(mwsl, b, blen);
1454 sgj_js_nv_ihexstr(jsp, jop, b, ull, NULL, nr_s);
1455 } else
1456 sgj_haj_vi_nex(jsp, jop, 2, mwsl, SGJ_SEP_COLON_1_SPACE,
1457 ull, true, "unit: LB");
1458 }
1459 if (len > 47) { /* added in sbc4r02 */
1460 u = sg_get_unaligned_be32(buff + 44);
1461 if (0 == u) {
1462 sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", matl, nr_s);
1463 sgj_convert_to_snake_name(matl, b, blen);
1464 sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s);
1465 } else
1466 sgj_haj_vi_nex(jsp, jop, 2, matl, SGJ_SEP_COLON_1_SPACE,
1467 u, true, "unit: LB");
1468
1469 u = sg_get_unaligned_be32(buff + 48);
1470 if (0 == u) {
1471 static const char * uawp = "unaligned atomic writes permitted";
1472
1473 sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", aa, uawp);
1474 sgj_convert_to_snake_name(aa, b, blen);
1475 sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, uawp);
1476 } else
1477 sgj_haj_vi_nex(jsp, jop, 2, aa, SGJ_SEP_COLON_1_SPACE,
1478 u, true, "unit: LB");
1479
1480 u = sg_get_unaligned_be32(buff + 52);
1481 if (0 == u) {
1482 static const char * ngr = "no granularity requirement";
1483
1484 sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", atlg, ngr);
1485 sgj_convert_to_snake_name(atlg, b, blen);
1486 sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, ngr);
1487 } else
1488 sgj_haj_vi_nex(jsp, jop, 2, aa, SGJ_SEP_COLON_1_SPACE,
1489 u, true, "unit: LB");
1490 }
1491 if (len > 56) {
1492 u = sg_get_unaligned_be32(buff + 56);
1493 if (0 == u) {
1494 sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", matlwab, nr_s);
1495 sgj_convert_to_snake_name(matlwab, b, blen);
1496 sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, nr_s);
1497 } else
1498 sgj_haj_vi_nex(jsp, jop, 2, matlwab, SGJ_SEP_COLON_1_SPACE,
1499 u, true, "unit: LB");
1500
1501 u = sg_get_unaligned_be32(buff + 60);
1502 if (0 == u) {
1503 static const char * cowa1b = "can only write atomic 1 block";
1504
1505 sgj_pr_hr(jsp, " %s: 0 blocks [%s]\n", mabs, cowa1b);
1506 sgj_convert_to_snake_name(mabs, b, blen);
1507 sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, cowa1b);
1508 } else
1509 sgj_haj_vi_nex(jsp, jop, 2, mabs, SGJ_SEP_COLON_1_SPACE,
1510 u, true, "unit: LB");
1511 }
1512 }
1513
1514 static const char * product_type_arr[] =
1515 {
1516 "Not specified",
1517 "CFast",
1518 "CompactFlash",
1519 "MemoryStick",
1520 "MultiMediaCard",
1521 "Secure Digital Card (SD)",
1522 "XQD",
1523 "Universal Flash Storage Card (UFS)",
1524 };
1525
1526 /* ZONED field here replaced by ZONED BLOCK DEVICE EXTENSION field in the
1527 * Zoned Block Device Characteristics VPD page. The new field includes
1528 * Zone Domains and Realms (see ZBC-2) */
1529 static const char * bdc_zoned_strs[] = {
1530 nr_s,
1531 "host-aware",
1532 "host-managed",
1533 rsv_s,
1534 };
1535
1536 /* VPD_BLOCK_DEV_CHARS 0xb1 ["bdc"] */
1537 void
decode_block_dev_ch_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)1538 decode_block_dev_ch_vpd(const uint8_t * buff, int len, struct opts_t * op,
1539 sgj_opaque_p jop)
1540 {
1541 int zoned;
1542 unsigned int u, k;
1543 sgj_state * jsp = &op->json_st;
1544 const char * cp;
1545 char b[144];
1546 static const char * mrr_j = "medium_rotation_rate";
1547 static const char * mrr_h = "Medium rotation rate";
1548 static const char * nrm = "Non-rotating medium (e.g. solid state)";
1549 static const char * pt_j = "product_type";
1550
1551 if (len < 64) {
1552 pr2serr("page length too short=%d\n", len);
1553 return;
1554 }
1555 u = sg_get_unaligned_be16(buff + 4);
1556 if (0 == u) {
1557 sgj_pr_hr(jsp, " %s is %s\n", mrr_h, nr_s);
1558 sgj_js_nv_ihexstr(jsp, jop, mrr_j, 0, NULL, nr_s);
1559 } else if (1 == u) {
1560 sgj_pr_hr(jsp, " %s\n", nrm);
1561 sgj_js_nv_ihexstr(jsp, jop, mrr_j, 1, NULL, nrm);
1562 } else if ((u < 0x401) || (0xffff == u)) {
1563 sgj_pr_hr(jsp, " %s [0x%x]\n", rsv_s, u);
1564 sgj_js_nv_ihexstr(jsp, jop, mrr_j, u, NULL, rsv_s);
1565 } else {
1566 sgj_js_nv_ihex_nex(jsp, jop, mrr_j, u, true,
1567 "unit: rpm; nominal rotation rate");
1568 }
1569 u = buff[6];
1570 k = SG_ARRAY_SIZE(product_type_arr);
1571 if (u < k) {
1572 sgj_pr_hr(jsp, " %s: %s\n", "Product type", product_type_arr[u]);
1573 sgj_js_nv_ihexstr(jsp, jop, pt_j, u, NULL, product_type_arr[u]);
1574 } else {
1575 sgj_pr_hr(jsp, " %s: %s [0x%x]\n", "Product type",
1576 (u < 0xf0) ? rsv_s : vs_s, u);
1577 sgj_js_nv_ihexstr(jsp, jop, pt_j, u, NULL, (u < 0xf0) ? rsv_s : vs_s);
1578 }
1579 sgj_haj_vi_nex(jsp, jop, 2, "WABEREQ", SGJ_SEP_EQUAL_NO_SPACE,
1580 (buff[7] >> 6) & 0x3, false,
1581 "Write After Block Erase REQuired");
1582 sgj_haj_vi_nex(jsp, jop, 2, "WACEREQ", SGJ_SEP_EQUAL_NO_SPACE,
1583 (buff[7] >> 4) & 0x3, false,
1584 "Write After Cryptographic Erase REQuired");
1585 u = buff[7] & 0xf;
1586 switch (u) {
1587 case 0:
1588 strcpy(b, nr_s);
1589 break;
1590 case 1:
1591 strcpy(b, "5.25 inch");
1592 break;
1593 case 2:
1594 strcpy(b, "3.5 inch");
1595 break;
1596 case 3:
1597 strcpy(b, "2.5 inch");
1598 break;
1599 case 4:
1600 strcpy(b, "1.8 inch");
1601 break;
1602 case 5:
1603 strcpy(b, "less then 1.8 inch");
1604 break;
1605 default:
1606 strcpy(b, rsv_s);
1607 break;
1608 }
1609 sgj_pr_hr(jsp, " Nominal form factor: %s\n", b);
1610 sgj_js_nv_ihexstr(jsp, jop, "nominal_forn_factor", u, NULL, b);
1611 sgj_haj_vi_nex(jsp, jop, 2, "MACT", SGJ_SEP_EQUAL_NO_SPACE,
1612 !!(buff[8] & 0x40), false, "Multiple ACTuator");
1613 zoned = (buff[8] >> 4) & 0x3; /* added sbc4r04, obsolete sbc5r01 */
1614 cp = bdc_zoned_strs[zoned];
1615 sgj_pr_hr(jsp, " ZONED=%d [%s]\n", zoned, cp);
1616 sgj_js_nv_ihexstr_nex(jsp, jop, "zoned", zoned, false, NULL,
1617 cp, "Added in SBC-4, obsolete in SBC-5");
1618 sgj_haj_vi_nex(jsp, jop, 2, "RBWZ", SGJ_SEP_EQUAL_NO_SPACE,
1619 !!(buff[8] & 0x4), false,
1620 "Background Operation Control Supported");
1621 sgj_haj_vi_nex(jsp, jop, 2, "FUAB", SGJ_SEP_EQUAL_NO_SPACE,
1622 !!(buff[8] & 0x2), false,
1623 "Force Unit Access Behaviour");
1624 sgj_haj_vi_nex(jsp, jop, 2, "VBULS", SGJ_SEP_EQUAL_NO_SPACE,
1625 !!(buff[8] & 0x1), false,
1626 "Verify Byte check Unmapped Lba Supported");
1627 u = sg_get_unaligned_be32(buff + 12);
1628 sgj_haj_vi_nex(jsp, jop, 2, "DEPOPULATION TIME", SGJ_SEP_COLON_1_SPACE,
1629 u, true, "unit: second");
1630 }
1631
1632 static const char * prov_type_arr[8] = {
1633 "not known or fully provisioned",
1634 "resource provisioned",
1635 "thin provisioned",
1636 rsv_s,
1637 rsv_s,
1638 rsv_s,
1639 rsv_s,
1640 rsv_s,
1641 };
1642
1643 /* VPD_LB_PROVISIONING 0xb2 ["lbpv"] */
1644 int
decode_block_lb_prov_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)1645 decode_block_lb_prov_vpd(const uint8_t * buff, int len, struct opts_t * op,
1646 sgj_opaque_p jop)
1647 {
1648 unsigned int u, dp, pt, t_exp;
1649 sgj_state * jsp = &op->json_st;
1650 const char * cp;
1651 char b[1024];
1652 static const int blen = sizeof(b);
1653 static const char * mp = "Minimum percentage";
1654 static const char * tp = "Threshold percentage";
1655 static const char * pgd = "Provisioning group descriptor";
1656
1657 if (len < 4) {
1658 pr2serr("page too short=%d\n", len);
1659 return SG_LIB_CAT_MALFORMED;
1660 }
1661 t_exp = buff[4];
1662 sgj_js_nv_ihexstr(jsp, jop, "threshold_exponent", t_exp, NULL,
1663 (0 == t_exp) ? ns_s : NULL);
1664 sgj_haj_vi_nex(jsp, jop, 2, "LBPU", SGJ_SEP_EQUAL_NO_SPACE,
1665 !!(buff[5] & 0x80), false,
1666 "Logical Block Provisioning Unmap command supported");
1667 sgj_haj_vi_nex(jsp, jop, 2, "LBPWS", SGJ_SEP_EQUAL_NO_SPACE,
1668 !!(buff[5] & 0x40), false, "Logical Block Provisioning "
1669 "Write Same (16) command supported");
1670 sgj_haj_vi_nex(jsp, jop, 2, "LBPWS10", SGJ_SEP_EQUAL_NO_SPACE,
1671 !!(buff[5] & 0x20), false, "Logical Block Provisioning "
1672 "Write Same (10) command supported");
1673 sgj_haj_vi_nex(jsp, jop, 2, "LBPRZ", SGJ_SEP_EQUAL_NO_SPACE,
1674 (0x7 & (buff[5] >> 2)), true,
1675 "Logical Block Provisioning Read Zero");
1676 sgj_haj_vi_nex(jsp, jop, 2, "ANC_SUP", SGJ_SEP_EQUAL_NO_SPACE,
1677 !!(buff[5] & 0x2), false,
1678 "ANChor SUPported");
1679 dp = !!(buff[5] & 0x1);
1680 sgj_haj_vi_nex(jsp, jop, 2, "DP", SGJ_SEP_EQUAL_NO_SPACE,
1681 dp, false, "Descriptor Present");
1682 u = 0x1f & (buff[6] >> 3); /* minimum percentage */
1683 if (0 == u)
1684 sgj_pr_hr(jsp, " %s: 0 [%s]\n", mp, nr_s);
1685 else
1686 sgj_pr_hr(jsp, " %s: %u\n", mp, u);
1687 sgj_convert_to_snake_name(mp, b, blen);
1688 sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, (0 == u) ? nr_s : NULL);
1689 pt = buff[6] & 0x7;
1690 cp = prov_type_arr[pt];
1691 if (pt > 2)
1692 snprintf(b, blen, " [%u]", u);
1693 else
1694 b[0] = '\0';
1695 sgj_pr_hr(jsp, " Provisioning type: %s%s\n", cp, b);
1696 sgj_js_nv_ihexstr(jsp, jop, "provisioning_type", pt, NULL, cp);
1697 u = buff[7]; /* threshold percentage */
1698 strcpy(b, tp);
1699 if (0 == u)
1700 sgj_pr_hr(jsp, " %s: 0 [percentages %s]\n", b, ns_s);
1701 else
1702 sgj_pr_hr(jsp, " %s: %u", b, u);
1703 sgj_convert_to_snake_name(tp, b, blen);
1704 sgj_js_nv_ihexstr(jsp, jop, b, u, NULL, (0 == u) ? ns_s : NULL);
1705 if (dp && (len > 11)) {
1706 int i_len;
1707 const uint8_t * bp;
1708 sgj_opaque_p jo2p;
1709
1710 bp = buff + 8;
1711 i_len = bp[3];
1712 if (0 == i_len) {
1713 pr2serr("%s too short=%d\n", pgd, i_len);
1714 return 0;
1715 }
1716 if (jsp->pr_as_json) {
1717 jo2p = sgj_snake_named_subobject_r(jsp, jop, pgd);
1718 sgj_js_designation_descriptor(jsp, jo2p, bp, i_len + 4);
1719 }
1720 sgj_pr_hr(jsp, " %s:\n", pgd);
1721 sg_get_designation_descriptor_str(" ", bp, i_len + 4, true,
1722 op->do_long, blen, b);
1723 if (jsp->pr_as_json && jsp->pr_out_hr)
1724 sgj_js_str_out(jsp, b, strlen(b));
1725 else
1726 sgj_pr_hr(jsp, "%s", b);
1727 }
1728 return 0;
1729 }
1730
1731 /* VPD_REFERRALS 0xb3 ["ref"] */
1732 void
decode_referrals_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)1733 decode_referrals_vpd(const uint8_t * buff, int len, struct opts_t * op,
1734 sgj_opaque_p jop)
1735 {
1736 uint32_t u;
1737 sgj_state * jsp = &op->json_st;
1738 char b[64];
1739
1740 if (len < 16) {
1741 pr2serr("Referrals VPD page length too short=%d\n", len);
1742 return;
1743 }
1744 u = sg_get_unaligned_be32(buff + 8);
1745 strcpy(b, " User data segment size: ");
1746 if (0 == u)
1747 sgj_pr_hr(jsp, "%s0 [per sense descriptor]\n", b);
1748 else
1749 sgj_pr_hr(jsp, "%s%u\n", b, u);
1750 sgj_js_nv_ihex(jsp, jop, "user_data_segment_size", u);
1751 u = sg_get_unaligned_be32(buff + 12);
1752 sgj_haj_vi(jsp, jop, 2, "User data segment multiplier",
1753 SGJ_SEP_COLON_1_SPACE, u, true);
1754 }
1755
1756 /* VPD_SUP_BLOCK_LENS 0xb4 ["sbl"] (added sbc4r01) */
1757 void
decode_sup_block_lens_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)1758 decode_sup_block_lens_vpd(const uint8_t * buff, int len, struct opts_t * op,
1759 sgj_opaque_p jap)
1760 {
1761 int k;
1762 unsigned int u;
1763 const uint8_t * bp;
1764 sgj_state * jsp = &op->json_st;
1765 sgj_opaque_p jo2p = NULL;
1766
1767 if (len < 4) {
1768 pr2serr("page length too short=%d\n", len);
1769 return;
1770 }
1771 len -= 4;
1772 bp = buff + 4;
1773 for (k = 0; k < len; k += 8, bp += 8) {
1774 if (jsp->pr_as_json)
1775 jo2p = sgj_new_unattached_object_r(jsp);
1776 u = sg_get_unaligned_be32(bp);
1777 sgj_haj_vi(jsp, jo2p, 2, "Logical block length",
1778 SGJ_SEP_COLON_1_SPACE, u, true);
1779 sgj_haj_vi_nex(jsp, jo2p, 4, "P_I_I_SUP",
1780 SGJ_SEP_COLON_1_SPACE, !!(bp[4] & 0x40), false,
1781 "Protection Information Interval SUPported");
1782 sgj_haj_vi_nex(jsp, jo2p, 4, "NO_PI_CHK",
1783 SGJ_SEP_COLON_1_SPACE, !!(bp[4] & 0x8), false,
1784 "NO Protection Information CHecKing");
1785 sgj_haj_vi_nex(jsp, jo2p, 4, "GRD_CHK", SGJ_SEP_COLON_1_SPACE,
1786 !!(bp[4] & 0x4), false, "GuaRD CHecK");
1787 sgj_haj_vi_nex(jsp, jo2p, 4, "APP_CHK", SGJ_SEP_COLON_1_SPACE,
1788 !!(bp[4] & 0x2), false, "APPlication tag CHecK");
1789 sgj_haj_vi_nex(jsp, jo2p, 4, "REF_CHK", SGJ_SEP_COLON_1_SPACE,
1790 !!(bp[4] & 0x1), false, "REFerence tag CHecK");
1791 sgj_haj_vi_nex(jsp, jo2p, 4, "T3PS", SGJ_SEP_COLON_1_SPACE,
1792 !!(bp[5] & 0x8), false, "Type 3 Protection Supported");
1793 sgj_haj_vi_nex(jsp, jo2p, 4, "T2PS", SGJ_SEP_COLON_1_SPACE,
1794 !!(bp[5] & 0x4), false, "Type 2 Protection Supported");
1795 sgj_haj_vi_nex(jsp, jo2p, 4, "T1PS", SGJ_SEP_COLON_1_SPACE,
1796 !!(bp[5] & 0x2), false, "Type 1 Protection Supported");
1797 sgj_haj_vi_nex(jsp, jo2p, 4, "T0PS", SGJ_SEP_COLON_1_SPACE,
1798 !!(bp[5] & 0x1), false, "Type 0 Protection Supported");
1799 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
1800 }
1801 }
1802
1803 /* VPD_BLOCK_DEV_C_EXTENS 0xb5 ["bdce"] (added sbc4r02) */
1804 void
decode_block_dev_char_ext_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)1805 decode_block_dev_char_ext_vpd(const uint8_t * buff, int len,
1806 struct opts_t * op, sgj_opaque_p jop)
1807 {
1808 bool b_active = false;
1809 bool combined = false;
1810 int n;
1811 uint32_t u;
1812 sgj_state * jsp = &op->json_st;
1813 const char * utp = null_s;
1814 const char * uup = null_s;
1815 const char * uip = null_s;
1816 char b[128];
1817 static const int blen = sizeof(b);
1818
1819 if (len < 16) {
1820 pr2serr("page length too short=%d\n", len);
1821 return;
1822 }
1823 switch (buff[5]) {
1824 case 1:
1825 utp = "Combined writes and reads";
1826 combined = true;
1827 break;
1828 case 2:
1829 utp = "Writes only";
1830 break;
1831 case 3:
1832 utp = "Separate writes and reads";
1833 b_active = true;
1834 break;
1835 default:
1836 utp = rsv_s;
1837 break;
1838 }
1839 sgj_haj_vistr(jsp, jop, 2, "Utilization type", SGJ_SEP_COLON_1_SPACE,
1840 buff[5], true, utp);
1841 switch (buff[6]) {
1842 case 2:
1843 uup = "megabytes";
1844 break;
1845 case 3:
1846 uup = "gigabytes";
1847 break;
1848 case 4:
1849 uup = "terabytes";
1850 break;
1851 case 5:
1852 uup = "petabytes";
1853 break;
1854 case 6:
1855 uup = "exabytes";
1856 break;
1857 default:
1858 uup = rsv_s;
1859 break;
1860 }
1861 sgj_haj_vistr(jsp, jop, 2, "Utilization units", SGJ_SEP_COLON_1_SPACE,
1862 buff[6], true, uup);
1863 switch (buff[7]) {
1864 case 0xa:
1865 uip = "per day";
1866 break;
1867 case 0xe:
1868 uip = "per year";
1869 break;
1870 default:
1871 uip = rsv_s;
1872 break;
1873 }
1874 sgj_haj_vistr(jsp, jop, 2, "Utilization interval", SGJ_SEP_COLON_1_SPACE,
1875 buff[7], true, uip);
1876 u = sg_get_unaligned_be32(buff + 8);
1877 sgj_haj_vistr(jsp, jop, 2, "Utilization B", SGJ_SEP_COLON_1_SPACE,
1878 u, true, (b_active ? NULL : rsv_s));
1879 n = sg_scnpr(b, blen, "%s: ", "Designed utilization");
1880 if (b_active)
1881 n += sg_scnpr(b + n, blen - n, "%u %s for reads and ", u, uup);
1882 u = sg_get_unaligned_be32(buff + 12);
1883 sgj_haj_vi(jsp, jop, 2, "Utilization A", SGJ_SEP_COLON_1_SPACE, u, true);
1884 n += sg_scnpr(b + n, blen - n, "%u %s for %swrites, %s", u, uup,
1885 combined ? "reads and " : null_s, uip);
1886 sgj_pr_hr(jsp, " %s\n", b);
1887 if (jsp->pr_string)
1888 sgj_js_nv_s(jsp, jop, "summary", b);
1889 }
1890
1891 /* VPD_ZBC_DEV_CHARS 0xb6 ["zdbch"] sbc or zbc [zbc2r04] */
1892 void
decode_zbdch_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)1893 decode_zbdch_vpd(const uint8_t * buff, int len, struct opts_t * op,
1894 sgj_opaque_p jop)
1895 {
1896 uint32_t u, pdt;
1897 sgj_state * jsp = &op->json_st;
1898 char b[128];
1899 static const int blen = sizeof(b);
1900
1901 if (op->do_hex) {
1902 hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
1903 return;
1904 }
1905 if (len < 64) {
1906 pr2serr("Zoned block device characteristics VPD page length too "
1907 "short=%d\n", len);
1908 return;
1909 }
1910 pdt = PDT_MASK & buff[0];
1911 sgj_pr_hr(jsp, " Peripheral device type: %s\n",
1912 sg_get_pdt_str(pdt, blen, b));
1913
1914 sgj_pr_hr(jsp, " Zoned block device extension: ");
1915 u = (buff[4] >> 4) & 0xf;
1916 switch (u) {
1917 case 0:
1918 if (PDT_ZBC == (PDT_MASK & buff[0]))
1919 strcpy(b, "host managed zoned block device");
1920 else
1921 strcpy(b, nr_s);
1922 break;
1923 case 1:
1924 strcpy(b, "host aware zoned block device model");
1925 break;
1926 case 2:
1927 strcpy(b, "Domains and realms zoned block device model");
1928 break;
1929 default:
1930 strcpy(b, rsv_s);
1931 break;
1932 }
1933 sgj_haj_vistr(jsp, jop, 2, "Zoned block device extension",
1934 SGJ_SEP_COLON_1_SPACE, u, true, b);
1935 sgj_haj_vi_nex(jsp, jop, 2, "AAORB", SGJ_SEP_COLON_1_SPACE,
1936 !!(buff[4] & 0x2), false,
1937 "Activation Aligned On Realm Boundaries");
1938 sgj_haj_vi_nex(jsp, jop, 2, "URSWRZ", SGJ_SEP_COLON_1_SPACE,
1939 !!(buff[4] & 0x1), false,
1940 "Unrestricted Read in Sequential Write Required Zone");
1941 u = sg_get_unaligned_be32(buff + 8);
1942 sgj_haj_vistr(jsp, jop, 2, "Optimal number of open sequential write "
1943 "preferred zones", SGJ_SEP_COLON_1_SPACE, u, true,
1944 (SG_LIB_UNBOUNDED_32BIT == u) ? nr_s : NULL);
1945 u = sg_get_unaligned_be32(buff + 12);
1946 sgj_haj_vistr(jsp, jop, 2, "Optimal number of non-sequentially "
1947 "written sequential write preferred zones",
1948 SGJ_SEP_COLON_1_SPACE, u, true,
1949 (SG_LIB_UNBOUNDED_32BIT == u) ? nr_s : NULL);
1950 u = sg_get_unaligned_be32(buff + 16);
1951 sgj_haj_vistr(jsp, jop, 2, "Maximum number of open sequential write "
1952 "required zones", SGJ_SEP_COLON_1_SPACE, u, true,
1953 (SG_LIB_UNBOUNDED_32BIT == u) ? nl_s : NULL);
1954 u = buff[23] & 0xf;
1955 switch (u) {
1956 case 0:
1957 strcpy(b, nr_s);
1958 break;
1959 case 1:
1960 strcpy(b, "Zoned starting LBAs aligned using constant zone lengths");
1961 break;
1962 case 0x8:
1963 strcpy(b, "Zoned starting LBAs potentially non-constant (as "
1964 "reported by REPORT ZONES)");
1965 break;
1966 default:
1967 strcpy(b, rsv_s);
1968 break;
1969 }
1970 sgj_haj_vistr(jsp, jop, 2, "Zoned alignment method",
1971 SGJ_SEP_COLON_1_SPACE, u, true, b);
1972 sgj_haj_vi(jsp, jop, 2, "Zone starting LBA granularity",
1973 SGJ_SEP_COLON_1_SPACE, sg_get_unaligned_be64(buff + 24), true);
1974 }
1975
1976 /* VPD_BLOCK_LIMITS_EXT 0xb7 ["ble"] SBC */
1977 void
decode_block_limits_ext_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)1978 decode_block_limits_ext_vpd(const uint8_t * buff, int len, struct opts_t * op,
1979 sgj_opaque_p jop)
1980 {
1981 uint32_t u;
1982 sgj_state * jsp = &op->json_st;
1983
1984 if (op->do_hex) {
1985 hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
1986 return;
1987 }
1988 if (len < 12) {
1989 pr2serr("page length too short=%d\n", len);
1990 return;
1991 }
1992 u = sg_get_unaligned_be16(buff + 6);
1993 sgj_haj_vistr(jsp, jop, 2, "Maximum number of streams",
1994 SGJ_SEP_COLON_1_SPACE, u, true,
1995 (0 == u) ? "Stream control not supported" : NULL);
1996 u = sg_get_unaligned_be16(buff + 8);
1997 sgj_haj_vi_nex(jsp, jop, 2, "Optimal stream write size",
1998 SGJ_SEP_COLON_1_SPACE, u, true, "unit: LB");
1999 u = sg_get_unaligned_be32(buff + 10);
2000 sgj_haj_vi_nex(jsp, jop, 2, "Stream granularity size",
2001 SGJ_SEP_COLON_1_SPACE, u, true,
2002 "unit: number of optimal stream write size blocks");
2003 if (len < 28)
2004 return;
2005 u = sg_get_unaligned_be32(buff + 16);
2006 sgj_haj_vistr_nex(jsp, jop, 2, "Maximum scattered LBA range transfer "
2007 "length", SGJ_SEP_COLON_1_SPACE, u, true,
2008 (0 == u ? nlr_s : NULL),
2009 "unit: LB (in a single LBA range descriptor)");
2010 u = sg_get_unaligned_be16(buff + 22);
2011 sgj_haj_vistr(jsp, jop, 2, "Maximum scattered LBA range descriptor "
2012 "count", SGJ_SEP_COLON_1_SPACE, u, true,
2013 (0 == u ? nlr_s : NULL));
2014 u = sg_get_unaligned_be32(buff + 24);
2015 sgj_haj_vistr_nex(jsp, jop, 2, "Maximum scattered transfer length",
2016 SGJ_SEP_COLON_1_SPACE, u, true,
2017 (0 == u ? nlr_s : NULL),
2018 "unit: LB (per single Write Scattered command)");
2019 }
2020
2021 static const char * sch_type_arr[8] = {
2022 rsv_s,
2023 "non-zoned",
2024 "host aware zoned",
2025 "host managed zoned",
2026 "zone domain and realms zoned",
2027 rsv_s,
2028 rsv_s,
2029 rsv_s,
2030 };
2031
2032 static char *
get_zone_align_method(uint8_t val,char * b,int blen)2033 get_zone_align_method(uint8_t val, char * b, int blen)
2034 {
2035 assert(blen > 32);
2036 switch (val) {
2037 case 0:
2038 strcpy(b, nr_s);
2039 break;
2040 case 1:
2041 strcpy(b, "using constant zone lengths");
2042 break;
2043 case 8:
2044 strcpy(b, "taking gap zones into account");
2045 break;
2046 default:
2047 strcpy(b, rsv_s);
2048 break;
2049 }
2050 return b;
2051 }
2052
2053 /* VPD_FORMAT_PRESETS 0xb8 ["fp"] (added sbc4r18) */
2054 void
decode_format_presets_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)2055 decode_format_presets_vpd(const uint8_t * buff, int len, struct opts_t * op,
2056 sgj_opaque_p jap)
2057 {
2058 uint8_t sch_type;
2059 int k;
2060 uint32_t u;
2061 uint64_t ul;
2062 sgj_state * jsp = &op->json_st;
2063 const uint8_t * bp;
2064 sgj_opaque_p jo2p, jo3p;
2065 const char * cp;
2066 char b[128];
2067 char d[64];
2068 static const int blen = sizeof(b);
2069 static const int dlen = sizeof(d);
2070 static const char * llczp = "Low LBA conventional zones percentage";
2071 static const char * hlczp = "High LBA conventional zones percentage";
2072 static const char * ztzd = "Zone type for zone domain";
2073
2074 if (op->do_hex) {
2075 hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
2076 return;
2077 }
2078 if (len < 4) {
2079 pr2serr("VPD page length too short=%d\n", len);
2080 return;
2081 }
2082 len -= 4;
2083 bp = buff + 4;
2084 for (k = 0; k < len; k += 64, bp += 64) {
2085 jo2p = sgj_new_unattached_object_r(jsp);
2086 sgj_haj_vi(jsp, jo2p, 2, "Preset identifier", SGJ_SEP_COLON_1_SPACE,
2087 sg_get_unaligned_be64(bp + 0), true);
2088 sch_type = bp[4];
2089 if (sch_type < 8) {
2090 cp = sch_type_arr[sch_type];
2091 if (rsv_s != cp)
2092 snprintf(b, blen, "%s block device", cp);
2093 else
2094 snprintf(b, blen, "%s", cp);
2095 } else
2096 strcpy(b, rsv_s);
2097 sgj_haj_vistr(jsp, jo2p, 4, "Schema type", SGJ_SEP_COLON_1_SPACE,
2098 sch_type, true, b);
2099 sgj_haj_vi(jsp, jo2p, 4, "Logical blocks per physical block "
2100 "exponent", SGJ_SEP_COLON_1_SPACE,
2101 0xf & bp[7], true);
2102 sgj_haj_vi_nex(jsp, jo2p, 4, "Logical block length",
2103 SGJ_SEP_COLON_1_SPACE, sg_get_unaligned_be32(bp + 8),
2104 true, "unit: byte");
2105 sgj_haj_vi(jsp, jo2p, 4, "Designed last Logical Block Address",
2106 SGJ_SEP_COLON_1_SPACE,
2107 sg_get_unaligned_be64(bp + 16), true);
2108 sgj_haj_vi_nex(jsp, jo2p, 4, "FMTPINFO", SGJ_SEP_COLON_1_SPACE,
2109 (bp[38] >> 6) & 0x3, false,
2110 "ForMaT Protection INFOrmation (see Format Unit)");
2111 sgj_haj_vi(jsp, jo2p, 4, "Protection field usage",
2112 SGJ_SEP_COLON_1_SPACE, bp[38] & 0x7, false);
2113 sgj_haj_vi(jsp, jo2p, 4, "Protection interval exponent",
2114 SGJ_SEP_COLON_1_SPACE, bp[39] & 0xf, true);
2115 jo3p = sgj_named_subobject_r(jsp, jo2p,
2116 "schema_type_specific_information");
2117 switch (sch_type) {
2118 case 2:
2119 sgj_pr_hr(jsp, " Defines zones for host aware device:\n");
2120 u = bp[40 + 0];
2121 sgj_pr_hr(jsp, " %s: %u.%u %%\n", llczp, u / 10, u % 10);
2122 sgj_convert_to_snake_name(llczp, b, blen);
2123 sgj_js_nv_ihex_nex(jsp, jo3p, b, u, true, "unit: 1/10 of a "
2124 "percent");
2125 u = bp[40 + 1];
2126 sgj_pr_hr(jsp, " %s: %u.%u %%\n", hlczp, u / 10, u % 10);
2127 sgj_convert_to_snake_name(hlczp, b, blen);
2128 sgj_js_nv_ihex_nex(jsp, jo3p, b, u, true, "unit: 1/10 of a "
2129 "percent");
2130 u = sg_get_unaligned_be32(bp + 40 + 12);
2131 sgj_haj_vistr(jsp, jo3p, 6, "Logical blocks per zone",
2132 SGJ_SEP_COLON_1_SPACE, u, true,
2133 (0 == u ? rsv_s : NULL));
2134 break;
2135 case 3:
2136 sgj_pr_hr(jsp, " Defines zones for host managed device:\n");
2137 u = bp[40 + 0];
2138 sgj_pr_hr(jsp, " %s: %u.%u %%\n", llczp, u / 10, u % 10);
2139 sgj_convert_to_snake_name(llczp, b, blen);
2140 sgj_js_nv_ihex_nex(jsp, jo3p, b, u, true, "unit: 1/10 of a "
2141 "percent");
2142 u = bp[40 + 1];
2143 sgj_pr_hr(jsp, " %s: %u.%u %%\n", hlczp, u / 10, u % 10);
2144 sgj_convert_to_snake_name(hlczp, b, blen);
2145 sgj_js_nv_ihex_nex(jsp, jo3p, b, u, true, "unit: 1/10 of a "
2146 "percent");
2147 u = bp[40 + 3] & 0x7;
2148 sgj_haj_vistr(jsp, jo3p, 6, "Designed zone alignment method",
2149 SGJ_SEP_COLON_1_SPACE, u, true,
2150 get_zone_align_method(u, d, dlen));
2151 ul = sg_get_unaligned_be64(bp + 40 + 4);
2152 sgj_haj_vi_nex(jsp, jo3p, 6, "Designed zone starting LBA "
2153 "granularity", SGJ_SEP_COLON_1_SPACE, ul, true,
2154 "unit: LB");
2155 u = sg_get_unaligned_be32(bp + 40 + 12);
2156 sgj_haj_vistr(jsp, jo3p, 6, "Logical blocks per zone",
2157 SGJ_SEP_COLON_1_SPACE, u, true,
2158 (0 == u ? rsv_s : NULL));
2159 break;
2160 case 4:
2161 sgj_pr_hr(jsp, " Defines zones for zone domains and realms "
2162 "device:\n");
2163 snprintf(b, blen, "%s 0", ztzd);
2164 u = bp[40 + 0];
2165 sg_get_zone_type_str((u >> 4) & 0xf, dlen, d);
2166 sgj_haj_vistr(jsp, jo3p, 6, b, SGJ_SEP_COLON_1_SPACE, u, true, d);
2167 snprintf(b, blen, "%s 1", ztzd);
2168 sg_get_zone_type_str(u & 0xf, dlen, d);
2169 sgj_haj_vistr(jsp, jo3p, 6, b, SGJ_SEP_COLON_1_SPACE, u, true, d);
2170
2171 snprintf(b, blen, "%s 2", ztzd);
2172 u = bp[40 + 1];
2173 sg_get_zone_type_str((u >> 4) & 0xf, dlen, d);
2174 sgj_haj_vistr(jsp, jo3p, 6, b, SGJ_SEP_COLON_1_SPACE, u, true, d);
2175 snprintf(b, blen, "%s 3", ztzd);
2176 sg_get_zone_type_str(u & 0xf, dlen, d);
2177 sgj_haj_vistr(jsp, jo3p, 6, b, SGJ_SEP_COLON_1_SPACE, u, true, d);
2178 u = bp[40 + 3] & 0x7;
2179 sgj_haj_vistr(jsp, jo3p, 6, "Designed zone alignment method",
2180 SGJ_SEP_COLON_1_SPACE, u, true,
2181 get_zone_align_method(u, d, dlen));
2182 ul = sg_get_unaligned_be64(bp + 40 + 4);
2183 sgj_haj_vi_nex(jsp, jo3p, 6, "Designed zone starting LBA "
2184 "granularity", SGJ_SEP_COLON_1_SPACE, ul, true,
2185 "unit: LB");
2186 u = sg_get_unaligned_be32(bp + 40 + 12);
2187 sgj_haj_vistr(jsp, jo3p, 6, "Logical blocks per zone",
2188 SGJ_SEP_COLON_1_SPACE, u, true,
2189 (0 == u ? rsv_s : NULL));
2190 ul = sg_get_unaligned_be64(bp + 40 + 16);
2191 sgj_haj_vi_nex(jsp, jo3p, 6, "Designed zone maximum address",
2192 SGJ_SEP_COLON_1_SPACE, ul, true, "unit: LBA");
2193 break;
2194 default:
2195 sgj_pr_hr(jsp, " No schema type specific information\n");
2196 break;
2197 }
2198 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2199 }
2200 }
2201
2202 /* VPD_CON_POS_RANGE 0xb9 (added sbc5r01) */
2203 void
decode_con_pos_range_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)2204 decode_con_pos_range_vpd(const uint8_t * buff, int len, struct opts_t * op,
2205 sgj_opaque_p jap)
2206 {
2207 int k;
2208 uint32_t u;
2209 sgj_state * jsp = &op->json_st;
2210 const uint8_t * bp;
2211 sgj_opaque_p jo2p;
2212
2213 if (op->do_hex) {
2214 hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
2215 return;
2216 }
2217 if (len < 64) {
2218 pr2serr("VPD page length too short=%d\n", len);
2219 return;
2220 }
2221 len -= 64;
2222 bp = buff + 64;
2223 for (k = 0; k < len; k += 32, bp += 32) {
2224 jo2p = sgj_new_unattached_object_r(jsp);
2225 sgj_haj_vi(jsp, jo2p, 2, "LBA range number",
2226 SGJ_SEP_COLON_1_SPACE, bp[0], true);
2227 u = bp[1];
2228 sgj_haj_vistr(jsp, jo2p, 4, "Number of storage elements",
2229 SGJ_SEP_COLON_1_SPACE, u, true, (0 == u ? nr_s : NULL));
2230 sgj_haj_vi(jsp, jo2p, 4, "Starting LBA", SGJ_SEP_COLON_1_SPACE,
2231 sg_get_unaligned_be64(bp + 8), true);
2232 sgj_haj_vi(jsp, jo2p, 4, "Number of LBAs", SGJ_SEP_COLON_1_SPACE,
2233 sg_get_unaligned_be64(bp + 16), true);
2234 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2235 }
2236 }
2237
2238 /* This is xcopy(LID4) related: "ROD" == Representation Of Data
2239 * Used by VPD_3PARTY_COPY 0x8f ["tpc"] */
2240 static void
decode_rod_descriptor(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)2241 decode_rod_descriptor(const uint8_t * buff, int len, struct opts_t * op,
2242 sgj_opaque_p jap)
2243 {
2244 uint8_t pdt;
2245 uint32_t u;
2246 int k, bump;
2247 uint64_t ull;
2248 const uint8_t * bp = buff;
2249 sgj_state * jsp = &op->json_st;
2250 sgj_opaque_p jo2p;
2251 char b[80];
2252 static const int blen = sizeof(b);
2253 static const char * ab_pdt = "abnormal use of 'pdt'";
2254
2255 for (k = 0; k < len; k += bump, bp += bump) {
2256 jo2p = sgj_new_unattached_object_r(jsp);
2257 bump = sg_get_unaligned_be16(bp + 2) + 4;
2258 pdt = 0x1f & bp[0];
2259 u = (bp[0] >> 5) & 0x7;
2260 sgj_js_nv_i(jsp, jo2p, "descriptor_format", u);
2261 if (0 != u) {
2262 sgj_pr_hr(jsp, " Unhandled descriptor (format %u, device type "
2263 "%u)\n", u, pdt);
2264 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2265 break;
2266 }
2267 switch (pdt) {
2268 case 0:
2269 /* Block ROD device type specific descriptor */
2270 sgj_js_nv_ihexstr_nex(jsp, jo2p, "peripheral_device_type",
2271 pdt, false, NULL, "Block ROD device "
2272 "type specific descriptor", ab_pdt);
2273 sgj_haj_vi_nex(jsp, jo2p, 4, "Optimal block ROD length "
2274 "granularity", SGJ_SEP_COLON_1_SPACE,
2275 sg_get_unaligned_be16(bp + 6), true, "unit: LB");
2276 ull = sg_get_unaligned_be64(bp + 8);
2277 sgj_haj_vi(jsp, jo2p, 4, "Maximum bytes in block ROD",
2278 SGJ_SEP_COLON_1_SPACE, ull, true);
2279 ull = sg_get_unaligned_be64(bp + 16);
2280 sgj_haj_vistr(jsp, jo2p, 4, "Optimal Bytes in block ROD "
2281 "transfer", SGJ_SEP_COLON_1_SPACE, ull, true,
2282 (SG_LIB_UNBOUNDED_64BIT == ull) ? nl_s : NULL);
2283 ull = sg_get_unaligned_be64(bp + 24);
2284 sgj_haj_vistr(jsp, jo2p, 4, "Optimal Bytes to token per "
2285 "segment", SGJ_SEP_COLON_1_SPACE, ull, true,
2286 (SG_LIB_UNBOUNDED_64BIT == ull) ? nl_s : NULL);
2287 ull = sg_get_unaligned_be64(bp + 32);
2288 sgj_haj_vistr(jsp, jo2p, 4, "Optimal Bytes from token per "
2289 "segment", SGJ_SEP_COLON_1_SPACE, ull, true,
2290 (SG_LIB_UNBOUNDED_64BIT == ull) ? nl_s : NULL);
2291 break;
2292 case 1:
2293 /* Stream ROD device type specific descriptor */
2294 sgj_js_nv_ihexstr_nex(jsp, jo2p, "peripheral_device_type",
2295 pdt, false, NULL, "Stream ROD device "
2296 "type specific descriptor", ab_pdt);
2297 ull = sg_get_unaligned_be64(bp + 8);
2298 sgj_haj_vi(jsp, jo2p, 4, "Maximum bytes in stream ROD",
2299 SGJ_SEP_COLON_1_SPACE, ull, true);
2300 ull = sg_get_unaligned_be64(bp + 16);
2301 snprintf(b, blen, " Optimal Bytes in stream ROD transfer: ");
2302 if (SG_LIB_UNBOUNDED_64BIT == ull)
2303 sgj_pr_hr(jsp, "%s-1 [no limit]\n", b);
2304 else
2305 sgj_pr_hr(jsp, "%s%" PRIu64 "\n", b, ull);
2306 break;
2307 case 3:
2308 /* Copy manager ROD device type specific descriptor */
2309 sgj_js_nv_ihexstr_nex(jsp, jo2p, "peripheral_device_type",
2310 pdt, false, NULL, "Copy manager ROD "
2311 "device type specific descriptor",
2312 ab_pdt);
2313 sgj_pr_hr(jsp, " Maximum Bytes in processor ROD: %" PRIu64 "\n",
2314 sg_get_unaligned_be64(bp + 8));
2315 ull = sg_get_unaligned_be64(bp + 16);
2316 snprintf(b, blen, " Optimal Bytes in processor ROD transfer: ");
2317 if (SG_LIB_UNBOUNDED_64BIT == ull)
2318 sgj_pr_hr(jsp, "%s-1 [no limit]\n", b);
2319 else
2320 sgj_pr_hr(jsp, "%s%" PRIu64 "\n", b, ull);
2321 break;
2322 default:
2323 sgj_js_nv_ihexstr(jsp, jo2p, "peripheral_device_type",
2324 pdt, NULL, "unknown");
2325 break;
2326 }
2327 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2328 }
2329 }
2330
2331 struct tpc_desc_type {
2332 uint8_t code;
2333 const char * name;
2334 };
2335
2336 static struct tpc_desc_type tpc_desc_arr[] = {
2337 {0x0, "block -> stream"},
2338 {0x1, "stream -> block"},
2339 {0x2, "block -> block"},
2340 {0x3, "stream -> stream"},
2341 {0x4, "inline -> stream"},
2342 {0x5, "embedded -> stream"},
2343 {0x6, "stream -> discard"},
2344 {0x7, "verify CSCD"},
2345 {0x8, "block<o> -> stream"},
2346 {0x9, "stream -> block<o>"},
2347 {0xa, "block<o> -> block<o>"},
2348 {0xb, "block -> stream & application_client"},
2349 {0xc, "stream -> block & application_client"},
2350 {0xd, "block -> block & application_client"},
2351 {0xe, "stream -> stream&application_client"},
2352 {0xf, "stream -> discard&application_client"},
2353 {0x10, "filemark -> tape"},
2354 {0x11, "space -> tape"}, /* obsolete: spc5r02 */
2355 {0x12, "locate -> tape"}, /* obsolete: spc5r02 */
2356 {0x13, "<i>tape -> <i>tape"},
2357 {0x14, "register persistent reservation key"},
2358 {0x15, "third party persistent reservation source I_T nexus"},
2359 {0x16, "<i>block -> <i>block"},
2360 {0x17, "positioning -> tape"}, /* this and next added spc5r02 */
2361 {0x18, "<loi>tape -> <loi>tape"}, /* loi: logical object identifier */
2362 {0xbe, "ROD <- block range(n)"},
2363 {0xbf, "ROD <- block range(1)"},
2364 {0xe0, "CSCD: FC N_Port_Name"},
2365 {0xe1, "CSCD: FC N_Port_ID"},
2366 {0xe2, "CSCD: FC N_Port_ID with N_Port_Name, checking"},
2367 {0xe3, "CSCD: Parallel interface: I_T"},
2368 {0xe4, "CSCD: Identification Descriptor"},
2369 {0xe5, "CSCD: IPv4"},
2370 {0xe6, "CSCD: Alias"},
2371 {0xe7, "CSCD: RDMA"},
2372 {0xe8, "CSCD: IEEE 1394 EUI-64"},
2373 {0xe9, "CSCD: SAS SSP"},
2374 {0xea, "CSCD: IPv6"},
2375 {0xeb, "CSCD: IP copy service"},
2376 {0xfe, "CSCD: ROD"},
2377 {0xff, "CSCD: extension"},
2378 {0x0, NULL},
2379 };
2380
2381 static const char *
get_tpc_desc_name(uint8_t code)2382 get_tpc_desc_name(uint8_t code)
2383 {
2384 const struct tpc_desc_type * dtp;
2385
2386 for (dtp = tpc_desc_arr; dtp->name; ++dtp) {
2387 if (code == dtp->code)
2388 return dtp->name;
2389 }
2390 return "";
2391 }
2392
2393 struct tpc_rod_type {
2394 uint32_t type;
2395 const char * name;
2396 };
2397
2398 static struct tpc_rod_type tpc_rod_arr[] = {
2399 {0x0, "copy manager internal"},
2400 {0x10000, "access upon reference"},
2401 {0x800000, "point in time copy - default"},
2402 {0x800001, "point in time copy - change vulnerable"},
2403 {0x800002, "point in time copy - persistent"},
2404 {0x80ffff, "point in time copy - any"},
2405 {0xffff0001, "block device zero"},
2406 {0x0, NULL},
2407 };
2408
2409 static const char *
get_tpc_rod_name(uint32_t rod_type)2410 get_tpc_rod_name(uint32_t rod_type)
2411 {
2412 const struct tpc_rod_type * rtp;
2413
2414 for (rtp = tpc_rod_arr; rtp->name; ++rtp) {
2415 if (rod_type == rtp->type)
2416 return rtp->name;
2417 }
2418 return "";
2419 }
2420
2421 struct cscd_desc_id_t {
2422 uint16_t id;
2423 const char * name;
2424 };
2425
2426 static struct cscd_desc_id_t cscd_desc_id_arr[] = {
2427 /* only values higher than 0x7ff are listed */
2428 {0xc000, "copy src or dst null LU, pdt=0"},
2429 {0xc001, "copy src or dst null LU, pdt=1"},
2430 {0xf800, "copy src or dst in ROD token"},
2431 {0xffff, "copy src or dst is copy manager LU"},
2432 {0x0, NULL},
2433 };
2434
2435 static const char *
get_cscd_desc_id_name(uint16_t cscd_desc_id)2436 get_cscd_desc_id_name(uint16_t cscd_desc_id)
2437 {
2438 const struct cscd_desc_id_t * cdip;
2439
2440 for (cdip = cscd_desc_id_arr; cdip->name; ++cdip) {
2441 if (cscd_desc_id == cdip->id)
2442 return cdip->name;
2443 }
2444 return "";
2445 }
2446
2447 static const char *
get_tpc_desc_type_s(uint32_t desc_type)2448 get_tpc_desc_type_s(uint32_t desc_type)
2449 {
2450 switch(desc_type) {
2451 case 0:
2452 return "Block Device ROD Limits";
2453 case 1:
2454 return "Supported Commands";
2455 case 4:
2456 return "Parameter Data";
2457 case 8:
2458 return "Supported Descriptors";
2459 case 0xc:
2460 return "Supported CSCD Descriptor IDs";
2461 case 0xd:
2462 return "Copy Group Identifier";
2463 case 0x106:
2464 return "ROD Token Features";
2465 case 0x108:
2466 return "Supported ROD Token and ROD Types";
2467 case 0x8001:
2468 return "General Copy Operations";
2469 case 0x9101:
2470 return "Stream Copy Operations";
2471 case 0xC001:
2472 return "Held Data";
2473 default:
2474 if ((desc_type >= 0xE000) && (desc_type <= 0xEFFF))
2475 return "Restricted";
2476 else
2477 return "Reserved";
2478 }
2479 }
2480
2481 /* VPD_3PARTY_COPY 3PC, third party copy 0x8f ["tpc"] */
2482 void
decode_3party_copy_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)2483 decode_3party_copy_vpd(const uint8_t * buff, int len,
2484 struct opts_t * op, sgj_opaque_p jap)
2485 {
2486 int j, k, m, bump, desc_type, desc_len, sa_len, pdt;
2487 uint32_t u, v;
2488 uint64_t ull;
2489 const uint8_t * bp;
2490 const char * cp;
2491 const char * dtp;
2492 sgj_state * jsp = &op->json_st;
2493 sgj_opaque_p jo2p = NULL;
2494 sgj_opaque_p ja2p = NULL;
2495 sgj_opaque_p jo3p = NULL;
2496 char b[144];
2497 static const int blen = sizeof(b);
2498
2499 if (len < 4) {
2500 pr2serr("VPD page length too short=%d\n", len);
2501 return;
2502 }
2503 if (3 == op->do_hex) {
2504 hex2stdout(buff, len, -1);
2505 return;
2506 }
2507 pdt = buff[0] & PDT_MASK;
2508 len -= 4;
2509 bp = buff + 4;
2510 for (k = 0; k < len; k += bump, bp += bump) {
2511 jo2p = sgj_new_unattached_object_r(jsp);
2512 desc_type = sg_get_unaligned_be16(bp);
2513 desc_len = sg_get_unaligned_be16(bp + 2);
2514 if (op->verbose)
2515 sgj_pr_hr(jsp, "Descriptor type=%d [0x%x] , len %d\n", desc_type,
2516 desc_type, desc_len);
2517 bump = 4 + desc_len;
2518 if ((k + bump) > len) {
2519 pr2serr("VPD page, short descriptor length=%d, left=%d\n", bump,
2520 (len - k));
2521 break;
2522 }
2523 if (0 == desc_len)
2524 goto skip; /* continue plus attach jo2p */
2525 if (2 == op->do_hex)
2526 hex2stdout(bp + 4, desc_len, 1);
2527 else if (op->do_hex > 2)
2528 hex2stdout(bp, bump, 1);
2529 else {
2530 int csll;
2531
2532 dtp = get_tpc_desc_type_s(desc_type);
2533 sgj_js_nv_ihexstr(jsp, jo2p, "third_party_copy_descriptor_type",
2534 desc_type, NULL, dtp);
2535 sgj_js_nv_ihex(jsp, jo2p, "third_party_copy_descriptor_length",
2536 desc_len);
2537
2538 switch (desc_type) {
2539 case 0x0000: /* Required if POPULATE TOKEN (or friend) used */
2540 sgj_pr_hr(jsp, " %s:\n", dtp);
2541 u = sg_get_unaligned_be16(bp + 10);
2542 sgj_haj_vistr(jsp, jo2p, 2, "Maximum range descriptors",
2543 SGJ_SEP_COLON_1_SPACE, u, true,
2544 (0 == u) ? nr_s : NULL);
2545 u = sg_get_unaligned_be32(bp + 12);
2546 if (0 == u)
2547 cp = nr_s;
2548 else if (SG_LIB_UNBOUNDED_32BIT == u)
2549 cp = "No maximum given";
2550 else
2551 cp = NULL;
2552 sgj_haj_vistr_nex(jsp, jo2p, 2, "Maximum inactivity timeout",
2553 SGJ_SEP_COLON_1_SPACE, u, true, cp,
2554 "unit: second");
2555 u = sg_get_unaligned_be32(bp + 16);
2556 sgj_haj_vistr_nex(jsp, jo2p, 2, "Default inactivity timeout",
2557 SGJ_SEP_COLON_1_SPACE, u, true,
2558 (0 == u) ? nr_s : NULL, "unit: second");
2559 ull = sg_get_unaligned_be64(bp + 20);
2560 sgj_haj_vistr_nex(jsp, jo2p, 2, "Maximum token transfer size",
2561 SGJ_SEP_COLON_1_SPACE, ull, true,
2562 (0 == ull) ? nr_s : NULL, "unit: LB");
2563 ull = sg_get_unaligned_be64(bp + 28);
2564 sgj_haj_vistr_nex(jsp, jo2p, 2, "Optimal transfer count",
2565 SGJ_SEP_COLON_1_SPACE, ull, true,
2566 (0 == ull) ? nr_s : NULL, "unit: LB");
2567 break;
2568 case 0x0001: /* Mandatory (SPC-4) */
2569 sgj_pr_hr(jsp, " %s:\n", "Commands supported list");
2570 ja2p = sgj_named_subarray_r(jsp, jo2p,
2571 "commands_supported_list");
2572 j = 0;
2573 csll = bp[4];
2574 if (csll >= desc_len) {
2575 pr2serr("Command supported list length (%d) >= "
2576 "descriptor length (%d), wrong so trim\n",
2577 csll, desc_len);
2578 csll = desc_len - 1;
2579 }
2580 while (j < csll) {
2581 uint8_t opc, sa;
2582 static const char * soc = "supported_operation_code";
2583 static const char * ssa = "supported_service_action";
2584
2585 jo3p = NULL;
2586 opc = bp[5 + j];
2587 sa_len = bp[6 + j];
2588 for (m = 0; (m < sa_len) && ((j + m) < csll); ++m) {
2589 jo3p = sgj_new_unattached_object_r(jsp);
2590 sa = bp[7 + j + m];
2591 sg_get_opcode_sa_name(opc, sa, pdt, blen, b);
2592 sgj_pr_hr(jsp, " %s\n", b);
2593 sgj_js_nv_s(jsp, jo3p, "name", b);
2594 sgj_js_nv_ihex(jsp, jo3p, soc, opc);
2595 sgj_js_nv_ihex(jsp, jo3p, ssa, sa);
2596 sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p);
2597 }
2598 if (0 == sa_len) {
2599 jo3p = sgj_new_unattached_object_r(jsp);
2600 sg_get_opcode_name(opc, pdt, blen, b);
2601 sgj_pr_hr(jsp, " %s\n", b);
2602 sgj_js_nv_s(jsp, jo3p, "name", b);
2603 sgj_js_nv_ihex(jsp, jo3p, soc, opc);
2604 sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p);
2605 } else if (m < sa_len)
2606 pr2serr("Supported service actions list length (%d) "
2607 "is too large\n", sa_len);
2608 j += m + 2;
2609 }
2610 break;
2611 case 0x0004:
2612 sgj_pr_hr(jsp, " %s:\n", dtp);
2613 sgj_haj_vi(jsp, jo2p, 2, "Maximum CSCD descriptor count",
2614 SGJ_SEP_COLON_1_SPACE,
2615 sg_get_unaligned_be16(bp + 8), true);
2616 sgj_haj_vi(jsp, jo2p, 2, "Maximum segment descriptor count",
2617 SGJ_SEP_COLON_1_SPACE,
2618 sg_get_unaligned_be16(bp + 10), true);
2619 sgj_haj_vi(jsp, jo2p, 2, "Maximum descriptor list length",
2620 SGJ_SEP_COLON_1_SPACE,
2621 sg_get_unaligned_be32(bp + 12), true);
2622 sgj_haj_vi(jsp, jo2p, 2, "Maximum inline data length",
2623 SGJ_SEP_COLON_1_SPACE,
2624 sg_get_unaligned_be32(bp + 17), true);
2625 break;
2626 case 0x0008:
2627 sgj_pr_hr(jsp, " Supported descriptors:\n");
2628 ja2p = sgj_named_subarray_r(jsp, jo2p,
2629 "supported_descriptor_list");
2630 for (j = 0; j < bp[4]; j++) {
2631 bool found_name;
2632
2633 jo3p = sgj_new_unattached_object_r(jsp);
2634 u = bp[5 + j];
2635 cp = get_tpc_desc_name(u);
2636 found_name = (strlen(cp) > 0);
2637 if (found_name)
2638 sgj_pr_hr(jsp, " %s [0x%x]\n", cp, u);
2639 else
2640 sgj_pr_hr(jsp, " 0x%x\n", u);
2641 sgj_js_nv_s(jsp, jo3p, "name", found_name ? cp : nr_s);
2642 sgj_js_nv_ihex(jsp, jo3p, "code", u);
2643 sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p);
2644 }
2645 break;
2646 case 0x000C:
2647 sgj_pr_hr(jsp, " Supported CSCD IDs (above 0x7ff):\n");
2648 ja2p = sgj_named_subarray_r(jsp, jo2p, "supported_cscd_"
2649 "descriptor_id_list");
2650 v = sg_get_unaligned_be16(bp + 4);
2651 for (j = 0; j < (int)v; j += 2) {
2652 bool found_name;
2653
2654 jo3p = sgj_new_unattached_object_r(jsp);
2655 u = sg_get_unaligned_be16(bp + 6 + j);
2656 cp = get_cscd_desc_id_name(u);
2657 found_name = (strlen(cp) > 0);
2658 if (found_name)
2659 sgj_pr_hr(jsp, " %s [0x%04x]\n", cp, u);
2660 else
2661 sgj_pr_hr(jsp, " 0x%04x\n", u);
2662 sgj_js_nv_s(jsp, jo3p, "name", found_name ? cp : nr_s);
2663 sgj_js_nv_ihex(jsp, jo3p, "id", u);
2664 sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p);
2665 }
2666 break;
2667 case 0x000D:
2668 sgj_pr_hr(jsp, " Copy group identifier:\n");
2669 u = bp[4];
2670 sg_t10_uuid_desig2str(bp + 5, u, 1 /* c_set */, false,
2671 true, NULL, blen, b);
2672 sgj_pr_hr(jsp, " Locally assigned UUID: %s", b);
2673 sgj_js_nv_s(jsp, jo2p, "locally_assigned_uuid", b);
2674 break;
2675 case 0x0106:
2676 sgj_pr_hr(jsp, " ROD token features:\n");
2677 sgj_haj_vi(jsp, jo2p, 2, "Remote tokens",
2678 SGJ_SEP_COLON_1_SPACE, bp[4] & 0x0f, true);
2679 u = sg_get_unaligned_be32(bp + 16);
2680 sgj_pr_hr(jsp, " Minimum token lifetime: %u seconds\n", u);
2681 sgj_js_nv_ihex_nex(jsp, jo2p, "minimum_token_lifetime", u,
2682 true, "unit: second");
2683 u = sg_get_unaligned_be32(bp + 20);
2684 sgj_pr_hr(jsp, " Maximum token lifetime: %u seconds\n", u);
2685 sgj_js_nv_ihex_nex(jsp, jo2p, "maximum_token_lifetime", u,
2686 true, "unit: second");
2687 u = sg_get_unaligned_be32(bp + 24);
2688 sgj_haj_vi_nex(jsp, jo2p, 2, "Maximum token inactivity "
2689 "timeout", SGJ_SEP_COLON_1_SPACE, u,
2690 true, "unit: second");
2691 u = sg_get_unaligned_be16(bp + 46);
2692 ja2p = sgj_named_subarray_r(jsp, jo2p,
2693 "rod_device_type_specific_features_descriptor_list");
2694 decode_rod_descriptor(bp + 48, u, op, ja2p);
2695 break;
2696 case 0x0108:
2697 sgj_pr_hr(jsp, " Supported ROD token and ROD types:\n");
2698 ja2p = sgj_named_subarray_r(jsp, jo2p, "rod_type_"
2699 "descriptor_list");
2700 for (j = 0; j < sg_get_unaligned_be16(bp + 6); j+= 64) {
2701 bool found_name;
2702
2703 jo3p = sgj_new_unattached_object_r(jsp);
2704 u = sg_get_unaligned_be32(bp + 8 + j);
2705 cp = get_tpc_rod_name(u);
2706 found_name = (strlen(cp) > 0);
2707 if (found_name > 0)
2708 sgj_pr_hr(jsp, " ROD type: %s [0x%x]\n", cp, u);
2709 else
2710 sgj_pr_hr(jsp, " ROD type: 0x%x\n", u);
2711 sgj_js_nv_ihexstr(jsp, jo3p, "rod_type", u, NULL,
2712 found_name ? cp : NULL);
2713 u = bp[8 + j + 4];
2714 sgj_pr_hr(jsp, " ECPY_INT: %s\n",
2715 (u & 0x80) ? y_s : n_s);
2716 sgj_js_nv_ihex_nex(jsp, jo3p, "ecpy_int", !!(0x80 & u),
2717 false, "Extended CoPY INTernal rods");
2718 sgj_pr_hr(jsp, " Token in: %s\n",
2719 (u & 0x2) ? y_s : n_s);
2720 sgj_js_nv_i(jsp, jo3p, "token_in", !!(0x2 & u));
2721 sgj_pr_hr(jsp, " Token out: %s\n",
2722 (u & 0x1) ? y_s : n_s);
2723 sgj_js_nv_i(jsp, jo3p, "token_out", !!(0x2 & u));
2724 u = sg_get_unaligned_be16(bp + 8 + j + 6);
2725 sgj_haj_vi(jsp, jo3p, 4, "Preference indicator",
2726 SGJ_SEP_COLON_1_SPACE, u, true);
2727 sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p);
2728 }
2729 break;
2730 case 0x8001: /* Mandatory (SPC-4) */
2731 sgj_pr_hr(jsp, " General copy operations:\n");
2732 u = sg_get_unaligned_be32(bp + 4);
2733 sgj_haj_vi(jsp, jo2p, 2, "Total concurrent copies",
2734 SGJ_SEP_COLON_1_SPACE, u, true);
2735 u = sg_get_unaligned_be32(bp + 8);
2736 sgj_haj_vi(jsp, jo2p, 2, "Maximum identified concurrent "
2737 "copies", SGJ_SEP_COLON_1_SPACE, u, true);
2738 u = sg_get_unaligned_be32(bp + 12);
2739 sgj_haj_vi_nex(jsp, jo2p, 2, "Maximum segment length",
2740 SGJ_SEP_COLON_1_SPACE, u, true, "unit: byte");
2741 u = bp[16]; /* field is power of 2 */
2742 sgj_haj_vi_nex(jsp, jo2p, 2, "Data segment granularity",
2743 SGJ_SEP_COLON_1_SPACE, u, true,
2744 "unit: 2^val LB");
2745 u = bp[17]; /* field is power of 2 */
2746 sgj_haj_vi_nex(jsp, jo2p, 2, "Inline data granularity",
2747 SGJ_SEP_COLON_1_SPACE, u, true,
2748 "unit: 2^val LB");
2749 break;
2750 case 0x9101:
2751 sgj_pr_hr(jsp, " Stream copy operations:\n");
2752 u = sg_get_unaligned_be32(bp + 4);
2753 sgj_haj_vi_nex(jsp, jo2p, 2, "Maximum stream device transfer "
2754 "size", SGJ_SEP_COLON_1_SPACE, u, true,
2755 "unit: byte");
2756 break;
2757 case 0xC001:
2758 sgj_pr_hr(jsp, " Held data:\n");
2759 u = sg_get_unaligned_be32(bp + 4);
2760 sgj_haj_vi_nex(jsp, jo2p, 2, "Held data limit",
2761 SGJ_SEP_COLON_1_SPACE, u, true,
2762 "unit: byte; (lower limit: minimum)");
2763 sgj_haj_vi_nex(jsp, jo2p, 2, "Held data granularity",
2764 SGJ_SEP_COLON_1_SPACE, bp[8], true,
2765 "unit: 2^val byte");
2766 break;
2767 default:
2768 pr2serr("Unexpected type=%d\n", desc_type);
2769 hex2stderr(bp, bump, 1);
2770 break;
2771 }
2772 }
2773 skip:
2774 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2775 jo2p = NULL;
2776 }
2777 if (jo2p)
2778 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2779 }
2780
2781 /* VPD_PROTO_LU 0x90 ["pslu"] */
2782 void
decode_proto_lu_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)2783 decode_proto_lu_vpd(const uint8_t * buff, int len, struct opts_t * op,
2784 sgj_opaque_p jap)
2785 {
2786 int k, bump, rel_port, desc_len, proto;
2787 const uint8_t * bp;
2788 sgj_state * jsp = &op->json_st;
2789 sgj_opaque_p jo2p = NULL;
2790 char b[128];
2791 static const int blen = sizeof(b);
2792
2793 if ((1 == op->do_hex) || (op->do_hex > 2)) {
2794 hex2stdout(buff, len, (1 == op->do_hex) ? 1 : -1);
2795 return;
2796 }
2797 if (len < 4) {
2798 pr2serr("VPD page length too short=%d\n", len);
2799 return;
2800 }
2801 len -= 4;
2802 bp = buff + 4;
2803 for (k = 0; k < len; k += bump, bp += bump) {
2804 jo2p = sgj_new_unattached_object_r(jsp);
2805 rel_port = sg_get_unaligned_be16(bp);
2806 sgj_haj_vi(jsp, jo2p, 2, "Relative port",
2807 SGJ_SEP_COLON_1_SPACE, rel_port, true);
2808 proto = bp[2] & 0xf;
2809 sg_get_trans_proto_str(proto, blen, b);
2810 sgj_haj_vistr(jsp, jo2p, 4, "Protocol identifier",
2811 SGJ_SEP_COLON_1_SPACE, proto, false, b);
2812 desc_len = sg_get_unaligned_be16(bp + 6);
2813 bump = 8 + desc_len;
2814 if ((k + bump) > len) {
2815 pr2serr("Protocol-specific logical unit information VPD page, "
2816 "short descriptor length=%d, left=%d\n", bump, (len - k));
2817 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2818 return;
2819 }
2820 if (0 == desc_len)
2821 goto again;
2822 if (2 == op->do_hex) {
2823 hex2stdout(bp + 8, desc_len, 1);
2824 goto again;
2825 }
2826 switch (proto) {
2827 case TPROTO_SAS:
2828 sgj_haj_vi(jsp, jo2p, 2, "TLR control supported",
2829 SGJ_SEP_COLON_1_SPACE, !!(bp[8] & 0x1), false);
2830 break;
2831 default:
2832 pr2serr("Unexpected proto=%d\n", proto);
2833 hex2stderr(bp, bump, 1);
2834 break;
2835 }
2836 again:
2837 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2838 }
2839 }
2840
2841 /* VPD_PROTO_PORT 0x91 ["pspo"] */
2842 void
decode_proto_port_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)2843 decode_proto_port_vpd(const uint8_t * buff, int len, struct opts_t * op,
2844 sgj_opaque_p jap)
2845 {
2846 bool pds, ssp_pers;
2847 int k, j, bump, rel_port, desc_len, proto, phy;
2848 const uint8_t * bp;
2849 const uint8_t * pidp;
2850 sgj_state * jsp = &op->json_st;
2851 sgj_opaque_p jo2p = NULL;
2852 sgj_opaque_p ja2p = NULL;
2853 sgj_opaque_p jo3p = NULL;
2854 char b[128];
2855 static const int blen = sizeof(b);
2856
2857 if ((1 == op->do_hex) || (op->do_hex > 2)) {
2858 hex2stdout(buff, len, (1 == op->do_hex) ? 1 : -1);
2859 return;
2860 }
2861 if (len < 4) {
2862 pr2serr("VPD page length too short=%d\n", len);
2863 return;
2864 }
2865 len -= 4;
2866 bp = buff + 4;
2867 for (k = 0; k < len; k += bump, bp += bump) {
2868 jo2p = sgj_new_unattached_object_r(jsp);
2869 rel_port = sg_get_unaligned_be16(bp);
2870 sgj_haj_vi(jsp, jo2p, 2, "Relative port",
2871 SGJ_SEP_COLON_1_SPACE, rel_port, true);
2872 proto = bp[2] & 0xf;
2873 sg_get_trans_proto_str(proto, blen, b);
2874 sgj_haj_vistr(jsp, jo2p, 4, "Protocol identifier",
2875 SGJ_SEP_COLON_1_SPACE, proto, false, b);
2876 desc_len = sg_get_unaligned_be16(bp + 6);
2877 bump = 8 + desc_len;
2878 if ((k + bump) > len) {
2879 pr2serr("VPD page, short descriptor length=%d, left=%d\n",
2880 bump, (len - k));
2881 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2882 return;
2883 }
2884 if (0 == desc_len)
2885 goto again;
2886 if (2 == op->do_hex) {
2887 hex2stdout(bp + 8, desc_len, 1);
2888 goto again;
2889 }
2890 switch (proto) {
2891 case TPROTO_SAS: /* page added in spl3r02 */
2892 pds = !!(bp[3] & 0x1);
2893 sgj_pr_hr(jsp, " power disable supported (pwr_d_s)=%d\n", pds);
2894 sgj_js_nv_ihex_nex(jsp, jo2p, "pwr_d_s", pds, false,
2895 "PoWeR Disable Supported");
2896 ja2p = sgj_named_subarray_r(jsp, jo2p,
2897 "sas_phy_information_descriptor_list");
2898 pidp = bp + 8;
2899 for (j = 0; j < desc_len; j += 4, pidp += 4) {
2900 jo3p = sgj_new_unattached_object_r(jsp);
2901 phy = pidp[1];
2902 ssp_pers = !!(0x1 & pidp[2]);
2903 sgj_pr_hr(jsp, " phy id=%d, SSP persistent capable=%d\n",
2904 phy, ssp_pers);
2905 sgj_js_nv_ihex(jsp, jo3p, "phy_identifier", phy);
2906 sgj_js_nv_i(jsp, jo3p, "ssp_persistent_capable", ssp_pers);
2907 sgj_js_nv_o(jsp, ja2p, NULL /* name */, jo3p);
2908 }
2909 break;
2910 default:
2911 pr2serr("Unexpected proto=%d\n", proto);
2912 hex2stderr(bp, bump, 1);
2913 break;
2914 }
2915 again:
2916 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2917 }
2918 }
2919
2920 /* VPD_LB_PROTECTION 0xb5 (SSC) [added in ssc5r02a] */
2921 void
decode_lb_protection_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)2922 decode_lb_protection_vpd(const uint8_t * buff, int len, struct opts_t * op,
2923 sgj_opaque_p jap)
2924 {
2925 int k, bump;
2926 const uint8_t * bp;
2927 sgj_state * jsp = &op->json_st;
2928 sgj_opaque_p jo2p = NULL;
2929
2930 if ((1 == op->do_hex) || (op->do_hex > 2)) {
2931 hex2stdout(buff, len, (1 == op->do_hex) ? 0 : -1);
2932 return;
2933 }
2934 if (len < 8) {
2935 pr2serr("VPD page length too short=%d\n", len);
2936 return;
2937 }
2938 len -= 8;
2939 bp = buff + 8;
2940 for (k = 0; k < len; k += bump, bp += bump) {
2941 jo2p = sgj_new_unattached_object_r(jsp);
2942 bump = 1 + bp[0];
2943 sgj_pr_hr(jsp, " method: %d, info_len: %d, LBP_W_C=%d, LBP_R_C=%d, "
2944 "RBDP_C=%d\n", bp[1], 0x3f & bp[2], !!(0x80 & bp[3]),
2945 !!(0x40 & bp[3]), !!(0x20 & bp[3]));
2946 sgj_js_nv_ihex(jsp, jo2p, "logical_block_protection_method", bp[1]);
2947 sgj_js_nv_ihex_nex(jsp, jo2p,
2948 "logical_block_protection_information_length",
2949 0x3f & bp[2], true, "unit: byte");
2950 sgj_js_nv_ihex_nex(jsp, jo2p, "lbp_w_c", !!(0x80 & bp[3]), false,
2951 "Logical Blocks Protected during Write supported");
2952 sgj_js_nv_ihex_nex(jsp, jo2p, "lbp_r_c", !!(0x40 & bp[3]), false,
2953 "Logical Blocks Protected during Read supported");
2954 sgj_js_nv_ihex_nex(jsp, jo2p, "rbdp_c", !!(0x20 & bp[3]), false,
2955 "Recover Buffered Data Protected supported");
2956 if ((k + bump) > len) {
2957 pr2serr("Logical block protection VPD page, short "
2958 "descriptor length=%d, left=%d\n", bump, (len - k));
2959 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2960 return;
2961 }
2962 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
2963 }
2964 }
2965
2966 /* VPD_TA_SUPPORTED 0xb2 ["tas"] */
2967 void
decode_tapealert_supported_vpd(const uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)2968 decode_tapealert_supported_vpd(const uint8_t * buff, int len,
2969 struct opts_t * op, sgj_opaque_p jop)
2970 {
2971 bool have_ta_strs = !! sg_lib_tapealert_strs[0];
2972 int k, mod, div, n;
2973 unsigned int supp;
2974 sgj_state * jsp = &op->json_st;
2975 char b[144];
2976 char d[64];
2977 static const int blen = sizeof(b);
2978
2979 if (len < 12) {
2980 pr2serr("VPD page length too short=%d\n", len);
2981 return;
2982 }
2983 b[0] ='\0';
2984 for (k = 1, n = 0; k < 0x41; ++k) {
2985 mod = ((k - 1) % 8);
2986 div = (k - 1) / 8;
2987 supp = !! (buff[4 + div] & (1 << (7 - mod)));
2988 if (jsp->pr_as_json) {
2989 snprintf(d, sizeof(d), "flag%02xh", k);
2990 if (have_ta_strs)
2991 sgj_js_nv_ihex_nex(jsp, jop, d, supp, false,
2992 sg_lib_tapealert_strs[k]);
2993 else
2994 sgj_js_nv_i(jsp, jop, d, supp);
2995 }
2996 if (0 == mod) {
2997 if (div > 0) {
2998 sgj_pr_hr(jsp, "%s\n", b);
2999 n = 0;
3000 }
3001 n += sg_scnpr(b + n, blen - n, " Flag%02Xh: %d", k, supp);
3002 } else
3003 n += sg_scnpr(b + n, blen - n, " %02Xh: %d", k, supp);
3004 }
3005 sgj_pr_hr(jsp, "%s\n", b);
3006 }
3007
3008 /*
3009 * Some of the vendor specific VPD pages are common as well. So place them here
3010 * to save on code duplication.
3011 */
3012
3013 static const char * lun_state_arr[] =
3014 {
3015 "LUN not bound or LUN_Z report",
3016 "LUN bound, but not owned by this SP",
3017 "LUN bound and owned by this SP",
3018 };
3019
3020 static const char * ip_mgmt_arr[] =
3021 {
3022 "No IP access",
3023 "Reserved (undefined)",
3024 "via IPv4",
3025 "via IPv6",
3026 };
3027
3028 static const char * sp_arr[] =
3029 {
3030 "SP A",
3031 "SP B",
3032 };
3033
3034 static const char * lun_op_arr[] =
3035 {
3036 "Normal operations",
3037 "I/O Operations being rejected, SP reboot or NDU in progress",
3038 };
3039
3040 static const char * failover_mode_arr[] =
3041 {
3042 "Legacy mode 0",
3043 "Unknown mode (1)",
3044 "Unknown mode (2)",
3045 "Unknown mode (3)",
3046 "Active/Passive (PNR) mode 1",
3047 "Unknown mode (5)",
3048 "Active/Active (ALUA) mode 4",
3049 "Unknown mode (7)",
3050 "Legacy mode 2",
3051 "Unknown mode (9)",
3052 "Unknown mode (10)",
3053 "Unknown mode (11)",
3054 "Unknown mode (12)",
3055 "Unknown mode (13)",
3056 "AIX Active/Passive (PAR) mode 3",
3057 "Unknown mode (15)",
3058 };
3059
3060 /* VPD_UPR_EMC,VPD_V_UPR_EMC 0xc0 ["upr","upr"] */
3061 void
decode_upr_vpd_c0_emc(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)3062 decode_upr_vpd_c0_emc(uint8_t * buff, int len, struct opts_t * op,
3063 sgj_opaque_p jop)
3064 {
3065 uint8_t uc;
3066 int k, n, ip_mgmt, vpp80, lun_z;
3067 sgj_state * jsp = &op->json_st;
3068 const char * cp;
3069 const char * c2p;
3070 char b[256];
3071 static const int blen = sizeof(b);
3072
3073 if (len < 3) {
3074 pr2serr("EMC upr VPD page [0xc0]: length too short=%d\n", len);
3075 return;
3076 }
3077 if (op->do_hex) {
3078 hex2stdout(buff, len, no_ascii_4hex(op));
3079 return;
3080 }
3081 if (buff[9] != 0x00) {
3082 pr2serr("Unsupported page revision %d, decoding not possible.\n",
3083 buff[9]);
3084 return;
3085 }
3086 for (k = 0, n = 0; k < 16; ++k)
3087 n += sg_scnpr(b + n, blen - n, "%02x", buff[10 + k]);
3088 sgj_haj_vs(jsp, jop, 2, "LUN WWN", SGJ_SEP_COLON_1_SPACE, b);
3089 snprintf(b, blen, "%.*s", buff[49], buff + 50);
3090 sgj_haj_vs(jsp, jop, 2, "Array Serial Number", SGJ_SEP_COLON_1_SPACE, b);
3091
3092 if (buff[4] > 0x02)
3093 snprintf(b, blen, "Unknown (%x)", buff[4]);
3094 else
3095 snprintf(b, blen, "%s", lun_state_arr[buff[4]]);
3096 sgj_haj_vistr(jsp, jop, 2, "LUN State", SGJ_SEP_COLON_1_SPACE,
3097 buff[4], true, b);
3098
3099 uc = buff[8];
3100 n = 0;
3101 if (uc > 0x01)
3102 n += sg_scnpr(b + n, blen - n, "Unknown SP (%x)", uc);
3103 else
3104 n += sg_scnpr(b + n, blen - n, "%s", sp_arr[uc]);
3105 sgj_js_nv_ihexstr(jsp, jop, "path_connects_to", uc, NULL, b);
3106 n += sg_scnpr(b + n, blen - n, ", Port Number: %u", buff[7]);
3107 sgj_pr_hr(jsp, " This path connects to: %s\n", b);
3108 sgj_js_nv_ihex(jsp, jop, "port_number", buff[7]);
3109
3110 if (buff[5] > 0x01)
3111 snprintf(b, blen, "Unknown (%x)\n", buff[5]);
3112 else
3113 snprintf(b, blen, "%s\n", sp_arr[buff[5]]);
3114 sgj_haj_vistr(jsp, jop, 2, "Default owner", SGJ_SEP_COLON_1_SPACE,
3115 buff[5], true, b);
3116
3117 cp = (buff[6] & 0x40) ? "supported" : "not supported";
3118 sgj_pr_hr(jsp, " NO_ATF: %s, Access Logix: %s\n",
3119 buff[6] & 0x80 ? "set" : "not set", cp);
3120 sgj_js_nv_i(jsp, jop, "no_atf", !! (buff[6] & 0x80));
3121 sgj_js_nv_istr(jsp, jop, "access_logix", !! (buff[6] & 0x40),
3122 NULL, cp);
3123
3124 ip_mgmt = (buff[6] >> 4) & 0x3;
3125 cp = ip_mgmt_arr[ip_mgmt];
3126 sgj_pr_hr(jsp, " SP IP Management Mode: %s\n", cp);
3127 sgj_js_nv_istr(jsp, jop, "sp_ip_management_mode", !! ip_mgmt,
3128 NULL, cp);
3129 if (ip_mgmt == 2) {
3130 snprintf(b, blen, "%u.%u.%u.%u", buff[44], buff[45], buff[46],
3131 buff[47]);
3132 sgj_pr_hr(jsp, " SP IPv4 address: %s\n", b);
3133 sgj_js_nv_s(jsp, jop, "sp_ipv4_address", b);
3134 } else if (ip_mgmt == 3) {
3135 printf(" SP IPv6 address: ");
3136 n = 0;
3137 for (k = 0; k < 16; ++k)
3138 n += sg_scnpr(b + n, blen - n, "%02x", buff[32 + k]);
3139 sgj_pr_hr(jsp, " SP IPv6 address: %s\n", b);
3140 sgj_js_nv_hex_bytes(jsp, jop, "sp_ipv6_address", buff + 32, 16);
3141 }
3142
3143 k = buff[28] & 0x0f;
3144 sgj_pr_hr(jsp, " System Type: %x, Failover mode: %s\n",
3145 buff[27], failover_mode_arr[k]);
3146 sgj_js_nv_ihex(jsp, jop, "system_type", buff[27]);
3147 sgj_js_nv_ihexstr(jsp, jop, "failover_mode", k, NULL,
3148 failover_mode_arr[k]);
3149
3150 vpp80 = buff[30] & 0x08;
3151 lun_z = buff[30] & 0x04;
3152 cp = vpp80 ? "array serial#" : "LUN serial#";
3153 c2p = lun_z ? "Set to 1" : "Unknown";
3154 sgj_pr_hr(jsp, " Inquiry VPP 0x80 returns: %s, Arraycommpath: %s\n",
3155 cp, c2p);
3156 sgj_js_nv_istr(jsp, jop, "inquiry_vpp_0x80_returns", !! vpp80, NULL, cp);
3157 sgj_js_nv_istr(jsp, jop, "arraycommpath", !! lun_z, NULL, c2p);
3158
3159 cp = buff[48] > 1 ? "undefined" : lun_op_arr[buff[48]];
3160 sgj_pr_hr(jsp, " Lun operations: %s\n", cp);
3161 sgj_js_nv_istr(jsp, jop, "lun_operations", 0x1 & buff[48], NULL, cp);
3162
3163 return;
3164 }
3165
3166 /* VPD_RDAC_VERS,VPD_V_SVER_RDAC 0xc2 ["rdac_vers", "swr4"] */
3167 void
decode_rdac_vpd_c2(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)3168 decode_rdac_vpd_c2(uint8_t * buff, int len, struct opts_t * op,
3169 sgj_opaque_p jop)
3170 {
3171 int i, n, v, r, m, p, d, y, num_part;
3172 sgj_state * jsp = &op->json_st;
3173 sgj_opaque_p jo2p = NULL;
3174 sgj_opaque_p jap = NULL;
3175 // const char * cp;
3176 // const char * c2p;
3177 char b[256];
3178 static const int blen = sizeof(b);
3179 char part[5];
3180
3181 if (len < 3) {
3182 pr2serr("Software Version VPD page length too short=%d\n", len);
3183 return;
3184 }
3185 if (op->do_hex) {
3186 hex2stdout(buff, len, no_ascii_4hex(op));
3187 return;
3188 }
3189 if (buff[4] != 's' && buff[5] != 'w' && buff[6] != 'r') {
3190 pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n",
3191 buff[4], buff[5], buff[6], buff[7]);
3192 return;
3193 }
3194 snprintf(b, blen, "%02x.%02x.%02x", buff[8], buff[9], buff[10]);
3195 sgj_haj_vs(jsp, jop, 2, "Software Version", SGJ_SEP_COLON_1_SPACE, b);
3196 snprintf(b, blen, "%02d/%02d/%02d\n", buff[11], buff[12], buff[13]);
3197 sgj_haj_vs(jsp, jop, 2, "Software Date", SGJ_SEP_COLON_1_SPACE, b);
3198 n = 0;
3199 n += sg_scnpr(b + n, blen - n, " Features:");
3200 if (buff[14] & 0x01)
3201 n += sg_scnpr(b + n, blen - n, " Dual Active,");
3202 if (buff[14] & 0x02)
3203 n += sg_scnpr(b + n, blen - n, " Series 3,");
3204 if (buff[14] & 0x04)
3205 n += sg_scnpr(b + n, blen - n, " Multiple Sub-enclosures,");
3206 if (buff[14] & 0x08)
3207 n += sg_scnpr(b + n, blen - n, " DCE/DRM/DSS/DVE,");
3208 if (buff[14] & 0x10)
3209 n += sg_scnpr(b + n, blen - n, " Asymmetric Logical Unit Access,");
3210 sgj_pr_hr(jsp, "%s\n", b);
3211 if (jsp->pr_as_json) {
3212 jo2p = sgj_snake_named_subobject_r(jsp, jop, "features");
3213 sgj_js_nv_i(jsp, jo2p, "dual_active", !! (buff[14] & 0x01));
3214 sgj_js_nv_i(jsp, jo2p, "series_3", !! (buff[14] & 0x02));
3215 sgj_js_nv_i(jsp, jo2p, "multiple_sub_enclosures",
3216 !! (buff[14] & 0x04));
3217 sgj_js_nv_i(jsp, jo2p, "dcm_drm_dss_dve", !! (buff[14] & 0x08));
3218 sgj_js_nv_i(jsp, jo2p, "asymmetric_logical_unit_access",
3219 !! (buff[14] & 0x10));
3220 }
3221 sgj_haj_vi(jsp, jop, 2, "Maximum number of LUNS",
3222 SGJ_SEP_COLON_1_SPACE, buff[15], true);
3223
3224 num_part = (len - 12) / 16;
3225 n = 16;
3226 printf(" Partitions: %d\n", num_part);
3227 sgj_haj_vi(jsp, jop, 2, "Partitions", SGJ_SEP_COLON_1_SPACE, num_part,
3228 true);
3229 if (num_part > 0)
3230 jap = sgj_named_subarray_r(jsp, jop, "partition_list");
3231 for (i = 0; i < num_part; i++) {
3232 memset(part,0, 5);
3233 memcpy(part, &buff[n], 4);
3234 sgj_pr_hr(jsp, " Name: %s\n", part);
3235 if (jsp->pr_as_json) {
3236 jo2p = sgj_new_unattached_object_r(jsp);
3237 sgj_js_nv_s(jsp, jo2p, "name", part);
3238 }
3239 n += 4;
3240 v = buff[n++];
3241 r = buff[n++];
3242 m = buff[n++];
3243 p = buff[n++];
3244 snprintf(b, blen, "%d.%d.%d.%d", v, r, m, p);
3245 sgj_pr_hr(jsp, " Version: %s\n", b);
3246 if (jsp->pr_as_json)
3247 sgj_js_nv_s(jsp, jo2p, "version", b);
3248 m = buff[n++];
3249 d = buff[n++];
3250 y = buff[n++];
3251 snprintf(b, blen, "%d/%d/%d\n", m, d, y);
3252 sgj_pr_hr(jsp, " Date: %s\n", b);
3253 if (jsp->pr_as_json) {
3254 sgj_js_nv_s(jsp, jo2p, "date", b);
3255 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
3256 }
3257
3258 n += 5;
3259 }
3260 return;
3261 }
3262
3263 static char *
decode_rdac_vpd_c9_aas_s(uint8_t aas,char * b,int blen)3264 decode_rdac_vpd_c9_aas_s(uint8_t aas, char * b, int blen)
3265 {
3266 // snprintf(" Asymmetric Access State:");
3267 switch(aas & 0x0F) {
3268 case 0x0:
3269 snprintf(b, blen, "Active/Optimized");
3270 break;
3271 case 0x1:
3272 snprintf(b, blen, "Active/Non-Optimized");
3273 break;
3274 case 0x2:
3275 snprintf(b, blen, "Standby");
3276 break;
3277 case 0x3:
3278 snprintf(b, blen, "Unavailable");
3279 break;
3280 case 0xE:
3281 snprintf(b, blen, "Offline");
3282 break;
3283 case 0xF:
3284 snprintf(b, blen, "Transitioning");
3285 break;
3286 default:
3287 snprintf(b, blen, "(unknown)");
3288 break;
3289 }
3290 return b;
3291 }
3292
3293 static char *
decode_rdac_vpd_c9_vs_s(uint8_t vendor,char * b,int blen)3294 decode_rdac_vpd_c9_vs_s(uint8_t vendor, char * b, int blen)
3295 {
3296 // printf(" Vendor Specific Field:");
3297 switch(vendor) {
3298 case 0x01:
3299 snprintf(b, blen, "Operating normally");
3300 break;
3301 case 0x02:
3302 snprintf(b, blen, "Non-responsive to queries");
3303 break;
3304 case 0x03:
3305 snprintf(b, blen, "Controller being held in reset");
3306 break;
3307 case 0x04:
3308 snprintf(b, blen, "Performing controller firmware download (1st "
3309 "controller)");
3310 break;
3311 case 0x05:
3312 snprintf(b, blen, "Performing controller firmware download (2nd "
3313 "controller)");
3314 break;
3315 case 0x06:
3316 snprintf(b, blen,
3317 "Quiesced as a result of an administrative request");
3318 break;
3319 case 0x07:
3320 snprintf(b, blen,
3321 "Service mode as a result of an administrative request");
3322 break;
3323 case 0xFF:
3324 snprintf(b, blen, "Details are not available");
3325 break;
3326 default:
3327 snprintf(b, blen, "(unknown)");
3328 break;
3329 }
3330 return b;
3331 }
3332
3333 /* VPD_RDAC_VAC,VPD_V_VAC_RDAC 0xc9 ["rdac_vac", "vac1"] */
3334 void
decode_rdac_vpd_c9(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)3335 decode_rdac_vpd_c9(uint8_t * buff, int len, struct opts_t * op,
3336 sgj_opaque_p jop)
3337 {
3338 bool vav;
3339 int n, n_hold;
3340 sgj_state * jsp = &op->json_st;
3341 char b[196];
3342 static const int blen = sizeof(b);
3343
3344 if (len < 3) {
3345 pr2serr("Volume Access Control VPD page length too short=%d\n", len);
3346 return;
3347 }
3348 if (op->do_hex) {
3349 hex2stdout(buff, len, no_ascii_4hex(op));
3350 return;
3351 }
3352 if (buff[4] != 'v' && buff[5] != 'a' && buff[6] != 'c') {
3353 pr2serr("Invalid page identifier %c%c%c%c, decoding "
3354 "not possible.\n" , buff[4], buff[5], buff[6], buff[7]);
3355 return;
3356 }
3357 if (buff[7] != '1') {
3358 pr2serr("Invalid page version '%c' (should be 1)\n", buff[7]);
3359 }
3360 n = ((buff[8] & 0xE0) == 0xE0 );
3361 if (n) {
3362 sgj_pr_hr(jsp, " IOShipping (ALUA): Enabled\n");
3363 sgj_js_nv_ihexstr_nex(jsp, jop, "ioshipping", n, true, NULL,
3364 "Enabled",
3365 "a.k.a. ALUA (Asymmetric Logical Unit Access)");
3366 } else {
3367 n = 0;
3368 n = snprintf(b, blen, " AVT:");
3369 n_hold = n;
3370 if (buff[8] & 0x80) {
3371 n += sg_scnpr(b + n, blen - n, " Enabled");
3372 if (buff[8] & 0x40)
3373 n += sg_scnpr(b + n, blen - n, " (Allow reads on sector 0)");
3374 sgj_pr_hr(jsp, "%s\n", b);
3375 sgj_js_nv_ihexstr(jsp, jop, "avt", buff[8], NULL, b + n_hold);
3376
3377 } else {
3378 sgj_pr_hr(jsp, "%s: Disabled\n", b);
3379 sgj_js_nv_ihexstr(jsp, jop, "avt", buff[8], NULL, "Disabled");
3380 }
3381 }
3382 vav = !! (0x1 & buff[8]);
3383 sgj_haj_vistr(jsp, jop, 2, "Volume access via", SGJ_SEP_COLON_1_SPACE,
3384 (int)vav, false,
3385 (vav ? "primary controller" : "alternate controller"));
3386
3387 if (buff[8] & 0x08) {
3388 n = buff[15] & 0xf;
3389 // printf(" Path priority: %d ", n);
3390 switch (n) {
3391 case 0x1:
3392 snprintf(b, blen, "(preferred path)");
3393 break;
3394 case 0x2:
3395 snprintf(b, blen, "(secondary path)");
3396 break;
3397 default:
3398 snprintf(b, blen, "(unknown)");
3399 break;
3400 }
3401 sgj_haj_vistr(jsp, jop, 2, "Path priority", SGJ_SEP_COLON_1_SPACE, n,
3402 true, b);
3403
3404 // printf(" Preferred Path Auto Changeable:");
3405 n = buff[14] & 0x3C;
3406 switch (n) {
3407 case 0x14:
3408 snprintf(b, blen, "No (User Disabled and Host Type Restricted)");
3409 break;
3410 case 0x18:
3411 snprintf(b, blen, "No (User Disabled)");
3412 break;
3413 case 0x24:
3414 snprintf(b, blen, "No (Host Type Restricted)");
3415 break;
3416 case 0x28:
3417 snprintf(b, blen, "Yes");
3418 break;
3419 default:
3420 snprintf(b, blen, "(Unknown)");
3421 break;
3422 }
3423 sgj_haj_vistr(jsp, jop, 2, "Preferred path auto changeable",
3424 SGJ_SEP_COLON_1_SPACE, n, true, b);
3425
3426 n = buff[14] & 0x03;
3427 // printf(" Implicit Failback:");
3428 switch (n) {
3429 case 0x1:
3430 snprintf(b, blen, "Disabled");
3431 break;
3432 case 0x2:
3433 snprintf(b, blen, "Enabled");
3434 break;
3435 default:
3436 snprintf(b, blen, "(Unknown)");
3437 break;
3438 }
3439 sgj_haj_vistr(jsp, jop, 2, "Implicit failback",
3440 SGJ_SEP_COLON_1_SPACE, n, false, b);
3441 } else {
3442 n = buff[9] & 0xf;
3443 // printf(" Path priority: %d ", buff[9] & 0xf);
3444 switch (n) {
3445 case 0x1:
3446 snprintf(b, blen, "(preferred path)");
3447 break;
3448 case 0x2:
3449 snprintf(b, blen, "(secondary path)");
3450 break;
3451 default:
3452 snprintf(b, blen, "(unknown)");
3453 break;
3454 }
3455 sgj_haj_vistr(jsp, jop, 2, "Path priority",
3456 SGJ_SEP_COLON_1_SPACE, n, false, b);
3457 }
3458
3459 n = !! (buff[8] & 0x80);
3460 sgj_haj_vi(jsp, jop, 2, "Target port group present",
3461 SGJ_SEP_COLON_1_SPACE, n, false);
3462 if (n) {
3463 sgj_opaque_p jo2p = NULL;
3464 sgj_opaque_p jo3p = NULL;
3465 static const char * tpg_s = "Target port group data";
3466 static const char * aas_s = "Asymmetric access state";
3467 static const char * vsf_s = "Vendor specific field";
3468 char d1[80];
3469 char d2[80];
3470
3471 sgj_pr_hr(jsp, " Target Port Group Data (This controller):\n");
3472 decode_rdac_vpd_c9_aas_s(buff[10], d1, sizeof(d1));
3473 decode_rdac_vpd_c9_vs_s(buff[11], d2, sizeof(d2));
3474 sgj_pr_hr(jsp, " %s: %s\n", aas_s, d1);
3475 sgj_pr_hr(jsp, " %s: %s\n", vsf_s, d2);
3476 if (jsp->pr_as_json) {
3477 jo2p = sgj_snake_named_subobject_r(jsp, jop, tpg_s);
3478 jo3p = sgj_snake_named_subobject_r(jsp, jo2p, "this_controller");
3479 sgj_convert_to_snake_name(aas_s, b, blen);
3480 sgj_js_nv_ihexstr(jsp, jo3p, b, buff[10], NULL, d1);
3481 sgj_convert_to_snake_name(vsf_s, b, blen);
3482 sgj_js_nv_ihexstr(jsp, jo3p, b, buff[11], NULL, d2);
3483 }
3484 sgj_pr_hr(jsp, " Target Port Group Data (Alternate controller):\n");
3485 // decode_rdac_vpd_c9_rtpg_data(buff[12], buff[13]);
3486
3487 decode_rdac_vpd_c9_aas_s(buff[12], d1, sizeof(d1));
3488 decode_rdac_vpd_c9_vs_s(buff[13], d2, sizeof(d2));
3489 sgj_pr_hr(jsp, " %s: %s\n", aas_s, d1);
3490 sgj_pr_hr(jsp, " %s: %s\n", vsf_s, d2);
3491 if (jsp->pr_as_json) {
3492 jo2p = sgj_snake_named_subobject_r(jsp, jop, tpg_s);
3493 jo3p = sgj_snake_named_subobject_r(jsp, jo2p,
3494 "alternate_controller");
3495 sgj_convert_to_snake_name(aas_s, b, blen);
3496 sgj_js_nv_ihexstr(jsp, jo3p, b, buff[12], NULL, d1);
3497 sgj_convert_to_snake_name(vsf_s, b, blen);
3498 sgj_js_nv_ihexstr(jsp, jo3p, b, buff[13], NULL, d2);
3499 }
3500 }
3501 }
3502