• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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