xref: /aosp_15_r20/external/sg3_utils/src/sg_inq.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
1*44704f69SBart Van Assche /* A utility program originally written for the Linux OS SCSI subsystem.
2*44704f69SBart Van Assche  * Copyright (C) 2000-2022 D. Gilbert
3*44704f69SBart Van Assche  * This program is free software; you can redistribute it and/or modify
4*44704f69SBart Van Assche  * it under the terms of the GNU General Public License as published by
5*44704f69SBart Van Assche  * the Free Software Foundation; either version 2, or (at your option)
6*44704f69SBart Van Assche  * any later version.
7*44704f69SBart Van Assche  *
8*44704f69SBart Van Assche  * SPDX-License-Identifier: GPL-2.0-or-later
9*44704f69SBart Van Assche  *
10*44704f69SBart Van Assche  * This program outputs information provided by a SCSI INQUIRY command.
11*44704f69SBart Van Assche  * It is mainly based on the SCSI SPC-6 document at https://www.t10.org .
12*44704f69SBart Van Assche  *
13*44704f69SBart Van Assche  * Acknowledgment:
14*44704f69SBart Van Assche  *    - Martin Schwenke <martin at meltin dot net> added the raw switch and
15*44704f69SBart Van Assche  *      other improvements [20020814]
16*44704f69SBart Van Assche  *    - Lars Marowsky-Bree <lmb at suse dot de> contributed Unit Path Report
17*44704f69SBart Van Assche  *      VPD page decoding for EMC CLARiiON devices [20041016]
18*44704f69SBart Van Assche  */
19*44704f69SBart Van Assche 
20*44704f69SBart Van Assche #include <unistd.h>
21*44704f69SBart Van Assche #include <fcntl.h>
22*44704f69SBart Van Assche #include <stdio.h>
23*44704f69SBart Van Assche #include <stdlib.h>
24*44704f69SBart Van Assche #include <stdbool.h>
25*44704f69SBart Van Assche #include <string.h>
26*44704f69SBart Van Assche #include <errno.h>
27*44704f69SBart Van Assche #include <ctype.h>
28*44704f69SBart Van Assche #include <getopt.h>
29*44704f69SBart Van Assche #define __STDC_FORMAT_MACROS 1
30*44704f69SBart Van Assche #include <inttypes.h>
31*44704f69SBart Van Assche #include <errno.h>
32*44704f69SBart Van Assche 
33*44704f69SBart Van Assche #ifdef SG_LIB_LINUX
34*44704f69SBart Van Assche #include <sys/ioctl.h>
35*44704f69SBart Van Assche #include <sys/types.h>
36*44704f69SBart Van Assche #include <sys/stat.h>
37*44704f69SBart Van Assche #include <linux/hdreg.h>
38*44704f69SBart Van Assche #endif
39*44704f69SBart Van Assche 
40*44704f69SBart Van Assche #ifdef HAVE_CONFIG_H
41*44704f69SBart Van Assche #include "config.h"
42*44704f69SBart Van Assche #endif
43*44704f69SBart Van Assche 
44*44704f69SBart Van Assche #include "sg_lib.h"
45*44704f69SBart Van Assche #include "sg_lib_data.h"
46*44704f69SBart Van Assche #include "sg_cmds_basic.h"
47*44704f69SBart Van Assche #include "sg_pt.h"
48*44704f69SBart Van Assche #include "sg_unaligned.h"
49*44704f69SBart Van Assche #include "sg_pr2serr.h"
50*44704f69SBart Van Assche #if (HAVE_NVME && (! IGNORE_NVME))
51*44704f69SBart Van Assche #include "sg_pt_nvme.h"
52*44704f69SBart Van Assche #endif
53*44704f69SBart Van Assche 
54*44704f69SBart Van Assche #include "sg_vpd_common.h"  /* for shared VPD page processing with sg_vpd */
55*44704f69SBart Van Assche 
56*44704f69SBart Van Assche static const char * version_str = "2.31 20220915";  /* spc6r06, sbc5r03 */
57*44704f69SBart Van Assche 
58*44704f69SBart Van Assche #define MY_NAME "sg_inq"
59*44704f69SBart Van Assche 
60*44704f69SBart Van Assche /* INQUIRY notes:
61*44704f69SBart Van Assche  * It is recommended that the initial allocation length given to a
62*44704f69SBart Van Assche  * standard INQUIRY is 36 (bytes), especially if this is the first
63*44704f69SBart Van Assche  * SCSI command sent to a logical unit. This is compliant with SCSI-2
64*44704f69SBart Van Assche  * and another major operating system. There are devices out there
65*44704f69SBart Van Assche  * that use one of the SCSI commands sets and lock up if they receive
66*44704f69SBart Van Assche  * an allocation length other than 36. This technique is sometimes
67*44704f69SBart Van Assche  * referred to as a "36 byte INQUIRY".
68*44704f69SBart Van Assche  *
69*44704f69SBart Van Assche  * A "standard" INQUIRY is one that has the EVPD and the CmdDt bits
70*44704f69SBart Van Assche  * clear.
71*44704f69SBart Van Assche  *
72*44704f69SBart Van Assche  * When doing device discovery on a SCSI transport (e.g. bus scanning)
73*44704f69SBart Van Assche  * the first SCSI command sent to a device should be a standard (36
74*44704f69SBart Van Assche  * byte) INQUIRY.
75*44704f69SBart Van Assche  *
76*44704f69SBart Van Assche  * The allocation length field in the INQUIRY command was changed
77*44704f69SBart Van Assche  * from 1 to 2 bytes in SPC-3, revision 9, 17 September 2002.
78*44704f69SBart Van Assche  * Be careful using allocation lengths greater than 252 bytes, especially
79*44704f69SBart Van Assche  * if the lower byte is 0x0 (e.g. a 512 byte allocation length may
80*44704f69SBart Van Assche  * not be a good arbitrary choice (as 512 == 0x200) ).
81*44704f69SBart Van Assche  *
82*44704f69SBart Van Assche  * From SPC-3 revision 16 the CmdDt bit in an INQUIRY is obsolete. There
83*44704f69SBart Van Assche  * is now a REPORT SUPPORTED OPERATION CODES command that yields similar
84*44704f69SBart Van Assche  * information [MAINTENANCE IN, service action = 0xc]; see sg_opcodes.
85*44704f69SBart Van Assche  */
86*44704f69SBart Van Assche 
87*44704f69SBart Van Assche // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TESTING
88*44704f69SBart Van Assche // #undef SG_SCSI_STRINGS
89*44704f69SBart Van Assche // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< TESTING
90*44704f69SBart Van Assche 
91*44704f69SBart Van Assche #define VPD_NOPE_WANT_STD_INQ -2        /* request for standard inquiry */
92*44704f69SBart Van Assche 
93*44704f69SBart Van Assche /* Vendor specific VPD pages (typically >= 0xc0) */
94*44704f69SBart Van Assche #define VPD_UPR_EMC 0xc0
95*44704f69SBart Van Assche #define VPD_RDAC_VERS 0xc2
96*44704f69SBart Van Assche #define VPD_RDAC_VAC 0xc9
97*44704f69SBart Van Assche 
98*44704f69SBart Van Assche /* values for selection one or more associations (2**vpd_assoc),
99*44704f69SBart Van Assche    except _AS_IS */
100*44704f69SBart Van Assche #define VPD_DI_SEL_LU 1
101*44704f69SBart Van Assche #define VPD_DI_SEL_TPORT 2
102*44704f69SBart Van Assche #define VPD_DI_SEL_TARGET 4
103*44704f69SBart Van Assche #define VPD_DI_SEL_AS_IS 32
104*44704f69SBart Van Assche 
105*44704f69SBart Van Assche #define DEF_ALLOC_LEN 252       /* highest 1 byte value that is modulo 4 */
106*44704f69SBart Van Assche #define SAFE_STD_INQ_RESP_LEN 36
107*44704f69SBart Van Assche #define MX_ALLOC_LEN (0xc000 + 0x80)
108*44704f69SBart Van Assche #define VPD_ATA_INFO_LEN  572
109*44704f69SBart Van Assche 
110*44704f69SBart Van Assche #define SENSE_BUFF_LEN  64       /* Arbitrary, could be larger */
111*44704f69SBart Van Assche #define INQUIRY_CMD     0x12
112*44704f69SBart Van Assche #define INQUIRY_CMDLEN  6
113*44704f69SBart Van Assche #define DEF_PT_TIMEOUT  60       /* 60 seconds */
114*44704f69SBart Van Assche 
115*44704f69SBart Van Assche 
116*44704f69SBart Van Assche uint8_t * rsp_buff;
117*44704f69SBart Van Assche 
118*44704f69SBart Van Assche static uint8_t * free_rsp_buff;
119*44704f69SBart Van Assche static const int rsp_buff_sz = MX_ALLOC_LEN + 1;
120*44704f69SBart Van Assche 
121*44704f69SBart Van Assche static char xtra_buff[MX_ALLOC_LEN + 1];
122*44704f69SBart Van Assche static char usn_buff[MX_ALLOC_LEN + 1];
123*44704f69SBart Van Assche 
124*44704f69SBart Van Assche static const char * find_version_descriptor_str(int value);
125*44704f69SBart Van Assche static void decode_dev_ids(const char * leadin, uint8_t * buff, int len,
126*44704f69SBart Van Assche                            struct opts_t * op, sgj_opaque_p jop);
127*44704f69SBart Van Assche static int vpd_decode(int sg_fd, struct opts_t * op, sgj_opaque_p jop,
128*44704f69SBart Van Assche                       int off);
129*44704f69SBart Van Assche 
130*44704f69SBart Van Assche // Test define that will only work for Linux
131*44704f69SBart Van Assche // #define HDIO_GET_IDENTITY 1
132*44704f69SBart Van Assche 
133*44704f69SBart Van Assche #if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS) && \
134*44704f69SBart Van Assche     defined(HDIO_GET_IDENTITY)
135*44704f69SBart Van Assche #include <sys/ioctl.h>
136*44704f69SBart Van Assche 
137*44704f69SBart Van Assche static int try_ata_identify(int ata_fd, int do_hex, int do_raw,
138*44704f69SBart Van Assche                             int verbose);
139*44704f69SBart Van Assche static void prepare_ata_identify(const struct opts_t * op, int inhex_len);
140*44704f69SBart Van Assche #endif
141*44704f69SBart Van Assche 
142*44704f69SBart Van Assche 
143*44704f69SBart Van Assche /* Note that this table is sorted by acronym */
144*44704f69SBart Van Assche static struct svpd_values_name_t t10_vpd_pg[] = {
145*44704f69SBart Van Assche     {VPD_AUTOMATION_DEV_SN, 0, 1, "adsn", "Automation device serial "
146*44704f69SBart Van Assche      "number (SSC)"},
147*44704f69SBart Van Assche     {VPD_ATA_INFO, 0, -1, "ai", "ATA information (SAT)"},
148*44704f69SBart Van Assche     {VPD_BLOCK_DEV_CHARS, 0, 0, "bdc",
149*44704f69SBart Van Assche      "Block device characteristics (SBC)"},
150*44704f69SBart Van Assche     {VPD_BLOCK_DEV_C_EXTENS, 0, 0, "bdce", "Block device characteristics "
151*44704f69SBart Van Assche      "extension (SBC)"},
152*44704f69SBart Van Assche     {VPD_BLOCK_LIMITS, 0, 0, "bl", "Block limits (SBC)"},
153*44704f69SBart Van Assche     {VPD_BLOCK_LIMITS_EXT, 0, 0, "ble", "Block limits extension (SBC)"},
154*44704f69SBart Van Assche     {VPD_CFA_PROFILE_INFO, 0, 0, "cfa", "CFA profile information"},
155*44704f69SBart Van Assche     {VPD_CON_POS_RANGE, 0, 0, "cpr", "Concurrent positioning ranges "
156*44704f69SBart Van Assche      "(SBC)"},
157*44704f69SBart Van Assche     {VPD_DEVICE_CONSTITUENTS, 0, -1, "dc", "Device constituents"},
158*44704f69SBart Van Assche     {VPD_DEVICE_ID, 0, -1, "di", "Device identification"},
159*44704f69SBart Van Assche #if 0           /* following found in sg_vpd */
160*44704f69SBart Van Assche     {VPD_DEVICE_ID, VPD_DI_SEL_AS_IS, -1, "di_asis", "Like 'di' "
161*44704f69SBart Van Assche      "but designators ordered as found"},
162*44704f69SBart Van Assche     {VPD_DEVICE_ID, VPD_DI_SEL_LU, -1, "di_lu", "Device identification, "
163*44704f69SBart Van Assche      "lu only"},
164*44704f69SBart Van Assche     {VPD_DEVICE_ID, VPD_DI_SEL_TPORT, -1, "di_port", "Device "
165*44704f69SBart Van Assche      "identification, target port only"},
166*44704f69SBart Van Assche     {VPD_DEVICE_ID, VPD_DI_SEL_TARGET, -1, "di_target", "Device "
167*44704f69SBart Van Assche      "identification, target device only"},
168*44704f69SBart Van Assche #endif
169*44704f69SBart Van Assche     {VPD_EXT_INQ, 0, -1, "ei", "Extended inquiry data"},
170*44704f69SBart Van Assche     {VPD_FORMAT_PRESETS, 0, 0, "fp", "Format presets"},
171*44704f69SBart Van Assche     {VPD_LB_PROTECTION, 0, 0, "lbpro", "Logical block protection (SSC)"},
172*44704f69SBart Van Assche     {VPD_LB_PROVISIONING, 0, 0, "lbpv", "Logical block provisioning "
173*44704f69SBart Van Assche      "(SBC)"},
174*44704f69SBart Van Assche     {VPD_MAN_ASS_SN, 0, 1, "mas", "Manufacturer assigned serial number (SSC)"},
175*44704f69SBart Van Assche     {VPD_MAN_ASS_SN, 0, 0x12, "masa",
176*44704f69SBart Van Assche      "Manufacturer assigned serial number (ADC)"},
177*44704f69SBart Van Assche     {VPD_MAN_NET_ADDR, 0, -1, "mna", "Management network addresses"},
178*44704f69SBart Van Assche     {VPD_MODE_PG_POLICY, 0, -1, "mpp", "Mode page policy"},
179*44704f69SBart Van Assche     {VPD_POWER_CONDITION, 0, -1, "po", "Power condition"},/* "pc" in sg_vpd */
180*44704f69SBart Van Assche     {VPD_POWER_CONSUMPTION, 0, -1, "psm", "Power consumption"},
181*44704f69SBart Van Assche     {VPD_PROTO_LU, 0, -1, "pslu", "Protocol-specific logical unit "
182*44704f69SBart Van Assche      "information"},
183*44704f69SBart Van Assche     {VPD_PROTO_PORT, 0, -1, "pspo", "Protocol-specific port information"},
184*44704f69SBart Van Assche     {VPD_REFERRALS, 0, 0, "ref", "Referrals (SBC)"},
185*44704f69SBart Van Assche     {VPD_SA_DEV_CAP, 0, 1, "sad",
186*44704f69SBart Van Assche      "Sequential access device capabilities (SSC)"},
187*44704f69SBart Van Assche     {VPD_SUP_BLOCK_LENS, 0, 0, "sbl", "Supported block lengths and "
188*44704f69SBart Van Assche      "protection types (SBC)"},
189*44704f69SBart Van Assche     {VPD_SCSI_FEATURE_SETS, 0, -1, "sfs", "SCSI Feature sets"},
190*44704f69SBart Van Assche     {VPD_SOFTW_INF_ID, 0, -1, "sii", "Software interface identification"},
191*44704f69SBart Van Assche     {VPD_NOPE_WANT_STD_INQ, 0, -1, "sinq", "Standard inquiry data format"},
192*44704f69SBart Van Assche     {VPD_UNIT_SERIAL_NUM, 0, -1, "sn", "Unit serial number"},
193*44704f69SBart Van Assche     {VPD_SCSI_PORTS, 0, -1, "sp", "SCSI ports"},
194*44704f69SBart Van Assche     {VPD_SUPPORTED_VPDS, 0, -1, "sv", "Supported VPD pages"},
195*44704f69SBart Van Assche     {VPD_TA_SUPPORTED, 0, 1, "tas", "TapeAlert supported flags (SSC)"},
196*44704f69SBart Van Assche     {VPD_3PARTY_COPY, 0, -1, "tpc", "Third party copy"},
197*44704f69SBart Van Assche     {VPD_ZBC_DEV_CHARS, 0, 0, "zbdch", "Zoned block device "
198*44704f69SBart Van Assche      "characteristics"},
199*44704f69SBart Van Assche     {0, 0, 0, NULL, NULL},
200*44704f69SBart Van Assche };
201*44704f69SBart Van Assche 
202*44704f69SBart Van Assche /* Some alternate acronyms for T10 VPD pages (compatibility with sg_vpd) */
203*44704f69SBart Van Assche static struct svpd_values_name_t alt_t10_vpd_pg[] = {
204*44704f69SBart Van Assche     {VPD_NOPE_WANT_STD_INQ, 0, -1, "stdinq", "Standard inquiry data format"},
205*44704f69SBart Van Assche     {VPD_POWER_CONDITION, 0, -1, "pc", "Power condition"},
206*44704f69SBart Van Assche     {0, 0, 0, NULL, NULL},
207*44704f69SBart Van Assche };
208*44704f69SBart Van Assche 
209*44704f69SBart Van Assche static struct svpd_values_name_t vs_vpd_pg[] = {
210*44704f69SBart Van Assche     /* Following are vendor specific */
211*44704f69SBart Van Assche     {SG_NVME_VPD_NICR, 0, -1, "nicr",
212*44704f69SBart Van Assche      "NVMe Identify Controller Response (sg3_utils)"},
213*44704f69SBart Van Assche     {VPD_RDAC_VAC, 0, -1, "rdac_vac", "RDAC volume access control (RDAC)"},
214*44704f69SBart Van Assche     {VPD_RDAC_VERS, 0, -1, "rdac_vers", "RDAC software version (RDAC)"},
215*44704f69SBart Van Assche     {VPD_UPR_EMC, 0, -1, "upr", "Unit path report (EMC)"},
216*44704f69SBart Van Assche     {0, 0, 0, NULL, NULL},
217*44704f69SBart Van Assche };
218*44704f69SBart Van Assche 
219*44704f69SBart Van Assche static struct option long_options[] = {
220*44704f69SBart Van Assche #if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS) && \
221*44704f69SBart Van Assche     defined(HDIO_GET_IDENTITY)
222*44704f69SBart Van Assche         {"ata", no_argument, 0, 'a'},
223*44704f69SBart Van Assche #endif
224*44704f69SBart Van Assche         {"block", required_argument, 0, 'B'},
225*44704f69SBart Van Assche         {"cmddt", no_argument, 0, 'c'},
226*44704f69SBart Van Assche         {"descriptors", no_argument, 0, 'd'},
227*44704f69SBart Van Assche         {"export", no_argument, 0, 'u'},
228*44704f69SBart Van Assche         {"extended", no_argument, 0, 'x'},
229*44704f69SBart Van Assche         {"force", no_argument, 0, 'f'},
230*44704f69SBart Van Assche         {"help", no_argument, 0, 'h'},
231*44704f69SBart Van Assche         {"hex", no_argument, 0, 'H'},
232*44704f69SBart Van Assche         {"id", no_argument, 0, 'i'},
233*44704f69SBart Van Assche         {"inhex", required_argument, 0, 'I'},
234*44704f69SBart Van Assche         {"len", required_argument, 0, 'l'},
235*44704f69SBart Van Assche         {"long", no_argument, 0, 'L'},
236*44704f69SBart Van Assche         {"maxlen", required_argument, 0, 'm'},
237*44704f69SBart Van Assche #ifdef SG_SCSI_STRINGS
238*44704f69SBart Van Assche         {"new", no_argument, 0, 'N'},
239*44704f69SBart Van Assche         {"old", no_argument, 0, 'O'},
240*44704f69SBart Van Assche #endif
241*44704f69SBart Van Assche         {"only", no_argument, 0, 'o'},
242*44704f69SBart Van Assche         {"page", required_argument, 0, 'p'},
243*44704f69SBart Van Assche         {"raw", no_argument, 0, 'r'},
244*44704f69SBart Van Assche         {"sinq_inraw", required_argument, 0, 'Q'},
245*44704f69SBart Van Assche         {"sinq-inraw", required_argument, 0, 'Q'},
246*44704f69SBart Van Assche         {"vendor", no_argument, 0, 's'},
247*44704f69SBart Van Assche         {"verbose", no_argument, 0, 'v'},
248*44704f69SBart Van Assche         {"version", no_argument, 0, 'V'},
249*44704f69SBart Van Assche         {"vpd", no_argument, 0, 'e'},
250*44704f69SBart Van Assche         {0, 0, 0, 0},
251*44704f69SBart Van Assche };
252*44704f69SBart Van Assche 
253*44704f69SBart Van Assche 
254*44704f69SBart Van Assche static void
usage()255*44704f69SBart Van Assche usage()
256*44704f69SBart Van Assche {
257*44704f69SBart Van Assche #if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS) && \
258*44704f69SBart Van Assche     defined(HDIO_GET_IDENTITY)
259*44704f69SBart Van Assche 
260*44704f69SBart Van Assche     pr2serr("Usage: sg_inq [--ata] [--block=0|1] [--cmddt] [--descriptors] "
261*44704f69SBart Van Assche             "[--export]\n"
262*44704f69SBart Van Assche             "              [--extended] [--help] [--hex] [--id] "
263*44704f69SBart Van Assche             "[--inhex=FN]\n"
264*44704f69SBart Van Assche             "              [--json[=JO]] [--len=LEN] [--long] "
265*44704f69SBart Van Assche             "[--maxlen=LEN]\n"
266*44704f69SBart Van Assche             "              [--only] [--page=PG] [--raw] [--sinq_inraw=RFN] "
267*44704f69SBart Van Assche             "[--vendor]\n"
268*44704f69SBart Van Assche             "              [--verbose] [--version] [--vpd] DEVICE\n"
269*44704f69SBart Van Assche             "  where:\n"
270*44704f69SBart Van Assche             "    --ata|-a        treat DEVICE as (directly attached) ATA "
271*44704f69SBart Van Assche             "device\n");
272*44704f69SBart Van Assche #else
273*44704f69SBart Van Assche     pr2serr("Usage: sg_inq [--block=0|1] [--cmddt] [--descriptors] "
274*44704f69SBart Van Assche             "[--export]\n"
275*44704f69SBart Van Assche             "              [--extended] [--help] [--hex] [--id] "
276*44704f69SBart Van Assche             "[--inhex=FN]\n"
277*44704f69SBart Van Assche             "              [--json[=JO]] [--len=LEN] [--long] "
278*44704f69SBart Van Assche             "[--maxlen=LEN]\n"
279*44704f69SBart Van Assche             "              [--only] [--page=PG] [--raw] [--sinq_inraw=RFN] "
280*44704f69SBart Van Assche             "[--verbose]\n"
281*44704f69SBart Van Assche             "              [--version] [--vpd] DEVICE\n"
282*44704f69SBart Van Assche             "  where:\n");
283*44704f69SBart Van Assche #endif
284*44704f69SBart Van Assche     pr2serr("    --block=0|1     0-> open(non-blocking); 1-> "
285*44704f69SBart Van Assche             "open(blocking)\n"
286*44704f69SBart Van Assche             "      -B 0|1        (def: depends on OS; Linux pt: 0)\n"
287*44704f69SBart Van Assche             "    --cmddt|-c      command support data mode (set opcode "
288*44704f69SBart Van Assche             "with '--page=PG')\n"
289*44704f69SBart Van Assche             "                    use twice for list of supported "
290*44704f69SBart Van Assche             "commands; obsolete\n"
291*44704f69SBart Van Assche             "    --descriptors|-d    fetch and decode version descriptors\n"
292*44704f69SBart Van Assche             "    --export|-u     SCSI_IDENT_<assoc>_<type>=<ident> output "
293*44704f69SBart Van Assche             "format.\n"
294*44704f69SBart Van Assche             "                    Defaults to device id page (0x83) if --page "
295*44704f69SBart Van Assche             "not given,\n"
296*44704f69SBart Van Assche             "                    only supported for VPD pages 0x80 and 0x83\n"
297*44704f69SBart Van Assche             "    --extended|-E|-x    decode extended INQUIRY data VPD page "
298*44704f69SBart Van Assche             "(0x86)\n"
299*44704f69SBart Van Assche             "    --force|-f      skip VPD page 0 check; directly fetch "
300*44704f69SBart Van Assche             "requested page\n"
301*44704f69SBart Van Assche             "    --help|-h       print usage message then exit\n"
302*44704f69SBart Van Assche             "    --hex|-H        output response in hex\n"
303*44704f69SBart Van Assche             "    --id|-i         decode device identification VPD page "
304*44704f69SBart Van Assche             "(0x83)\n"
305*44704f69SBart Van Assche             "    --inhex=FN|-I FN    read ASCII hex from file FN instead of "
306*44704f69SBart Van Assche             "DEVICE;\n"
307*44704f69SBart Van Assche             "                        if used with --raw then read binary "
308*44704f69SBart Van Assche             "from FN\n"
309*44704f69SBart Van Assche             "    --json[=JO]|-j[JO]    output in JSON instead of human "
310*44704f69SBart Van Assche             "readable text.\n"
311*44704f69SBart Van Assche             "                          Use --json=? for JSON help\n"
312*44704f69SBart Van Assche             "    --len=LEN|-l LEN    requested response length (def: 0 "
313*44704f69SBart Van Assche             "-> fetch 36\n"
314*44704f69SBart Van Assche             "                        bytes first, then fetch again as "
315*44704f69SBart Van Assche             "indicated)\n"
316*44704f69SBart Van Assche             "    --long|-L       supply extra information on NVMe devices\n"
317*44704f69SBart Van Assche             "    --maxlen=LEN|-m LEN    same as '--len='\n"
318*44704f69SBart Van Assche             "    --old|-O        use old interface (use as first option)\n"
319*44704f69SBart Van Assche             "    --only|-o       for std inquiry do not fetch serial number "
320*44704f69SBart Van Assche             "vpd page;\n"
321*44704f69SBart Van Assche             "                    for NVMe device only do Identify "
322*44704f69SBart Van Assche             "controller\n"
323*44704f69SBart Van Assche             "    --page=PG|-p PG     Vital Product Data (VPD) page number "
324*44704f69SBart Van Assche             "or\n"
325*44704f69SBart Van Assche             "                        abbreviation (opcode number if "
326*44704f69SBart Van Assche             "'--cmddt' given)\n"
327*44704f69SBart Van Assche             "    --raw|-r        output response in binary (to stdout)\n"
328*44704f69SBart Van Assche             "    --sinq_inraw=RFN|-Q RFN    read raw (binary) standard "
329*44704f69SBart Van Assche             "INQUIRY\n"
330*44704f69SBart Van Assche             "                               response from the RFN filename\n"
331*44704f69SBart Van Assche             "    --vendor|-s     show vendor specific fields in std "
332*44704f69SBart Van Assche             "inquiry\n"
333*44704f69SBart Van Assche             "    --verbose|-v    increase verbosity\n"
334*44704f69SBart Van Assche             "    --version|-V    print version string then exit\n"
335*44704f69SBart Van Assche             "    --vpd|-e        vital product data (set page with "
336*44704f69SBart Van Assche             "'--page=PG')\n\n"
337*44704f69SBart Van Assche             "Sends a SCSI INQUIRY command to the DEVICE and decodes the "
338*44704f69SBart Van Assche             "response.\nAlternatively it decodes the INQUIRY response held "
339*44704f69SBart Van Assche             "in file FN. If no\noptions given then it sends a 'standard' "
340*44704f69SBart Van Assche             "INQUIRY command to DEVICE. Can\nlist VPD pages with '--vpd' or "
341*44704f69SBart Van Assche             "'--page=PG' option.\n");
342*44704f69SBart Van Assche }
343*44704f69SBart Van Assche 
344*44704f69SBart Van Assche #ifdef SG_SCSI_STRINGS
345*44704f69SBart Van Assche static void
usage_old()346*44704f69SBart Van Assche usage_old()
347*44704f69SBart Van Assche {
348*44704f69SBart Van Assche #ifdef SG_LIB_LINUX
349*44704f69SBart Van Assche     pr2serr("Usage:  sg_inq [-a] [-A] [-b] [-B=0|1] [-c] [-cl] [-d] [-e] "
350*44704f69SBart Van Assche             "[-h]\n"
351*44704f69SBart Van Assche             "               [-H] [-i] [-I=FN] [-j[=JO]] [-l=LEN] [-L] [-m] "
352*44704f69SBart Van Assche             "[-M]\n"
353*44704f69SBart Van Assche             "               [-o] [-p=VPD_PG] [-P] [-r] [-s] [-u] [-U] [-v] "
354*44704f69SBart Van Assche             "[-V]\n"
355*44704f69SBart Van Assche             "               [-x] [-36] [-?] DEVICE\n"
356*44704f69SBart Van Assche             "  where:\n"
357*44704f69SBart Van Assche             "    -a    decode ATA information VPD page (0x89)\n"
358*44704f69SBart Van Assche             "    -A    treat <device> as (directly attached) ATA device\n");
359*44704f69SBart Van Assche #else
360*44704f69SBart Van Assche     pr2serr("Usage:  sg_inq [-a] [-b] [-B 0|1] [-c] [-cl] [-d] [-e] [-h] "
361*44704f69SBart Van Assche             "[-H]\n"
362*44704f69SBart Van Assche             "               [-i] [-l=LEN] [-L] [-m] [-M] [-o] "
363*44704f69SBart Van Assche             "[-p=VPD_PG]\n"
364*44704f69SBart Van Assche             "               [-P] [-r] [-s] [-u] [-v] [-V] [-x] [-36] "
365*44704f69SBart Van Assche             "[-?]\n"
366*44704f69SBart Van Assche             "               DEVICE\n"
367*44704f69SBart Van Assche             "  where:\n"
368*44704f69SBart Van Assche             "    -a    decode ATA information VPD page (0x89)\n");
369*44704f69SBart Van Assche 
370*44704f69SBart Van Assche #endif  /* SG_LIB_LINUX */
371*44704f69SBart Van Assche     pr2serr("    -b    decode Block limits VPD page (0xb0) (SBC)\n"
372*44704f69SBart Van Assche             "    -B=0|1    0-> open(non-blocking); 1->open(blocking)\n"
373*44704f69SBart Van Assche             "    -c    set CmdDt mode (use -o for opcode) [obsolete]\n"
374*44704f69SBart Van Assche             "    -cl   list supported commands using CmdDt mode [obsolete]\n"
375*44704f69SBart Van Assche             "    -d    decode: version descriptors or VPD page\n"
376*44704f69SBart Van Assche             "    -e    set VPD mode (use -p for page code)\n"
377*44704f69SBart Van Assche             "    -h    output in hex (ASCII to the right)\n"
378*44704f69SBart Van Assche             "    -H    output in hex (ASCII to the right) [same as '-h']\n"
379*44704f69SBart Van Assche             "    -i    decode device identification VPD page (0x83)\n"
380*44704f69SBart Van Assche             "    -I=FN    use ASCII hex in file FN instead of DEVICE\n"
381*44704f69SBart Van Assche             "    -j[=JO]    output in JSON instead of human readable "
382*44704f69SBart Van Assche             "text.\n"
383*44704f69SBart Van Assche             "    -l=LEN    requested response length (def: 0 "
384*44704f69SBart Van Assche             "-> fetch 36\n"
385*44704f69SBart Van Assche             "                    bytes first, then fetch again as "
386*44704f69SBart Van Assche             "indicated)\n"
387*44704f69SBart Van Assche             "    -L    supply extra information on NVMe devices\n"
388*44704f69SBart Van Assche             "    -m    decode management network addresses VPD page "
389*44704f69SBart Van Assche             "(0x85)\n"
390*44704f69SBart Van Assche             "    -M    decode mode page policy VPD page (0x87)\n"
391*44704f69SBart Van Assche             "    -N|--new   use new interface\n"
392*44704f69SBart Van Assche             "    -o    for std inquiry only do that, not serial number vpd "
393*44704f69SBart Van Assche             "page\n"
394*44704f69SBart Van Assche             "    -p=VPD_PG    vpd page code in hex (def: 0)\n"
395*44704f69SBart Van Assche             "    -P    decode Unit Path Report VPD page (0xc0) (EMC)\n"
396*44704f69SBart Van Assche             "    -r    output response in binary ('-rr': output for hdparm)\n"
397*44704f69SBart Van Assche             "    -s    decode SCSI Ports VPD page (0x88)\n"
398*44704f69SBart Van Assche             "    -u    SCSI_IDENT_<assoc>_<type>=<ident> output format\n"
399*44704f69SBart Van Assche             "    -v    verbose (output cdb and, if non-zero, resid)\n"
400*44704f69SBart Van Assche             "    -V    output version string\n"
401*44704f69SBart Van Assche             "    -x    decode extended INQUIRY data VPD page (0x86)\n"
402*44704f69SBart Van Assche             "    -36   perform standard INQUIRY with a 36 byte response\n"
403*44704f69SBart Van Assche             "    -?    output this usage message\n\n"
404*44704f69SBart Van Assche             "If no options given then sends a standard SCSI INQUIRY "
405*44704f69SBart Van Assche             "command and\ndecodes the response.\n");
406*44704f69SBart Van Assche }
407*44704f69SBart Van Assche 
408*44704f69SBart Van Assche static void
usage_for(const struct opts_t * op)409*44704f69SBart Van Assche usage_for(const struct opts_t * op)
410*44704f69SBart Van Assche {
411*44704f69SBart Van Assche     if (op->opt_new)
412*44704f69SBart Van Assche         usage();
413*44704f69SBart Van Assche     else
414*44704f69SBart Van Assche         usage_old();
415*44704f69SBart Van Assche }
416*44704f69SBart Van Assche 
417*44704f69SBart Van Assche #else  /* SG_SCSI_STRINGS */
418*44704f69SBart Van Assche 
419*44704f69SBart Van Assche static void
usage_for(const struct opts_t * op)420*44704f69SBart Van Assche usage_for(const struct opts_t * op)
421*44704f69SBart Van Assche {
422*44704f69SBart Van Assche     if (op) { }         /* suppress warning */
423*44704f69SBart Van Assche     usage();
424*44704f69SBart Van Assche }
425*44704f69SBart Van Assche 
426*44704f69SBart Van Assche #endif /* SG_SCSI_STRINGS */
427*44704f69SBart Van Assche 
428*44704f69SBart Van Assche /* Processes command line options according to new option format. Returns
429*44704f69SBart Van Assche  * 0 is ok, else SG_LIB_SYNTAX_ERROR is returned. */
430*44704f69SBart Van Assche static int
new_parse_cmd_line(struct opts_t * op,int argc,char * argv[])431*44704f69SBart Van Assche new_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
432*44704f69SBart Van Assche {
433*44704f69SBart Van Assche     int c, n;
434*44704f69SBart Van Assche 
435*44704f69SBart Van Assche     while (1) {
436*44704f69SBart Van Assche         int option_index = 0;
437*44704f69SBart Van Assche 
438*44704f69SBart Van Assche #ifdef SG_LIB_LINUX
439*44704f69SBart Van Assche #ifdef SG_SCSI_STRINGS
440*44704f69SBart Van Assche         c = getopt_long(argc, argv, "aB:cdeEfhHiI:j::l:Lm:M:NoOp:Q:rsuvVx",
441*44704f69SBart Van Assche                         long_options, &option_index);
442*44704f69SBart Van Assche #else
443*44704f69SBart Van Assche         c = getopt_long(argc, argv, "B:cdeEfhHiI:j::l:Lm:M:op:Q:rsuvVx",
444*44704f69SBart Van Assche                         long_options, &option_index);
445*44704f69SBart Van Assche #endif /* SG_SCSI_STRINGS */
446*44704f69SBart Van Assche #else  /* SG_LIB_LINUX */
447*44704f69SBart Van Assche #ifdef SG_SCSI_STRINGS
448*44704f69SBart Van Assche         c = getopt_long(argc, argv, "B:cdeEfhHiI:j::l:Lm:M:NoOp:Q:rsuvVx",
449*44704f69SBart Van Assche                         long_options, &option_index);
450*44704f69SBart Van Assche #else
451*44704f69SBart Van Assche         c = getopt_long(argc, argv, "B:cdeEfhHiI:j::l:Lm:M:op:Q:rsuvVx",
452*44704f69SBart Van Assche                         long_options, &option_index);
453*44704f69SBart Van Assche #endif /* SG_SCSI_STRINGS */
454*44704f69SBart Van Assche #endif /* SG_LIB_LINUX */
455*44704f69SBart Van Assche         if (c == -1)
456*44704f69SBart Van Assche             break;
457*44704f69SBart Van Assche 
458*44704f69SBart Van Assche         switch (c) {
459*44704f69SBart Van Assche #if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS) && \
460*44704f69SBart Van Assche     defined(HDIO_GET_IDENTITY)
461*44704f69SBart Van Assche         case 'a':
462*44704f69SBart Van Assche             op->do_ata = true;
463*44704f69SBart Van Assche             break;
464*44704f69SBart Van Assche #endif
465*44704f69SBart Van Assche         case 'B':
466*44704f69SBart Van Assche             if ('-' == optarg[0])
467*44704f69SBart Van Assche                 n = -1;
468*44704f69SBart Van Assche             else {
469*44704f69SBart Van Assche                 n = sg_get_num(optarg);
470*44704f69SBart Van Assche                 if ((n < 0) || (n > 1)) {
471*44704f69SBart Van Assche                     pr2serr("bad argument to '--block=' want 0 or 1\n");
472*44704f69SBart Van Assche                     usage_for(op);
473*44704f69SBart Van Assche                     return SG_LIB_SYNTAX_ERROR;
474*44704f69SBart Van Assche                 }
475*44704f69SBart Van Assche             }
476*44704f69SBart Van Assche             op->do_block = n;
477*44704f69SBart Van Assche             break;
478*44704f69SBart Van Assche         case 'c':
479*44704f69SBart Van Assche             ++op->do_cmddt;
480*44704f69SBart Van Assche             break;
481*44704f69SBart Van Assche         case 'd':
482*44704f69SBart Van Assche             op->do_descriptors = true;
483*44704f69SBart Van Assche             break;
484*44704f69SBart Van Assche         case 'e':
485*44704f69SBart Van Assche             op->do_vpd = true;
486*44704f69SBart Van Assche             break;
487*44704f69SBart Van Assche         case 'E':       /* --extended */
488*44704f69SBart Van Assche         case 'x':
489*44704f69SBart Van Assche             op->do_decode = true;
490*44704f69SBart Van Assche             op->do_vpd = true;
491*44704f69SBart Van Assche             op->vpd_pn = VPD_EXT_INQ;
492*44704f69SBart Van Assche             op->page_given = true;
493*44704f69SBart Van Assche             break;
494*44704f69SBart Van Assche         case 'f':
495*44704f69SBart Van Assche             op->do_force = true;
496*44704f69SBart Van Assche             break;
497*44704f69SBart Van Assche         case 'h':
498*44704f69SBart Van Assche             ++op->do_help;
499*44704f69SBart Van Assche             break;
500*44704f69SBart Van Assche         case 'j':
501*44704f69SBart Van Assche             if (! sgj_init_state(&op->json_st, optarg)) {
502*44704f69SBart Van Assche                 int bad_char = op->json_st.first_bad_char;
503*44704f69SBart Van Assche                 char e[1500];
504*44704f69SBart Van Assche 
505*44704f69SBart Van Assche                 if (bad_char) {
506*44704f69SBart Van Assche                     pr2serr("bad argument to --json= option, unrecognized "
507*44704f69SBart Van Assche                             "character '%c'\n\n", bad_char);
508*44704f69SBart Van Assche                 }
509*44704f69SBart Van Assche                 sg_json_usage(0, e, sizeof(e));
510*44704f69SBart Van Assche                 pr2serr("%s", e);
511*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
512*44704f69SBart Van Assche             }
513*44704f69SBart Van Assche             break;
514*44704f69SBart Van Assche         case 'o':
515*44704f69SBart Van Assche             op->do_only = true;
516*44704f69SBart Van Assche             break;
517*44704f69SBart Van Assche         case '?':
518*44704f69SBart Van Assche             if (! op->do_help)
519*44704f69SBart Van Assche                 ++op->do_help;
520*44704f69SBart Van Assche             break;
521*44704f69SBart Van Assche         case 'H':
522*44704f69SBart Van Assche             ++op->do_hex;
523*44704f69SBart Van Assche             break;
524*44704f69SBart Van Assche         case 'i':
525*44704f69SBart Van Assche             op->do_decode = true;
526*44704f69SBart Van Assche             op->do_vpd = true;
527*44704f69SBart Van Assche             op->vpd_pn = VPD_DEVICE_ID;
528*44704f69SBart Van Assche             op->page_given = true;
529*44704f69SBart Van Assche             break;
530*44704f69SBart Van Assche         case 'I':
531*44704f69SBart Van Assche             op->inhex_fn = optarg;
532*44704f69SBart Van Assche             break;
533*44704f69SBart Van Assche         case 'l':
534*44704f69SBart Van Assche         case 'm':
535*44704f69SBart Van Assche             n = sg_get_num(optarg);
536*44704f69SBart Van Assche             if ((n < 0) || (n > 65532)) {
537*44704f69SBart Van Assche                 pr2serr("bad argument to '--len='\n");
538*44704f69SBart Van Assche                 usage_for(op);
539*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
540*44704f69SBart Van Assche             }
541*44704f69SBart Van Assche             if ((n > 0) && (n < 4)) {
542*44704f69SBart Van Assche                 pr2serr("Changing that '--maxlen=' value to 4\n");
543*44704f69SBart Van Assche                 n = 4;
544*44704f69SBart Van Assche             }
545*44704f69SBart Van Assche             op->maxlen = n;
546*44704f69SBart Van Assche             break;
547*44704f69SBart Van Assche         case 'M':
548*44704f69SBart Van Assche             if (op->vend_prod) {
549*44704f69SBart Van Assche                 pr2serr("only one '--vendor=' option permitted\n");
550*44704f69SBart Van Assche                 usage();
551*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
552*44704f69SBart Van Assche             } else
553*44704f69SBart Van Assche                 op->vend_prod = optarg;
554*44704f69SBart Van Assche             break;
555*44704f69SBart Van Assche         case 'L':
556*44704f69SBart Van Assche             ++op->do_long;
557*44704f69SBart Van Assche             break;
558*44704f69SBart Van Assche #ifdef SG_SCSI_STRINGS
559*44704f69SBart Van Assche         case 'N':
560*44704f69SBart Van Assche             break;      /* ignore */
561*44704f69SBart Van Assche         case 'O':
562*44704f69SBart Van Assche             op->opt_new = false;
563*44704f69SBart Van Assche             return 0;
564*44704f69SBart Van Assche #endif
565*44704f69SBart Van Assche         case 'p':
566*44704f69SBart Van Assche             op->page_str = optarg;
567*44704f69SBart Van Assche             op->page_given = true;
568*44704f69SBart Van Assche             break;
569*44704f69SBart Van Assche         case 'Q':
570*44704f69SBart Van Assche             op->sinq_inraw_fn = optarg;
571*44704f69SBart Van Assche             break;
572*44704f69SBart Van Assche         case 'r':
573*44704f69SBart Van Assche             ++op->do_raw;
574*44704f69SBart Van Assche             break;
575*44704f69SBart Van Assche         case 's':
576*44704f69SBart Van Assche             ++op->do_vendor;
577*44704f69SBart Van Assche             break;
578*44704f69SBart Van Assche         case 'u':
579*44704f69SBart Van Assche             op->do_export = true;
580*44704f69SBart Van Assche             break;
581*44704f69SBart Van Assche         case 'v':
582*44704f69SBart Van Assche             op->verbose_given = true;
583*44704f69SBart Van Assche             ++op->verbose;
584*44704f69SBart Van Assche             break;
585*44704f69SBart Van Assche         case 'V':
586*44704f69SBart Van Assche             op->version_given = true;
587*44704f69SBart Van Assche             break;
588*44704f69SBart Van Assche         default:
589*44704f69SBart Van Assche             pr2serr("unrecognised option code %c [0x%x]\n", c, c);
590*44704f69SBart Van Assche             if (op->do_help)
591*44704f69SBart Van Assche                 break;
592*44704f69SBart Van Assche             usage_for(op);
593*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
594*44704f69SBart Van Assche         }
595*44704f69SBart Van Assche     }
596*44704f69SBart Van Assche     if (optind < argc) {
597*44704f69SBart Van Assche         if (NULL == op->device_name) {
598*44704f69SBart Van Assche             op->device_name = argv[optind];
599*44704f69SBart Van Assche             ++optind;
600*44704f69SBart Van Assche         }
601*44704f69SBart Van Assche         if (optind < argc) {
602*44704f69SBart Van Assche             for (; optind < argc; ++optind)
603*44704f69SBart Van Assche                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
604*44704f69SBart Van Assche             usage_for(op);
605*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
606*44704f69SBart Van Assche         }
607*44704f69SBart Van Assche     }
608*44704f69SBart Van Assche     return 0;
609*44704f69SBart Van Assche }
610*44704f69SBart Van Assche 
611*44704f69SBart Van Assche #ifdef SG_SCSI_STRINGS
612*44704f69SBart Van Assche /* Processes command line options according to old option format. Returns
613*44704f69SBart Van Assche  * 0 is ok, else SG_LIB_SYNTAX_ERROR is returned. */
614*44704f69SBart Van Assche static int
old_parse_cmd_line(struct opts_t * op,int argc,char * argv[])615*44704f69SBart Van Assche old_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
616*44704f69SBart Van Assche {
617*44704f69SBart Van Assche     bool jmp_out;
618*44704f69SBart Van Assche     int k, plen, num, n;
619*44704f69SBart Van Assche     const char * cp;
620*44704f69SBart Van Assche 
621*44704f69SBart Van Assche     for (k = 1; k < argc; ++k) {
622*44704f69SBart Van Assche         cp = argv[k];
623*44704f69SBart Van Assche         plen = strlen(cp);
624*44704f69SBart Van Assche         if (plen <= 0)
625*44704f69SBart Van Assche             continue;
626*44704f69SBart Van Assche         if ('-' == *cp) {
627*44704f69SBart Van Assche             for (--plen, ++cp, jmp_out = false; plen > 0; --plen, ++cp) {
628*44704f69SBart Van Assche                 switch (*cp) {
629*44704f69SBart Van Assche                 case '3':
630*44704f69SBart Van Assche                     if ('6' == *(cp + 1)) {
631*44704f69SBart Van Assche                         op->maxlen = 36;
632*44704f69SBart Van Assche                         --plen;
633*44704f69SBart Van Assche                         ++cp;
634*44704f69SBart Van Assche                     } else
635*44704f69SBart Van Assche                         jmp_out = true;
636*44704f69SBart Van Assche                     break;
637*44704f69SBart Van Assche                 case 'a':
638*44704f69SBart Van Assche                     op->vpd_pn = VPD_ATA_INFO;
639*44704f69SBart Van Assche                     op->do_vpd = true;
640*44704f69SBart Van Assche                     op->page_given = true;
641*44704f69SBart Van Assche                     ++op->num_pages;
642*44704f69SBart Van Assche                     break;
643*44704f69SBart Van Assche #ifdef SG_LIB_LINUX
644*44704f69SBart Van Assche                 case 'A':
645*44704f69SBart Van Assche                     op->do_ata = true;
646*44704f69SBart Van Assche                     break;
647*44704f69SBart Van Assche #endif
648*44704f69SBart Van Assche                 case 'b':
649*44704f69SBart Van Assche                     op->vpd_pn = VPD_BLOCK_LIMITS;
650*44704f69SBart Van Assche                     op->do_vpd = true;
651*44704f69SBart Van Assche                     op->page_given = true;
652*44704f69SBart Van Assche                     ++op->num_pages;
653*44704f69SBart Van Assche                     break;
654*44704f69SBart Van Assche                 case 'c':
655*44704f69SBart Van Assche                     ++op->do_cmddt;
656*44704f69SBart Van Assche                     if ('l' == *(cp + 1)) {
657*44704f69SBart Van Assche                         ++op->do_cmddt;
658*44704f69SBart Van Assche                         --plen;
659*44704f69SBart Van Assche                         ++cp;
660*44704f69SBart Van Assche                     }
661*44704f69SBart Van Assche                     break;
662*44704f69SBart Van Assche                 case 'd':
663*44704f69SBart Van Assche                     op->do_descriptors = true;
664*44704f69SBart Van Assche                     op->do_decode = true;
665*44704f69SBart Van Assche                     break;
666*44704f69SBart Van Assche                 case 'e':
667*44704f69SBart Van Assche                     op->do_vpd = true;
668*44704f69SBart Van Assche                     break;
669*44704f69SBart Van Assche                 case 'f':
670*44704f69SBart Van Assche                     op->do_force = true;
671*44704f69SBart Van Assche                     break;
672*44704f69SBart Van Assche                 case 'h':
673*44704f69SBart Van Assche                 case 'H':
674*44704f69SBart Van Assche                     ++op->do_hex;
675*44704f69SBart Van Assche                     break;
676*44704f69SBart Van Assche                 case 'i':
677*44704f69SBart Van Assche                     op->vpd_pn = VPD_DEVICE_ID;
678*44704f69SBart Van Assche                     op->do_vpd = true;
679*44704f69SBart Van Assche                     op->page_given = true;
680*44704f69SBart Van Assche                     ++op->num_pages;
681*44704f69SBart Van Assche                     break;
682*44704f69SBart Van Assche                 case 'L':
683*44704f69SBart Van Assche                     ++op->do_long;
684*44704f69SBart Van Assche                     break;
685*44704f69SBart Van Assche                 case 'm':
686*44704f69SBart Van Assche                     op->vpd_pn = VPD_MAN_NET_ADDR;
687*44704f69SBart Van Assche                     op->do_vpd = true;
688*44704f69SBart Van Assche                     ++op->num_pages;
689*44704f69SBart Van Assche                     op->page_given = true;
690*44704f69SBart Van Assche                     break;
691*44704f69SBart Van Assche                 case 'M':
692*44704f69SBart Van Assche                     op->vpd_pn = VPD_MODE_PG_POLICY;
693*44704f69SBart Van Assche                     op->do_vpd = true;
694*44704f69SBart Van Assche                     op->page_given = true;
695*44704f69SBart Van Assche                     ++op->num_pages;
696*44704f69SBart Van Assche                     break;
697*44704f69SBart Van Assche                 case 'N':
698*44704f69SBart Van Assche                     op->opt_new = true;
699*44704f69SBart Van Assche                     return 0;
700*44704f69SBart Van Assche                 case 'o':
701*44704f69SBart Van Assche                     op->do_only = true;
702*44704f69SBart Van Assche                     break;
703*44704f69SBart Van Assche                 case 'O':
704*44704f69SBart Van Assche                     break;
705*44704f69SBart Van Assche                 case 'P':
706*44704f69SBart Van Assche                     op->vpd_pn = VPD_UPR_EMC;
707*44704f69SBart Van Assche                     op->do_vpd = true;
708*44704f69SBart Van Assche                     op->page_given = true;
709*44704f69SBart Van Assche                     ++op->num_pages;
710*44704f69SBart Van Assche                     break;
711*44704f69SBart Van Assche                 case 'r':
712*44704f69SBart Van Assche                     ++op->do_raw;
713*44704f69SBart Van Assche                     break;
714*44704f69SBart Van Assche                 case 's':
715*44704f69SBart Van Assche                     op->vpd_pn = VPD_SCSI_PORTS;
716*44704f69SBart Van Assche                     op->do_vpd = true;
717*44704f69SBart Van Assche                     op->page_given = true;
718*44704f69SBart Van Assche                     ++op->num_pages;
719*44704f69SBart Van Assche                     break;
720*44704f69SBart Van Assche                 case 'u':
721*44704f69SBart Van Assche                     op->do_export = true;
722*44704f69SBart Van Assche                     break;
723*44704f69SBart Van Assche                 case 'v':
724*44704f69SBart Van Assche                     op->verbose_given = true;
725*44704f69SBart Van Assche                     ++op->verbose;
726*44704f69SBart Van Assche                     break;
727*44704f69SBart Van Assche                 case 'V':
728*44704f69SBart Van Assche                     op->version_given = true;
729*44704f69SBart Van Assche                     break;
730*44704f69SBart Van Assche                 case 'x':
731*44704f69SBart Van Assche                     op->vpd_pn = VPD_EXT_INQ;
732*44704f69SBart Van Assche                     op->do_vpd = true;
733*44704f69SBart Van Assche                     op->page_given = true;
734*44704f69SBart Van Assche                     ++op->num_pages;
735*44704f69SBart Van Assche                     break;
736*44704f69SBart Van Assche                 case '?':
737*44704f69SBart Van Assche                     if (! op->do_help)
738*44704f69SBart Van Assche                         ++op->do_help;
739*44704f69SBart Van Assche                     break;
740*44704f69SBart Van Assche                 default:
741*44704f69SBart Van Assche                     jmp_out = true;
742*44704f69SBart Van Assche                     break;
743*44704f69SBart Van Assche                 }
744*44704f69SBart Van Assche                 if (jmp_out)
745*44704f69SBart Van Assche                     break;
746*44704f69SBart Van Assche             }
747*44704f69SBart Van Assche             if (plen <= 0)
748*44704f69SBart Van Assche                 continue;
749*44704f69SBart Van Assche             else if (0 == strncmp("B=", cp, 2)) {
750*44704f69SBart Van Assche                 num = sscanf(cp + 2, "%d", &n);
751*44704f69SBart Van Assche                 if ((1 != num) || (n < 0) || (n > 1)) {
752*44704f69SBart Van Assche                     pr2serr("'B=' option expects 0 or 1\n");
753*44704f69SBart Van Assche                     usage_for(op);
754*44704f69SBart Van Assche                     return SG_LIB_SYNTAX_ERROR;
755*44704f69SBart Van Assche                 }
756*44704f69SBart Van Assche                 op->do_block = n;
757*44704f69SBart Van Assche             } else if (0 == strncmp("I=", cp, 2))
758*44704f69SBart Van Assche                 op->inhex_fn = cp + 2;
759*44704f69SBart Van Assche             else if ('j' == *cp) { /* handle either '-j' or '-j=<JO>' */
760*44704f69SBart Van Assche                 const char * c2p = (('=' == *(cp + 1)) ? cp + 2 : NULL);
761*44704f69SBart Van Assche 
762*44704f69SBart Van Assche                 if (! sgj_init_state(&op->json_st, c2p)) {
763*44704f69SBart Van Assche                     int bad_char = op->json_st.first_bad_char;
764*44704f69SBart Van Assche                     char e[1500];
765*44704f69SBart Van Assche 
766*44704f69SBart Van Assche                     if (bad_char) {
767*44704f69SBart Van Assche                         pr2serr("bad argument to --json= option, unrecognized "
768*44704f69SBart Van Assche                                 "character '%c'\n\n", bad_char);
769*44704f69SBart Van Assche                     }
770*44704f69SBart Van Assche                     sg_json_usage(0, e, sizeof(e));
771*44704f69SBart Van Assche                     pr2serr("%s", e);
772*44704f69SBart Van Assche                     return SG_LIB_SYNTAX_ERROR;
773*44704f69SBart Van Assche                 }
774*44704f69SBart Van Assche             } else if (0 == strncmp("l=", cp, 2)) {
775*44704f69SBart Van Assche                 num = sscanf(cp + 2, "%d", &n);
776*44704f69SBart Van Assche                 if ((1 != num) || (n < 1)) {
777*44704f69SBart Van Assche                     pr2serr("Inappropriate value after 'l=' option\n");
778*44704f69SBart Van Assche                     usage_for(op);
779*44704f69SBart Van Assche                     return SG_LIB_SYNTAX_ERROR;
780*44704f69SBart Van Assche                 } else if (n > MX_ALLOC_LEN) {
781*44704f69SBart Van Assche                     pr2serr("value after 'l=' option too large\n");
782*44704f69SBart Van Assche                     return SG_LIB_SYNTAX_ERROR;
783*44704f69SBart Van Assche                 }
784*44704f69SBart Van Assche                 if ((n > 0) && (n < 4)) {
785*44704f69SBart Van Assche                     pr2serr("Changing that '-l=' value to 4\n");
786*44704f69SBart Van Assche                     n = 4;
787*44704f69SBart Van Assche                 }
788*44704f69SBart Van Assche                 op->maxlen = n;
789*44704f69SBart Van Assche             } else if (0 == strncmp("p=", cp, 2)) {
790*44704f69SBart Van Assche                 op->page_str = cp + 2;
791*44704f69SBart Van Assche                 op->page_given = true;
792*44704f69SBart Van Assche             } else if (0 == strncmp("-old", cp, 4))
793*44704f69SBart Van Assche                 ;
794*44704f69SBart Van Assche             else if (jmp_out) {
795*44704f69SBart Van Assche                 pr2serr("Unrecognized option: %s\n", cp);
796*44704f69SBart Van Assche                 usage_for(op);
797*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
798*44704f69SBart Van Assche             }
799*44704f69SBart Van Assche         } else if (0 == op->device_name)
800*44704f69SBart Van Assche             op->device_name = cp;
801*44704f69SBart Van Assche         else {
802*44704f69SBart Van Assche             pr2serr("too many arguments, got: %s, not expecting: %s\n",
803*44704f69SBart Van Assche                     op->device_name, cp);
804*44704f69SBart Van Assche             usage_for(op);
805*44704f69SBart Van Assche             return SG_LIB_SYNTAX_ERROR;
806*44704f69SBart Van Assche         }
807*44704f69SBart Van Assche     }
808*44704f69SBart Van Assche     return 0;
809*44704f69SBart Van Assche }
810*44704f69SBart Van Assche 
811*44704f69SBart Van Assche /* Process command line options. First check using new option format unless
812*44704f69SBart Van Assche  * the SG3_UTILS_OLD_OPTS environment variable is defined which causes the
813*44704f69SBart Van Assche  * old option format to be checked first. Both new and old format can be
814*44704f69SBart Van Assche  * countermanded by a '-O' and '-N' options respectively. As soon as either
815*44704f69SBart Van Assche  * of these options is detected (when processing the other format), processing
816*44704f69SBart Van Assche  * stops and is restarted using the other format. Clear? */
817*44704f69SBart Van Assche static int
parse_cmd_line(struct opts_t * op,int argc,char * argv[])818*44704f69SBart Van Assche parse_cmd_line(struct opts_t * op, int argc, char * argv[])
819*44704f69SBart Van Assche {
820*44704f69SBart Van Assche     int res;
821*44704f69SBart Van Assche     char * cp;
822*44704f69SBart Van Assche 
823*44704f69SBart Van Assche     cp = getenv("SG3_UTILS_OLD_OPTS");
824*44704f69SBart Van Assche     if (cp) {
825*44704f69SBart Van Assche         op->opt_new = false;
826*44704f69SBart Van Assche         res = old_parse_cmd_line(op, argc, argv);
827*44704f69SBart Van Assche         if ((0 == res) && op->opt_new)
828*44704f69SBart Van Assche             res = new_parse_cmd_line(op, argc, argv);
829*44704f69SBart Van Assche     } else {
830*44704f69SBart Van Assche         op->opt_new = true;
831*44704f69SBart Van Assche         res = new_parse_cmd_line(op, argc, argv);
832*44704f69SBart Van Assche         if ((0 == res) && (! op->opt_new))
833*44704f69SBart Van Assche             res = old_parse_cmd_line(op, argc, argv);
834*44704f69SBart Van Assche     }
835*44704f69SBart Van Assche     return res;
836*44704f69SBart Van Assche }
837*44704f69SBart Van Assche 
838*44704f69SBart Van Assche #else  /* SG_SCSI_STRINGS */
839*44704f69SBart Van Assche 
840*44704f69SBart Van Assche static int
parse_cmd_line(struct opts_t * op,int argc,char * argv[])841*44704f69SBart Van Assche parse_cmd_line(struct opts_t * op, int argc, char * argv[])
842*44704f69SBart Van Assche {
843*44704f69SBart Van Assche     return new_parse_cmd_line(op, argc, argv);
844*44704f69SBart Van Assche }
845*44704f69SBart Van Assche 
846*44704f69SBart Van Assche #endif  /* SG_SCSI_STRINGS */
847*44704f69SBart Van Assche 
848*44704f69SBart Van Assche 
849*44704f69SBart Van Assche static const struct svpd_values_name_t *
sdp_find_vpd_by_acron(const char * ap)850*44704f69SBart Van Assche sdp_find_vpd_by_acron(const char * ap)
851*44704f69SBart Van Assche {
852*44704f69SBart Van Assche     const struct svpd_values_name_t * vnp;
853*44704f69SBart Van Assche 
854*44704f69SBart Van Assche     for (vnp = t10_vpd_pg; vnp->acron; ++vnp) {
855*44704f69SBart Van Assche         if (0 == strcmp(vnp->acron, ap))
856*44704f69SBart Van Assche             return vnp;
857*44704f69SBart Van Assche     }
858*44704f69SBart Van Assche     for (vnp = alt_t10_vpd_pg; vnp->acron; ++vnp) {
859*44704f69SBart Van Assche         if (0 == strcmp(vnp->acron, ap))
860*44704f69SBart Van Assche             return vnp;
861*44704f69SBart Van Assche     }
862*44704f69SBart Van Assche     for (vnp = vs_vpd_pg; vnp->acron; ++vnp) {
863*44704f69SBart Van Assche         if (0 == strcmp(vnp->acron, ap))
864*44704f69SBart Van Assche             return vnp;
865*44704f69SBart Van Assche     }
866*44704f69SBart Van Assche     return NULL;
867*44704f69SBart Van Assche }
868*44704f69SBart Van Assche 
869*44704f69SBart Van Assche static void
enumerate_vpds()870*44704f69SBart Van Assche enumerate_vpds()
871*44704f69SBart Van Assche {
872*44704f69SBart Van Assche     const struct svpd_values_name_t * vnp;
873*44704f69SBart Van Assche 
874*44704f69SBart Van Assche     printf("T10 defined VPD pages:\n");
875*44704f69SBart Van Assche     for (vnp = t10_vpd_pg; vnp->acron; ++vnp) {
876*44704f69SBart Van Assche         if (vnp->name) {
877*44704f69SBart Van Assche             if (vnp->value < 0)
878*44704f69SBart Van Assche                 printf("  %-10s   -1      %s\n", vnp->acron, vnp->name);
879*44704f69SBart Van Assche             else
880*44704f69SBart Van Assche                 printf("  %-10s 0x%02x      %s\n", vnp->acron, vnp->value,
881*44704f69SBart Van Assche                        vnp->name);
882*44704f69SBart Van Assche         }
883*44704f69SBart Van Assche     }
884*44704f69SBart Van Assche     printf("Vendor specific VPD pages:\n");
885*44704f69SBart Van Assche     for (vnp = vs_vpd_pg; vnp->acron; ++vnp) {
886*44704f69SBart Van Assche         if (vnp->name) {
887*44704f69SBart Van Assche             if (vnp->value < 0)
888*44704f69SBart Van Assche                 printf("  %-10s   -1      %s\n", vnp->acron, vnp->name);
889*44704f69SBart Van Assche             else
890*44704f69SBart Van Assche                 printf("  %-10s 0x%02x      %s\n", vnp->acron, vnp->value,
891*44704f69SBart Van Assche                        vnp->name);
892*44704f69SBart Van Assche         }
893*44704f69SBart Van Assche     }
894*44704f69SBart Van Assche }
895*44704f69SBart Van Assche 
896*44704f69SBart Van Assche static void
dStrRaw(const char * str,int len)897*44704f69SBart Van Assche dStrRaw(const char * str, int len)
898*44704f69SBart Van Assche {
899*44704f69SBart Van Assche     int k;
900*44704f69SBart Van Assche 
901*44704f69SBart Van Assche     for (k = 0; k < len; ++k)
902*44704f69SBart Van Assche         printf("%c", str[k]);
903*44704f69SBart Van Assche }
904*44704f69SBart Van Assche 
905*44704f69SBart Van Assche /* Strip initial and trailing whitespaces; convert one or repeated
906*44704f69SBart Van Assche  * whitespaces to a single "_"; convert non-printable characters to "."
907*44704f69SBart Van Assche  * and if there are no valid (i.e. printable) characters return 0.
908*44704f69SBart Van Assche  * Process 'str' in place (i.e. it's input and output) and return the
909*44704f69SBart Van Assche  * length of the output, excluding the trailing '\0'. To cover any
910*44704f69SBart Van Assche  * potential unicode string an intermediate zero is skipped; two
911*44704f69SBart Van Assche  * consecutive zeroes indicate a string termination.
912*44704f69SBart Van Assche  */
913*44704f69SBart Van Assche static int
encode_whitespaces(uint8_t * str,int inlen)914*44704f69SBart Van Assche encode_whitespaces(uint8_t *str, int inlen)
915*44704f69SBart Van Assche {
916*44704f69SBart Van Assche     int k, res;
917*44704f69SBart Van Assche     int j;
918*44704f69SBart Van Assche     bool valid = false;
919*44704f69SBart Van Assche     int outlen = inlen, zeroes = 0;
920*44704f69SBart Van Assche 
921*44704f69SBart Van Assche     /* Skip initial whitespaces */
922*44704f69SBart Van Assche     for (j = 0; (j < inlen) && isblank(str[j]); ++j)
923*44704f69SBart Van Assche         ;
924*44704f69SBart Van Assche     if (j < inlen) {
925*44704f69SBart Van Assche         /* Skip possible unicode prefix characters */
926*44704f69SBart Van Assche         for ( ; (j < inlen) && (str[j] < 0x20); ++j)
927*44704f69SBart Van Assche             ;
928*44704f69SBart Van Assche     }
929*44704f69SBart Van Assche     k = j;
930*44704f69SBart Van Assche     /* Strip trailing whitespaces */
931*44704f69SBart Van Assche     while ((outlen > k) &&
932*44704f69SBart Van Assche            (isblank(str[outlen - 1]) || ('\0' == str[outlen - 1]))) {
933*44704f69SBart Van Assche         str[outlen - 1] = '\0';
934*44704f69SBart Van Assche         outlen--;
935*44704f69SBart Van Assche     }
936*44704f69SBart Van Assche     for (res = 0; k < outlen; ++k) {
937*44704f69SBart Van Assche         if (isblank(str[k])) {
938*44704f69SBart Van Assche             if ((res > 0) && ('_' != str[res - 1])) {
939*44704f69SBart Van Assche                 str[res++] = '_';
940*44704f69SBart Van Assche                 valid = true;
941*44704f69SBart Van Assche             }
942*44704f69SBart Van Assche             zeroes = 0;
943*44704f69SBart Van Assche         } else if (! isprint(str[k])) {
944*44704f69SBart Van Assche             if (str[k] == 0x00) {
945*44704f69SBart Van Assche                 /* Stop on more than one consecutive zero */
946*44704f69SBart Van Assche                 if (zeroes)
947*44704f69SBart Van Assche                     break;
948*44704f69SBart Van Assche                 zeroes++;
949*44704f69SBart Van Assche                 continue;
950*44704f69SBart Van Assche             }
951*44704f69SBart Van Assche             str[res++] = '.';
952*44704f69SBart Van Assche             zeroes = 0;
953*44704f69SBart Van Assche         } else {
954*44704f69SBart Van Assche             str[res++] = str[k];
955*44704f69SBart Van Assche             valid = true;
956*44704f69SBart Van Assche             zeroes = 0;
957*44704f69SBart Van Assche         }
958*44704f69SBart Van Assche     }
959*44704f69SBart Van Assche     if (! valid)
960*44704f69SBart Van Assche         res = 0;
961*44704f69SBart Van Assche     if (res < inlen)
962*44704f69SBart Van Assche         str[res] = '\0';
963*44704f69SBart Van Assche     return res;
964*44704f69SBart Van Assche }
965*44704f69SBart Van Assche 
966*44704f69SBart Van Assche static int
encode_unicode(uint8_t * str,int inlen)967*44704f69SBart Van Assche encode_unicode(uint8_t *str, int inlen)
968*44704f69SBart Van Assche {
969*44704f69SBart Van Assche     int k = 0, res;
970*44704f69SBart Van Assche     int zeroes = 0;
971*44704f69SBart Van Assche 
972*44704f69SBart Van Assche     for (res = 0; k < inlen; ++k) {
973*44704f69SBart Van Assche         if (str[k] == 0x00) {
974*44704f69SBart Van Assche             if (zeroes) {
975*44704f69SBart Van Assche                 str[res++] = '\0';
976*44704f69SBart Van Assche                 break;
977*44704f69SBart Van Assche             }
978*44704f69SBart Van Assche             zeroes++;
979*44704f69SBart Van Assche         } else {
980*44704f69SBart Van Assche             zeroes = 0;
981*44704f69SBart Van Assche             if (isprint(str[k]))
982*44704f69SBart Van Assche                 str[res++] = str[k];
983*44704f69SBart Van Assche             else
984*44704f69SBart Van Assche                 str[res++] = ' ';
985*44704f69SBart Van Assche         }
986*44704f69SBart Van Assche     }
987*44704f69SBart Van Assche 
988*44704f69SBart Van Assche     return res;
989*44704f69SBart Van Assche }
990*44704f69SBart Van Assche 
991*44704f69SBart Van Assche static int
encode_string(char * out,const uint8_t * in,int inlen)992*44704f69SBart Van Assche encode_string(char *out, const uint8_t *in, int inlen)
993*44704f69SBart Van Assche {
994*44704f69SBart Van Assche     int i, j = 0;
995*44704f69SBart Van Assche 
996*44704f69SBart Van Assche     for (i = 0; (i < inlen); ++i) {
997*44704f69SBart Van Assche         if (isblank(in[i]) || !isprint(in[i])) {
998*44704f69SBart Van Assche             sprintf(&out[j], "\\x%02x", in[i]);
999*44704f69SBart Van Assche             j += 4;
1000*44704f69SBart Van Assche         } else {
1001*44704f69SBart Van Assche             out[j] = in[i];
1002*44704f69SBart Van Assche             j++;
1003*44704f69SBart Van Assche         }
1004*44704f69SBart Van Assche     }
1005*44704f69SBart Van Assche     out[j] = '\0';
1006*44704f69SBart Van Assche     return j;
1007*44704f69SBart Van Assche }
1008*44704f69SBart Van Assche 
1009*44704f69SBart Van Assche static const struct svpd_values_name_t *
get_vpd_page_info(int vpd_page_num,int dev_pdt)1010*44704f69SBart Van Assche get_vpd_page_info(int vpd_page_num, int dev_pdt)
1011*44704f69SBart Van Assche {
1012*44704f69SBart Van Assche     int decay_pdt;
1013*44704f69SBart Van Assche     const struct svpd_values_name_t * vnp;
1014*44704f69SBart Van Assche     const struct svpd_values_name_t * prev_vnp;
1015*44704f69SBart Van Assche 
1016*44704f69SBart Van Assche     if (vpd_page_num < 0xb0) {  /* take T10 first match */
1017*44704f69SBart Van Assche         for (vnp = t10_vpd_pg; vnp->acron; ++vnp) {
1018*44704f69SBart Van Assche             if (vnp->value == vpd_page_num)
1019*44704f69SBart Van Assche                 return vnp;
1020*44704f69SBart Van Assche         }
1021*44704f69SBart Van Assche         return NULL;
1022*44704f69SBart Van Assche     } else if (vpd_page_num < 0xc0) {
1023*44704f69SBart Van Assche         for (vnp = t10_vpd_pg; vnp->acron; ++vnp) {
1024*44704f69SBart Van Assche             if (vnp->value == vpd_page_num)
1025*44704f69SBart Van Assche                 break;
1026*44704f69SBart Van Assche         }
1027*44704f69SBart Van Assche         if (NULL == vnp->acron)
1028*44704f69SBart Van Assche             return NULL;
1029*44704f69SBart Van Assche         if (vnp->pdt == dev_pdt)        /* exact match */
1030*44704f69SBart Van Assche             return vnp;
1031*44704f69SBart Van Assche         prev_vnp = vnp;
1032*44704f69SBart Van Assche 
1033*44704f69SBart Van Assche         for (++vnp; vnp->acron; ++vnp) {
1034*44704f69SBart Van Assche             if (vnp->value == vpd_page_num)
1035*44704f69SBart Van Assche                 break;
1036*44704f69SBart Van Assche         }
1037*44704f69SBart Van Assche         decay_pdt = sg_lib_pdt_decay(dev_pdt);
1038*44704f69SBart Van Assche         if (NULL == vnp->acron) {
1039*44704f69SBart Van Assche             if (decay_pdt == prev_vnp->pdt)
1040*44704f69SBart Van Assche                 return prev_vnp;
1041*44704f69SBart Van Assche             return NULL;
1042*44704f69SBart Van Assche         }
1043*44704f69SBart Van Assche         if ((vnp->pdt == dev_pdt) || (vnp->pdt == decay_pdt))
1044*44704f69SBart Van Assche             return vnp;
1045*44704f69SBart Van Assche         if (decay_pdt == prev_vnp->pdt)
1046*44704f69SBart Van Assche             return prev_vnp;
1047*44704f69SBart Van Assche 
1048*44704f69SBart Van Assche         for (++vnp; vnp->acron; ++vnp) {
1049*44704f69SBart Van Assche             if (vnp->value == vpd_page_num)
1050*44704f69SBart Van Assche                 break;
1051*44704f69SBart Van Assche         }
1052*44704f69SBart Van Assche         if (NULL == vnp->acron)
1053*44704f69SBart Van Assche             return NULL;
1054*44704f69SBart Van Assche         if ((vnp->pdt == dev_pdt) || (vnp->pdt == decay_pdt))
1055*44704f69SBart Van Assche             return vnp;
1056*44704f69SBart Van Assche         return NULL;            /* give up */
1057*44704f69SBart Van Assche     } else {    /* vendor specific: vpd >= 0xc0 */
1058*44704f69SBart Van Assche         for (vnp = vs_vpd_pg; vnp->acron; ++vnp) {
1059*44704f69SBart Van Assche             if (vnp->pdt == dev_pdt)
1060*44704f69SBart Van Assche                 return vnp;
1061*44704f69SBart Van Assche         }
1062*44704f69SBart Van Assche         return NULL;
1063*44704f69SBart Van Assche     }
1064*44704f69SBart Van Assche }
1065*44704f69SBart Van Assche 
1066*44704f69SBart Van Assche static int
svpd_inhex_decode_all(struct opts_t * op,sgj_opaque_p jop)1067*44704f69SBart Van Assche svpd_inhex_decode_all(struct opts_t * op, sgj_opaque_p jop)
1068*44704f69SBart Van Assche {
1069*44704f69SBart Van Assche     int k, res, pn;
1070*44704f69SBart Van Assche     int max_pn = 255;
1071*44704f69SBart Van Assche     int bump, off;
1072*44704f69SBart Van Assche     int in_len = op->maxlen;
1073*44704f69SBart Van Assche     int prev_pn = -1;
1074*44704f69SBart Van Assche     sgj_state * jsp = &op->json_st;
1075*44704f69SBart Van Assche     uint8_t vpd0_buff[512];
1076*44704f69SBart Van Assche     uint8_t * rp = vpd0_buff;
1077*44704f69SBart Van Assche 
1078*44704f69SBart Van Assche     if (op->vpd_pn > 0)
1079*44704f69SBart Van Assche         max_pn = op->vpd_pn;
1080*44704f69SBart Van Assche 
1081*44704f69SBart Van Assche     res = 0;
1082*44704f69SBart Van Assche     if (op->page_given && (VPD_NOPE_WANT_STD_INQ == op->vpd_pn))
1083*44704f69SBart Van Assche         return vpd_decode(-1, op, jop, 0);
1084*44704f69SBart Van Assche 
1085*44704f69SBart Van Assche     for (k = 0, off = 0; off < in_len; ++k, off += bump) {
1086*44704f69SBart Van Assche         rp = rsp_buff + off;
1087*44704f69SBart Van Assche         pn = rp[1];
1088*44704f69SBart Van Assche         bump = sg_get_unaligned_be16(rp + 2) + 4;
1089*44704f69SBart Van Assche         if ((off + bump) > in_len) {
1090*44704f69SBart Van Assche             pr2serr("%s: page 0x%x size (%d) exceeds buffer\n", __func__,
1091*44704f69SBart Van Assche                     pn, bump);
1092*44704f69SBart Van Assche             bump = in_len - off;
1093*44704f69SBart Van Assche         }
1094*44704f69SBart Van Assche         if (op->page_given && (pn != op->vpd_pn))
1095*44704f69SBart Van Assche             continue;
1096*44704f69SBart Van Assche         if (pn <= prev_pn) {
1097*44704f69SBart Van Assche             pr2serr("%s: prev_pn=0x%x, this pn=0x%x, not ascending so "
1098*44704f69SBart Van Assche                     "exit\n", __func__, prev_pn, pn);
1099*44704f69SBart Van Assche             break;
1100*44704f69SBart Van Assche         }
1101*44704f69SBart Van Assche         prev_pn = pn;
1102*44704f69SBart Van Assche         op->vpd_pn = pn;
1103*44704f69SBart Van Assche         if (pn > max_pn) {
1104*44704f69SBart Van Assche             if (op->verbose > 2)
1105*44704f69SBart Van Assche                 pr2serr("%s: skipping as this pn=0x%x exceeds "
1106*44704f69SBart Van Assche                         "max_pn=0x%x\n", __func__, pn, max_pn);
1107*44704f69SBart Van Assche             continue;
1108*44704f69SBart Van Assche         }
1109*44704f69SBart Van Assche         if (op->do_long) {
1110*44704f69SBart Van Assche             if (jsp->pr_as_json)
1111*44704f69SBart Van Assche                sgj_pr_hr(jsp, "[0x%x]:\n", pn);
1112*44704f69SBart Van Assche             else
1113*44704f69SBart Van Assche                sgj_pr_hr(jsp, "[0x%x] ", pn);
1114*44704f69SBart Van Assche         }
1115*44704f69SBart Van Assche 
1116*44704f69SBart Van Assche         res = vpd_decode(-1, op, jop, off);
1117*44704f69SBart Van Assche         if (SG_LIB_CAT_OTHER == res) {
1118*44704f69SBart Van Assche             if (op->verbose)
1119*44704f69SBart Van Assche                 pr2serr("Can't decode VPD page=0x%x\n", pn);
1120*44704f69SBart Van Assche         }
1121*44704f69SBart Van Assche     }
1122*44704f69SBart Van Assche     return res;
1123*44704f69SBart Van Assche }
1124*44704f69SBart Van Assche 
1125*44704f69SBart Van Assche static void
decode_supported_vpd_4inq(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)1126*44704f69SBart Van Assche decode_supported_vpd_4inq(uint8_t * buff, int len, struct opts_t * op,
1127*44704f69SBart Van Assche                           sgj_opaque_p jap)
1128*44704f69SBart Van Assche {
1129*44704f69SBart Van Assche     int vpd, k, rlen, pdt;
1130*44704f69SBart Van Assche     sgj_state * jsp = &op->json_st;
1131*44704f69SBart Van Assche     sgj_opaque_p jo2p;
1132*44704f69SBart Van Assche     const struct svpd_values_name_t * vnp;
1133*44704f69SBart Van Assche     char b[64];
1134*44704f69SBart Van Assche 
1135*44704f69SBart Van Assche     if (op->do_hex) {
1136*44704f69SBart Van Assche         hex2stdout(buff, len, no_ascii_4hex(op));
1137*44704f69SBart Van Assche         return;
1138*44704f69SBart Van Assche     }
1139*44704f69SBart Van Assche     if (len < 4) {
1140*44704f69SBart Van Assche         pr2serr("Supported VPD pages VPD page length too short=%d\n", len);
1141*44704f69SBart Van Assche         return;
1142*44704f69SBart Van Assche     }
1143*44704f69SBart Van Assche     pdt = PDT_MASK & buff[0];
1144*44704f69SBart Van Assche     rlen = buff[3] + 4;
1145*44704f69SBart Van Assche     if (rlen > len)
1146*44704f69SBart Van Assche         pr2serr("Supported VPD pages VPD page truncated, indicates %d, got "
1147*44704f69SBart Van Assche                 "%d\n", rlen, len);
1148*44704f69SBart Van Assche     else
1149*44704f69SBart Van Assche         len = rlen;
1150*44704f69SBart Van Assche     sgj_pr_hr(jsp, "   Supported VPD pages:\n");
1151*44704f69SBart Van Assche     for (k = 0; k < len - 4; ++k) {
1152*44704f69SBart Van Assche         vpd = buff[4 + k];
1153*44704f69SBart Van Assche         snprintf(b, sizeof(b), "0x%x", vpd);
1154*44704f69SBart Van Assche         vnp = get_vpd_page_info(vpd, pdt);
1155*44704f69SBart Van Assche         if (jsp->pr_as_json && jap) {
1156*44704f69SBart Van Assche             jo2p = sgj_new_unattached_object_r(jsp);
1157*44704f69SBart Van Assche             sgj_js_nv_i(jsp, jo2p, "i", vpd);
1158*44704f69SBart Van Assche             sgj_js_nv_s(jsp, jo2p, "hex", b + 2);
1159*44704f69SBart Van Assche             sgj_js_nv_s(jsp, jo2p, "name", vnp ? vnp->name : "unknown");
1160*44704f69SBart Van Assche             sgj_js_nv_s(jsp, jo2p, "acronym", vnp ? vnp->acron : "unknown");
1161*44704f69SBart Van Assche             sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
1162*44704f69SBart Van Assche         }
1163*44704f69SBart Van Assche         if (vnp)
1164*44704f69SBart Van Assche             sgj_pr_hr(jsp, "     %s\t%s\n", b, vnp->name);
1165*44704f69SBart Van Assche         else
1166*44704f69SBart Van Assche             sgj_pr_hr(jsp, "     %s\n", b);
1167*44704f69SBart Van Assche     }
1168*44704f69SBart Van Assche }
1169*44704f69SBart Van Assche 
1170*44704f69SBart Van Assche static bool
vpd_page_is_supported(uint8_t * vpd_pg0,int v0_len,int pg_num,int vb)1171*44704f69SBart Van Assche vpd_page_is_supported(uint8_t * vpd_pg0, int v0_len, int pg_num, int vb)
1172*44704f69SBart Van Assche {
1173*44704f69SBart Van Assche     int k, rlen;
1174*44704f69SBart Van Assche 
1175*44704f69SBart Van Assche     if (v0_len < 4)
1176*44704f69SBart Van Assche         return false;
1177*44704f69SBart Van Assche 
1178*44704f69SBart Van Assche     rlen = vpd_pg0[3] + 4;
1179*44704f69SBart Van Assche     if (rlen > v0_len)
1180*44704f69SBart Van Assche         pr2serr("Supported VPD pages VPD page truncated, indicates %d, got "
1181*44704f69SBart Van Assche                 "%d\n", rlen, v0_len);
1182*44704f69SBart Van Assche     else
1183*44704f69SBart Van Assche         v0_len = rlen;
1184*44704f69SBart Van Assche     if (vb > 1) {
1185*44704f69SBart Van Assche         pr2serr("Supported VPD pages, hex list: ");
1186*44704f69SBart Van Assche         hex2stderr(vpd_pg0 + 4, v0_len - 4, -1);
1187*44704f69SBart Van Assche     }
1188*44704f69SBart Van Assche     for (k = 4; k < v0_len; ++k) {
1189*44704f69SBart Van Assche         if(vpd_pg0[k] == pg_num)
1190*44704f69SBart Van Assche             return true;
1191*44704f69SBart Van Assche     }
1192*44704f69SBart Van Assche     return false;
1193*44704f69SBart Van Assche }
1194*44704f69SBart Van Assche 
1195*44704f69SBart Van Assche /* ASCII Information VPD pages (page numbers: 0x1 to 0x7f) */
1196*44704f69SBart Van Assche static void
decode_ascii_inf(uint8_t * buff,int len,struct opts_t * op)1197*44704f69SBart Van Assche decode_ascii_inf(uint8_t * buff, int len, struct opts_t * op)
1198*44704f69SBart Van Assche {
1199*44704f69SBart Van Assche     int al, k, bump;
1200*44704f69SBart Van Assche     uint8_t * bp;
1201*44704f69SBart Van Assche     uint8_t * p;
1202*44704f69SBart Van Assche     sgj_state * jsp = &op->json_st;
1203*44704f69SBart Van Assche 
1204*44704f69SBart Van Assche     if (op->do_hex) {
1205*44704f69SBart Van Assche         hex2stdout(buff, len, no_ascii_4hex(op));
1206*44704f69SBart Van Assche         return;
1207*44704f69SBart Van Assche     }
1208*44704f69SBart Van Assche     if (len < 4) {
1209*44704f69SBart Van Assche         pr2serr("ASCII information VPD page length too short=%d\n", len);
1210*44704f69SBart Van Assche         return;
1211*44704f69SBart Van Assche     }
1212*44704f69SBart Van Assche     if (4 == len)
1213*44704f69SBart Van Assche         return;
1214*44704f69SBart Van Assche     al = buff[4];
1215*44704f69SBart Van Assche     if ((al + 5) > len)
1216*44704f69SBart Van Assche         al = len - 5;
1217*44704f69SBart Van Assche     for (k = 0, bp = buff + 5; k < al; k += bump, bp += bump) {
1218*44704f69SBart Van Assche         p = (uint8_t *)memchr(bp, 0, al - k);
1219*44704f69SBart Van Assche         if (! p) {
1220*44704f69SBart Van Assche             sgj_pr_hr(jsp, "  %.*s\n", al - k, (const char *)bp);
1221*44704f69SBart Van Assche             break;
1222*44704f69SBart Van Assche         }
1223*44704f69SBart Van Assche         sgj_pr_hr(jsp, "  %s\n", (const char *)bp);
1224*44704f69SBart Van Assche         bump = (p - bp) + 1;
1225*44704f69SBart Van Assche     }
1226*44704f69SBart Van Assche     bp = buff + 5 + al;
1227*44704f69SBart Van Assche     if (bp < (buff + len)) {
1228*44704f69SBart Van Assche         sgj_pr_hr(jsp, "Vendor specific information in hex:\n");
1229*44704f69SBart Van Assche         hex2stdout(bp, len - (al + 5), 0);
1230*44704f69SBart Van Assche     }
1231*44704f69SBart Van Assche }
1232*44704f69SBart Van Assche 
1233*44704f69SBart Van Assche static void
decode_id_vpd(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)1234*44704f69SBart Van Assche decode_id_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jap)
1235*44704f69SBart Van Assche {
1236*44704f69SBart Van Assche     if (len < 4) {
1237*44704f69SBart Van Assche         pr2serr("Device identification VPD page length too "
1238*44704f69SBart Van Assche                 "short=%d\n", len);
1239*44704f69SBart Van Assche         return;
1240*44704f69SBart Van Assche     }
1241*44704f69SBart Van Assche     decode_dev_ids("Device identification", buff + 4, len - 4, op, jap);
1242*44704f69SBart Van Assche }
1243*44704f69SBart Van Assche 
1244*44704f69SBart Van Assche /* VPD_SCSI_PORTS   0x88  ["sp"] */
1245*44704f69SBart Van Assche static void
decode_scsi_ports_vpd_4inq(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)1246*44704f69SBart Van Assche decode_scsi_ports_vpd_4inq(uint8_t * buff, int len, struct opts_t * op,
1247*44704f69SBart Van Assche                            sgj_opaque_p jap)
1248*44704f69SBart Van Assche {
1249*44704f69SBart Van Assche     int k, bump, rel_port, ip_tid_len, tpd_len;
1250*44704f69SBart Van Assche     uint8_t * bp;
1251*44704f69SBart Van Assche     sgj_state * jsp = &op->json_st;
1252*44704f69SBart Van Assche     sgj_opaque_p jo2p;
1253*44704f69SBart Van Assche 
1254*44704f69SBart Van Assche     if (len < 4) {
1255*44704f69SBart Van Assche         pr2serr("SCSI Ports VPD page length too short=%d\n", len);
1256*44704f69SBart Van Assche         return;
1257*44704f69SBart Van Assche     }
1258*44704f69SBart Van Assche     if (op->do_hex > 2) {
1259*44704f69SBart Van Assche         hex2stdout(buff, len, -1);
1260*44704f69SBart Van Assche         return;
1261*44704f69SBart Van Assche     }
1262*44704f69SBart Van Assche     len -= 4;
1263*44704f69SBart Van Assche     bp = buff + 4;
1264*44704f69SBart Van Assche     for (k = 0; k < len; k += bump, bp += bump) {
1265*44704f69SBart Van Assche         jo2p = sgj_new_unattached_object_r(jsp);
1266*44704f69SBart Van Assche         rel_port = sg_get_unaligned_be16(bp + 2);
1267*44704f69SBart Van Assche         sgj_pr_hr(jsp, "Relative port=%d\n", rel_port);
1268*44704f69SBart Van Assche         sgj_js_nv_i(jsp, jo2p, "relative_port", rel_port);
1269*44704f69SBart Van Assche         ip_tid_len = sg_get_unaligned_be16(bp + 6);
1270*44704f69SBart Van Assche         bump = 8 + ip_tid_len;
1271*44704f69SBart Van Assche         if ((k + bump) > len) {
1272*44704f69SBart Van Assche             pr2serr("SCSI Ports VPD page, short descriptor "
1273*44704f69SBart Van Assche                     "length=%d, left=%d\n", bump, (len - k));
1274*44704f69SBart Van Assche             sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
1275*44704f69SBart Van Assche             return;
1276*44704f69SBart Van Assche         }
1277*44704f69SBart Van Assche         if (ip_tid_len > 0) {
1278*44704f69SBart Van Assche             if (op->do_hex) {
1279*44704f69SBart Van Assche                 printf(" Initiator port transport id:\n");
1280*44704f69SBart Van Assche                 hex2stdout((bp + 8), ip_tid_len, no_ascii_4hex(op));
1281*44704f69SBart Van Assche             } else {
1282*44704f69SBart Van Assche                 char b[1024];
1283*44704f69SBart Van Assche 
1284*44704f69SBart Van Assche                 sg_decode_transportid_str("    ", bp + 8, ip_tid_len,
1285*44704f69SBart Van Assche                                           true, sizeof(b), b);
1286*44704f69SBart Van Assche                 if (jsp->pr_as_json)
1287*44704f69SBart Van Assche                     sgj_js_nv_s(jsp, jo2p, "initiator_port_transport_id", b);
1288*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "%s",
1289*44704f69SBart Van Assche                           sg_decode_transportid_str("    ", bp + 8,
1290*44704f69SBart Van Assche                                             ip_tid_len, true, sizeof(b), b));
1291*44704f69SBart Van Assche             }
1292*44704f69SBart Van Assche         }
1293*44704f69SBart Van Assche         tpd_len = sg_get_unaligned_be16(bp + bump + 2);
1294*44704f69SBart Van Assche         if ((k + bump + tpd_len + 4) > len) {
1295*44704f69SBart Van Assche             pr2serr("SCSI Ports VPD page, short descriptor(tgt) "
1296*44704f69SBart Van Assche                     "length=%d, left=%d\n", bump, (len - k));
1297*44704f69SBart Van Assche             sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
1298*44704f69SBart Van Assche             return;
1299*44704f69SBart Van Assche         }
1300*44704f69SBart Van Assche         if (tpd_len > 0) {
1301*44704f69SBart Van Assche             sgj_pr_hr(jsp, " Target port descriptor(s):\n");
1302*44704f69SBart Van Assche             if (op->do_hex)
1303*44704f69SBart Van Assche                 hex2stdout(bp + bump + 4, tpd_len, no_ascii_4hex(op));
1304*44704f69SBart Van Assche             else {
1305*44704f69SBart Van Assche                 sgj_opaque_p ja2p = sgj_named_subarray_r(jsp, jo2p,
1306*44704f69SBart Van Assche                                         "target_port_descriptor_list");
1307*44704f69SBart Van Assche 
1308*44704f69SBart Van Assche                 decode_dev_ids("SCSI Ports", bp + bump + 4, tpd_len,
1309*44704f69SBart Van Assche                                op, ja2p);
1310*44704f69SBart Van Assche             }
1311*44704f69SBart Van Assche         }
1312*44704f69SBart Van Assche         bump += tpd_len + 4;
1313*44704f69SBart Van Assche         sgj_js_nv_o(jsp, jap, NULL /* name */, jo2p);
1314*44704f69SBart Van Assche     }
1315*44704f69SBart Van Assche }
1316*44704f69SBart Van Assche 
1317*44704f69SBart Van Assche /* These are target port, device server (i.e. target) and LU identifiers */
1318*44704f69SBart Van Assche static void
decode_dev_ids(const char * leadin,uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jap)1319*44704f69SBart Van Assche decode_dev_ids(const char * leadin, uint8_t * buff, int len,
1320*44704f69SBart Van Assche                struct opts_t * op, sgj_opaque_p jap)
1321*44704f69SBart Van Assche {
1322*44704f69SBart Van Assche     int u, j, m, id_len, p_id, c_set, piv, assoc, desig_type, i_len;
1323*44704f69SBart Van Assche     int off, ci_off, c_id, d_id, naa, vsi, k, n;
1324*44704f69SBart Van Assche     uint64_t vsei, id_ext, ccc_id;
1325*44704f69SBart Van Assche     const uint8_t * bp;
1326*44704f69SBart Van Assche     const uint8_t * ip;
1327*44704f69SBart Van Assche     const char * cp;
1328*44704f69SBart Van Assche     sgj_state * jsp = &op->json_st;
1329*44704f69SBart Van Assche     char b[256];
1330*44704f69SBart Van Assche     char d[64];
1331*44704f69SBart Van Assche     static const int blen = sizeof(b);
1332*44704f69SBart Van Assche     static const int dlen = sizeof(d);
1333*44704f69SBart Van Assche 
1334*44704f69SBart Van Assche     if (jsp->pr_as_json) {
1335*44704f69SBart Van Assche         int ret = filter_json_dev_ids(buff, len, -1, op, jap);
1336*44704f69SBart Van Assche 
1337*44704f69SBart Van Assche         if (ret || (! jsp->pr_out_hr))
1338*44704f69SBart Van Assche             return;
1339*44704f69SBart Van Assche     }
1340*44704f69SBart Van Assche     if (buff[2] > 2) {  /* SPC-3,4,5 buff[2] is upper byte of length */
1341*44704f69SBart Van Assche         /*
1342*44704f69SBart Van Assche          * Reference the 3rd byte of the first Identification descriptor
1343*44704f69SBart Van Assche          * of a page 83 reply to determine whether the reply is compliant
1344*44704f69SBart Van Assche          * with SCSI-2 or SPC-2/3 specifications.  A zero value in the
1345*44704f69SBart Van Assche          * 3rd byte indicates an SPC-2/3 conforming reply ( the field is
1346*44704f69SBart Van Assche          * reserved ).  This byte will be non-zero for a SCSI-2
1347*44704f69SBart Van Assche          * conforming page 83 reply from these EMC Symmetrix models since
1348*44704f69SBart Van Assche          * the 7th byte of the reply corresponds to the 4th and 5th
1349*44704f69SBart Van Assche          * nibbles of the 6-byte OUI for EMC, that is, 0x006048.
1350*44704f69SBart Van Assche          */
1351*44704f69SBart Van Assche         i_len = len;
1352*44704f69SBart Van Assche         ip = bp = buff;
1353*44704f69SBart Van Assche         c_set = 1;
1354*44704f69SBart Van Assche         assoc = 0;
1355*44704f69SBart Van Assche         piv = 0;
1356*44704f69SBart Van Assche         p_id = 0xf;
1357*44704f69SBart Van Assche         desig_type = 3;
1358*44704f69SBart Van Assche         j = 1;
1359*44704f69SBart Van Assche         off = 16;
1360*44704f69SBart Van Assche         sgj_pr_hr(jsp, "  Pre-SPC descriptor, descriptor length: %d\n",
1361*44704f69SBart Van Assche                   i_len);
1362*44704f69SBart Van Assche         goto decode;
1363*44704f69SBart Van Assche     }
1364*44704f69SBart Van Assche 
1365*44704f69SBart Van Assche     for (j = 1, off = -1;
1366*44704f69SBart Van Assche          (u = sg_vpd_dev_id_iter(buff, len, &off, -1, -1, -1)) == 0;
1367*44704f69SBart Van Assche          ++j) {
1368*44704f69SBart Van Assche         bp = buff + off;
1369*44704f69SBart Van Assche         i_len = bp[3];
1370*44704f69SBart Van Assche         id_len = i_len + 4;
1371*44704f69SBart Van Assche         sgj_pr_hr(jsp, "  Designation descriptor number %d, "
1372*44704f69SBart Van Assche                   "descriptor length: %d\n", j, id_len);
1373*44704f69SBart Van Assche         if ((off + id_len) > len) {
1374*44704f69SBart Van Assche             pr2serr("%s VPD page error: designator length longer "
1375*44704f69SBart Van Assche                     "than\n     remaining response length=%d\n", leadin,
1376*44704f69SBart Van Assche                     (len - off));
1377*44704f69SBart Van Assche             return;
1378*44704f69SBart Van Assche         }
1379*44704f69SBart Van Assche         ip = bp + 4;
1380*44704f69SBart Van Assche         p_id = ((bp[0] >> 4) & 0xf);   /* protocol identifier */
1381*44704f69SBart Van Assche         c_set = (bp[0] & 0xf);         /* code set */
1382*44704f69SBart Van Assche         piv = ((bp[1] & 0x80) ? 1 : 0); /* protocol identifier valid */
1383*44704f69SBart Van Assche         assoc = ((bp[1] >> 4) & 0x3);
1384*44704f69SBart Van Assche         desig_type = (bp[1] & 0xf);
1385*44704f69SBart Van Assche   decode:
1386*44704f69SBart Van Assche         if (piv && ((1 == assoc) || (2 == assoc)))
1387*44704f69SBart Van Assche             sgj_pr_hr(jsp, "    transport: %s\n",
1388*44704f69SBart Van Assche                       sg_get_trans_proto_str(p_id, dlen, d));
1389*44704f69SBart Van Assche         n = 0;
1390*44704f69SBart Van Assche         cp = sg_get_desig_type_str(desig_type);
1391*44704f69SBart Van Assche         n += sg_scnpr(b + n, blen - n, "    designator_type: %s,  ",
1392*44704f69SBart Van Assche                       cp ? cp : "-");
1393*44704f69SBart Van Assche         cp = sg_get_desig_code_set_str(c_set);
1394*44704f69SBart Van Assche         sgj_pr_hr(jsp, "%scode_set: %s\n", b, cp ? cp : "-");
1395*44704f69SBart Van Assche         cp = sg_get_desig_assoc_str(assoc);
1396*44704f69SBart Van Assche         sgj_pr_hr(jsp, "    associated with the %s\n", cp ? cp : "-");
1397*44704f69SBart Van Assche         if (op->do_hex) {
1398*44704f69SBart Van Assche             sgj_pr_hr(jsp, "    designator header(hex): %.2x %.2x %.2x %.2x\n",
1399*44704f69SBart Van Assche                    bp[0], bp[1], bp[2], bp[3]);
1400*44704f69SBart Van Assche             sgj_pr_hr(jsp, "    designator:\n");
1401*44704f69SBart Van Assche             hex2stdout(ip, i_len, 0);
1402*44704f69SBart Van Assche             continue;
1403*44704f69SBart Van Assche         }
1404*44704f69SBart Van Assche         switch (desig_type) {
1405*44704f69SBart Van Assche         case 0: /* vendor specific */
1406*44704f69SBart Van Assche             k = 0;
1407*44704f69SBart Van Assche             if ((2 == c_set) || (3 == c_set)) { /* ASCII or UTF-8 */
1408*44704f69SBart Van Assche                 for (k = 0; (k < i_len) && isprint(ip[k]); ++k)
1409*44704f69SBart Van Assche                     ;
1410*44704f69SBart Van Assche                 if (k >= i_len)
1411*44704f69SBart Van Assche                     k = 1;
1412*44704f69SBart Van Assche             }
1413*44704f69SBart Van Assche             if (k)
1414*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "      vendor specific: %.*s\n", i_len, ip);
1415*44704f69SBart Van Assche             else {
1416*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "      vendor specific:\n");
1417*44704f69SBart Van Assche                 hex2stdout(ip, i_len, -1);
1418*44704f69SBart Van Assche             }
1419*44704f69SBart Van Assche             break;
1420*44704f69SBart Van Assche         case 1: /* T10 vendor identification */
1421*44704f69SBart Van Assche             sgj_pr_hr(jsp, "      vendor id: %.8s\n", ip);
1422*44704f69SBart Van Assche             if (i_len > 8) {
1423*44704f69SBart Van Assche                 if ((2 == c_set) || (3 == c_set)) { /* ASCII or UTF-8 */
1424*44704f69SBart Van Assche                     sgj_pr_hr(jsp, "      vendor specific: %.*s\n", i_len - 8,
1425*44704f69SBart Van Assche                               ip + 8);
1426*44704f69SBart Van Assche                 } else {
1427*44704f69SBart Van Assche                     n = 0;
1428*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n,
1429*44704f69SBart Van Assche                                   "      vendor specific: 0x");
1430*44704f69SBart Van Assche                     for (m = 8; m < i_len; ++m)
1431*44704f69SBart Van Assche                         n += sg_scnpr(b + n, blen - n, "%02x", ip[m]);
1432*44704f69SBart Van Assche                     sgj_pr_hr(jsp, "%s\n", b);
1433*44704f69SBart Van Assche                 }
1434*44704f69SBart Van Assche             }
1435*44704f69SBart Van Assche             break;
1436*44704f69SBart Van Assche         case 2: /* EUI-64 based */
1437*44704f69SBart Van Assche             sgj_pr_hr(jsp, "      EUI-64 based %d byte identifier\n", i_len);
1438*44704f69SBart Van Assche             if (1 != c_set) {
1439*44704f69SBart Van Assche                 pr2serr("      << expected binary code_set (1)>>\n");
1440*44704f69SBart Van Assche                 hex2stderr(ip, i_len, -1);
1441*44704f69SBart Van Assche                 break;
1442*44704f69SBart Van Assche             }
1443*44704f69SBart Van Assche             ci_off = 0;
1444*44704f69SBart Van Assche             n = 0;
1445*44704f69SBart Van Assche             b[0] = '\0';
1446*44704f69SBart Van Assche             if (16 == i_len) {
1447*44704f69SBart Van Assche                 ci_off = 8;
1448*44704f69SBart Van Assche                 id_ext = sg_get_unaligned_be64(ip);
1449*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n,
1450*44704f69SBart Van Assche                               "      Identifier extension: 0x%" PRIx64 "\n",
1451*44704f69SBart Van Assche                               id_ext);
1452*44704f69SBart Van Assche             } else if ((8 != i_len) && (12 != i_len)) {
1453*44704f69SBart Van Assche                 pr2serr("      << can only decode 8, 12 and 16 "
1454*44704f69SBart Van Assche                         "byte ids>>\n");
1455*44704f69SBart Van Assche                 hex2stderr(ip, i_len, -1);
1456*44704f69SBart Van Assche                 break;
1457*44704f69SBart Van Assche             }
1458*44704f69SBart Van Assche             ccc_id = sg_get_unaligned_be64(ip + ci_off);
1459*44704f69SBart Van Assche             sgj_pr_hr(jsp, "%s      IEEE identifier: 0x%" PRIx64 "\n", b,
1460*44704f69SBart Van Assche                       ccc_id);
1461*44704f69SBart Van Assche             if (12 == i_len) {
1462*44704f69SBart Van Assche                 d_id = sg_get_unaligned_be32(ip + 8);
1463*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "      Directory ID: 0x%x\n", d_id);
1464*44704f69SBart Van Assche             }
1465*44704f69SBart Van Assche             n = 0;
1466*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "      [0x");
1467*44704f69SBart Van Assche             for (m = 0; m < i_len; ++m)
1468*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%02x", ip[m]);
1469*44704f69SBart Van Assche             sgj_pr_hr(jsp, "%s]\n", b);
1470*44704f69SBart Van Assche             break;
1471*44704f69SBart Van Assche         case 3: /* NAA <n> */
1472*44704f69SBart Van Assche             naa = (ip[0] >> 4) & 0xff;
1473*44704f69SBart Van Assche             if (1 != c_set) {
1474*44704f69SBart Van Assche                 pr2serr("      << expected binary code_set (1), got %d for "
1475*44704f69SBart Van Assche                         "NAA=%d>>\n", c_set, naa);
1476*44704f69SBart Van Assche                 hex2stderr(ip, i_len, -1);
1477*44704f69SBart Van Assche                 break;
1478*44704f69SBart Van Assche             }
1479*44704f69SBart Van Assche             switch (naa) {
1480*44704f69SBart Van Assche             case 2:     /* NAA 2: IEEE Extended */
1481*44704f69SBart Van Assche                 if (8 != i_len) {
1482*44704f69SBart Van Assche                     pr2serr("      << unexpected NAA 2 identifier "
1483*44704f69SBart Van Assche                             "length: 0x%x>>\n", i_len);
1484*44704f69SBart Van Assche                     hex2stderr(ip, i_len, -1);
1485*44704f69SBart Van Assche                     break;
1486*44704f69SBart Van Assche                 }
1487*44704f69SBart Van Assche                 d_id = (((ip[0] & 0xf) << 8) | ip[1]);
1488*44704f69SBart Van Assche                 c_id = sg_get_unaligned_be24(ip + 2);
1489*44704f69SBart Van Assche                 vsi = sg_get_unaligned_be24(ip + 5);
1490*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "      NAA 2, vendor specific identifier A: "
1491*44704f69SBart Van Assche                           "0x%x\n", d_id);
1492*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "      AOI: 0x%x\n", c_id);
1493*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "      vendor specific identifier B: 0x%x\n",
1494*44704f69SBart Van Assche                           vsi);
1495*44704f69SBart Van Assche                 n = 0;
1496*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "      [0x");
1497*44704f69SBart Van Assche                 for (m = 0; m < 8; ++m)
1498*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "%02x", ip[m]);
1499*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "%s]\n", b);
1500*44704f69SBart Van Assche                 break;
1501*44704f69SBart Van Assche             case 3:     /* NAA 3: Locally assigned */
1502*44704f69SBart Van Assche                 if (8 != i_len) {
1503*44704f69SBart Van Assche                     pr2serr("      << unexpected NAA 3 identifier "
1504*44704f69SBart Van Assche                             "length: 0x%x>>\n", i_len);
1505*44704f69SBart Van Assche                     hex2stderr(ip, i_len, -1);
1506*44704f69SBart Van Assche                     break;
1507*44704f69SBart Van Assche                 }
1508*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "      NAA 3, Locally assigned:\n");
1509*44704f69SBart Van Assche                 n = 0;
1510*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "      [0x");
1511*44704f69SBart Van Assche                 for (m = 0; m < 8; ++m)
1512*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "%02x", ip[m]);
1513*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "%s]\n", b);
1514*44704f69SBart Van Assche                 break;
1515*44704f69SBart Van Assche             case 5:     /* NAA 5: IEEE Registered */
1516*44704f69SBart Van Assche                 if (8 != i_len) {
1517*44704f69SBart Van Assche                     pr2serr("      << unexpected NAA 5 identifier "
1518*44704f69SBart Van Assche                             "length: 0x%x>>\n", i_len);
1519*44704f69SBart Van Assche                     hex2stderr(ip, i_len, -1);
1520*44704f69SBart Van Assche                     break;
1521*44704f69SBart Van Assche                 }
1522*44704f69SBart Van Assche                 c_id = (((ip[0] & 0xf) << 20) | (ip[1] << 12) |
1523*44704f69SBart Van Assche                         (ip[2] << 4) | ((ip[3] & 0xf0) >> 4));
1524*44704f69SBart Van Assche                 vsei = ip[3] & 0xf;
1525*44704f69SBart Van Assche                 for (m = 1; m < 5; ++m) {
1526*44704f69SBart Van Assche                     vsei <<= 8;
1527*44704f69SBart Van Assche                     vsei |= ip[3 + m];
1528*44704f69SBart Van Assche                 }
1529*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "      NAA 5, AOI: 0x%x\n", c_id);
1530*44704f69SBart Van Assche                 n = 0;
1531*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "      Vendor Specific "
1532*44704f69SBart Van Assche                               "Identifier: 0x%" PRIx64 "\n", vsei);
1533*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "      [0x");
1534*44704f69SBart Van Assche                 for (m = 0; m < 8; ++m)
1535*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "%02x", ip[m]);
1536*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "%s]\n", b);
1537*44704f69SBart Van Assche                 break;
1538*44704f69SBart Van Assche             case 6:     /* NAA 6: IEEE Registered extended */
1539*44704f69SBart Van Assche                 if (16 != i_len) {
1540*44704f69SBart Van Assche                     pr2serr("      << unexpected NAA 6 identifier "
1541*44704f69SBart Van Assche                             "length: 0x%x>>\n", i_len);
1542*44704f69SBart Van Assche                     hex2stderr(ip, i_len, 0);
1543*44704f69SBart Van Assche                     break;
1544*44704f69SBart Van Assche                 }
1545*44704f69SBart Van Assche                 c_id = (((ip[0] & 0xf) << 20) | (ip[1] << 12) |
1546*44704f69SBart Van Assche                         (ip[2] << 4) | ((ip[3] & 0xf0) >> 4));
1547*44704f69SBart Van Assche                 vsei = ip[3] & 0xf;
1548*44704f69SBart Van Assche                 for (m = 1; m < 5; ++m) {
1549*44704f69SBart Van Assche                     vsei <<= 8;
1550*44704f69SBart Van Assche                     vsei |= ip[3 + m];
1551*44704f69SBart Van Assche                 }
1552*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "      NAA 6, AOI: 0x%x\n", c_id);
1553*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "      Vendor Specific Identifier: 0x%"
1554*44704f69SBart Van Assche                           PRIx64 "\n", vsei);
1555*44704f69SBart Van Assche                 vsei = sg_get_unaligned_be64(ip + 8);
1556*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "      Vendor Specific Identifier Extension: "
1557*44704f69SBart Van Assche                           "0x%" PRIx64 "\n", vsei);
1558*44704f69SBart Van Assche                 n = 0;
1559*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "      [0x");
1560*44704f69SBart Van Assche                 for (m = 0; m < 16; ++m)
1561*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "%02x", ip[m]);
1562*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "%s]\n", b);
1563*44704f69SBart Van Assche                 break;
1564*44704f69SBart Van Assche             default:
1565*44704f69SBart Van Assche                 pr2serr("      << bad NAA nibble , expect 2, 3, 5 or 6, "
1566*44704f69SBart Van Assche                         "got %d>>\n", naa);
1567*44704f69SBart Van Assche                 hex2stderr(ip, i_len, -1);
1568*44704f69SBart Van Assche                 break;
1569*44704f69SBart Van Assche             }
1570*44704f69SBart Van Assche             break;
1571*44704f69SBart Van Assche         case 4: /* Relative target port */
1572*44704f69SBart Van Assche             if ((1 != c_set) || (1 != assoc) || (4 != i_len)) {
1573*44704f69SBart Van Assche                 pr2serr("      << expected binary code_set, target "
1574*44704f69SBart Van Assche                         "port association, length 4>>\n");
1575*44704f69SBart Van Assche                 hex2stderr(ip, i_len, -1);
1576*44704f69SBart Van Assche                 break;
1577*44704f69SBart Van Assche             }
1578*44704f69SBart Van Assche             d_id = sg_get_unaligned_be16(ip + 2);
1579*44704f69SBart Van Assche             sgj_pr_hr(jsp, "      Relative target port: 0x%x\n", d_id);
1580*44704f69SBart Van Assche             break;
1581*44704f69SBart Van Assche         case 5: /* (primary) Target port group */
1582*44704f69SBart Van Assche             if ((1 != c_set) || (1 != assoc) || (4 != i_len)) {
1583*44704f69SBart Van Assche                 pr2serr("      << expected binary code_set, target "
1584*44704f69SBart Van Assche                         "port association, length 4>>\n");
1585*44704f69SBart Van Assche                 hex2stderr(ip, i_len, -1);
1586*44704f69SBart Van Assche                 break;
1587*44704f69SBart Van Assche             }
1588*44704f69SBart Van Assche             d_id = sg_get_unaligned_be16(ip + 2);
1589*44704f69SBart Van Assche             sgj_pr_hr(jsp, "      Target port group: 0x%x\n", d_id);
1590*44704f69SBart Van Assche             break;
1591*44704f69SBart Van Assche         case 6: /* Logical unit group */
1592*44704f69SBart Van Assche             if ((1 != c_set) || (0 != assoc) || (4 != i_len)) {
1593*44704f69SBart Van Assche                 pr2serr("      << expected binary code_set, logical "
1594*44704f69SBart Van Assche                         "unit association, length 4>>\n");
1595*44704f69SBart Van Assche                 hex2stderr(ip, i_len, -1);
1596*44704f69SBart Van Assche                 break;
1597*44704f69SBart Van Assche             }
1598*44704f69SBart Van Assche             d_id = sg_get_unaligned_be16(ip + 2);
1599*44704f69SBart Van Assche             sgj_pr_hr(jsp, "      Logical unit group: 0x%x\n", d_id);
1600*44704f69SBart Van Assche             break;
1601*44704f69SBart Van Assche         case 7: /* MD5 logical unit identifier */
1602*44704f69SBart Van Assche             if ((1 != c_set) || (0 != assoc)) {
1603*44704f69SBart Van Assche                 pr2serr("      << expected binary code_set, logical "
1604*44704f69SBart Van Assche                         "unit association>>\n");
1605*44704f69SBart Van Assche                 hex2stderr(ip, i_len, -1);
1606*44704f69SBart Van Assche                 break;
1607*44704f69SBart Van Assche             }
1608*44704f69SBart Van Assche             sgj_pr_hr(jsp, "      MD5 logical unit identifier:\n");
1609*44704f69SBart Van Assche             if (jsp->pr_out_hr)
1610*44704f69SBart Van Assche                 sgj_js_str_out(jsp, (const char *)ip, i_len);
1611*44704f69SBart Van Assche             else
1612*44704f69SBart Van Assche                 hex2stdout(ip, i_len, -1);
1613*44704f69SBart Van Assche             break;
1614*44704f69SBart Van Assche         case 8: /* SCSI name string */
1615*44704f69SBart Van Assche             if (3 != c_set) {
1616*44704f69SBart Van Assche                 if (2 == c_set) {
1617*44704f69SBart Van Assche                     if (op->verbose)
1618*44704f69SBart Van Assche                         pr2serr("      << expected UTF-8, use ASCII>>\n");
1619*44704f69SBart Van Assche                 } else {
1620*44704f69SBart Van Assche                     pr2serr("      << expected UTF-8 code_set>>\n");
1621*44704f69SBart Van Assche                     hex2stderr(ip, i_len, -1);
1622*44704f69SBart Van Assche                     break;
1623*44704f69SBart Van Assche                 }
1624*44704f69SBart Van Assche             }
1625*44704f69SBart Van Assche             sgj_pr_hr(jsp, "      SCSI name string:\n");
1626*44704f69SBart Van Assche             /* does %s print out UTF-8 ok??
1627*44704f69SBart Van Assche              * Seems to depend on the locale. Looks ok here with my
1628*44704f69SBart Van Assche              * locale setting: en_AU.UTF-8
1629*44704f69SBart Van Assche              */
1630*44704f69SBart Van Assche             sgj_pr_hr(jsp, "      %.*s\n", i_len, (const char *)ip);
1631*44704f69SBart Van Assche             break;
1632*44704f69SBart Van Assche         case 9: /* Protocol specific port identifier */
1633*44704f69SBart Van Assche             /* added in spc4r36, PIV must be set, proto_id indicates */
1634*44704f69SBart Van Assche             /* whether UAS (USB) or SOP (PCIe) or ... */
1635*44704f69SBart Van Assche             if (! piv)
1636*44704f69SBart Van Assche                 pr2serr("      >>>> Protocol specific port identifier "
1637*44704f69SBart Van Assche                         "expects protocol\n"
1638*44704f69SBart Van Assche                         "           identifier to be valid and it is not\n");
1639*44704f69SBart Van Assche             if (TPROTO_UAS == p_id) {
1640*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "      USB device address: 0x%x\n",
1641*44704f69SBart Van Assche                           0x7f & ip[0]);
1642*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "      USB interface number: 0x%x\n", ip[2]);
1643*44704f69SBart Van Assche             } else if (TPROTO_SOP == p_id) {
1644*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "      PCIe routing ID, bus number: 0x%x\n",
1645*44704f69SBart Van Assche                           ip[0]);
1646*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "          function number: 0x%x\n", ip[1]);
1647*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "          [or device number: 0x%x, function "
1648*44704f69SBart Van Assche                           "number: 0x%x]\n", (0x1f & (ip[1] >> 3)),
1649*44704f69SBart Van Assche                           0x7 & ip[1]);
1650*44704f69SBart Van Assche             } else
1651*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "      >>>> unexpected protocol identifier: "
1652*44704f69SBart Van Assche                           "%s\n           with Protocol specific port "
1653*44704f69SBart Van Assche                           "identifier\n", sg_get_trans_proto_str(p_id, dlen,
1654*44704f69SBart Van Assche                                                                  d));
1655*44704f69SBart Van Assche             break;
1656*44704f69SBart Van Assche         case 0xa: /* UUID identifier [spc5r08] RFC 4122 */
1657*44704f69SBart Van Assche             if (1 != c_set) {
1658*44704f69SBart Van Assche                 pr2serr("      << expected binary code_set >>\n");
1659*44704f69SBart Van Assche                 hex2stderr(ip, i_len, 0);
1660*44704f69SBart Van Assche                 break;
1661*44704f69SBart Van Assche             }
1662*44704f69SBart Van Assche             if ((1 != ((ip[0] >> 4) & 0xf)) || (18 != i_len)) {
1663*44704f69SBart Van Assche                 pr2serr("      << expected locally assigned UUID, 16 bytes "
1664*44704f69SBart Van Assche                         "long >>\n");
1665*44704f69SBart Van Assche                 hex2stderr(ip, i_len, 0);
1666*44704f69SBart Van Assche                 break;
1667*44704f69SBart Van Assche             }
1668*44704f69SBart Van Assche             n = 0;
1669*44704f69SBart Van Assche             n += sg_scnpr(b + n, blen - n, "      Locally assigned UUID: ");
1670*44704f69SBart Van Assche             for (m = 0; m < 16; ++m) {
1671*44704f69SBart Van Assche                 if ((4 == m) || (6 == m) || (8 == m) || (10 == m))
1672*44704f69SBart Van Assche                     n += sg_scnpr(b + n, blen - n, "-");
1673*44704f69SBart Van Assche                 n += sg_scnpr(b + n, blen - n, "%02x", ip[2 + m]);
1674*44704f69SBart Van Assche             }
1675*44704f69SBart Van Assche             sgj_pr_hr(jsp, "%s\n", b);
1676*44704f69SBart Van Assche             break;
1677*44704f69SBart Van Assche         default: /* reserved */
1678*44704f69SBart Van Assche             pr2serr("      reserved designator=0x%x\n", desig_type);
1679*44704f69SBart Van Assche             hex2stderr(ip, i_len, -1);
1680*44704f69SBart Van Assche             break;
1681*44704f69SBart Van Assche         }
1682*44704f69SBart Van Assche     }
1683*44704f69SBart Van Assche     if (-2 == u)
1684*44704f69SBart Van Assche         pr2serr("%s VPD page error: around offset=%d\n", leadin, off);
1685*44704f69SBart Van Assche }
1686*44704f69SBart Van Assche 
1687*44704f69SBart Van Assche /* The --export and --json options are assumed to be mutually exclusive.
1688*44704f69SBart Van Assche  * Here the former takes precedence. */
1689*44704f69SBart Van Assche static void
export_dev_ids(uint8_t * buff,int len,int verbose)1690*44704f69SBart Van Assche export_dev_ids(uint8_t * buff, int len, int verbose)
1691*44704f69SBart Van Assche {
1692*44704f69SBart Van Assche     int u, j, m, id_len, c_set, assoc, desig_type, i_len;
1693*44704f69SBart Van Assche     int off, d_id, naa, k, p_id;
1694*44704f69SBart Van Assche     uint8_t * bp;
1695*44704f69SBart Van Assche     uint8_t * ip;
1696*44704f69SBart Van Assche     const char * assoc_str;
1697*44704f69SBart Van Assche     const char * suffix;
1698*44704f69SBart Van Assche 
1699*44704f69SBart Van Assche     if (buff[2] != 0) {
1700*44704f69SBart Van Assche         /*
1701*44704f69SBart Van Assche          * Cf decode_dev_ids() for details
1702*44704f69SBart Van Assche          */
1703*44704f69SBart Van Assche         i_len = len;
1704*44704f69SBart Van Assche         ip = buff;
1705*44704f69SBart Van Assche         c_set = 1;
1706*44704f69SBart Van Assche         assoc = 0;
1707*44704f69SBart Van Assche         p_id = 0xf;
1708*44704f69SBart Van Assche         desig_type = 3;
1709*44704f69SBart Van Assche         j = 1;
1710*44704f69SBart Van Assche         off = 16;
1711*44704f69SBart Van Assche         goto decode;
1712*44704f69SBart Van Assche     }
1713*44704f69SBart Van Assche 
1714*44704f69SBart Van Assche     for (j = 1, off = -1;
1715*44704f69SBart Van Assche          (u = sg_vpd_dev_id_iter(buff, len, &off, -1, -1, -1)) == 0;
1716*44704f69SBart Van Assche          ++j) {
1717*44704f69SBart Van Assche         bp = buff + off;
1718*44704f69SBart Van Assche         i_len = bp[3];
1719*44704f69SBart Van Assche         id_len = i_len + 4;
1720*44704f69SBart Van Assche         if ((off + id_len) > len) {
1721*44704f69SBart Van Assche             if (verbose)
1722*44704f69SBart Van Assche                 pr2serr("Device Identification VPD page error: designator "
1723*44704f69SBart Van Assche                         "length longer than\n     remaining response "
1724*44704f69SBart Van Assche                         "length=%d\n", (len - off));
1725*44704f69SBart Van Assche             return;
1726*44704f69SBart Van Assche         }
1727*44704f69SBart Van Assche         ip = bp + 4;
1728*44704f69SBart Van Assche         p_id = ((bp[0] >> 4) & 0xf);   /* protocol identifier */
1729*44704f69SBart Van Assche         c_set = (bp[0] & 0xf);
1730*44704f69SBart Van Assche         assoc = ((bp[1] >> 4) & 0x3);
1731*44704f69SBart Van Assche         desig_type = (bp[1] & 0xf);
1732*44704f69SBart Van Assche   decode:
1733*44704f69SBart Van Assche         switch (assoc) {
1734*44704f69SBart Van Assche             case 0:
1735*44704f69SBart Van Assche                 assoc_str = "LUN";
1736*44704f69SBart Van Assche                 break;
1737*44704f69SBart Van Assche             case 1:
1738*44704f69SBart Van Assche                 assoc_str = "PORT";
1739*44704f69SBart Van Assche                 break;
1740*44704f69SBart Van Assche             case 2:
1741*44704f69SBart Van Assche                 assoc_str = "TARGET";
1742*44704f69SBart Van Assche                 break;
1743*44704f69SBart Van Assche             default:
1744*44704f69SBart Van Assche                 if (verbose)
1745*44704f69SBart Van Assche                     pr2serr("    Invalid association %d\n", assoc);
1746*44704f69SBart Van Assche                 return;
1747*44704f69SBart Van Assche         }
1748*44704f69SBart Van Assche         switch (desig_type) {
1749*44704f69SBart Van Assche         case 0: /* vendor specific */
1750*44704f69SBart Van Assche             if (i_len == 0 || i_len > 128)
1751*44704f69SBart Van Assche                 break;
1752*44704f69SBart Van Assche             if ((2 == c_set) || (3 == c_set)) { /* ASCII or UTF-8 */
1753*44704f69SBart Van Assche                 k = encode_whitespaces(ip, i_len);
1754*44704f69SBart Van Assche                 /* udev-conforming character encoding */
1755*44704f69SBart Van Assche                 if (k > 0) {
1756*44704f69SBart Van Assche                     printf("SCSI_IDENT_%s_VENDOR=", assoc_str);
1757*44704f69SBart Van Assche                     for (m = 0; m < k; ++m) {
1758*44704f69SBart Van Assche                         if ((ip[m] >= '0' && ip[m] <= '9') ||
1759*44704f69SBart Van Assche                             (ip[m] >= 'A' && ip[m] <= 'Z') ||
1760*44704f69SBart Van Assche                             (ip[m] >= 'a' && ip[m] <= 'z') ||
1761*44704f69SBart Van Assche                             strchr("#+-.:=@_", ip[m]) != NULL)
1762*44704f69SBart Van Assche                             printf("%c", ip[m]);
1763*44704f69SBart Van Assche                         else
1764*44704f69SBart Van Assche                             printf("\\x%02x", ip[m]);
1765*44704f69SBart Van Assche                     }
1766*44704f69SBart Van Assche                     printf("\n");
1767*44704f69SBart Van Assche                 }
1768*44704f69SBart Van Assche             } else {
1769*44704f69SBart Van Assche                 printf("SCSI_IDENT_%s_VENDOR=", assoc_str);
1770*44704f69SBart Van Assche                 for (m = 0; m < i_len; ++m)
1771*44704f69SBart Van Assche                     printf("%02x", (unsigned int)ip[m]);
1772*44704f69SBart Van Assche                 printf("\n");
1773*44704f69SBart Van Assche             }
1774*44704f69SBart Van Assche             break;
1775*44704f69SBart Van Assche         case 1: /* T10 vendor identification */
1776*44704f69SBart Van Assche             printf("SCSI_IDENT_%s_T10=", assoc_str);
1777*44704f69SBart Van Assche             if ((2 == c_set) || (3 == c_set)) {
1778*44704f69SBart Van Assche                 k = encode_whitespaces(ip, i_len);
1779*44704f69SBart Van Assche                 /* udev-conforming character encoding */
1780*44704f69SBart Van Assche                 for (m = 0; m < k; ++m) {
1781*44704f69SBart Van Assche                     if ((ip[m] >= '0' && ip[m] <= '9') ||
1782*44704f69SBart Van Assche                         (ip[m] >= 'A' && ip[m] <= 'Z') ||
1783*44704f69SBart Van Assche                         (ip[m] >= 'a' && ip[m] <= 'z') ||
1784*44704f69SBart Van Assche                         strchr("#+-.:=@_", ip[m]) != NULL)
1785*44704f69SBart Van Assche                         printf("%c", ip[m]);
1786*44704f69SBart Van Assche                     else
1787*44704f69SBart Van Assche                         printf("\\x%02x", ip[m]);
1788*44704f69SBart Van Assche                 }
1789*44704f69SBart Van Assche                 printf("\n");
1790*44704f69SBart Van Assche                 if (!memcmp(ip, "ATA_", 4)) {
1791*44704f69SBart Van Assche                     printf("SCSI_IDENT_%s_ATA=%.*s\n", assoc_str,
1792*44704f69SBart Van Assche                            k - 4, ip + 4);
1793*44704f69SBart Van Assche                 }
1794*44704f69SBart Van Assche             } else {
1795*44704f69SBart Van Assche                 for (m = 0; m < i_len; ++m)
1796*44704f69SBart Van Assche                     printf("%02x", (unsigned int)ip[m]);
1797*44704f69SBart Van Assche                 printf("\n");
1798*44704f69SBart Van Assche             }
1799*44704f69SBart Van Assche             break;
1800*44704f69SBart Van Assche         case 2: /* EUI-64 based */
1801*44704f69SBart Van Assche             if (1 != c_set) {
1802*44704f69SBart Van Assche                 if (verbose) {
1803*44704f69SBart Van Assche                     pr2serr("      << expected binary code_set (1)>>\n");
1804*44704f69SBart Van Assche                     hex2stderr(ip, i_len, 0);
1805*44704f69SBart Van Assche                 }
1806*44704f69SBart Van Assche                 break;
1807*44704f69SBart Van Assche             }
1808*44704f69SBart Van Assche             printf("SCSI_IDENT_%s_EUI64=", assoc_str);
1809*44704f69SBart Van Assche             for (m = 0; m < i_len; ++m)
1810*44704f69SBart Van Assche                 printf("%02x", (unsigned int)ip[m]);
1811*44704f69SBart Van Assche             printf("\n");
1812*44704f69SBart Van Assche             break;
1813*44704f69SBart Van Assche         case 3: /* NAA */
1814*44704f69SBart Van Assche             if (1 != c_set) {
1815*44704f69SBart Van Assche                 if (verbose) {
1816*44704f69SBart Van Assche                     pr2serr("      << expected binary code_set (1)>>\n");
1817*44704f69SBart Van Assche                     hex2stderr(ip, i_len, 0);
1818*44704f69SBart Van Assche                 }
1819*44704f69SBart Van Assche                 break;
1820*44704f69SBart Van Assche             }
1821*44704f69SBart Van Assche             /*
1822*44704f69SBart Van Assche              * Unfortunately, there are some (broken) implementations
1823*44704f69SBart Van Assche              * which return _several_ NAA descriptors.
1824*44704f69SBart Van Assche              * So add a suffix to differentiate between them.
1825*44704f69SBart Van Assche              */
1826*44704f69SBart Van Assche             naa = (ip[0] >> 4) & 0xff;
1827*44704f69SBart Van Assche             switch (naa) {
1828*44704f69SBart Van Assche                 case 6:
1829*44704f69SBart Van Assche                     suffix="REGEXT";
1830*44704f69SBart Van Assche                     break;
1831*44704f69SBart Van Assche                 case 5:
1832*44704f69SBart Van Assche                     suffix="REG";
1833*44704f69SBart Van Assche                     break;
1834*44704f69SBart Van Assche                 case 2:
1835*44704f69SBart Van Assche                     suffix="EXT";
1836*44704f69SBart Van Assche                     break;
1837*44704f69SBart Van Assche                 case 3:
1838*44704f69SBart Van Assche                 default:
1839*44704f69SBart Van Assche                     suffix="LOCAL";
1840*44704f69SBart Van Assche                     break;
1841*44704f69SBart Van Assche             }
1842*44704f69SBart Van Assche             printf("SCSI_IDENT_%s_NAA_%s=", assoc_str, suffix);
1843*44704f69SBart Van Assche             for (m = 0; m < i_len; ++m)
1844*44704f69SBart Van Assche                 printf("%02x", (unsigned int)ip[m]);
1845*44704f69SBart Van Assche             printf("\n");
1846*44704f69SBart Van Assche             break;
1847*44704f69SBart Van Assche         case 4: /* Relative target port */
1848*44704f69SBart Van Assche             if ((1 != c_set) || (1 != assoc) || (4 != i_len)) {
1849*44704f69SBart Van Assche                 if (verbose) {
1850*44704f69SBart Van Assche                     pr2serr("      << expected binary code_set, target "
1851*44704f69SBart Van Assche                             "port association, length 4>>\n");
1852*44704f69SBart Van Assche                     hex2stderr(ip, i_len, 0);
1853*44704f69SBart Van Assche                 }
1854*44704f69SBart Van Assche                 break;
1855*44704f69SBart Van Assche             }
1856*44704f69SBart Van Assche             d_id = sg_get_unaligned_be16(ip + 2);
1857*44704f69SBart Van Assche             printf("SCSI_IDENT_%s_RELATIVE=%d\n", assoc_str, d_id);
1858*44704f69SBart Van Assche             break;
1859*44704f69SBart Van Assche         case 5: /* (primary) Target port group */
1860*44704f69SBart Van Assche             if ((1 != c_set) || (1 != assoc) || (4 != i_len)) {
1861*44704f69SBart Van Assche                 if (verbose) {
1862*44704f69SBart Van Assche                     pr2serr("      << expected binary code_set, target "
1863*44704f69SBart Van Assche                             "port association, length 4>>\n");
1864*44704f69SBart Van Assche                     hex2stderr(ip, i_len, 0);
1865*44704f69SBart Van Assche                 }
1866*44704f69SBart Van Assche                 break;
1867*44704f69SBart Van Assche             }
1868*44704f69SBart Van Assche             d_id = sg_get_unaligned_be16(ip + 2);
1869*44704f69SBart Van Assche             printf("SCSI_IDENT_%s_TARGET_PORT_GROUP=0x%x\n", assoc_str, d_id);
1870*44704f69SBart Van Assche             break;
1871*44704f69SBart Van Assche         case 6: /* Logical unit group */
1872*44704f69SBart Van Assche             if ((1 != c_set) || (0 != assoc) || (4 != i_len)) {
1873*44704f69SBart Van Assche                 if (verbose) {
1874*44704f69SBart Van Assche                     pr2serr("      << expected binary code_set, logical "
1875*44704f69SBart Van Assche                             "unit association, length 4>>\n");
1876*44704f69SBart Van Assche                     hex2stderr(ip, i_len, 0);
1877*44704f69SBart Van Assche                 }
1878*44704f69SBart Van Assche                 break;
1879*44704f69SBart Van Assche             }
1880*44704f69SBart Van Assche             d_id = sg_get_unaligned_be16(ip + 2);
1881*44704f69SBart Van Assche             printf("SCSI_IDENT_%s_LOGICAL_UNIT_GROUP=0x%x\n", assoc_str, d_id);
1882*44704f69SBart Van Assche             break;
1883*44704f69SBart Van Assche         case 7: /* MD5 logical unit identifier */
1884*44704f69SBart Van Assche             if ((1 != c_set) || (0 != assoc)) {
1885*44704f69SBart Van Assche                 if (verbose) {
1886*44704f69SBart Van Assche                     pr2serr("      << expected binary code_set, logical "
1887*44704f69SBart Van Assche                             "unit association>>\n");
1888*44704f69SBart Van Assche                     hex2stderr(ip, i_len, 0);
1889*44704f69SBart Van Assche                 }
1890*44704f69SBart Van Assche                 break;
1891*44704f69SBart Van Assche             }
1892*44704f69SBart Van Assche             printf("SCSI_IDENT_%s_MD5=", assoc_str);
1893*44704f69SBart Van Assche             hex2stdout(ip, i_len, -1);
1894*44704f69SBart Van Assche             break;
1895*44704f69SBart Van Assche         case 8: /* SCSI name string */
1896*44704f69SBart Van Assche             if (3 != c_set) {
1897*44704f69SBart Van Assche                 if (verbose) {
1898*44704f69SBart Van Assche                     pr2serr("      << expected UTF-8 code_set>>\n");
1899*44704f69SBart Van Assche                     hex2stderr(ip, i_len, -1);
1900*44704f69SBart Van Assche                 }
1901*44704f69SBart Van Assche                 break;
1902*44704f69SBart Van Assche             }
1903*44704f69SBart Van Assche             if (! (strncmp((const char *)ip, "eui.", 4) ||
1904*44704f69SBart Van Assche                    strncmp((const char *)ip, "EUI.", 4) ||
1905*44704f69SBart Van Assche                    strncmp((const char *)ip, "naa.", 4) ||
1906*44704f69SBart Van Assche                    strncmp((const char *)ip, "NAA.", 4) ||
1907*44704f69SBart Van Assche                    strncmp((const char *)ip, "iqn.", 4))) {
1908*44704f69SBart Van Assche                 if (verbose) {
1909*44704f69SBart Van Assche                     pr2serr("      << expected name string prefix>>\n");
1910*44704f69SBart Van Assche                     hex2stderr(ip, i_len, -1);
1911*44704f69SBart Van Assche                 }
1912*44704f69SBart Van Assche                 break;
1913*44704f69SBart Van Assche             }
1914*44704f69SBart Van Assche 
1915*44704f69SBart Van Assche             printf("SCSI_IDENT_%s_NAME=%.*s\n", assoc_str, i_len,
1916*44704f69SBart Van Assche                    (const char *)ip);
1917*44704f69SBart Van Assche             break;
1918*44704f69SBart Van Assche         case 9: /*  Protocol specific port identifier */
1919*44704f69SBart Van Assche             if (TPROTO_UAS == p_id) {
1920*44704f69SBart Van Assche                 if ((4 != i_len) || (1 != assoc)) {
1921*44704f69SBart Van Assche                     if (verbose) {
1922*44704f69SBart Van Assche                         pr2serr("      << UAS (USB) expected target "
1923*44704f69SBart Van Assche                                 "port association>>\n");
1924*44704f69SBart Van Assche                         hex2stderr(ip, i_len, 0);
1925*44704f69SBart Van Assche                     }
1926*44704f69SBart Van Assche                     break;
1927*44704f69SBart Van Assche                 }
1928*44704f69SBart Van Assche                 printf("SCSI_IDENT_%s_UAS_DEVICE_ADDRESS=0x%x\n", assoc_str,
1929*44704f69SBart Van Assche                        ip[0] & 0x7f);
1930*44704f69SBart Van Assche                 printf("SCSI_IDENT_%s_UAS_INTERFACE_NUMBER=0x%x\n", assoc_str,
1931*44704f69SBart Van Assche                        ip[2]);
1932*44704f69SBart Van Assche             } else if (TPROTO_SOP == p_id) {
1933*44704f69SBart Van Assche                 if ((4 != i_len) && (8 != i_len)) {   /* spc4r36h confused */
1934*44704f69SBart Van Assche                     if (verbose) {
1935*44704f69SBart Van Assche                         pr2serr("      << SOP (PCIe) descriptor "
1936*44704f69SBart Van Assche                                 "length=%d >>\n", i_len);
1937*44704f69SBart Van Assche                         hex2stderr(ip, i_len, 0);
1938*44704f69SBart Van Assche                     }
1939*44704f69SBart Van Assche                     break;
1940*44704f69SBart Van Assche                 }
1941*44704f69SBart Van Assche                 printf("SCSI_IDENT_%s_SOP_ROUTING_ID=0x%x\n", assoc_str,
1942*44704f69SBart Van Assche                        sg_get_unaligned_be16(ip + 0));
1943*44704f69SBart Van Assche             } else {
1944*44704f69SBart Van Assche                 pr2serr("      << Protocol specific port identifier "
1945*44704f69SBart Van Assche                         "protocol_id=0x%x>>\n", p_id);
1946*44704f69SBart Van Assche             }
1947*44704f69SBart Van Assche             break;
1948*44704f69SBart Van Assche         case 0xa: /* UUID based */
1949*44704f69SBart Van Assche             if (1 != c_set) {
1950*44704f69SBart Van Assche                 if (verbose) {
1951*44704f69SBart Van Assche                     pr2serr("      << expected binary code_set (1)>>\n");
1952*44704f69SBart Van Assche                     hex2stderr(ip, i_len, 0);
1953*44704f69SBart Van Assche                 }
1954*44704f69SBart Van Assche                 break;
1955*44704f69SBart Van Assche             }
1956*44704f69SBart Van Assche             if (i_len < 18) {
1957*44704f69SBart Van Assche                 if (verbose) {
1958*44704f69SBart Van Assche                     pr2serr("      << short UUID field expected 18 or more, "
1959*44704f69SBart Van Assche                             "got %d >>\n", i_len);
1960*44704f69SBart Van Assche                     hex2stderr(ip, i_len, 0);
1961*44704f69SBart Van Assche                 }
1962*44704f69SBart Van Assche                 break;
1963*44704f69SBart Van Assche             }
1964*44704f69SBart Van Assche             printf("SCSI_IDENT_%s_UUID=", assoc_str);
1965*44704f69SBart Van Assche             for (m = 2; m < i_len; ++m) {
1966*44704f69SBart Van Assche                 if ((6 == m) || (8 == m) || (10 == m) || (12 == m))
1967*44704f69SBart Van Assche                     printf("-%02x", (unsigned int)ip[m]);
1968*44704f69SBart Van Assche                 else
1969*44704f69SBart Van Assche                     printf("%02x", (unsigned int)ip[m]);
1970*44704f69SBart Van Assche             }
1971*44704f69SBart Van Assche             printf("\n");
1972*44704f69SBart Van Assche             break;
1973*44704f69SBart Van Assche         default: /* reserved */
1974*44704f69SBart Van Assche             if (verbose) {
1975*44704f69SBart Van Assche                 pr2serr("      reserved designator=0x%x\n", desig_type);
1976*44704f69SBart Van Assche                 hex2stderr(ip, i_len, -1);
1977*44704f69SBart Van Assche             }
1978*44704f69SBart Van Assche             break;
1979*44704f69SBart Van Assche         }
1980*44704f69SBart Van Assche     }
1981*44704f69SBart Van Assche     if (-2 == u && verbose)
1982*44704f69SBart Van Assche         pr2serr("Device identification VPD page error: "
1983*44704f69SBart Van Assche                 "around offset=%d\n", off);
1984*44704f69SBart Van Assche }
1985*44704f69SBart Van Assche 
1986*44704f69SBart Van Assche /* VPD_BLOCK_LIMITS  0xb0 ["bl"]  (SBC) */
1987*44704f69SBart Van Assche /* VPD_SA_DEV_CAP  0xb0 ["sad"]  (SSC) */
1988*44704f69SBart Van Assche /* Sequential access device characteristics,  ssc+smc */
1989*44704f69SBart Van Assche /* OSD information, osd */
1990*44704f69SBart Van Assche static void
decode_b0_vpd(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)1991*44704f69SBart Van Assche decode_b0_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jop)
1992*44704f69SBart Van Assche {
1993*44704f69SBart Van Assche     int pdt = PDT_MASK & buff[0];
1994*44704f69SBart Van Assche     sgj_state * jsp = &op->json_st;
1995*44704f69SBart Van Assche 
1996*44704f69SBart Van Assche     if (op->do_hex) {
1997*44704f69SBart Van Assche         hex2stdout(buff, len, no_ascii_4hex(op));
1998*44704f69SBart Van Assche         return;
1999*44704f69SBart Van Assche     }
2000*44704f69SBart Van Assche     switch (pdt) {
2001*44704f69SBart Van Assche     case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
2002*44704f69SBart Van Assche         /* done by decode_block_limits_vpd() */
2003*44704f69SBart Van Assche         break;
2004*44704f69SBart Van Assche     case PDT_TAPE: case PDT_MCHANGER:
2005*44704f69SBart Van Assche         sgj_haj_vi_nex(jsp, jop, 2, "TSMC", SGJ_SEP_EQUAL_NO_SPACE,
2006*44704f69SBart Van Assche                        !!(buff[4] & 0x2), false, "Tape Stream Mirror "
2007*44704f69SBart Van Assche                        "Capable");
2008*44704f69SBart Van Assche         sgj_haj_vi_nex(jsp, jop, 2, "WORM", SGJ_SEP_EQUAL_NO_SPACE,
2009*44704f69SBart Van Assche                        !!(buff[4] & 0x1), false, "Write Once Read Multiple "
2010*44704f69SBart Van Assche                        "supported");
2011*44704f69SBart Van Assche 
2012*44704f69SBart Van Assche         break;
2013*44704f69SBart Van Assche     case PDT_OSD:
2014*44704f69SBart Van Assche     default:
2015*44704f69SBart Van Assche         pr2serr("  Unable to decode pdt=0x%x, in hex:\n", pdt);
2016*44704f69SBart Van Assche         hex2stderr(buff, len, 0);
2017*44704f69SBart Van Assche         break;
2018*44704f69SBart Van Assche     }
2019*44704f69SBart Van Assche }
2020*44704f69SBart Van Assche 
2021*44704f69SBart Van Assche /* VPD_BLOCK_DEV_CHARS sbc  0xb1 ["bdc"] */
2022*44704f69SBart Van Assche /* VPD_MAN_ASS_SN ssc */
2023*44704f69SBart Van Assche static void
decode_b1_vpd(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)2024*44704f69SBart Van Assche decode_b1_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jop)
2025*44704f69SBart Van Assche {
2026*44704f69SBart Van Assche     int pdt;
2027*44704f69SBart Van Assche     sgj_state * jsp = &op->json_st;
2028*44704f69SBart Van Assche 
2029*44704f69SBart Van Assche     if (op->do_hex) {
2030*44704f69SBart Van Assche         hex2stdout(buff, len, no_ascii_4hex(op));
2031*44704f69SBart Van Assche         return;
2032*44704f69SBart Van Assche     }
2033*44704f69SBart Van Assche     pdt = PDT_MASK & buff[0];
2034*44704f69SBart Van Assche     switch (pdt) {
2035*44704f69SBart Van Assche     case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
2036*44704f69SBart Van Assche         /* now done by decode_block_dev_ch_vpd() in sg_vpd_common.c */
2037*44704f69SBart Van Assche         break;
2038*44704f69SBart Van Assche     case PDT_TAPE: case PDT_MCHANGER: case PDT_ADC:
2039*44704f69SBart Van Assche         sgj_pr_hr(jsp, "  Manufacturer-assigned serial number: %.*s\n",
2040*44704f69SBart Van Assche                   len - 4, buff + 4);
2041*44704f69SBart Van Assche         sgj_js_nv_s_len(jsp, jop, "manufacturer_assigned_serial_number",
2042*44704f69SBart Van Assche                         (const char *)buff + 4, len - 4);
2043*44704f69SBart Van Assche         break;
2044*44704f69SBart Van Assche     default:
2045*44704f69SBart Van Assche         pr2serr("  Unable to decode pdt=0x%x, in hex:\n", pdt);
2046*44704f69SBart Van Assche         hex2stderr(buff, len, 0);
2047*44704f69SBart Van Assche         break;
2048*44704f69SBart Van Assche     }
2049*44704f69SBart Van Assche }
2050*44704f69SBart Van Assche 
2051*44704f69SBart Van Assche /* VPD_REFERRALS sbc          0xb3 ["ref"] */
2052*44704f69SBart Van Assche /* VPD_AUTOMATION_DEV_SN ssc  0xb3 ["adsn"] */
2053*44704f69SBart Van Assche static void
decode_b3_vpd(uint8_t * buff,int len,struct opts_t * op,sgj_opaque_p jop)2054*44704f69SBart Van Assche decode_b3_vpd(uint8_t * buff, int len, struct opts_t * op, sgj_opaque_p jop)
2055*44704f69SBart Van Assche {
2056*44704f69SBart Van Assche     int pdt;
2057*44704f69SBart Van Assche     sgj_state * jsp = &op->json_st;
2058*44704f69SBart Van Assche 
2059*44704f69SBart Van Assche     if (op->do_hex) {
2060*44704f69SBart Van Assche         hex2stdout(buff, len, no_ascii_4hex(op));
2061*44704f69SBart Van Assche         return;
2062*44704f69SBart Van Assche     }
2063*44704f69SBart Van Assche     pdt = buff[0] & PDT_MASK;
2064*44704f69SBart Van Assche     switch (pdt) {
2065*44704f69SBart Van Assche     case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
2066*44704f69SBart Van Assche         /* now done in decode_referrals_vpd() in sg_vpd_common.c */
2067*44704f69SBart Van Assche         break;
2068*44704f69SBart Van Assche     case PDT_TAPE: case PDT_MCHANGER:
2069*44704f69SBart Van Assche         sgj_pr_hr(jsp, "  Automation device serial number: %.*s\n",
2070*44704f69SBart Van Assche                   len - 4, buff + 4);
2071*44704f69SBart Van Assche         sgj_js_nv_s_len(jsp, jop, "automation_device_serial_number",
2072*44704f69SBart Van Assche                         (const char *)buff + 4, len - 4);
2073*44704f69SBart Van Assche         break;
2074*44704f69SBart Van Assche     default:
2075*44704f69SBart Van Assche         pr2serr("  Unable to decode pdt=0x%x, in hex:\n", pdt);
2076*44704f69SBart Van Assche         hex2stderr(buff, len, 0);
2077*44704f69SBart Van Assche         break;
2078*44704f69SBart Van Assche     }
2079*44704f69SBart Van Assche }
2080*44704f69SBart Van Assche 
2081*44704f69SBart Van Assche #if 0
2082*44704f69SBart Van Assche static void
2083*44704f69SBart Van Assche decode_rdac_vpd_c9_rtpg_data(uint8_t aas, uint8_t vendor)
2084*44704f69SBart Van Assche {
2085*44704f69SBart Van Assche     printf("  Asymmetric Access State:");
2086*44704f69SBart Van Assche     switch(aas & 0x0F) {
2087*44704f69SBart Van Assche         case 0x0:
2088*44704f69SBart Van Assche             printf(" Active/Optimized");
2089*44704f69SBart Van Assche             break;
2090*44704f69SBart Van Assche         case 0x1:
2091*44704f69SBart Van Assche             printf(" Active/Non-Optimized");
2092*44704f69SBart Van Assche             break;
2093*44704f69SBart Van Assche         case 0x2:
2094*44704f69SBart Van Assche             printf(" Standby");
2095*44704f69SBart Van Assche             break;
2096*44704f69SBart Van Assche         case 0x3:
2097*44704f69SBart Van Assche             printf(" Unavailable");
2098*44704f69SBart Van Assche             break;
2099*44704f69SBart Van Assche         case 0xE:
2100*44704f69SBart Van Assche             printf(" Offline");
2101*44704f69SBart Van Assche             break;
2102*44704f69SBart Van Assche         case 0xF:
2103*44704f69SBart Van Assche             printf(" Transitioning");
2104*44704f69SBart Van Assche             break;
2105*44704f69SBart Van Assche         default:
2106*44704f69SBart Van Assche             printf(" (unknown)");
2107*44704f69SBart Van Assche             break;
2108*44704f69SBart Van Assche     }
2109*44704f69SBart Van Assche     printf("\n");
2110*44704f69SBart Van Assche 
2111*44704f69SBart Van Assche     printf("  Vendor Specific Field:");
2112*44704f69SBart Van Assche     switch(vendor) {
2113*44704f69SBart Van Assche         case 0x01:
2114*44704f69SBart Van Assche             printf(" Operating normally");
2115*44704f69SBart Van Assche             break;
2116*44704f69SBart Van Assche         case 0x02:
2117*44704f69SBart Van Assche             printf(" Non-responsive to queries");
2118*44704f69SBart Van Assche             break;
2119*44704f69SBart Van Assche         case 0x03:
2120*44704f69SBart Van Assche             printf(" Controller being held in reset");
2121*44704f69SBart Van Assche             break;
2122*44704f69SBart Van Assche         case 0x04:
2123*44704f69SBart Van Assche             printf(" Performing controller firmware download (1st "
2124*44704f69SBart Van Assche                    "controller)");
2125*44704f69SBart Van Assche             break;
2126*44704f69SBart Van Assche         case 0x05:
2127*44704f69SBart Van Assche             printf(" Performing controller firmware download (2nd "
2128*44704f69SBart Van Assche                    "controller)");
2129*44704f69SBart Van Assche             break;
2130*44704f69SBart Van Assche         case 0x06:
2131*44704f69SBart Van Assche             printf(" Quiesced as a result of an administrative request");
2132*44704f69SBart Van Assche             break;
2133*44704f69SBart Van Assche         case 0x07:
2134*44704f69SBart Van Assche             printf(" Service mode as a result of an administrative request");
2135*44704f69SBart Van Assche             break;
2136*44704f69SBart Van Assche         case 0xFF:
2137*44704f69SBart Van Assche             printf(" Details are not available");
2138*44704f69SBart Van Assche             break;
2139*44704f69SBart Van Assche         default:
2140*44704f69SBart Van Assche             printf(" (unknown)");
2141*44704f69SBart Van Assche             break;
2142*44704f69SBart Van Assche     }
2143*44704f69SBart Van Assche     printf("\n");
2144*44704f69SBart Van Assche }
2145*44704f69SBart Van Assche 
2146*44704f69SBart Van Assche static void
2147*44704f69SBart Van Assche decode_rdac_vpd_c9(uint8_t * buff, int len, struct opts_t * op)
2148*44704f69SBart Van Assche {
2149*44704f69SBart Van Assche     if (len < 3) {
2150*44704f69SBart Van Assche         pr2serr("Volume Access Control VPD page length too short=%d\n", len);
2151*44704f69SBart Van Assche         return;
2152*44704f69SBart Van Assche     }
2153*44704f69SBart Van Assche     if (op->do_hex) {
2154*44704f69SBart Van Assche         hex2stdout(buff, len, no_ascii_4hex(op));
2155*44704f69SBart Van Assche         return;
2156*44704f69SBart Van Assche     }
2157*44704f69SBart Van Assche     if (buff[4] != 'v' && buff[5] != 'a' && buff[6] != 'c') {
2158*44704f69SBart Van Assche         pr2serr("Invalid page identifier %c%c%c%c, decoding "
2159*44704f69SBart Van Assche                 "not possible.\n" , buff[4], buff[5], buff[6], buff[7]);
2160*44704f69SBart Van Assche         return;
2161*44704f69SBart Van Assche     }
2162*44704f69SBart Van Assche     if (buff[7] != '1') {
2163*44704f69SBart Van Assche         pr2serr("Invalid page version '%c' (should be 1)\n", buff[7]);
2164*44704f69SBart Van Assche     }
2165*44704f69SBart Van Assche     if ( (buff[8] & 0xE0) == 0xE0 ) {
2166*44704f69SBart Van Assche         printf("  IOShipping (ALUA): Enabled\n");
2167*44704f69SBart Van Assche     } else {
2168*44704f69SBart Van Assche         printf("  AVT:");
2169*44704f69SBart Van Assche         if (buff[8] & 0x80) {
2170*44704f69SBart Van Assche             printf(" Enabled");
2171*44704f69SBart Van Assche             if (buff[8] & 0x40)
2172*44704f69SBart Van Assche                 printf(" (Allow reads on sector 0)");
2173*44704f69SBart Van Assche             printf("\n");
2174*44704f69SBart Van Assche         } else {
2175*44704f69SBart Van Assche             printf(" Disabled\n");
2176*44704f69SBart Van Assche         }
2177*44704f69SBart Van Assche     }
2178*44704f69SBart Van Assche     printf("  Volume Access via: ");
2179*44704f69SBart Van Assche     if (buff[8] & 0x01)
2180*44704f69SBart Van Assche         printf("primary controller\n");
2181*44704f69SBart Van Assche     else
2182*44704f69SBart Van Assche         printf("alternate controller\n");
2183*44704f69SBart Van Assche 
2184*44704f69SBart Van Assche     if (buff[8] & 0x08) {
2185*44704f69SBart Van Assche         printf("  Path priority: %d ", buff[15] & 0xf);
2186*44704f69SBart Van Assche         switch(buff[15] & 0xf) {
2187*44704f69SBart Van Assche             case 0x1:
2188*44704f69SBart Van Assche                 printf("(preferred path)\n");
2189*44704f69SBart Van Assche                 break;
2190*44704f69SBart Van Assche             case 0x2:
2191*44704f69SBart Van Assche                 printf("(secondary path)\n");
2192*44704f69SBart Van Assche                 break;
2193*44704f69SBart Van Assche             default:
2194*44704f69SBart Van Assche                 printf("(unknown)\n");
2195*44704f69SBart Van Assche                 break;
2196*44704f69SBart Van Assche         }
2197*44704f69SBart Van Assche 
2198*44704f69SBart Van Assche         printf("  Preferred Path Auto Changeable:");
2199*44704f69SBart Van Assche         switch(buff[14] & 0x3C) {
2200*44704f69SBart Van Assche             case 0x14:
2201*44704f69SBart Van Assche                 printf(" No (User Disabled and Host Type Restricted)\n");
2202*44704f69SBart Van Assche                 break;
2203*44704f69SBart Van Assche             case 0x18:
2204*44704f69SBart Van Assche                 printf(" No (User Disabled)\n");
2205*44704f69SBart Van Assche                 break;
2206*44704f69SBart Van Assche             case 0x24:
2207*44704f69SBart Van Assche                 printf(" No (Host Type Restricted)\n");
2208*44704f69SBart Van Assche                 break;
2209*44704f69SBart Van Assche             case 0x28:
2210*44704f69SBart Van Assche                 printf(" Yes\n");
2211*44704f69SBart Van Assche                 break;
2212*44704f69SBart Van Assche             default:
2213*44704f69SBart Van Assche                 printf(" (Unknown)\n");
2214*44704f69SBart Van Assche                 break;
2215*44704f69SBart Van Assche         }
2216*44704f69SBart Van Assche 
2217*44704f69SBart Van Assche         printf("  Implicit Failback:");
2218*44704f69SBart Van Assche         switch(buff[14] & 0x03) {
2219*44704f69SBart Van Assche             case 0x1:
2220*44704f69SBart Van Assche                 printf(" Disabled\n");
2221*44704f69SBart Van Assche                 break;
2222*44704f69SBart Van Assche             case 0x2:
2223*44704f69SBart Van Assche                 printf(" Enabled\n");
2224*44704f69SBart Van Assche                 break;
2225*44704f69SBart Van Assche             default:
2226*44704f69SBart Van Assche                 printf(" (Unknown)\n");
2227*44704f69SBart Van Assche                 break;
2228*44704f69SBart Van Assche         }
2229*44704f69SBart Van Assche     } else {
2230*44704f69SBart Van Assche         printf("  Path priority: %d ", buff[9] & 0xf);
2231*44704f69SBart Van Assche         switch(buff[9] & 0xf) {
2232*44704f69SBart Van Assche             case 0x1:
2233*44704f69SBart Van Assche                 printf("(preferred path)\n");
2234*44704f69SBart Van Assche                 break;
2235*44704f69SBart Van Assche             case 0x2:
2236*44704f69SBart Van Assche                 printf("(secondary path)\n");
2237*44704f69SBart Van Assche                 break;
2238*44704f69SBart Van Assche             default:
2239*44704f69SBart Van Assche                 printf("(unknown)\n");
2240*44704f69SBart Van Assche                 break;
2241*44704f69SBart Van Assche         }
2242*44704f69SBart Van Assche     }
2243*44704f69SBart Van Assche 
2244*44704f69SBart Van Assche     if (buff[8] & 0x80) {
2245*44704f69SBart Van Assche         printf(" Target Port Group Data (This controller):\n");
2246*44704f69SBart Van Assche         decode_rdac_vpd_c9_rtpg_data(buff[10], buff[11]);
2247*44704f69SBart Van Assche 
2248*44704f69SBart Van Assche         printf(" Target Port Group Data (Alternate controller):\n");
2249*44704f69SBart Van Assche         decode_rdac_vpd_c9_rtpg_data(buff[12], buff[13]);
2250*44704f69SBart Van Assche     }
2251*44704f69SBart Van Assche 
2252*44704f69SBart Van Assche     return;
2253*44704f69SBart Van Assche }
2254*44704f69SBart Van Assche #endif
2255*44704f69SBart Van Assche 
2256*44704f69SBart Van Assche extern const char * sg_ansi_version_arr[];
2257*44704f69SBart Van Assche 
2258*44704f69SBart Van Assche static const char *
get_ansi_version_str(int version,char * b,int blen)2259*44704f69SBart Van Assche get_ansi_version_str(int version, char * b, int blen)
2260*44704f69SBart Van Assche {
2261*44704f69SBart Van Assche     version &= 0xf;
2262*44704f69SBart Van Assche     b[blen - 1] = '\0';
2263*44704f69SBart Van Assche     strncpy(b, sg_ansi_version_arr[version], blen - 1);
2264*44704f69SBart Van Assche     return b;
2265*44704f69SBart Van Assche }
2266*44704f69SBart Van Assche 
2267*44704f69SBart Van Assche static void
std_inq_decode(struct opts_t * op,sgj_opaque_p jop,int off)2268*44704f69SBart Van Assche std_inq_decode(struct opts_t * op, sgj_opaque_p jop, int off)
2269*44704f69SBart Van Assche {
2270*44704f69SBart Van Assche     int len, pqual, pdt, ansi_version, k, j;
2271*44704f69SBart Van Assche     sgj_state * jsp = &op->json_st;
2272*44704f69SBart Van Assche     bool as_json = jsp->pr_as_json;
2273*44704f69SBart Van Assche     const char * cp;
2274*44704f69SBart Van Assche     const uint8_t * rp;
2275*44704f69SBart Van Assche     int vdesc_arr[8];
2276*44704f69SBart Van Assche     char b[128];
2277*44704f69SBart Van Assche     static const int blen = sizeof(b);
2278*44704f69SBart Van Assche 
2279*44704f69SBart Van Assche     rp = rsp_buff + off;
2280*44704f69SBart Van Assche     memset(vdesc_arr, 0, sizeof(vdesc_arr));
2281*44704f69SBart Van Assche     if (op->do_raw) {
2282*44704f69SBart Van Assche         dStrRaw((const char *)rp, op->maxlen);
2283*44704f69SBart Van Assche         return;
2284*44704f69SBart Van Assche     } else if (op->do_hex) {
2285*44704f69SBart Van Assche         /* with -H, print with address, -HH without */
2286*44704f69SBart Van Assche         hex2stdout(rp, op->maxlen, no_ascii_4hex(op));
2287*44704f69SBart Van Assche         return;
2288*44704f69SBart Van Assche     }
2289*44704f69SBart Van Assche     pqual = (rp[0] & 0xe0) >> 5;
2290*44704f69SBart Van Assche     if (! op->do_raw && ! op->do_export) {
2291*44704f69SBart Van Assche         strcpy(b, "standard INQUIRY:");
2292*44704f69SBart Van Assche         if (0 == pqual)
2293*44704f69SBart Van Assche             sgj_pr_hr(jsp, "%s\n", b);
2294*44704f69SBart Van Assche         else if (1 == pqual)
2295*44704f69SBart Van Assche             sgj_pr_hr(jsp, "%s [PQ indicates LU temporarily unavailable]\n",
2296*44704f69SBart Van Assche                       b);
2297*44704f69SBart Van Assche         else if (3 == pqual)
2298*44704f69SBart Van Assche             sgj_pr_hr(jsp, "%s [PQ indicates LU not accessible via this "
2299*44704f69SBart Van Assche                       "port]\n", b);
2300*44704f69SBart Van Assche         else
2301*44704f69SBart Van Assche             sgj_pr_hr(jsp, "%s [reserved or vendor specific qualifier "
2302*44704f69SBart Van Assche                       "[%d]]\n", b, pqual);
2303*44704f69SBart Van Assche     }
2304*44704f69SBart Van Assche     len = rp[4] + 5;
2305*44704f69SBart Van Assche     /* N.B. rp[2] full byte is 'version' in SPC-2,3,4 but in SPC
2306*44704f69SBart Van Assche      * [spc-r11a (1997)] bits 6,7: ISO/IEC version; bits 3-5: ECMA
2307*44704f69SBart Van Assche      * version; bits 0-2: SCSI version */
2308*44704f69SBart Van Assche     ansi_version = rp[2] & 0x7;       /* Only take SCSI version */
2309*44704f69SBart Van Assche     pdt = rp[0] & PDT_MASK;
2310*44704f69SBart Van Assche     if (op->do_export) {
2311*44704f69SBart Van Assche         printf("SCSI_TPGS=%d\n", (rp[5] & 0x30) >> 4);
2312*44704f69SBart Van Assche         cp = sg_get_pdt_str(pdt, blen, b);
2313*44704f69SBart Van Assche         if (strlen(cp) > 0)
2314*44704f69SBart Van Assche             printf("SCSI_TYPE=%s\n", cp);
2315*44704f69SBart Van Assche     } else {
2316*44704f69SBart Van Assche         sgj_pr_hr(jsp, "  PQual=%d  PDT=%d  RMB=%d  LU_CONG=%d  "
2317*44704f69SBart Van Assche                   "hot_pluggable=%d  version=0x%02x ", pqual, pdt,
2318*44704f69SBart Van Assche                   !!(rp[1] & 0x80), !!(rp[1] & 0x40), (rp[1] >> 4) & 0x3,
2319*44704f69SBart Van Assche                   (unsigned int)rp[2]);
2320*44704f69SBart Van Assche         sgj_pr_hr(jsp, " [%s]\n", get_ansi_version_str(ansi_version, b,
2321*44704f69SBart Van Assche                                                        blen));
2322*44704f69SBart Van Assche         sgj_pr_hr(jsp, "  [AERC=%d]  [TrmTsk=%d]  NormACA=%d  HiSUP=%d "
2323*44704f69SBart Van Assche                   " Resp_data_format=%d\n  SCCS=%d  ", !!(rp[3] & 0x80),
2324*44704f69SBart Van Assche                   !!(rp[3] & 0x40), !!(rp[3] & 0x20), !!(rp[3] & 0x10),
2325*44704f69SBart Van Assche                   rp[3] & 0x0f, !!(rp[5] & 0x80));
2326*44704f69SBart Van Assche         sgj_pr_hr(jsp, "ACC=%d  TPGS=%d  3PC=%d  Protect=%d ",
2327*44704f69SBart Van Assche                   !!(rp[5] & 0x40), ((rp[5] & 0x30) >> 4), !!(rp[5] & 0x08),
2328*44704f69SBart Van Assche                   !!(rp[5] & 0x01));
2329*44704f69SBart Van Assche         sgj_pr_hr(jsp, " [BQue=%d]\n  EncServ=%d  ", !!(rp[6] & 0x80),
2330*44704f69SBart Van Assche                   !!(rp[6] & 0x40));
2331*44704f69SBart Van Assche         if (rp[6] & 0x10)
2332*44704f69SBart Van Assche             sgj_pr_hr(jsp, "MultiP=1 (VS=%d)  ", !!(rp[6] & 0x20));
2333*44704f69SBart Van Assche         else
2334*44704f69SBart Van Assche             sgj_pr_hr(jsp, "MultiP=0  ");
2335*44704f69SBart Van Assche         sgj_pr_hr(jsp, "[MChngr=%d]  [ACKREQQ=%d]  Addr16=%d\n  "
2336*44704f69SBart Van Assche                   "[RelAdr=%d]  ", !!(rp[6] & 0x08), !!(rp[6] & 0x04),
2337*44704f69SBart Van Assche                   !!(rp[6] & 0x01), !!(rp[7] & 0x80));
2338*44704f69SBart Van Assche         sgj_pr_hr(jsp, "WBus16=%d  Sync=%d  [Linked=%d]  [TranDis=%d]  ",
2339*44704f69SBart Van Assche                   !!(rp[7] & 0x20), !!(rp[7] & 0x10), !!(rp[7] & 0x08),
2340*44704f69SBart Van Assche                   !!(rp[7] & 0x04));
2341*44704f69SBart Van Assche         sgj_pr_hr(jsp, "CmdQue=%d\n", !!(rp[7] & 0x02));
2342*44704f69SBart Van Assche         if (op->maxlen > 56)
2343*44704f69SBart Van Assche             sgj_pr_hr(jsp, "  [SPI: Clocking=0x%x  QAS=%d  IUS=%d]\n",
2344*44704f69SBart Van Assche                       (rp[56] & 0x0c) >> 2, !!(rp[56] & 0x2),
2345*44704f69SBart Van Assche                       !!(rp[56] & 0x1));
2346*44704f69SBart Van Assche         if (op->maxlen >= len)
2347*44704f69SBart Van Assche             sgj_pr_hr(jsp, "    length=%d (0x%x)", len, len);
2348*44704f69SBart Van Assche         else
2349*44704f69SBart Van Assche             sgj_pr_hr(jsp, "    length=%d (0x%x), but only fetched %d bytes",
2350*44704f69SBart Van Assche                       len, len, op->maxlen);
2351*44704f69SBart Van Assche         if ((ansi_version >= 2) && (len < SAFE_STD_INQ_RESP_LEN))
2352*44704f69SBart Van Assche             sgj_pr_hr(jsp, "\n  [for SCSI>=2, len>=36 is expected]");
2353*44704f69SBart Van Assche         cp = sg_get_pdt_str(pdt, blen, b);
2354*44704f69SBart Van Assche         if (strlen(cp) > 0)
2355*44704f69SBart Van Assche             sgj_pr_hr(jsp, "   Peripheral device type: %s\n", cp);
2356*44704f69SBart Van Assche     }
2357*44704f69SBart Van Assche     if (op->maxlen <= 8) {
2358*44704f69SBart Van Assche         if (! op->do_export)
2359*44704f69SBart Van Assche             sgj_pr_hr(jsp, " Inquiry response length=%d, no vendor, product "
2360*44704f69SBart Van Assche                       "or revision data\n", op->maxlen);
2361*44704f69SBart Van Assche     } else {
2362*44704f69SBart Van Assche         int i;
2363*44704f69SBart Van Assche 
2364*44704f69SBart Van Assche         memcpy(xtra_buff, &rp[8], 8);
2365*44704f69SBart Van Assche         xtra_buff[8] = '\0';
2366*44704f69SBart Van Assche         /* Fixup any tab characters */
2367*44704f69SBart Van Assche         for (i = 0; i < 8; ++i)
2368*44704f69SBart Van Assche             if (xtra_buff[i] == 0x09)
2369*44704f69SBart Van Assche                 xtra_buff[i] = ' ';
2370*44704f69SBart Van Assche         if (op->do_export) {
2371*44704f69SBart Van Assche             len = encode_whitespaces((uint8_t *)xtra_buff, 8);
2372*44704f69SBart Van Assche             if (len > 0) {
2373*44704f69SBart Van Assche                 printf("SCSI_VENDOR=%s\n", xtra_buff);
2374*44704f69SBart Van Assche                 encode_string(xtra_buff, &rp[8], 8);
2375*44704f69SBart Van Assche                 printf("SCSI_VENDOR_ENC=%s\n", xtra_buff);
2376*44704f69SBart Van Assche             }
2377*44704f69SBart Van Assche         } else
2378*44704f69SBart Van Assche             sgj_pr_hr(jsp, " Vendor identification: %s\n", xtra_buff);
2379*44704f69SBart Van Assche         if (op->maxlen <= 16) {
2380*44704f69SBart Van Assche             if (! op->do_export)
2381*44704f69SBart Van Assche                 sgj_pr_hr(jsp, " Product identification: <none>\n");
2382*44704f69SBart Van Assche         } else {
2383*44704f69SBart Van Assche             memcpy(xtra_buff, &rp[16], 16);
2384*44704f69SBart Van Assche             xtra_buff[16] = '\0';
2385*44704f69SBart Van Assche             if (op->do_export) {
2386*44704f69SBart Van Assche                 len = encode_whitespaces((uint8_t *)xtra_buff, 16);
2387*44704f69SBart Van Assche                 if (len > 0) {
2388*44704f69SBart Van Assche                     printf("SCSI_MODEL=%s\n", xtra_buff);
2389*44704f69SBart Van Assche                     encode_string(xtra_buff, &rp[16], 16);
2390*44704f69SBart Van Assche                     printf("SCSI_MODEL_ENC=%s\n", xtra_buff);
2391*44704f69SBart Van Assche                 }
2392*44704f69SBart Van Assche             } else
2393*44704f69SBart Van Assche                 sgj_pr_hr(jsp, " Product identification: %s\n", xtra_buff);
2394*44704f69SBart Van Assche         }
2395*44704f69SBart Van Assche         if (op->maxlen <= 32) {
2396*44704f69SBart Van Assche             if (! op->do_export)
2397*44704f69SBart Van Assche                 sgj_pr_hr(jsp, " Product revision level: <none>\n");
2398*44704f69SBart Van Assche         } else {
2399*44704f69SBart Van Assche             memcpy(xtra_buff, &rp[32], 4);
2400*44704f69SBart Van Assche             xtra_buff[4] = '\0';
2401*44704f69SBart Van Assche             if (op->do_export) {
2402*44704f69SBart Van Assche                 len = encode_whitespaces((uint8_t *)xtra_buff, 4);
2403*44704f69SBart Van Assche                 if (len > 0)
2404*44704f69SBart Van Assche                     printf("SCSI_REVISION=%s\n", xtra_buff);
2405*44704f69SBart Van Assche             } else
2406*44704f69SBart Van Assche                 sgj_pr_hr(jsp, " Product revision level: %s\n", xtra_buff);
2407*44704f69SBart Van Assche         }
2408*44704f69SBart Van Assche         if (op->do_vendor && (op->maxlen > 36) && ('\0' != rp[36]) &&
2409*44704f69SBart Van Assche             (' ' != rp[36])) {
2410*44704f69SBart Van Assche             memcpy(xtra_buff, &rp[36], op->maxlen < 56 ? op->maxlen - 36 :
2411*44704f69SBart Van Assche                    20);
2412*44704f69SBart Van Assche             if (op->do_export) {
2413*44704f69SBart Van Assche                 len = encode_whitespaces((uint8_t *)xtra_buff, 20);
2414*44704f69SBart Van Assche                 if (len > 0)
2415*44704f69SBart Van Assche                     printf("VENDOR_SPECIFIC=%s\n", xtra_buff);
2416*44704f69SBart Van Assche             } else
2417*44704f69SBart Van Assche                 sgj_pr_hr(jsp, " Vendor specific: %s\n", xtra_buff);
2418*44704f69SBart Van Assche         }
2419*44704f69SBart Van Assche         if (op->do_descriptors) {
2420*44704f69SBart Van Assche             for (j = 0, k = 58; ((j < 8) && ((k + 1) < op->maxlen));
2421*44704f69SBart Van Assche                  k +=2, ++j)
2422*44704f69SBart Van Assche                 vdesc_arr[j] = sg_get_unaligned_be16(rp + k);
2423*44704f69SBart Van Assche         }
2424*44704f69SBart Van Assche         if ((op->do_vendor > 1) && (op->maxlen > 96)) {
2425*44704f69SBart Van Assche             memcpy(xtra_buff, &rp[96], op->maxlen - 96);
2426*44704f69SBart Van Assche             if (op->do_export) {
2427*44704f69SBart Van Assche                 len = encode_whitespaces((uint8_t *)xtra_buff,
2428*44704f69SBart Van Assche                                          op->maxlen - 96);
2429*44704f69SBart Van Assche                 if (len > 0)
2430*44704f69SBart Van Assche                     printf("VENDOR_SPECIFIC=%s\n", xtra_buff);
2431*44704f69SBart Van Assche             } else
2432*44704f69SBart Van Assche                 sgj_pr_hr(jsp, " Vendor specific: %s\n", xtra_buff);
2433*44704f69SBart Van Assche         }
2434*44704f69SBart Van Assche         if (op->do_vendor && (op->maxlen > 243) &&
2435*44704f69SBart Van Assche             (0 == strncmp("OPEN-V", (const char *)&rp[16], 6))) {
2436*44704f69SBart Van Assche            memcpy(xtra_buff, &rp[212], 32);
2437*44704f69SBart Van Assche            if (op->do_export) {
2438*44704f69SBart Van Assche                 len = encode_whitespaces((uint8_t *)xtra_buff, 32);
2439*44704f69SBart Van Assche                 if (len > 0)
2440*44704f69SBart Van Assche                     printf("VENDOR_SPECIFIC_OPEN-V_LDEV_NAME=%s\n", xtra_buff);
2441*44704f69SBart Van Assche             } else
2442*44704f69SBart Van Assche                 sgj_pr_hr(jsp, " Vendor specific OPEN-V LDEV Name: %s\n",
2443*44704f69SBart Van Assche                           xtra_buff);
2444*44704f69SBart Van Assche         }
2445*44704f69SBart Van Assche     }
2446*44704f69SBart Van Assche     if (! op->do_export) {
2447*44704f69SBart Van Assche         sgj_opaque_p jo2p = NULL;
2448*44704f69SBart Van Assche 
2449*44704f69SBart Van Assche         if (as_json)
2450*44704f69SBart Van Assche             jo2p = std_inq_decode_js(rp, op->maxlen, op, jop);
2451*44704f69SBart Van Assche         if ((0 == op->maxlen) && usn_buff[0])
2452*44704f69SBart Van Assche             sgj_pr_hr(jsp, " Unit serial number: %s\n", usn_buff);
2453*44704f69SBart Van Assche         if (op->do_descriptors) {
2454*44704f69SBart Van Assche             sgj_opaque_p jap = sgj_named_subarray_r(jsp, jo2p,
2455*44704f69SBart Van Assche                                                 "version_descriptor_list");
2456*44704f69SBart Van Assche             if (0 == vdesc_arr[0]) {
2457*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "\n");
2458*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "  No version descriptors available\n");
2459*44704f69SBart Van Assche             } else {
2460*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "\n");
2461*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "  Version descriptors:\n");
2462*44704f69SBart Van Assche                 for (k = 0; k < 8; ++k) {
2463*44704f69SBart Van Assche                     sgj_opaque_p jo3p = sgj_new_unattached_object_r(jsp);
2464*44704f69SBart Van Assche                     int vdv = vdesc_arr[k];
2465*44704f69SBart Van Assche 
2466*44704f69SBart Van Assche                     if (0 == vdv)
2467*44704f69SBart Van Assche                         break;
2468*44704f69SBart Van Assche                     cp = find_version_descriptor_str(vdv);
2469*44704f69SBart Van Assche                     if (cp)
2470*44704f69SBart Van Assche                         sgj_pr_hr(jsp, "    %s\n", cp);
2471*44704f69SBart Van Assche                     else
2472*44704f69SBart Van Assche                         sgj_pr_hr(jsp, "    [unrecognised version descriptor "
2473*44704f69SBart Van Assche                                   "code: 0x%x]\n", vdv);
2474*44704f69SBart Van Assche                     sgj_js_nv_ihexstr(jsp, jo3p, "version_descriptor", vdv,
2475*44704f69SBart Van Assche                                       NULL, cp ? cp : "unknown");
2476*44704f69SBart Van Assche                     sgj_js_nv_o(jsp, jap, NULL /* name */, jo3p);
2477*44704f69SBart Van Assche                 }
2478*44704f69SBart Van Assche             }
2479*44704f69SBart Van Assche         }
2480*44704f69SBart Van Assche     }
2481*44704f69SBart Van Assche }
2482*44704f69SBart Van Assche 
2483*44704f69SBart Van Assche /* Returns 0 if Unit Serial Number VPD page contents found, else see
2484*44704f69SBart Van Assche  * sg_ll_inquiry_v2() return values */
2485*44704f69SBart Van Assche static int
fetch_unit_serial_num(int sg_fd,char * obuff,int obuff_len,int verbose)2486*44704f69SBart Van Assche fetch_unit_serial_num(int sg_fd, char * obuff, int obuff_len, int verbose)
2487*44704f69SBart Van Assche {
2488*44704f69SBart Van Assche     int len, k, res, c;
2489*44704f69SBart Van Assche     uint8_t * b;
2490*44704f69SBart Van Assche     uint8_t * free_b;
2491*44704f69SBart Van Assche 
2492*44704f69SBart Van Assche     b = sg_memalign(DEF_ALLOC_LEN, 0, &free_b, false);
2493*44704f69SBart Van Assche     if (NULL == b) {
2494*44704f69SBart Van Assche         pr2serr("%s: unable to allocate on heap\n", __func__);
2495*44704f69SBart Van Assche         return sg_convert_errno(ENOMEM);
2496*44704f69SBart Van Assche     }
2497*44704f69SBart Van Assche     res = vpd_fetch_page(sg_fd, b, VPD_SUPPORTED_VPDS,
2498*44704f69SBart Van Assche                          -1 /* 1 byte alloc_len */, false, verbose, &len);
2499*44704f69SBart Van Assche     if (res) {
2500*44704f69SBart Van Assche         if (verbose > 2)
2501*44704f69SBart Van Assche             pr2serr("%s: no supported VPDs page\n", __func__);
2502*44704f69SBart Van Assche         res = SG_LIB_CAT_MALFORMED;
2503*44704f69SBart Van Assche         goto fini;
2504*44704f69SBart Van Assche     }
2505*44704f69SBart Van Assche     if (! vpd_page_is_supported(b, len, VPD_UNIT_SERIAL_NUM, verbose)) {
2506*44704f69SBart Van Assche         res = sg_convert_errno(EDOM); /* was SG_LIB_CAT_ILLEGAL_REQ */
2507*44704f69SBart Van Assche         goto fini;
2508*44704f69SBart Van Assche     }
2509*44704f69SBart Van Assche 
2510*44704f69SBart Van Assche     memset(b, 0xff, 4); /* guard against empty response */
2511*44704f69SBart Van Assche     res = vpd_fetch_page(sg_fd, b, VPD_UNIT_SERIAL_NUM, -1, false, verbose,
2512*44704f69SBart Van Assche                          &len);
2513*44704f69SBart Van Assche     if ((0 == res) && (len > 3)) {
2514*44704f69SBart Van Assche         len -= 4;
2515*44704f69SBart Van Assche         len = (len < (obuff_len - 1)) ? len : (obuff_len - 1);
2516*44704f69SBart Van Assche         if (len > 0) {
2517*44704f69SBart Van Assche             /* replace non-printable characters (except NULL) with space */
2518*44704f69SBart Van Assche             for (k = 0; k < len; ++k) {
2519*44704f69SBart Van Assche                 c = b[4 + k];
2520*44704f69SBart Van Assche                 if (c)
2521*44704f69SBart Van Assche                     obuff[k] = isprint(c) ? c : ' ';
2522*44704f69SBart Van Assche                 else
2523*44704f69SBart Van Assche                     break;
2524*44704f69SBart Van Assche             }
2525*44704f69SBart Van Assche             obuff[k] = '\0';
2526*44704f69SBart Van Assche             res = 0;
2527*44704f69SBart Van Assche             goto fini;
2528*44704f69SBart Van Assche         } else {
2529*44704f69SBart Van Assche             if (verbose > 2)
2530*44704f69SBart Van Assche                 pr2serr("%s: bad sn VPD page\n", __func__);
2531*44704f69SBart Van Assche             res = SG_LIB_CAT_MALFORMED;
2532*44704f69SBart Van Assche         }
2533*44704f69SBart Van Assche     } else {
2534*44704f69SBart Van Assche         if (verbose > 2)
2535*44704f69SBart Van Assche             pr2serr("%s: no supported VPDs page\n", __func__);
2536*44704f69SBart Van Assche         res = SG_LIB_CAT_MALFORMED;
2537*44704f69SBart Van Assche     }
2538*44704f69SBart Van Assche fini:
2539*44704f69SBart Van Assche     if (free_b)
2540*44704f69SBart Van Assche         free(free_b);
2541*44704f69SBart Van Assche     return res;
2542*44704f69SBart Van Assche }
2543*44704f69SBart Van Assche 
2544*44704f69SBart Van Assche 
2545*44704f69SBart Van Assche /* Process a standard INQUIRY data format (response).
2546*44704f69SBart Van Assche  * Returns 0 if successful */
2547*44704f69SBart Van Assche static int
std_inq_process(int sg_fd,struct opts_t * op,sgj_opaque_p jop,int off)2548*44704f69SBart Van Assche std_inq_process(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int off)
2549*44704f69SBart Van Assche {
2550*44704f69SBart Van Assche     int res, len, rlen, act_len;
2551*44704f69SBart Van Assche     int vb, resid;
2552*44704f69SBart Van Assche     char buff[48];
2553*44704f69SBart Van Assche 
2554*44704f69SBart Van Assche     if (sg_fd < 0) {    /* assume --inhex=FD usage */
2555*44704f69SBart Van Assche         std_inq_decode(op, jop, off);
2556*44704f69SBart Van Assche         return 0;
2557*44704f69SBart Van Assche     }
2558*44704f69SBart Van Assche     rlen = (op->maxlen > 0) ? op->maxlen : SAFE_STD_INQ_RESP_LEN;
2559*44704f69SBart Van Assche     vb = op->verbose;
2560*44704f69SBart Van Assche     res = sg_ll_inquiry_v2(sg_fd, false, 0, rsp_buff, rlen, DEF_PT_TIMEOUT,
2561*44704f69SBart Van Assche                            &resid, false, vb);
2562*44704f69SBart Van Assche     if (0 == res) {
2563*44704f69SBart Van Assche         if ((vb > 4) && ((rlen - resid) > 0)) {
2564*44704f69SBart Van Assche             pr2serr("Safe (36 byte) Inquiry response:\n");
2565*44704f69SBart Van Assche             hex2stderr(rsp_buff, rlen - resid, 0);
2566*44704f69SBart Van Assche         }
2567*44704f69SBart Van Assche         len = rsp_buff[4] + 5;
2568*44704f69SBart Van Assche         if ((len > SAFE_STD_INQ_RESP_LEN) && (len < 256) &&
2569*44704f69SBart Van Assche             (0 == op->maxlen)) {
2570*44704f69SBart Van Assche             rlen = len;
2571*44704f69SBart Van Assche             memset(rsp_buff, 0, rlen);
2572*44704f69SBart Van Assche             if (sg_ll_inquiry_v2(sg_fd, false, 0, rsp_buff, rlen,
2573*44704f69SBart Van Assche                                  DEF_PT_TIMEOUT, &resid, true, vb)) {
2574*44704f69SBart Van Assche                 pr2serr("second INQUIRY (%d byte) failed\n", len);
2575*44704f69SBart Van Assche                 return SG_LIB_CAT_OTHER;
2576*44704f69SBart Van Assche             }
2577*44704f69SBart Van Assche             if (len != (rsp_buff[4] + 5)) {
2578*44704f69SBart Van Assche                 pr2serr("strange, consecutive INQUIRYs yield different "
2579*44704f69SBart Van Assche                         "'additional lengths'\n");
2580*44704f69SBart Van Assche                 len = rsp_buff[4] + 5;
2581*44704f69SBart Van Assche             }
2582*44704f69SBart Van Assche         }
2583*44704f69SBart Van Assche         if (op->maxlen > 0)
2584*44704f69SBart Van Assche             act_len = rlen;
2585*44704f69SBart Van Assche         else
2586*44704f69SBart Van Assche             act_len = (rlen < len) ? rlen : len;
2587*44704f69SBart Van Assche         /* don't use more than HBA's resid says was transferred from LU */
2588*44704f69SBart Van Assche         if (act_len > (rlen - resid))
2589*44704f69SBart Van Assche             act_len = rlen - resid;
2590*44704f69SBart Van Assche         if (act_len < SAFE_STD_INQ_RESP_LEN)
2591*44704f69SBart Van Assche             rsp_buff[act_len] = '\0';
2592*44704f69SBart Van Assche         if ((! op->do_only) && (! op->do_export) && (0 == op->maxlen)) {
2593*44704f69SBart Van Assche             if (fetch_unit_serial_num(sg_fd, usn_buff, sizeof(usn_buff), vb))
2594*44704f69SBart Van Assche                 usn_buff[0] = '\0';
2595*44704f69SBart Van Assche         }
2596*44704f69SBart Van Assche         op->maxlen = act_len;
2597*44704f69SBart Van Assche         std_inq_decode(op, jop, 0);
2598*44704f69SBart Van Assche         return 0;
2599*44704f69SBart Van Assche     } else if (res < 0) { /* could be an ATA device */
2600*44704f69SBart Van Assche #if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS) && \
2601*44704f69SBart Van Assche     defined(HDIO_GET_IDENTITY)
2602*44704f69SBart Van Assche         /* Try an ATA Identify Device command */
2603*44704f69SBart Van Assche         res = try_ata_identify(sg_fd, op->do_hex, op->do_raw, vb);
2604*44704f69SBart Van Assche         if (0 != res) {
2605*44704f69SBart Van Assche             pr2serr("SCSI INQUIRY, NVMe Identify and fetching ATA "
2606*44704f69SBart Van Assche                     "information failed on %s\n", op->device_name);
2607*44704f69SBart Van Assche             return (res < 0) ? SG_LIB_CAT_OTHER : res;
2608*44704f69SBart Van Assche         }
2609*44704f69SBart Van Assche #else
2610*44704f69SBart Van Assche         pr2serr("SCSI INQUIRY failed on %s, res=%d\n",
2611*44704f69SBart Van Assche                 op->device_name, res);
2612*44704f69SBart Van Assche         return res;
2613*44704f69SBart Van Assche #endif
2614*44704f69SBart Van Assche     } else {
2615*44704f69SBart Van Assche         char b[80];
2616*44704f69SBart Van Assche 
2617*44704f69SBart Van Assche         if (vb > 0) {
2618*44704f69SBart Van Assche             pr2serr("    inquiry: failed requesting %d byte response: ", rlen);
2619*44704f69SBart Van Assche             if (resid && (vb > 1))
2620*44704f69SBart Van Assche                 snprintf(buff, sizeof(buff), " [resid=%d]", resid);
2621*44704f69SBart Van Assche             else
2622*44704f69SBart Van Assche                 buff[0] = '\0';
2623*44704f69SBart Van Assche             sg_get_category_sense_str(res, sizeof(b), b, vb);
2624*44704f69SBart Van Assche             pr2serr("%s%s\n", b, buff);
2625*44704f69SBart Van Assche         }
2626*44704f69SBart Van Assche         return res;
2627*44704f69SBart Van Assche     }
2628*44704f69SBart Van Assche     return 0;
2629*44704f69SBart Van Assche }
2630*44704f69SBart Van Assche 
2631*44704f69SBart Van Assche #ifdef SG_SCSI_STRINGS
2632*44704f69SBart Van Assche /* Returns 0 if successful */
2633*44704f69SBart Van Assche static int
cmddt_process(int sg_fd,const struct opts_t * op)2634*44704f69SBart Van Assche cmddt_process(int sg_fd, const struct opts_t * op)
2635*44704f69SBart Van Assche {
2636*44704f69SBart Van Assche     int k, j, num, len, pdt, reserved_cmddt, support_num, res;
2637*44704f69SBart Van Assche     char op_name[128];
2638*44704f69SBart Van Assche 
2639*44704f69SBart Van Assche     memset(rsp_buff, 0, rsp_buff_sz);
2640*44704f69SBart Van Assche     if (op->do_cmddt > 1) {
2641*44704f69SBart Van Assche         printf("Supported command list:\n");
2642*44704f69SBart Van Assche         for (k = 0; k < 256; ++k) {
2643*44704f69SBart Van Assche             res = sg_ll_inquiry(sg_fd, true /* cmddt */, false, k, rsp_buff,
2644*44704f69SBart Van Assche                                 DEF_ALLOC_LEN, true, op->verbose);
2645*44704f69SBart Van Assche             if (0 == res) {
2646*44704f69SBart Van Assche                 pdt = rsp_buff[0] & PDT_MASK;
2647*44704f69SBart Van Assche                 support_num = rsp_buff[1] & 7;
2648*44704f69SBart Van Assche                 reserved_cmddt = rsp_buff[4];
2649*44704f69SBart Van Assche                 if ((3 == support_num) || (5 == support_num)) {
2650*44704f69SBart Van Assche                     num = rsp_buff[5];
2651*44704f69SBart Van Assche                     for (j = 0; j < num; ++j)
2652*44704f69SBart Van Assche                         printf(" %.2x", (int)rsp_buff[6 + j]);
2653*44704f69SBart Van Assche                     if (5 == support_num)
2654*44704f69SBart Van Assche                         printf("  [vendor specific manner (5)]");
2655*44704f69SBart Van Assche                     sg_get_opcode_name((uint8_t)k, pdt,
2656*44704f69SBart Van Assche                                        sizeof(op_name) - 1, op_name);
2657*44704f69SBart Van Assche                     op_name[sizeof(op_name) - 1] = '\0';
2658*44704f69SBart Van Assche                     printf("  %s\n", op_name);
2659*44704f69SBart Van Assche                 } else if ((4 == support_num) || (6 == support_num))
2660*44704f69SBart Van Assche                     printf("  opcode=0x%.2x vendor specific (%d)\n",
2661*44704f69SBart Van Assche                            k, support_num);
2662*44704f69SBart Van Assche                 else if ((0 == support_num) && (reserved_cmddt > 0)) {
2663*44704f69SBart Van Assche                     printf("  opcode=0x%.2x ignored cmddt bit, "
2664*44704f69SBart Van Assche                            "given standard INQUIRY response, stop\n", k);
2665*44704f69SBart Van Assche                     break;
2666*44704f69SBart Van Assche                 }
2667*44704f69SBart Van Assche             } else if (SG_LIB_CAT_ILLEGAL_REQ == res)
2668*44704f69SBart Van Assche                 break;
2669*44704f69SBart Van Assche             else {
2670*44704f69SBart Van Assche                 pr2serr("CmdDt INQUIRY on opcode=0x%.2x: failed\n", k);
2671*44704f69SBart Van Assche                 break;
2672*44704f69SBart Van Assche             }
2673*44704f69SBart Van Assche         }
2674*44704f69SBart Van Assche     }
2675*44704f69SBart Van Assche     else {
2676*44704f69SBart Van Assche         res = sg_ll_inquiry(sg_fd, true /* cmddt */, false, op->vpd_pn,
2677*44704f69SBart Van Assche                             rsp_buff, DEF_ALLOC_LEN, true, op->verbose);
2678*44704f69SBart Van Assche         if (0 == res) {
2679*44704f69SBart Van Assche             pdt = rsp_buff[0] & PDT_MASK;
2680*44704f69SBart Van Assche             if (! op->do_raw) {
2681*44704f69SBart Van Assche                 printf("CmdDt INQUIRY, opcode=0x%.2x:  [", op->vpd_pn);
2682*44704f69SBart Van Assche                 sg_get_opcode_name((uint8_t)op->vpd_pn, pdt,
2683*44704f69SBart Van Assche                                    sizeof(op_name) - 1, op_name);
2684*44704f69SBart Van Assche                 op_name[sizeof(op_name) - 1] = '\0';
2685*44704f69SBart Van Assche                 printf("%s]\n", op_name);
2686*44704f69SBart Van Assche             }
2687*44704f69SBart Van Assche             len = rsp_buff[5] + 6;
2688*44704f69SBart Van Assche             reserved_cmddt = rsp_buff[4];
2689*44704f69SBart Van Assche             if (op->do_hex)
2690*44704f69SBart Van Assche                 hex2stdout(rsp_buff, len, no_ascii_4hex(op));
2691*44704f69SBart Van Assche             else if (op->do_raw)
2692*44704f69SBart Van Assche                 dStrRaw((const char *)rsp_buff, len);
2693*44704f69SBart Van Assche             else {
2694*44704f69SBart Van Assche                 bool prnt_cmd = false;
2695*44704f69SBart Van Assche                 const char * desc_p;
2696*44704f69SBart Van Assche 
2697*44704f69SBart Van Assche                 support_num = rsp_buff[1] & 7;
2698*44704f69SBart Van Assche                 num = rsp_buff[5];
2699*44704f69SBart Van Assche                 switch (support_num) {
2700*44704f69SBart Van Assche                 case 0:
2701*44704f69SBart Van Assche                     if (0 == reserved_cmddt)
2702*44704f69SBart Van Assche                         desc_p = "no data available";
2703*44704f69SBart Van Assche                     else
2704*44704f69SBart Van Assche                         desc_p = "ignored cmddt bit, standard INQUIRY "
2705*44704f69SBart Van Assche                                  "response";
2706*44704f69SBart Van Assche                     break;
2707*44704f69SBart Van Assche                 case 1: desc_p = "not supported"; break;
2708*44704f69SBart Van Assche                 case 2: desc_p = "reserved (2)"; break;
2709*44704f69SBart Van Assche                 case 3: desc_p = "supported as per standard";
2710*44704f69SBart Van Assche                         prnt_cmd = true;
2711*44704f69SBart Van Assche                         break;
2712*44704f69SBart Van Assche                 case 4: desc_p = "vendor specific (4)"; break;
2713*44704f69SBart Van Assche                 case 5: desc_p = "supported in vendor specific way";
2714*44704f69SBart Van Assche                         prnt_cmd = true;
2715*44704f69SBart Van Assche                         break;
2716*44704f69SBart Van Assche                 case 6: desc_p = "vendor specific (6)"; break;
2717*44704f69SBart Van Assche                 case 7: desc_p = "reserved (7)"; break;
2718*44704f69SBart Van Assche                 }
2719*44704f69SBart Van Assche                 if (prnt_cmd) {
2720*44704f69SBart Van Assche                     printf("  Support field: %s [", desc_p);
2721*44704f69SBart Van Assche                     for (j = 0; j < num; ++j)
2722*44704f69SBart Van Assche                         printf(" %.2x", (int)rsp_buff[6 + j]);
2723*44704f69SBart Van Assche                     printf(" ]\n");
2724*44704f69SBart Van Assche                 } else
2725*44704f69SBart Van Assche                     printf("  Support field: %s\n", desc_p);
2726*44704f69SBart Van Assche             }
2727*44704f69SBart Van Assche         } else if (SG_LIB_CAT_ILLEGAL_REQ != res) {
2728*44704f69SBart Van Assche             if (! op->do_raw) {
2729*44704f69SBart Van Assche                 printf("CmdDt INQUIRY, opcode=0x%.2x:  [", op->vpd_pn);
2730*44704f69SBart Van Assche                 sg_get_opcode_name((uint8_t)op->vpd_pn, 0,
2731*44704f69SBart Van Assche                                    sizeof(op_name) - 1, op_name);
2732*44704f69SBart Van Assche                 op_name[sizeof(op_name) - 1] = '\0';
2733*44704f69SBart Van Assche                 printf("%s]\n", op_name);
2734*44704f69SBart Van Assche             }
2735*44704f69SBart Van Assche             pr2serr("CmdDt INQUIRY on opcode=0x%.2x: failed\n", op->vpd_pn);
2736*44704f69SBart Van Assche         }
2737*44704f69SBart Van Assche     }
2738*44704f69SBart Van Assche     return res;
2739*44704f69SBart Van Assche }
2740*44704f69SBart Van Assche 
2741*44704f69SBart Van Assche #else /* SG_SCSI_STRINGS */
2742*44704f69SBart Van Assche 
2743*44704f69SBart Van Assche /* Returns 0. */
2744*44704f69SBart Van Assche static int
cmddt_process(int sg_fd,const struct opts_t * op)2745*44704f69SBart Van Assche cmddt_process(int sg_fd, const struct opts_t * op)
2746*44704f69SBart Van Assche {
2747*44704f69SBart Van Assche     if (sg_fd) { }      /* suppress warning */
2748*44704f69SBart Van Assche     if (op) { }         /* suppress warning */
2749*44704f69SBart Van Assche     pr2serr("'--cmddt' not implemented, use sg_opcodes\n");
2750*44704f69SBart Van Assche     return 0;
2751*44704f69SBart Van Assche }
2752*44704f69SBart Van Assche 
2753*44704f69SBart Van Assche #endif /* SG_SCSI_STRINGS */
2754*44704f69SBart Van Assche 
2755*44704f69SBart Van Assche 
2756*44704f69SBart Van Assche /* Returns 0 if successful */
2757*44704f69SBart Van Assche static int
vpd_mainly_hex(int sg_fd,struct opts_t * op,sgj_opaque_p jap,int off)2758*44704f69SBart Van Assche vpd_mainly_hex(int sg_fd, struct opts_t * op, sgj_opaque_p jap, int off)
2759*44704f69SBart Van Assche {
2760*44704f69SBart Van Assche     bool as_json;
2761*44704f69SBart Van Assche     bool json_o_hr;
2762*44704f69SBart Van Assche     int res, len, n;
2763*44704f69SBart Van Assche     char b[128];
2764*44704f69SBart Van Assche     sgj_state * jsp = &op->json_st;
2765*44704f69SBart Van Assche     const char * cp;
2766*44704f69SBart Van Assche     uint8_t * rp;
2767*44704f69SBart Van Assche 
2768*44704f69SBart Van Assche     as_json = jsp->pr_as_json;
2769*44704f69SBart Van Assche     json_o_hr = as_json && jsp->pr_out_hr;
2770*44704f69SBart Van Assche     rp = rsp_buff + off;
2771*44704f69SBart Van Assche     if ((! op->do_raw) && (op->do_hex < 3)) {
2772*44704f69SBart Van Assche         if (op->do_hex)
2773*44704f69SBart Van Assche             printf("VPD INQUIRY, page code=0x%.2x:\n", op->vpd_pn);
2774*44704f69SBart Van Assche         else
2775*44704f69SBart Van Assche             sgj_pr_hr(jsp, "VPD INQUIRY, page code=0x%.2x:\n", op->vpd_pn);
2776*44704f69SBart Van Assche     }
2777*44704f69SBart Van Assche     if (sg_fd < 0) {
2778*44704f69SBart Van Assche         len = sg_get_unaligned_be16(rp + 2) + 4;
2779*44704f69SBart Van Assche         res = 0;
2780*44704f69SBart Van Assche     } else {
2781*44704f69SBart Van Assche         memset(rp, 0, DEF_ALLOC_LEN);
2782*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, op->vpd_pn, op->maxlen,
2783*44704f69SBart Van Assche                              op->do_quiet, op->verbose, &len);
2784*44704f69SBart Van Assche     }
2785*44704f69SBart Van Assche     if (0 == res) {
2786*44704f69SBart Van Assche         if (op->do_raw)
2787*44704f69SBart Van Assche             dStrRaw((const char *)rp, len);
2788*44704f69SBart Van Assche         else {
2789*44704f69SBart Van Assche             int pdt = pdt = rp[0] & PDT_MASK;
2790*44704f69SBart Van Assche 
2791*44704f69SBart Van Assche             if (0 == op->vpd_pn)
2792*44704f69SBart Van Assche                 decode_supported_vpd_4inq(rp, len, op, jap);
2793*44704f69SBart Van Assche             else {
2794*44704f69SBart Van Assche                 if (op->verbose) {
2795*44704f69SBart Van Assche                     cp = sg_get_pdt_str(pdt, sizeof(b), b);
2796*44704f69SBart Van Assche                     if (op->do_hex)
2797*44704f69SBart Van Assche                         printf("   [PQual=%d  Peripheral device type: %s]\n",
2798*44704f69SBart Van Assche                                (rp[0] & 0xe0) >> 5, cp);
2799*44704f69SBart Van Assche                     else
2800*44704f69SBart Van Assche                         sgj_pr_hr(jsp, "   [PQual=%d  Peripheral device "
2801*44704f69SBart Van Assche                                   "type: %s]\n", (rp[0] & 0xe0) >> 5, cp);
2802*44704f69SBart Van Assche                 }
2803*44704f69SBart Van Assche                 if (json_o_hr && (0 == op->do_hex) && (len > 0) &&
2804*44704f69SBart Van Assche                     (len < UINT16_MAX)) {
2805*44704f69SBart Van Assche                     char * p;
2806*44704f69SBart Van Assche 
2807*44704f69SBart Van Assche                     n = len * 4;
2808*44704f69SBart Van Assche                     p = malloc(n);
2809*44704f69SBart Van Assche                     if (p) {
2810*44704f69SBart Van Assche                         n = hex2str(rp, len, NULL, 1, n - 1, p);
2811*44704f69SBart Van Assche                         sgj_js_str_out(jsp, p, n);
2812*44704f69SBart Van Assche                     }
2813*44704f69SBart Van Assche                 } else
2814*44704f69SBart Van Assche                     hex2stdout(rp, len, no_ascii_4hex(op));
2815*44704f69SBart Van Assche             }
2816*44704f69SBart Van Assche         }
2817*44704f69SBart Van Assche     } else {
2818*44704f69SBart Van Assche         if (SG_LIB_CAT_ILLEGAL_REQ == res)
2819*44704f69SBart Van Assche             pr2serr("    inquiry: field in cdb illegal (page not "
2820*44704f69SBart Van Assche                     "supported)\n");
2821*44704f69SBart Van Assche         else {
2822*44704f69SBart Van Assche             sg_get_category_sense_str(res, sizeof(b), b, op->verbose);
2823*44704f69SBart Van Assche             pr2serr("    inquiry: %s\n", b);
2824*44704f69SBart Van Assche         }
2825*44704f69SBart Van Assche     }
2826*44704f69SBart Van Assche     return res;
2827*44704f69SBart Van Assche }
2828*44704f69SBart Van Assche 
2829*44704f69SBart Van Assche static int
recurse_vpd_decode(struct opts_t * op,sgj_opaque_p jop,int off)2830*44704f69SBart Van Assche recurse_vpd_decode(struct opts_t * op, sgj_opaque_p jop, int off)
2831*44704f69SBart Van Assche {
2832*44704f69SBart Van Assche     return vpd_decode(-1, op, jop, off);
2833*44704f69SBart Van Assche }
2834*44704f69SBart Van Assche 
2835*44704f69SBart Van Assche /* Returns 0 if successful */
2836*44704f69SBart Van Assche static int
vpd_decode(int sg_fd,struct opts_t * op,sgj_opaque_p jop,int off)2837*44704f69SBart Van Assche vpd_decode(int sg_fd, struct opts_t * op, sgj_opaque_p jop, int off)
2838*44704f69SBart Van Assche {
2839*44704f69SBart Van Assche     bool bad = false;
2840*44704f69SBart Van Assche     bool qt = op->do_quiet;
2841*44704f69SBart Van Assche     int len, pdt, pn, vb /*, pqual */;
2842*44704f69SBart Van Assche     int res = 0;
2843*44704f69SBart Van Assche     sgj_state * jsp = &op->json_st;
2844*44704f69SBart Van Assche     bool as_json = jsp->pr_as_json;
2845*44704f69SBart Van Assche     sgj_opaque_p jo2p = NULL;
2846*44704f69SBart Van Assche     sgj_opaque_p jap = NULL;
2847*44704f69SBart Van Assche     const char * np;
2848*44704f69SBart Van Assche     const char * ep = "";
2849*44704f69SBart Van Assche     uint8_t * rp;
2850*44704f69SBart Van Assche 
2851*44704f69SBart Van Assche     rp = rsp_buff + off;
2852*44704f69SBart Van Assche     vb = op->verbose;
2853*44704f69SBart Van Assche     if ((off > 0) && (VPD_NOPE_WANT_STD_INQ != op->vpd_pn))
2854*44704f69SBart Van Assche         pn = rp[1];
2855*44704f69SBart Van Assche     else
2856*44704f69SBart Van Assche         pn = op->vpd_pn;
2857*44704f69SBart Van Assche     if (sg_fd != -1 && !op->do_force && pn != VPD_SUPPORTED_VPDS) {
2858*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, VPD_SUPPORTED_VPDS, op->maxlen,
2859*44704f69SBart Van Assche                              qt, vb, &len);
2860*44704f69SBart Van Assche         if (res)
2861*44704f69SBart Van Assche             goto out;
2862*44704f69SBart Van Assche         if (! vpd_page_is_supported(rp, len, pn, vb)) {
2863*44704f69SBart Van Assche             if (vb)
2864*44704f69SBart Van Assche                 pr2serr("Given VPD page not in supported list, use --force "
2865*44704f69SBart Van Assche                         "to override this check\n");
2866*44704f69SBart Van Assche             res = sg_convert_errno(EDOM); /* was SG_LIB_CAT_ILLEGAL_REQ */
2867*44704f69SBart Van Assche             goto out;
2868*44704f69SBart Van Assche         }
2869*44704f69SBart Van Assche     }
2870*44704f69SBart Van Assche     switch (pn) {
2871*44704f69SBart Van Assche     case VPD_SUPPORTED_VPDS:            /* 0x0  ["sv"] */
2872*44704f69SBart Van Assche         np = "Supported VPD pages VPD page";
2873*44704f69SBart Van Assche         if (!op->do_raw && (op->do_hex < 3))
2874*44704f69SBart Van Assche             sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np);
2875*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
2876*44704f69SBart Van Assche         if (res)
2877*44704f69SBart Van Assche             break;
2878*44704f69SBart Van Assche         if (op->do_raw)
2879*44704f69SBart Van Assche             dStrRaw((const char *)rp, len);
2880*44704f69SBart Van Assche         else if (op->do_hex)
2881*44704f69SBart Van Assche             hex2stdout(rp, len, no_ascii_4hex(op));
2882*44704f69SBart Van Assche         else {
2883*44704f69SBart Van Assche             if (as_json) {
2884*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
2885*44704f69SBart Van Assche                 jap = sgj_named_subarray_r(jsp, jo2p,
2886*44704f69SBart Van Assche                                   "supported_vpd_page_list");
2887*44704f69SBart Van Assche             }
2888*44704f69SBart Van Assche             decode_supported_vpd_4inq(rp, len, op, jap);
2889*44704f69SBart Van Assche         }
2890*44704f69SBart Van Assche         break;
2891*44704f69SBart Van Assche     case VPD_UNIT_SERIAL_NUM:           /* 0x80  ["sn"] */
2892*44704f69SBart Van Assche         np = "Unit serial number VPD page";
2893*44704f69SBart Van Assche         if (! op->do_raw && ! op->do_export && (op->do_hex < 3))
2894*44704f69SBart Van Assche             sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np);
2895*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
2896*44704f69SBart Van Assche         if (res)
2897*44704f69SBart Van Assche             break;
2898*44704f69SBart Van Assche         if (op->do_raw)
2899*44704f69SBart Van Assche             dStrRaw((const char *)rp, len);
2900*44704f69SBart Van Assche         else if (op->do_hex)
2901*44704f69SBart Van Assche             hex2stdout(rp, len, no_ascii_4hex(op));
2902*44704f69SBart Van Assche         else {
2903*44704f69SBart Van Assche             char obuff[DEF_ALLOC_LEN];
2904*44704f69SBart Van Assche             int k, m;
2905*44704f69SBart Van Assche 
2906*44704f69SBart Van Assche             memset(obuff, 0, sizeof(obuff));
2907*44704f69SBart Van Assche             len -= 4;
2908*44704f69SBart Van Assche             if (len >= (int)sizeof(obuff))
2909*44704f69SBart Van Assche                 len = sizeof(obuff) - 1;
2910*44704f69SBart Van Assche             memcpy(obuff, rp + 4, len);
2911*44704f69SBart Van Assche             if (op->do_export) {
2912*44704f69SBart Van Assche                 k = encode_whitespaces((uint8_t *)obuff, len);
2913*44704f69SBart Van Assche                 if (k > 0) {
2914*44704f69SBart Van Assche                     printf("SCSI_IDENT_SERIAL=");
2915*44704f69SBart Van Assche                     /* udev-conforming character encoding */
2916*44704f69SBart Van Assche                     for (m = 0; m < k; ++m) {
2917*44704f69SBart Van Assche                         if ((obuff[m] >= '0' && obuff[m] <= '9') ||
2918*44704f69SBart Van Assche                             (obuff[m] >= 'A' && obuff[m] <= 'Z') ||
2919*44704f69SBart Van Assche                             (obuff[m] >= 'a' && obuff[m] <= 'z') ||
2920*44704f69SBart Van Assche                             strchr("#+-.:=@_", obuff[m]) != NULL)
2921*44704f69SBart Van Assche                             printf("%c", obuff[m]);
2922*44704f69SBart Van Assche                         else
2923*44704f69SBart Van Assche                             printf("\\x%02x", obuff[m]);
2924*44704f69SBart Van Assche                     }
2925*44704f69SBart Van Assche                     printf("\n");
2926*44704f69SBart Van Assche                 }
2927*44704f69SBart Van Assche             } else {
2928*44704f69SBart Van Assche                 if (as_json)
2929*44704f69SBart Van Assche                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
2930*44704f69SBart Van Assche                 k = encode_unicode((uint8_t *)obuff, len);
2931*44704f69SBart Van Assche                 if (k > 0) {
2932*44704f69SBart Van Assche                     sgj_pr_hr(jsp, "  Unit serial number: %s\n", obuff);
2933*44704f69SBart Van Assche                     sgj_js_nv_s(jsp, jo2p, "unit_serial_number", obuff);
2934*44704f69SBart Van Assche                 }
2935*44704f69SBart Van Assche             }
2936*44704f69SBart Van Assche         }
2937*44704f69SBart Van Assche         break;
2938*44704f69SBart Van Assche     case VPD_DEVICE_ID:         /* 0x83  ["di"] */
2939*44704f69SBart Van Assche         np = "Device Identification VPD page";
2940*44704f69SBart Van Assche         if (! op->do_raw && ! op->do_export && (op->do_hex < 3))
2941*44704f69SBart Van Assche             sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np);
2942*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
2943*44704f69SBart Van Assche         if (res)
2944*44704f69SBart Van Assche             break;
2945*44704f69SBart Van Assche         if (op->do_raw)
2946*44704f69SBart Van Assche             dStrRaw((const char *)rp, len);
2947*44704f69SBart Van Assche         else if (op->do_hex > 2)
2948*44704f69SBart Van Assche             hex2stdout(rp, len, -1);
2949*44704f69SBart Van Assche         else if (op->do_export && (! as_json))
2950*44704f69SBart Van Assche             export_dev_ids(rp + 4, len - 4, op->verbose);
2951*44704f69SBart Van Assche         else {
2952*44704f69SBart Van Assche             if (as_json) {
2953*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
2954*44704f69SBart Van Assche                 jap = sgj_named_subarray_r(jsp, jo2p,
2955*44704f69SBart Van Assche                                   "designation_descriptor_list");
2956*44704f69SBart Van Assche             }
2957*44704f69SBart Van Assche             decode_id_vpd(rp, len, op, jap);
2958*44704f69SBart Van Assche         }
2959*44704f69SBart Van Assche         break;
2960*44704f69SBart Van Assche     case VPD_SOFTW_INF_ID:      /* 0x84  ["sii"] */
2961*44704f69SBart Van Assche         np = "Software interface identification VPD page";
2962*44704f69SBart Van Assche         if (! op->do_raw && (op->do_hex < 3))
2963*44704f69SBart Van Assche             sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np);
2964*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
2965*44704f69SBart Van Assche         if (res)
2966*44704f69SBart Van Assche             break;
2967*44704f69SBart Van Assche         if (op->do_raw)
2968*44704f69SBart Van Assche             dStrRaw((const char *)rp, len);
2969*44704f69SBart Van Assche         else {
2970*44704f69SBart Van Assche             if (as_json) {
2971*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
2972*44704f69SBart Van Assche                 jap = sgj_named_subarray_r(jsp, jo2p,
2973*44704f69SBart Van Assche                                   "software_interface_identifier_list");
2974*44704f69SBart Van Assche             }
2975*44704f69SBart Van Assche             decode_softw_inf_id(rp, len, op, jap);
2976*44704f69SBart Van Assche         }
2977*44704f69SBart Van Assche         break;
2978*44704f69SBart Van Assche     case VPD_MAN_NET_ADDR:    /* 0x85 ["mna"] */
2979*44704f69SBart Van Assche         np = "Management network addresses page";
2980*44704f69SBart Van Assche         if (!op->do_raw && (op->do_hex < 3))
2981*44704f69SBart Van Assche             sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np);
2982*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
2983*44704f69SBart Van Assche         if (res)
2984*44704f69SBart Van Assche             break;
2985*44704f69SBart Van Assche         if (op->do_raw)
2986*44704f69SBart Van Assche             dStrRaw((const char *)rp, len);
2987*44704f69SBart Van Assche         else {
2988*44704f69SBart Van Assche             // pdt = rp[0] & PDT_MASK;
2989*44704f69SBart Van Assche             // pdt_str = sg_get_pdt_str(pdt, sizeof(d), d);
2990*44704f69SBart Van Assche             // pqual = (rp[0] & 0xe0) >> 5;
2991*44704f69SBart Van Assche             if (as_json) {
2992*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
2993*44704f69SBart Van Assche                 jap = sgj_named_subarray_r(jsp, jo2p,
2994*44704f69SBart Van Assche                                   "network_services_descriptor_list");
2995*44704f69SBart Van Assche             }
2996*44704f69SBart Van Assche             decode_net_man_vpd(rp, len, op, jap);
2997*44704f69SBart Van Assche         }
2998*44704f69SBart Van Assche         break;
2999*44704f69SBart Van Assche     case VPD_EXT_INQ:           /* 0x86  ["ei"] */
3000*44704f69SBart Van Assche         np = "Extended INQUIRY data";
3001*44704f69SBart Van Assche         if (!op->do_raw && (op->do_hex < 3))
3002*44704f69SBart Van Assche             sgj_pr_hr(jsp, "VPD INQUIRY: %s page\n", np);
3003*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3004*44704f69SBart Van Assche         if (res)
3005*44704f69SBart Van Assche             break;
3006*44704f69SBart Van Assche         if (op->do_raw)
3007*44704f69SBart Van Assche             dStrRaw((const char *)rp, len);
3008*44704f69SBart Van Assche         else {
3009*44704f69SBart Van Assche             bool protect = false;
3010*44704f69SBart Van Assche 
3011*44704f69SBart Van Assche             op->protect_not_sure = false;
3012*44704f69SBart Van Assche             if ((sg_fd >= 0) && (! op->do_force)) {
3013*44704f69SBart Van Assche                 struct sg_simple_inquiry_resp sir;
3014*44704f69SBart Van Assche 
3015*44704f69SBart Van Assche                 res = sg_simple_inquiry(sg_fd, &sir, false, vb);
3016*44704f69SBart Van Assche                 if (res) {
3017*44704f69SBart Van Assche                     if (op->verbose)
3018*44704f69SBart Van Assche                         pr2serr("%s: sg_simple_inquiry() failed, res=%d\n",
3019*44704f69SBart Van Assche                                 __func__, res);
3020*44704f69SBart Van Assche                     op->protect_not_sure = true;
3021*44704f69SBart Van Assche                 } else
3022*44704f69SBart Van Assche                     protect = !!(sir.byte_5 & 0x1); /* SPC-3 and later */
3023*44704f69SBart Van Assche             } else
3024*44704f69SBart Van Assche                 op->protect_not_sure = true;
3025*44704f69SBart Van Assche             if (as_json)
3026*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3027*44704f69SBart Van Assche             decode_x_inq_vpd(rp, len, protect, op, jo2p);
3028*44704f69SBart Van Assche         }
3029*44704f69SBart Van Assche         break;
3030*44704f69SBart Van Assche     case VPD_MODE_PG_POLICY:            /*  0x87  ["mpp"] */
3031*44704f69SBart Van Assche         np = "Mode page policy";
3032*44704f69SBart Van Assche         if (!op->do_raw && (op->do_hex < 3))
3033*44704f69SBart Van Assche             sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np);
3034*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3035*44704f69SBart Van Assche         if (res)
3036*44704f69SBart Van Assche             break;
3037*44704f69SBart Van Assche         if (op->do_raw)
3038*44704f69SBart Van Assche             dStrRaw((const char *)rp, len);
3039*44704f69SBart Van Assche         else {
3040*44704f69SBart Van Assche             if (as_json) {
3041*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3042*44704f69SBart Van Assche                 jap = sgj_named_subarray_r(jsp, jo2p,
3043*44704f69SBart Van Assche                                   "mode_page_policy_descriptor_list");
3044*44704f69SBart Van Assche             }
3045*44704f69SBart Van Assche             decode_mode_policy_vpd(rp, len, op, jap);
3046*44704f69SBart Van Assche         }
3047*44704f69SBart Van Assche         break;
3048*44704f69SBart Van Assche     case VPD_SCSI_PORTS:        /* 0x88  ["sp"] */
3049*44704f69SBart Van Assche         np = "SCSI Ports VPD page";
3050*44704f69SBart Van Assche         if (!op->do_raw && (op->do_hex < 3))
3051*44704f69SBart Van Assche             sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np);
3052*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3053*44704f69SBart Van Assche         if (res)
3054*44704f69SBart Van Assche             break;
3055*44704f69SBart Van Assche         if (op->do_raw)
3056*44704f69SBart Van Assche             dStrRaw((const char *)rp, len);
3057*44704f69SBart Van Assche         else {
3058*44704f69SBart Van Assche             if (as_json) {
3059*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3060*44704f69SBart Van Assche                 jap = sgj_named_subarray_r(jsp, jo2p,
3061*44704f69SBart Van Assche                                   "scsi_ports_descriptor_list");
3062*44704f69SBart Van Assche             }
3063*44704f69SBart Van Assche             decode_scsi_ports_vpd_4inq(rp, len, op, jap);
3064*44704f69SBart Van Assche         }
3065*44704f69SBart Van Assche         break;
3066*44704f69SBart Van Assche     case VPD_ATA_INFO:          /* 0x89  ["ai"] */
3067*44704f69SBart Van Assche         np = "ATA information VPD page";
3068*44704f69SBart Van Assche         if (!op->do_raw && (op->do_hex < 3))
3069*44704f69SBart Van Assche             sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np);
3070*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3071*44704f69SBart Van Assche         if (res)
3072*44704f69SBart Van Assche             break;
3073*44704f69SBart Van Assche         /* format output for 'hdparm --Istdin' with '-rr' or '-HHH' */
3074*44704f69SBart Van Assche         if ((2 == op->do_raw) || (3 == op->do_hex))
3075*44704f69SBart Van Assche             dWordHex((const unsigned short *)(rp + 60), 256, -2,
3076*44704f69SBart Van Assche                      sg_is_big_endian());
3077*44704f69SBart Van Assche         else if (op->do_raw)
3078*44704f69SBart Van Assche             dStrRaw((const char *)rp, len);
3079*44704f69SBart Van Assche         else {
3080*44704f69SBart Van Assche             if (as_json)
3081*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3082*44704f69SBart Van Assche             else
3083*44704f69SBart Van Assche                 op->do_long = true;
3084*44704f69SBart Van Assche             decode_ata_info_vpd(rp, len, op, jo2p);
3085*44704f69SBart Van Assche         }
3086*44704f69SBart Van Assche         break;
3087*44704f69SBart Van Assche     case VPD_POWER_CONDITION:   /* 0x8a   ["pc"] */
3088*44704f69SBart Van Assche         np = "Power condition VPD page";
3089*44704f69SBart Van Assche         if (!op->do_raw && (op->do_hex < 3))
3090*44704f69SBart Van Assche             sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np);
3091*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3092*44704f69SBart Van Assche         if (res)
3093*44704f69SBart Van Assche             break;
3094*44704f69SBart Van Assche         if (op->do_raw)
3095*44704f69SBart Van Assche             dStrRaw((const char *)rp, len);
3096*44704f69SBart Van Assche         else {
3097*44704f69SBart Van Assche             if (as_json)
3098*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3099*44704f69SBart Van Assche             decode_power_condition(rp, len, op, jo2p);
3100*44704f69SBart Van Assche         }
3101*44704f69SBart Van Assche         break;
3102*44704f69SBart Van Assche     case VPD_DEVICE_CONSTITUENTS:       /* 0x8b  ["dc"] */
3103*44704f69SBart Van Assche         np = "Device constituents page VPD page";
3104*44704f69SBart Van Assche         if (!op->do_raw && (op->do_hex < 3))
3105*44704f69SBart Van Assche             sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np);
3106*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3107*44704f69SBart Van Assche         if (res)
3108*44704f69SBart Van Assche             break;
3109*44704f69SBart Van Assche         if (op->do_raw)
3110*44704f69SBart Van Assche             dStrRaw((const char *)rp, len);
3111*44704f69SBart Van Assche         else {
3112*44704f69SBart Van Assche             if (as_json) {
3113*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3114*44704f69SBart Van Assche                 jap = sgj_named_subarray_r(jsp, jo2p,
3115*44704f69SBart Van Assche                                   "constituent_descriptor_list");
3116*44704f69SBart Van Assche             }
3117*44704f69SBart Van Assche             decode_dev_constit_vpd(rp, len, op, jap, recurse_vpd_decode);
3118*44704f69SBart Van Assche         }
3119*44704f69SBart Van Assche         break;
3120*44704f69SBart Van Assche     case VPD_CFA_PROFILE_INFO:    /* 0x8c ["cfa"] */
3121*44704f69SBart Van Assche         np = "CFA profile information VPD page";
3122*44704f69SBart Van Assche         if (!op->do_raw && (op->do_hex < 3))
3123*44704f69SBart Van Assche             sgj_pr_hr(jsp, "%s:\n", np);
3124*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3125*44704f69SBart Van Assche         if (0 == res) {
3126*44704f69SBart Van Assche             if (op->do_raw)
3127*44704f69SBart Van Assche                 dStrRaw((const char *)rp, len);
3128*44704f69SBart Van Assche             else {
3129*44704f69SBart Van Assche                 if (as_json) {
3130*44704f69SBart Van Assche                     jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3131*44704f69SBart Van Assche                     jap = sgj_named_subarray_r(jsp, jo2p,
3132*44704f69SBart Van Assche                                       "cfa_profile_descriptor_list");
3133*44704f69SBart Van Assche                 }
3134*44704f69SBart Van Assche                 decode_cga_profile_vpd(rp, len, op, jap);
3135*44704f69SBart Van Assche             }
3136*44704f69SBart Van Assche         }
3137*44704f69SBart Van Assche         break;
3138*44704f69SBart Van Assche     case VPD_POWER_CONSUMPTION:   /* 0x8d   ["psm"] */
3139*44704f69SBart Van Assche         np = "Power consumption VPD page";
3140*44704f69SBart Van Assche         if (!op->do_raw && (op->do_hex < 3))
3141*44704f69SBart Van Assche             sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np);
3142*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3143*44704f69SBart Van Assche         if (res)
3144*44704f69SBart Van Assche             break;
3145*44704f69SBart Van Assche         if (op->do_raw)
3146*44704f69SBart Van Assche             dStrRaw((const char *)rp, len);
3147*44704f69SBart Van Assche         else {
3148*44704f69SBart Van Assche             if (as_json) {
3149*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3150*44704f69SBart Van Assche                 jap = sgj_named_subarray_r(jsp, jo2p,
3151*44704f69SBart Van Assche                                   "power_consumption_descriptor_list");
3152*44704f69SBart Van Assche             }
3153*44704f69SBart Van Assche             decode_power_consumption(rp, len, op, jap);
3154*44704f69SBart Van Assche         }
3155*44704f69SBart Van Assche         break;
3156*44704f69SBart Van Assche     case VPD_3PARTY_COPY:       /* 0x8f  ["tpc"] */
3157*44704f69SBart Van Assche         np = "Third party copy VPD page";
3158*44704f69SBart Van Assche         if (!op->do_raw && (op->do_hex < 3))
3159*44704f69SBart Van Assche             sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np);
3160*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3161*44704f69SBart Van Assche         if (res)
3162*44704f69SBart Van Assche             break;
3163*44704f69SBart Van Assche         if (op->do_raw)
3164*44704f69SBart Van Assche             dStrRaw((const char *)rp, len);
3165*44704f69SBart Van Assche         else {
3166*44704f69SBart Van Assche             if (as_json) {
3167*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3168*44704f69SBart Van Assche                 jap = sgj_named_subarray_r(jsp, jo2p,
3169*44704f69SBart Van Assche                                   "third_party_copy_descriptor_list");
3170*44704f69SBart Van Assche             }
3171*44704f69SBart Van Assche             decode_3party_copy_vpd(rp, len, op, jap);
3172*44704f69SBart Van Assche         }
3173*44704f69SBart Van Assche         break;
3174*44704f69SBart Van Assche     case VPD_PROTO_LU:          /* 0x90  ["pslu"] */
3175*44704f69SBart Van Assche         np = "Protocol specific logical unit information VPD page";
3176*44704f69SBart Van Assche         if (!op->do_raw && (op->do_hex < 3))
3177*44704f69SBart Van Assche             sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np);
3178*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3179*44704f69SBart Van Assche         if (res)
3180*44704f69SBart Van Assche             break;
3181*44704f69SBart Van Assche         if (op->do_raw)
3182*44704f69SBart Van Assche             dStrRaw((const char *)rp, len);
3183*44704f69SBart Van Assche         else {
3184*44704f69SBart Van Assche             if (as_json) {
3185*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3186*44704f69SBart Van Assche                 jap = sgj_named_subarray_r(jsp, jo2p,
3187*44704f69SBart Van Assche                                   "logical_unit_information_descriptor_list");
3188*44704f69SBart Van Assche             }
3189*44704f69SBart Van Assche             decode_proto_lu_vpd(rp, len, op, jap);
3190*44704f69SBart Van Assche         }
3191*44704f69SBart Van Assche         break;
3192*44704f69SBart Van Assche     case VPD_PROTO_PORT:        /* 0x91  ["pspo"] */
3193*44704f69SBart Van Assche         np = "Protocol specific port information VPD page";
3194*44704f69SBart Van Assche         if (!op->do_raw && (op->do_hex < 3))
3195*44704f69SBart Van Assche             sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np);
3196*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3197*44704f69SBart Van Assche         if (res)
3198*44704f69SBart Van Assche             break;
3199*44704f69SBart Van Assche         if (op->do_raw)
3200*44704f69SBart Van Assche             dStrRaw((const char *)rp, len);
3201*44704f69SBart Van Assche         else {
3202*44704f69SBart Van Assche             if (as_json) {
3203*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3204*44704f69SBart Van Assche                 jap = sgj_named_subarray_r(jsp, jo2p,
3205*44704f69SBart Van Assche                                   "port_information_descriptor_list");
3206*44704f69SBart Van Assche             }
3207*44704f69SBart Van Assche             decode_proto_port_vpd(rp, len, op, jap);
3208*44704f69SBart Van Assche         }
3209*44704f69SBart Van Assche         break;
3210*44704f69SBart Van Assche     case VPD_SCSI_FEATURE_SETS:         /* 0x92  ["sfs"] */
3211*44704f69SBart Van Assche         np = "SCSI Feature sets VPD page";
3212*44704f69SBart Van Assche         if (!op->do_raw && (op->do_hex < 3))
3213*44704f69SBart Van Assche             sgj_pr_hr(jsp, "VPD INQUIRY: %s\n", np);
3214*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3215*44704f69SBart Van Assche         if (res)
3216*44704f69SBart Van Assche             break;
3217*44704f69SBart Van Assche         if (op->do_raw)
3218*44704f69SBart Van Assche             dStrRaw((const char *)rp, len);
3219*44704f69SBart Van Assche         else {
3220*44704f69SBart Van Assche             if (as_json) {
3221*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3222*44704f69SBart Van Assche                 jap = sgj_named_subarray_r(jsp, jo2p,
3223*44704f69SBart Van Assche                                   "feature_set_code_list");
3224*44704f69SBart Van Assche             }
3225*44704f69SBart Van Assche             decode_feature_sets_vpd(rp, len, op, jap);
3226*44704f69SBart Van Assche         }
3227*44704f69SBart Van Assche         break;
3228*44704f69SBart Van Assche     case 0xb0:  /* VPD pages in B0h to BFh range depend on pdt */
3229*44704f69SBart Van Assche         np = NULL;
3230*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3231*44704f69SBart Van Assche         if (0 == res) {
3232*44704f69SBart Van Assche             bool bl = false;
3233*44704f69SBart Van Assche             bool sad = false;
3234*44704f69SBart Van Assche             bool oi = false;
3235*44704f69SBart Van Assche 
3236*44704f69SBart Van Assche             ep = "";
3237*44704f69SBart Van Assche             if (op->do_raw) {
3238*44704f69SBart Van Assche                 dStrRaw((const char *)rp, len);
3239*44704f69SBart Van Assche                 break;
3240*44704f69SBart Van Assche             }
3241*44704f69SBart Van Assche             pdt = rp[0] & PDT_MASK;
3242*44704f69SBart Van Assche             switch (pdt) {
3243*44704f69SBart Van Assche             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
3244*44704f69SBart Van Assche                 np = "Block limits VPD page";
3245*44704f69SBart Van Assche                 ep = "(SBC)";
3246*44704f69SBart Van Assche                 bl = true;
3247*44704f69SBart Van Assche                 break;
3248*44704f69SBart Van Assche             case PDT_TAPE: case PDT_MCHANGER:
3249*44704f69SBart Van Assche                 np = "Sequential-access device capabilities VPD page";
3250*44704f69SBart Van Assche                 ep = "(SSC)";
3251*44704f69SBart Van Assche                 sad = true;
3252*44704f69SBart Van Assche                 break;
3253*44704f69SBart Van Assche             case PDT_OSD:
3254*44704f69SBart Van Assche                 np = "OSD information VPD page";
3255*44704f69SBart Van Assche                 ep = "(OSD)";
3256*44704f69SBart Van Assche                 oi = true;
3257*44704f69SBart Van Assche                 break;
3258*44704f69SBart Van Assche             default:
3259*44704f69SBart Van Assche                 np = NULL;
3260*44704f69SBart Van Assche                 break;
3261*44704f69SBart Van Assche             }
3262*44704f69SBart Van Assche             if (op->do_hex < 3) {
3263*44704f69SBart Van Assche                 if (NULL == np)
3264*44704f69SBart Van Assche                     sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
3265*44704f69SBart Van Assche                 else
3266*44704f69SBart Van Assche                     sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep);
3267*44704f69SBart Van Assche             }
3268*44704f69SBart Van Assche             if (as_json)
3269*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3270*44704f69SBart Van Assche             if (bl)
3271*44704f69SBart Van Assche                 decode_block_limits_vpd(rp, len, op, jo2p);
3272*44704f69SBart Van Assche             else if (sad || oi)
3273*44704f69SBart Van Assche                 decode_b0_vpd(rp, len, op, jop);
3274*44704f69SBart Van Assche         } else if (! op->do_raw)
3275*44704f69SBart Van Assche             pr2serr("VPD INQUIRY: page=0xb0\n");
3276*44704f69SBart Van Assche         break;
3277*44704f69SBart Van Assche     case 0xb1:  /* VPD pages in B0h to BFh range depend on pdt */
3278*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3279*44704f69SBart Van Assche         if (0 == res) {
3280*44704f69SBart Van Assche             bool bdc = false;
3281*44704f69SBart Van Assche             static const char * masn =
3282*44704f69SBart Van Assche                         "Manufactured-assigned serial number VPD page";
3283*44704f69SBart Van Assche 
3284*44704f69SBart Van Assche             if (op->do_raw) {
3285*44704f69SBart Van Assche                 dStrRaw((const char *)rp, len);
3286*44704f69SBart Van Assche                 break;
3287*44704f69SBart Van Assche             }
3288*44704f69SBart Van Assche             pdt = rp[0] & PDT_MASK;
3289*44704f69SBart Van Assche             switch (pdt) {
3290*44704f69SBart Van Assche             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
3291*44704f69SBart Van Assche                 np = "Block device characteristics VPD page";
3292*44704f69SBart Van Assche                 ep = "(SBC)";
3293*44704f69SBart Van Assche                 bdc = true;
3294*44704f69SBart Van Assche                 break;
3295*44704f69SBart Van Assche             case PDT_TAPE: case PDT_MCHANGER:
3296*44704f69SBart Van Assche                 np = masn;
3297*44704f69SBart Van Assche                 ep = "(SSC)";
3298*44704f69SBart Van Assche                 break;
3299*44704f69SBart Van Assche             case PDT_OSD:
3300*44704f69SBart Van Assche                 np = "Security token VPD page";
3301*44704f69SBart Van Assche                 ep = "(OSD)";
3302*44704f69SBart Van Assche                 break;
3303*44704f69SBart Van Assche             case PDT_ADC:
3304*44704f69SBart Van Assche                 np = masn;
3305*44704f69SBart Van Assche                 ep = "(ADC)";
3306*44704f69SBart Van Assche                 break;
3307*44704f69SBart Van Assche             default:
3308*44704f69SBart Van Assche                 np = NULL;
3309*44704f69SBart Van Assche                 printf("VPD INQUIRY: page=0x%x, pdt=0x%x\n", 0xb1, pdt);
3310*44704f69SBart Van Assche                 break;
3311*44704f69SBart Van Assche             }
3312*44704f69SBart Van Assche             if (op->do_hex < 3) {
3313*44704f69SBart Van Assche                 if (NULL == np)
3314*44704f69SBart Van Assche                     sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
3315*44704f69SBart Van Assche                 else
3316*44704f69SBart Van Assche                     sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep);
3317*44704f69SBart Van Assche             }
3318*44704f69SBart Van Assche             if (as_json)
3319*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3320*44704f69SBart Van Assche             if (bdc)
3321*44704f69SBart Van Assche                 decode_block_dev_ch_vpd(rp, len, op, jo2p);
3322*44704f69SBart Van Assche             else
3323*44704f69SBart Van Assche                 decode_b1_vpd(rp, len, op, jo2p);
3324*44704f69SBart Van Assche         } else if (! op->do_raw)
3325*44704f69SBart Van Assche             pr2serr("VPD INQUIRY: page=0xb1\n");
3326*44704f69SBart Van Assche         break;
3327*44704f69SBart Van Assche     case 0xb2:  /* VPD pages in B0h to BFh range depend on pdt */
3328*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3329*44704f69SBart Van Assche         if (0 == res) {
3330*44704f69SBart Van Assche             bool lbpv = false;
3331*44704f69SBart Van Assche             bool tas = false;
3332*44704f69SBart Van Assche 
3333*44704f69SBart Van Assche             if (op->do_raw) {
3334*44704f69SBart Van Assche                 dStrRaw((const char *)rp, len);
3335*44704f69SBart Van Assche                 break;
3336*44704f69SBart Van Assche             }
3337*44704f69SBart Van Assche             pdt = rp[0] & PDT_MASK;
3338*44704f69SBart Van Assche             switch (pdt) {
3339*44704f69SBart Van Assche             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
3340*44704f69SBart Van Assche                 np = "Logical block provisioning VPD page";
3341*44704f69SBart Van Assche                 ep = "(SBC)";
3342*44704f69SBart Van Assche                 lbpv = true;
3343*44704f69SBart Van Assche                 break;
3344*44704f69SBart Van Assche             case PDT_TAPE: case PDT_MCHANGER:
3345*44704f69SBart Van Assche                 np = "TapeAlert supported flags VPD page";
3346*44704f69SBart Van Assche                 ep = "(SSC)";
3347*44704f69SBart Van Assche                 tas = true;
3348*44704f69SBart Van Assche                 break;
3349*44704f69SBart Van Assche             default:
3350*44704f69SBart Van Assche                 np = NULL;
3351*44704f69SBart Van Assche                 break;
3352*44704f69SBart Van Assche             }
3353*44704f69SBart Van Assche             if (op->do_hex < 3) {
3354*44704f69SBart Van Assche                 if (NULL == np)
3355*44704f69SBart Van Assche                     sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
3356*44704f69SBart Van Assche                 else
3357*44704f69SBart Van Assche                     sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep);
3358*44704f69SBart Van Assche             }
3359*44704f69SBart Van Assche             if (as_json)
3360*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3361*44704f69SBart Van Assche             if (lbpv)
3362*44704f69SBart Van Assche                 return decode_block_lb_prov_vpd(rp, len, op, jo2p);
3363*44704f69SBart Van Assche             else if (tas)
3364*44704f69SBart Van Assche                 decode_tapealert_supported_vpd(rp, len, op, jo2p);
3365*44704f69SBart Van Assche             else
3366*44704f69SBart Van Assche                 return vpd_mainly_hex(sg_fd, op, NULL, off);
3367*44704f69SBart Van Assche         } else if (! op->do_raw)
3368*44704f69SBart Van Assche             pr2serr("VPD INQUIRY: page=0xb2\n");
3369*44704f69SBart Van Assche         break;
3370*44704f69SBart Van Assche     case 0xb3:
3371*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3372*44704f69SBart Van Assche         if (0 == res) {
3373*44704f69SBart Van Assche             bool ref = false;
3374*44704f69SBart Van Assche 
3375*44704f69SBart Van Assche             if (op->do_raw) {
3376*44704f69SBart Van Assche                 dStrRaw((const char *)rp, len);
3377*44704f69SBart Van Assche                 break;
3378*44704f69SBart Van Assche             }
3379*44704f69SBart Van Assche             pdt = rp[0] & PDT_MASK;
3380*44704f69SBart Van Assche             switch (pdt) {
3381*44704f69SBart Van Assche             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
3382*44704f69SBart Van Assche                 np = "Referrals VPD page";
3383*44704f69SBart Van Assche                 ep = "(SBC)";
3384*44704f69SBart Van Assche                 ref = true;
3385*44704f69SBart Van Assche                 break;
3386*44704f69SBart Van Assche             default:
3387*44704f69SBart Van Assche                 np = NULL;
3388*44704f69SBart Van Assche                 break;
3389*44704f69SBart Van Assche             }
3390*44704f69SBart Van Assche             if (op->do_hex < 3) {
3391*44704f69SBart Van Assche                 if (NULL == np)
3392*44704f69SBart Van Assche                     sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
3393*44704f69SBart Van Assche                 else
3394*44704f69SBart Van Assche                     sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep);
3395*44704f69SBart Van Assche             }
3396*44704f69SBart Van Assche             if (as_json)
3397*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3398*44704f69SBart Van Assche             if (ref)
3399*44704f69SBart Van Assche                 decode_referrals_vpd(rp, len, op, jo2p);
3400*44704f69SBart Van Assche             else
3401*44704f69SBart Van Assche                 decode_b3_vpd(rp, len, op, jo2p);
3402*44704f69SBart Van Assche             return 0;
3403*44704f69SBart Van Assche         } else if (! op->do_raw)
3404*44704f69SBart Van Assche             pr2serr("VPD INQUIRY: page=0xb3\n");
3405*44704f69SBart Van Assche         break;
3406*44704f69SBart Van Assche     case 0xb4:
3407*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3408*44704f69SBart Van Assche         if (0 == res) {
3409*44704f69SBart Van Assche             bool sbl = false;
3410*44704f69SBart Van Assche             bool dtde = false;
3411*44704f69SBart Van Assche 
3412*44704f69SBart Van Assche             if (op->do_raw) {
3413*44704f69SBart Van Assche                 dStrRaw((const char *)rp, len);
3414*44704f69SBart Van Assche                 break;
3415*44704f69SBart Van Assche             }
3416*44704f69SBart Van Assche             pdt = rp[0] & PDT_MASK;
3417*44704f69SBart Van Assche             switch (pdt) {
3418*44704f69SBart Van Assche             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
3419*44704f69SBart Van Assche                 np = "Supported block lengths and protection types VPD page";
3420*44704f69SBart Van Assche                 ep = "(SBC)";
3421*44704f69SBart Van Assche                 sbl = true;
3422*44704f69SBart Van Assche                 break;
3423*44704f69SBart Van Assche             case PDT_TAPE: case PDT_MCHANGER:
3424*44704f69SBart Van Assche                 np = "Device transfer data element VPD page";
3425*44704f69SBart Van Assche                 ep = "(SSC)";
3426*44704f69SBart Van Assche                 dtde = true;
3427*44704f69SBart Van Assche                 break;
3428*44704f69SBart Van Assche             default:
3429*44704f69SBart Van Assche                 np = NULL;
3430*44704f69SBart Van Assche                 break;
3431*44704f69SBart Van Assche             }
3432*44704f69SBart Van Assche             if (op->do_hex < 3) {
3433*44704f69SBart Van Assche                 if (NULL == np)
3434*44704f69SBart Van Assche                     sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
3435*44704f69SBart Van Assche                 else
3436*44704f69SBart Van Assche                     sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep);
3437*44704f69SBart Van Assche             }
3438*44704f69SBart Van Assche             if (as_json)
3439*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3440*44704f69SBart Van Assche             if (sbl) {
3441*44704f69SBart Van Assche                 if (as_json)
3442*44704f69SBart Van Assche                     jap = sgj_named_subarray_r(jsp, jo2p, "logical_block_"
3443*44704f69SBart Van Assche                             "length_and_protection_types_descriptor_list");
3444*44704f69SBart Van Assche                 decode_sup_block_lens_vpd(rp, len, op, jap);
3445*44704f69SBart Van Assche             } else if (dtde) {
3446*44704f69SBart Van Assche                 if (! jsp->pr_as_json)
3447*44704f69SBart Van Assche                     hex2stdout(rp + 4, len - 4, 1);
3448*44704f69SBart Van Assche                 sgj_js_nv_hex_bytes(jsp, jop, "device_transfer_data_element",
3449*44704f69SBart Van Assche                                     rp + 4, len - 4);
3450*44704f69SBart Van Assche             } else
3451*44704f69SBart Van Assche                 return vpd_mainly_hex(sg_fd, op, NULL, off);
3452*44704f69SBart Van Assche             return 0;
3453*44704f69SBart Van Assche         } else if (! op->do_raw)
3454*44704f69SBart Van Assche             pr2serr("VPD INQUIRY: page=0xb4\n");
3455*44704f69SBart Van Assche         break;
3456*44704f69SBart Van Assche     case 0xb5:
3457*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3458*44704f69SBart Van Assche         if (0 == res) {
3459*44704f69SBart Van Assche             bool bdce = false;
3460*44704f69SBart Van Assche             bool lbp = false;
3461*44704f69SBart Van Assche 
3462*44704f69SBart Van Assche             if (op->do_raw) {
3463*44704f69SBart Van Assche                 dStrRaw((const char *)rp, len);
3464*44704f69SBart Van Assche                 break;
3465*44704f69SBart Van Assche             }
3466*44704f69SBart Van Assche             pdt = rp[0] & PDT_MASK;
3467*44704f69SBart Van Assche             switch (pdt) {
3468*44704f69SBart Van Assche             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
3469*44704f69SBart Van Assche                 np = "Block device characteristics VPD page";
3470*44704f69SBart Van Assche                 ep = "(SBC)";
3471*44704f69SBart Van Assche                 bdce = true;
3472*44704f69SBart Van Assche                 break;
3473*44704f69SBart Van Assche             case PDT_TAPE: case PDT_MCHANGER:
3474*44704f69SBart Van Assche                 np = "Logical block protection VPD page";
3475*44704f69SBart Van Assche                 ep = "(SSC)";
3476*44704f69SBart Van Assche                 lbp = true;
3477*44704f69SBart Van Assche                 break;
3478*44704f69SBart Van Assche             default:
3479*44704f69SBart Van Assche                 np = NULL;
3480*44704f69SBart Van Assche                 break;
3481*44704f69SBart Van Assche             }
3482*44704f69SBart Van Assche             if (op->do_hex < 3) {
3483*44704f69SBart Van Assche                 if (NULL == np)
3484*44704f69SBart Van Assche                     sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
3485*44704f69SBart Van Assche                 else
3486*44704f69SBart Van Assche                     sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep);
3487*44704f69SBart Van Assche             }
3488*44704f69SBart Van Assche             if (as_json)
3489*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3490*44704f69SBart Van Assche             if (bdce)
3491*44704f69SBart Van Assche                 decode_block_dev_char_ext_vpd(rp, len, op, jo2p);
3492*44704f69SBart Van Assche             else if (lbp) {     /* VPD_LB_PROTECTION  0xb5 ["lbpro"] (SSC) */
3493*44704f69SBart Van Assche                 if (as_json)
3494*44704f69SBart Van Assche                     jap = sgj_named_subarray_r(jsp, jo2p,
3495*44704f69SBart Van Assche                      "logical_block_protection_method_descriptor_list");
3496*44704f69SBart Van Assche                 decode_lb_protection_vpd(rp, len, op, jap);
3497*44704f69SBart Van Assche             } else
3498*44704f69SBart Van Assche                 return vpd_mainly_hex(sg_fd, op, NULL, off);
3499*44704f69SBart Van Assche             return 0;
3500*44704f69SBart Van Assche         } else if (! op->do_raw)
3501*44704f69SBart Van Assche             pr2serr("VPD INQUIRY: page=0xb5\n");
3502*44704f69SBart Van Assche         break;
3503*44704f69SBart Van Assche     case 0xb6:
3504*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3505*44704f69SBart Van Assche         if (0 == res) {
3506*44704f69SBart Van Assche             bool zbdch = false;
3507*44704f69SBart Van Assche 
3508*44704f69SBart Van Assche             if (op->do_raw) {
3509*44704f69SBart Van Assche                 dStrRaw((const char *)rp, len);
3510*44704f69SBart Van Assche                 break;
3511*44704f69SBart Van Assche             }
3512*44704f69SBart Van Assche             pdt = rp[0] & PDT_MASK;
3513*44704f69SBart Van Assche             switch (pdt) {
3514*44704f69SBart Van Assche             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
3515*44704f69SBart Van Assche                 np = "Zoned block device characteristics VPD page";
3516*44704f69SBart Van Assche                 ep = "(SBC, ZBC)";
3517*44704f69SBart Van Assche                 zbdch = true;
3518*44704f69SBart Van Assche                 break;
3519*44704f69SBart Van Assche             default:
3520*44704f69SBart Van Assche                 np = NULL;
3521*44704f69SBart Van Assche                 break;
3522*44704f69SBart Van Assche             }
3523*44704f69SBart Van Assche             if (op->do_hex < 3) {
3524*44704f69SBart Van Assche                 if (NULL == np)
3525*44704f69SBart Van Assche                     sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
3526*44704f69SBart Van Assche                 else
3527*44704f69SBart Van Assche                     sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep);
3528*44704f69SBart Van Assche             }
3529*44704f69SBart Van Assche             if (as_json)
3530*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3531*44704f69SBart Van Assche             if (zbdch)
3532*44704f69SBart Van Assche                 decode_zbdch_vpd(rp, len, op, jo2p);
3533*44704f69SBart Van Assche             else
3534*44704f69SBart Van Assche                 return vpd_mainly_hex(sg_fd, op, NULL, off);
3535*44704f69SBart Van Assche             return 0;
3536*44704f69SBart Van Assche         } else if (! op->do_raw)
3537*44704f69SBart Van Assche             pr2serr("VPD INQUIRY: page=0xb6\n");
3538*44704f69SBart Van Assche         break;
3539*44704f69SBart Van Assche     case 0xb7:
3540*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3541*44704f69SBart Van Assche         if (0 == res) {
3542*44704f69SBart Van Assche             bool ble = false;
3543*44704f69SBart Van Assche 
3544*44704f69SBart Van Assche             if (op->do_raw) {
3545*44704f69SBart Van Assche                 dStrRaw((const char *)rp, len);
3546*44704f69SBart Van Assche                 break;
3547*44704f69SBart Van Assche             }
3548*44704f69SBart Van Assche             pdt = rp[0] & PDT_MASK;
3549*44704f69SBart Van Assche             switch (pdt) {
3550*44704f69SBart Van Assche             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
3551*44704f69SBart Van Assche                 np = "Block limits extension VPD page";
3552*44704f69SBart Van Assche                 ep = "(SBC)";
3553*44704f69SBart Van Assche                 ble = true;
3554*44704f69SBart Van Assche                 break;
3555*44704f69SBart Van Assche             default:
3556*44704f69SBart Van Assche                 np = NULL;
3557*44704f69SBart Van Assche                 break;
3558*44704f69SBart Van Assche             }
3559*44704f69SBart Van Assche             if (op->do_hex < 3) {
3560*44704f69SBart Van Assche                 if (NULL == np)
3561*44704f69SBart Van Assche                     sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
3562*44704f69SBart Van Assche                 else
3563*44704f69SBart Van Assche                     sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep);
3564*44704f69SBart Van Assche             }
3565*44704f69SBart Van Assche             if (as_json)
3566*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3567*44704f69SBart Van Assche             if (ble)
3568*44704f69SBart Van Assche                 decode_block_limits_ext_vpd(rp, len, op, jo2p);
3569*44704f69SBart Van Assche             else
3570*44704f69SBart Van Assche                 return vpd_mainly_hex(sg_fd, op, NULL, off);
3571*44704f69SBart Van Assche             return 0;
3572*44704f69SBart Van Assche         } else if (! op->do_raw)
3573*44704f69SBart Van Assche             pr2serr("VPD INQUIRY: page=0xb7\n");
3574*44704f69SBart Van Assche         break;
3575*44704f69SBart Van Assche     case 0xb8:
3576*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3577*44704f69SBart Van Assche         if (0 == res) {
3578*44704f69SBart Van Assche             bool fp = false;
3579*44704f69SBart Van Assche 
3580*44704f69SBart Van Assche             if (op->do_raw) {
3581*44704f69SBart Van Assche                 dStrRaw((const char *)rp, len);
3582*44704f69SBart Van Assche                 break;
3583*44704f69SBart Van Assche             }
3584*44704f69SBart Van Assche             pdt = rp[0] & PDT_MASK;
3585*44704f69SBart Van Assche             switch (pdt) {
3586*44704f69SBart Van Assche             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
3587*44704f69SBart Van Assche                 np = "Format presets VPD page";
3588*44704f69SBart Van Assche                 ep = "(SBC)";
3589*44704f69SBart Van Assche                 fp = true;
3590*44704f69SBart Van Assche                 break;
3591*44704f69SBart Van Assche             default:
3592*44704f69SBart Van Assche                 np = NULL;
3593*44704f69SBart Van Assche                 break;
3594*44704f69SBart Van Assche             }
3595*44704f69SBart Van Assche             if (op->do_hex < 3) {
3596*44704f69SBart Van Assche                 if (NULL == np)
3597*44704f69SBart Van Assche                     sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
3598*44704f69SBart Van Assche                 else
3599*44704f69SBart Van Assche                     sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep);
3600*44704f69SBart Van Assche             }
3601*44704f69SBart Van Assche             if (as_json) {
3602*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3603*44704f69SBart Van Assche                 jap = sgj_named_subarray_r(jsp, jo2p, "format_preset_"
3604*44704f69SBart Van Assche                             "descriptor_list");
3605*44704f69SBart Van Assche             }
3606*44704f69SBart Van Assche             if (fp)
3607*44704f69SBart Van Assche                 decode_format_presets_vpd(rp, len, op, jap);
3608*44704f69SBart Van Assche             else
3609*44704f69SBart Van Assche                 return vpd_mainly_hex(sg_fd, op, NULL, off);
3610*44704f69SBart Van Assche             return 0;
3611*44704f69SBart Van Assche         } else if (! op->do_raw)
3612*44704f69SBart Van Assche             pr2serr("VPD INQUIRY: page=0xb8\n");
3613*44704f69SBart Van Assche         break;
3614*44704f69SBart Van Assche     case 0xb9:
3615*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3616*44704f69SBart Van Assche         if (0 == res) {
3617*44704f69SBart Van Assche             bool cpr = false;
3618*44704f69SBart Van Assche 
3619*44704f69SBart Van Assche             if (op->do_raw) {
3620*44704f69SBart Van Assche                 dStrRaw((const char *)rp, len);
3621*44704f69SBart Van Assche                 break;
3622*44704f69SBart Van Assche             }
3623*44704f69SBart Van Assche             pdt = rp[0] & PDT_MASK;
3624*44704f69SBart Van Assche             switch (pdt) {
3625*44704f69SBart Van Assche             case PDT_DISK: case PDT_WO: case PDT_OPTICAL: case PDT_ZBC:
3626*44704f69SBart Van Assche                 np = "Concurrent positioning LBAs VPD page";
3627*44704f69SBart Van Assche                 ep = "(SBC)";
3628*44704f69SBart Van Assche                 cpr = true;
3629*44704f69SBart Van Assche                 break;
3630*44704f69SBart Van Assche             default:
3631*44704f69SBart Van Assche                 np = NULL;
3632*44704f69SBart Van Assche                 break;
3633*44704f69SBart Van Assche             }
3634*44704f69SBart Van Assche             if (op->do_hex < 3) {
3635*44704f69SBart Van Assche                 if (NULL == np)
3636*44704f69SBart Van Assche                     sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
3637*44704f69SBart Van Assche                 else
3638*44704f69SBart Van Assche                     sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep);
3639*44704f69SBart Van Assche             }
3640*44704f69SBart Van Assche             if (as_json) {
3641*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3642*44704f69SBart Van Assche                 jap = sgj_named_subarray_r(jsp, jo2p, "lba_range_"
3643*44704f69SBart Van Assche                                            "descriptor_list");
3644*44704f69SBart Van Assche             }
3645*44704f69SBart Van Assche             if (cpr)
3646*44704f69SBart Van Assche                 decode_con_pos_range_vpd(rp, len, op, jap);
3647*44704f69SBart Van Assche             else
3648*44704f69SBart Van Assche                 return vpd_mainly_hex(sg_fd, op, NULL, off);
3649*44704f69SBart Van Assche             return 0;
3650*44704f69SBart Van Assche         } else if (! op->do_raw)
3651*44704f69SBart Van Assche             pr2serr("VPD INQUIRY: page=0xb8\n");
3652*44704f69SBart Van Assche         break;
3653*44704f69SBart Van Assche     /* Vendor specific VPD pages (>= 0xc0) */
3654*44704f69SBart Van Assche     case VPD_UPR_EMC:   /* 0xc0 */
3655*44704f69SBart Van Assche         np = "Unit path report VPD page";
3656*44704f69SBart Van Assche         ep = "(EMC)";
3657*44704f69SBart Van Assche         if (!op->do_raw && (op->do_hex < 3))
3658*44704f69SBart Van Assche             sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep);
3659*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, -1, qt, vb, &len);
3660*44704f69SBart Van Assche         if (res)
3661*44704f69SBart Van Assche             break;
3662*44704f69SBart Van Assche         if (op->do_raw)
3663*44704f69SBart Van Assche             dStrRaw((const char *)rp, len);
3664*44704f69SBart Van Assche         else {
3665*44704f69SBart Van Assche             if (as_json)
3666*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3667*44704f69SBart Van Assche             decode_upr_vpd_c0_emc(rp, len, op, jo2p);
3668*44704f69SBart Van Assche         }
3669*44704f69SBart Van Assche         break;
3670*44704f69SBart Van Assche     case VPD_RDAC_VERS:         /* 0xc2 */
3671*44704f69SBart Van Assche         np = "Software Version VPD page";
3672*44704f69SBart Van Assche         ep = "(RDAC)";
3673*44704f69SBart Van Assche         if (!op->do_raw && (op->do_hex < 3))
3674*44704f69SBart Van Assche             sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep);
3675*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, -1, qt, vb, &len);
3676*44704f69SBart Van Assche         if (res)
3677*44704f69SBart Van Assche             break;
3678*44704f69SBart Van Assche         if (op->do_raw)
3679*44704f69SBart Van Assche             dStrRaw((const char *)rp, len);
3680*44704f69SBart Van Assche         else {
3681*44704f69SBart Van Assche             if (as_json)
3682*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3683*44704f69SBart Van Assche             decode_rdac_vpd_c2(rp, len, op, jo2p);
3684*44704f69SBart Van Assche         }
3685*44704f69SBart Van Assche         break;
3686*44704f69SBart Van Assche     case VPD_RDAC_VAC:          /* 0xc9 */
3687*44704f69SBart Van Assche         np = "Volume access control VPD page";
3688*44704f69SBart Van Assche         ep = "(RDAC)";
3689*44704f69SBart Van Assche         if (!op->do_raw && (op->do_hex < 3))
3690*44704f69SBart Van Assche             sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep);
3691*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, -1, qt, vb, &len);
3692*44704f69SBart Van Assche         if (res)
3693*44704f69SBart Van Assche             break;
3694*44704f69SBart Van Assche         if (op->do_raw)
3695*44704f69SBart Van Assche             dStrRaw((const char *)rp, len);
3696*44704f69SBart Van Assche         else {
3697*44704f69SBart Van Assche             if (as_json)
3698*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3699*44704f69SBart Van Assche             decode_rdac_vpd_c9(rp, len, op, jo2p);
3700*44704f69SBart Van Assche         }
3701*44704f69SBart Van Assche         break;
3702*44704f69SBart Van Assche     case SG_NVME_VPD_NICR:          /* 0xde */
3703*44704f69SBart Van Assche         np = "NVMe Identify Controller Response VPD page";
3704*44704f69SBart Van Assche         /* NVMe: Identify Controller data structure (CNS 01h) */
3705*44704f69SBart Van Assche         ep = "(sg3_utils)";
3706*44704f69SBart Van Assche         res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3707*44704f69SBart Van Assche         if (res) {
3708*44704f69SBart Van Assche             sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep);
3709*44704f69SBart Van Assche             break;
3710*44704f69SBart Van Assche         }
3711*44704f69SBart Van Assche         if (op->do_raw) {
3712*44704f69SBart Van Assche             dStrRaw((const char *)rp, len);
3713*44704f69SBart Van Assche             break;
3714*44704f69SBart Van Assche         }
3715*44704f69SBart Van Assche         pdt = rp[0] & PDT_MASK;
3716*44704f69SBart Van Assche         if (op->do_hex < 3) {
3717*44704f69SBart Van Assche             if (NULL == np)
3718*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "VPD page=0x%x, pdt=0x%x:\n", pn, pdt);
3719*44704f69SBart Van Assche             else
3720*44704f69SBart Van Assche                 sgj_pr_hr(jsp, "VPD INQUIRY: %s %s\n", np, ep);
3721*44704f69SBart Van Assche         }
3722*44704f69SBart Van Assche         if (len < 16) {
3723*44704f69SBart Van Assche             pr2serr("%s expected to be > 15 bytes long (got: %d)\n", ep, len);
3724*44704f69SBart Van Assche             break;
3725*44704f69SBart Van Assche         } else {
3726*44704f69SBart Van Assche             int n = len - 16;
3727*44704f69SBart Van Assche 
3728*44704f69SBart Van Assche             if (n > 4096) {
3729*44704f69SBart Van Assche                 pr2serr("NVMe Identify response expected to be <= 4096 "
3730*44704f69SBart Van Assche                         "bytes (got: %d)\n", n);
3731*44704f69SBart Van Assche                 break;
3732*44704f69SBart Van Assche             }
3733*44704f69SBart Van Assche             if (op->do_hex)
3734*44704f69SBart Van Assche                 hex2stdout(rp, len, no_ascii_4hex(op));
3735*44704f69SBart Van Assche             else if (as_json) {
3736*44704f69SBart Van Assche                 jo2p = sg_vpd_js_hdr(jsp, jop, np, rp);
3737*44704f69SBart Van Assche                 sgj_js_nv_hex_bytes(jsp, jo2p, "response_bytes", rp + 16, n);
3738*44704f69SBart Van Assche             } else
3739*44704f69SBart Van Assche                 hex2stdout(rp + 16, n, 1);
3740*44704f69SBart Van Assche         }
3741*44704f69SBart Van Assche         break;
3742*44704f69SBart Van Assche     default:
3743*44704f69SBart Van Assche         bad = true;
3744*44704f69SBart Van Assche         break;
3745*44704f69SBart Van Assche     }
3746*44704f69SBart Van Assche     if (bad) {
3747*44704f69SBart Van Assche         if ((pn > 0) && (pn < 0x80)) {
3748*44704f69SBart Van Assche             if (!op->do_raw && (op->do_hex < 3))
3749*44704f69SBart Van Assche                 printf("VPD INQUIRY: ASCII information page, FRU code=0x%x\n",
3750*44704f69SBart Van Assche                        pn);
3751*44704f69SBart Van Assche             res = vpd_fetch_page(sg_fd, rp, pn, op->maxlen, qt, vb, &len);
3752*44704f69SBart Van Assche             if (0 == res) {
3753*44704f69SBart Van Assche                 if (op->do_raw)
3754*44704f69SBart Van Assche                     dStrRaw((const char *)rp, len);
3755*44704f69SBart Van Assche                 else
3756*44704f69SBart Van Assche                     decode_ascii_inf(rp, len, op);
3757*44704f69SBart Van Assche             }
3758*44704f69SBart Van Assche         } else {
3759*44704f69SBart Van Assche             if (op->do_hex < 3)
3760*44704f69SBart Van Assche                 pr2serr(" Only hex output supported.\n");
3761*44704f69SBart Van Assche             return vpd_mainly_hex(sg_fd, op, NULL, off);
3762*44704f69SBart Van Assche         }
3763*44704f69SBart Van Assche     }
3764*44704f69SBart Van Assche out:
3765*44704f69SBart Van Assche     if (res) {
3766*44704f69SBart Van Assche         char b[80];
3767*44704f69SBart Van Assche 
3768*44704f69SBart Van Assche         if (SG_LIB_CAT_ILLEGAL_REQ == res)
3769*44704f69SBart Van Assche             pr2serr("    inquiry: field in cdb illegal (page not "
3770*44704f69SBart Van Assche                     "supported)\n");
3771*44704f69SBart Van Assche         else {
3772*44704f69SBart Van Assche             sg_get_category_sense_str(res, sizeof(b), b, vb);
3773*44704f69SBart Van Assche             pr2serr("    inquiry: %s\n", b);
3774*44704f69SBart Van Assche         }
3775*44704f69SBart Van Assche     }
3776*44704f69SBart Van Assche     return res;
3777*44704f69SBart Van Assche }
3778*44704f69SBart Van Assche 
3779*44704f69SBart Van Assche #if (HAVE_NVME && (! IGNORE_NVME))
3780*44704f69SBart Van Assche 
3781*44704f69SBart Van Assche static void
nvme_hex_raw(const uint8_t * b,int b_len,const struct opts_t * op)3782*44704f69SBart Van Assche nvme_hex_raw(const uint8_t * b, int b_len, const struct opts_t * op)
3783*44704f69SBart Van Assche {
3784*44704f69SBart Van Assche     if (op->do_raw)
3785*44704f69SBart Van Assche         dStrRaw((const char *)b, b_len);
3786*44704f69SBart Van Assche     else if (op->do_hex) {
3787*44704f69SBart Van Assche         if (op->do_hex < 3) {
3788*44704f69SBart Van Assche             printf("data_in buffer:\n");
3789*44704f69SBart Van Assche             hex2stdout(b, b_len, (2 == op->do_hex));
3790*44704f69SBart Van Assche         } else
3791*44704f69SBart Van Assche             hex2stdout(b, b_len, -1);
3792*44704f69SBart Van Assche     }
3793*44704f69SBart Van Assche }
3794*44704f69SBart Van Assche 
3795*44704f69SBart Van Assche static const char * rperf[] = {"Best", "Better", "Good", "Degraded"};
3796*44704f69SBart Van Assche 
3797*44704f69SBart Van Assche static void
show_nvme_id_ns(const uint8_t * dinp,int do_long)3798*44704f69SBart Van Assche show_nvme_id_ns(const uint8_t * dinp, int do_long)
3799*44704f69SBart Van Assche {
3800*44704f69SBart Van Assche     bool got_eui_128 = false;
3801*44704f69SBart Van Assche     uint32_t u, k, off, num_lbaf, flbas, flba_info, md_size, lb_size;
3802*44704f69SBart Van Assche     uint64_t ns_sz, eui_64;
3803*44704f69SBart Van Assche 
3804*44704f69SBart Van Assche     num_lbaf = dinp[25] + 1;  /* spec says this is "0's based value" */
3805*44704f69SBart Van Assche     flbas = dinp[26] & 0xf;   /* index of active LBA format (for this ns) */
3806*44704f69SBart Van Assche     ns_sz = sg_get_unaligned_le64(dinp + 0);
3807*44704f69SBart Van Assche     eui_64 = sg_get_unaligned_be64(dinp + 120);  /* N.B. big endian */
3808*44704f69SBart Van Assche     if (! sg_all_zeros(dinp + 104, 16))
3809*44704f69SBart Van Assche         got_eui_128 = true;
3810*44704f69SBart Van Assche     printf("    Namespace size/capacity: %" PRIu64 "/%" PRIu64
3811*44704f69SBart Van Assche            " blocks\n", ns_sz, sg_get_unaligned_le64(dinp + 8));
3812*44704f69SBart Van Assche     printf("    Namespace utilization: %" PRIu64 " blocks\n",
3813*44704f69SBart Van Assche            sg_get_unaligned_le64(dinp + 16));
3814*44704f69SBart Van Assche     if (got_eui_128) {          /* N.B. big endian */
3815*44704f69SBart Van Assche         printf("    NGUID: 0x%02x", dinp[104]);
3816*44704f69SBart Van Assche         for (k = 1; k < 16; ++k)
3817*44704f69SBart Van Assche             printf("%02x", dinp[104 + k]);
3818*44704f69SBart Van Assche         printf("\n");
3819*44704f69SBart Van Assche     } else if (do_long)
3820*44704f69SBart Van Assche         printf("    NGUID: 0x0\n");
3821*44704f69SBart Van Assche     if (eui_64)
3822*44704f69SBart Van Assche         printf("    EUI-64: 0x%" PRIx64 "\n", eui_64); /* N.B. big endian */
3823*44704f69SBart Van Assche     printf("    Number of LBA formats: %u\n", num_lbaf);
3824*44704f69SBart Van Assche     printf("    Index LBA size: %u\n", flbas);
3825*44704f69SBart Van Assche     for (k = 0, off = 128; k < num_lbaf; ++k, off += 4) {
3826*44704f69SBart Van Assche         printf("    LBA format %u support:", k);
3827*44704f69SBart Van Assche         if (k == flbas)
3828*44704f69SBart Van Assche             printf(" <-- active\n");
3829*44704f69SBart Van Assche         else
3830*44704f69SBart Van Assche             printf("\n");
3831*44704f69SBart Van Assche         flba_info = sg_get_unaligned_le32(dinp + off);
3832*44704f69SBart Van Assche         md_size = flba_info & 0xffff;
3833*44704f69SBart Van Assche         lb_size = flba_info >> 16 & 0xff;
3834*44704f69SBart Van Assche         if (lb_size > 31) {
3835*44704f69SBart Van Assche             pr2serr("%s: logical block size exponent of %u implies a LB "
3836*44704f69SBart Van Assche                     "size larger than 4 billion bytes, ignore\n", __func__,
3837*44704f69SBart Van Assche                     lb_size);
3838*44704f69SBart Van Assche             continue;
3839*44704f69SBart Van Assche         }
3840*44704f69SBart Van Assche         lb_size = 1U << lb_size;
3841*44704f69SBart Van Assche         ns_sz *= lb_size;
3842*44704f69SBart Van Assche         ns_sz /= 500*1000*1000;
3843*44704f69SBart Van Assche         if (ns_sz & 0x1)
3844*44704f69SBart Van Assche             ns_sz = (ns_sz / 2) + 1;
3845*44704f69SBart Van Assche         else
3846*44704f69SBart Van Assche             ns_sz = ns_sz / 2;
3847*44704f69SBart Van Assche         u = (flba_info >> 24) & 0x3;
3848*44704f69SBart Van Assche         printf("      Logical block size: %u bytes\n", lb_size);
3849*44704f69SBart Van Assche         printf("      Approximate namespace size: %" PRIu64 " GB\n", ns_sz);
3850*44704f69SBart Van Assche         printf("      Metadata size: %u bytes\n", md_size);
3851*44704f69SBart Van Assche         printf("      Relative performance: %s [0x%x]\n", rperf[u], u);
3852*44704f69SBart Van Assche     }
3853*44704f69SBart Van Assche }
3854*44704f69SBart Van Assche 
3855*44704f69SBart Van Assche /* Send Identify(CNS=0, nsid) and decode the Identify namespace response */
3856*44704f69SBart Van Assche static int
nvme_id_namespace(struct sg_pt_base * ptvp,uint32_t nsid,struct sg_nvme_passthru_cmd * id_cmdp,uint8_t * id_dinp,int id_din_len,const struct opts_t * op)3857*44704f69SBart Van Assche nvme_id_namespace(struct sg_pt_base * ptvp, uint32_t nsid,
3858*44704f69SBart Van Assche                   struct sg_nvme_passthru_cmd * id_cmdp, uint8_t * id_dinp,
3859*44704f69SBart Van Assche                   int id_din_len, const struct opts_t * op)
3860*44704f69SBart Van Assche {
3861*44704f69SBart Van Assche     int ret = 0;
3862*44704f69SBart Van Assche     int vb = op->verbose;
3863*44704f69SBart Van Assche     uint8_t resp[16];
3864*44704f69SBart Van Assche 
3865*44704f69SBart Van Assche     clear_scsi_pt_obj(ptvp);
3866*44704f69SBart Van Assche     id_cmdp->nsid = nsid;
3867*44704f69SBart Van Assche     id_cmdp->cdw10 = 0x0;       /* CNS=0x0 Identify NS (CNTID=0) */
3868*44704f69SBart Van Assche     id_cmdp->cdw11 = 0x0;       /* NVMSETID=0 (only valid when CNS=0x4) */
3869*44704f69SBart Van Assche     id_cmdp->cdw14 = 0x0;       /* UUID index (assume not supported) */
3870*44704f69SBart Van Assche     set_scsi_pt_data_in(ptvp, id_dinp, id_din_len);
3871*44704f69SBart Van Assche     set_scsi_pt_sense(ptvp, resp, sizeof(resp));
3872*44704f69SBart Van Assche     set_scsi_pt_cdb(ptvp, (const uint8_t *)id_cmdp, sizeof(*id_cmdp));
3873*44704f69SBart Van Assche     ret = do_scsi_pt(ptvp, -1, 0 /* timeout (def: 1 min) */, vb);
3874*44704f69SBart Van Assche     if (vb > 2)
3875*44704f69SBart Van Assche         pr2serr("%s: do_scsi_pt() result is %d\n", __func__, ret);
3876*44704f69SBart Van Assche     if (ret) {
3877*44704f69SBart Van Assche         if (SCSI_PT_DO_BAD_PARAMS == ret)
3878*44704f69SBart Van Assche             ret = SG_LIB_SYNTAX_ERROR;
3879*44704f69SBart Van Assche         else if (SCSI_PT_DO_TIMEOUT == ret)
3880*44704f69SBart Van Assche             ret = SG_LIB_CAT_TIMEOUT;
3881*44704f69SBart Van Assche         else if (ret < 0)
3882*44704f69SBart Van Assche             ret = sg_convert_errno(-ret);
3883*44704f69SBart Van Assche         return ret;
3884*44704f69SBart Van Assche     }
3885*44704f69SBart Van Assche     if (op->do_hex || op->do_raw) {
3886*44704f69SBart Van Assche         nvme_hex_raw(id_dinp, id_din_len, op);
3887*44704f69SBart Van Assche         return 0;
3888*44704f69SBart Van Assche     }
3889*44704f69SBart Van Assche     show_nvme_id_ns(id_dinp, op->do_long);
3890*44704f69SBart Van Assche     return 0;
3891*44704f69SBart Van Assche }
3892*44704f69SBart Van Assche 
3893*44704f69SBart Van Assche static void
show_nvme_id_ctrl(const uint8_t * dinp,const char * dev_name,int do_long)3894*44704f69SBart Van Assche show_nvme_id_ctrl(const uint8_t *dinp, const char *dev_name, int do_long)
3895*44704f69SBart Van Assche {
3896*44704f69SBart Van Assche     bool got_fguid;
3897*44704f69SBart Van Assche     uint8_t ver_min, ver_ter, mtds;
3898*44704f69SBart Van Assche     uint16_t ver_maj, oacs, oncs;
3899*44704f69SBart Van Assche     uint32_t k, ver, max_nsid, npss, j, n, m;
3900*44704f69SBart Van Assche     uint64_t sz1, sz2;
3901*44704f69SBart Van Assche     const uint8_t * up;
3902*44704f69SBart Van Assche 
3903*44704f69SBart Van Assche     max_nsid = sg_get_unaligned_le32(dinp + 516); /* NN */
3904*44704f69SBart Van Assche     printf("Identify controller for %s:\n", dev_name);
3905*44704f69SBart Van Assche     printf("  Model number: %.40s\n", (const char *)(dinp + 24));
3906*44704f69SBart Van Assche     printf("  Serial number: %.20s\n", (const char *)(dinp + 4));
3907*44704f69SBart Van Assche     printf("  Firmware revision: %.8s\n", (const char *)(dinp + 64));
3908*44704f69SBart Van Assche     ver = sg_get_unaligned_le32(dinp + 80);
3909*44704f69SBart Van Assche     ver_maj = (ver >> 16);
3910*44704f69SBart Van Assche     ver_min = (ver >> 8) & 0xff;
3911*44704f69SBart Van Assche     ver_ter = (ver & 0xff);
3912*44704f69SBart Van Assche     printf("  Version: %u.%u", ver_maj, ver_min);
3913*44704f69SBart Van Assche     if ((ver_maj > 1) || ((1 == ver_maj) && (ver_min > 2)) ||
3914*44704f69SBart Van Assche         ((1 == ver_maj) && (2 == ver_min) && (ver_ter > 0)))
3915*44704f69SBart Van Assche         printf(".%u\n", ver_ter);
3916*44704f69SBart Van Assche     else
3917*44704f69SBart Van Assche         printf("\n");
3918*44704f69SBart Van Assche     oacs = sg_get_unaligned_le16(dinp + 256);
3919*44704f69SBart Van Assche     if (0x1ff & oacs) {
3920*44704f69SBart Van Assche         printf("  Optional admin command support:\n");
3921*44704f69SBart Van Assche         if (0x200 & oacs)
3922*44704f69SBart Van Assche             printf("    Get LBA status\n");     /* NVMe 1.4 */
3923*44704f69SBart Van Assche         if (0x100 & oacs)
3924*44704f69SBart Van Assche             printf("    Doorbell buffer config\n");
3925*44704f69SBart Van Assche         if (0x80 & oacs)
3926*44704f69SBart Van Assche             printf("    Virtualization management\n");
3927*44704f69SBart Van Assche         if (0x40 & oacs)
3928*44704f69SBart Van Assche             printf("    NVMe-MI send and NVMe-MI receive\n");
3929*44704f69SBart Van Assche         if (0x20 & oacs)
3930*44704f69SBart Van Assche             printf("    Directive send and directive receive\n");
3931*44704f69SBart Van Assche         if (0x10 & oacs)
3932*44704f69SBart Van Assche             printf("    Device self-test\n");
3933*44704f69SBart Van Assche         if (0x8 & oacs)
3934*44704f69SBart Van Assche             printf("    Namespace management and attachment\n");
3935*44704f69SBart Van Assche         if (0x4 & oacs)
3936*44704f69SBart Van Assche             printf("    Firmware download and commit\n");
3937*44704f69SBart Van Assche         if (0x2 & oacs)
3938*44704f69SBart Van Assche             printf("    Format NVM\n");
3939*44704f69SBart Van Assche         if (0x1 & oacs)
3940*44704f69SBart Van Assche             printf("    Security send and receive\n");
3941*44704f69SBart Van Assche     } else
3942*44704f69SBart Van Assche         printf("  No optional admin command support\n");
3943*44704f69SBart Van Assche     oncs = sg_get_unaligned_le16(dinp + 256);
3944*44704f69SBart Van Assche     if (0x7f & oncs) {
3945*44704f69SBart Van Assche         printf("  Optional NVM command support:\n");
3946*44704f69SBart Van Assche         if (0x80 & oncs)
3947*44704f69SBart Van Assche             printf("    Verify\n");     /* NVMe 1.4 */
3948*44704f69SBart Van Assche         if (0x40 & oncs)
3949*44704f69SBart Van Assche             printf("    Timestamp feature\n");
3950*44704f69SBart Van Assche         if (0x20 & oncs)
3951*44704f69SBart Van Assche             printf("    Reservations\n");
3952*44704f69SBart Van Assche         if (0x10 & oncs)
3953*44704f69SBart Van Assche             printf("    Save and Select fields non-zero\n");
3954*44704f69SBart Van Assche         if (0x8 & oncs)
3955*44704f69SBart Van Assche             printf("    Write zeroes\n");
3956*44704f69SBart Van Assche         if (0x4 & oncs)
3957*44704f69SBart Van Assche             printf("    Dataset management\n");
3958*44704f69SBart Van Assche         if (0x2 & oncs)
3959*44704f69SBart Van Assche             printf("    Write uncorrectable\n");
3960*44704f69SBart Van Assche         if (0x1 & oncs)
3961*44704f69SBart Van Assche             printf("    Compare\n");
3962*44704f69SBart Van Assche     } else
3963*44704f69SBart Van Assche         printf("  No optional NVM command support\n");
3964*44704f69SBart Van Assche     printf("  PCI vendor ID VID/SSVID: 0x%x/0x%x\n",
3965*44704f69SBart Van Assche            sg_get_unaligned_le16(dinp + 0),
3966*44704f69SBart Van Assche            sg_get_unaligned_le16(dinp + 2));
3967*44704f69SBart Van Assche     printf("  IEEE OUI Identifier: 0x%x\n",  /* this has been renamed AOI */
3968*44704f69SBart Van Assche            sg_get_unaligned_le24(dinp + 73));
3969*44704f69SBart Van Assche     got_fguid = ! sg_all_zeros(dinp + 112, 16);
3970*44704f69SBart Van Assche     if (got_fguid) {
3971*44704f69SBart Van Assche         printf("  FGUID: 0x%02x", dinp[112]);
3972*44704f69SBart Van Assche         for (k = 1; k < 16; ++k)
3973*44704f69SBart Van Assche             printf("%02x", dinp[112 + k]);
3974*44704f69SBart Van Assche         printf("\n");
3975*44704f69SBart Van Assche     } else if (do_long)
3976*44704f69SBart Van Assche         printf("  FGUID: 0x0\n");
3977*44704f69SBart Van Assche     printf("  Controller ID: 0x%x\n", sg_get_unaligned_le16(dinp + 78));
3978*44704f69SBart Van Assche     if (do_long) {      /* Bytes 240 to 255 are reserved for NVME-MI */
3979*44704f69SBart Van Assche         printf("  NVMe Management Interface [MI] settings:\n");
3980*44704f69SBart Van Assche         printf("    Enclosure: %d [NVMEE]\n", !! (0x2 & dinp[253]));
3981*44704f69SBart Van Assche         printf("    NVMe Storage device: %d [NVMESD]\n",
3982*44704f69SBart Van Assche                !! (0x1 & dinp[253]));
3983*44704f69SBart Van Assche         printf("    Management endpoint capabilities, over a PCIe port: %d "
3984*44704f69SBart Van Assche                "[PCIEME]\n",
3985*44704f69SBart Van Assche                !! (0x2 & dinp[255]));
3986*44704f69SBart Van Assche         printf("    Management endpoint capabilities, over a SMBus/I2C port: "
3987*44704f69SBart Van Assche                "%d [SMBUSME]\n", !! (0x1 & dinp[255]));
3988*44704f69SBart Van Assche     }
3989*44704f69SBart Van Assche     printf("  Number of namespaces: %u\n", max_nsid);
3990*44704f69SBart Van Assche     sz1 = sg_get_unaligned_le64(dinp + 280);  /* lower 64 bits */
3991*44704f69SBart Van Assche     sz2 = sg_get_unaligned_le64(dinp + 288);  /* upper 64 bits */
3992*44704f69SBart Van Assche     if (sz2)
3993*44704f69SBart Van Assche         printf("  Total NVM capacity: huge ...\n");
3994*44704f69SBart Van Assche     else if (sz1)
3995*44704f69SBart Van Assche         printf("  Total NVM capacity: %" PRIu64 " bytes\n", sz1);
3996*44704f69SBart Van Assche     mtds = dinp[77];
3997*44704f69SBart Van Assche     printf("  Maximum data transfer size: ");
3998*44704f69SBart Van Assche     if (mtds)
3999*44704f69SBart Van Assche         printf("%u pages\n", 1U << mtds);
4000*44704f69SBart Van Assche     else
4001*44704f69SBart Van Assche         printf("<unlimited>\n");
4002*44704f69SBart Van Assche 
4003*44704f69SBart Van Assche     if (do_long) {
4004*44704f69SBart Van Assche         const char * const non_op = "does not process I/O";
4005*44704f69SBart Van Assche         const char * const operat = "processes I/O";
4006*44704f69SBart Van Assche         const char * cp;
4007*44704f69SBart Van Assche 
4008*44704f69SBart Van Assche         printf("  Total NVM capacity: 0 bytes\n");
4009*44704f69SBart Van Assche         npss = dinp[263] + 1;
4010*44704f69SBart Van Assche         up = dinp + 2048;
4011*44704f69SBart Van Assche         for (k = 0; k < npss; ++k, up += 32) {
4012*44704f69SBart Van Assche             n = sg_get_unaligned_le16(up + 0);
4013*44704f69SBart Van Assche             n *= (0x1 & up[3]) ? 1 : 100;    /* unit: 100 microWatts */
4014*44704f69SBart Van Assche             j = n / 10;                      /* unit: 1 milliWatts */
4015*44704f69SBart Van Assche             m = j % 1000;
4016*44704f69SBart Van Assche             j /= 1000;
4017*44704f69SBart Van Assche             cp = (0x2 & up[3]) ? non_op : operat;
4018*44704f69SBart Van Assche             printf("  Power state %u: Max power: ", k);
4019*44704f69SBart Van Assche             if (0 == j) {
4020*44704f69SBart Van Assche                 m = n % 10;
4021*44704f69SBart Van Assche                 n /= 10;
4022*44704f69SBart Van Assche                 printf("%u.%u milliWatts, %s\n", n, m, cp);
4023*44704f69SBart Van Assche             } else
4024*44704f69SBart Van Assche                 printf("%u.%03u Watts, %s\n", j, m, cp);
4025*44704f69SBart Van Assche             n = sg_get_unaligned_le32(up + 4);
4026*44704f69SBart Van Assche             if (0 == n)
4027*44704f69SBart Van Assche                 printf("    [ENLAT], ");
4028*44704f69SBart Van Assche             else
4029*44704f69SBart Van Assche                 printf("    ENLAT=%u, ", n);
4030*44704f69SBart Van Assche             n = sg_get_unaligned_le32(up + 8);
4031*44704f69SBart Van Assche             if (0 == n)
4032*44704f69SBart Van Assche                 printf("[EXLAT], ");
4033*44704f69SBart Van Assche             else
4034*44704f69SBart Van Assche                 printf("EXLAT=%u, ", n);
4035*44704f69SBart Van Assche             n = 0x1f & up[12];
4036*44704f69SBart Van Assche             printf("RRT=%u, ", n);
4037*44704f69SBart Van Assche             n = 0x1f & up[13];
4038*44704f69SBart Van Assche             printf("RRL=%u, ", n);
4039*44704f69SBart Van Assche             n = 0x1f & up[14];
4040*44704f69SBart Van Assche             printf("RWT=%u, ", n);
4041*44704f69SBart Van Assche             n = 0x1f & up[15];
4042*44704f69SBart Van Assche             printf("RWL=%u\n", n);
4043*44704f69SBart Van Assche         }
4044*44704f69SBart Van Assche     }
4045*44704f69SBart Van Assche }
4046*44704f69SBart Van Assche 
4047*44704f69SBart Van Assche /* Send a NVMe Identify(CNS=1) and decode Controller info. If the
4048*44704f69SBart Van Assche  * device name includes a namespace indication (e.g. /dev/nvme0ns1) then
4049*44704f69SBart Van Assche  * an Identify namespace command is sent to that namespace (e.g. 1). If the
4050*44704f69SBart Van Assche  * device name does not contain a namespace indication (e.g. /dev/nvme0)
4051*44704f69SBart Van Assche  * and --only is not given then nvme_id_namespace() is sent for each
4052*44704f69SBart Van Assche  * namespace in the controller. Namespaces number sequentially starting at
4053*44704f69SBart Van Assche  * 1 . The CNS (Controller or Namespace Structure) field is CDW10 7:0, was
4054*44704f69SBart Van Assche  * only bit 0 in NVMe 1.0 and bits 1:0 in NVMe 1.1, thereafter 8 bits. */
4055*44704f69SBart Van Assche static int
do_nvme_identify_ctrl(int pt_fd,const struct opts_t * op)4056*44704f69SBart Van Assche do_nvme_identify_ctrl(int pt_fd, const struct opts_t * op)
4057*44704f69SBart Van Assche {
4058*44704f69SBart Van Assche     int ret = 0;
4059*44704f69SBart Van Assche     int vb = op->verbose;
4060*44704f69SBart Van Assche     uint32_t k, nsid, max_nsid;
4061*44704f69SBart Van Assche     struct sg_pt_base * ptvp;
4062*44704f69SBart Van Assche     struct sg_nvme_passthru_cmd identify_cmd;
4063*44704f69SBart Van Assche     struct sg_nvme_passthru_cmd * id_cmdp = &identify_cmd;
4064*44704f69SBart Van Assche     uint8_t * id_dinp = NULL;
4065*44704f69SBart Van Assche     uint8_t * free_id_dinp = NULL;
4066*44704f69SBart Van Assche     const uint32_t pg_sz = sg_get_page_size();
4067*44704f69SBart Van Assche     uint8_t resp[16];
4068*44704f69SBart Van Assche 
4069*44704f69SBart Van Assche     if (op->do_raw) {
4070*44704f69SBart Van Assche         if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
4071*44704f69SBart Van Assche             perror("sg_set_binary_mode");
4072*44704f69SBart Van Assche             return SG_LIB_FILE_ERROR;
4073*44704f69SBart Van Assche         }
4074*44704f69SBart Van Assche     }
4075*44704f69SBart Van Assche     ptvp = construct_scsi_pt_obj_with_fd(pt_fd, vb);
4076*44704f69SBart Van Assche     if (NULL == ptvp) {
4077*44704f69SBart Van Assche         pr2serr("%s: memory problem\n", __func__);
4078*44704f69SBart Van Assche         return sg_convert_errno(ENOMEM);
4079*44704f69SBart Van Assche     }
4080*44704f69SBart Van Assche     memset(id_cmdp, 0, sizeof(*id_cmdp));
4081*44704f69SBart Van Assche     id_cmdp->opcode = 0x6;
4082*44704f69SBart Van Assche     nsid = get_pt_nvme_nsid(ptvp);
4083*44704f69SBart Van Assche     id_cmdp->cdw10 = 0x1;       /* CNS=0x1 --> Identify controller */
4084*44704f69SBart Van Assche     /* id_cmdp->nsid is a "don't care" when CNS=1, so leave as 0 */
4085*44704f69SBart Van Assche     id_dinp = sg_memalign(pg_sz, pg_sz, &free_id_dinp, false);
4086*44704f69SBart Van Assche     if (NULL == id_dinp) {
4087*44704f69SBart Van Assche         pr2serr("%s: sg_memalign problem\n", __func__);
4088*44704f69SBart Van Assche         return sg_convert_errno(ENOMEM);
4089*44704f69SBart Van Assche     }
4090*44704f69SBart Van Assche     set_scsi_pt_data_in(ptvp, id_dinp, pg_sz);
4091*44704f69SBart Van Assche     set_scsi_pt_cdb(ptvp, (const uint8_t *)id_cmdp, sizeof(*id_cmdp));
4092*44704f69SBart Van Assche     set_scsi_pt_sense(ptvp, resp, sizeof(resp));
4093*44704f69SBart Van Assche     ret = do_scsi_pt(ptvp, -1, 0 /* timeout (def: 1 min) */, vb);
4094*44704f69SBart Van Assche     if (vb > 2)
4095*44704f69SBart Van Assche         pr2serr("%s: do_scsi_pt result is %d\n", __func__, ret);
4096*44704f69SBart Van Assche     if (ret) {
4097*44704f69SBart Van Assche         if (SCSI_PT_DO_BAD_PARAMS == ret)
4098*44704f69SBart Van Assche             ret = SG_LIB_SYNTAX_ERROR;
4099*44704f69SBart Van Assche         else if (SCSI_PT_DO_TIMEOUT == ret)
4100*44704f69SBart Van Assche             ret = SG_LIB_CAT_TIMEOUT;
4101*44704f69SBart Van Assche         else if (ret < 0)
4102*44704f69SBart Van Assche             ret = sg_convert_errno(-ret);
4103*44704f69SBart Van Assche         goto err_out;
4104*44704f69SBart Van Assche     }
4105*44704f69SBart Van Assche     max_nsid = sg_get_unaligned_le32(id_dinp + 516); /* NN */
4106*44704f69SBart Van Assche     if (op->do_raw || op->do_hex) {
4107*44704f69SBart Van Assche         if (op->do_only || (SG_NVME_CTL_NSID == nsid ) ||
4108*44704f69SBart Van Assche             (SG_NVME_BROADCAST_NSID == nsid)) {
4109*44704f69SBart Van Assche             nvme_hex_raw(id_dinp, pg_sz, op);
4110*44704f69SBart Van Assche             goto fini;
4111*44704f69SBart Van Assche         }
4112*44704f69SBart Van Assche         goto skip1;
4113*44704f69SBart Van Assche     }
4114*44704f69SBart Van Assche     show_nvme_id_ctrl(id_dinp, op->device_name, op->do_long);
4115*44704f69SBart Van Assche skip1:
4116*44704f69SBart Van Assche     if (op->do_only)
4117*44704f69SBart Van Assche         goto fini;
4118*44704f69SBart Van Assche     if (nsid > 0) {
4119*44704f69SBart Van Assche         if (! (op->do_raw || (op->do_hex > 2))) {
4120*44704f69SBart Van Assche             printf("  Namespace %u (deduced from device name):\n", nsid);
4121*44704f69SBart Van Assche             if (nsid > max_nsid)
4122*44704f69SBart Van Assche                 pr2serr("NSID from device (%u) should not exceed number of "
4123*44704f69SBart Van Assche                         "namespaces (%u)\n", nsid, max_nsid);
4124*44704f69SBart Van Assche         }
4125*44704f69SBart Van Assche         ret = nvme_id_namespace(ptvp, nsid, id_cmdp, id_dinp, pg_sz, op);
4126*44704f69SBart Van Assche         if (ret)
4127*44704f69SBart Van Assche             goto err_out;
4128*44704f69SBart Van Assche 
4129*44704f69SBart Van Assche     } else {        /* nsid=0 so char device; loop over all namespaces */
4130*44704f69SBart Van Assche         for (k = 1; k <= max_nsid; ++k) {
4131*44704f69SBart Van Assche             if ((! op->do_raw) || (op->do_hex < 3))
4132*44704f69SBart Van Assche                 printf("  Namespace %u (of %u):\n", k, max_nsid);
4133*44704f69SBart Van Assche             ret = nvme_id_namespace(ptvp, k, id_cmdp, id_dinp, pg_sz, op);
4134*44704f69SBart Van Assche             if (ret)
4135*44704f69SBart Van Assche                 goto err_out;
4136*44704f69SBart Van Assche             if (op->do_raw || op->do_hex)
4137*44704f69SBart Van Assche                 goto fini;
4138*44704f69SBart Van Assche         }
4139*44704f69SBart Van Assche     }
4140*44704f69SBart Van Assche fini:
4141*44704f69SBart Van Assche     ret = 0;
4142*44704f69SBart Van Assche err_out:
4143*44704f69SBart Van Assche     destruct_scsi_pt_obj(ptvp);
4144*44704f69SBart Van Assche     free(free_id_dinp);
4145*44704f69SBart Van Assche     return ret;
4146*44704f69SBart Van Assche }
4147*44704f69SBart Van Assche #endif          /* (HAVE_NVME && (! IGNORE_NVME)) */
4148*44704f69SBart Van Assche 
4149*44704f69SBart Van Assche 
4150*44704f69SBart Van Assche int
main(int argc,char * argv[])4151*44704f69SBart Van Assche main(int argc, char * argv[])
4152*44704f69SBart Van Assche {
4153*44704f69SBart Van Assche     bool as_json;
4154*44704f69SBart Van Assche     int res, n, err;
4155*44704f69SBart Van Assche     int sg_fd = -1;
4156*44704f69SBart Van Assche     int ret = 0;
4157*44704f69SBart Van Assche     int subvalue = 0;
4158*44704f69SBart Van Assche     int inhex_len = 0;
4159*44704f69SBart Van Assche     int inraw_len = 0;
4160*44704f69SBart Van Assche     const char * cp;
4161*44704f69SBart Van Assche     const struct svpd_values_name_t * vnp;
4162*44704f69SBart Van Assche     sgj_state * jsp;
4163*44704f69SBart Van Assche     sgj_opaque_p jop = NULL;
4164*44704f69SBart Van Assche     struct opts_t opts SG_C_CPP_ZERO_INIT;
4165*44704f69SBart Van Assche     struct opts_t * op;
4166*44704f69SBart Van Assche 
4167*44704f69SBart Van Assche     op = &opts;
4168*44704f69SBart Van Assche     op->invoker = SG_VPD_INV_SG_INQ;
4169*44704f69SBart Van Assche     op->vpd_pn = -1;
4170*44704f69SBart Van Assche     op->vend_prod_num = -1;
4171*44704f69SBart Van Assche     op->page_pdt = -1;
4172*44704f69SBart Van Assche     op->do_block = -1;         /* use default for OS */
4173*44704f69SBart Van Assche     res = parse_cmd_line(op, argc, argv);
4174*44704f69SBart Van Assche     if (res)
4175*44704f69SBart Van Assche         return SG_LIB_SYNTAX_ERROR;
4176*44704f69SBart Van Assche     if (op->do_help) {
4177*44704f69SBart Van Assche         usage_for(op);
4178*44704f69SBart Van Assche         if (op->do_help > 1) {
4179*44704f69SBart Van Assche             pr2serr("\n>>> Available VPD page abbreviations:\n");
4180*44704f69SBart Van Assche             enumerate_vpds();
4181*44704f69SBart Van Assche         }
4182*44704f69SBart Van Assche         return 0;
4183*44704f69SBart Van Assche     }
4184*44704f69SBart Van Assche 
4185*44704f69SBart Van Assche #ifdef DEBUG
4186*44704f69SBart Van Assche     pr2serr("In DEBUG mode, ");
4187*44704f69SBart Van Assche     if (op->verbose_given && op->version_given) {
4188*44704f69SBart Van Assche         pr2serr("but override: '-vV' given, zero verbose and continue\n");
4189*44704f69SBart Van Assche         op->verbose_given = false;
4190*44704f69SBart Van Assche         op->version_given = false;
4191*44704f69SBart Van Assche         op->verbose = 0;
4192*44704f69SBart Van Assche     } else if (! op->verbose_given) {
4193*44704f69SBart Van Assche         pr2serr("set '-vv'\n");
4194*44704f69SBart Van Assche         op->verbose = 2;
4195*44704f69SBart Van Assche     } else
4196*44704f69SBart Van Assche         pr2serr("keep verbose=%d\n", op->verbose);
4197*44704f69SBart Van Assche #else
4198*44704f69SBart Van Assche     if (op->verbose_given && op->version_given)
4199*44704f69SBart Van Assche         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
4200*44704f69SBart Van Assche #endif
4201*44704f69SBart Van Assche     if (op->version_given) {
4202*44704f69SBart Van Assche         pr2serr("Version string: %s\n", version_str);
4203*44704f69SBart Van Assche         return 0;
4204*44704f69SBart Van Assche     }
4205*44704f69SBart Van Assche     jsp = &op->json_st;
4206*44704f69SBart Van Assche     as_json = jsp->pr_as_json;
4207*44704f69SBart Van Assche     if (op->page_str) {
4208*44704f69SBart Van Assche         if (op->vpd_pn >= 0) {
4209*44704f69SBart Van Assche             pr2serr("Given '-p' option and another option that "
4210*44704f69SBart Van Assche                     "implies a page\n");
4211*44704f69SBart Van Assche             return SG_LIB_CONTRADICT;
4212*44704f69SBart Van Assche         }
4213*44704f69SBart Van Assche         if ('-' == op->page_str[0])
4214*44704f69SBart Van Assche             op->vpd_pn = VPD_NOPE_WANT_STD_INQ;
4215*44704f69SBart Van Assche         else if (isalpha((uint8_t)op->page_str[0])) {
4216*44704f69SBart Van Assche             vnp = sdp_find_vpd_by_acron(op->page_str);
4217*44704f69SBart Van Assche             if (NULL == vnp) {
4218*44704f69SBart Van Assche #ifdef SG_SCSI_STRINGS
4219*44704f69SBart Van Assche                 if (op->opt_new)
4220*44704f69SBart Van Assche                     pr2serr("abbreviation %s given to '--page=' "
4221*44704f69SBart Van Assche                             "not recognized\n", op->page_str);
4222*44704f69SBart Van Assche                 else
4223*44704f69SBart Van Assche                     pr2serr("abbreviation %s given to '-p=' "
4224*44704f69SBart Van Assche                             "not recognized\n", op->page_str);
4225*44704f69SBart Van Assche #else
4226*44704f69SBart Van Assche                 pr2serr("abbreviation %s given to '--page=' "
4227*44704f69SBart Van Assche                         "not recognized\n", op->page_str);
4228*44704f69SBart Van Assche #endif
4229*44704f69SBart Van Assche                 pr2serr(">>> Available abbreviations:\n");
4230*44704f69SBart Van Assche                 enumerate_vpds();
4231*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
4232*44704f69SBart Van Assche             }
4233*44704f69SBart Van Assche             // if ((1 != op->do_hex) && (0 == op->do_raw))
4234*44704f69SBart Van Assche             if (0 == op->do_raw)
4235*44704f69SBart Van Assche                 op->do_decode = true;
4236*44704f69SBart Van Assche             op->vpd_pn = vnp->value;
4237*44704f69SBart Van Assche             subvalue = vnp->subvalue;
4238*44704f69SBart Van Assche             op->page_pdt = vnp->pdt;
4239*44704f69SBart Van Assche         } else {
4240*44704f69SBart Van Assche             cp = strchr(op->page_str, ',');
4241*44704f69SBart Van Assche             if (cp && op->vend_prod) {
4242*44704f69SBart Van Assche                 pr2serr("the --page=pg,vp and the --vendor=vp forms overlap, "
4243*44704f69SBart Van Assche                         "choose one or the other\n");
4244*44704f69SBart Van Assche                 ret = SG_LIB_SYNTAX_ERROR;
4245*44704f69SBart Van Assche                 goto err_out;
4246*44704f69SBart Van Assche             }
4247*44704f69SBart Van Assche             op->vpd_pn = sg_get_num_nomult(op->page_str);
4248*44704f69SBart Van Assche             if ((op->vpd_pn < 0) || (op->vpd_pn > 255)) {
4249*44704f69SBart Van Assche                 pr2serr("Bad page code value after '-p' option\n");
4250*44704f69SBart Van Assche                 printf("Available standard VPD pages:\n");
4251*44704f69SBart Van Assche                 enumerate_vpds(/* 1, 1 */);
4252*44704f69SBart Van Assche                 ret = SG_LIB_SYNTAX_ERROR;
4253*44704f69SBart Van Assche                 goto err_out;
4254*44704f69SBart Van Assche             }
4255*44704f69SBart Van Assche             if (cp) {
4256*44704f69SBart Van Assche                 if (isdigit((uint8_t)*(cp + 1)))
4257*44704f69SBart Van Assche                     op->vend_prod_num = sg_get_num_nomult(cp + 1);
4258*44704f69SBart Van Assche                 else
4259*44704f69SBart Van Assche                     op->vend_prod_num = svpd_find_vp_num_by_acron(cp + 1);
4260*44704f69SBart Van Assche                 if ((op->vend_prod_num < 0) || (op->vend_prod_num > 255)) {
4261*44704f69SBart Van Assche                     pr2serr("Bad vendor/product acronym after comma in '-p' "
4262*44704f69SBart Van Assche                             "option\n");
4263*44704f69SBart Van Assche                     if (op->vend_prod_num < 0)
4264*44704f69SBart Van Assche                         svpd_enumerate_vendor(-1);
4265*44704f69SBart Van Assche                     ret = SG_LIB_SYNTAX_ERROR;
4266*44704f69SBart Van Assche                     goto err_out;
4267*44704f69SBart Van Assche                 }
4268*44704f69SBart Van Assche                 subvalue = op->vend_prod_num;
4269*44704f69SBart Van Assche             } else if (op->vend_prod) {
4270*44704f69SBart Van Assche                 if (isdigit((uint8_t)op->vend_prod[0]))
4271*44704f69SBart Van Assche                     op->vend_prod_num = sg_get_num_nomult(op->vend_prod);
4272*44704f69SBart Van Assche                 else
4273*44704f69SBart Van Assche                     op->vend_prod_num =
4274*44704f69SBart Van Assche                         svpd_find_vp_num_by_acron(op->vend_prod);
4275*44704f69SBart Van Assche                 if ((op->vend_prod_num < 0) || (op->vend_prod_num > 255)) {
4276*44704f69SBart Van Assche                     pr2serr("Bad vendor/product acronym after '--vendor=' "
4277*44704f69SBart Van Assche                             "option\n");
4278*44704f69SBart Van Assche                     svpd_enumerate_vendor(-1);
4279*44704f69SBart Van Assche                     ret = SG_LIB_SYNTAX_ERROR;
4280*44704f69SBart Van Assche                     goto err_out;
4281*44704f69SBart Van Assche                 }
4282*44704f69SBart Van Assche                 subvalue = op->vend_prod_num;
4283*44704f69SBart Van Assche             }
4284*44704f69SBart Van Assche         }
4285*44704f69SBart Van Assche         if (op->verbose > 3)
4286*44704f69SBart Van Assche            pr2serr("'--page=' matched pn=%d [0x%x], subvalue=%d\n",
4287*44704f69SBart Van Assche                    op->vpd_pn, op->vpd_pn, subvalue);
4288*44704f69SBart Van Assche #if 0
4289*44704f69SBart Van Assche         else {
4290*44704f69SBart Van Assche #ifdef SG_SCSI_STRINGS
4291*44704f69SBart Van Assche             if (op->opt_new) {
4292*44704f69SBart Van Assche                 n = sg_get_num(op->page_str);
4293*44704f69SBart Van Assche                 if ((n < 0) || (n > 255)) {
4294*44704f69SBart Van Assche                     pr2serr("Bad argument to '--page=', "
4295*44704f69SBart Van Assche                             "expecting 0 to 255 inclusive\n");
4296*44704f69SBart Van Assche                     usage_for(op);
4297*44704f69SBart Van Assche                     return SG_LIB_SYNTAX_ERROR;
4298*44704f69SBart Van Assche                 }
4299*44704f69SBart Van Assche                 if ((1 != op->do_hex) && (0 == op->do_raw))
4300*44704f69SBart Van Assche                     op->do_decode = true;
4301*44704f69SBart Van Assche             } else {
4302*44704f69SBart Van Assche                 int num;
4303*44704f69SBart Van Assche                 unsigned int u;
4304*44704f69SBart Van Assche 
4305*44704f69SBart Van Assche                 num = sscanf(op->page_str, "%x", &u);
4306*44704f69SBart Van Assche                 if ((1 != num) || (u > 255)) {
4307*44704f69SBart Van Assche                     pr2serr("Inappropriate value after '-o=' "
4308*44704f69SBart Van Assche                             "or '-p=' option\n");
4309*44704f69SBart Van Assche                     usage_for(op);
4310*44704f69SBart Van Assche                     return SG_LIB_SYNTAX_ERROR;
4311*44704f69SBart Van Assche                 }
4312*44704f69SBart Van Assche                 n = u;
4313*44704f69SBart Van Assche             }
4314*44704f69SBart Van Assche #else
4315*44704f69SBart Van Assche             n = sg_get_num(op->page_str);
4316*44704f69SBart Van Assche             if ((n < 0) || (n > 255)) {
4317*44704f69SBart Van Assche                 pr2serr("Bad argument to '--page=', "
4318*44704f69SBart Van Assche                         "expecting 0 to 255 inclusive\n");
4319*44704f69SBart Van Assche                 usage_for(op);
4320*44704f69SBart Van Assche                 return SG_LIB_SYNTAX_ERROR;
4321*44704f69SBart Van Assche             }
4322*44704f69SBart Van Assche             if ((1 != op->do_hex) && (0 == op->do_raw))
4323*44704f69SBart Van Assche                 op->do_decode = true;
4324*44704f69SBart Van Assche #endif /* SG_SCSI_STRINGS */
4325*44704f69SBart Van Assche             op->vpd_pn = n;
4326*44704f69SBart Van Assche         }
4327*44704f69SBart Van Assche #endif
4328*44704f69SBart Van Assche     } else if (op->vend_prod) {
4329*44704f69SBart Van Assche         if (isdigit((uint8_t)op->vend_prod[0]))
4330*44704f69SBart Van Assche             op->vend_prod_num = sg_get_num_nomult(op->vend_prod);
4331*44704f69SBart Van Assche         else
4332*44704f69SBart Van Assche             op->vend_prod_num = svpd_find_vp_num_by_acron(op->vend_prod);
4333*44704f69SBart Van Assche         if ((op->vend_prod_num < 0) || (op->vend_prod_num > 255)) {
4334*44704f69SBart Van Assche             pr2serr("Bad vendor/product acronym after '--vendor=' "
4335*44704f69SBart Van Assche                     "option\n");
4336*44704f69SBart Van Assche             svpd_enumerate_vendor(-1);
4337*44704f69SBart Van Assche             ret = SG_LIB_SYNTAX_ERROR;
4338*44704f69SBart Van Assche             goto err_out;
4339*44704f69SBart Van Assche         }
4340*44704f69SBart Van Assche         subvalue = op->vend_prod_num;
4341*44704f69SBart Van Assche     }
4342*44704f69SBart Van Assche     if (as_json)
4343*44704f69SBart Van Assche         jop = sgj_start_r(MY_NAME, version_str, argc, argv, jsp);
4344*44704f69SBart Van Assche 
4345*44704f69SBart Van Assche     rsp_buff = sg_memalign(rsp_buff_sz, 0 /* page align */, &free_rsp_buff,
4346*44704f69SBart Van Assche                            false);
4347*44704f69SBart Van Assche     if (NULL == rsp_buff) {
4348*44704f69SBart Van Assche         pr2serr("Unable to allocate %d bytes on heap\n", rsp_buff_sz);
4349*44704f69SBart Van Assche         return sg_convert_errno(ENOMEM);
4350*44704f69SBart Van Assche     }
4351*44704f69SBart Van Assche     if (op->sinq_inraw_fn) {
4352*44704f69SBart Van Assche         if (op->do_cmddt) {
4353*44704f69SBart Van Assche             pr2serr("Don't support --cmddt with --sinq-inraw= option\n");
4354*44704f69SBart Van Assche             ret = SG_LIB_CONTRADICT;
4355*44704f69SBart Van Assche             goto err_out;
4356*44704f69SBart Van Assche         }
4357*44704f69SBart Van Assche         if ((ret = sg_f2hex_arr(op->sinq_inraw_fn, true, false, rsp_buff,
4358*44704f69SBart Van Assche                                 &inraw_len, rsp_buff_sz))) {
4359*44704f69SBart Van Assche             goto err_out;
4360*44704f69SBart Van Assche         }
4361*44704f69SBart Van Assche         if (inraw_len < 36) {
4362*44704f69SBart Van Assche             pr2serr("Unable to read 36 or more bytes from %s\n",
4363*44704f69SBart Van Assche                     op->sinq_inraw_fn);
4364*44704f69SBart Van Assche             ret = SG_LIB_FILE_ERROR;
4365*44704f69SBart Van Assche             goto err_out;
4366*44704f69SBart Van Assche         }
4367*44704f69SBart Van Assche         memcpy(op->std_inq_a,  rsp_buff, 36);
4368*44704f69SBart Van Assche         op->std_inq_a_valid = true;
4369*44704f69SBart Van Assche     }
4370*44704f69SBart Van Assche     if (op->inhex_fn) {
4371*44704f69SBart Van Assche         if (op->device_name) {
4372*44704f69SBart Van Assche             pr2serr("Cannot have both a DEVICE and --inhex= option\n");
4373*44704f69SBart Van Assche             ret = SG_LIB_CONTRADICT;
4374*44704f69SBart Van Assche             goto err_out;
4375*44704f69SBart Van Assche         }
4376*44704f69SBart Van Assche         if (op->do_cmddt) {
4377*44704f69SBart Van Assche             pr2serr("Don't support --cmddt with --inhex= option\n");
4378*44704f69SBart Van Assche             ret = SG_LIB_CONTRADICT;
4379*44704f69SBart Van Assche             goto err_out;
4380*44704f69SBart Van Assche         }
4381*44704f69SBart Van Assche         err = sg_f2hex_arr(op->inhex_fn, !!op->do_raw, false, rsp_buff,
4382*44704f69SBart Van Assche                            &inhex_len, rsp_buff_sz);
4383*44704f69SBart Van Assche         if (err) {
4384*44704f69SBart Van Assche             if (err < 0)
4385*44704f69SBart Van Assche                 err = sg_convert_errno(-err);
4386*44704f69SBart Van Assche             ret = err;
4387*44704f69SBart Van Assche             goto err_out;
4388*44704f69SBart Van Assche         }
4389*44704f69SBart Van Assche         op->do_raw = 0;         /* don't want raw on output with --inhex= */
4390*44704f69SBart Van Assche         if (-1 == op->vpd_pn) {       /* may be able to deduce VPD page */
4391*44704f69SBart Van Assche             if (op->page_pdt < 0)
4392*44704f69SBart Van Assche                 op->page_pdt = PDT_MASK & rsp_buff[0];
4393*44704f69SBart Van Assche             if ((0x2 == (0xf & rsp_buff[3])) && (rsp_buff[2] > 2)) {
4394*44704f69SBart Van Assche                 if (op->verbose)
4395*44704f69SBart Van Assche                     pr2serr("Guessing from --inhex= this is a standard "
4396*44704f69SBart Van Assche                             "INQUIRY\n");
4397*44704f69SBart Van Assche             } else if (rsp_buff[2] <= 2) {
4398*44704f69SBart Van Assche                 /*
4399*44704f69SBart Van Assche                  * Removable devices have the RMB bit set, which would
4400*44704f69SBart Van Assche                  * present itself as vpd page 0x80 output if we're not
4401*44704f69SBart Van Assche                  * careful
4402*44704f69SBart Van Assche                  *
4403*44704f69SBart Van Assche                  * Serial number must be right-aligned ASCII data in
4404*44704f69SBart Van Assche                  * bytes 5-7; standard INQUIRY will have flags here.
4405*44704f69SBart Van Assche                  */
4406*44704f69SBart Van Assche                 if (rsp_buff[1] == 0x80 &&
4407*44704f69SBart Van Assche                     (rsp_buff[5] < 0x20 || rsp_buff[5] > 0x80 ||
4408*44704f69SBart Van Assche                      rsp_buff[6] < 0x20 || rsp_buff[6] > 0x80 ||
4409*44704f69SBart Van Assche                      rsp_buff[7] < 0x20 || rsp_buff[7] > 0x80)) {
4410*44704f69SBart Van Assche                     if (op->verbose)
4411*44704f69SBart Van Assche                         pr2serr("Guessing from --inhex= this is a "
4412*44704f69SBart Van Assche                                 "standard INQUIRY\n");
4413*44704f69SBart Van Assche                 } else {
4414*44704f69SBart Van Assche                     if (op->verbose)
4415*44704f69SBart Van Assche                         pr2serr("Guessing from --inhex= this is VPD "
4416*44704f69SBart Van Assche                                 "page 0x%x\n", rsp_buff[1]);
4417*44704f69SBart Van Assche                     op->vpd_pn = rsp_buff[1];
4418*44704f69SBart Van Assche                     op->do_vpd = true;
4419*44704f69SBart Van Assche                     if ((1 != op->do_hex) && (0 == op->do_raw))
4420*44704f69SBart Van Assche                         op->do_decode = true;
4421*44704f69SBart Van Assche                 }
4422*44704f69SBart Van Assche             } else {
4423*44704f69SBart Van Assche                 if (op->verbose)
4424*44704f69SBart Van Assche                     pr2serr("page number unclear from --inhex, hope it's a "
4425*44704f69SBart Van Assche                             "standard INQUIRY\n");
4426*44704f69SBart Van Assche             }
4427*44704f69SBart Van Assche         } else
4428*44704f69SBart Van Assche             op->do_vpd = true;
4429*44704f69SBart Van Assche         if (op->do_vpd) {   /* Allow for multiple VPD pages from 'sg_vpd -a' */
4430*44704f69SBart Van Assche             op->maxlen = inhex_len;
4431*44704f69SBart Van Assche             ret = svpd_inhex_decode_all(op, jop);
4432*44704f69SBart Van Assche             goto fini2;
4433*44704f69SBart Van Assche         }
4434*44704f69SBart Van Assche     } else if (0 == op->device_name) {
4435*44704f69SBart Van Assche         pr2serr("No DEVICE argument given\n\n");
4436*44704f69SBart Van Assche         usage_for(op);
4437*44704f69SBart Van Assche         ret = SG_LIB_SYNTAX_ERROR;
4438*44704f69SBart Van Assche         goto err_out;
4439*44704f69SBart Van Assche     }
4440*44704f69SBart Van Assche     if (VPD_NOPE_WANT_STD_INQ == op->vpd_pn)
4441*44704f69SBart Van Assche         op->vpd_pn = -1;  /* now past guessing, set to normal indication */
4442*44704f69SBart Van Assche 
4443*44704f69SBart Van Assche     if (op->do_export) {
4444*44704f69SBart Van Assche         if (op->vpd_pn != -1) {
4445*44704f69SBart Van Assche             if (op->vpd_pn != VPD_DEVICE_ID &&
4446*44704f69SBart Van Assche                 op->vpd_pn != VPD_UNIT_SERIAL_NUM) {
4447*44704f69SBart Van Assche                 pr2serr("Option '--export' only supported for VPD pages 0x80 "
4448*44704f69SBart Van Assche                         "and 0x83\n");
4449*44704f69SBart Van Assche                 usage_for(op);
4450*44704f69SBart Van Assche                 ret = SG_LIB_CONTRADICT;
4451*44704f69SBart Van Assche                 goto err_out;
4452*44704f69SBart Van Assche             }
4453*44704f69SBart Van Assche             op->do_decode = true;
4454*44704f69SBart Van Assche             op->do_vpd = true;
4455*44704f69SBart Van Assche         }
4456*44704f69SBart Van Assche     }
4457*44704f69SBart Van Assche 
4458*44704f69SBart Van Assche     if ((0 == op->do_cmddt) && (op->vpd_pn >= 0) && op->page_given)
4459*44704f69SBart Van Assche         op->do_vpd = true;
4460*44704f69SBart Van Assche 
4461*44704f69SBart Van Assche     if (op->do_raw && op->do_hex) {
4462*44704f69SBart Van Assche         pr2serr("Can't do hex and raw at the same time\n");
4463*44704f69SBart Van Assche         usage_for(op);
4464*44704f69SBart Van Assche         ret = SG_LIB_CONTRADICT;
4465*44704f69SBart Van Assche         goto err_out;
4466*44704f69SBart Van Assche     }
4467*44704f69SBart Van Assche     if (op->do_vpd && op->do_cmddt) {
4468*44704f69SBart Van Assche #ifdef SG_SCSI_STRINGS
4469*44704f69SBart Van Assche         if (op->opt_new)
4470*44704f69SBart Van Assche             pr2serr("Can't use '--cmddt' with VPD pages\n");
4471*44704f69SBart Van Assche         else
4472*44704f69SBart Van Assche             pr2serr("Can't have both '-e' and '-c' (or '-cl')\n");
4473*44704f69SBart Van Assche #else
4474*44704f69SBart Van Assche         pr2serr("Can't use '--cmddt' with VPD pages\n");
4475*44704f69SBart Van Assche #endif
4476*44704f69SBart Van Assche         usage_for(op);
4477*44704f69SBart Van Assche         ret = SG_LIB_CONTRADICT;
4478*44704f69SBart Van Assche         goto err_out;
4479*44704f69SBart Van Assche     }
4480*44704f69SBart Van Assche     if (((op->do_vpd || op->do_cmddt)) && (op->vpd_pn < 0))
4481*44704f69SBart Van Assche         op->vpd_pn = 0;
4482*44704f69SBart Van Assche     if (op->num_pages > 1) {
4483*44704f69SBart Van Assche         pr2serr("Can only fetch one page (VPD or Cmd) at a time\n");
4484*44704f69SBart Van Assche         usage_for(op);
4485*44704f69SBart Van Assche         ret = SG_LIB_SYNTAX_ERROR;
4486*44704f69SBart Van Assche         goto err_out;
4487*44704f69SBart Van Assche     }
4488*44704f69SBart Van Assche     if (op->do_descriptors) {
4489*44704f69SBart Van Assche         if ((op->maxlen > 0) && (op->maxlen < 60)) {
4490*44704f69SBart Van Assche             pr2serr("version descriptors need INQUIRY response "
4491*44704f69SBart Van Assche                     "length >= 60 bytes\n");
4492*44704f69SBart Van Assche             ret = SG_LIB_SYNTAX_ERROR;
4493*44704f69SBart Van Assche             goto err_out;
4494*44704f69SBart Van Assche         }
4495*44704f69SBart Van Assche         if (op->do_vpd || op->do_cmddt) {
4496*44704f69SBart Van Assche             pr2serr("version descriptors require standard INQUIRY\n");
4497*44704f69SBart Van Assche             ret = SG_LIB_SYNTAX_ERROR;
4498*44704f69SBart Van Assche             goto err_out;
4499*44704f69SBart Van Assche         }
4500*44704f69SBart Van Assche     }
4501*44704f69SBart Van Assche     if (op->num_pages && op->do_ata) {
4502*44704f69SBart Van Assche         pr2serr("Can't use '-A' with an explicit decode VPD page option\n");
4503*44704f69SBart Van Assche         ret = SG_LIB_CONTRADICT;
4504*44704f69SBart Van Assche         goto err_out;
4505*44704f69SBart Van Assche     }
4506*44704f69SBart Van Assche 
4507*44704f69SBart Van Assche     if (op->do_raw) {
4508*44704f69SBart Van Assche         if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
4509*44704f69SBart Van Assche             perror("sg_set_binary_mode");
4510*44704f69SBart Van Assche             ret = SG_LIB_FILE_ERROR;
4511*44704f69SBart Van Assche             goto err_out;
4512*44704f69SBart Van Assche         }
4513*44704f69SBart Van Assche     }
4514*44704f69SBart Van Assche     if (op->inhex_fn) {
4515*44704f69SBart Van Assche         if (op->do_vpd) {
4516*44704f69SBart Van Assche             if (op->do_decode)
4517*44704f69SBart Van Assche                 ret = vpd_decode(-1, op, jop, 0);
4518*44704f69SBart Van Assche             else
4519*44704f69SBart Van Assche                 ret = vpd_mainly_hex(-1, op, NULL, 0);
4520*44704f69SBart Van Assche             goto err_out;
4521*44704f69SBart Van Assche         }
4522*44704f69SBart Van Assche #if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS) && \
4523*44704f69SBart Van Assche     defined(HDIO_GET_IDENTITY)
4524*44704f69SBart Van Assche         else if (op->do_ata) {
4525*44704f69SBart Van Assche             prepare_ata_identify(op, inhex_len);
4526*44704f69SBart Van Assche             ret = 0;
4527*44704f69SBart Van Assche             goto err_out;
4528*44704f69SBart Van Assche         }
4529*44704f69SBart Van Assche #endif
4530*44704f69SBart Van Assche         else {
4531*44704f69SBart Van Assche             op->maxlen = inhex_len;
4532*44704f69SBart Van Assche             ret = std_inq_process(-1, op, jop, 0);
4533*44704f69SBart Van Assche             goto err_out;
4534*44704f69SBart Van Assche         }
4535*44704f69SBart Van Assche     }
4536*44704f69SBart Van Assche 
4537*44704f69SBart Van Assche #if defined(O_NONBLOCK) && defined(O_RDONLY)
4538*44704f69SBart Van Assche     if (op->do_block >= 0) {
4539*44704f69SBart Van Assche         n = O_RDONLY | (op->do_block ? 0 : O_NONBLOCK);
4540*44704f69SBart Van Assche         if ((sg_fd = sg_cmds_open_flags(op->device_name, n,
4541*44704f69SBart Van Assche                                         op->verbose)) < 0) {
4542*44704f69SBart Van Assche             pr2serr("sg_inq: error opening file: %s: %s\n",
4543*44704f69SBart Van Assche                     op->device_name, safe_strerror(-sg_fd));
4544*44704f69SBart Van Assche             ret = sg_convert_errno(-sg_fd);
4545*44704f69SBart Van Assche             if (ret < 0)
4546*44704f69SBart Van Assche                 ret = SG_LIB_FILE_ERROR;
4547*44704f69SBart Van Assche             goto err_out;
4548*44704f69SBart Van Assche         }
4549*44704f69SBart Van Assche 
4550*44704f69SBart Van Assche     } else {
4551*44704f69SBart Van Assche         if ((sg_fd = sg_cmds_open_device(op->device_name, true /* ro */,
4552*44704f69SBart Van Assche                                          op->verbose)) < 0) {
4553*44704f69SBart Van Assche             pr2serr("sg_inq: error opening file: %s: %s\n",
4554*44704f69SBart Van Assche                     op->device_name, safe_strerror(-sg_fd));
4555*44704f69SBart Van Assche             ret = sg_convert_errno(-sg_fd);
4556*44704f69SBart Van Assche             if (ret < 0)
4557*44704f69SBart Van Assche                 ret = SG_LIB_FILE_ERROR;
4558*44704f69SBart Van Assche             goto err_out;
4559*44704f69SBart Van Assche         }
4560*44704f69SBart Van Assche     }
4561*44704f69SBart Van Assche #else
4562*44704f69SBart Van Assche     if ((sg_fd = sg_cmds_open_device(op->device_name, true /* ro */,
4563*44704f69SBart Van Assche                                      op->verbose)) < 0) {
4564*44704f69SBart Van Assche         pr2serr("sg_inq: error opening file: %s: %s\n",
4565*44704f69SBart Van Assche                 op->device_name, safe_strerror(-sg_fd));
4566*44704f69SBart Van Assche         ret = sg_convert_errno(-sg_fd);
4567*44704f69SBart Van Assche         if (ret < 0)
4568*44704f69SBart Van Assche             ret = SG_LIB_FILE_ERROR;
4569*44704f69SBart Van Assche         goto err_out;
4570*44704f69SBart Van Assche     }
4571*44704f69SBart Van Assche #endif
4572*44704f69SBart Van Assche     memset(rsp_buff, 0, rsp_buff_sz);
4573*44704f69SBart Van Assche 
4574*44704f69SBart Van Assche #if (HAVE_NVME && (! IGNORE_NVME))
4575*44704f69SBart Van Assche     n = check_pt_file_handle(sg_fd, op->device_name, op->verbose);
4576*44704f69SBart Van Assche     if (op->verbose > 1)
4577*44704f69SBart Van Assche         pr2serr("check_pt_file_handle()-->%d, page_given: %s\n", n,
4578*44704f69SBart Van Assche                 (op->page_given ? "yes" : "no"));
4579*44704f69SBart Van Assche     if (n > 2) {   /* NVMe char or NVMe block */
4580*44704f69SBart Van Assche         op->possible_nvme = true;
4581*44704f69SBart Van Assche         if (! op->page_given) {
4582*44704f69SBart Van Assche             ret = do_nvme_identify_ctrl(sg_fd, op);
4583*44704f69SBart Van Assche             goto fini2;
4584*44704f69SBart Van Assche         }
4585*44704f69SBart Van Assche     }
4586*44704f69SBart Van Assche #endif
4587*44704f69SBart Van Assche 
4588*44704f69SBart Van Assche #if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS) && \
4589*44704f69SBart Van Assche     defined(HDIO_GET_IDENTITY)
4590*44704f69SBart Van Assche     if (op->do_ata) {
4591*44704f69SBart Van Assche         res = try_ata_identify(sg_fd, op->do_hex, op->do_raw,
4592*44704f69SBart Van Assche                                op->verbose);
4593*44704f69SBart Van Assche         if (0 != res) {
4594*44704f69SBart Van Assche             pr2serr("fetching ATA information failed on %s\n",
4595*44704f69SBart Van Assche                     op->device_name);
4596*44704f69SBart Van Assche             ret = SG_LIB_CAT_OTHER;
4597*44704f69SBart Van Assche         } else
4598*44704f69SBart Van Assche             ret = 0;
4599*44704f69SBart Van Assche         goto fini3;
4600*44704f69SBart Van Assche     }
4601*44704f69SBart Van Assche #endif
4602*44704f69SBart Van Assche 
4603*44704f69SBart Van Assche     if ((! op->do_cmddt) && (! op->do_vpd)) {
4604*44704f69SBart Van Assche         /* So it's a standard INQUIRY, try ATA IDENTIFY if that fails */
4605*44704f69SBart Van Assche         ret = std_inq_process(sg_fd, op, jop, 0);
4606*44704f69SBart Van Assche         if (ret)
4607*44704f69SBart Van Assche             goto err_out;
4608*44704f69SBart Van Assche     } else if (op->do_cmddt) {
4609*44704f69SBart Van Assche         if (op->vpd_pn < 0)
4610*44704f69SBart Van Assche             op->vpd_pn = 0;
4611*44704f69SBart Van Assche         ret = cmddt_process(sg_fd, op);
4612*44704f69SBart Van Assche         if (ret)
4613*44704f69SBart Van Assche             goto err_out;
4614*44704f69SBart Van Assche     } else if (op->do_vpd) {
4615*44704f69SBart Van Assche         if (op->do_decode) {
4616*44704f69SBart Van Assche             ret = vpd_decode(sg_fd, op, jop, 0);
4617*44704f69SBart Van Assche             if (ret)
4618*44704f69SBart Van Assche                 goto err_out;
4619*44704f69SBart Van Assche         } else {
4620*44704f69SBart Van Assche             ret = vpd_mainly_hex(sg_fd, op, NULL, 0);
4621*44704f69SBart Van Assche             if (ret)
4622*44704f69SBart Van Assche                 goto err_out;
4623*44704f69SBart Van Assche         }
4624*44704f69SBart Van Assche     }
4625*44704f69SBart Van Assche 
4626*44704f69SBart Van Assche #if (HAVE_NVME && (! IGNORE_NVME))
4627*44704f69SBart Van Assche fini2:
4628*44704f69SBart Van Assche #endif
4629*44704f69SBart Van Assche #if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS) && \
4630*44704f69SBart Van Assche     defined(HDIO_GET_IDENTITY)
4631*44704f69SBart Van Assche fini3:
4632*44704f69SBart Van Assche #endif
4633*44704f69SBart Van Assche 
4634*44704f69SBart Van Assche err_out:
4635*44704f69SBart Van Assche     if (free_rsp_buff)
4636*44704f69SBart Van Assche         free(free_rsp_buff);
4637*44704f69SBart Van Assche     if ((0 == op->verbose) && (! op->do_export)) {
4638*44704f69SBart Van Assche         if (! sg_if_can2stderr("sg_inq failed: ", ret))
4639*44704f69SBart Van Assche             pr2serr("Some error occurred, try again with '-v' or '-vv' for "
4640*44704f69SBart Van Assche                     "more information\n");
4641*44704f69SBart Van Assche     }
4642*44704f69SBart Van Assche     res = (sg_fd >= 0) ? sg_cmds_close_device(sg_fd) : 0;
4643*44704f69SBart Van Assche     if (res < 0) {
4644*44704f69SBart Van Assche         pr2serr("close error: %s\n", safe_strerror(-res));
4645*44704f69SBart Van Assche         if (0 == ret)
4646*44704f69SBart Van Assche             ret = sg_convert_errno(-res);
4647*44704f69SBart Van Assche     }
4648*44704f69SBart Van Assche     ret = (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
4649*44704f69SBart Van Assche     if (as_json) {
4650*44704f69SBart Van Assche         if (0 == op->do_hex)
4651*44704f69SBart Van Assche             sgj_js2file(jsp, NULL, ret, stdout);
4652*44704f69SBart Van Assche         sgj_finish(jsp);
4653*44704f69SBart Van Assche     }
4654*44704f69SBart Van Assche     return ret;
4655*44704f69SBart Van Assche }
4656*44704f69SBart Van Assche 
4657*44704f69SBart Van Assche 
4658*44704f69SBart Van Assche #if defined(SG_LIB_LINUX) && defined(SG_SCSI_STRINGS) && \
4659*44704f69SBart Van Assche     defined(HDIO_GET_IDENTITY)
4660*44704f69SBart Van Assche /* Following code permits ATA IDENTIFY commands to be performed on
4661*44704f69SBart Van Assche    ATA non "Packet Interface" devices (e.g. ATA disks).
4662*44704f69SBart Van Assche    GPL-ed code borrowed from smartmontools (smartmontools.sf.net).
4663*44704f69SBart Van Assche    Copyright (C) 2002-4 Bruce Allen
4664*44704f69SBart Van Assche                 <[email protected]>
4665*44704f69SBart Van Assche  */
4666*44704f69SBart Van Assche #ifndef ATA_IDENTIFY_DEVICE
4667*44704f69SBart Van Assche #define ATA_IDENTIFY_DEVICE 0xec
4668*44704f69SBart Van Assche #define ATA_IDENTIFY_PACKET_DEVICE 0xa1
4669*44704f69SBart Van Assche #endif
4670*44704f69SBart Van Assche #ifndef HDIO_DRIVE_CMD
4671*44704f69SBart Van Assche #define HDIO_DRIVE_CMD    0x031f
4672*44704f69SBart Van Assche #endif
4673*44704f69SBart Van Assche 
4674*44704f69SBart Van Assche /* Needed parts of the ATA DRIVE IDENTIFY Structure. Those labeled
4675*44704f69SBart Van Assche  * word* are NOT used.
4676*44704f69SBart Van Assche  */
4677*44704f69SBart Van Assche struct ata_identify_device {
4678*44704f69SBart Van Assche   unsigned short words000_009[10];
4679*44704f69SBart Van Assche   uint8_t  serial_no[20];
4680*44704f69SBart Van Assche   unsigned short words020_022[3];
4681*44704f69SBart Van Assche   uint8_t  fw_rev[8];
4682*44704f69SBart Van Assche   uint8_t  model[40];
4683*44704f69SBart Van Assche   unsigned short words047_079[33];
4684*44704f69SBart Van Assche   unsigned short major_rev_num;
4685*44704f69SBart Van Assche   unsigned short minor_rev_num;
4686*44704f69SBart Van Assche   unsigned short command_set_1;
4687*44704f69SBart Van Assche   unsigned short command_set_2;
4688*44704f69SBart Van Assche   unsigned short command_set_extension;
4689*44704f69SBart Van Assche   unsigned short cfs_enable_1;
4690*44704f69SBart Van Assche   unsigned short word086;
4691*44704f69SBart Van Assche   unsigned short csf_default;
4692*44704f69SBart Van Assche   unsigned short words088_255[168];
4693*44704f69SBart Van Assche };
4694*44704f69SBart Van Assche 
4695*44704f69SBart Van Assche #define ATA_IDENTIFY_BUFF_SZ  sizeof(struct ata_identify_device)
4696*44704f69SBart Van Assche #define HDIO_DRIVE_CMD_OFFSET 4
4697*44704f69SBart Van Assche 
4698*44704f69SBart Van Assche static int
ata_command_interface(int device,char * data,bool * atapi_flag,int verbose)4699*44704f69SBart Van Assche ata_command_interface(int device, char *data, bool * atapi_flag, int verbose)
4700*44704f69SBart Van Assche {
4701*44704f69SBart Van Assche     int err;
4702*44704f69SBart Van Assche     uint8_t buff[ATA_IDENTIFY_BUFF_SZ + HDIO_DRIVE_CMD_OFFSET];
4703*44704f69SBart Van Assche     unsigned short get_ident[256];
4704*44704f69SBart Van Assche 
4705*44704f69SBart Van Assche     if (atapi_flag)
4706*44704f69SBart Van Assche         *atapi_flag = false;
4707*44704f69SBart Van Assche     memset(buff, 0, sizeof(buff));
4708*44704f69SBart Van Assche     if (ioctl(device, HDIO_GET_IDENTITY, &get_ident) < 0) {
4709*44704f69SBart Van Assche         err = errno;
4710*44704f69SBart Van Assche         if (ENOTTY == err) {
4711*44704f69SBart Van Assche             if (verbose > 1)
4712*44704f69SBart Van Assche                 pr2serr("HDIO_GET_IDENTITY failed with ENOTTY, "
4713*44704f69SBart Van Assche                         "try HDIO_DRIVE_CMD ioctl ...\n");
4714*44704f69SBart Van Assche             buff[0] = ATA_IDENTIFY_DEVICE;
4715*44704f69SBart Van Assche             buff[3] = 1;
4716*44704f69SBart Van Assche             if (ioctl(device, HDIO_DRIVE_CMD, buff) < 0) {
4717*44704f69SBart Van Assche                 if (verbose)
4718*44704f69SBart Van Assche                     pr2serr("HDIO_DRIVE_CMD(ATA_IDENTIFY_DEVICE) "
4719*44704f69SBart Van Assche                             "ioctl failed:\n\t%s [%d]\n",
4720*44704f69SBart Van Assche                             safe_strerror(err), err);
4721*44704f69SBart Van Assche                 return sg_convert_errno(err);
4722*44704f69SBart Van Assche             }
4723*44704f69SBart Van Assche             memcpy(data, buff + HDIO_DRIVE_CMD_OFFSET, ATA_IDENTIFY_BUFF_SZ);
4724*44704f69SBart Van Assche             return 0;
4725*44704f69SBart Van Assche         } else {
4726*44704f69SBart Van Assche             if (verbose)
4727*44704f69SBart Van Assche                 pr2serr("HDIO_GET_IDENTITY ioctl failed:\n"
4728*44704f69SBart Van Assche                         "\t%s [%d]\n", safe_strerror(err), err);
4729*44704f69SBart Van Assche             return sg_convert_errno(err);
4730*44704f69SBart Van Assche         }
4731*44704f69SBart Van Assche     } else if (verbose > 1)
4732*44704f69SBart Van Assche         pr2serr("HDIO_GET_IDENTITY succeeded\n");
4733*44704f69SBart Van Assche     if (0x2 == ((get_ident[0] >> 14) &0x3)) {   /* ATAPI device */
4734*44704f69SBart Van Assche         if (verbose > 1)
4735*44704f69SBart Van Assche             pr2serr("assume ATAPI device from HDIO_GET_IDENTITY response\n");
4736*44704f69SBart Van Assche         memset(buff, 0, sizeof(buff));
4737*44704f69SBart Van Assche         buff[0] = ATA_IDENTIFY_PACKET_DEVICE;
4738*44704f69SBart Van Assche         buff[3] = 1;
4739*44704f69SBart Van Assche         if (ioctl(device, HDIO_DRIVE_CMD, buff) < 0) {
4740*44704f69SBart Van Assche             err = errno;
4741*44704f69SBart Van Assche             if (verbose)
4742*44704f69SBart Van Assche                 pr2serr("HDIO_DRIVE_CMD(ATA_IDENTIFY_PACKET_DEVICE) ioctl "
4743*44704f69SBart Van Assche                         "failed:\n\t%s [%d]\n", safe_strerror(err), err);
4744*44704f69SBart Van Assche             buff[0] = ATA_IDENTIFY_DEVICE;
4745*44704f69SBart Van Assche             buff[3] = 1;
4746*44704f69SBart Van Assche             if (ioctl(device, HDIO_DRIVE_CMD, buff) < 0) {
4747*44704f69SBart Van Assche                 err = errno;
4748*44704f69SBart Van Assche                 if (verbose)
4749*44704f69SBart Van Assche                     pr2serr("HDIO_DRIVE_CMD(ATA_IDENTIFY_DEVICE) ioctl "
4750*44704f69SBart Van Assche                             "failed:\n\t%s [%d]\n", safe_strerror(err), err);
4751*44704f69SBart Van Assche                 return sg_convert_errno(err);
4752*44704f69SBart Van Assche             }
4753*44704f69SBart Van Assche         } else if (atapi_flag) {
4754*44704f69SBart Van Assche             *atapi_flag = true;
4755*44704f69SBart Van Assche             if (verbose > 1)
4756*44704f69SBart Van Assche                 pr2serr("HDIO_DRIVE_CMD(ATA_IDENTIFY_DEVICE) succeeded\n");
4757*44704f69SBart Van Assche         }
4758*44704f69SBart Van Assche     } else {    /* assume non-packet device */
4759*44704f69SBart Van Assche         buff[0] = ATA_IDENTIFY_DEVICE;
4760*44704f69SBart Van Assche         buff[3] = 1;
4761*44704f69SBart Van Assche         if (ioctl(device, HDIO_DRIVE_CMD, buff) < 0) {
4762*44704f69SBart Van Assche             err = errno;
4763*44704f69SBart Van Assche             if (verbose)
4764*44704f69SBart Van Assche                 pr2serr("HDIO_DRIVE_CMD(ATA_IDENTIFY_DEVICE) ioctl failed:"
4765*44704f69SBart Van Assche                         "\n\t%s [%d]\n", safe_strerror(err), err);
4766*44704f69SBart Van Assche             return sg_convert_errno(err);
4767*44704f69SBart Van Assche         } else if (verbose > 1)
4768*44704f69SBart Van Assche             pr2serr("HDIO_DRIVE_CMD(ATA_IDENTIFY_DEVICE) succeeded\n");
4769*44704f69SBart Van Assche     }
4770*44704f69SBart Van Assche     /* if the command returns data, copy it back */
4771*44704f69SBart Van Assche     memcpy(data, buff + HDIO_DRIVE_CMD_OFFSET, ATA_IDENTIFY_BUFF_SZ);
4772*44704f69SBart Van Assche     return 0;
4773*44704f69SBart Van Assche }
4774*44704f69SBart Van Assche 
4775*44704f69SBart Van Assche static void
show_ata_identify(const struct ata_identify_device * aidp,bool atapi,int vb)4776*44704f69SBart Van Assche show_ata_identify(const struct ata_identify_device * aidp, bool atapi,
4777*44704f69SBart Van Assche                   int vb)
4778*44704f69SBart Van Assche {
4779*44704f69SBart Van Assche     int res;
4780*44704f69SBart Van Assche     char model[64];
4781*44704f69SBart Van Assche     char serial[64];
4782*44704f69SBart Van Assche     char firm[64];
4783*44704f69SBart Van Assche 
4784*44704f69SBart Van Assche     printf("%s device: model, serial number and firmware revision:\n",
4785*44704f69SBart Van Assche            (atapi ? "ATAPI" : "ATA"));
4786*44704f69SBart Van Assche     res = sg_ata_get_chars((const unsigned short *)aidp->model,
4787*44704f69SBart Van Assche                            0, 20, sg_is_big_endian(), model);
4788*44704f69SBart Van Assche     model[res] = '\0';
4789*44704f69SBart Van Assche     res = sg_ata_get_chars((const unsigned short *)aidp->serial_no,
4790*44704f69SBart Van Assche                            0, 10, sg_is_big_endian(), serial);
4791*44704f69SBart Van Assche     serial[res] = '\0';
4792*44704f69SBart Van Assche     res = sg_ata_get_chars((const unsigned short *)aidp->fw_rev,
4793*44704f69SBart Van Assche                            0, 4, sg_is_big_endian(), firm);
4794*44704f69SBart Van Assche     firm[res] = '\0';
4795*44704f69SBart Van Assche     printf("  %s %s %s\n", model, serial, firm);
4796*44704f69SBart Van Assche     if (vb) {
4797*44704f69SBart Van Assche         if (atapi)
4798*44704f69SBart Van Assche             printf("ATA IDENTIFY PACKET DEVICE response "
4799*44704f69SBart Van Assche                    "(256 words):\n");
4800*44704f69SBart Van Assche         else
4801*44704f69SBart Van Assche             printf("ATA IDENTIFY DEVICE response (256 words):\n");
4802*44704f69SBart Van Assche         dWordHex((const unsigned short *)aidp, 256, 0,
4803*44704f69SBart Van Assche                  sg_is_big_endian());
4804*44704f69SBart Van Assche     }
4805*44704f69SBart Van Assche }
4806*44704f69SBart Van Assche 
4807*44704f69SBart Van Assche static void
prepare_ata_identify(const struct opts_t * op,int inhex_len)4808*44704f69SBart Van Assche prepare_ata_identify(const struct opts_t * op, int inhex_len)
4809*44704f69SBart Van Assche {
4810*44704f69SBart Van Assche     int n = inhex_len;
4811*44704f69SBart Van Assche     struct ata_identify_device ata_ident;
4812*44704f69SBart Van Assche 
4813*44704f69SBart Van Assche     if (n < 16) {
4814*44704f69SBart Van Assche         pr2serr("%s: got only %d bytes, give up\n", __func__, n);
4815*44704f69SBart Van Assche         return;
4816*44704f69SBart Van Assche     } else if (n < 512)
4817*44704f69SBart Van Assche         pr2serr("%s: expect 512 bytes or more, got %d, continue\n", __func__,
4818*44704f69SBart Van Assche                 n);
4819*44704f69SBart Van Assche     else if (n > 512)
4820*44704f69SBart Van Assche         n = 512;
4821*44704f69SBart Van Assche     memset(&ata_ident, 0, sizeof(ata_ident));
4822*44704f69SBart Van Assche     memcpy(&ata_ident, rsp_buff, n);
4823*44704f69SBart Van Assche     show_ata_identify(&ata_ident, false, op->verbose);
4824*44704f69SBart Van Assche }
4825*44704f69SBart Van Assche 
4826*44704f69SBart Van Assche /* Returns 0 if successful, else errno of error */
4827*44704f69SBart Van Assche static int
try_ata_identify(int ata_fd,int do_hex,int do_raw,int verbose)4828*44704f69SBart Van Assche try_ata_identify(int ata_fd, int do_hex, int do_raw, int verbose)
4829*44704f69SBart Van Assche {
4830*44704f69SBart Van Assche     bool atapi;
4831*44704f69SBart Van Assche     int res;
4832*44704f69SBart Van Assche     struct ata_identify_device ata_ident;
4833*44704f69SBart Van Assche 
4834*44704f69SBart Van Assche     memset(&ata_ident, 0, sizeof(ata_ident));
4835*44704f69SBart Van Assche     res = ata_command_interface(ata_fd, (char *)&ata_ident, &atapi, verbose);
4836*44704f69SBart Van Assche     if (res)
4837*44704f69SBart Van Assche         return res;
4838*44704f69SBart Van Assche     if ((2 == do_raw) || (3 == do_hex))
4839*44704f69SBart Van Assche         dWordHex((const unsigned short *)&ata_ident, 256, -2,
4840*44704f69SBart Van Assche                  sg_is_big_endian());
4841*44704f69SBart Van Assche     else if (do_raw)
4842*44704f69SBart Van Assche         dStrRaw((const char *)&ata_ident, 512);
4843*44704f69SBart Van Assche     else {
4844*44704f69SBart Van Assche         if (do_hex) {
4845*44704f69SBart Van Assche             if (atapi)
4846*44704f69SBart Van Assche                 printf("ATA IDENTIFY PACKET DEVICE response ");
4847*44704f69SBart Van Assche             else
4848*44704f69SBart Van Assche                 printf("ATA IDENTIFY DEVICE response ");
4849*44704f69SBart Van Assche             if (do_hex > 1) {
4850*44704f69SBart Van Assche                 printf("(512 bytes):\n");
4851*44704f69SBart Van Assche                 hex2stdout((const uint8_t *)&ata_ident, 512, 0);
4852*44704f69SBart Van Assche             } else {
4853*44704f69SBart Van Assche                 printf("(256 words):\n");
4854*44704f69SBart Van Assche                 dWordHex((const unsigned short *)&ata_ident, 256, 0,
4855*44704f69SBart Van Assche                          sg_is_big_endian());
4856*44704f69SBart Van Assche             }
4857*44704f69SBart Van Assche         } else
4858*44704f69SBart Van Assche             show_ata_identify(&ata_ident, atapi, verbose);
4859*44704f69SBart Van Assche     }
4860*44704f69SBart Van Assche     return 0;
4861*44704f69SBart Van Assche }
4862*44704f69SBart Van Assche #endif
4863*44704f69SBart Van Assche 
4864*44704f69SBart Van Assche /* structure defined in sg_lib_data.h */
4865*44704f69SBart Van Assche extern struct sg_lib_simple_value_name_t sg_version_descriptor_arr[];
4866*44704f69SBart Van Assche 
4867*44704f69SBart Van Assche 
4868*44704f69SBart Van Assche static const char *
find_version_descriptor_str(int value)4869*44704f69SBart Van Assche find_version_descriptor_str(int value)
4870*44704f69SBart Van Assche {
4871*44704f69SBart Van Assche     int k;
4872*44704f69SBart Van Assche     const struct sg_lib_simple_value_name_t * vdp;
4873*44704f69SBart Van Assche 
4874*44704f69SBart Van Assche     for (k = 0; ((vdp = sg_version_descriptor_arr + k) && vdp->name); ++k) {
4875*44704f69SBart Van Assche         if (value == vdp->value)
4876*44704f69SBart Van Assche             return vdp->name;
4877*44704f69SBart Van Assche         if (value < vdp->value)
4878*44704f69SBart Van Assche             break;
4879*44704f69SBart Van Assche     }
4880*44704f69SBart Van Assche     return NULL;
4881*44704f69SBart Van Assche }
4882