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 <getopt.h>
19 #define __STDC_FORMAT_MACROS 1
20 #include <inttypes.h>
21 #include <errno.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include "sg_lib.h"
30 #include "sg_cmds_basic.h"
31 #include "sg_unaligned.h"
32 #include "sg_pr2serr.h"
33
34 #include "sg_vpd_common.h" /* shared with sg_inq */
35
36 /* This utility program was originally written for the Linux OS SCSI subsystem.
37
38 This program fetches Vital Product Data (VPD) pages from the given
39 device and outputs it as directed. VPD pages are obtained via a
40 SCSI INQUIRY command. Most of the data in this program is obtained
41 from the SCSI SPC-4 document at https://www.t10.org .
42
43 */
44
45 static const char * version_str = "1.83 20220915"; /* spc6r06 + sbc5r03 */
46
47 #define MY_NAME "sg_vpd"
48
49 /* Device identification VPD page associations */
50 #define VPD_ASSOC_LU 0
51 #define VPD_ASSOC_TPORT 1
52 #define VPD_ASSOC_TDEVICE 2
53
54 /* values for selection one or more associations (2**vpd_assoc),
55 except _AS_IS */
56 #define VPD_DI_SEL_LU 1
57 #define VPD_DI_SEL_TPORT 2
58 #define VPD_DI_SEL_TARGET 4
59 #define VPD_DI_SEL_AS_IS 32
60
61 #define DEF_ALLOC_LEN 252
62 #define MIN_MAXLEN 16
63 #define MX_ALLOC_LEN (0xc000 + 0x80)
64 #define VPD_ATA_INFO_LEN 572
65
66 #define SENSE_BUFF_LEN 64 /* Arbitrary, could be larger */
67 #define INQUIRY_CMD 0x12
68 #define INQUIRY_CMDLEN 6
69 #define DEF_PT_TIMEOUT 60 /* 60 seconds */
70
71
72 uint8_t * rsp_buff;
73
74 static int svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop,
75 int subvalue, int off, const char * prefix);
76 static int svpd_unable_to_decode(int sg_fd, struct opts_t * op, int subvalue,
77 int off);
78
79 static int filter_dev_ids(const char * print_if_found, int num_leading,
80 uint8_t * buff, int len, int m_assoc,
81 struct opts_t * op, sgj_opaque_p jop);
82
83 static const int rsp_buff_sz = MX_ALLOC_LEN + 2;
84
85 static uint8_t * free_rsp_buff;
86
87 static struct option long_options[] = {
88 {"all", no_argument, 0, 'a'},
89 {"enumerate", no_argument, 0, 'e'},
90 {"examine", no_argument, 0, 'E'},
91 {"force", no_argument, 0, 'f'},
92 {"help", no_argument, 0, 'h'},
93 {"hex", no_argument, 0, 'H'},
94 {"ident", no_argument, 0, 'i'},
95 {"inhex", required_argument, 0, 'I'},
96 {"json", optional_argument, 0, 'j'},
97 {"long", no_argument, 0, 'l'},
98 {"maxlen", required_argument, 0, 'm'},
99 {"page", required_argument, 0, 'p'},
100 {"quiet", no_argument, 0, 'q'},
101 {"raw", no_argument, 0, 'r'},
102 {"sinq_inraw", required_argument, 0, 'Q'},
103 {"sinq-inraw", required_argument, 0, 'Q'},
104 {"vendor", required_argument, 0, 'M'},
105 {"verbose", no_argument, 0, 'v'},
106 {"version", no_argument, 0, 'V'},
107 {0, 0, 0, 0},
108 };
109
110
111 /* arranged in alphabetical order by acronym */
112 static struct svpd_values_name_t standard_vpd_pg[] = {
113 {VPD_AUTOMATION_DEV_SN, 0, 1, "adsn", "Automation device serial "
114 "number (SSC)"},
115 {VPD_ATA_INFO, 0, -1, "ai", "ATA information (SAT)"},
116 {VPD_ASCII_OP_DEF, 0, -1, "aod",
117 "ASCII implemented operating definition (obsolete)"},
118 {VPD_BLOCK_DEV_CHARS, 0, 0, "bdc", "Block device characteristics "
119 "(SBC)"},
120 {VPD_BLOCK_DEV_C_EXTENS, 0, 0, "bdce", "Block device characteristics "
121 "extension (SBC)"},
122 {VPD_BLOCK_LIMITS, 0, 0, "bl", "Block limits (SBC)"},
123 {VPD_BLOCK_LIMITS_EXT, 0, 0, "ble", "Block limits extension (SBC)"},
124 {VPD_CFA_PROFILE_INFO, 0, 0, "cfa", "CFA profile information"},
125 {VPD_CON_POS_RANGE, 0, 0, "cpr", "Concurrent positioning ranges"},
126 {VPD_DEVICE_CONSTITUENTS, 0, -1, "dc", "Device constituents"},
127 {VPD_DEVICE_ID, 0, -1, "di", "Device identification"},
128 {VPD_DEVICE_ID, VPD_DI_SEL_AS_IS, -1, "di_asis", "Like 'di' "
129 "but designators ordered as found"},
130 {VPD_DEVICE_ID, VPD_DI_SEL_LU, -1, "di_lu", "Device identification, "
131 "lu only"},
132 {VPD_DEVICE_ID, VPD_DI_SEL_TPORT, -1, "di_port", "Device "
133 "identification, target port only"},
134 {VPD_DEVICE_ID, VPD_DI_SEL_TARGET, -1, "di_target", "Device "
135 "identification, target device only"},
136 {VPD_DTDE_ADDRESS, 0, 1, "dtde",
137 "Data transfer device element address (SSC)"},
138 {VPD_EXT_INQ, 0, -1, "ei", "Extended inquiry data"},
139 {VPD_FORMAT_PRESETS, 0, 0, "fp", "Format presets"},
140 {VPD_IMP_OP_DEF, 0, -1, "iod",
141 "Implemented operating definition (obsolete)"},
142 {VPD_LB_PROTECTION, 0, 0, "lbpro", "Logical block protection (SSC)"},
143 {VPD_LB_PROVISIONING, 0, 0, "lbpv", "Logical block provisioning (SBC)"},
144 {VPD_MAN_ASS_SN, 0, 1, "mas", "Manufacturer assigned serial number (SSC)"},
145 {VPD_MAN_ASS_SN, 0, 0x12, "masa",
146 "Manufacturer assigned serial number (ADC)"},
147 {VPD_MAN_NET_ADDR, 0, -1, "mna", "Management network addresses"},
148 {VPD_MODE_PG_POLICY, 0, -1, "mpp", "Mode page policy"},
149 {VPD_OSD_INFO, 0, 0x11, "oi", "OSD information"},
150 {VPD_POWER_CONDITION, 0, -1, "pc", "Power condition"},/* "po" in sg_inq */
151 {VPD_POWER_CONSUMPTION, 0, -1, "psm", "Power consumption"},
152 {VPD_PROTO_LU, 0, -1, "pslu", "Protocol-specific logical unit "
153 "information"},
154 {VPD_PROTO_PORT, 0, -1, "pspo", "Protocol-specific port information"},
155 {VPD_REFERRALS, 0, 0, "ref", "Referrals (SBC)"},
156 {VPD_SA_DEV_CAP, 0, 1, "sad",
157 "Sequential access device capabilities (SSC)"},
158 {VPD_SUP_BLOCK_LENS, 0, 0, "sbl", "Supported block lengths and "
159 "protection types (SBC)"},
160 {VPD_SCSI_FEATURE_SETS, 0, -1, "sfs", "SCSI feature sets"},
161 {VPD_SOFTW_INF_ID, 0, -1, "sii", "Software interface identification"},
162 {VPD_NOPE_WANT_STD_INQ, 0, -1, "sinq", "Standard inquiry data format"},
163 {VPD_UNIT_SERIAL_NUM, 0, -1, "sn", "Unit serial number"},
164 {VPD_SCSI_PORTS, 0, -1, "sp", "SCSI ports"},
165 {VPD_SECURITY_TOKEN, 0, 0x11, "st", "Security token (OSD)"},
166 {VPD_SUPPORTED_VPDS, 0, -1, "sv", "Supported VPD pages"},
167 {VPD_TA_SUPPORTED, 0, 1, "tas", "TapeAlert supported flags (SSC)"},
168 {VPD_3PARTY_COPY, 0, -1, "tpc", "Third party copy"},
169 {VPD_ZBC_DEV_CHARS, 0, -1, "zbdch", "Zoned block device characteristics"},
170 /* Use pdt of -1 since this page both for pdt=0 and pdt=0x14 */
171 {0, 0, 0, NULL, NULL},
172 };
173
174
175 static void
usage()176 usage()
177 {
178 pr2serr("Usage: sg_vpd [--all] [--enumerate] [--examine] [--force] "
179 "[--help] [--hex]\n"
180 " [--ident] [--inhex=FN] [--long] [--maxlen=LEN] "
181 "[--page=PG]\n"
182 " [--quiet] [--raw] [--sinq_inraw=RFN] "
183 "[--vendor=VP] [--verbose]\n"
184 " [--version] DEVICE\n");
185 pr2serr(" where:\n"
186 " --all|-a output all pages listed in the supported "
187 "pages VPD\n"
188 " page\n"
189 " --enumerate|-e enumerate known VPD pages names (ignore "
190 "DEVICE),\n"
191 " can be used with --page=num to search\n"
192 " --examine|-E starting at 0x80 scan pages code to 0xff\n"
193 " --force|-f skip VPD page 0 (supported VPD pages) "
194 "checking\n"
195 " --help|-h output this usage message then exit\n"
196 " --hex|-H output page in ASCII hexadecimal\n"
197 " --ident|-i output device identification VPD page, "
198 "twice for\n"
199 " short logical unit designator (equiv: "
200 "'-qp di_lu')\n"
201 " --inhex=FN|-I FN read ASCII hex from file FN instead of "
202 "DEVICE;\n"
203 " if used with --raw then read binary "
204 "from FN\n"
205 " --json[=JO]|-j[JO] output in JSON instead of human "
206 "readable text.\n"
207 " Use --json=? for JSON help\n"
208 " --long|-l perform extra decoding\n"
209 " --maxlen=LEN|-m LEN max response length (allocation "
210 "length in cdb)\n"
211 " (def: 0 -> 252 bytes)\n"
212 " --page=PG|-p PG fetch VPD page where PG is an "
213 "acronym, or a decimal\n"
214 " number unless hex indicator "
215 "is given (e.g. '0x83');\n"
216 " can also take PG,VP as an "
217 "operand\n"
218 " --quiet|-q suppress some output when decoding\n"
219 " --raw|-r output page in binary; if --inhex=FN is "
220 "also\n"
221 " given, FN is in binary (else FN is in "
222 "hex)\n"
223 " --sinq_inraw=RFN|-Q RFN read raw (binary) standard "
224 "INQUIRY\n"
225 " response from the RFN filename\n"
226 " --vendor=VP|-M VP vendor/product abbreviation [or "
227 "number]\n"
228 " --verbose|-v increase verbosity\n"
229 " --version|-V print version string and exit\n\n"
230 "Fetch Vital Product Data (VPD) page using SCSI INQUIRY or "
231 "decodes VPD\npage response held in file FN. To list available "
232 "pages use '-e'. Also\n'-p -1' or '-p sinq' yields the standard "
233 "INQUIRY response.\n");
234 }
235
236 static const struct svpd_values_name_t *
sdp_get_vpd_detail(int page_num,int subvalue,int pdt)237 sdp_get_vpd_detail(int page_num, int subvalue, int pdt)
238 {
239 const struct svpd_values_name_t * vnp;
240 int sv, ty;
241
242 sv = (subvalue < 0) ? 1 : 0;
243 ty = (pdt < 0) ? 1 : 0;
244 for (vnp = standard_vpd_pg; vnp->acron; ++vnp) {
245 if ((page_num == vnp->value) &&
246 (sv || (subvalue == vnp->subvalue)) &&
247 (ty || (pdt == vnp->pdt)))
248 return vnp;
249 }
250 if (! ty)
251 return sdp_get_vpd_detail(page_num, subvalue, -1);
252 if (! sv)
253 return sdp_get_vpd_detail(page_num, -1, -1);
254 return NULL;
255 }
256
257 static const struct svpd_values_name_t *
sdp_find_vpd_by_acron(const char * ap)258 sdp_find_vpd_by_acron(const char * ap)
259 {
260 const struct svpd_values_name_t * vnp;
261
262 for (vnp = standard_vpd_pg; vnp->acron; ++vnp) {
263 if (0 == strcmp(vnp->acron, ap))
264 return vnp;
265 }
266 return NULL;
267 }
268
269 static void
enumerate_vpds(int standard,int vendor)270 enumerate_vpds(int standard, int vendor)
271 {
272 const struct svpd_values_name_t * vnp;
273
274 if (standard) {
275 for (vnp = standard_vpd_pg; vnp->acron; ++vnp) {
276 if (vnp->name) {
277 if (vnp->value < 0)
278 printf(" %-10s -1 %s\n", vnp->acron, vnp->name);
279 else
280 printf(" %-10s 0x%02x %s\n", vnp->acron, vnp->value,
281 vnp->name);
282 }
283 }
284 }
285 if (vendor)
286 svpd_enumerate_vendor(-2);
287 }
288
289 static int
count_standard_vpds(int vpd_pn)290 count_standard_vpds(int vpd_pn)
291 {
292 const struct svpd_values_name_t * vnp;
293 int matches = 0;
294
295 for (vnp = standard_vpd_pg; vnp->acron; ++vnp) {
296 if ((vpd_pn == vnp->value) && vnp->name) {
297 if (0 == matches)
298 printf("Matching standard VPD pages:\n");
299 ++matches;
300 if (vnp->value < 0)
301 printf(" %-10s -1 %s\n", vnp->acron, vnp->name);
302 else
303 printf(" %-10s 0x%02x %s\n", vnp->acron, vnp->value,
304 vnp->name);
305 }
306 }
307 return matches;
308 }
309
310 static void
dStrRaw(const uint8_t * str,int len)311 dStrRaw(const uint8_t * str, int len)
312 {
313 int k;
314
315 for (k = 0; k < len; ++k)
316 printf("%c", str[k]);
317 }
318
319 /* Assume index is less than 16 */
320 static const char * sg_ansi_version_arr[16] =
321 {
322 "no conformance claimed",
323 "SCSI-1", /* obsolete, ANSI X3.131-1986 */
324 "SCSI-2", /* obsolete, ANSI X3.131-1994 */
325 "SPC", /* withdrawn, ANSI INCITS 301-1997 */
326 "SPC-2", /* ANSI INCITS 351-2001, ISO/IEC 14776-452 */
327 "SPC-3", /* ANSI INCITS 408-2005, ISO/IEC 14776-453 */
328 "SPC-4", /* ANSI INCITS 513-2015 */
329 "SPC-5", /* ANSI INCITS 502-2020 */
330 "ecma=1, [8h]",
331 "ecma=1, [9h]",
332 "ecma=1, [Ah]",
333 "ecma=1, [Bh]",
334 "reserved [Ch]",
335 "reserved [Dh]",
336 "reserved [Eh]",
337 "reserved [Fh]",
338 };
339
340 static void
std_inq_decode(uint8_t * b,int len,struct opts_t * op,sgj_opaque_p jop)341 std_inq_decode(uint8_t * b, int len, struct opts_t * op, sgj_opaque_p jop)
342 {
343 uint8_t ver;
344 int pqual, pdt, hp, j, n;
345 sgj_state * jsp = &op->json_st;
346 const char * cp;
347 char c[256];
348 static const int clen = sizeof(c);
349 static const char * np = "Standard INQUIRY data format:";
350
351 if (len < 4) {
352 pr2serr("%s: len [%d] too short\n", __func__, len);
353 return;
354 }
355 pqual = (b[0] & 0xe0) >> 5;
356 pdt = b[0] & PDT_MASK;
357 hp = (b[1] >> 4) & 0x3;
358 ver = b[2];
359 sgj_pr_hr(jsp, "%s", np);
360 if (0 == pqual)
361 sgj_pr_hr(jsp, "\n");
362 else {
363 cp = pqual_str(pqual);
364
365 if (pqual < 3)
366 sgj_pr_hr(jsp, " [PQ indicates %s]\n", cp);
367 else
368 sgj_pr_hr(jsp, " [PQ indicates %s [0x%x] ]\n", cp, pqual);
369 }
370 sgj_pr_hr(jsp, " PQual=%d PDT=%d RMB=%d LU_CONG=%d hot_pluggable="
371 "%d version=0x%02x [%s]\n", pqual, pdt, !!(b[1] & 0x80),
372 !!(b[1] & 0x40), hp, ver, sg_ansi_version_arr[ver & 0xf]);
373 sgj_pr_hr(jsp, " [AERC=%d] [TrmTsk=%d] NormACA=%d HiSUP=%d "
374 " Resp_data_format=%d\n",
375 !!(b[3] & 0x80), !!(b[3] & 0x40), !!(b[3] & 0x20),
376 !!(b[3] & 0x10), b[3] & 0x0f);
377 if (len < 5)
378 goto skip1;
379 j = b[4] + 5;
380 if (op->verbose > 2)
381 pr2serr(">> requested %d bytes, %d bytes available\n", len, j);
382 sgj_pr_hr(jsp, " SCCS=%d ACC=%d TPGS=%d 3PC=%d Protect=%d "
383 "[BQue=%d]\n", !!(b[5] & 0x80), !!(b[5] & 0x40),
384 ((b[5] & 0x30) >> 4), !!(b[5] & 0x08), !!(b[5] & 0x01),
385 !!(b[6] & 0x80));
386 n = 0;
387 n += sg_scnpr(c + n, clen - n, "EncServ=%d ", !!(b[6] & 0x40));
388 if (b[6] & 0x10)
389 n += sg_scnpr(c + n, clen - n, "MultiP=1 (VS=%d) ", !!(b[6] & 0x20));
390 else
391 n += sg_scnpr(c + n, clen - n, "MultiP=0 ");
392 n += sg_scnpr(c + n, clen - n, "[MChngr=%d] [ACKREQQ=%d] Addr16=%d",
393 !!(b[6] & 0x08), !!(b[6] & 0x04), !!(b[6] & 0x01));
394 sgj_pr_hr(jsp, " %s\n", c);
395 sgj_pr_hr(jsp, " [RelAdr=%d] WBus16=%d Sync=%d [Linked=%d] "
396 "[TranDis=%d] CmdQue=%d\n", !!(b[7] & 0x80), !!(b[7] & 0x20),
397 !!(b[7] & 0x10), !!(b[7] & 0x08), !!(b[7] & 0x04),
398 !!(b[7] & 0x02));
399 if (len < 36)
400 goto skip1;
401 sgj_pr_hr(jsp, " %s: %.8s\n", t10_vendor_id_hr, b + 8);
402 sgj_pr_hr(jsp, " %s: %.16s\n", product_id_hr, b + 16);
403 sgj_pr_hr(jsp, " %s: %.4s\n", product_rev_lev_hr, b + 32);
404 skip1:
405 if (! jsp->pr_as_json || (len < 8))
406 return;
407 std_inq_decode_js(b, len, op, jop);
408 }
409
410 /* VPD_DEVICE_ID 0x83 ["di, di_asis, di_lu, di_port, di_target"] */
411 static void
device_id_vpd_variants(uint8_t * buff,int len,int subvalue,struct opts_t * op,sgj_opaque_p jap)412 device_id_vpd_variants(uint8_t * buff, int len, int subvalue,
413 struct opts_t * op, sgj_opaque_p jap)
414 {
415 int m_a, blen;
416 uint8_t * b;
417
418 if (len < 4) {
419 pr2serr("Device identification VPD page length too short=%d\n", len);
420 return;
421 }
422 blen = len - 4;
423 b = buff + 4;
424 m_a = -1;
425 if (0 == subvalue) {
426 filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_LU), 0, b, blen,
427 VPD_ASSOC_LU, op, jap);
428 filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TPORT), 0, b, blen,
429 VPD_ASSOC_TPORT, op, jap);
430 filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TDEVICE), 0, b, blen,
431 VPD_ASSOC_TDEVICE, op, jap);
432 } else if (VPD_DI_SEL_AS_IS == subvalue)
433 filter_dev_ids(NULL, 0, b, blen, m_a, op, jap);
434 else {
435 if (VPD_DI_SEL_LU & subvalue)
436 filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_LU), 0, b, blen,
437 VPD_ASSOC_LU, op, jap);
438 if (VPD_DI_SEL_TPORT & subvalue)
439 filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TPORT), 0, b,
440 blen, VPD_ASSOC_TPORT, op, jap);
441 if (VPD_DI_SEL_TARGET & subvalue)
442 filter_dev_ids(sg_get_desig_assoc_str(VPD_ASSOC_TDEVICE), 0,
443 b, blen, VPD_ASSOC_TDEVICE, op, jap);
444 }
445 }
446
447 static void /* VPD_SUPPORTED_VPDS ["sv"] */
decode_supported_vpd_4vpd(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)448 decode_supported_vpd_4vpd(uint8_t * buff, int len, struct opts_t * op,
449 sgj_opaque_p jap)
450 {
451 uint8_t pn;
452 int k, rlen, pdt;
453 sgj_state * jsp = &op->json_st;
454 sgj_opaque_p jo2p;
455 const struct svpd_values_name_t * vnp;
456 uint8_t * bp;
457 char b[144];
458 static const int blen = sizeof(b);
459 static const char * svps = "Supported VPD pages";
460
461 if ((1 == op->do_hex) || (op->do_hex > 2)) {
462 hex2stdout(buff, len, no_ascii_4hex(op));
463 return;
464 }
465 pdt = PDT_MASK & buff[0];
466 rlen = buff[3] + 4;
467 if (rlen > len)
468 pr2serr("%s VPD page truncated, indicates %d, got %d\n", svps, rlen,
469 len);
470 else
471 len = rlen;
472 if (len < 4) {
473 pr2serr("%s VPD page length too short=%d\n", svps, len);
474 return;
475 }
476 len -= 4;
477 bp = buff + 4;
478
479 for (k = 0; k < len; ++k) {
480 pn = bp[k];
481 snprintf(b, blen, "0x%02x", pn);
482 vnp = sdp_get_vpd_detail(pn, -1, pdt);
483 if (vnp) {
484 if (op->do_long)
485 sgj_pr_hr(jsp, " %s %s [%s]\n", b, vnp->name, vnp->acron);
486 else
487 sgj_pr_hr(jsp, " %s [%s]\n", vnp->name, vnp->acron);
488 } else if (op->vend_prod_num >= 0) {
489 vnp = svpd_find_vendor_by_num(pn, op->vend_prod_num);
490 if (vnp) {
491 if (op->do_long)
492 sgj_pr_hr(jsp, " %s %s [%s]\n", b, vnp->name,
493 vnp->acron);
494 else
495 sgj_pr_hr(jsp, " %s [%s]\n", vnp->name, vnp->acron);
496 } else
497 sgj_pr_hr(jsp, " %s\n", b);
498 } else
499 sgj_pr_hr(jsp, " %s\n", b);
500 if (jsp->pr_as_json) {
501 jo2p = sgj_new_unattached_object_r(jsp);
502 sgj_js_nv_i(jsp, jo2p, "i", pn);
503 sgj_js_nv_s(jsp, jo2p, "hex", b + 2);
504 if (vnp) {
505 sgj_js_nv_s(jsp, jo2p, "name", vnp->name);
506 sgj_js_nv_s(jsp, jo2p, "acronym", vnp->acron);
507 } else {
508 sgj_js_nv_s(jsp, jo2p, "name", "unknown");
509 sgj_js_nv_s(jsp, jo2p, "acronym", "unknown");
510 }
511 sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
512 }
513 }
514 }
515
516 /* VPD_SCSI_PORTS 0x88 ["sp"] */
517 static void
decode_scsi_ports_vpd_4vpd(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)518 decode_scsi_ports_vpd_4vpd(uint8_t * buff, int len, struct opts_t * op,
519 sgj_opaque_p jap)
520 {
521 int k, bump, rel_port, ip_tid_len, tpd_len;
522 sgj_state * jsp = &op->json_st;
523 sgj_opaque_p jo2p = NULL;
524 sgj_opaque_p ja2p = NULL;
525 uint8_t * bp;
526
527 if ((1 == op->do_hex) || (op->do_hex > 2)) {
528 hex2stdout(buff, len, no_ascii_4hex(op));
529 return;
530 }
531 if (len < 4) {
532 pr2serr("SCSI Ports VPD page length too short=%d\n", len);
533 return;
534 }
535 len -= 4;
536 bp = buff + 4;
537 for (k = 0; k < len; k += bump, bp += bump) {
538 rel_port = sg_get_unaligned_be16(bp + 2);
539 sgj_pr_hr(jsp, " Relative port=%d\n", rel_port);
540 jo2p = sgj_new_unattached_object_r(jsp);
541 sgj_js_nv_i(jsp, jo2p, "relative_port", rel_port);
542 ip_tid_len = sg_get_unaligned_be16(bp + 6);
543 bump = 8 + ip_tid_len;
544 if ((k + bump) > len) {
545 pr2serr("SCSI Ports VPD page, short descriptor "
546 "length=%d, left=%d\n", bump, (len - k));
547 return;
548 }
549 if (ip_tid_len > 0) {
550 if (op->do_hex > 1) {
551 sgj_pr_hr(jsp, " Initiator port transport id:\n");
552 hex2stdout((bp + 8), ip_tid_len, 1);
553 } else {
554 char b[1024];
555
556 sg_decode_transportid_str(" ", bp + 8, ip_tid_len,
557 true, sizeof(b), b);
558 if (jsp->pr_as_json)
559 sgj_js_nv_s(jsp, jo2p, "initiator_port_transport_id", b);
560 sgj_pr_hr(jsp, "%s",
561 sg_decode_transportid_str(" ", bp + 8,
562 ip_tid_len, true, sizeof(b), b));
563 }
564 }
565 tpd_len = sg_get_unaligned_be16(bp + bump + 2);
566 if ((k + bump + tpd_len + 4) > len) {
567 pr2serr("SCSI Ports VPD page, short descriptor(tgt) "
568 "length=%d, left=%d\n", bump, (len - k));
569 return;
570 }
571 if (tpd_len > 0) {
572 if (op->do_hex > 1) {
573 sgj_pr_hr(jsp, " Target port descriptor(s):\n");
574 hex2stdout(bp + bump + 4, tpd_len, 1);
575 } else {
576 if ((0 == op->do_quiet) || (ip_tid_len > 0))
577 sgj_pr_hr(jsp, " Target port descriptor(s):\n");
578 if (jsp->pr_as_json) {
579 sgj_opaque_p jo3p = sgj_named_subobject_r(jsp, jo2p,
580 "target_port");
581
582 ja2p = sgj_named_subarray_r(jsp, jo3p,
583 "designation_descriptor_list");
584 }
585 filter_dev_ids("", 2 /* leading spaces */, bp + bump + 4,
586 tpd_len, VPD_ASSOC_TPORT, op, ja2p);
587 }
588 }
589 bump += tpd_len + 4;
590 sgj_js_nv_o(jsp, jap, NULL, jo2p);
591 }
592 }
593
594 /* Prints outs an abridged set of device identification designators
595 selected by association, designator type and/or code set. Not used
596 for JSON output. */
597 static int
filter_dev_ids_quiet(uint8_t * buff,int len,int m_assoc)598 filter_dev_ids_quiet(uint8_t * buff, int len, int m_assoc)
599 {
600 int k, m, p_id, c_set, piv, desig_type, i_len, naa, off, u;
601 int assoc, is_sas, rtp;
602 const uint8_t * bp;
603 const uint8_t * ip;
604 uint8_t sas_tport_addr[8];
605
606 rtp = 0;
607 memset(sas_tport_addr, 0, sizeof(sas_tport_addr));
608 for (k = 0, off = -1; true; ++k) {
609 if ((0 == k) && (0 != buff[2])) {
610 /* first already in buff */
611 if (m_assoc != VPD_ASSOC_LU)
612 return 0;
613 ip = buff;
614 c_set = 1;
615 assoc = VPD_ASSOC_LU;
616 is_sas = 0;
617 desig_type = 3;
618 i_len = 16;
619 } else {
620 u = sg_vpd_dev_id_iter(buff, len, &off, m_assoc, -1, -1);
621 if (0 != u)
622 break;
623 bp = buff + off;
624 i_len = bp[3];
625 if ((off + i_len + 4) > len) {
626 pr2serr(" VPD page error: designator length longer than\n"
627 " remaining response length=%d\n", (len - off));
628 return SG_LIB_CAT_MALFORMED;
629 }
630 ip = bp + 4;
631 p_id = ((bp[0] >> 4) & 0xf);
632 c_set = (bp[0] & 0xf);
633 piv = ((bp[1] & 0x80) ? 1 : 0);
634 is_sas = (piv && (6 == p_id)) ? 1 : 0;
635 assoc = ((bp[1] >> 4) & 0x3);
636 desig_type = (bp[1] & 0xf);
637 }
638 switch (desig_type) {
639 case 0: /* vendor specific */
640 break;
641 case 1: /* T10 vendor identification */
642 break;
643 case 2: /* EUI-64 based */
644 if ((8 != i_len) && (12 != i_len) && (16 != i_len))
645 pr2serr(" << expect 8, 12 and 16 byte "
646 "EUI, got %d>>\n", i_len);
647 printf(" 0x");
648 for (m = 0; m < i_len; ++m)
649 printf("%02x", (unsigned int)ip[m]);
650 printf("\n");
651 break;
652 case 3: /* NAA */
653 naa = (ip[0] >> 4) & 0xff;
654 if (1 != c_set) {
655 pr2serr(" << expected binary code_set (1), got %d for "
656 "NAA=%d>>\n", c_set, naa);
657 hex2stderr(ip, i_len, 0);
658 break;
659 }
660 switch (naa) {
661 case 2: /* NAA IEEE extended */
662 if (8 != i_len) {
663 pr2serr(" << unexpected NAA 2 identifier "
664 "length: 0x%x>>\n", i_len);
665 hex2stderr(ip, i_len, 0);
666 break;
667 }
668 printf(" 0x");
669 for (m = 0; m < 8; ++m)
670 printf("%02x", (unsigned int)ip[m]);
671 printf("\n");
672 break;
673 case 3: /* Locally assigned */
674 case 5: /* IEEE Registered */
675 if (8 != i_len) {
676 pr2serr(" << unexpected NAA 3 or 5 "
677 "identifier length: 0x%x>>\n", i_len);
678 hex2stderr(ip, i_len, 0);
679 break;
680 }
681 if ((0 == is_sas) || (1 != assoc)) {
682 printf(" 0x");
683 for (m = 0; m < 8; ++m)
684 printf("%02x", (unsigned int)ip[m]);
685 printf("\n");
686 } else if (rtp) {
687 printf(" 0x");
688 for (m = 0; m < 8; ++m)
689 printf("%02x", (unsigned int)ip[m]);
690 printf(",0x%x\n", rtp);
691 rtp = 0;
692 } else {
693 if (sas_tport_addr[0]) {
694 printf(" 0x");
695 for (m = 0; m < 8; ++m)
696 printf("%02x", (unsigned int)sas_tport_addr[m]);
697 printf("\n");
698 }
699 memcpy(sas_tport_addr, ip, sizeof(sas_tport_addr));
700 }
701 break;
702 case 6: /* NAA IEEE registered extended */
703 if (16 != i_len) {
704 pr2serr(" << unexpected NAA 6 identifier length: "
705 "0x%x>>\n", i_len);
706 hex2stderr(ip, i_len, 0);
707 break;
708 }
709 printf(" 0x");
710 for (m = 0; m < 16; ++m)
711 printf("%02x", (unsigned int)ip[m]);
712 printf("\n");
713 break;
714 default:
715 pr2serr(" << bad NAA nibble, expected 2, 3, 5 or 6, got "
716 "%d>>\n", naa);
717 hex2stderr(ip, i_len, 0);
718 break;
719 }
720 break;
721 case 4: /* Relative target port */
722 if ((0 == is_sas) || (1 != c_set) || (1 != assoc) || (4 != i_len))
723 break;
724 rtp = sg_get_unaligned_be16(ip + 2);
725 if (sas_tport_addr[0]) {
726 printf(" 0x");
727 for (m = 0; m < 8; ++m)
728 printf("%02x", (unsigned int)sas_tport_addr[m]);
729 printf(",0x%x\n", rtp);
730 memset(sas_tport_addr, 0, sizeof(sas_tport_addr));
731 rtp = 0;
732 }
733 break;
734 case 5: /* (primary) Target port group */
735 break;
736 case 6: /* Logical unit group */
737 break;
738 case 7: /* MD5 logical unit identifier */
739 break;
740 case 8: /* SCSI name string */
741 if (c_set < 2) { /* quietly accept ASCII for UTF-8 */
742 pr2serr(" << expected UTF-8 code_set>>\n");
743 hex2stderr(ip, i_len, 0);
744 break;
745 }
746 if (! (strncmp((const char *)ip, "eui.", 4) ||
747 strncmp((const char *)ip, "EUI.", 4) ||
748 strncmp((const char *)ip, "naa.", 4) ||
749 strncmp((const char *)ip, "NAA.", 4) ||
750 strncmp((const char *)ip, "iqn.", 4))) {
751 pr2serr(" << expected name string prefix>>\n");
752 hex2stderr(ip, i_len, -1);
753 break;
754 }
755 /* does %s print out UTF-8 ok??
756 * Seems to depend on the locale. Looks ok here with my
757 * locale setting: en_AU.UTF-8
758 */
759 printf(" %.*s\n", i_len, (const char *)ip);
760 break;
761 case 9: /* Protocol specific port identifier */
762 break;
763 case 0xa: /* UUID identifier [spc5r08] RFC 4122 */
764 if ((1 != c_set) || (18 != i_len) || (1 != ((ip[0] >> 4) & 0xf)))
765 break;
766 for (m = 0; m < 16; ++m) {
767 if ((4 == m) || (6 == m) || (8 == m) || (10 == m))
768 printf("-");
769 printf("%02x", (unsigned int)ip[2 + m]);
770 }
771 printf("\n");
772 break;
773 default: /* reserved */
774 break;
775 }
776 }
777 if (sas_tport_addr[0]) {
778 printf(" 0x");
779 for (m = 0; m < 8; ++m)
780 printf("%02x", (unsigned int)sas_tport_addr[m]);
781 printf("\n");
782 }
783 if (-2 == u) {
784 pr2serr("VPD page error: short designator around offset %d\n", off);
785 return SG_LIB_CAT_MALFORMED;
786 }
787 return 0;
788 }
789
790 /* Prints outs designation descriptors (dd_s) selected by association,
791 designator type and/or code set. VPD_DEVICE_ID and VPD_SCSI_PORTS */
792 static int
filter_dev_ids(const char * print_if_found,int num_leading,uint8_t * buff,int len,int m_assoc,struct opts_t * op,sgj_opaque_p jap)793 filter_dev_ids(const char * print_if_found, int num_leading, uint8_t * buff,
794 int len, int m_assoc, struct opts_t * op, sgj_opaque_p jap)
795 {
796 bool printed, sgj_out_hr;
797 int assoc, off, u, i_len;
798 const uint8_t * bp;
799 sgj_state * jsp = &op->json_st;
800 char b[1024];
801 char sp[82];
802 static const int blen = sizeof(b);
803
804 if (op->do_quiet && (! jsp->pr_as_json))
805 return filter_dev_ids_quiet(buff, len, m_assoc);
806 sgj_out_hr = false;
807 if (jsp->pr_as_json) {
808 int ret = filter_json_dev_ids(buff, len, m_assoc, op, jap);
809
810 if (ret || (! jsp->pr_out_hr))
811 return ret;
812 sgj_out_hr = true;
813 }
814 if (num_leading > (int)(sizeof(sp) - 2))
815 num_leading = sizeof(sp) - 2;
816 if (num_leading > 0)
817 snprintf(sp, sizeof(sp), "%*c", num_leading, ' ');
818 else
819 sp[0] = '\0';
820 if (buff[2] != 0) { /* all valid dd_s should have 0 in this byte */
821 if (op->verbose)
822 pr2serr("%s: designation descriptors byte 2 should be 0\n"
823 "perhaps this is a standard inquiry response, ignore\n",
824 __func__);
825 return 0;
826 }
827 off = -1;
828 printed = false;
829 while ((u = sg_vpd_dev_id_iter(buff, len, &off, m_assoc, -1, -1)) == 0) {
830 bp = buff + off;
831 i_len = bp[3];
832 if ((off + i_len + 4) > len) {
833 pr2serr(" VPD page error: designator length longer than\n"
834 " remaining response length=%d\n", (len - off));
835 return SG_LIB_CAT_MALFORMED;
836 }
837 assoc = ((bp[1] >> 4) & 0x3);
838 if (print_if_found && (! printed)) {
839 printed = true;
840 if (strlen(print_if_found) > 0) {
841 snprintf(b, blen, " %s:", print_if_found);
842 if (sgj_out_hr)
843 sgj_js_str_out(jsp, b, strlen(b));
844 else
845 printf("%s\n", b);
846 }
847 }
848 if (NULL == print_if_found) {
849 snprintf(b, blen, " %s%s:", sp, sg_get_desig_assoc_str(assoc));
850 if (sgj_out_hr)
851 sgj_js_str_out(jsp, b, strlen(b));
852 else
853 printf("%s\n", b);
854 }
855 sg_get_designation_descriptor_str(sp, bp, i_len + 4, false,
856 op->do_long, blen, b);
857 if (sgj_out_hr)
858 sgj_js_str_out(jsp, b, strlen(b));
859 else
860 printf("%s", b);
861 }
862 if (-2 == u) {
863 pr2serr("VPD page error: short designator around offset %d\n", off);
864 return SG_LIB_CAT_MALFORMED;
865 }
866 return 0;
867 }
868
869 /* VPD_BLOCK_LIMITS sbc */
870 /* VPD_SA_DEV_CAP ssc */
871 /* VPD_OSD_INFO osd */
872 static void
decode_b0_vpd(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)873 decode_b0_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jop)
874 {
875 int pdt = PDT_MASK & buff[0];
876 sgj_state * jsp = &op->json_st;
877
878 if (op->do_hex) {
879 hex2stdout(buff, len, no_ascii_4hex(op));
880 return;
881 }
882 switch (pdt) {
883 case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
884 /* now done by decode_block_limits_vpd() in sg_vpd_common.c */
885 break;
886 case PDT_TAPE: case PDT_MCHANGER:
887 sgj_haj_vi_nex(jsp, jop, 2, "TSMC", SGJ_SEP_EQUAL_NO_SPACE,
888 !!(buff[4] & 0x2), false, "Tape Stream Mirror "
889 "Capable");
890 sgj_haj_vi_nex(jsp, jop, 2, "WORM", SGJ_SEP_EQUAL_NO_SPACE,
891 !!(buff[4] & 0x1), false, "Write Once Read Multiple "
892 "supported");
893 break;
894 case PDT_OSD:
895 default:
896 pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt);
897 hex2stderr(buff, len, 0);
898 break;
899 }
900 }
901
902 /* VPD_BLOCK_DEV_CHARS sbc 0xb1 ["bdc"] */
903 /* VPD_MAN_ASS_SN ssc */
904 /* VPD_SECURITY_TOKEN osd */
905 /* VPD_ES_DEV_CHARS ses-4 */
906 static void
decode_b1_vpd(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)907 decode_b1_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jop)
908 {
909 int pdt;
910 sgj_state * jsp = &op->json_st;
911
912 pdt = buff[0] & PDT_MASK;
913 if (op->do_hex) {
914 hex2stdout(buff, len, no_ascii_4hex(op));
915 return;
916 }
917 switch (pdt) {
918 case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
919 /* now done by decode_block_dev_ch_vpd() in sg_vpd_common.c */
920 case PDT_TAPE: case PDT_MCHANGER: case PDT_ADC:
921 sgj_pr_hr(jsp, " Manufacturer-assigned serial number: %.*s\n",
922 len - 4, buff + 4);
923 sgj_js_nv_s_len(jsp, jop, "manufacturer_assigned_serial_number",
924 (const char *)buff + 4, len - 4);
925 break;
926 default:
927 pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt);
928 hex2stderr(buff, len, 0);
929 break;
930 }
931 }
932
933 /* VPD_LB_PROVISIONING sbc */
934 /* VPD_TA_SUPPORTED ssc */
935 static void
decode_b2_vpd(uint8_t * buff,int len,int pdt,struct opts_t * op)936 decode_b2_vpd(uint8_t * buff, int len, int pdt, struct opts_t * op)
937 {
938 if (op->do_hex) {
939 hex2stdout(buff, len, no_ascii_4hex(op));
940 return;
941 }
942 switch (pdt) {
943 case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
944 /* decode_block_lb_prov_vpd() is now in sg_vpd_common.c */
945 break;
946 case PDT_TAPE: case PDT_MCHANGER:
947 /* decode_tapealert_supported_vpd() is now in sg_vpd_common.c */
948 break;
949 default:
950 pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt);
951 hex2stderr(buff, len, 0);
952 break;
953 }
954 }
955
956 /* VPD_REFERRALS sbc 0xb3 ["ref"] */
957 /* VPD_AUTOMATION_DEV_SN ssc 0xb3 ["adsn"] */
958 static void
decode_b3_vpd(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)959 decode_b3_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jop)
960 {
961 int pdt;
962 sgj_state * jsp = &op->json_st;
963
964 if (op->do_hex) {
965 hex2stdout(buff, len, no_ascii_4hex(op));
966 return;
967 }
968 pdt = buff[0] & PDT_MASK;
969 switch (pdt) {
970 case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
971 /* now done in decode_referrals_vpd() in sg_vpd_common.c */
972 break;
973 case PDT_TAPE: case PDT_MCHANGER:
974 sgj_pr_hr(jsp, " Automation device serial number: %.*s\n",
975 len - 4, buff + 4);
976 sgj_js_nv_s_len(jsp, jop, "automation_device_serial_number",
977 (const char *)buff + 4, len - 4);
978 break;
979 default:
980 pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt);
981 hex2stderr(buff, len, 0);
982 break;
983 }
984 }
985
986 /* VPD_SUP_BLOCK_LENS sbc ["sbl"] */
987 /* VPD_DTDE_ADDRESS ssc */
988 static void
decode_b4_vpd(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)989 decode_b4_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jop)
990 {
991 int pdt = buff[0] & PDT_MASK;
992 sgj_state * jsp = &op->json_st;
993
994 if (op->do_hex) {
995 hex2stdout(buff, len, no_ascii_4hex(op));
996 return;
997 }
998 switch (pdt) {
999 case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
1000 /* now done by decode_sup_block_lens_vpd() in sg_vpd_common.c */
1001 break;
1002 case PDT_TAPE: case PDT_MCHANGER:
1003 sgj_pr_hr(jsp, " Device transfer data element:\n");
1004 if (! jsp->pr_as_json)
1005 hex2stdout(buff + 4, len - 4, 1);
1006 sgj_js_nv_hex_bytes(jsp, jop, "device_transfer_data_element",
1007 buff + 4, len - 4);
1008 break;
1009 default:
1010 pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt);
1011 hex2stderr(buff, len, 0);
1012 break;
1013 }
1014 }
1015
1016 /* VPD_BLOCK_DEV_C_EXTENS sbc */
1017 /* VPD_LB_PROTECTION 0xb5 ["lbpro"] ssc */
1018 static void
decode_b5_vpd(uint8_t * b,int len,int do_hex,int pdt)1019 decode_b5_vpd(uint8_t * b, int len, int do_hex, int pdt)
1020 {
1021 if (do_hex) {
1022 hex2stdout(b, len, (1 == do_hex) ? 0 : -1);
1023 return;
1024 }
1025 switch (pdt) {
1026 case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
1027 /* now done by decode_block_dev_char_ext_vpd() in sg_vpd_common.c */
1028 break;
1029 case PDT_TAPE: case PDT_MCHANGER:
1030 /* now done by decode_lb_protection_vpd() in sg_vpd_common.c */
1031 break;
1032 default:
1033 pr2serr(" Unable to decode pdt=0x%x, in hex:\n", pdt);
1034 hex2stderr(b, len, 0);
1035 break;
1036 }
1037 }
1038
1039 /* Returns 0 if successful */
1040 static int
svpd_unable_to_decode(int sg_fd,struct opts_t * op,int subvalue,int off)1041 svpd_unable_to_decode(int sg_fd, struct opts_t * op, int subvalue, int off)
1042 {
1043 bool as_json, json_o_hr, hex0;
1044 int res, len, n;
1045 sgj_state * jsp = &op->json_st;
1046 uint8_t * rp;
1047
1048 as_json = jsp->pr_as_json;
1049 json_o_hr = as_json && jsp->pr_out_hr;
1050 hex0 = (0 == op->do_hex);
1051 rp = rsp_buff + off;
1052 if (hex0 && (! op->do_raw) && (! op->examine_given))
1053 sgj_pr_hr(jsp, "Only hex output supported\n");
1054 if ((!op->do_raw) && (op->do_hex < 2) && (! op->examine_given)) {
1055 if (subvalue) {
1056 if (hex0)
1057 sgj_pr_hr(jsp, "VPD page code=0x%.2x, subvalue=0x%.2x:\n",
1058 op->vpd_pn, subvalue);
1059 else
1060 printf("VPD page code=0x%.2x, subvalue=0x%.2x:\n", op->vpd_pn,
1061 subvalue);
1062 } else if (op->vpd_pn >= 0) {
1063 if (hex0)
1064 sgj_pr_hr(jsp, "VPD page code=0x%.2x:\n", op->vpd_pn);
1065 else
1066 printf("VPD page code=0x%.2x:\n", op->vpd_pn);
1067 } else {
1068 if (hex0)
1069 sgj_pr_hr(jsp, "VPD page code=%d:\n", op->vpd_pn);
1070 else
1071 printf("VPD page code=%d:\n", op->vpd_pn);
1072 }
1073 }
1074
1075 res = vpd_fetch_page(sg_fd, rp, op->vpd_pn, op->maxlen, op->do_quiet,
1076 op->verbose, &len);
1077 if (0 == res) {
1078 if (op->do_raw)
1079 dStrRaw(rp, len);
1080 else {
1081 if (json_o_hr && hex0 && (len > 0) && (len < UINT16_MAX)) {
1082 char * p;
1083
1084 n = len * 4;
1085 p = malloc(n);
1086 if (p) {
1087 n = hex2str(rp, len, NULL, 1, n - 1, p);
1088 sgj_js_str_out(jsp, p, n);
1089 }
1090 } else
1091 hex2stdout(rp, len, no_ascii_4hex(op));
1092 }
1093 } else if ((! op->do_quiet) && (! op->examine_given)) {
1094 if (op->vpd_pn >= 0)
1095 pr2serr("fetching VPD page code=0x%.2x: failed\n", op->vpd_pn);
1096 else
1097 pr2serr("fetching VPD page code=%d: failed\n", op->vpd_pn);
1098 }
1099 return res;
1100 }
1101
1102 static int
recurse_vpd_decode(struct opts_t * op,sgj_opaque_p jop,int off)1103 recurse_vpd_decode(struct opts_t * op, sgj_opaque_p jop, int off)
1104 {
1105 int res = svpd_decode_t10(-1, op, jop, 0, off, NULL);
1106
1107 if (SG_LIB_CAT_OTHER == res) {
1108 res = svpd_decode_vendor(-1, op, jop, off);
1109 if (SG_LIB_CAT_OTHER == res)
1110 svpd_unable_to_decode(-1, op, 0, off);
1111 }
1112 return res;
1113 }
1114
1115 /* Returns 0 if successful. If don't know how to decode, returns
1116 * SG_LIB_CAT_OTHER else see sg_ll_inquiry(). */
1117 static int
svpd_decode_t10(int sg_fd,struct opts_t * op,sgj_opaque_p jop,int subvalue,int off,const char * prefix)1118 svpd_decode_t10(int sg_fd, struct opts_t * op, sgj_opaque_p jop,
1119 int subvalue, int off, const char * prefix)
1120 {
1121 bool allow_name, allow_if_found, long_notquiet, qt;
1122 bool vpd_supported = false;
1123 bool inhex_active = (-1 == sg_fd);
1124 bool exam_not_given = ! op->examine_given;
1125 int len, pdt, pqual, num, k, resid, alloc_len, pn, vb;
1126 int res = 0;
1127 sgj_state * jsp = &op->json_st;
1128 uint8_t * rp;
1129 sgj_opaque_p jap = NULL;
1130 sgj_opaque_p jo2p = NULL;
1131 const char * np;
1132 const char * ep;
1133 const char * pre = (prefix ? prefix : "");
1134 const char * pdt_str;
1135 bool as_json = jsp->pr_as_json;
1136 bool not_json = ! as_json;
1137 char obuff[DEF_ALLOC_LEN];
1138 char d[48];
1139
1140 vb = op->verbose;
1141 qt = op->do_quiet;
1142 long_notquiet = op->do_long && (! op->do_quiet);
1143 if (op->do_raw || (op->do_quiet && (! op->do_long) && (! op->do_all)) ||
1144 (op->do_hex >= 3) || op->examine_given)
1145 allow_name = false;
1146 else
1147 allow_name = true;
1148 allow_if_found = op->examine_given && (! op->do_quiet);
1149 rp = rsp_buff + off;
1150 pn = op->vpd_pn;
1151 if ((off > 0) && (VPD_NOPE_WANT_STD_INQ != op->vpd_pn))
1152 pn = rp[1];
1153 else
1154 pn = op->vpd_pn;
1155 if (!inhex_active && !op->do_force && exam_not_given &&
1156 pn != VPD_NOPE_WANT_STD_INQ &&
1157 pn != VPD_SUPPORTED_VPDS) {
1158 res = vpd_fetch_page(sg_fd, rp, VPD_SUPPORTED_VPDS, op->maxlen, qt,
1159 vb, &len);
1160 if (res)
1161 return res;
1162
1163 num = rp[3];
1164 if (num > (len - 4))
1165 num = (len - 4);
1166 if (vb > 1) {
1167 pr2serr("Supported VPD pages, hex list: ");
1168 hex2stderr(rp + 4, num, -1);
1169 }
1170 for (k = 0; k < num; ++k) {
1171 if (pn == rp[4 + k]) {
1172 vpd_supported = true;
1173 break;
1174 }
1175 }
1176 if (! vpd_supported) { /* get creative, was SG_LIB_CAT_ILLEGAL_REQ */
1177 if (vb)
1178 pr2serr("Given VPD page not in supported list, use --force "
1179 "to override this check\n");
1180 return sg_convert_errno(EDOM);
1181 }
1182 }
1183 pdt = rp[0] & PDT_MASK;
1184 pdt_str = sg_get_pdt_str(pdt, sizeof(d), d);
1185 pqual = (rp[0] & 0xe0) >> 5;
1186
1187 switch(pn) {
1188 case VPD_NOPE_WANT_STD_INQ: /* -2 (want standard inquiry response) */
1189 if (!inhex_active) {
1190 if (op->maxlen > 0)
1191 alloc_len = op->maxlen;
1192 else if (op->do_long)
1193 alloc_len = DEF_ALLOC_LEN;
1194 else
1195 alloc_len = 36;
1196 res = sg_ll_inquiry_v2(sg_fd, false, 0, rp, alloc_len,
1197 DEF_PT_TIMEOUT, &resid, ! op->do_quiet, vb);
1198 } else {
1199 alloc_len = op->maxlen;
1200 resid = 0;
1201 res = 0;
1202 }
1203 if (0 == res) {
1204 alloc_len -= resid;
1205 if (op->do_raw)
1206 dStrRaw(rp, alloc_len);
1207 else if (op->do_hex) {
1208 if (! op->do_quiet && (op->do_hex < 3))
1209 sgj_pr_hr(jsp, "Standard Inquiry data format:\n");
1210 hex2stdout(rp, alloc_len, (1 == op->do_hex) ? 0 : -1);
1211 } else
1212 std_inq_decode(rp, alloc_len, op, jop);
1213 return 0;
1214 }
1215 break;
1216 case VPD_SUPPORTED_VPDS: /* 0x0 ["sv"] */
1217 np = "Supported VPD pages VPD page";
1218 if (allow_name)
1219 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1220 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1221 if (0 == res) {
1222 if (! allow_name && allow_if_found)
1223 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1224 if (op->do_raw)
1225 dStrRaw(rp, len);
1226 else if (op->do_hex)
1227 hex2stdout(rp, len, no_ascii_4hex(op));
1228 else {
1229 if (vb || long_notquiet)
1230 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
1231 "%s]\n", pqual, pdt_str);
1232 num = rp[3];
1233 if (num > (len - 4))
1234 num = (len - 4);
1235 if (as_json) {
1236 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1237 jap = sgj_named_subarray_r(jsp, jo2p,
1238 "supported_vpd_page_list");
1239 }
1240 decode_supported_vpd_4vpd(rp, len, op, jap);
1241 }
1242 return 0;
1243 }
1244 break;
1245 case VPD_UNIT_SERIAL_NUM: /* 0x80 ["sn"] */
1246 np = "Unit serial number VPD page";
1247 if (allow_name && not_json)
1248 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1249 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1250 if (0 == res) {
1251 if (! allow_name && allow_if_found)
1252 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1253 if (op->do_raw)
1254 dStrRaw(rp, len);
1255 else if (op->do_hex)
1256 hex2stdout(rp, len, no_ascii_4hex(op));
1257 else {
1258 if (vb || long_notquiet)
1259 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
1260 "%s]\n", pqual, pdt_str);
1261 memset(obuff, 0, sizeof(obuff));
1262 len -= 4;
1263 if (len >= (int)sizeof(obuff))
1264 len = sizeof(obuff) - 1;
1265 memcpy(obuff, rp + 4, len);
1266 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1267 sgj_haj_vs(jsp, jo2p, 2, np, SGJ_SEP_COLON_1_SPACE, obuff);
1268 }
1269 return 0;
1270 }
1271 break;
1272 case VPD_DEVICE_ID: /* 0x83 ["di, di_asis, di_lu, di_port, di_target"] */
1273 np = "Device Identification VPD page";
1274 if (allow_name)
1275 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1276 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1277 if (0 == res) {
1278 if (! allow_name && allow_if_found)
1279 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1280 if (op->do_raw)
1281 dStrRaw(rp, len);
1282 else if (op->do_hex)
1283 hex2stdout(rp, len, no_ascii_4hex(op));
1284 else {
1285 if (vb || long_notquiet)
1286 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
1287 "%s]\n", pqual, pdt_str);
1288 if (as_json) {
1289 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1290 jap = sgj_named_subarray_r(jsp, jo2p,
1291 "designation_descriptor_list");
1292 }
1293 device_id_vpd_variants(rp, len, subvalue, op, jap);
1294 }
1295 return 0;
1296 }
1297 break;
1298 case VPD_SOFTW_INF_ID: /* 0x84 ["sii"] */
1299 np = "Software interface identification VPD page";
1300 if (allow_name)
1301 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1302 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1303 if (0 == res) {
1304 if (! allow_name && allow_if_found)
1305 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1306 if (op->do_raw)
1307 dStrRaw(rp, len);
1308 else {
1309 if (vb || long_notquiet)
1310 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
1311 "%s]\n", pqual, pdt_str);
1312 if (as_json) {
1313 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1314 jap = sgj_named_subarray_r(jsp, jo2p,
1315 "software_interface_identifier_list");
1316 }
1317 decode_softw_inf_id(rp, len, op, jap);
1318 }
1319 return 0;
1320 }
1321 break;
1322 case VPD_MAN_NET_ADDR: /* 0x85 ["mna"] */
1323 np= "Management network addresses VPD page";
1324 if (allow_name)
1325 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1326 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1327 if (0 == res) {
1328 if (! allow_name && allow_if_found)
1329 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1330 if (op->do_raw)
1331 dStrRaw(rp, len);
1332 else {
1333 if (as_json) {
1334 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1335 jap = sgj_named_subarray_r(jsp, jo2p,
1336 "network_services_descriptor_list");
1337 }
1338 decode_net_man_vpd(rp, len, op, jap);
1339 }
1340 return 0;
1341 }
1342 break;
1343 case VPD_EXT_INQ: /* 0x86 ["ei"] */
1344 np = "extended INQUIRY data VPD page";
1345 if (allow_name)
1346 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1347 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1348 if (0 == res) {
1349 if (! allow_name && allow_if_found)
1350 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1351 if (op->do_raw)
1352 dStrRaw(rp, len);
1353 else {
1354 bool protect = false;
1355
1356 op->protect_not_sure = false;
1357 if (op->std_inq_a_valid)
1358 protect = !! (0x1 & op->std_inq_a[5]);
1359 else if ((sg_fd >= 0) && (! op->do_force)) {
1360 struct sg_simple_inquiry_resp sir;
1361
1362 res = sg_simple_inquiry(sg_fd, &sir, false, vb);
1363 if (res) {
1364 if (op->verbose)
1365 pr2serr("%s: sg_simple_inquiry() failed, "
1366 "res=%d\n", __func__, res);
1367 op->protect_not_sure = true;
1368 } else
1369 protect = !!(sir.byte_5 & 0x1); /* SPC-3 and later */
1370 } else
1371 op->protect_not_sure = true;
1372 if (vb || long_notquiet)
1373 sgj_pr_hr(jsp," [PQual=%d Peripheral device type: "
1374 "%s]\n", pqual, pdt_str);
1375 if (as_json)
1376 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1377 decode_x_inq_vpd(rp, len, protect, op, jo2p);
1378 }
1379 return 0;
1380 }
1381 break;
1382 case VPD_MODE_PG_POLICY: /* 0x87 */
1383 np = "Mode page policy VPD page";
1384 if (allow_name)
1385 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1386 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1387 if (0 == res) {
1388 if (! allow_name && allow_if_found)
1389 sgj_pr_hr(jsp, "%s%s:\n", (prefix ? prefix : ""), np);
1390 if (op->do_raw)
1391 dStrRaw(rp, len);
1392 else {
1393 if (vb || long_notquiet)
1394 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
1395 "%s]\n", pqual, pdt_str);
1396 if (as_json) {
1397 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1398 jap = sgj_named_subarray_r(jsp, jo2p,
1399 "mode_page_policy_descriptor_list");
1400 }
1401 decode_mode_policy_vpd(rp, len, op, jap);
1402 }
1403 return 0;
1404 }
1405 break;
1406 case VPD_SCSI_PORTS: /* 0x88 ["sp"] */
1407 np = "SCSI Ports VPD page";
1408 if (allow_name)
1409 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1410 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1411 if (0 == res) {
1412 if (! allow_name && allow_if_found)
1413 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1414 if (op->do_raw)
1415 dStrRaw(rp, len);
1416 else {
1417 if (vb || long_notquiet)
1418 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
1419 "%s]\n", pqual, pdt_str);
1420 if (as_json) {
1421 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1422 jap = sgj_named_subarray_r(jsp, jo2p,
1423 "scsi_ports_descriptor_list");
1424 }
1425 decode_scsi_ports_vpd_4vpd(rp, len, op, jap);
1426 }
1427 return 0;
1428 }
1429 break;
1430 case VPD_ATA_INFO: /* 0x89 ['ai"] */
1431 np = "ATA information VPD page";
1432 if (allow_name)
1433 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1434 alloc_len = op->maxlen ? op->maxlen : VPD_ATA_INFO_LEN;
1435 res = vpd_fetch_page(sg_fd, rp, pn, alloc_len, qt, vb, &len);
1436 if (0 == res) {
1437 if (! allow_name && allow_if_found)
1438 sgj_pr_hr(jsp, "%s%s:\n", (prefix ? prefix : ""), np);
1439 if ((2 == op->do_raw) || (3 == op->do_hex)) { /* for hdparm */
1440 if (len < (60 + 512))
1441 pr2serr("ATA_INFO VPD page len (%d) less than expected "
1442 "572\n", len);
1443 else
1444 dWordHex((const unsigned short *)(rp + 60), 256, -2,
1445 sg_is_big_endian());
1446 }
1447 else if (op->do_raw)
1448 dStrRaw(rp, len);
1449 else {
1450 if (vb || long_notquiet)
1451 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
1452 "%s]\n", pqual, pdt_str);
1453 if (as_json)
1454 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1455 decode_ata_info_vpd(rp, len, op, jo2p);
1456 }
1457 return 0;
1458 }
1459 break;
1460 case VPD_POWER_CONDITION: /* 0x8a ["pc"] */
1461 np = "Power condition VPD page:";
1462 if (allow_name)
1463 sgj_pr_hr(jsp, "%s%s\n", pre, np);
1464 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1465 if (0 == res) {
1466 if (! allow_name && allow_if_found)
1467 sgj_pr_hr(jsp, "%s%s\n", pre, np);
1468 if (op->do_raw)
1469 dStrRaw(rp, len);
1470 else {
1471 if (vb || long_notquiet)
1472 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
1473 "%s]\n", pqual, pdt_str);
1474 if (as_json)
1475 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1476 decode_power_condition(rp, len, op, jo2p);
1477 }
1478 return 0;
1479 }
1480 break;
1481 case VPD_DEVICE_CONSTITUENTS: /* 0x8b ["dc"] */
1482 np = "Device constituents VPD page";
1483 if (allow_name)
1484 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1485 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1486 if (0 == res) {
1487 if (! allow_name && allow_if_found)
1488 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1489 if (op->do_raw)
1490 dStrRaw(rp, len);
1491 else {
1492 if (vb || long_notquiet)
1493 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
1494 "%s]\n", pqual, pdt_str);
1495 if (as_json) {
1496 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1497 jap = sgj_named_subarray_r(jsp, jo2p,
1498 "constituent_descriptor_list");
1499 }
1500 decode_dev_constit_vpd(rp, len, op, jap, recurse_vpd_decode);
1501 }
1502 return 0;
1503 }
1504 break;
1505 case VPD_CFA_PROFILE_INFO: /* 0x8c ["cfa"] */
1506 np = "CFA profile information VPD page";
1507 if (allow_name)
1508 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1509 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1510 if (0 == res) {
1511 if (! allow_name && allow_if_found)
1512 sgj_pr_hr(jsp, "%s%s\n", pre, np);
1513 if (op->do_raw)
1514 dStrRaw(rp, len);
1515 else {
1516 if (vb || long_notquiet)
1517 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
1518 "%s]\n", pqual, pdt_str);
1519 if (as_json) {
1520 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1521 jap = sgj_named_subarray_r(jsp, jo2p,
1522 "cfa_profile_descriptor_list");
1523 }
1524 decode_cga_profile_vpd(rp, len, op, jap);
1525 }
1526 return 0;
1527 }
1528 break;
1529 case VPD_POWER_CONSUMPTION: /* 0x8d ["psm"] */
1530 np = "Power consumption VPD page";
1531 if (allow_name)
1532 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1533 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1534 if (0 == res) {
1535 if (! allow_name && allow_if_found)
1536 sgj_pr_hr(jsp, "%s%s\n", pre, np);
1537 if (op->do_raw)
1538 dStrRaw(rp, len);
1539 else {
1540 if (vb || long_notquiet)
1541 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
1542 "%s]\n", pqual, pdt_str);
1543 if (as_json) {
1544 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1545 jap = sgj_named_subarray_r(jsp, jo2p,
1546 "power_consumption_descriptor_list");
1547 }
1548 decode_power_consumption(rp, len, op, jap);
1549 }
1550 return 0;
1551 }
1552 break;
1553 case VPD_3PARTY_COPY: /* 0x8f */
1554 np = "Third party copy VPD page"; /* ["tpc"] */
1555 if (allow_name)
1556 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1557 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1558 if (0 == res) {
1559 if (! allow_name && allow_if_found)
1560 sgj_pr_hr(jsp, "%s%s\n", pre, np);
1561 if (op->do_raw)
1562 dStrRaw(rp, len);
1563 else {
1564 if (vb || long_notquiet)
1565 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
1566 "%s]\n", pqual, pdt_str);
1567 if (as_json) {
1568 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1569 jap = sgj_named_subarray_r(jsp, jo2p,
1570 "third_party_copy_descriptors");
1571 }
1572 decode_3party_copy_vpd(rp, len, op, jap);
1573 }
1574 return 0;
1575 }
1576 break;
1577 case VPD_PROTO_LU: /* 0x90 ["pslu"] */
1578 np = "Protocol-specific logical unit information VPD page";
1579 if (allow_name)
1580 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1581 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1582 if (0 == res) {
1583 if (! allow_name && allow_if_found)
1584 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1585 if (op->do_raw)
1586 dStrRaw(rp, len);
1587 else {
1588 if (vb || long_notquiet)
1589 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
1590 "%s]\n", pqual, pdt_str);
1591 if (as_json) {
1592 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1593 jap = sgj_named_subarray_r(jsp, jo2p,
1594 "logical_unit_information_descriptor_list");
1595 }
1596 decode_proto_lu_vpd(rp, len, op, jap);
1597 }
1598 return 0;
1599 }
1600 break;
1601 case VPD_PROTO_PORT: /* 0x91 ["pspo"] */
1602 np = "Protocol-specific port VPD page";
1603 if (allow_name)
1604 sgj_pr_hr(jsp, "%s%s\n", pre, np);
1605 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1606 if (0 == res) {
1607 if (! allow_name && allow_if_found)
1608 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1609 if (op->do_raw)
1610 dStrRaw(rp, len);
1611 else {
1612 if (vb || long_notquiet)
1613 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
1614 "%s]\n", pqual, pdt_str);
1615 if (as_json) {
1616 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1617 jap = sgj_named_subarray_r(jsp, jo2p,
1618 "port_information_descriptor_list");
1619 }
1620 decode_proto_port_vpd(rp, len, op, jap);
1621 }
1622 return 0;
1623 }
1624 break;
1625 case VPD_SCSI_FEATURE_SETS: /* 0x92 ["sfs"] */
1626 np = "SCSI Feature sets VPD page";
1627 if (allow_name)
1628 sgj_pr_hr(jsp, "%s%s:\n", pre, np);
1629 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1630 if (0 == res) {
1631 if (! allow_name && allow_if_found)
1632 sgj_pr_hr(jsp, "%s%s\n", pre, np);
1633 if (op->do_raw)
1634 dStrRaw(rp, len);
1635 else {
1636 if (vb || long_notquiet)
1637 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
1638 "%s]\n", pqual, pdt_str);
1639 if (as_json) {
1640 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1641 jap = sgj_named_subarray_r(jsp, jo2p,
1642 "feature_set_code_list");
1643 }
1644 decode_feature_sets_vpd(rp, len, op, jap);
1645 }
1646 return 0;
1647 }
1648 break;
1649 case 0xb0: /* depends on pdt */
1650 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1651 if (0 == res) {
1652 bool bl = false;
1653 bool sad = false;
1654 bool oi = false;
1655
1656 switch (pdt) {
1657 case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
1658 np = "Block limits VPD page";
1659 ep = "(SBC)";
1660 bl = true;
1661 break;
1662 case PDT_TAPE: case PDT_MCHANGER:
1663 np = "Sequential-access device capabilities VPD page";
1664 ep = "(SSC)";
1665 sad = true;
1666 break;
1667 case PDT_OSD:
1668 np = "OSD information VPD page";
1669 ep = "(OSD)";
1670 oi = true;
1671 break;
1672 default:
1673 np = NULL;
1674 break;
1675 }
1676 if (NULL == np)
1677 sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
1678 else if (allow_name || allow_if_found)
1679 sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : "");
1680 if (op->do_raw)
1681 dStrRaw(rp, len);
1682 else {
1683 if (vb || long_notquiet)
1684 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
1685 "%s]\n", pqual, pdt_str);
1686 if (as_json)
1687 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1688 if (bl)
1689 decode_block_limits_vpd(rp, len, op, jo2p);
1690 else if (sad) {
1691 decode_b0_vpd(rp, len, op, jop);
1692 } else if (oi) {
1693 decode_b0_vpd(rp, len, op, jop);
1694 } else {
1695
1696 }
1697 }
1698 return 0;
1699 } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
1700 exam_not_given)
1701 sgj_pr_hr(jsp, "%sVPD page=0xb0\n", pre);
1702 break;
1703 case 0xb1: /* depends on pdt */
1704 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1705 if (0 == res) {
1706 bool bdc = false;
1707 static const char * masn =
1708 "Manufactured-assigned serial number VPD page";
1709
1710 switch (pdt) {
1711 case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
1712 np = "Block device characteristics VPD page";
1713 ep = "(SBC)";
1714 bdc = true;
1715 break;
1716 case PDT_TAPE: case PDT_MCHANGER:
1717 np = masn;
1718 ep = "(SSC)";
1719 break;
1720 case PDT_OSD:
1721 np = "Security token VPD page";
1722 ep = "(OSD)";
1723 break;
1724 case PDT_ADC:
1725 np = masn;
1726 ep = "(ADC)";
1727 break;
1728 default:
1729 np = NULL;
1730 break;
1731 }
1732 if (NULL == np)
1733 sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
1734 else if (allow_name || allow_if_found)
1735 sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : "");
1736 if (op->do_raw)
1737 dStrRaw(rp, len);
1738 else {
1739 if (vb || long_notquiet)
1740 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
1741 "%s]\n", pqual, pdt_str);
1742 if (as_json)
1743 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1744 if (bdc)
1745 decode_block_dev_ch_vpd(rp, len, op, jo2p);
1746 else
1747 decode_b1_vpd(rp, len, op, jo2p);
1748 }
1749 return 0;
1750 } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
1751 exam_not_given)
1752 sgj_pr_hr(jsp, "%sVPD page=0xb1\n", pre);
1753 break;
1754 case 0xb2: /* VPD page depends on pdt */
1755 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1756 if (0 == res) {
1757 bool lbpv = false;
1758 bool tas = false;
1759
1760 switch (pdt) {
1761 case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
1762 np = "Logical block provisioning VPD page";
1763 ep = "(SBC)";
1764 lbpv = true;
1765 break;
1766 case PDT_TAPE: case PDT_MCHANGER:
1767 np = "TapeAlert supported flags VPD page";
1768 ep = "(SSC)";
1769 tas = true;
1770 break;
1771 default:
1772 np = NULL;
1773 break;
1774 }
1775 if (NULL == np)
1776 sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
1777 else if (allow_name || allow_if_found)
1778 sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : "");
1779 if (op->do_raw)
1780 dStrRaw(rp, len);
1781 else {
1782 if (vb || long_notquiet)
1783 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
1784 "%s]\n", pqual, pdt_str);
1785 if (as_json)
1786 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1787 if (lbpv)
1788 decode_block_lb_prov_vpd(rp, len, op, jo2p);
1789 else if (tas)
1790 decode_tapealert_supported_vpd(rp, len, op, jo2p);
1791 else
1792 decode_b2_vpd(rp, len, pdt, op);
1793 }
1794 return 0;
1795 } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
1796 exam_not_given)
1797 sgj_pr_hr(jsp, "%sVPD page=0xb2\n", pre);
1798 break;
1799 case 0xb3: /* VPD page depends on pdt */
1800 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1801 if (0 == res) {
1802 bool ref = false;
1803
1804 pdt = rp[0] & PDT_MASK;
1805 switch (pdt) {
1806 case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
1807 np = "Referrals VPD page";
1808 ep = "(SBC)";
1809 ref = true;
1810 break;
1811 case PDT_TAPE: case PDT_MCHANGER:
1812 np = "Automation device serial number VPD page";
1813 ep = "(SSC)";
1814 break;
1815 default:
1816 np = NULL;
1817 break;
1818 }
1819 if (NULL == np)
1820 sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
1821 else if (allow_name || allow_if_found)
1822 sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : "");
1823 if (op->do_raw)
1824 dStrRaw(rp, len);
1825 else {
1826 if (vb || long_notquiet)
1827 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
1828 "%s]\n", pqual, pdt_str);
1829 if (as_json)
1830 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1831 if (ref)
1832 decode_referrals_vpd(rp, len, op, jo2p);
1833 else
1834 decode_b3_vpd(rp, len, op, jo2p);
1835 }
1836 return 0;
1837 } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
1838 exam_not_given)
1839 sgj_pr_hr(jsp, "%sVPD page=0xb3\n", pre);
1840 break;
1841 case 0xb4: /* VPD page depends on pdt */
1842 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1843 if (0 == res) {
1844 bool sbl = false;
1845
1846 pdt = rp[0] & PDT_MASK;
1847 switch (pdt) {
1848 case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
1849 np = "Supported block lengths and protection types VPD page";
1850 ep = "(SBC)";
1851 sbl = true;
1852 break;
1853 case PDT_TAPE: case PDT_MCHANGER:
1854 np = "Data transfer device element address";
1855 ep = "(SSC)";
1856 break;
1857 default:
1858 np = NULL;
1859 break;
1860 }
1861 if (NULL == np)
1862 sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
1863 else if (allow_name || allow_if_found)
1864 sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : "");
1865 if (op->do_raw)
1866 dStrRaw(rp, len);
1867 else {
1868 if (vb || long_notquiet)
1869 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
1870 "%s]\n", pqual, pdt_str);
1871 if (as_json)
1872 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1873 if (sbl) {
1874 if (as_json)
1875 jap = sgj_named_subarray_r(jsp, jo2p, "logical_block_"
1876 "length_and_protection_types_descriptor_list");
1877 decode_sup_block_lens_vpd(rp, len, op, jap);
1878 } else
1879 decode_b4_vpd(rp, len, op, jo2p);
1880 }
1881 return 0;
1882 } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
1883 exam_not_given)
1884 sgj_pr_hr(jsp, "%sVPD page=0xb4\n", pre);
1885 break;
1886 case 0xb5: /* VPD page depends on pdt */
1887 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1888 if (0 == res) {
1889 bool bdce = false;
1890 bool lbp = false;
1891
1892 pdt = rp[0] & PDT_MASK;
1893 switch (pdt) {
1894 case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
1895 np = "Block device characteristics extension VPD page";
1896 ep = "(SBC)";
1897 bdce = true;
1898 break;
1899 case PDT_TAPE: case PDT_MCHANGER:
1900 np = "Logical block protection VPD page";
1901 ep = "(SSC)";
1902 lbp = true;
1903 break;
1904 default:
1905 np = NULL;
1906 break;
1907 }
1908 if (NULL == np)
1909 sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
1910 else if (allow_name || allow_if_found)
1911 sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : "");
1912 if (op->do_raw)
1913 dStrRaw(rp, len);
1914 else {
1915 if (vb || long_notquiet)
1916 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
1917 "%s]\n", pqual, pdt_str);
1918 if (as_json)
1919 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1920 if (bdce)
1921 decode_block_dev_char_ext_vpd(rp, len, op, jo2p);
1922 else if (lbp) {
1923 if (as_json)
1924 jap = sgj_named_subarray_r(jsp, jo2p,
1925 "logical_block_protection_method_descriptor_list");
1926 decode_lb_protection_vpd(rp, len, op, jap);
1927 } else
1928 decode_b5_vpd(rp, len, op->do_hex, pdt);
1929 }
1930 return 0;
1931 } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
1932 exam_not_given)
1933 sgj_pr_hr(jsp, "%sVPD page=0xb5\n", pre);
1934 break;
1935 case VPD_ZBC_DEV_CHARS: /* 0xb6 for both pdt=0 and pdt=0x14 */
1936 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1937 if (0 == res) {
1938 bool zbdch = false;
1939
1940 pdt = rp[0] & PDT_MASK;
1941 switch (pdt) {
1942 case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
1943 np = "Zoned block device characteristics VPD page";
1944 ep = "(SBC, ZBC)";
1945 zbdch = true;
1946 break;
1947 default:
1948 np = NULL;
1949 break;
1950 }
1951 if (NULL == np)
1952 sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
1953 else if (allow_name || allow_if_found)
1954 sgj_pr_hr(jsp, "%s%s %s\n", pre, np, ep ? ep : "");
1955 if (op->do_raw)
1956 dStrRaw(rp, len);
1957 else {
1958 if (vb || long_notquiet)
1959 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
1960 "%s]\n", pqual, pdt_str);
1961 if (as_json)
1962 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
1963 if (zbdch)
1964 decode_zbdch_vpd(rp, len, op, jo2p);
1965 else
1966 return SG_LIB_CAT_OTHER;
1967 }
1968 return 0;
1969 } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
1970 exam_not_given)
1971 sgj_pr_hr(jsp, "%sVPD page=0xb6\n", pre);
1972 break;
1973 case 0xb7:
1974 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
1975 if (0 == res) {
1976 bool ble = false;
1977
1978 pdt = rp[0] & PDT_MASK;
1979 switch (pdt) {
1980 case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
1981 np = "Block limits extension VPD page";
1982 ep = "(SBC)";
1983 ble = true;
1984 break;
1985 default:
1986 np = NULL;
1987 break;
1988 }
1989 if (NULL == np)
1990 sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
1991 else if (allow_name || allow_if_found)
1992 sgj_pr_hr(jsp, "%s%s %s:\n", pre, np, ep ? ep : "");
1993 if (op->do_raw)
1994 dStrRaw(rp, len);
1995 else {
1996 if (vb || long_notquiet)
1997 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
1998 "%s]\n", pqual, pdt_str);
1999 if (as_json)
2000 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
2001 if (ble)
2002 decode_block_limits_ext_vpd(rp, len, op, jo2p);
2003 else
2004 return SG_LIB_CAT_OTHER;
2005 }
2006 return 0;
2007 } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
2008 exam_not_given)
2009 sgj_pr_hr(jsp, "%sVPD page=0xb7\n", pre);
2010 break;
2011 case 0xb8: /* VPD_FORMAT_PRESETS */
2012 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
2013 if (0 == res) {
2014 bool fp = false;
2015
2016 pdt = rp[0] & PDT_MASK;
2017 switch (pdt) {
2018 case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
2019 np = "Format presets VPD page";
2020 ep = "(SBC)";
2021 fp = true;
2022 break;
2023 default:
2024 np = NULL;
2025 break;
2026 }
2027 if (NULL == np)
2028 sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
2029 else if (allow_name || allow_if_found)
2030 sgj_pr_hr(jsp, "%s%s %s:\n", pre, np, ep ? ep : "");
2031 if (op->do_raw)
2032 dStrRaw(rp, len);
2033 else {
2034 if (vb || long_notquiet)
2035 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
2036 "%s]\n", pqual, pdt_str);
2037 if (as_json) {
2038 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
2039 jap = sgj_named_subarray_r(jsp, jo2p, "format_preset_"
2040 "descriptor_list");
2041 }
2042 if (fp)
2043 decode_format_presets_vpd(rp, len, op, jap);
2044 else
2045 return SG_LIB_CAT_OTHER;
2046 }
2047 return 0;
2048 } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
2049 exam_not_given)
2050 sgj_pr_hr(jsp, "%sVPD page=0xb8\n", pre);
2051 break;
2052 case 0xb9: /* VPD_CON_POS_RANGE */
2053 res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
2054 if (0 == res) {
2055 bool cpr = false; /* ["cpr"] */
2056
2057 pdt = rp[0] & PDT_MASK;
2058 switch (pdt) {
2059 case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
2060 np = "Concurrent positioning ranges VPD page";
2061 ep = "(SBC)";
2062 cpr = true;
2063 break;
2064 default:
2065 np = NULL;
2066 break;
2067 }
2068 if (NULL == np)
2069 sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
2070 else if (allow_name || allow_if_found)
2071 sgj_pr_hr(jsp, "%s%s %s:\n", pre, np, ep ? ep : "");
2072 if (op->do_raw)
2073 dStrRaw(rp, len);
2074 else {
2075 if (vb || long_notquiet)
2076 sgj_pr_hr(jsp, " [PQual=%d Peripheral device type: "
2077 "%s]\n", pqual, pdt_str);
2078 if (as_json) {
2079 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
2080 jap = sgj_named_subarray_r(jsp, jo2p, "lba_range_"
2081 "descriptor_list");
2082 }
2083 if (cpr)
2084 decode_con_pos_range_vpd(rp, len, op, jap);
2085 else
2086 return SG_LIB_CAT_OTHER;
2087 }
2088 return 0;
2089 } else if ((! op->do_raw) && (! op->do_quiet) && (op->do_hex < 3) &&
2090 exam_not_given)
2091 sgj_pr_hr(jsp, "%sVPD page=0xb8\n", pre);
2092 break;
2093 default:
2094 return SG_LIB_CAT_OTHER;
2095 }
2096 return res;
2097 }
2098
2099 static int
svpd_decode_all(int sg_fd,struct opts_t * op,sgj_opaque_p jop)2100 svpd_decode_all(int sg_fd, struct opts_t * op, sgj_opaque_p jop)
2101 {
2102 int k, res, rlen, n, pn;
2103 int max_pn = 255;
2104 int any_err = 0;
2105 sgj_state * jsp = &op->json_st;
2106 uint8_t vpd0_buff[512];
2107 uint8_t * rp = vpd0_buff;
2108
2109 if (op->vpd_pn > 0)
2110 max_pn = op->vpd_pn;
2111 if (sg_fd >= 0) { /* have valid open file descriptor (handle) */
2112 res = vpd_fetch_page(sg_fd, rp, VPD_SUPPORTED_VPDS, op->maxlen,
2113 op->do_quiet, op->verbose, &rlen);
2114 if (res) {
2115 if (! op->do_quiet) {
2116 if (SG_LIB_CAT_ABORTED_COMMAND == res)
2117 pr2serr("%s: VPD page 0, aborted command\n", __func__);
2118 else if (res) {
2119 char b[80];
2120
2121 sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
2122 pr2serr("%s: fetching VPD page 0 failed: %s\n", __func__,
2123 b);
2124 }
2125 }
2126 return res;
2127 }
2128 n = sg_get_unaligned_be16(rp + 2);
2129 if (n > (rlen - 4)) {
2130 if (op->verbose)
2131 pr2serr("%s: rlen=%d > page0 size=%d\n", __func__, rlen,
2132 n + 4);
2133 n = (rlen - 4);
2134 }
2135 for (k = 0; k < n; ++k) {
2136 pn = rp[4 + k];
2137 if (pn > max_pn)
2138 continue;
2139 op->vpd_pn = pn;
2140 if (k > 0)
2141 sgj_pr_hr(jsp, "\n");
2142 if (op->do_long) {
2143 if (jsp->pr_as_json)
2144 sgj_pr_hr(jsp, "[0x%x]:\n", pn);
2145 else
2146 printf("[0x%x] ", pn);
2147 }
2148
2149 res = svpd_decode_t10(sg_fd, op, jop, 0, 0, NULL);
2150 if (SG_LIB_CAT_OTHER == res) {
2151 res = svpd_decode_vendor(sg_fd, op, jop, 0);
2152 if (SG_LIB_CAT_OTHER == res)
2153 res = svpd_unable_to_decode(sg_fd, op, 0, 0);
2154 }
2155 if (! op->do_quiet) {
2156 if (SG_LIB_CAT_ABORTED_COMMAND == res)
2157 pr2serr("fetching VPD page failed, aborted command\n");
2158 else if (res) {
2159 char b[80];
2160
2161 sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
2162 pr2serr("fetching VPD page failed: %s\n", b);
2163 }
2164 }
2165 if (res)
2166 any_err = res;
2167 }
2168 res = any_err;
2169 } else { /* input is coming from --inhex=FN */
2170 int bump, off;
2171 int in_len = op->maxlen;
2172 int prev_pn = -1;
2173
2174 res = 0;
2175 if (op->page_given && (VPD_NOPE_WANT_STD_INQ == op->vpd_pn))
2176 return svpd_decode_t10(-1, op, jop, 0, 0, NULL);
2177
2178 for (k = 0, off = 0; off < in_len; ++k, off += bump) {
2179 rp = rsp_buff + off;
2180 pn = rp[1];
2181 bump = sg_get_unaligned_be16(rp + 2) + 4;
2182 if ((off + bump) > in_len) {
2183 pr2serr("%s: page 0x%x size (%d) exceeds buffer\n", __func__,
2184 pn, bump);
2185 bump = in_len - off;
2186 }
2187 if (op->page_given && (pn != op->vpd_pn))
2188 continue;
2189 if (pn <= prev_pn) {
2190 pr2serr("%s: prev_pn=0x%x, this pn=0x%x, not ascending so "
2191 "exit\n", __func__, prev_pn, pn);
2192 break;
2193 }
2194 prev_pn = pn;
2195 op->vpd_pn = pn;
2196 if (pn > max_pn) {
2197 if (op->verbose > 2)
2198 pr2serr("%s: skipping as this pn=0x%x exceeds "
2199 "max_pn=0x%x\n", __func__, pn, max_pn);
2200 continue;
2201 }
2202 if (op->do_long) {
2203 if (jsp->pr_as_json)
2204 sgj_pr_hr(jsp, "[0x%x]:\n", pn);
2205 else
2206 printf("[0x%x] ", pn);
2207 }
2208
2209 res = svpd_decode_t10(-1, op, jop, 0, off, NULL);
2210 if (SG_LIB_CAT_OTHER == res) {
2211 res = svpd_decode_vendor(-1, op, jop, off);
2212 if (SG_LIB_CAT_OTHER == res)
2213 res = svpd_unable_to_decode(-1, op, 0, off);
2214 }
2215 }
2216 }
2217 return res;
2218 }
2219
2220 static int
svpd_examine_all(int sg_fd,struct opts_t * op,sgj_opaque_p jop)2221 svpd_examine_all(int sg_fd, struct opts_t * op, sgj_opaque_p jop)
2222 {
2223 bool first = true;
2224 bool got_one = false;
2225 int k, res, start;
2226 int max_pn;
2227 int any_err = 0;
2228 sgj_state * jsp = &op->json_st;
2229 char b[80];
2230
2231 max_pn = (op->page_given ? op->vpd_pn : 0xff);
2232 switch (op->examine) {
2233 case 1:
2234 start = 0x80;
2235 break;
2236 case 2:
2237 start = 0x0;
2238 break;
2239 default:
2240 start = 0xc0;
2241 break;
2242 }
2243 if (start > max_pn) { /* swap them around */
2244 k = start;
2245 start = max_pn;
2246 max_pn = k;
2247 }
2248 for (k = start; k <= max_pn; ++k) {
2249 op->vpd_pn = k;
2250 if (first)
2251 first = false;
2252 else if (got_one) {
2253 sgj_pr_hr(jsp, "\n");
2254 got_one = false;
2255 }
2256 if (op->do_long)
2257 snprintf(b, sizeof(b), "[0x%x] ", k);
2258 else
2259 b[0] = '\0';
2260 res = svpd_decode_t10(sg_fd, op, jop, 0, 0, b);
2261 if (SG_LIB_CAT_OTHER == res) {
2262 res = svpd_decode_vendor(sg_fd, op, jop, 0);
2263 if (SG_LIB_CAT_OTHER == res)
2264 res = svpd_unable_to_decode(sg_fd, op, 0, 0);
2265 }
2266 if (! op->do_quiet) {
2267 if (SG_LIB_CAT_ABORTED_COMMAND == res)
2268 pr2serr("fetching VPD page failed, aborted command\n");
2269 else if (res && (SG_LIB_CAT_ILLEGAL_REQ != res)) {
2270 /* SG_LIB_CAT_ILLEGAL_REQ expected as well examine all */
2271 sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
2272 pr2serr("fetching VPD page failed: %s\n", b);
2273 }
2274 }
2275 if (res && (SG_LIB_CAT_ILLEGAL_REQ != res))
2276 any_err = res;
2277 if (0 == res)
2278 got_one = true;
2279 }
2280 return any_err;
2281 }
2282
2283
2284 int
main(int argc,char * argv[])2285 main(int argc, char * argv[])
2286 {
2287 bool as_json;
2288 int c, res, matches;
2289 int sg_fd = -1;
2290 int inhex_len = 0;
2291 int inraw_len = 0;
2292 int ret = 0;
2293 int subvalue = 0;
2294 const char * cp;
2295 sgj_state * jsp;
2296 sgj_opaque_p jop = NULL;
2297 const struct svpd_values_name_t * vnp;
2298 struct opts_t opts SG_C_CPP_ZERO_INIT;
2299 struct opts_t * op = &opts;
2300
2301 op->invoker = SG_VPD_INV_SG_VPD;
2302 dup_sanity_chk((int)sizeof(opts), (int)sizeof(*vnp));
2303 op->vend_prod_num = -1;
2304 while (1) {
2305 int option_index = 0;
2306
2307 c = getopt_long(argc, argv, "aeEfhHiI:j::lm:M:p:qQ:rvV", long_options,
2308 &option_index);
2309 if (c == -1)
2310 break;
2311
2312 switch (c) {
2313 case 'a':
2314 op->do_all = true;
2315 break;
2316 case 'e':
2317 op->do_enum = true;
2318 break;
2319 case 'E':
2320 ++op->examine;
2321 op->examine_given = true;
2322 break;
2323 case 'f':
2324 op->do_force = true;
2325 break;
2326 case 'h':
2327 case '?':
2328 usage();
2329 return 0;
2330 case 'H':
2331 ++op->do_hex;
2332 break;
2333 case 'i':
2334 ++op->do_ident;
2335 break;
2336 case 'I':
2337 if (op->inhex_fn) {
2338 pr2serr("only one '--inhex=' option permitted\n");
2339 usage();
2340 return SG_LIB_SYNTAX_ERROR;
2341 } else
2342 op->inhex_fn = optarg;
2343 break;
2344 case 'j':
2345 if (! sgj_init_state(&op->json_st, optarg)) {
2346 int bad_char = op->json_st.first_bad_char;
2347 char e[1500];
2348
2349 if (bad_char) {
2350 pr2serr("bad argument to --json= option, unrecognized "
2351 "character '%c'\n\n", bad_char);
2352 }
2353 sg_json_usage(0, e, sizeof(e));
2354 pr2serr("%s", e);
2355 return SG_LIB_SYNTAX_ERROR;
2356 }
2357 break;
2358 case 'l':
2359 op->do_long = true;
2360 break;
2361 case 'm':
2362 op->maxlen = sg_get_num(optarg);
2363 if ((op->maxlen < 0) || (op->maxlen > MX_ALLOC_LEN)) {
2364 pr2serr("argument to '--maxlen' should be %d or less\n",
2365 MX_ALLOC_LEN);
2366 return SG_LIB_SYNTAX_ERROR;
2367 }
2368 if ((op->maxlen > 0) && (op->maxlen < MIN_MAXLEN)) {
2369 pr2serr("Warning: overriding '--maxlen' < %d, using "
2370 "default\n", MIN_MAXLEN);
2371 op->maxlen = 0;
2372 }
2373 break;
2374 case 'M':
2375 if (op->vend_prod) {
2376 pr2serr("only one '--vendor=' option permitted\n");
2377 usage();
2378 return SG_LIB_SYNTAX_ERROR;
2379 } else
2380 op->vend_prod = optarg;
2381 break;
2382 case 'p':
2383 if (op->page_str) {
2384 pr2serr("only one '--page=' option permitted\n");
2385 usage();
2386 return SG_LIB_SYNTAX_ERROR;
2387 } else
2388 op->page_str = optarg;
2389 op->page_given = true;
2390 break;
2391 case 'q':
2392 op->do_quiet = true;
2393 break;
2394 case 'Q':
2395 op->sinq_inraw_fn = optarg;
2396 break;
2397 case 'r':
2398 ++op->do_raw;
2399 break;
2400 case 'v':
2401 op->verbose_given = true;
2402 ++op->verbose;
2403 break;
2404 case 'V':
2405 op->version_given = true;
2406 break;
2407 default:
2408 pr2serr("unrecognised option code 0x%x ??\n", c);
2409 usage();
2410 return SG_LIB_SYNTAX_ERROR;
2411 }
2412 }
2413 if (optind < argc) {
2414 if (NULL == op->device_name) {
2415 op->device_name = argv[optind];
2416 ++optind;
2417 }
2418 if (optind < argc) {
2419 for (; optind < argc; ++optind)
2420 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
2421 usage();
2422 return SG_LIB_SYNTAX_ERROR;
2423 }
2424 }
2425
2426 #ifdef DEBUG
2427 pr2serr("In DEBUG mode, ");
2428 if (op->verbose_given && op->version_given) {
2429 pr2serr("but override: '-vV' given, zero verbose and continue\n");
2430 op->verbose_given = false;
2431 op->version_given = false;
2432 op->verbose = 0;
2433 } else if (! op->verbose_given) {
2434 pr2serr("set '-vv'\n");
2435 op->verbose = 2;
2436 } else
2437 pr2serr("keep verbose=%d\n", op->verbose);
2438 #else
2439 if (op->verbose_given && op->version_given)
2440 pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
2441 #endif
2442 if (op->version_given) {
2443 pr2serr("version: %s\n", version_str);
2444 return 0;
2445 }
2446
2447 jsp = &op->json_st;
2448 if (op->do_enum) {
2449 if (op->device_name)
2450 pr2serr("Device name %s ignored when --enumerate given\n",
2451 op->device_name);
2452 if (op->vend_prod) {
2453 if (isdigit((uint8_t)op->vend_prod[0])) {
2454 op->vend_prod_num = sg_get_num_nomult(op->vend_prod);
2455 if ((op->vend_prod_num < 0) || (op->vend_prod_num > 10)) {
2456 pr2serr("Bad vendor/product number after '--vendor=' "
2457 "option\n");
2458 return SG_LIB_SYNTAX_ERROR;
2459 }
2460 } else {
2461 op->vend_prod_num = svpd_find_vp_num_by_acron(op->vend_prod);
2462 if (op->vend_prod_num < 0) {
2463 pr2serr("Bad vendor/product acronym after '--vendor=' "
2464 "option\n");
2465 return SG_LIB_SYNTAX_ERROR;
2466 }
2467 }
2468 svpd_enumerate_vendor(op->vend_prod_num);
2469 return 0;
2470 }
2471 if (op->page_str) {
2472 if ((0 == strcmp("-1", op->page_str)) ||
2473 (0 == strcmp("-2", op->page_str)))
2474 op->vpd_pn = VPD_NOPE_WANT_STD_INQ;
2475 else if (isdigit((uint8_t)op->page_str[0])) {
2476 op->vpd_pn = sg_get_num_nomult(op->page_str);
2477 if ((op->vpd_pn < 0) || (op->vpd_pn > 255)) {
2478 pr2serr("Bad page code value after '-p' option\n");
2479 return SG_LIB_SYNTAX_ERROR;
2480 }
2481 } else {
2482 pr2serr("with --enumerate only search using VPD page "
2483 "numbers\n");
2484 return SG_LIB_SYNTAX_ERROR;
2485 }
2486 matches = count_standard_vpds(op->vpd_pn);
2487 if (0 == matches)
2488 matches = svpd_count_vendor_vpds(op->vpd_pn,
2489 op->vend_prod_num);
2490 if (0 == matches)
2491 sgj_pr_hr(jsp, "No matches found for VPD page number 0x%x\n",
2492 op->vpd_pn);
2493 } else { /* enumerate standard then vendor VPD pages */
2494 sgj_pr_hr(jsp, "Standard VPD pages:\n");
2495 enumerate_vpds(1, 1);
2496 }
2497 return 0;
2498 }
2499
2500 as_json = jsp->pr_as_json;
2501 if (as_json)
2502 jop = sgj_start_r(MY_NAME, version_str, argc, argv, jsp);
2503
2504 if (op->page_str) {
2505 if ('-' == op->page_str[0])
2506 op->vpd_pn = VPD_NOPE_WANT_STD_INQ;
2507 else if (isalpha((uint8_t)op->page_str[0])) {
2508 vnp = sdp_find_vpd_by_acron(op->page_str);
2509 if (NULL == vnp) {
2510 vnp = svpd_find_vendor_by_acron(op->page_str);
2511 if (NULL == vnp) {
2512 if (0 == strcmp("stdinq", op->page_str)) {
2513 vnp = sdp_find_vpd_by_acron("sinq");
2514 } else {
2515 pr2serr("abbreviation doesn't match a VPD page\n");
2516 sgj_pr_hr(jsp, "Available standard VPD pages:\n");
2517 enumerate_vpds(1, 1);
2518 ret = SG_LIB_SYNTAX_ERROR;
2519 goto fini;
2520 }
2521 }
2522 }
2523 op->vpd_pn = vnp->value;
2524 subvalue = vnp->subvalue;
2525 op->vend_prod_num = subvalue;
2526 } else {
2527 cp = strchr(op->page_str, ',');
2528 if (cp && op->vend_prod) {
2529 pr2serr("the --page=pg,vp and the --vendor=vp forms overlap, "
2530 "choose one or the other\n");
2531 ret = SG_LIB_SYNTAX_ERROR;
2532 goto fini;
2533 }
2534 op->vpd_pn = sg_get_num_nomult(op->page_str);
2535 if ((op->vpd_pn < 0) || (op->vpd_pn > 255)) {
2536 pr2serr("Bad page code value after '-p' option\n");
2537 sgj_pr_hr(jsp, "Available standard VPD pages:\n");
2538 enumerate_vpds(1, 1);
2539 ret = SG_LIB_SYNTAX_ERROR;
2540 goto fini;
2541 }
2542 if (cp) {
2543 if (isdigit((uint8_t)*(cp + 1)))
2544 op->vend_prod_num = sg_get_num_nomult(cp + 1);
2545 else
2546 op->vend_prod_num = svpd_find_vp_num_by_acron(cp + 1);
2547 if ((op->vend_prod_num < 0) || (op->vend_prod_num > 255)) {
2548 pr2serr("Bad vendor/product acronym after comma in '-p' "
2549 "option\n");
2550 if (op->vend_prod_num < 0)
2551 svpd_enumerate_vendor(-1);
2552 ret = SG_LIB_SYNTAX_ERROR;
2553 goto fini;
2554 }
2555 subvalue = op->vend_prod_num;
2556 } else if (op->vend_prod) {
2557 if (isdigit((uint8_t)op->vend_prod[0]))
2558 op->vend_prod_num = sg_get_num_nomult(op->vend_prod);
2559 else
2560 op->vend_prod_num =
2561 svpd_find_vp_num_by_acron(op->vend_prod);
2562 if ((op->vend_prod_num < 0) || (op->vend_prod_num > 255)) {
2563 pr2serr("Bad vendor/product acronym after '--vendor=' "
2564 "option\n");
2565 svpd_enumerate_vendor(-1);
2566 ret = SG_LIB_SYNTAX_ERROR;
2567 goto fini;
2568 }
2569 subvalue = op->vend_prod_num;
2570 }
2571 }
2572 if (op->verbose > 3)
2573 pr2serr("'--page=' matched pn=%d [0x%x], subvalue=%d\n",
2574 op->vpd_pn, op->vpd_pn, subvalue);
2575 } else if (op->vend_prod) {
2576 if (isdigit((uint8_t)op->vend_prod[0]))
2577 op->vend_prod_num = sg_get_num_nomult(op->vend_prod);
2578 else
2579 op->vend_prod_num = svpd_find_vp_num_by_acron(op->vend_prod);
2580 if ((op->vend_prod_num < 0) || (op->vend_prod_num > 255)) {
2581 pr2serr("Bad vendor/product acronym after '--vendor=' "
2582 "option\n");
2583 svpd_enumerate_vendor(-1);
2584 ret = SG_LIB_SYNTAX_ERROR;
2585 goto fini;
2586 }
2587 subvalue = op->vend_prod_num;
2588 }
2589
2590 rsp_buff = sg_memalign(rsp_buff_sz, 0 /* page align */, &free_rsp_buff,
2591 false);
2592 if (NULL == rsp_buff) {
2593 pr2serr("Unable to allocate %d bytes on heap\n", rsp_buff_sz);
2594 ret = sg_convert_errno(ENOMEM);
2595 goto fini;
2596 }
2597 if (op->sinq_inraw_fn) {
2598 if ((ret = sg_f2hex_arr(op->sinq_inraw_fn, true, false, rsp_buff,
2599 &inraw_len, rsp_buff_sz))) {
2600 goto err_out;
2601 }
2602 if (inraw_len < 36) {
2603 pr2serr("Unable to read 36 or more bytes from %s\n",
2604 op->sinq_inraw_fn);
2605 ret = SG_LIB_FILE_ERROR;
2606 goto err_out;
2607 }
2608 memcpy(op->std_inq_a, rsp_buff, 36);
2609 op->std_inq_a_valid = true;
2610 }
2611 if (op->inhex_fn) {
2612 if (op->device_name) {
2613 pr2serr("Cannot have both a DEVICE and --inhex= option\n");
2614 ret = SG_LIB_SYNTAX_ERROR;
2615 goto err_out;
2616 }
2617 if ((ret = sg_f2hex_arr(op->inhex_fn, !!op->do_raw, false, rsp_buff,
2618 &inhex_len, rsp_buff_sz))) {
2619 goto err_out;
2620 }
2621 if (op->verbose > 2)
2622 pr2serr("Read %d [0x%x] bytes of user supplied data\n", inhex_len,
2623 inhex_len);
2624 if (op->verbose > 3)
2625 hex2stderr(rsp_buff, inhex_len, 0);
2626 op->do_raw = 0; /* don't want raw on output with --inhex= */
2627 if ((NULL == op->page_str) && (! op->do_all)) {
2628 /* may be able to deduce VPD page */
2629 if ((0x2 == (0xf & rsp_buff[3])) && (rsp_buff[2] > 2)) {
2630 if (op->verbose)
2631 pr2serr("Guessing from --inhex= this is a standard "
2632 "INQUIRY\n");
2633 } else if (rsp_buff[2] <= 2) {
2634 if (op->verbose)
2635 pr2serr("Guessing from --inhex this is VPD page 0x%x\n",
2636 rsp_buff[1]);
2637 op->vpd_pn = rsp_buff[1];
2638 } else {
2639 if (op->vpd_pn > 0x80) {
2640 op->vpd_pn = rsp_buff[1];
2641 if (op->verbose)
2642 pr2serr("Guessing from --inhex this is VPD page "
2643 "0x%x\n", rsp_buff[1]);
2644 } else {
2645 op->vpd_pn = VPD_NOPE_WANT_STD_INQ;
2646 if (op->verbose)
2647 pr2serr("page number unclear from --inhex, hope "
2648 "it's a standard INQUIRY response\n");
2649 }
2650 }
2651 }
2652 } else if ((NULL == op->device_name) && (! op->std_inq_a_valid)) {
2653 pr2serr("No DEVICE argument given\n\n");
2654 usage();
2655 ret = SG_LIB_SYNTAX_ERROR;
2656 goto err_out;
2657 }
2658
2659 if (op->do_raw && op->do_hex) {
2660 pr2serr("Can't do hex and raw at the same time\n");
2661 usage();
2662 ret = SG_LIB_SYNTAX_ERROR;
2663 goto err_out;
2664 }
2665 if (op->do_ident) {
2666 op->vpd_pn = VPD_DEVICE_ID;
2667 if (op->do_ident > 1) {
2668 if (! op->do_long)
2669 op->do_quiet = true;
2670 subvalue = VPD_DI_SEL_LU;
2671 }
2672 }
2673 if (op->do_raw) {
2674 if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
2675 perror("sg_set_binary_mode");
2676 ret = SG_LIB_FILE_ERROR;
2677 goto err_out;
2678 }
2679 }
2680
2681 if (op->inhex_fn) {
2682 if ((0 == op->maxlen) || (inhex_len < op->maxlen))
2683 op->maxlen = inhex_len;
2684 if (op->do_all || op->page_given)
2685 res = svpd_decode_all(-1, op, jop);
2686 else {
2687 res = svpd_decode_t10(-1, op, jop, subvalue, 0, NULL);
2688 if (SG_LIB_CAT_OTHER == res) {
2689 res = svpd_decode_vendor(-1, op, jop, 0);
2690 if (SG_LIB_CAT_OTHER == res)
2691 res = svpd_unable_to_decode(-1, op, subvalue, 0);
2692 }
2693 }
2694 ret = res;
2695 goto err_out;
2696 } else if (op->std_inq_a_valid && (NULL == op->device_name)) {
2697 /* nothing else to do ... */
2698 /* --sinq_inraw=RFN contents still in rsp_buff */
2699 if (op->do_raw)
2700 dStrRaw(rsp_buff, inraw_len);
2701 else if (op->do_hex) {
2702 if (! op->do_quiet && (op->do_hex < 3))
2703 sgj_pr_hr(jsp, "Standard Inquiry data format:\n");
2704 hex2stdout(rsp_buff, inraw_len, (1 == op->do_hex) ? 0 : -1);
2705 } else
2706 std_inq_decode(rsp_buff, inraw_len, op, jop);
2707 ret = 0;
2708 goto fini;
2709 }
2710
2711 if ((sg_fd = sg_cmds_open_device(op->device_name, true /* ro */,
2712 op->verbose)) < 0) {
2713 if (op->verbose > 0)
2714 pr2serr("error opening file: %s: %s\n", op->device_name,
2715 safe_strerror(-sg_fd));
2716 ret = sg_convert_errno(-sg_fd);
2717 if (ret < 0)
2718 ret = SG_LIB_FILE_ERROR;
2719 goto err_out;
2720 }
2721
2722 if (op->examine_given) {
2723 ret = svpd_examine_all(sg_fd, op, jop);
2724 } else if (op->do_all)
2725 ret = svpd_decode_all(sg_fd, op, jop);
2726 else {
2727 memset(rsp_buff, 0, rsp_buff_sz);
2728
2729 res = svpd_decode_t10(sg_fd, op, jop, subvalue, 0, NULL);
2730 if (SG_LIB_CAT_OTHER == res) {
2731 res = svpd_decode_vendor(sg_fd, op, jop, 0);
2732 if (SG_LIB_CAT_OTHER == res)
2733 res = svpd_unable_to_decode(sg_fd, op, subvalue, 0);
2734 }
2735 if (! op->do_quiet) {
2736 if (SG_LIB_CAT_ABORTED_COMMAND == res)
2737 pr2serr("fetching VPD page failed, aborted command\n");
2738 else if (res) {
2739 char b[80];
2740
2741 sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
2742 pr2serr("fetching VPD page failed: %s\n", b);
2743 }
2744 }
2745 ret = res;
2746 }
2747 err_out:
2748 if (free_rsp_buff)
2749 free(free_rsp_buff);
2750 if ((0 == op->verbose) && (! op->do_quiet)) {
2751 if (! sg_if_can2stderr("sg_vpd failed: ", ret))
2752 pr2serr("Some error occurred, try again with '-v' or '-vv' for "
2753 "more information\n");
2754 }
2755 fini:
2756 res = (sg_fd >= 0) ? sg_cmds_close_device(sg_fd) : 0;
2757
2758 if (res < 0) {
2759 pr2serr("close error: %s\n", safe_strerror(-res));
2760 if (0 == ret)
2761 ret = sg_convert_errno(-res);
2762 }
2763 ret = (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
2764 if (as_json) {
2765 if (0 == op->do_hex)
2766 sgj_js2file(jsp, NULL, ret, stdout);
2767 sgj_finish(jsp);
2768 }
2769 return ret;
2770 }
2771