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 <stdbool.h>
15 #include <string.h>
16 #define __STDC_FORMAT_MACROS 1
17 #include <inttypes.h>
18
19 #ifndef SG_LIB_MINGW
20 #include <time.h>
21 #endif
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 #include "sg_lib.h"
27 #include "sg_cmds_basic.h"
28 #include "sg_unaligned.h"
29 #include "sg_pr2serr.h"
30
31 #include "sg_vpd_common.h"
32
33 /* This is a companion file to sg_vpd.c . It contains logic to output and
34 decode vendor specific VPD pages
35
36 This program fetches Vital Product Data (VPD) pages from the given
37 device and outputs it as directed. VPD pages are obtained via a
38 SCSI INQUIRY command. Most of the data in this program is obtained
39 from the SCSI SPC-4 document at https://www.t10.org .
40
41 Acknowledgments:
42 - Lars Marowsky-Bree <lmb at suse dot de> contributed Unit Path Report
43 VPD page decoding for EMC CLARiiON devices [20041016]
44 - Hannes Reinecke <hare at suse dot de> contributed RDAC vendor
45 specific VPD pages [20060421]
46 - Jonathan McDowell <noodles at hp dot com> contributed HP/3PAR InServ
47 VPD page [0xc0] containing volume information [20110922]
48
49 */
50
51 /* vendor/product identifiers */
52 #define VPD_VP_SEAGATE 0
53 #define VPD_VP_RDAC 1
54 #define VPD_VP_EMC 2
55 #define VPD_VP_DDS 3
56 #define VPD_VP_HP3PAR 4
57 #define VPD_VP_IBM_LTO 5
58 #define VPD_VP_HP_LTO 6
59 #define VPD_VP_WDC_HITACHI 7
60 #define VPD_VP_NVME 8
61 #define VPD_VP_SG 9 /* this package/library as a vendor */
62
63
64 /* vendor VPD pages */
65 #define VPD_V_HIT_PG3 0x3
66 #define VPD_V_HP3PAR 0xc0
67 #define VPD_V_FIRM_SEA 0xc0
68 #define VPD_V_UPR_EMC 0xc0
69 #define VPD_V_HVER_RDAC 0xc0
70 #define VPD_V_FVER_DDS 0xc0
71 #define VPD_V_FVER_LTO 0xc0
72 #define VPD_V_DCRL_LTO 0xc0
73 #define VPD_V_DATC_SEA 0xc1
74 #define VPD_V_FVER_RDAC 0xc1
75 #define VPD_V_HVER_LTO 0xc1
76 #define VPD_V_DSN_LTO 0xc1
77 #define VPD_V_JUMP_SEA 0xc2
78 #define VPD_V_SVER_RDAC 0xc2
79 #define VPD_V_PCA_LTO 0xc2
80 #define VPD_V_DEV_BEH_SEA 0xc3
81 #define VPD_V_FEAT_RDAC 0xc3
82 #define VPD_V_MECH_LTO 0xc3
83 #define VPD_V_SUBS_RDAC 0xc4
84 #define VPD_V_HEAD_LTO 0xc4
85 #define VPD_V_ACI_LTO 0xc5
86 #define VPD_V_DUCD_LTO 0xc7
87 #define VPD_V_EDID_RDAC 0xc8
88 #define VPD_V_MPDS_LTO 0xc8
89 #define VPD_V_VAC_RDAC 0xc9
90 #define VPD_V_RVSI_RDAC 0xca
91 #define VPD_V_SAID_RDAC 0xd0
92 #define VPD_V_HIT_PG_D1 0xd1
93 #define VPD_V_HIT_PG_D2 0xd2
94
95
96 #define DEF_ALLOC_LEN 252
97 #define MX_ALLOC_LEN (0xc000 + 0x80)
98
99 void
dup_sanity_chk(int sz_opts_t,int sz_values_name_t)100 dup_sanity_chk(int sz_opts_t, int sz_values_name_t)
101 {
102 const size_t my_sz_opts_t = sizeof(struct opts_t);
103 const size_t my_sz_values_name_t = sizeof(struct svpd_values_name_t);
104
105 if (sz_opts_t != (int)my_sz_opts_t)
106 pr2serr(">>> struct opts_t differs in size from sg_vpd.c [%d != "
107 "%d]\n", (int)my_sz_opts_t, sz_opts_t);
108 if (sz_values_name_t != (int)my_sz_values_name_t)
109 pr2serr(">>> struct svpd_values_name_t differs in size from "
110 "sg_vpd.c [%d != %d]\n", (int)my_sz_values_name_t,
111 sz_values_name_t);
112 }
113
114 static bool
is_like_pdt(int actual_pdt,const struct svpd_values_name_t * vnp)115 is_like_pdt(int actual_pdt, const struct svpd_values_name_t * vnp)
116 {
117 if (actual_pdt == vnp->pdt)
118 return true;
119 if (PDT_DISK == vnp->pdt) {
120 switch (actual_pdt) {
121 case PDT_DISK:
122 case PDT_RBC:
123 case PDT_PROCESSOR:
124 case PDT_SAC:
125 case PDT_ZBC:
126 return true;
127 default:
128 return false;
129 }
130 } else if (PDT_TAPE == vnp->pdt) {
131 switch (actual_pdt) {
132 case PDT_TAPE:
133 case PDT_MCHANGER:
134 case PDT_ADC:
135 return true;
136 default:
137 return false;
138 }
139 } else
140 return false;
141 }
142
143 static const struct svpd_values_name_t *
svpd_get_v_detail(int page_num,int vend_prod_num,int pdt)144 svpd_get_v_detail(int page_num, int vend_prod_num, int pdt)
145 {
146 const struct svpd_values_name_t * vnp;
147 int vp, ty;
148
149 vp = (vend_prod_num < 0) ? 1 : 0;
150 ty = (pdt < 0) ? 1 : 0;
151 for (vnp = vendor_vpd_pg; vnp->acron; ++vnp) {
152 if ((page_num == vnp->value) &&
153 (vp || (vend_prod_num == vnp->subvalue)) &&
154 (ty || is_like_pdt(pdt, vnp)))
155 return vnp;
156 }
157 #if 0
158 if (! ty)
159 return svpd_get_v_detail(page_num, vend_prod_num, -1);
160 if (! vp)
161 return svpd_get_v_detail(page_num, -1, pdt);
162 #endif
163 return NULL;
164 }
165
166 const struct svpd_values_name_t *
svpd_find_vendor_by_num(int page_num,int vend_prod_num)167 svpd_find_vendor_by_num(int page_num, int vend_prod_num)
168 {
169 const struct svpd_values_name_t * vnp;
170
171 for (vnp = vendor_vpd_pg; vnp->acron; ++vnp) {
172 if ((page_num == vnp->value) &&
173 ((vend_prod_num < 0) || (vend_prod_num == vnp->subvalue)))
174 return vnp;
175 }
176 return NULL;
177 }
178
179 const struct svpd_values_name_t *
svpd_find_vendor_by_acron(const char * ap)180 svpd_find_vendor_by_acron(const char * ap)
181 {
182 const struct svpd_values_name_t * vnp;
183
184 for (vnp = vendor_vpd_pg; vnp->acron; ++vnp) {
185 if (0 == strcmp(vnp->acron, ap))
186 return vnp;
187 }
188 return NULL;
189 }
190
191 int
svpd_count_vendor_vpds(int vpd_pn,int vend_prod_num)192 svpd_count_vendor_vpds(int vpd_pn, int vend_prod_num)
193 {
194 const struct svpd_values_name_t * vnp;
195 int matches;
196
197 for (vnp = vendor_vpd_pg, matches = 0; vnp->acron; ++vnp) {
198 if ((vpd_pn == vnp->value) && vnp->name) {
199 if ((vend_prod_num < 0) || (vend_prod_num == vnp->subvalue)) {
200 if (0 == matches)
201 printf("Matching vendor specific VPD pages:\n");
202 ++matches;
203 printf(" %-10s 0x%02x,%d %s\n", vnp->acron,
204 vnp->value, vnp->subvalue, vnp->name);
205 }
206 }
207 }
208 return matches;
209 }
210
211 static void
dStrRaw(const uint8_t * str,int len)212 dStrRaw(const uint8_t * str, int len)
213 {
214 int k;
215
216 for (k = 0; k < len; ++k)
217 printf("%c", str[k]);
218 }
219
220 static void
decode_vpd_c0_hp3par(uint8_t * buff,int len)221 decode_vpd_c0_hp3par(uint8_t * buff, int len)
222 {
223 int rev;
224 long offset;
225
226 if (len < 24) {
227 pr2serr("HP/3PAR vendor specific VPD page length too short=%d\n",
228 len);
229 return;
230 }
231
232 rev = buff[4];
233 printf(" Page revision: %d\n", rev);
234
235 printf(" Volume type: %s\n", (buff[5] & 0x01) ? "tpvv" :
236 (buff[5] & 0x02) ? "snap" : "base");
237 printf(" Reclaim supported: %s\n", (buff[5] & 0x04) ? "yes" : "no");
238 printf(" ATS supported: %s\n", (buff[5] & 0x10) ? "yes" : "no");
239 printf(" XCopy supported: %s\n", (buff[5] & 0x20) ? "yes" : "no");
240
241 if (rev > 3) {
242 printf(" VV ID: %" PRIu64 "\n", sg_get_unaligned_be64(buff + 28));
243 offset = 44;
244 printf(" Volume name: %s\n", &buff[offset]);
245
246 printf(" Domain ID: %d\n", sg_get_unaligned_be32(buff + 36));
247
248 offset += sg_get_unaligned_be32(buff + offset - 4) + 4;
249 printf(" Domain Name: %s\n", &buff[offset]);
250
251 offset += sg_get_unaligned_be32(buff + offset - 4) + 4;
252 printf(" User CPG: %s\n", &buff[offset]);
253
254 offset += sg_get_unaligned_be32(buff + offset - 4) + 4;
255 printf(" Snap CPG: %s\n", &buff[offset]);
256
257 offset += sg_get_unaligned_be32(buff + offset - 4);
258
259 printf(" VV policies: %s,%s,%s,%s\n",
260 (buff[offset + 3] & 0x01) ? "stale_ss" : "no_stale_ss",
261 (buff[offset + 3] & 0x02) ? "one_host" : "no_one_host",
262 (buff[offset + 3] & 0x04) ? "tp_bzero" : "no_tp_bzero",
263 (buff[offset + 3] & 0x08) ? "zero_detect" : "no_zero_detect");
264
265 }
266
267 if (buff[5] & 0x04) {
268 printf(" Allocation unit: %d\n", sg_get_unaligned_be32(buff + 8));
269
270 printf(" Data pool size: %" PRIu64 "\n",
271 sg_get_unaligned_be64(buff + 12));
272
273 printf(" Space allocated: %" PRIu64 "\n",
274 sg_get_unaligned_be64(buff + 20));
275 }
276 return;
277 }
278
279
280 static void
decode_firm_vpd_c0_sea(uint8_t * buff,int len)281 decode_firm_vpd_c0_sea(uint8_t * buff, int len)
282 {
283 if (len < 28) {
284 pr2serr("Seagate firmware numbers VPD page length too short=%d\n",
285 len);
286 return;
287 }
288 if (28 == len) {
289 printf(" SCSI firmware release number: %.8s\n", buff + 4);
290 printf(" Servo ROM release number: %.8s\n", buff + 20);
291 } else {
292 printf(" SCSI firmware release number: %.8s\n", buff + 4);
293 printf(" Servo ROM release number: %.8s\n", buff + 12);
294 printf(" SAP block point numbers (major/minor): %.8s\n", buff + 20);
295 if (len < 36)
296 return;
297 printf(" Servo firmware release date: %.4s\n", buff + 28);
298 printf(" Servo ROM release date: %.4s\n", buff + 32);
299 if (len < 44)
300 return;
301 printf(" SAP firmware release number: %.8s\n", buff + 36);
302 if (len < 52)
303 return;
304 printf(" SAP firmware release date: %.4s\n", buff + 44);
305 printf(" SAP firmware release year: %.4s\n", buff + 48);
306 if (len < 60)
307 return;
308 printf(" SAP manufacturing key: %.4s\n", buff + 52);
309 printf(" Servo firmware product family and product family "
310 "member: %.4s\n", buff + 56);
311 }
312 }
313
314 static void
decode_date_code_vpd_c1_sea(uint8_t * buff,int len)315 decode_date_code_vpd_c1_sea(uint8_t * buff, int len)
316 {
317 if (len < 20) {
318 pr2serr("Seagate Data code VPD page length too short=%d\n",
319 len);
320 return;
321 }
322 printf(" ETF log (mmddyyyy): %.8s\n", buff + 4);
323 printf(" Compile date code (mmddyyyy): %.8s\n", buff + 12);
324 }
325
326 static void
decode_dev_beh_vpd_c3_sea(uint8_t * buff,int len)327 decode_dev_beh_vpd_c3_sea(uint8_t * buff, int len)
328 {
329 if (len < 25) {
330 pr2serr("Seagate Device behaviour VPD page length too short=%d\n",
331 len);
332 return;
333 }
334 printf(" Version number: %d\n", buff[4]);
335 printf(" Behaviour code: %d\n", buff[5]);
336 printf(" Behaviour code version number: %d\n", buff[6]);
337 printf(" ASCII family number: %.16s\n", buff + 7);
338 printf(" Number of interleaves: %d\n", buff[23]);
339 printf(" Default number of cache segments: %d\n", buff[24]);
340 }
341
342 static void
decode_rdac_vpd_c0(uint8_t * buff,int len)343 decode_rdac_vpd_c0(uint8_t * buff, int len)
344 {
345 int memsize;
346 char name[65];
347
348 if (len < 3) {
349 pr2serr("Hardware Version VPD page length too short=%d\n", len);
350 return;
351 }
352 if (buff[4] != 'h' && buff[5] != 'w' && buff[6] != 'r') {
353 pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n",
354 buff[4], buff[5], buff[6], buff[7]);
355 return;
356 }
357 printf(" Number of channels: %x\n", buff[8]);
358 memsize = sg_get_unaligned_be16(buff + 10);
359 printf(" Processor Memory Size: %d\n", memsize);
360 memset(name, 0, 65);
361 memcpy(name, buff + 16, 64);
362 printf(" Board Name: %s\n", name);
363 memset(name, 0, 65);
364 memcpy(name, buff + 80, 16);
365 printf(" Board Part Number: %s\n", name);
366 memset(name, 0, 65);
367 memcpy(name, buff + 96, 12);
368 printf(" Schematic Number: %s\n", name);
369 memset(name, 0, 65);
370 memcpy(name, buff + 108, 4);
371 printf(" Schematic Revision Number: %s\n", name);
372 memset(name, 0, 65);
373 memcpy(name, buff + 112, 16);
374 printf(" Board Serial Number: %s\n", name);
375 memset(name, 0, 65);
376 memcpy(name, buff + 144, 8);
377 printf(" Date of Manufacture: %s\n", name);
378 memset(name, 0, 65);
379 memcpy(name, buff + 152, 2);
380 printf(" Board Revision: %s\n", name);
381 memset(name, 0, 65);
382 memcpy(name, buff + 154, 4);
383 printf(" Board Identifier: %s\n", name);
384
385 return;
386 }
387
388 static void
decode_rdac_vpd_c1(uint8_t * buff,int len)389 decode_rdac_vpd_c1(uint8_t * buff, int len)
390 {
391 int i, n, v, r, m, p, d, y, num_part;
392 char part[5];
393
394 if (len < 3) {
395 pr2serr("Firmware Version VPD page length too short=%d\n", len);
396 return;
397 }
398 if (buff[4] != 'f' && buff[5] != 'w' && buff[6] != 'r') {
399 pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n",
400 buff[4], buff[5], buff[6], buff[7]);
401 return;
402 }
403 printf(" Firmware Version: %02x.%02x.%02x\n", buff[8], buff[9], buff[10]);
404 printf(" Firmware Date: %02d/%02d/%02d\n", buff[11], buff[12], buff[13]);
405
406 num_part = (len - 12) / 16;
407 n = 16;
408 printf(" Partitions: %d\n", num_part);
409 for (i = 0; i < num_part; i++) {
410 memset(part,0, 5);
411 memcpy(part, &buff[n], 4);
412 printf(" Name: %s\n", part);
413 n += 4;
414 v = buff[n++];
415 r = buff[n++];
416 m = buff[n++];
417 p = buff[n++];
418 printf(" Version: %d.%d.%d.%d\n", v, r, m, p);
419 m = buff[n++];
420 d = buff[n++];
421 y = buff[n++];
422 printf(" Date: %d/%d/%d\n", m, d, y);
423
424 n += 5;
425 }
426
427 return;
428 }
429
430 static void
decode_rdac_vpd_c3(uint8_t * buff,int len)431 decode_rdac_vpd_c3(uint8_t * buff, int len)
432 {
433 if (len < 0x2c) {
434 pr2serr("Feature parameters VPD page length too short=%d\n", len);
435 return;
436 }
437 if (buff[4] != 'p' && buff[5] != 'r' && buff[6] != 'm') {
438 pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n",
439 buff[4], buff[5], buff[6], buff[7]);
440 return;
441 }
442 printf(" Maximum number of drives per LUN: %d\n", buff[8]);
443 printf(" Maximum number of hot spare drives: %d\n", buff[9]);
444 printf(" UTM: %s\n", buff[11] & 0x80?"enabled":"disabled");
445 if ((buff[11] & 0x80))
446 printf(" UTM LUN: %02x\n", buff[11] & 0x7f);
447 printf(" Persistent Reservations Bus Reset Support: %s\n",
448 (buff[12] & 0x01) ? "enabled" : "disabled");
449 return;
450 }
451
452 static void
decode_rdac_vpd_c4(uint8_t * buff,int len)453 decode_rdac_vpd_c4(uint8_t * buff, int len)
454 {
455 char subsystem_id[17];
456 char subsystem_rev[5];
457 char slot_id[3];
458
459 if (len < 0x1c) {
460 pr2serr("Subsystem identifier VPD page length too short=%d\n", len);
461 return;
462 }
463 if (buff[4] != 's' && buff[5] != 'u' && buff[6] != 'b') {
464 pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n",
465 buff[4], buff[5], buff[6], buff[7]);
466 return;
467 }
468 memset(subsystem_id, 0, 17);
469 memcpy(subsystem_id, &buff[8], 16);
470 memset(subsystem_rev, 0, 5);
471 memcpy(subsystem_rev, &buff[24], 4);
472 slot_id[0] = buff[28];
473 slot_id[1] = buff[29];
474 slot_id[2] = 0;
475
476 printf(" Subsystem ID: %s\n Subsystem Revision: %s",
477 subsystem_id, subsystem_rev);
478 if (!strcmp(subsystem_rev, "10.0"))
479 printf(" (Board ID 4884)\n");
480 else if (!strcmp(subsystem_rev, "12.0"))
481 printf(" (Board ID 5884)\n");
482 else if (!strcmp(subsystem_rev, "13.0"))
483 printf(" (Board ID 2882)\n");
484 else if (!strcmp(subsystem_rev, "13.1"))
485 printf(" (Board ID 2880)\n");
486 else if (!strcmp(subsystem_rev, "14.0"))
487 printf(" (Board ID 2822)\n");
488 else if (!strcmp(subsystem_rev, "15.0"))
489 printf(" (Board ID 6091)\n");
490 else if (!strcmp(subsystem_rev, "16.0"))
491 printf(" (Board ID 3992)\n");
492 else if (!strcmp(subsystem_rev, "16.1"))
493 printf(" (Board ID 3991)\n");
494 else if (!strcmp(subsystem_rev, "17.0"))
495 printf(" (Board ID 1331)\n");
496 else if (!strcmp(subsystem_rev, "17.1"))
497 printf(" (Board ID 1332)\n");
498 else if (!strcmp(subsystem_rev, "17.3"))
499 printf(" (Board ID 1532)\n");
500 else if (!strcmp(subsystem_rev, "17.4"))
501 printf(" (Board ID 1932)\n");
502 else if (!strcmp(subsystem_rev, "42.0"))
503 printf(" (Board ID 26x0)\n");
504 else if (!strcmp(subsystem_rev, "43.0"))
505 printf(" (Board ID 498x)\n");
506 else if (!strcmp(subsystem_rev, "44.0"))
507 printf(" (Board ID 548x)\n");
508 else if (!strcmp(subsystem_rev, "45.0"))
509 printf(" (Board ID 5501)\n");
510 else if (!strcmp(subsystem_rev, "46.0"))
511 printf(" (Board ID 2701)\n");
512 else if (!strcmp(subsystem_rev, "47.0"))
513 printf(" (Board ID 5601)\n");
514 else
515 printf(" (Board ID unknown)\n");
516
517 printf(" Slot ID: %s\n", slot_id);
518
519 return;
520 }
521
522 static void
convert_binary_to_ascii(uint8_t * src,uint8_t * dst,int len)523 convert_binary_to_ascii(uint8_t * src, uint8_t * dst, int len)
524 {
525 int i;
526
527 for (i = 0; i < len; i++) {
528 sprintf((char *)(dst+2*i), "%02x", *(src+i));
529 }
530 }
531
532 static void
decode_rdac_vpd_c8(uint8_t * buff,int len)533 decode_rdac_vpd_c8(uint8_t * buff, int len)
534 {
535 int i;
536 #ifndef SG_LIB_MINGW
537 time_t tstamp;
538 #endif
539 char *c;
540 char label[61];
541 int label_len;
542 char uuid[33];
543 int uuid_len;
544 uint8_t port_id[128];
545 int n;
546
547 if (len < 0xab) {
548 pr2serr("Extended Device Identification VPD page length too "
549 "short=%d\n", len);
550 return;
551 }
552 if (buff[4] != 'e' && buff[5] != 'd' && buff[6] != 'i') {
553 pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n",
554 buff[4], buff[5], buff[6], buff[7]);
555 return;
556 }
557
558 uuid_len = buff[11];
559
560 for (i = 0, c = uuid; i < uuid_len; i++) {
561 sprintf(c,"%02x",buff[12 + i]);
562 c += 2;
563 }
564
565 printf(" Volume Unique Identifier: %s\n", uuid);
566 #ifndef SG_LIB_MINGW
567 tstamp = sg_get_unaligned_be32(buff + 24);
568 printf(" Creation Number: %d, Timestamp: %s",
569 sg_get_unaligned_be16(buff + 22), ctime(&tstamp));
570 #else
571 printf(" Creation Number: %d, Timestamp value: %u",
572 sg_get_unaligned_be16(buff + 22),
573 sg_get_unaligned_be32(buff + 24));
574 #endif
575 memset(label, 0, 61);
576 label_len = buff[28];
577 for(i = 0; i < (label_len - 1); ++i)
578 *(label + i) = buff[29 + (2 * i) + 1];
579 printf(" Volume User Label: %s\n", label);
580
581 uuid_len = buff[89];
582
583 for (i = 0, c = uuid; i < uuid_len; i++) {
584 sprintf(c,"%02x",buff[90 + i]);
585 c += 2;
586 }
587
588 printf(" Storage Array Unique Identifier: %s\n", uuid);
589 memset(label, 0, 61);
590 label_len = buff[106];
591 for(i = 0; i < (label_len - 1); ++i)
592 *(label + i) = buff[107 + (2 * i) + 1];
593 printf(" Storage Array User Label: %s\n", label);
594
595 for (i = 0, c = uuid; i < 8; i++) {
596 sprintf(c,"%02x",buff[167 + i]);
597 c += 2;
598 }
599
600 printf(" Logical Unit Number: %s\n", uuid);
601
602 /* Initiator transport ID */
603 if ( buff[10] & 0x01 ) {
604 memset(port_id, 0, 128);
605 printf(" Transport Protocol: ");
606 switch (buff[175] & 0x0F) {
607 case TPROTO_FCP: /* FC */
608 printf("FC\n");
609 convert_binary_to_ascii(&buff[183], port_id, 8);
610 n = 199;
611 break;
612 case TPROTO_SRP: /* SRP */
613 printf("SRP\n");
614 convert_binary_to_ascii(&buff[183], port_id, 8);
615 n = 199;
616 break;
617 case TPROTO_ISCSI: /* iSCSI */
618 printf("iSCSI\n");
619 n = sg_get_unaligned_be32(buff + 177);
620 memcpy(port_id, &buff[179], n);
621 n = 179 + n;
622 break;
623 case TPROTO_SAS: /* SAS */
624 printf("SAS\n");
625 convert_binary_to_ascii(&buff[179], port_id, 8);
626 n = 199;
627 break;
628 default:
629 return; /* Can't continue decoding, so return */
630 }
631
632 printf(" Initiator Port Identifier: %s\n", port_id);
633 if ( buff[10] & 0x02 ) {
634 memset(port_id, 0, 128);
635 memcpy(port_id, &buff[n], 8);
636 printf(" Supplemental Vendor ID: %s\n", port_id);
637 }
638 }
639
640 return;
641 }
642
643 #if 0
644 static void
645 decode_rdac_vpd_c9_rtpg_data(uint8_t aas, uint8_t vendor)
646 {
647 printf(" Asymmetric Access State:");
648 switch(aas & 0x0F) {
649 case 0x0:
650 printf(" Active/Optimized");
651 break;
652 case 0x1:
653 printf(" Active/Non-Optimized");
654 break;
655 case 0x2:
656 printf(" Standby");
657 break;
658 case 0x3:
659 printf(" Unavailable");
660 break;
661 case 0xE:
662 printf(" Offline");
663 break;
664 case 0xF:
665 printf(" Transitioning");
666 break;
667 default:
668 printf(" (unknown)");
669 break;
670 }
671 printf("\n");
672
673 printf(" Vendor Specific Field:");
674 switch(vendor) {
675 case 0x01:
676 printf(" Operating normally");
677 break;
678 case 0x02:
679 printf(" Non-responsive to queries");
680 break;
681 case 0x03:
682 printf(" Controller being held in reset");
683 break;
684 case 0x04:
685 printf(" Performing controller firmware download (1st controller)");
686 break;
687 case 0x05:
688 printf(" Performing controller firmware download (2nd controller)");
689 break;
690 case 0x06:
691 printf(" Quiesced as a result of an administrative request");
692 break;
693 case 0x07:
694 printf(" Service mode as a result of an administrative request");
695 break;
696 case 0xFF:
697 printf(" Details are not available");
698 break;
699 default:
700 printf(" (unknown)");
701 break;
702 }
703 printf("\n");
704 }
705
706 static void
707 decode_rdac_vpd_c9(uint8_t * buff, int len)
708 {
709 if (len < 3) {
710 pr2serr("Volume Access Control VPD page length too short=%d\n", len);
711 return;
712 }
713 if (buff[4] != 'v' && buff[5] != 'a' && buff[6] != 'c') {
714 pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n",
715 buff[4], buff[5], buff[6], buff[7]);
716 return;
717 }
718 if (buff[7] != '1') {
719 pr2serr("Invalid page version '%c' (should be 1)\n", buff[7]);
720 }
721 if ( (buff[8] & 0xE0) == 0xE0 ) {
722 printf(" IOShipping (ALUA): Enabled\n");
723 } else {
724 printf(" AVT:");
725 if (buff[8] & 0x80) {
726 printf(" Enabled");
727 if (buff[8] & 0x40)
728 printf(" (Allow reads on sector 0)");
729 printf("\n");
730 } else {
731 printf(" Disabled\n");
732 }
733 }
734 printf(" Volume Access via: ");
735 if (buff[8] & 0x01)
736 printf("primary controller\n");
737 else
738 printf("alternate controller\n");
739
740 if (buff[8] & 0x08) {
741 printf(" Path priority: %d ", buff[15] & 0xf);
742 switch(buff[15] & 0xf) {
743 case 0x1:
744 printf("(preferred path)\n");
745 break;
746 case 0x2:
747 printf("(secondary path)\n");
748 break;
749 default:
750 printf("(unknown)\n");
751 break;
752 }
753
754 printf(" Preferred Path Auto Changeable:");
755 switch(buff[14] & 0x3C) {
756 case 0x14:
757 printf(" No (User Disabled and Host Type Restricted)\n");
758 break;
759 case 0x18:
760 printf(" No (User Disabled)\n");
761 break;
762 case 0x24:
763 printf(" No (Host Type Restricted)\n");
764 break;
765 case 0x28:
766 printf(" Yes\n");
767 break;
768 default:
769 printf(" (Unknown)\n");
770 break;
771 }
772
773 printf(" Implicit Failback:");
774 switch(buff[14] & 0x03) {
775 case 0x1:
776 printf(" Disabled\n");
777 break;
778 case 0x2:
779 printf(" Enabled\n");
780 break;
781 default:
782 printf(" (Unknown)\n");
783 break;
784 }
785 } else {
786 printf(" Path priority: %d ", buff[9] & 0xf);
787 switch(buff[9] & 0xf) {
788 case 0x1:
789 printf("(preferred path)\n");
790 break;
791 case 0x2:
792 printf("(secondary path)\n");
793 break;
794 default:
795 printf("(unknown)\n");
796 break;
797 }
798 }
799
800
801 if (buff[8] & 0x80) {
802 printf(" Target Port Group Data (This controller):\n");
803 decode_rdac_vpd_c9_rtpg_data(buff[10], buff[11]);
804
805 printf(" Target Port Group Data (Alternate controller):\n");
806 decode_rdac_vpd_c9_rtpg_data(buff[12], buff[13]);
807 }
808 }
809 #endif
810
811 static void
decode_rdac_vpd_ca(uint8_t * buff,int len)812 decode_rdac_vpd_ca(uint8_t * buff, int len)
813 {
814 int i;
815
816 if (len < 16) {
817 pr2serr("Replicated Volume Source Identifier VPD page length too "
818 "short=%d\n", len);
819 return;
820 }
821 if (buff[4] != 'r' && buff[5] != 'v' && buff[6] != 's') {
822 pr2serr("Invalid page identifier %c%c%c%c, decoding not possible.\n",
823 buff[4], buff[5], buff[6], buff[7]);
824 return;
825 }
826 if (buff[8] & 0x01) {
827 printf(" Snapshot Volume\n");
828 printf(" Base Volume WWID: ");
829 for (i = 0; i < 16; i++)
830 printf("%02x", buff[10 + i]);
831 printf("\n");
832 } else if (buff[8] & 0x02) {
833 printf(" Copy Target Volume\n");
834 printf(" Source Volume WWID: ");
835 for (i = 0; i < 16; i++)
836 printf("%02x", buff[10 + i]);
837 printf("\n");
838 } else
839 printf(" Neither a snapshot nor a copy target volume\n");
840
841 return;
842 }
843
844 static void
decode_rdac_vpd_d0(uint8_t * buff,int len)845 decode_rdac_vpd_d0(uint8_t * buff, int len)
846 {
847 int i;
848
849 if (len < 20) {
850 pr2serr("Storage Array World Wide Name VPD page length too "
851 "short=%d\n", len);
852 return;
853 }
854 printf(" Storage Array WWN: ");
855 for (i = 0; i < 16; i++)
856 printf("%02x", buff[8 + i]);
857 printf("\n");
858
859 return;
860 }
861
862
863 static void
decode_dds_vpd_c0(uint8_t * buff,int len)864 decode_dds_vpd_c0(uint8_t * buff, int len)
865 {
866 char firmware_rev[25];
867 char build_date[43];
868 char hw_conf[21];
869 char fw_conf[21];
870
871 if (len < 0xb3) {
872 pr2serr("Vendor-Unique Firmware revision page invalid length=%d\n",
873 len);
874 return;
875 }
876 memset(firmware_rev, 0x0, 25);
877 memcpy(firmware_rev, &buff[5], 24);
878
879 printf(" %s\n", firmware_rev);
880
881 memset(build_date, 0x0, 43);
882 memcpy(build_date, &buff[30], 42);
883
884 printf(" %s\n", build_date);
885
886 memset(hw_conf, 0x0, 21);
887 memcpy(hw_conf, &buff[73], 20);
888 printf(" %s\n", hw_conf);
889
890 memset(fw_conf, 0x0, 21);
891 memcpy(fw_conf, &buff[94], 20);
892 printf(" %s\n", fw_conf);
893 return;
894 }
895
896 static void
decode_hp_lto_vpd_cx(uint8_t * buff,int len,int page)897 decode_hp_lto_vpd_cx(uint8_t * buff, int len, int page)
898 {
899 char str[32];
900 const char *comp = NULL;
901
902 if (len < 0x5c) {
903 pr2serr("Driver Component Revision Levels page invalid length=%d\n",
904 len);
905 return;
906 }
907 switch (page) {
908 case 0xc0:
909 comp = "Firmware";
910 break;
911 case 0xc1:
912 comp = "Hardware";
913 break;
914 case 0xc2:
915 comp = "PCA";
916 break;
917 case 0xc3:
918 comp = "Mechanism";
919 break;
920 case 0xc4:
921 comp = "Head Assy";
922 break;
923 case 0xc5:
924 comp = "ACI";
925 break;
926 }
927 if (!comp) {
928 pr2serr("Driver Component Revision Level invalid page=0x%02x\n",
929 page);
930 return;
931 }
932
933 memset(str, 0x0, 32);
934 memcpy(str, &buff[4], 26);
935 printf(" %s\n", str);
936
937 memset(str, 0x0, 32);
938 memcpy(str, &buff[30], 19);
939 printf(" %s\n", str);
940
941 memset(str, 0x0, 32);
942 memcpy(str, &buff[49], 24);
943 printf(" %s\n", str);
944
945 memset(str, 0x0, 32);
946 memcpy(str, &buff[73], 23);
947 printf(" %s\n", str);
948 return;
949 }
950
951 static void
decode_ibm_lto_dcrl(uint8_t * buff,int len)952 decode_ibm_lto_dcrl(uint8_t * buff, int len)
953 {
954 if (len < 0x2b) {
955 pr2serr("Driver Component Revision Levels page (IBM LTO) invalid "
956 "length=%d\n", len);
957 return;
958 }
959 printf(" Code name: %.12s\n", buff + 4);
960 printf(" Time (hhmmss): %.7s\n", buff + 16);
961 printf(" Date (yyyymmdd): %.8s\n", buff + 23);
962 printf(" Platform: %.12s\n", buff + 31);
963 }
964
965 static void
decode_ibm_lto_dsn(uint8_t * buff,int len)966 decode_ibm_lto_dsn(uint8_t * buff, int len)
967 {
968 if (len < 0x1c) {
969 pr2serr("Driver Serial Numbers page (IBM LTO) invalid "
970 "length=%d\n", len);
971 return;
972 }
973 printf(" Manufacturing serial number: %.12s\n", buff + 4);
974 printf(" Reported serial number: %.12s\n", buff + 16);
975 }
976
977 static void
decode_vpd_3_hit(uint8_t * b,int blen)978 decode_vpd_3_hit(uint8_t * b, int blen)
979 {
980 uint16_t plen = sg_get_unaligned_be16(b + 2);
981
982 if ((plen < 184) || (blen < 184)) {
983 pr2serr("Hitachi VPD page 0x3 length (%u) shorter than %u\n",
984 plen + 4, 184 + 4);
985 return;
986 }
987 printf(" ASCII uCode Identifier: %.12s\n", b + 24);
988 printf(" ASCII servo P/N: %.4s\n", b + 36);
989 printf(" Major Version: %.2s\n", b + 40);
990 printf(" Minor Version: %.2s\n", b + 42);
991 printf(" User Count: %.4s\n", b + 44);
992 printf(" Build Number: %.4s\n", b + 48);
993 printf(" Build Date String: %.32s\n", b + 52);
994 printf(" Product ID: %.8s\n", b + 84);
995 printf(" Interface ID: %.8s\n", b + 92);
996 printf(" Code Type: %.8s\n", b + 100);
997 printf(" User Name: %.12s\n", b + 108);
998 printf(" Machine Name: %.16s\n", b + 120);
999 printf(" Directory Name: %.32s\n", b + 136);
1000 printf(" Operating state: %u\n", sg_get_unaligned_be32(b + 168));
1001 printf(" Functional Mode: %u\n", sg_get_unaligned_be32(b + 172));
1002 printf(" Degraded Reason: %u\n", sg_get_unaligned_be32(b + 176));
1003 printf(" Broken Reason: %u\n", sg_get_unaligned_be32(b + 180));
1004 printf(" Code Mode: %u\n", sg_get_unaligned_be32(b + 184));
1005 printf(" Revision: %.4s\n", b + 188);
1006 }
1007
1008 static void
decode_vpd_d1_hit(uint8_t * b,int blen)1009 decode_vpd_d1_hit(uint8_t * b, int blen)
1010 {
1011 uint16_t plen = sg_get_unaligned_be16(b + 2);
1012
1013 if ((plen < 80) || (blen < 80)) {
1014 pr2serr("Hitachi VPD page 0xd1 length (%u) shorter than %u\n",
1015 plen + 4, 80 + 4);
1016 return;
1017 }
1018 printf(" ASCII Media Disk Definition: %.16s\n", b + 4);
1019 printf(" ASCII Motor Serial Number: %.16s\n", b + 20);
1020 printf(" ASCII Flex Assembly Serial Number: %.16s\n", b + 36);
1021 printf(" ASCII Actuator Serial Number: %.16s\n", b + 52);
1022 printf(" ASCII Device Enclosure Serial Number: %.16s\n", b + 68);
1023 }
1024
1025 static void
decode_vpd_d2_hit(uint8_t * b,int blen)1026 decode_vpd_d2_hit(uint8_t * b, int blen)
1027 {
1028 uint16_t plen = sg_get_unaligned_be16(b + 2);
1029
1030 if ((plen < 52) || (blen < 52)) {
1031 pr2serr("Hitachi VPD page 0xd2 length (%u) shorter than %u\n",
1032 plen + 4, 52 + 4);
1033 return;
1034 }
1035 if ((blen - 4) == 120) {
1036 printf(" HDC Version: %.*s\n", b[4], b + 5);
1037 printf(" Card Serial Number: %.*s\n", b[24], b + 25);
1038 printf(" NAND Flash Version: %.*s\n", b[44], b + 45);
1039 printf(" Card Assembly Part Number: %.*s\n", b[64], b + 65);
1040 printf(" Second Card Serial Number: %.*s\n", b[84], b + 85);
1041 printf(" Second Card Assembly Part Number: %.*s\n", b[104], b + 105);
1042 } else {
1043 printf(" ASCII HDC Version: %.16s\n", b + 5);
1044 printf(" ASCII Card Serial Number: %.16s\n", b + 22);
1045 printf(" ASCII Card Assembly Part Number: %.16s\n", b + 39);
1046 }
1047 }
1048
1049 /* Returns 0 if successful, see sg_ll_inquiry() plus SG_LIB_CAT_OTHER for
1050 unsupported page */
1051 int
svpd_decode_vendor(int sg_fd,struct opts_t * op,sgj_opaque_p jop,int off)1052 svpd_decode_vendor(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int off)
1053 {
1054 bool hex0 = (0 == op->do_hex);
1055 bool as_json;
1056 int len, pdt, plen, pn;
1057 int alloc_len = op->maxlen;
1058 int res = 0;
1059 const struct svpd_values_name_t * vnp;
1060 sgj_state * jsp = &op->json_st;
1061 sgj_opaque_p jo2p = NULL;
1062 uint8_t * rp;
1063 char name[80];
1064
1065 as_json = jsp->pr_as_json;
1066 pn = op->vpd_pn;
1067 switch (pn) { /* VPD codes that we support vendor pages for */
1068 case 0x3:
1069 case 0xc0:
1070 case 0xc1:
1071 case 0xc2:
1072 case 0xc3:
1073 case 0xc4:
1074 case 0xc5:
1075 case 0xc8:
1076 case 0xc9:
1077 case 0xca:
1078 case 0xd0:
1079 case 0xd1:
1080 case 0xd2:
1081 case 0xde:
1082 break;
1083 default: /* not known so return prior to fetching page */
1084 return SG_LIB_CAT_OTHER;
1085 }
1086 rp = rsp_buff + off;
1087 if (sg_fd >= 0) {
1088 if (0 == alloc_len)
1089 alloc_len = DEF_ALLOC_LEN;
1090 }
1091 res = vpd_fetch_page(sg_fd, rp, pn, alloc_len, op->do_quiet, op->verbose,
1092 &len);
1093 if (res) {
1094 pr2serr("Vendor VPD page=0x%x failed to fetch\n", pn);
1095 return res;
1096 }
1097 pdt = rp[0] & PDT_MASK;
1098 vnp = svpd_get_v_detail(pn, op->vend_prod_num, pdt);
1099 if (vnp && vnp->name)
1100 snprintf(name, sizeof(name), "%s", vnp->name);
1101 else
1102 snprintf(name, sizeof(name) - 1, "Vendor VPD page=0x%x", pn);
1103 if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3))
1104 sgj_pr_hr(jsp, "%s VPD Page:\n", name);
1105 if (op->do_raw)
1106 dStrRaw(rp, len);
1107 else {
1108 switch(pn) {
1109 case 0x3:
1110 if (hex0 && (VPD_VP_WDC_HITACHI == op->vend_prod_num))
1111 decode_vpd_3_hit(rp, len);
1112 else
1113 res = SG_LIB_CAT_OTHER;
1114 break;
1115 case 0xc0:
1116 if (! hex0)
1117 hex2stdout(rp, len, no_ascii_4hex(op));
1118 else if (VPD_VP_SEAGATE == op->vend_prod_num)
1119 decode_firm_vpd_c0_sea(rp, len);
1120 else if (VPD_VP_EMC == op->vend_prod_num) {
1121 if (as_json)
1122 jo2p = sg_vpd_js_hdr(jsp, jop,
1123 "Unit serial number VPD page", rp);
1124 decode_upr_vpd_c0_emc(rp, len, op, jo2p);
1125 } else if (VPD_VP_HP3PAR == op->vend_prod_num)
1126 decode_vpd_c0_hp3par(rp, len);
1127 else if (VPD_VP_RDAC == op->vend_prod_num)
1128 decode_rdac_vpd_c0(rp, len);
1129 else if (VPD_VP_DDS == op->vend_prod_num)
1130 decode_dds_vpd_c0(rp, len);
1131 else if (VPD_VP_IBM_LTO == op->vend_prod_num)
1132 decode_ibm_lto_dcrl(rp, len);
1133 else if (VPD_VP_HP_LTO == op->vend_prod_num)
1134 decode_hp_lto_vpd_cx(rp, len, pn);
1135 else
1136 res = SG_LIB_CAT_OTHER;
1137 break;
1138 case 0xc1:
1139 if (! hex0)
1140 hex2stdout(rp, len, no_ascii_4hex(op));
1141 else if (VPD_VP_SEAGATE == op->vend_prod_num)
1142 decode_date_code_vpd_c1_sea(rp, len);
1143 else if (VPD_VP_RDAC == op->vend_prod_num)
1144 decode_rdac_vpd_c1(rp, len);
1145 else if (VPD_VP_IBM_LTO == op->vend_prod_num)
1146 decode_ibm_lto_dsn(rp, len);
1147 else if (VPD_VP_HP_LTO == op->vend_prod_num)
1148 decode_hp_lto_vpd_cx(rp, len, pn);
1149 else
1150 res = SG_LIB_CAT_OTHER;
1151 break;
1152 case 0xc2:
1153 if (! hex0)
1154 hex2stdout(rp, len, no_ascii_4hex(op));
1155 else if (VPD_VP_RDAC == op->vend_prod_num) {
1156 if (as_json)
1157 jo2p = sg_vpd_js_hdr(jsp, jop,
1158 "Software version VPD page", rp);
1159 decode_rdac_vpd_c2(rp, len, op, jo2p);
1160 } else if (VPD_VP_HP_LTO == op->vend_prod_num)
1161 decode_hp_lto_vpd_cx(rp, len, pn);
1162 else
1163 res = SG_LIB_CAT_OTHER;
1164 break;
1165 case 0xc3:
1166 if (! hex0)
1167 hex2stdout(rp, len, no_ascii_4hex(op));
1168 else if (VPD_VP_SEAGATE == op->vend_prod_num)
1169 decode_dev_beh_vpd_c3_sea(rp, len);
1170 else if (VPD_VP_RDAC == op->vend_prod_num)
1171 decode_rdac_vpd_c3(rp, len);
1172 else if (VPD_VP_HP_LTO == op->vend_prod_num)
1173 decode_hp_lto_vpd_cx(rp, len, pn);
1174 else
1175 res = SG_LIB_CAT_OTHER;
1176 break;
1177 case 0xc4:
1178 if (! hex0)
1179 hex2stdout(rp, len, no_ascii_4hex(op));
1180 else if (VPD_VP_RDAC == op->vend_prod_num)
1181 decode_rdac_vpd_c4(rp, len);
1182 else if (VPD_VP_HP_LTO == op->vend_prod_num)
1183 decode_hp_lto_vpd_cx(rp, len, pn);
1184 else
1185 res = SG_LIB_CAT_OTHER;
1186 break;
1187 case 0xc5:
1188 if (! hex0)
1189 hex2stdout(rp, len, no_ascii_4hex(op));
1190 else if (VPD_VP_HP_LTO == op->vend_prod_num)
1191 decode_hp_lto_vpd_cx(rp, len, pn);
1192 else
1193 res = SG_LIB_CAT_OTHER;
1194 break;
1195 case 0xc8:
1196 if (! hex0)
1197 hex2stdout(rp, len, no_ascii_4hex(op));
1198 else if (VPD_VP_RDAC == op->vend_prod_num)
1199 decode_rdac_vpd_c8(rp, len);
1200 else
1201 res = SG_LIB_CAT_OTHER;
1202 break;
1203 case 0xc9:
1204 if (! hex0)
1205 hex2stdout(rp, len, no_ascii_4hex(op));
1206 else if (VPD_VP_RDAC == op->vend_prod_num) {
1207 if (as_json)
1208 jo2p = sg_vpd_js_hdr(jsp, jop,
1209 "Volume access control VPD page", rp);
1210 decode_rdac_vpd_c9(rp, len, op, jo2p);
1211 } else
1212 res = SG_LIB_CAT_OTHER;
1213 break;
1214 case 0xca:
1215 if (! hex0)
1216 hex2stdout(rp, len, no_ascii_4hex(op));
1217 else if (VPD_VP_RDAC == op->vend_prod_num)
1218 decode_rdac_vpd_ca(rp, len);
1219 else
1220 res = SG_LIB_CAT_OTHER;
1221 break;
1222 case 0xd0:
1223 if (! hex0)
1224 hex2stdout(rp, len, no_ascii_4hex(op));
1225 else if (VPD_VP_RDAC == op->vend_prod_num)
1226 decode_rdac_vpd_d0(rp, len);
1227 else
1228 res = SG_LIB_CAT_OTHER;
1229 break;
1230 case 0xd1:
1231 if (! hex0)
1232 hex2stdout(rp, len, no_ascii_4hex(op));
1233 else if (VPD_VP_WDC_HITACHI == op->vend_prod_num)
1234 decode_vpd_d1_hit(rp, len);
1235 else
1236 res = SG_LIB_CAT_OTHER;
1237 break;
1238 case 0xd2:
1239 if (! hex0)
1240 hex2stdout(rp, len, no_ascii_4hex(op));
1241 else if (VPD_VP_WDC_HITACHI == op->vend_prod_num)
1242 decode_vpd_d2_hit(rp, len);
1243 else
1244 res = SG_LIB_CAT_OTHER;
1245 break;
1246 case SG_NVME_VPD_NICR: /* 0xde */
1247 if (VPD_VP_SG != op->vend_prod_num) {
1248 res = SG_LIB_CAT_OTHER;
1249 break;
1250 }
1251 /* NVMe: Identify Controller data structure (CNS 01h) */
1252 plen = sg_get_unaligned_be16(rp + 2) + 4;
1253 if (plen > len) { /* fetch the whole page */
1254 res = vpd_fetch_page(sg_fd, rp, pn, plen,
1255 op->do_quiet, op->verbose, &len);
1256 if (res) {
1257 pr2serr("Vendor VPD page=0x%x failed to fetch\n", pn);
1258 return res;
1259 }
1260 }
1261 if (len < 16) {
1262 pr2serr("%s expected to be > 15 bytes long (got: %d)\n",
1263 name, len);
1264 break;
1265 } else {
1266 int n = len - 16;
1267 const char * np = "NVMe Identify Controller Response VPD page";
1268 /* NVMe: Identify Controller data structure (CNS 01h) */
1269 const char * ep = "(sg3_utils)";
1270
1271 if (n > 4096) {
1272 pr2serr("NVMe Identify response expected to be "
1273 "<= 4096 bytes (got: %d)\n", n);
1274 break;
1275 }
1276 if (op->do_hex < 3)
1277 sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep);
1278 if (op->do_hex)
1279 hex2stdout(rp, len, no_ascii_4hex(op));
1280 else if (jsp->pr_as_json) {
1281 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1282 sgj_js_nv_hex_bytes(jsp, jo2p, "response_bytes",
1283 rp + 16, n);
1284 } else
1285 hex2stdout(rp + 16, n, 1);
1286 }
1287 break;
1288 default:
1289 res = SG_LIB_CAT_OTHER;
1290 }
1291 }
1292 if (res && op->verbose)
1293 pr2serr("%s: can't decode pn=0x%x, vend_prod_num=%d\n", __func__,
1294 pn, op->vend_prod_num);
1295 return res;
1296 }
1297