1*44704f69SBart Van Assche /*
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 MODE SENSE command.
11*44704f69SBart Van Assche * Does 10 byte MODE SENSE commands by default, Trent Piepho added a "-6"
12*44704f69SBart Van Assche * switch for force 6 byte mode sense commands.
13*44704f69SBart Van Assche * This utility cannot modify mode pages. See the sdparm utility for that.
14*44704f69SBart Van Assche */
15*44704f69SBart Van Assche
16*44704f69SBart Van Assche #include <unistd.h>
17*44704f69SBart Van Assche #include <fcntl.h>
18*44704f69SBart Van Assche #include <stdio.h>
19*44704f69SBart Van Assche #include <stdlib.h>
20*44704f69SBart Van Assche #include <stdarg.h>
21*44704f69SBart Van Assche #include <stdbool.h>
22*44704f69SBart Van Assche #include <string.h>
23*44704f69SBart Van Assche #include <ctype.h>
24*44704f69SBart Van Assche #include <errno.h>
25*44704f69SBart Van Assche #include <getopt.h>
26*44704f69SBart Van Assche
27*44704f69SBart Van Assche #ifdef HAVE_CONFIG_H
28*44704f69SBart Van Assche #include "config.h"
29*44704f69SBart Van Assche #endif
30*44704f69SBart Van Assche #include "sg_lib.h"
31*44704f69SBart Van Assche #include "sg_cmds_basic.h"
32*44704f69SBart Van Assche #include "sg_unaligned.h"
33*44704f69SBart Van Assche #include "sg_pr2serr.h"
34*44704f69SBart Van Assche
35*44704f69SBart Van Assche static const char * version_str = "1.75 20220202";
36*44704f69SBart Van Assche
37*44704f69SBart Van Assche #define DEF_ALLOC_LEN (1024 * 4)
38*44704f69SBart Van Assche #define DEF_6_ALLOC_LEN 252
39*44704f69SBart Van Assche #define UNLIKELY_ABOVE_LEN 512
40*44704f69SBart Van Assche #define PG_CODE_ALL 0x3f
41*44704f69SBart Van Assche #define PG_CODE_MASK 0x3f
42*44704f69SBart Van Assche #define PG_CODE_MAX 0x3f
43*44704f69SBart Van Assche #define SPG_CODE_ALL 0xff
44*44704f69SBart Van Assche #define PROTO_SPECIFIC_1 0x18
45*44704f69SBart Van Assche #define PROTO_SPECIFIC_2 0x19
46*44704f69SBart Van Assche
47*44704f69SBart Van Assche #define EBUFF_SZ 256
48*44704f69SBart Van Assche
49*44704f69SBart Van Assche
50*44704f69SBart Van Assche struct opts_t {
51*44704f69SBart Van Assche bool do_dbd;
52*44704f69SBart Van Assche bool do_dbout;
53*44704f69SBart Van Assche bool do_examine;
54*44704f69SBart Van Assche bool do_flexible;
55*44704f69SBart Van Assche bool do_list;
56*44704f69SBart Van Assche bool do_llbaa;
57*44704f69SBart Van Assche bool do_six;
58*44704f69SBart Van Assche bool o_readwrite;
59*44704f69SBart Van Assche bool subpg_code_given;
60*44704f69SBart Van Assche bool opt_new;
61*44704f69SBart Van Assche bool verbose_given;
62*44704f69SBart Van Assche bool version_given;
63*44704f69SBart Van Assche int do_all;
64*44704f69SBart Van Assche int do_help;
65*44704f69SBart Van Assche int do_hex;
66*44704f69SBart Van Assche int maxlen;
67*44704f69SBart Van Assche int do_raw;
68*44704f69SBart Van Assche int verbose;
69*44704f69SBart Van Assche int page_control;
70*44704f69SBart Van Assche int pg_code;
71*44704f69SBart Van Assche int subpg_code;
72*44704f69SBart Van Assche const char * device_name;
73*44704f69SBart Van Assche const char * page_acron;
74*44704f69SBart Van Assche };
75*44704f69SBart Van Assche
76*44704f69SBart Van Assche struct page_code_desc {
77*44704f69SBart Van Assche int page_code;
78*44704f69SBart Van Assche int subpage_code;
79*44704f69SBart Van Assche const char * acron;
80*44704f69SBart Van Assche const char * desc;
81*44704f69SBart Van Assche };
82*44704f69SBart Van Assche
83*44704f69SBart Van Assche struct pc_desc_group {
84*44704f69SBart Van Assche struct page_code_desc * pcdp;
85*44704f69SBart Van Assche const char * group_name;
86*44704f69SBart Van Assche };
87*44704f69SBart Van Assche
88*44704f69SBart Van Assche static struct option long_options[] = {
89*44704f69SBart Van Assche {"all", no_argument, 0, 'a'},
90*44704f69SBart Van Assche {"control", required_argument, 0, 'c'},
91*44704f69SBart Van Assche {"dbd", no_argument, 0, 'd'},
92*44704f69SBart Van Assche {"dbout", no_argument, 0, 'D'},
93*44704f69SBart Van Assche {"examine", no_argument, 0, 'e'},
94*44704f69SBart Van Assche {"flexible", no_argument, 0, 'f'},
95*44704f69SBart Van Assche {"help", no_argument, 0, 'h'},
96*44704f69SBart Van Assche {"hex", no_argument, 0, 'H'},
97*44704f69SBart Van Assche {"list", no_argument, 0, 'l'},
98*44704f69SBart Van Assche {"llbaa", no_argument, 0, 'L'},
99*44704f69SBart Van Assche {"maxlen", required_argument, 0, 'm'},
100*44704f69SBart Van Assche {"new", no_argument, 0, 'N'},
101*44704f69SBart Van Assche {"old", no_argument, 0, 'O'},
102*44704f69SBart Van Assche {"page", required_argument, 0, 'p'},
103*44704f69SBart Van Assche {"raw", no_argument, 0, 'r'},
104*44704f69SBart Van Assche {"read-write", no_argument, 0, 'w'},
105*44704f69SBart Van Assche {"read_write", no_argument, 0, 'w'},
106*44704f69SBart Van Assche {"readwrite", no_argument, 0, 'w'},
107*44704f69SBart Van Assche {"six", no_argument, 0, '6'},
108*44704f69SBart Van Assche {"verbose", no_argument, 0, 'v'},
109*44704f69SBart Van Assche {"version", no_argument, 0, 'V'},
110*44704f69SBart Van Assche {0, 0, 0, 0},
111*44704f69SBart Van Assche };
112*44704f69SBart Van Assche
113*44704f69SBart Van Assche /* Common to all SCSI devices (found in SPCx). In numerical order */
114*44704f69SBart Van Assche static struct page_code_desc pc_desc_common[] = {
115*44704f69SBart Van Assche {0x0, 0x0, "ua", "Unit Attention condition [vendor specific format]"},
116*44704f69SBart Van Assche {0x2, 0x0, "dr", "Disconnect-Reconnect"},
117*44704f69SBart Van Assche {0x9, 0x0, "pd", "Peripheral device (obsolete)"},
118*44704f69SBart Van Assche {0xa, 0x0, "co", "Control"},
119*44704f69SBart Van Assche {0xa, 0x1, "coe", "Control extension"},
120*44704f69SBart Van Assche {0xa, 0x3, "cdla", "Command duration limit A"},
121*44704f69SBart Van Assche {0xa, 0x4, "cdlb", "Command duration limit B"},
122*44704f69SBart Van Assche {0xa, 0x7, "cdt2a", "Command duration limit T2A"}, /* spc6r01 */
123*44704f69SBart Van Assche {0xa, 0x8, "cdt2b", "Command duration limit T2B"}, /* spc6r01 */
124*44704f69SBart Van Assche {0x15, 0x0, "ext_", "Extended"},
125*44704f69SBart Van Assche {0x16, 0x0, "edts", "Extended device-type specific"},
126*44704f69SBart Van Assche {0x18, 0x0, "pslu", "Protocol specific lu"},
127*44704f69SBart Van Assche {0x19, 0x0, "pspo", "Protocol specific port"},
128*44704f69SBart Van Assche {0x1a, 0x0, "po", "Power condition"},
129*44704f69SBart Van Assche {0x1a, 0x1, "ps", "Power consumption"},
130*44704f69SBart Van Assche {0x1c, 0x0, "ie", "Informational exceptions control"},
131*44704f69SBart Van Assche {PG_CODE_ALL, 0x0, "asmp", "[yields all supported pages]"},
132*44704f69SBart Van Assche {PG_CODE_ALL, SPG_CODE_ALL,"asmsp",
133*44704f69SBart Van Assche "[yields all supported pages and subpages]"},
134*44704f69SBart Van Assche {0x0, 0x0, NULL, NULL},
135*44704f69SBart Van Assche };
136*44704f69SBart Van Assche
137*44704f69SBart Van Assche static struct page_code_desc pc_desc_disk[] = {
138*44704f69SBart Van Assche {0x1, 0x0, "rw", "Read-Write error recovery"},
139*44704f69SBart Van Assche {0x3, 0x0, "fo", "Format (obsolete)"},
140*44704f69SBart Van Assche {0x4, 0x0, "rd", "Rigid disk geometry (obsolete)"},
141*44704f69SBart Van Assche {0x5, 0x0, "fd", "Flexible disk (obsolete)"},
142*44704f69SBart Van Assche {0x7, 0x0, "ve", "Verify error recovery"},
143*44704f69SBart Van Assche {0x8, 0x0, "ca", "Caching"},
144*44704f69SBart Van Assche {0xa, 0x2, "atag", "Application tag"},
145*44704f69SBart Van Assche {0xa, 0x5, "ioad", "IO advice hints grouping"}, /* added sbc4r06 */
146*44704f69SBart Van Assche {0xa, 0x6, "bop", "Background operation control"}, /* added sbc4r07 */
147*44704f69SBart Van Assche {0xa, 0xf1, "pat", "Parallel ATA control (SAT)"},
148*44704f69SBart Van Assche {0xa, 0xf2, "afc", "ATA feature control (SAT)"}, /* added 20-085r2 */
149*44704f69SBart Van Assche {0xb, 0x0, "mts", "Medium types supported (obsolete)"},
150*44704f69SBart Van Assche {0xc, 0x0, "not", "Notch and partition (obsolete)"},
151*44704f69SBart Van Assche {0xd, 0x0, "pco", "Power condition (obsolete, moved to 0x1a)"},
152*44704f69SBart Van Assche {0x10, 0x0, "xo", "XOR control"}, /* obsolete in sbc3r32 */
153*44704f69SBart Van Assche {0x1a, 0xf1, "apo", "ATA Power condition"},
154*44704f69SBart Van Assche {0x1c, 0x1, "bc", "Background control"},
155*44704f69SBart Van Assche {0x1c, 0x2, "lbp", "Logical block provisioning"},
156*44704f69SBart Van Assche {0x0, 0x0, NULL, NULL},
157*44704f69SBart Van Assche };
158*44704f69SBart Van Assche
159*44704f69SBart Van Assche static struct page_code_desc pc_desc_tape[] = {
160*44704f69SBart Van Assche {0x1, 0x0, "rw", "Read-Write error recovery"},
161*44704f69SBart Van Assche {0xa, 0xf0, "cdp", "Control data protection"},
162*44704f69SBart Van Assche {0xf, 0x0, "dac", "Data Compression"},
163*44704f69SBart Van Assche {0x10, 0x0, "dc", "Device configuration"},
164*44704f69SBart Van Assche {0x10, 0x1, "dcs", "Device configuration extension"},
165*44704f69SBart Van Assche {0x11, 0x0, "mpa", "Medium Partition [1]"},
166*44704f69SBart Van Assche {0x12, 0x0, "mpa2", "Medium Partition [2]"},
167*44704f69SBart Van Assche {0x13, 0x0, "mpa3", "Medium Partition [3]"},
168*44704f69SBart Van Assche {0x14, 0x0, "mpar", "Medium Partition [4]"},
169*44704f69SBart Van Assche {0x1c, 0x0, "ie", "Informational exceptions control (tape version)"},
170*44704f69SBart Van Assche {0x1d, 0x0, "mco", "Medium configuration"},
171*44704f69SBart Van Assche {0x0, 0x0, NULL, NULL},
172*44704f69SBart Van Assche };
173*44704f69SBart Van Assche
174*44704f69SBart Van Assche static struct page_code_desc pc_desc_cddvd[] = {
175*44704f69SBart Van Assche {0x1, 0x0, "rw", "Read-Write error recovery"},
176*44704f69SBart Van Assche {0x3, 0x0, "mrw", "Mount Rainer rewritable"},
177*44704f69SBart Van Assche {0x5, 0x0, "wp", "Write parameters"},
178*44704f69SBart Van Assche {0x7, 0x0, "ve", "Verify error recovery"},
179*44704f69SBart Van Assche {0x8, 0x0, "ca", "Caching"},
180*44704f69SBart Van Assche {0xd, 0x0, "cddp", "CD device parameters (obsolete)"},
181*44704f69SBart Van Assche {0xe, 0x0, "cda", "CD audio"},
182*44704f69SBart Van Assche {0x1a, 0x0, "po", "Power condition (mmc)"},
183*44704f69SBart Van Assche {0x1c, 0x0, "ffrc", "Fault/failure reporting control (mmc)"},
184*44704f69SBart Van Assche {0x1d, 0x0, "tp", "Timeout and protect"},
185*44704f69SBart Van Assche {0x2a, 0x0, "cms", "MM capabilities and mechanical status (obsolete)"},
186*44704f69SBart Van Assche {0x0, 0x0, NULL, NULL},
187*44704f69SBart Van Assche };
188*44704f69SBart Van Assche
189*44704f69SBart Van Assche static struct page_code_desc pc_desc_smc[] = {
190*44704f69SBart Van Assche {0x1d, 0x0, "eaa", "Element address assignment"},
191*44704f69SBart Van Assche {0x1e, 0x0, "tgp", "Transport geometry parameters"},
192*44704f69SBart Van Assche {0x1f, 0x0, "dcs", "Device capabilities"},
193*44704f69SBart Van Assche {0x1f, 0x41, "edc", "Extended device capabilities"},
194*44704f69SBart Van Assche {0x0, 0x0, NULL, NULL},
195*44704f69SBart Van Assche };
196*44704f69SBart Van Assche
197*44704f69SBart Van Assche static struct page_code_desc pc_desc_scc[] = {
198*44704f69SBart Van Assche {0x1b, 0x0, "sslm", "LUN mapping"},
199*44704f69SBart Van Assche {0x0, 0x0, NULL, NULL},
200*44704f69SBart Van Assche };
201*44704f69SBart Van Assche
202*44704f69SBart Van Assche static struct page_code_desc pc_desc_ses[] = {
203*44704f69SBart Van Assche {0x14, 0x0, "esm", "Enclosure services management"},
204*44704f69SBart Van Assche {0x0, 0x0, NULL, NULL},
205*44704f69SBart Van Assche };
206*44704f69SBart Van Assche
207*44704f69SBart Van Assche static struct page_code_desc pc_desc_rbc[] = {
208*44704f69SBart Van Assche {0x6, 0x0, "rbc", "RBC device parameters"},
209*44704f69SBart Van Assche {0x0, 0x0, NULL, NULL},
210*44704f69SBart Van Assche };
211*44704f69SBart Van Assche
212*44704f69SBart Van Assche static struct page_code_desc pc_desc_adc[] = {
213*44704f69SBart Van Assche /* {0xe, 0x0, "ADC device configuration"}, */
214*44704f69SBart Van Assche {0xe, 0x1, "adtd", "Target device"},
215*44704f69SBart Van Assche {0xe, 0x2, "addp", "DT device primary port"},
216*44704f69SBart Van Assche {0xe, 0x3, "adlu", "Logical unit"},
217*44704f69SBart Van Assche {0xe, 0x4, "adts", "Target device serial number"},
218*44704f69SBart Van Assche {0x0, 0x0, NULL, NULL},
219*44704f69SBart Van Assche };
220*44704f69SBart Van Assche
221*44704f69SBart Van Assche
222*44704f69SBart Van Assche /* Transport reated mode pages */
223*44704f69SBart Van Assche static struct page_code_desc pc_desc_t_fcp[] = {
224*44704f69SBart Van Assche {0x18, 0x0, "pl", "LU control"},
225*44704f69SBart Van Assche {0x19, 0x0, "pp", "Port control"},
226*44704f69SBart Van Assche {0x0, 0x0, NULL, NULL},
227*44704f69SBart Van Assche };
228*44704f69SBart Van Assche
229*44704f69SBart Van Assche static struct page_code_desc pc_desc_t_spi4[] = {
230*44704f69SBart Van Assche {0x18, 0x0, "luc", "LU control"},
231*44704f69SBart Van Assche {0x19, 0x0, "pp", "Port control short format"},
232*44704f69SBart Van Assche {0x19, 0x1, "mc", "Margin control"},
233*44704f69SBart Van Assche {0x19, 0x2, "stc", "Saved training configuration value"},
234*44704f69SBart Van Assche {0x19, 0x3, "ns", "Negotiated settings"},
235*44704f69SBart Van Assche {0x19, 0x4, "rtc", "Report transfer capabilities"},
236*44704f69SBart Van Assche {0x0, 0x0, NULL, NULL},
237*44704f69SBart Van Assche };
238*44704f69SBart Van Assche
239*44704f69SBart Van Assche /* SAS protocol layers now in SPL standards */
240*44704f69SBart Van Assche static struct page_code_desc pc_desc_t_sas[] = {
241*44704f69SBart Van Assche {0x18, 0x0, "pslu", "Protocol specific logical unit (SPL)"},
242*44704f69SBart Van Assche {0x19, 0x0, "pspo", "Protocol specific port (SPL)"},
243*44704f69SBart Van Assche {0x19, 0x1, "pcd", "Phy control and discover (SPL)"},
244*44704f69SBart Van Assche {0x19, 0x2, "spc", "Shared port control (SPL)"},
245*44704f69SBart Van Assche {0x19, 0x3, "sep", "Enhanced phy control (SPL)"},
246*44704f69SBart Van Assche {0x19, 0x4, "oobm", "Out of band management control (SPL)"}, /* spl5r01 */
247*44704f69SBart Van Assche {0x0, 0x0, NULL, NULL},
248*44704f69SBart Van Assche };
249*44704f69SBart Van Assche
250*44704f69SBart Van Assche static struct page_code_desc pc_desc_t_adc[] = {
251*44704f69SBart Van Assche {0xe, 0x1, "addt", "Target device"},
252*44704f69SBart Van Assche {0xe, 0x2, "addp", "DT device primary port"},
253*44704f69SBart Van Assche {0xe, 0x3, "adlu", "Logical unit"},
254*44704f69SBart Van Assche {0x18, 0x0, "pslu", "Protocol specific lu"},
255*44704f69SBart Van Assche {0x19, 0x0, "pspo", "Protocol specific port"},
256*44704f69SBart Van Assche {0x0, 0x0, NULL, NULL},
257*44704f69SBart Van Assche };
258*44704f69SBart Van Assche
259*44704f69SBart Van Assche static struct page_code_desc pc_desc_zbc[] = {
260*44704f69SBart Van Assche {0x1, 0x0, "rw", "Read-Write error recovery"},
261*44704f69SBart Van Assche {0x7, 0x0, "ve", "Verify error recovery"},
262*44704f69SBart Van Assche {0x8, 0x0, "ca", "Caching"},
263*44704f69SBart Van Assche {0xa, 0x2, "atag", "Application tag"},
264*44704f69SBart Van Assche {0xa, 0xf, "zbdct", "Zoned block device control"}, /* zbc2r04a */
265*44704f69SBart Van Assche {0x1c, 0x1, "bc", "Background control"},
266*44704f69SBart Van Assche {0x0, 0x0, NULL, NULL},
267*44704f69SBart Van Assche };
268*44704f69SBart Van Assche
269*44704f69SBart Van Assche struct pc_desc_group pcd_gr_arr[] = {
270*44704f69SBart Van Assche {pc_desc_common, "common"},
271*44704f69SBart Van Assche {pc_desc_disk, "disk"},
272*44704f69SBart Van Assche {pc_desc_tape, "tape"},
273*44704f69SBart Van Assche {pc_desc_cddvd, "cd/dvd"},
274*44704f69SBart Van Assche {pc_desc_smc, "media changer"},
275*44704f69SBart Van Assche {pc_desc_scc, "scsi controller"},
276*44704f69SBart Van Assche {pc_desc_ses, "enclosure"},
277*44704f69SBart Van Assche {pc_desc_rbc, "reduced block"},
278*44704f69SBart Van Assche {pc_desc_adc, "adc"},
279*44704f69SBart Van Assche {pc_desc_zbc, "zbc"},
280*44704f69SBart Van Assche {pc_desc_t_fcp, "transport: FCP"},
281*44704f69SBart Van Assche {pc_desc_t_spi4, "transport: SPI"},
282*44704f69SBart Van Assche {pc_desc_t_sas, "transport: SAS"},
283*44704f69SBart Van Assche {pc_desc_t_adc, "transport: ADC"},
284*44704f69SBart Van Assche
285*44704f69SBart Van Assche {NULL, NULL},
286*44704f69SBart Van Assche };
287*44704f69SBart Van Assche
288*44704f69SBart Van Assche
289*44704f69SBart Van Assche
290*44704f69SBart Van Assche static void
usage()291*44704f69SBart Van Assche usage()
292*44704f69SBart Van Assche {
293*44704f69SBart Van Assche printf("Usage: sg_modes [--all] [--control=PC] [--dbd] [--dbout] "
294*44704f69SBart Van Assche "[--examine]\n"
295*44704f69SBart Van Assche " [--flexible] [--help] [--hex] [--list] "
296*44704f69SBart Van Assche "[--llbaa]\n"
297*44704f69SBart Van Assche " [--maxlen=LEN] [--page=PG[,SPG]] [--raw] [-R] "
298*44704f69SBart Van Assche "[--readwrite]\n"
299*44704f69SBart Van Assche " [--six] [--verbose] [--version] [DEVICE]\n"
300*44704f69SBart Van Assche " where:\n"
301*44704f69SBart Van Assche " --all|-a get all mode pages supported by device\n"
302*44704f69SBart Van Assche " use twice to get all mode pages and subpages\n"
303*44704f69SBart Van Assche " --control=PC|-c PC page control (default: 0)\n"
304*44704f69SBart Van Assche " 0: current, 1: changeable,\n"
305*44704f69SBart Van Assche " 2: (manufacturer's) defaults, 3: saved\n"
306*44704f69SBart Van Assche " --dbd|-d disable block descriptors (DBD field in cdb)\n"
307*44704f69SBart Van Assche " --dbout|-D disable block descriptor output\n"
308*44704f69SBart Van Assche " --examine|-e examine pages # 0 through to 0x3e, note if "
309*44704f69SBart Van Assche "found\n"
310*44704f69SBart Van Assche " --flexible|-f be flexible, cope with MODE SENSE 6/10 "
311*44704f69SBart Van Assche "response mixup\n");
312*44704f69SBart Van Assche printf(" --help|-h print usage message then exit\n"
313*44704f69SBart Van Assche " --hex|-H output full response in hex\n"
314*44704f69SBart Van Assche " use twice to output page number and header "
315*44704f69SBart Van Assche "in hex\n"
316*44704f69SBart Van Assche " --list|-l list common page codes for device peripheral "
317*44704f69SBart Van Assche "type,\n"
318*44704f69SBart Van Assche " if no device given then assume disk type\n"
319*44704f69SBart Van Assche " --llbaa|-L set Long LBA Accepted (LLBAA field in mode "
320*44704f69SBart Van Assche "sense (10) cdb)\n"
321*44704f69SBart Van Assche " --maxlen=LEN|-m LEN max response length (allocation "
322*44704f69SBart Van Assche "length in cdb)\n"
323*44704f69SBart Van Assche " (def: 0 -> 4096 or 252 (for MODE "
324*44704f69SBart Van Assche "SENSE 6) bytes)\n"
325*44704f69SBart Van Assche " --page=PG|-p PG page code to fetch (def: 63). May be "
326*44704f69SBart Van Assche "acronym\n"
327*44704f69SBart Van Assche " --page=PG,SPG|-p PG,SPG\n"
328*44704f69SBart Van Assche " page code and subpage code to fetch "
329*44704f69SBart Van Assche "(defs: 63,0)\n"
330*44704f69SBart Van Assche " --raw|-r output response in binary to stdout\n"
331*44704f69SBart Van Assche " -R mode page response to stdout, a byte per "
332*44704f69SBart Van Assche "line in ASCII\n"
333*44704f69SBart Van Assche " hex (same result as '--raw --raw')\n"
334*44704f69SBart Van Assche " --readwrite|-w open DEVICE read-write (def: open "
335*44704f69SBart Van Assche "read-only)\n"
336*44704f69SBart Van Assche " --six|-6|-s use MODE SENSE(6), by default uses MODE "
337*44704f69SBart Van Assche "SENSE(10)\n"
338*44704f69SBart Van Assche " --verbose|-v increase verbosity\n"
339*44704f69SBart Van Assche " --old|-O use old interface (use as first option)\n"
340*44704f69SBart Van Assche " --version|-V output version string then exit\n\n"
341*44704f69SBart Van Assche "Performs a SCSI MODE SENSE (10 or 6) command. To access and "
342*44704f69SBart Van Assche "possibly change\nmode page fields see the sdparm utility.\n");
343*44704f69SBart Van Assche }
344*44704f69SBart Van Assche
345*44704f69SBart Van Assche static void
usage_old()346*44704f69SBart Van Assche usage_old()
347*44704f69SBart Van Assche {
348*44704f69SBart Van Assche printf("Usage: sg_modes [-a] [-A] [-c=PC] [-d] [-D] [-e] [-f] [-h] "
349*44704f69SBart Van Assche "[-H] [-l] [-L]\n"
350*44704f69SBart Van Assche " [-m=LEN] [-p=PG[,SPG]] [-r] [-subp=SPG] [-v] "
351*44704f69SBart Van Assche "[-V] [-6]\n"
352*44704f69SBart Van Assche " [DEVICE]\n"
353*44704f69SBart Van Assche " where:\n"
354*44704f69SBart Van Assche " -a get all mode pages supported by device\n"
355*44704f69SBart Van Assche " -A get all mode pages and subpages supported by device\n"
356*44704f69SBart Van Assche " -c=PC page control (def: 0 [current],"
357*44704f69SBart Van Assche " 1 [changeable],\n"
358*44704f69SBart Van Assche " 2 [default], 3 [saved])\n"
359*44704f69SBart Van Assche " -d disable block descriptors (DBD field in cdb)\n"
360*44704f69SBart Van Assche " -D disable block descriptor output\n"
361*44704f69SBart Van Assche " -e examine pages # 0 through to 0x3e, note if found\n"
362*44704f69SBart Van Assche " -f be flexible, cope with MODE SENSE 6/10 response "
363*44704f69SBart Van Assche "mixup\n");
364*44704f69SBart Van Assche printf(" -h output page number and header in hex\n"
365*44704f69SBart Van Assche " -H output page number and header in hex (same as '-h')\n"
366*44704f69SBart Van Assche " -l list common page codes for device peripheral type,\n"
367*44704f69SBart Van Assche " if no device given then assume disk type\n"
368*44704f69SBart Van Assche " -L set Long LBA Accepted (LLBAA field in mode sense "
369*44704f69SBart Van Assche "10 cdb)\n"
370*44704f69SBart Van Assche " -m=LEN max response length (allocation length in cdb)\n"
371*44704f69SBart Van Assche " (def: 0 -> 4096 or 252 (for MODE SENSE 6) bytes)\n"
372*44704f69SBart Van Assche " -p=PG page code in hex (def: 3f). No acronym allowed\n"
373*44704f69SBart Van Assche " -p=PG,SPG both in hex, (defs: 3f,0)\n"
374*44704f69SBart Van Assche " -r mode page output to stdout, a byte per line in "
375*44704f69SBart Van Assche "ASCII hex\n"
376*44704f69SBart Van Assche " -subp=SPG sub page code in hex (def: 0)\n"
377*44704f69SBart Van Assche " -v verbose\n"
378*44704f69SBart Van Assche " -V output version string\n"
379*44704f69SBart Van Assche " -6 Use MODE SENSE(6), by default uses MODE SENSE(10)\n"
380*44704f69SBart Van Assche " -N|--new use new interface\n"
381*44704f69SBart Van Assche " -? output this usage message\n\n"
382*44704f69SBart Van Assche "Performs a SCSI MODE SENSE (10 or 6) command\n");
383*44704f69SBart Van Assche }
384*44704f69SBart Van Assche
385*44704f69SBart Van Assche static void
enum_pc_desc(void)386*44704f69SBart Van Assche enum_pc_desc(void)
387*44704f69SBart Van Assche {
388*44704f69SBart Van Assche bool first = true;
389*44704f69SBart Van Assche const struct pc_desc_group * pcd_grp = pcd_gr_arr;
390*44704f69SBart Van Assche char b[128];
391*44704f69SBart Van Assche
392*44704f69SBart Van Assche for ( ; pcd_grp->pcdp; ++pcd_grp) {
393*44704f69SBart Van Assche const struct page_code_desc * pcdp = pcd_grp->pcdp;
394*44704f69SBart Van Assche
395*44704f69SBart Van Assche if (first)
396*44704f69SBart Van Assche first = false;
397*44704f69SBart Van Assche else
398*44704f69SBart Van Assche printf("\n");
399*44704f69SBart Van Assche printf("Mode pages group: %s:\n", pcd_grp->group_name);
400*44704f69SBart Van Assche for ( ; pcdp->acron; ++pcdp) {
401*44704f69SBart Van Assche if (pcdp->subpage_code > 0)
402*44704f69SBart Van Assche snprintf(b, sizeof(b), "[0x%x,0x%x]", pcdp->page_code,
403*44704f69SBart Van Assche pcdp->subpage_code);
404*44704f69SBart Van Assche else
405*44704f69SBart Van Assche snprintf(b, sizeof(b), "[0x%x]", pcdp->page_code);
406*44704f69SBart Van Assche printf(" %s: %s %s\n", pcdp->acron, pcdp->desc, b);
407*44704f69SBart Van Assche }
408*44704f69SBart Van Assche }
409*44704f69SBart Van Assche }
410*44704f69SBart Van Assche
411*44704f69SBart Van Assche static const struct page_code_desc *
find_pc_desc(const char * acron)412*44704f69SBart Van Assche find_pc_desc(const char * acron)
413*44704f69SBart Van Assche {
414*44704f69SBart Van Assche const struct pc_desc_group * pcd_grp = pcd_gr_arr;
415*44704f69SBart Van Assche
416*44704f69SBart Van Assche for ( ; pcd_grp->pcdp; ++pcd_grp) {
417*44704f69SBart Van Assche const struct page_code_desc * pcdp = pcd_grp->pcdp;
418*44704f69SBart Van Assche
419*44704f69SBart Van Assche for ( ; pcdp->acron; ++pcdp) {
420*44704f69SBart Van Assche if (0 == strcmp(acron, pcdp->acron))
421*44704f69SBart Van Assche return pcdp;
422*44704f69SBart Van Assche }
423*44704f69SBart Van Assche }
424*44704f69SBart Van Assche return NULL;
425*44704f69SBart Van Assche }
426*44704f69SBart Van Assche
427*44704f69SBart Van Assche static void
usage_for(const struct opts_t * op)428*44704f69SBart Van Assche usage_for(const struct opts_t * op)
429*44704f69SBart Van Assche {
430*44704f69SBart Van Assche if (op->opt_new)
431*44704f69SBart Van Assche usage();
432*44704f69SBart Van Assche else
433*44704f69SBart Van Assche usage_old();
434*44704f69SBart Van Assche }
435*44704f69SBart Van Assche
436*44704f69SBart Van Assche /* Processes command line options according to new option format. Returns
437*44704f69SBart Van Assche * 0 is ok, else SG_LIB_SYNTAX_ERROR is returned. */
438*44704f69SBart Van Assche static int
new_parse_cmd_line(struct opts_t * op,int argc,char * argv[])439*44704f69SBart Van Assche new_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
440*44704f69SBart Van Assche {
441*44704f69SBart Van Assche int c, n, nn;
442*44704f69SBart Van Assche char * cp;
443*44704f69SBart Van Assche
444*44704f69SBart Van Assche while (1) {
445*44704f69SBart Van Assche int option_index = 0;
446*44704f69SBart Van Assche
447*44704f69SBart Van Assche c = getopt_long(argc, argv, "6aAc:dDefhHlLm:NOp:rRsvV", long_options,
448*44704f69SBart Van Assche &option_index);
449*44704f69SBart Van Assche if (c == -1)
450*44704f69SBart Van Assche break;
451*44704f69SBart Van Assche
452*44704f69SBart Van Assche switch (c) {
453*44704f69SBart Van Assche case '6':
454*44704f69SBart Van Assche op->do_six = true;
455*44704f69SBart Van Assche break;
456*44704f69SBart Van Assche case 'a':
457*44704f69SBart Van Assche ++op->do_all;
458*44704f69SBart Van Assche break;
459*44704f69SBart Van Assche case 'A':
460*44704f69SBart Van Assche op->do_all += 2;
461*44704f69SBart Van Assche break;
462*44704f69SBart Van Assche case 'c':
463*44704f69SBart Van Assche n = sg_get_num(optarg);
464*44704f69SBart Van Assche if ((n < 0) || (n > 3)) {
465*44704f69SBart Van Assche pr2serr("bad argument to '--control='\n");
466*44704f69SBart Van Assche usage();
467*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
468*44704f69SBart Van Assche }
469*44704f69SBart Van Assche op->page_control = n;
470*44704f69SBart Van Assche break;
471*44704f69SBart Van Assche case 'd':
472*44704f69SBart Van Assche op->do_dbd = true;
473*44704f69SBart Van Assche break;
474*44704f69SBart Van Assche case 'D':
475*44704f69SBart Van Assche op->do_dbout = true;
476*44704f69SBart Van Assche break;
477*44704f69SBart Van Assche case 'e':
478*44704f69SBart Van Assche op->do_examine = true;
479*44704f69SBart Van Assche break;
480*44704f69SBart Van Assche case 'f':
481*44704f69SBart Van Assche op->do_flexible = true;
482*44704f69SBart Van Assche break;
483*44704f69SBart Van Assche case 'h':
484*44704f69SBart Van Assche case '?':
485*44704f69SBart Van Assche ++op->do_help;
486*44704f69SBart Van Assche break;
487*44704f69SBart Van Assche case 'H':
488*44704f69SBart Van Assche ++op->do_hex;
489*44704f69SBart Van Assche break;
490*44704f69SBart Van Assche case 'l':
491*44704f69SBart Van Assche op->do_list = true;
492*44704f69SBart Van Assche break;
493*44704f69SBart Van Assche case 'L':
494*44704f69SBart Van Assche op->do_llbaa = true;
495*44704f69SBart Van Assche break;
496*44704f69SBart Van Assche case 'm':
497*44704f69SBart Van Assche n = sg_get_num(optarg);
498*44704f69SBart Van Assche if ((n < 0) || (n > 65535)) {
499*44704f69SBart Van Assche pr2serr("bad argument to '--maxlen='\n");
500*44704f69SBart Van Assche usage();
501*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
502*44704f69SBart Van Assche }
503*44704f69SBart Van Assche if ((n > 0) && (n < 4)) {
504*44704f69SBart Van Assche pr2serr("Changing that '--maxlen=' value to 4\n");
505*44704f69SBart Van Assche n = 4;
506*44704f69SBart Van Assche }
507*44704f69SBart Van Assche op->maxlen = n;
508*44704f69SBart Van Assche break;
509*44704f69SBart Van Assche case 'N':
510*44704f69SBart Van Assche break; /* ignore */
511*44704f69SBart Van Assche case 'O':
512*44704f69SBart Van Assche op->opt_new = false;
513*44704f69SBart Van Assche return 0;
514*44704f69SBart Van Assche case 'p':
515*44704f69SBart Van Assche if (isalpha((uint8_t)optarg[0])) {
516*44704f69SBart Van Assche const struct page_code_desc * pcdp;
517*44704f69SBart Van Assche
518*44704f69SBart Van Assche op->page_acron = optarg;
519*44704f69SBart Van Assche if (0 == memcmp("xxx", optarg, 3)) {
520*44704f69SBart Van Assche enum_pc_desc();
521*44704f69SBart Van Assche return SG_LIB_OK_FALSE; /* for quick exit */
522*44704f69SBart Van Assche }
523*44704f69SBart Van Assche pcdp = find_pc_desc(optarg);
524*44704f69SBart Van Assche if (pcdp) {
525*44704f69SBart Van Assche if (pcdp->subpage_code > 0) {
526*44704f69SBart Van Assche op->subpg_code = pcdp->subpage_code;
527*44704f69SBart Van Assche op->subpg_code_given = true;
528*44704f69SBart Van Assche }
529*44704f69SBart Van Assche op->pg_code = pcdp->page_code;
530*44704f69SBart Van Assche } else {
531*44704f69SBart Van Assche pr2serr(" Couldn't match acronym '%s', try '-p xxx' for "
532*44704f69SBart Van Assche "list\n", optarg);
533*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
534*44704f69SBart Van Assche }
535*44704f69SBart Van Assche } else {
536*44704f69SBart Van Assche cp = strchr(optarg, ',');
537*44704f69SBart Van Assche n = sg_get_num_nomult(optarg);
538*44704f69SBart Van Assche if ((n < 0) || (n > 63)) {
539*44704f69SBart Van Assche pr2serr("Bad argument to '--page='\n");
540*44704f69SBart Van Assche usage();
541*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
542*44704f69SBart Van Assche }
543*44704f69SBart Van Assche if (cp) {
544*44704f69SBart Van Assche nn = sg_get_num_nomult(cp + 1);
545*44704f69SBart Van Assche if ((nn < 0) || (nn > 255)) {
546*44704f69SBart Van Assche pr2serr("Bad second value in argument to "
547*44704f69SBart Van Assche "'--page='\n");
548*44704f69SBart Van Assche usage();
549*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
550*44704f69SBart Van Assche }
551*44704f69SBart Van Assche op->subpg_code = nn;
552*44704f69SBart Van Assche op->subpg_code_given = true;
553*44704f69SBart Van Assche }
554*44704f69SBart Van Assche op->pg_code = n;
555*44704f69SBart Van Assche }
556*44704f69SBart Van Assche break;
557*44704f69SBart Van Assche case 'r':
558*44704f69SBart Van Assche ++op->do_raw;
559*44704f69SBart Van Assche break;
560*44704f69SBart Van Assche case 'R':
561*44704f69SBart Van Assche op->do_raw += 2;
562*44704f69SBart Van Assche break;
563*44704f69SBart Van Assche case 's':
564*44704f69SBart Van Assche op->do_six = true;
565*44704f69SBart Van Assche break;
566*44704f69SBart Van Assche case 'v':
567*44704f69SBart Van Assche op->verbose_given = true;
568*44704f69SBart Van Assche ++op->verbose;
569*44704f69SBart Van Assche break;
570*44704f69SBart Van Assche case 'V':
571*44704f69SBart Van Assche op->version_given = true;
572*44704f69SBart Van Assche break;
573*44704f69SBart Van Assche case 'w':
574*44704f69SBart Van Assche op->o_readwrite = true;
575*44704f69SBart Van Assche break;
576*44704f69SBart Van Assche default:
577*44704f69SBart Van Assche pr2serr("unrecognised option code %c [0x%x]\n", c, c);
578*44704f69SBart Van Assche if (op->do_help)
579*44704f69SBart Van Assche break;
580*44704f69SBart Van Assche usage();
581*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
582*44704f69SBart Van Assche }
583*44704f69SBart Van Assche }
584*44704f69SBart Van Assche if (optind < argc) {
585*44704f69SBart Van Assche if (NULL == op->device_name) {
586*44704f69SBart Van Assche op->device_name = argv[optind];
587*44704f69SBart Van Assche ++optind;
588*44704f69SBart Van Assche }
589*44704f69SBart Van Assche if (optind < argc) {
590*44704f69SBart Van Assche for (; optind < argc; ++optind)
591*44704f69SBart Van Assche pr2serr("Unexpected extra argument: %s\n",
592*44704f69SBart Van Assche argv[optind]);
593*44704f69SBart Van Assche usage();
594*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
595*44704f69SBart Van Assche }
596*44704f69SBart Van Assche }
597*44704f69SBart Van Assche return 0;
598*44704f69SBart Van Assche }
599*44704f69SBart Van Assche
600*44704f69SBart Van Assche /* Processes command line options according to old option format. Returns
601*44704f69SBart Van Assche * 0 is ok, else SG_LIB_SYNTAX_ERROR is returned. */
602*44704f69SBart Van Assche static int
old_parse_cmd_line(struct opts_t * op,int argc,char * argv[])603*44704f69SBart Van Assche old_parse_cmd_line(struct opts_t * op, int argc, char * argv[])
604*44704f69SBart Van Assche {
605*44704f69SBart Van Assche bool jmp_out;
606*44704f69SBart Van Assche int k, plen, num, n;
607*44704f69SBart Van Assche char pc1;
608*44704f69SBart Van Assche unsigned int u, uu;
609*44704f69SBart Van Assche const char * cp;
610*44704f69SBart Van Assche
611*44704f69SBart Van Assche for (k = 1; k < argc; ++k) {
612*44704f69SBart Van Assche cp = argv[k];
613*44704f69SBart Van Assche plen = strlen(cp);
614*44704f69SBart Van Assche if (plen <= 0)
615*44704f69SBart Van Assche continue;
616*44704f69SBart Van Assche if ('-' == *cp) {
617*44704f69SBart Van Assche for (--plen, ++cp, jmp_out = false; plen > 0; --plen, ++cp) {
618*44704f69SBart Van Assche switch (*cp) {
619*44704f69SBart Van Assche case '6':
620*44704f69SBart Van Assche op->do_six = true;
621*44704f69SBart Van Assche break;
622*44704f69SBart Van Assche case 'a':
623*44704f69SBart Van Assche ++op->do_all;
624*44704f69SBart Van Assche break;
625*44704f69SBart Van Assche case 'A':
626*44704f69SBart Van Assche op->do_all += 2;
627*44704f69SBart Van Assche break;
628*44704f69SBart Van Assche case 'd':
629*44704f69SBart Van Assche op->do_dbd = true;
630*44704f69SBart Van Assche break;
631*44704f69SBart Van Assche case 'D':
632*44704f69SBart Van Assche op->do_dbout = true;
633*44704f69SBart Van Assche break;
634*44704f69SBart Van Assche case 'e':
635*44704f69SBart Van Assche op->do_examine = true;
636*44704f69SBart Van Assche break;
637*44704f69SBart Van Assche case 'f':
638*44704f69SBart Van Assche op->do_flexible = true;
639*44704f69SBart Van Assche break;
640*44704f69SBart Van Assche case 'h':
641*44704f69SBart Van Assche case 'H':
642*44704f69SBart Van Assche op->do_hex += 2;
643*44704f69SBart Van Assche break;
644*44704f69SBart Van Assche case 'l':
645*44704f69SBart Van Assche op->do_list = true;
646*44704f69SBart Van Assche break;
647*44704f69SBart Van Assche case 'L':
648*44704f69SBart Van Assche op->do_llbaa = true;
649*44704f69SBart Van Assche break;
650*44704f69SBart Van Assche case 'N':
651*44704f69SBart Van Assche op->opt_new = true;
652*44704f69SBart Van Assche return 0;
653*44704f69SBart Van Assche case 'O':
654*44704f69SBart Van Assche break;
655*44704f69SBart Van Assche case 'r':
656*44704f69SBart Van Assche op->do_raw += 2;
657*44704f69SBart Van Assche break;
658*44704f69SBart Van Assche case 'v':
659*44704f69SBart Van Assche op->verbose_given = true;
660*44704f69SBart Van Assche ++op->verbose;
661*44704f69SBart Van Assche break;
662*44704f69SBart Van Assche case 'V':
663*44704f69SBart Van Assche op->version_given = true;
664*44704f69SBart Van Assche break;
665*44704f69SBart Van Assche case '?':
666*44704f69SBart Van Assche ++op->do_help;
667*44704f69SBart Van Assche break;
668*44704f69SBart Van Assche default:
669*44704f69SBart Van Assche jmp_out = true;
670*44704f69SBart Van Assche break;
671*44704f69SBart Van Assche }
672*44704f69SBart Van Assche if (jmp_out)
673*44704f69SBart Van Assche break;
674*44704f69SBart Van Assche }
675*44704f69SBart Van Assche if (plen <= 0)
676*44704f69SBart Van Assche continue;
677*44704f69SBart Van Assche if (0 == strncmp("c=", cp, 2)) {
678*44704f69SBart Van Assche num = sscanf(cp + 2, "%x", &u);
679*44704f69SBart Van Assche if ((1 != num) || (u > 3)) {
680*44704f69SBart Van Assche pr2serr("Bad page control after 'c=' option\n");
681*44704f69SBart Van Assche usage_old();
682*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
683*44704f69SBart Van Assche }
684*44704f69SBart Van Assche op->page_control = u;
685*44704f69SBart Van Assche } else if (0 == strncmp("m=", cp, 2)) {
686*44704f69SBart Van Assche num = sscanf(cp + 2, "%d", &n);
687*44704f69SBart Van Assche if ((1 != num) || (n < 0) || (n > 65535)) {
688*44704f69SBart Van Assche pr2serr("Bad argument after 'm=' option\n");
689*44704f69SBart Van Assche usage_old();
690*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
691*44704f69SBart Van Assche }
692*44704f69SBart Van Assche if ((n > 0) && (n < 4)) {
693*44704f69SBart Van Assche pr2serr("Changing that '-m=' value to 4\n");
694*44704f69SBart Van Assche n = 4;
695*44704f69SBart Van Assche }
696*44704f69SBart Van Assche op->maxlen = n;
697*44704f69SBart Van Assche } else if (0 == strncmp("p=", cp, 2)) {
698*44704f69SBart Van Assche pc1 = *(cp + 2);
699*44704f69SBart Van Assche if (isalpha(pc1) && ((islower(pc1) && (pc1 > 'f')) ||
700*44704f69SBart Van Assche (isupper(pc1) && (pc1 > 'F')))) {
701*44704f69SBart Van Assche pr2serr("Old format doesn't accept mode page acronyms: "
702*44704f69SBart Van Assche "%s\n", cp + 2);
703*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
704*44704f69SBart Van Assche }
705*44704f69SBart Van Assche if (NULL == strchr(cp + 2, ',')) {
706*44704f69SBart Van Assche num = sscanf(cp + 2, "%x", &u);
707*44704f69SBart Van Assche if ((1 != num) || (u > 63)) {
708*44704f69SBart Van Assche pr2serr("Bad page code value after 'p=' option\n");
709*44704f69SBart Van Assche usage_old();
710*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
711*44704f69SBart Van Assche }
712*44704f69SBart Van Assche op->pg_code = u;
713*44704f69SBart Van Assche } else if (2 == sscanf(cp + 2, "%x,%x", &u, &uu)) {
714*44704f69SBart Van Assche if (uu > 255) {
715*44704f69SBart Van Assche pr2serr("Bad subpage code value after 'p=' option\n");
716*44704f69SBart Van Assche usage_old();
717*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
718*44704f69SBart Van Assche }
719*44704f69SBart Van Assche op->pg_code = u;
720*44704f69SBart Van Assche op->subpg_code = uu;
721*44704f69SBart Van Assche op->subpg_code_given = true;
722*44704f69SBart Van Assche } else {
723*44704f69SBart Van Assche pr2serr("Bad page code, subpage code sequence after 'p=' "
724*44704f69SBart Van Assche "option\n");
725*44704f69SBart Van Assche usage_old();
726*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
727*44704f69SBart Van Assche }
728*44704f69SBart Van Assche } else if (0 == strncmp("subp=", cp, 5)) {
729*44704f69SBart Van Assche num = sscanf(cp + 5, "%x", &u);
730*44704f69SBart Van Assche if ((1 != num) || (u > 255)) {
731*44704f69SBart Van Assche pr2serr("Bad sub page code after 'subp=' option\n");
732*44704f69SBart Van Assche usage_old();
733*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
734*44704f69SBart Van Assche }
735*44704f69SBart Van Assche op->subpg_code = u;
736*44704f69SBart Van Assche op->subpg_code_given = true;
737*44704f69SBart Van Assche if (-1 == op->pg_code)
738*44704f69SBart Van Assche op->pg_code = 0;
739*44704f69SBart Van Assche } else if (0 == strncmp("-old", cp, 4))
740*44704f69SBart Van Assche ;
741*44704f69SBart Van Assche else if (jmp_out) {
742*44704f69SBart Van Assche pr2serr("Unrecognized option: %s\n", cp);
743*44704f69SBart Van Assche usage_old();
744*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
745*44704f69SBart Van Assche }
746*44704f69SBart Van Assche } else if (0 == op->device_name)
747*44704f69SBart Van Assche op->device_name = cp;
748*44704f69SBart Van Assche else {
749*44704f69SBart Van Assche pr2serr("too many arguments, got: %s, not expecting: %s\n",
750*44704f69SBart Van Assche op->device_name, cp);
751*44704f69SBart Van Assche usage_old();
752*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
753*44704f69SBart Van Assche }
754*44704f69SBart Van Assche }
755*44704f69SBart Van Assche return 0;
756*44704f69SBart Van Assche }
757*44704f69SBart Van Assche
758*44704f69SBart Van Assche /* Process command line options. First check using new option format unless
759*44704f69SBart Van Assche * the SG3_UTILS_OLD_OPTS environment variable is defined which causes the
760*44704f69SBart Van Assche * old option format to be checked first. Both new and old format can be
761*44704f69SBart Van Assche * countermanded by a '-O' and '-N' options respectively. As soon as either
762*44704f69SBart Van Assche * of these options is detected (when processing the other format), processing
763*44704f69SBart Van Assche * stops and is restarted using the other format. Clear? */
764*44704f69SBart Van Assche static int
parse_cmd_line(struct opts_t * op,int argc,char * argv[])765*44704f69SBart Van Assche parse_cmd_line(struct opts_t * op, int argc, char * argv[])
766*44704f69SBart Van Assche {
767*44704f69SBart Van Assche int res;
768*44704f69SBart Van Assche char * cp;
769*44704f69SBart Van Assche
770*44704f69SBart Van Assche cp = getenv("SG3_UTILS_OLD_OPTS");
771*44704f69SBart Van Assche if (cp) {
772*44704f69SBart Van Assche op->opt_new = false;
773*44704f69SBart Van Assche res = old_parse_cmd_line(op, argc, argv);
774*44704f69SBart Van Assche if ((0 == res) && op->opt_new)
775*44704f69SBart Van Assche res = new_parse_cmd_line(op, argc, argv);
776*44704f69SBart Van Assche } else {
777*44704f69SBart Van Assche op->opt_new = true;
778*44704f69SBart Van Assche res = new_parse_cmd_line(op, argc, argv);
779*44704f69SBart Van Assche if ((0 == res) && (! op->opt_new))
780*44704f69SBart Van Assche res = old_parse_cmd_line(op, argc, argv);
781*44704f69SBart Van Assche }
782*44704f69SBart Van Assche return res;
783*44704f69SBart Van Assche }
784*44704f69SBart Van Assche
785*44704f69SBart Van Assche static void
dStrRaw(const uint8_t * str,int len)786*44704f69SBart Van Assche dStrRaw(const uint8_t * str, int len)
787*44704f69SBart Van Assche {
788*44704f69SBart Van Assche int k;
789*44704f69SBart Van Assche
790*44704f69SBart Van Assche for (k = 0; k < len; ++k)
791*44704f69SBart Van Assche printf("%c", str[k]);
792*44704f69SBart Van Assche }
793*44704f69SBart Van Assche
794*44704f69SBart Van Assche /* Note to coverity: this function is safe as long as the page_code_desc
795*44704f69SBart Van Assche * objects pointed to by pcdp have a sentinel object at the end of each
796*44704f69SBart Van Assche * array. And they do by design.*/
797*44704f69SBart Van Assche static int
count_desc_elems(const struct page_code_desc * pcdp)798*44704f69SBart Van Assche count_desc_elems(const struct page_code_desc * pcdp)
799*44704f69SBart Van Assche {
800*44704f69SBart Van Assche int k;
801*44704f69SBart Van Assche
802*44704f69SBart Van Assche for (k = 0; k < 1024; ++k, ++pcdp) {
803*44704f69SBart Van Assche if (NULL == pcdp->acron)
804*44704f69SBart Van Assche return k;
805*44704f69SBart Van Assche }
806*44704f69SBart Van Assche pr2serr("%s: sanity check trip, invalid pc_desc table\n", __func__);
807*44704f69SBart Van Assche return k;
808*44704f69SBart Van Assche }
809*44704f69SBart Van Assche
810*44704f69SBart Van Assche /* Returns pointer to base of table for scsi_ptype or pointer to common
811*44704f69SBart Van Assche * table if scsi_ptype is -1. Yields numbers of elements in returned
812*44704f69SBart Van Assche * table via pointer sizep. If scsi_ptype not known then returns NULL
813*44704f69SBart Van Assche * with *sizep set to zero. */
814*44704f69SBart Van Assche static struct page_code_desc *
get_mpage_tbl_size(int scsi_ptype,int * sizep)815*44704f69SBart Van Assche get_mpage_tbl_size(int scsi_ptype, int * sizep)
816*44704f69SBart Van Assche {
817*44704f69SBart Van Assche switch (scsi_ptype)
818*44704f69SBart Van Assche {
819*44704f69SBart Van Assche case -1: /* common list */
820*44704f69SBart Van Assche *sizep = count_desc_elems(pc_desc_common);
821*44704f69SBart Van Assche return &pc_desc_common[0];
822*44704f69SBart Van Assche case PDT_DISK: /* disk (direct access) type devices */
823*44704f69SBart Van Assche case PDT_WO:
824*44704f69SBart Van Assche case PDT_OPTICAL:
825*44704f69SBart Van Assche *sizep = count_desc_elems(pc_desc_disk);
826*44704f69SBart Van Assche return &pc_desc_disk[0];
827*44704f69SBart Van Assche case PDT_TAPE: /* tape devices */
828*44704f69SBart Van Assche case PDT_PRINTER:
829*44704f69SBart Van Assche *sizep = count_desc_elems(pc_desc_tape);
830*44704f69SBart Van Assche return &pc_desc_tape[0];
831*44704f69SBart Van Assche case PDT_MMC: /* cd/dvd/bd devices */
832*44704f69SBart Van Assche *sizep = count_desc_elems(pc_desc_cddvd);
833*44704f69SBart Van Assche return &pc_desc_cddvd[0];
834*44704f69SBart Van Assche case PDT_MCHANGER: /* medium changer devices */
835*44704f69SBart Van Assche *sizep = count_desc_elems(pc_desc_smc);
836*44704f69SBart Van Assche return &pc_desc_smc[0];
837*44704f69SBart Van Assche case PDT_SAC: /* storage array devices */
838*44704f69SBart Van Assche *sizep = count_desc_elems(pc_desc_scc);
839*44704f69SBart Van Assche return &pc_desc_scc[0];
840*44704f69SBart Van Assche case PDT_SES: /* enclosure services devices */
841*44704f69SBart Van Assche *sizep = count_desc_elems(pc_desc_ses);
842*44704f69SBart Van Assche return &pc_desc_ses[0];
843*44704f69SBart Van Assche case PDT_RBC: /* simplified direct access device */
844*44704f69SBart Van Assche *sizep = count_desc_elems(pc_desc_rbc);
845*44704f69SBart Van Assche return &pc_desc_rbc[0];
846*44704f69SBart Van Assche case PDT_ADC: /* automation device/interface */
847*44704f69SBart Van Assche *sizep = count_desc_elems(pc_desc_adc);
848*44704f69SBart Van Assche return &pc_desc_adc[0];
849*44704f69SBart Van Assche case PDT_ZBC:
850*44704f69SBart Van Assche *sizep = count_desc_elems(pc_desc_zbc);
851*44704f69SBart Van Assche return &pc_desc_zbc[0];
852*44704f69SBart Van Assche }
853*44704f69SBart Van Assche *sizep = 0;
854*44704f69SBart Van Assche return NULL;
855*44704f69SBart Van Assche }
856*44704f69SBart Van Assche
857*44704f69SBart Van Assche
858*44704f69SBart Van Assche static struct page_code_desc *
get_mpage_trans_tbl_size(int t_proto,int * sizep)859*44704f69SBart Van Assche get_mpage_trans_tbl_size(int t_proto, int * sizep)
860*44704f69SBart Van Assche {
861*44704f69SBart Van Assche switch (t_proto)
862*44704f69SBart Van Assche {
863*44704f69SBart Van Assche case TPROTO_FCP:
864*44704f69SBart Van Assche *sizep = count_desc_elems(pc_desc_t_fcp);
865*44704f69SBart Van Assche return &pc_desc_t_fcp[0];
866*44704f69SBart Van Assche case TPROTO_SPI:
867*44704f69SBart Van Assche *sizep = count_desc_elems(pc_desc_t_spi4);
868*44704f69SBart Van Assche return &pc_desc_t_spi4[0];
869*44704f69SBart Van Assche case TPROTO_SAS:
870*44704f69SBart Van Assche *sizep = count_desc_elems(pc_desc_t_sas);
871*44704f69SBart Van Assche return &pc_desc_t_sas[0];
872*44704f69SBart Van Assche case TPROTO_ADT:
873*44704f69SBart Van Assche *sizep = count_desc_elems(pc_desc_t_adc);
874*44704f69SBart Van Assche return &pc_desc_t_adc[0];
875*44704f69SBart Van Assche }
876*44704f69SBart Van Assche *sizep = 0;
877*44704f69SBart Van Assche return NULL;
878*44704f69SBart Van Assche }
879*44704f69SBart Van Assche
880*44704f69SBart Van Assche static const char *
find_page_code_desc(int page_num,int subpage_num,int scsi_ptype,bool encserv,bool mchngr,int t_proto)881*44704f69SBart Van Assche find_page_code_desc(int page_num, int subpage_num, int scsi_ptype,
882*44704f69SBart Van Assche bool encserv, bool mchngr, int t_proto)
883*44704f69SBart Van Assche {
884*44704f69SBart Van Assche int k, num, decayed_pdt;
885*44704f69SBart Van Assche const struct page_code_desc * pcdp;
886*44704f69SBart Van Assche
887*44704f69SBart Van Assche if (t_proto >= 0) {
888*44704f69SBart Van Assche pcdp = get_mpage_trans_tbl_size(t_proto, &num);
889*44704f69SBart Van Assche if (pcdp) {
890*44704f69SBart Van Assche for (k = 0; k < num; ++k, ++pcdp) {
891*44704f69SBart Van Assche if ((page_num == pcdp->page_code) &&
892*44704f69SBart Van Assche (subpage_num == pcdp->subpage_code))
893*44704f69SBart Van Assche return pcdp->desc;
894*44704f69SBart Van Assche else if (page_num < pcdp->page_code)
895*44704f69SBart Van Assche break;
896*44704f69SBart Van Assche }
897*44704f69SBart Van Assche }
898*44704f69SBart Van Assche }
899*44704f69SBart Van Assche try_again:
900*44704f69SBart Van Assche pcdp = get_mpage_tbl_size(scsi_ptype, &num);
901*44704f69SBart Van Assche if (pcdp) {
902*44704f69SBart Van Assche for (k = 0; k < num; ++k, ++pcdp) {
903*44704f69SBart Van Assche if ((page_num == pcdp->page_code) &&
904*44704f69SBart Van Assche (subpage_num == pcdp->subpage_code))
905*44704f69SBart Van Assche return pcdp->desc;
906*44704f69SBart Van Assche else if (page_num < pcdp->page_code)
907*44704f69SBart Van Assche break;
908*44704f69SBart Van Assche }
909*44704f69SBart Van Assche }
910*44704f69SBart Van Assche decayed_pdt = sg_lib_pdt_decay(scsi_ptype);
911*44704f69SBart Van Assche if (decayed_pdt != scsi_ptype) {
912*44704f69SBart Van Assche scsi_ptype = decayed_pdt;
913*44704f69SBart Van Assche goto try_again;
914*44704f69SBart Van Assche }
915*44704f69SBart Van Assche if ((0xd != scsi_ptype) && encserv) {
916*44704f69SBart Van Assche /* check for attached enclosure services processor */
917*44704f69SBart Van Assche pcdp = get_mpage_tbl_size(0xd, &num);
918*44704f69SBart Van Assche if (pcdp) {
919*44704f69SBart Van Assche for (k = 0; k < num; ++k, ++pcdp) {
920*44704f69SBart Van Assche if ((page_num == pcdp->page_code) &&
921*44704f69SBart Van Assche (subpage_num == pcdp->subpage_code))
922*44704f69SBart Van Assche return pcdp->desc;
923*44704f69SBart Van Assche else if (page_num < pcdp->page_code)
924*44704f69SBart Van Assche break;
925*44704f69SBart Van Assche }
926*44704f69SBart Van Assche }
927*44704f69SBart Van Assche }
928*44704f69SBart Van Assche if ((0x8 != scsi_ptype) && mchngr) {
929*44704f69SBart Van Assche /* check for attached medium changer device */
930*44704f69SBart Van Assche pcdp = get_mpage_tbl_size(0x8, &num);
931*44704f69SBart Van Assche if (pcdp) {
932*44704f69SBart Van Assche for (k = 0; k < num; ++k, ++pcdp) {
933*44704f69SBart Van Assche if ((page_num == pcdp->page_code) &&
934*44704f69SBart Van Assche (subpage_num == pcdp->subpage_code))
935*44704f69SBart Van Assche return pcdp->desc;
936*44704f69SBart Van Assche else if (page_num < pcdp->page_code)
937*44704f69SBart Van Assche break;
938*44704f69SBart Van Assche }
939*44704f69SBart Van Assche }
940*44704f69SBart Van Assche }
941*44704f69SBart Van Assche pcdp = get_mpage_tbl_size(-1, &num);
942*44704f69SBart Van Assche for (k = 0; k < num; ++k, ++pcdp) {
943*44704f69SBart Van Assche if ((page_num == pcdp->page_code) &&
944*44704f69SBart Van Assche (subpage_num == pcdp->subpage_code))
945*44704f69SBart Van Assche return pcdp->desc;
946*44704f69SBart Van Assche else if (page_num < pcdp->page_code)
947*44704f69SBart Van Assche break;
948*44704f69SBart Van Assche }
949*44704f69SBart Van Assche return NULL;
950*44704f69SBart Van Assche }
951*44704f69SBart Van Assche
952*44704f69SBart Van Assche static void
list_page_codes(int scsi_ptype,bool encserv,bool mchngr,int t_proto)953*44704f69SBart Van Assche list_page_codes(int scsi_ptype, bool encserv, bool mchngr, int t_proto)
954*44704f69SBart Van Assche {
955*44704f69SBart Van Assche int num, num_ptype, pg, spg, c, d;
956*44704f69SBart Van Assche bool valid_transport;
957*44704f69SBart Van Assche const struct page_code_desc * dp;
958*44704f69SBart Van Assche const struct page_code_desc * pe_dp;
959*44704f69SBart Van Assche char b[64];
960*44704f69SBart Van Assche
961*44704f69SBart Van Assche valid_transport = ((t_proto >= 0) && (t_proto <= 0xf));
962*44704f69SBart Van Assche printf("Page[,subpage] Name\n");
963*44704f69SBart Van Assche printf("=====================\n");
964*44704f69SBart Van Assche dp = get_mpage_tbl_size(-1, &num);
965*44704f69SBart Van Assche pe_dp = get_mpage_tbl_size(scsi_ptype, &num_ptype);
966*44704f69SBart Van Assche while (1) {
967*44704f69SBart Van Assche pg = dp ? dp->page_code : PG_CODE_ALL + 1;
968*44704f69SBart Van Assche spg = dp ? dp->subpage_code : SPG_CODE_ALL;
969*44704f69SBart Van Assche c = (pg << 8) + spg;
970*44704f69SBart Van Assche pg = pe_dp ? pe_dp->page_code : PG_CODE_ALL + 1;
971*44704f69SBart Van Assche spg = pe_dp ? pe_dp->subpage_code : SPG_CODE_ALL;
972*44704f69SBart Van Assche d = (pg << 8) + spg;
973*44704f69SBart Van Assche if (valid_transport &&
974*44704f69SBart Van Assche ((PROTO_SPECIFIC_1 == c) || (PROTO_SPECIFIC_2 == c)))
975*44704f69SBart Van Assche dp = (--num <= 0) ? NULL : (dp + 1); /* skip protocol specific */
976*44704f69SBart Van Assche else if (c == d) {
977*44704f69SBart Van Assche if (pe_dp) {
978*44704f69SBart Van Assche if (pe_dp->subpage_code)
979*44704f69SBart Van Assche printf(" 0x%02x,0x%02x * %s\n", pe_dp->page_code,
980*44704f69SBart Van Assche pe_dp->subpage_code, pe_dp->desc);
981*44704f69SBart Van Assche else
982*44704f69SBart Van Assche printf(" 0x%02x * %s\n", pe_dp->page_code,
983*44704f69SBart Van Assche pe_dp->desc);
984*44704f69SBart Van Assche pe_dp = (--num_ptype <= 0) ? NULL : (pe_dp + 1);
985*44704f69SBart Van Assche }
986*44704f69SBart Van Assche if (dp)
987*44704f69SBart Van Assche dp = (--num <= 0) ? NULL : (dp + 1);
988*44704f69SBart Van Assche } else if (c < d) {
989*44704f69SBart Van Assche if (dp) {
990*44704f69SBart Van Assche if (dp->subpage_code)
991*44704f69SBart Van Assche printf(" 0x%02x,0x%02x %s\n", dp->page_code,
992*44704f69SBart Van Assche dp->subpage_code, dp->desc);
993*44704f69SBart Van Assche else
994*44704f69SBart Van Assche printf(" 0x%02x %s\n", dp->page_code,
995*44704f69SBart Van Assche dp->desc);
996*44704f69SBart Van Assche dp = (--num <= 0) ? NULL : (dp + 1);
997*44704f69SBart Van Assche }
998*44704f69SBart Van Assche } else {
999*44704f69SBart Van Assche if (pe_dp) {
1000*44704f69SBart Van Assche if (pe_dp->subpage_code)
1001*44704f69SBart Van Assche printf(" 0x%02x,0x%02x %s\n", pe_dp->page_code,
1002*44704f69SBart Van Assche pe_dp->subpage_code, pe_dp->desc);
1003*44704f69SBart Van Assche else
1004*44704f69SBart Van Assche printf(" 0x%02x %s\n", pe_dp->page_code,
1005*44704f69SBart Van Assche pe_dp->desc);
1006*44704f69SBart Van Assche pe_dp = (--num_ptype <= 0) ? NULL : (pe_dp + 1);
1007*44704f69SBart Van Assche }
1008*44704f69SBart Van Assche }
1009*44704f69SBart Van Assche if ((NULL == dp) && (NULL == pe_dp))
1010*44704f69SBart Van Assche break;
1011*44704f69SBart Van Assche }
1012*44704f69SBart Van Assche if ((0xd != scsi_ptype) && encserv) {
1013*44704f69SBart Van Assche /* check for attached enclosure services processor */
1014*44704f69SBart Van Assche printf("\n Attached enclosure services processor\n");
1015*44704f69SBart Van Assche dp = get_mpage_tbl_size(0xd, &num);
1016*44704f69SBart Van Assche while (dp) {
1017*44704f69SBart Van Assche if (dp->subpage_code)
1018*44704f69SBart Van Assche printf(" 0x%02x,0x%02x %s\n", dp->page_code,
1019*44704f69SBart Van Assche dp->subpage_code, dp->desc);
1020*44704f69SBart Van Assche else
1021*44704f69SBart Van Assche printf(" 0x%02x %s\n", dp->page_code,
1022*44704f69SBart Van Assche dp->desc);
1023*44704f69SBart Van Assche dp = (--num <= 0) ? NULL : (dp + 1);
1024*44704f69SBart Van Assche }
1025*44704f69SBart Van Assche }
1026*44704f69SBart Van Assche if ((0x8 != scsi_ptype) && mchngr) {
1027*44704f69SBart Van Assche /* check for attached medium changer device */
1028*44704f69SBart Van Assche printf("\n Attached medium changer device\n");
1029*44704f69SBart Van Assche dp = get_mpage_tbl_size(0x8, &num);
1030*44704f69SBart Van Assche while (dp) {
1031*44704f69SBart Van Assche if (dp->subpage_code)
1032*44704f69SBart Van Assche printf(" 0x%02x,0x%02x %s\n", dp->page_code,
1033*44704f69SBart Van Assche dp->subpage_code, dp->desc);
1034*44704f69SBart Van Assche else
1035*44704f69SBart Van Assche printf(" 0x%02x %s\n", dp->page_code,
1036*44704f69SBart Van Assche dp->desc);
1037*44704f69SBart Van Assche dp = (--num <= 0) ? NULL : (dp + 1);
1038*44704f69SBart Van Assche }
1039*44704f69SBart Van Assche }
1040*44704f69SBart Van Assche if (valid_transport) {
1041*44704f69SBart Van Assche printf("\n Transport protocol: %s\n",
1042*44704f69SBart Van Assche sg_get_trans_proto_str(t_proto, sizeof(b), b));
1043*44704f69SBart Van Assche dp = get_mpage_trans_tbl_size(t_proto, &num);
1044*44704f69SBart Van Assche while (dp) {
1045*44704f69SBart Van Assche if (dp->subpage_code)
1046*44704f69SBart Van Assche printf(" 0x%02x,0x%02x %s\n", dp->page_code,
1047*44704f69SBart Van Assche dp->subpage_code, dp->desc);
1048*44704f69SBart Van Assche else
1049*44704f69SBart Van Assche printf(" 0x%02x %s\n", dp->page_code,
1050*44704f69SBart Van Assche dp->desc);
1051*44704f69SBart Van Assche dp = (--num <= 0) ? NULL : (dp + 1);
1052*44704f69SBart Van Assche }
1053*44704f69SBart Van Assche }
1054*44704f69SBart Van Assche }
1055*44704f69SBart Van Assche
1056*44704f69SBart Van Assche /* Returns 0 for ok, else error value */
1057*44704f69SBart Van Assche static int
examine_pages(int sg_fd,int inq_pdt,bool encserv,bool mchngr,const struct opts_t * op)1058*44704f69SBart Van Assche examine_pages(int sg_fd, int inq_pdt, bool encserv, bool mchngr,
1059*44704f69SBart Van Assche const struct opts_t * op)
1060*44704f69SBart Van Assche {
1061*44704f69SBart Van Assche bool header_printed;
1062*44704f69SBart Van Assche int k, mresp_len, len, resid;
1063*44704f69SBart Van Assche int res = 0;
1064*44704f69SBart Van Assche const int mx_len = op->do_six ? DEF_6_ALLOC_LEN : DEF_ALLOC_LEN;
1065*44704f69SBart Van Assche const char * cp;
1066*44704f69SBart Van Assche uint8_t * rbuf;
1067*44704f69SBart Van Assche uint8_t * free_rbuf = NULL;
1068*44704f69SBart Van Assche
1069*44704f69SBart Van Assche rbuf = sg_memalign(mx_len, 0, &free_rbuf, false);
1070*44704f69SBart Van Assche if (NULL == rbuf) {
1071*44704f69SBart Van Assche pr2serr("%s: out of heap\n", __func__);
1072*44704f69SBart Van Assche return sg_convert_errno(ENOMEM);
1073*44704f69SBart Van Assche }
1074*44704f69SBart Van Assche mresp_len = (op->do_raw || op->do_hex) ? mx_len : 4;
1075*44704f69SBart Van Assche for (header_printed = false, k = 0; k < PG_CODE_MAX; ++k) {
1076*44704f69SBart Van Assche resid = 0;
1077*44704f69SBart Van Assche if (op->do_six) {
1078*44704f69SBart Van Assche res = sg_ll_mode_sense6(sg_fd, 0, 0, k, 0, rbuf, mresp_len,
1079*44704f69SBart Van Assche true, op->verbose);
1080*44704f69SBart Van Assche if (SG_LIB_CAT_INVALID_OP == res) {
1081*44704f69SBart Van Assche pr2serr(">>>>>> try again without the '-6' switch for a 10 "
1082*44704f69SBart Van Assche "byte MODE SENSE command\n");
1083*44704f69SBart Van Assche goto out;
1084*44704f69SBart Van Assche } else if (SG_LIB_CAT_NOT_READY == res) {
1085*44704f69SBart Van Assche pr2serr("MODE SENSE (6) failed, device not ready\n");
1086*44704f69SBart Van Assche goto out;
1087*44704f69SBart Van Assche }
1088*44704f69SBart Van Assche } else {
1089*44704f69SBart Van Assche res = sg_ll_mode_sense10_v2(sg_fd, 0, 0, 0, k, 0, rbuf, mresp_len,
1090*44704f69SBart Van Assche 0, &resid, true, op->verbose);
1091*44704f69SBart Van Assche if (SG_LIB_CAT_INVALID_OP == res) {
1092*44704f69SBart Van Assche pr2serr(">>>>>> try again with a '-6' switch for a 6 byte "
1093*44704f69SBart Van Assche "MODE SENSE command\n");
1094*44704f69SBart Van Assche goto out;
1095*44704f69SBart Van Assche } else if (SG_LIB_CAT_NOT_READY == res) {
1096*44704f69SBart Van Assche pr2serr("MODE SENSE (10) failed, device not ready\n");
1097*44704f69SBart Van Assche goto out;
1098*44704f69SBart Van Assche }
1099*44704f69SBart Van Assche }
1100*44704f69SBart Van Assche if (0 == res) {
1101*44704f69SBart Van Assche len = sg_msense_calc_length(rbuf, mresp_len, op->do_six, NULL);
1102*44704f69SBart Van Assche if (resid > 0) {
1103*44704f69SBart Van Assche mresp_len -= resid;
1104*44704f69SBart Van Assche if (mresp_len < 0) {
1105*44704f69SBart Van Assche pr2serr("%s: MS(10) resid=%d implies negative response "
1106*44704f69SBart Van Assche "length (%d)\n", __func__, resid, mresp_len);
1107*44704f69SBart Van Assche res = SG_LIB_WILD_RESID;
1108*44704f69SBart Van Assche goto out;
1109*44704f69SBart Van Assche }
1110*44704f69SBart Van Assche }
1111*44704f69SBart Van Assche if (len > mresp_len)
1112*44704f69SBart Van Assche len = mresp_len;
1113*44704f69SBart Van Assche if (op->do_raw) {
1114*44704f69SBart Van Assche dStrRaw(rbuf, len);
1115*44704f69SBart Van Assche continue;
1116*44704f69SBart Van Assche }
1117*44704f69SBart Van Assche if (op->do_hex > 2) {
1118*44704f69SBart Van Assche hex2stdout(rbuf, len, -1);
1119*44704f69SBart Van Assche continue;
1120*44704f69SBart Van Assche }
1121*44704f69SBart Van Assche if (! header_printed) {
1122*44704f69SBart Van Assche printf("Discovered mode pages:\n");
1123*44704f69SBart Van Assche header_printed = true;
1124*44704f69SBart Van Assche }
1125*44704f69SBart Van Assche cp = find_page_code_desc(k, 0, inq_pdt, encserv, mchngr, -1);
1126*44704f69SBart Van Assche if (cp)
1127*44704f69SBart Van Assche printf(" %s\n", cp);
1128*44704f69SBart Van Assche else
1129*44704f69SBart Van Assche printf(" [0x%x]\n", k);
1130*44704f69SBart Van Assche if (op->do_hex)
1131*44704f69SBart Van Assche hex2stdout(rbuf, len, 1);
1132*44704f69SBart Van Assche } else if (op->verbose) {
1133*44704f69SBart Van Assche char b[80];
1134*44704f69SBart Van Assche
1135*44704f69SBart Van Assche sg_get_category_sense_str(res, sizeof(b), b, op->verbose - 1);
1136*44704f69SBart Van Assche pr2serr("MODE SENSE (%s) failed: %s\n", (op->do_six ? "6" : "10"),
1137*44704f69SBart Van Assche b);
1138*44704f69SBart Van Assche }
1139*44704f69SBart Van Assche }
1140*44704f69SBart Van Assche out:
1141*44704f69SBart Van Assche if (free_rbuf)
1142*44704f69SBart Van Assche free(free_rbuf);
1143*44704f69SBart Van Assche return res;
1144*44704f69SBart Van Assche }
1145*44704f69SBart Van Assche
1146*44704f69SBart Van Assche static const char * pg_control_str_arr[] = {
1147*44704f69SBart Van Assche "current",
1148*44704f69SBart Van Assche "changeable",
1149*44704f69SBart Van Assche "default",
1150*44704f69SBart Van Assche "saved",
1151*44704f69SBart Van Assche };
1152*44704f69SBart Van Assche
1153*44704f69SBart Van Assche
1154*44704f69SBart Van Assche int
main(int argc,char * argv[])1155*44704f69SBart Van Assche main(int argc, char * argv[])
1156*44704f69SBart Van Assche {
1157*44704f69SBart Van Assche bool resp_mode6, longlba, spf;
1158*44704f69SBart Van Assche bool encserv = false;
1159*44704f69SBart Van Assche bool mchngr = false;
1160*44704f69SBart Van Assche uint8_t uc;
1161*44704f69SBart Van Assche int k, num, len, res, md_len, bd_len, page_num, resid;
1162*44704f69SBart Van Assche int density_code_off, t_proto, inq_pdt, num_ua_pages, vb;
1163*44704f69SBart Van Assche int sg_fd = -1;
1164*44704f69SBart Van Assche int ret = 0;
1165*44704f69SBart Van Assche int rsp_buff_sz = DEF_ALLOC_LEN;
1166*44704f69SBart Van Assche const char * descp;
1167*44704f69SBart Van Assche struct opts_t * op;
1168*44704f69SBart Van Assche uint8_t * rsp_buff = NULL;
1169*44704f69SBart Van Assche uint8_t * free_rsp_buff = NULL;
1170*44704f69SBart Van Assche uint8_t * bp;
1171*44704f69SBart Van Assche const char * cdbLenStr;
1172*44704f69SBart Van Assche struct sg_simple_inquiry_resp inq_out;
1173*44704f69SBart Van Assche struct opts_t opts;
1174*44704f69SBart Van Assche char b[80];
1175*44704f69SBart Van Assche char ebuff[EBUFF_SZ];
1176*44704f69SBart Van Assche char pdt_name[64];
1177*44704f69SBart Van Assche
1178*44704f69SBart Van Assche op = &opts;
1179*44704f69SBart Van Assche memset(op, 0, sizeof(opts));
1180*44704f69SBart Van Assche op->pg_code = -1;
1181*44704f69SBart Van Assche res = parse_cmd_line(op, argc, argv);
1182*44704f69SBart Van Assche if (res)
1183*44704f69SBart Van Assche return (SG_LIB_OK_FALSE == res) ? 0 : res;
1184*44704f69SBart Van Assche if (op->do_help) {
1185*44704f69SBart Van Assche usage_for(op);
1186*44704f69SBart Van Assche return 0;
1187*44704f69SBart Van Assche }
1188*44704f69SBart Van Assche #ifdef DEBUG
1189*44704f69SBart Van Assche pr2serr("In DEBUG mode, ");
1190*44704f69SBart Van Assche if (op->verbose_given && op->version_given) {
1191*44704f69SBart Van Assche pr2serr("but override: '-vV' given, zero verbose and continue\n");
1192*44704f69SBart Van Assche op->verbose_given = false;
1193*44704f69SBart Van Assche op->version_given = false;
1194*44704f69SBart Van Assche op->verbose = 0;
1195*44704f69SBart Van Assche } else if (! op->verbose_given) {
1196*44704f69SBart Van Assche pr2serr("set '-vv'\n");
1197*44704f69SBart Van Assche op->verbose = 2;
1198*44704f69SBart Van Assche } else
1199*44704f69SBart Van Assche pr2serr("keep verbose=%d\n", op->verbose);
1200*44704f69SBart Van Assche #else
1201*44704f69SBart Van Assche if (op->verbose_given && op->version_given)
1202*44704f69SBart Van Assche pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
1203*44704f69SBart Van Assche #endif
1204*44704f69SBart Van Assche if (op->version_given) {
1205*44704f69SBart Van Assche pr2serr("Version string: %s\n", version_str);
1206*44704f69SBart Van Assche return 0;
1207*44704f69SBart Van Assche }
1208*44704f69SBart Van Assche vb = op->verbose;
1209*44704f69SBart Van Assche if (vb && op->page_acron) {
1210*44704f69SBart Van Assche pr2serr("page acronynm: '%s' maps to page_code=0x%x",
1211*44704f69SBart Van Assche op->page_acron, op->pg_code);
1212*44704f69SBart Van Assche if (op->subpg_code > 0)
1213*44704f69SBart Van Assche pr2serr(", subpage_code=0x%x\n", op->subpg_code);
1214*44704f69SBart Van Assche else
1215*44704f69SBart Van Assche pr2serr("\n");
1216*44704f69SBart Van Assche }
1217*44704f69SBart Van Assche
1218*44704f69SBart Van Assche if (NULL == op->device_name) {
1219*44704f69SBart Van Assche if (op->do_list) {
1220*44704f69SBart Van Assche if ((op->pg_code < 0) || (op->pg_code > PG_CODE_MAX)) {
1221*44704f69SBart Van Assche printf(" Assume peripheral device type: disk\n");
1222*44704f69SBart Van Assche list_page_codes(0, false, false, -1);
1223*44704f69SBart Van Assche } else {
1224*44704f69SBart Van Assche printf(" peripheral device type: %s\n",
1225*44704f69SBart Van Assche sg_get_pdt_str(op->pg_code, sizeof(pdt_name),
1226*44704f69SBart Van Assche pdt_name));
1227*44704f69SBart Van Assche if (op->subpg_code_given)
1228*44704f69SBart Van Assche list_page_codes(op->pg_code, false, false,
1229*44704f69SBart Van Assche op->subpg_code);
1230*44704f69SBart Van Assche else
1231*44704f69SBart Van Assche list_page_codes(op->pg_code, false, false, -1);
1232*44704f69SBart Van Assche }
1233*44704f69SBart Van Assche return 0;
1234*44704f69SBart Van Assche }
1235*44704f69SBart Van Assche pr2serr("No DEVICE argument given\n\n");
1236*44704f69SBart Van Assche usage_for(op);
1237*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
1238*44704f69SBart Van Assche }
1239*44704f69SBart Van Assche
1240*44704f69SBart Van Assche if (op->do_examine && (op->pg_code >= 0)) {
1241*44704f69SBart Van Assche pr2serr("can't give '-e' and a page number\n");
1242*44704f69SBart Van Assche return SG_LIB_CONTRADICT;
1243*44704f69SBart Van Assche }
1244*44704f69SBart Van Assche
1245*44704f69SBart Van Assche if (op->do_six && op->do_llbaa) {
1246*44704f69SBart Van Assche pr2serr("LLBAA not defined for MODE SENSE 6, try without '-L'\n");
1247*44704f69SBart Van Assche return SG_LIB_CONTRADICT;
1248*44704f69SBart Van Assche }
1249*44704f69SBart Van Assche if (op->maxlen > 0) {
1250*44704f69SBart Van Assche if (op->do_six && (op->maxlen > 255)) {
1251*44704f69SBart Van Assche pr2serr("For Mode Sense (6) maxlen cannot exceed 255\n");
1252*44704f69SBart Van Assche return SG_LIB_SYNTAX_ERROR;
1253*44704f69SBart Van Assche }
1254*44704f69SBart Van Assche rsp_buff = sg_memalign(op->maxlen, 0, &free_rsp_buff, false);
1255*44704f69SBart Van Assche rsp_buff_sz = op->maxlen;
1256*44704f69SBart Van Assche } else { /* maxlen == 0 */
1257*44704f69SBart Van Assche rsp_buff = sg_memalign(rsp_buff_sz, 0, &free_rsp_buff, false);
1258*44704f69SBart Van Assche if (op->do_six)
1259*44704f69SBart Van Assche rsp_buff_sz = DEF_6_ALLOC_LEN;
1260*44704f69SBart Van Assche }
1261*44704f69SBart Van Assche if (NULL == rsp_buff) { /* check for both sg_memalign()s */
1262*44704f69SBart Van Assche pr2serr("Unable to allocate %d bytes on heap\n", rsp_buff_sz);
1263*44704f69SBart Van Assche return sg_convert_errno(ENOMEM);
1264*44704f69SBart Van Assche }
1265*44704f69SBart Van Assche /* If no pages or list selected than treat as 'a' */
1266*44704f69SBart Van Assche if (! ((op->pg_code >= 0) || op->do_all || op->do_list || op->do_examine))
1267*44704f69SBart Van Assche op->do_all = 1;
1268*44704f69SBart Van Assche
1269*44704f69SBart Van Assche if (op->do_raw) {
1270*44704f69SBart Van Assche if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
1271*44704f69SBart Van Assche perror("sg_set_binary_mode");
1272*44704f69SBart Van Assche ret = SG_LIB_FILE_ERROR;
1273*44704f69SBart Van Assche goto fini;
1274*44704f69SBart Van Assche }
1275*44704f69SBart Van Assche }
1276*44704f69SBart Van Assche
1277*44704f69SBart Van Assche if ((sg_fd = sg_cmds_open_device(op->device_name, ! op->o_readwrite,
1278*44704f69SBart Van Assche vb)) < 0) {
1279*44704f69SBart Van Assche pr2serr("error opening file: %s: %s\n", op->device_name,
1280*44704f69SBart Van Assche safe_strerror(-sg_fd));
1281*44704f69SBart Van Assche ret = sg_convert_errno(-sg_fd);
1282*44704f69SBart Van Assche goto fini;
1283*44704f69SBart Van Assche }
1284*44704f69SBart Van Assche
1285*44704f69SBart Van Assche if ((res = sg_simple_inquiry(sg_fd, &inq_out, true, vb))) {
1286*44704f69SBart Van Assche pr2serr("%s doesn't respond to a SCSI INQUIRY\n", op->device_name);
1287*44704f69SBart Van Assche ret = (res > 0) ? res : sg_convert_errno(-res);
1288*44704f69SBart Van Assche goto fini;
1289*44704f69SBart Van Assche }
1290*44704f69SBart Van Assche inq_pdt = inq_out.peripheral_type;
1291*44704f69SBart Van Assche encserv = !! (0x40 & inq_out.byte_6);
1292*44704f69SBart Van Assche mchngr = !! (0x8 & inq_out.byte_6);
1293*44704f69SBart Van Assche if ((0 == op->do_raw) && (op->do_hex < 3))
1294*44704f69SBart Van Assche printf(" %.8s %.16s %.4s peripheral_type: %s [0x%x]\n",
1295*44704f69SBart Van Assche inq_out.vendor, inq_out.product, inq_out.revision,
1296*44704f69SBart Van Assche sg_get_pdt_str(inq_pdt, sizeof(pdt_name), pdt_name), inq_pdt);
1297*44704f69SBart Van Assche if (op->do_list) {
1298*44704f69SBart Van Assche if (op->subpg_code_given)
1299*44704f69SBart Van Assche list_page_codes(inq_pdt, encserv, mchngr, op->subpg_code);
1300*44704f69SBart Van Assche else
1301*44704f69SBart Van Assche list_page_codes(inq_pdt, encserv, mchngr, -1);
1302*44704f69SBart Van Assche goto fini;
1303*44704f69SBart Van Assche }
1304*44704f69SBart Van Assche if (op->do_examine) {
1305*44704f69SBart Van Assche ret = examine_pages(sg_fd, inq_pdt, encserv, mchngr, op);
1306*44704f69SBart Van Assche goto fini;
1307*44704f69SBart Van Assche }
1308*44704f69SBart Van Assche if (PG_CODE_ALL == op->pg_code) {
1309*44704f69SBart Van Assche if (0 == op->do_all)
1310*44704f69SBart Van Assche ++op->do_all;
1311*44704f69SBart Van Assche } else if (op->do_all)
1312*44704f69SBart Van Assche op->pg_code = PG_CODE_ALL;
1313*44704f69SBart Van Assche if (op->do_all > 1)
1314*44704f69SBart Van Assche op->subpg_code = SPG_CODE_ALL;
1315*44704f69SBart Van Assche
1316*44704f69SBart Van Assche if (op->do_raw > 1) {
1317*44704f69SBart Van Assche if (op->do_all) {
1318*44704f69SBart Van Assche if (op->opt_new)
1319*44704f69SBart Van Assche pr2serr("'-R' requires a specific (sub)page, not all\n");
1320*44704f69SBart Van Assche else
1321*44704f69SBart Van Assche pr2serr("'-r' requires a specific (sub)page, not all\n");
1322*44704f69SBart Van Assche usage_for(op);
1323*44704f69SBart Van Assche ret = SG_LIB_CONTRADICT;
1324*44704f69SBart Van Assche goto fini;
1325*44704f69SBart Van Assche }
1326*44704f69SBart Van Assche }
1327*44704f69SBart Van Assche
1328*44704f69SBart Van Assche resid = 0;
1329*44704f69SBart Van Assche if (op->do_six) {
1330*44704f69SBart Van Assche res = sg_ll_mode_sense6(sg_fd, op->do_dbd, op->page_control,
1331*44704f69SBart Van Assche op->pg_code, op->subpg_code, rsp_buff,
1332*44704f69SBart Van Assche rsp_buff_sz, true, vb);
1333*44704f69SBart Van Assche if (SG_LIB_CAT_INVALID_OP == res)
1334*44704f69SBart Van Assche pr2serr(">>>>>> try again without the '-6' switch for a 10 byte "
1335*44704f69SBart Van Assche "MODE SENSE command\n");
1336*44704f69SBart Van Assche } else {
1337*44704f69SBart Van Assche res = sg_ll_mode_sense10_v2(sg_fd, op->do_llbaa, op->do_dbd,
1338*44704f69SBart Van Assche op->page_control, op->pg_code,
1339*44704f69SBart Van Assche op->subpg_code, rsp_buff, rsp_buff_sz,
1340*44704f69SBart Van Assche 0, &resid, true, vb);
1341*44704f69SBart Van Assche if (SG_LIB_CAT_INVALID_OP == res)
1342*44704f69SBart Van Assche pr2serr(">>>>>> try again with a '-6' switch for a 6 byte MODE "
1343*44704f69SBart Van Assche "SENSE command\n");
1344*44704f69SBart Van Assche }
1345*44704f69SBart Van Assche if (SG_LIB_CAT_ILLEGAL_REQ == res) {
1346*44704f69SBart Van Assche if (op->subpg_code > 0)
1347*44704f69SBart Van Assche pr2serr("invalid field in cdb (perhaps subpages not "
1348*44704f69SBart Van Assche "supported)\n");
1349*44704f69SBart Van Assche else if (op->page_control > 0)
1350*44704f69SBart Van Assche pr2serr("invalid field in cdb (perhaps page control (PC) not "
1351*44704f69SBart Van Assche "supported)\n");
1352*44704f69SBart Van Assche else
1353*44704f69SBart Van Assche pr2serr("invalid field in cdb (perhaps page 0x%x not "
1354*44704f69SBart Van Assche "supported)\n", op->pg_code);
1355*44704f69SBart Van Assche } else if (res) {
1356*44704f69SBart Van Assche sg_get_category_sense_str(res, sizeof(b), b, vb);
1357*44704f69SBart Van Assche pr2serr("%s\n", b);
1358*44704f69SBart Van Assche }
1359*44704f69SBart Van Assche ret = res;
1360*44704f69SBart Van Assche if (0 == res) {
1361*44704f69SBart Van Assche int medium_type, specific, headerlen;
1362*44704f69SBart Van Assche
1363*44704f69SBart Van Assche ret = 0;
1364*44704f69SBart Van Assche resp_mode6 = op->do_six;
1365*44704f69SBart Van Assche if (op->do_flexible) {
1366*44704f69SBart Van Assche num = rsp_buff[0];
1367*44704f69SBart Van Assche if (op->do_six && (num < 3))
1368*44704f69SBart Van Assche resp_mode6 = false;
1369*44704f69SBart Van Assche if ((! op->do_six) && (num > 5)) {
1370*44704f69SBart Van Assche if ((num > 11) && (0 == (num % 2)) && (0 == rsp_buff[4]) &&
1371*44704f69SBart Van Assche (0 == rsp_buff[5]) && (0 == rsp_buff[6])) {
1372*44704f69SBart Van Assche rsp_buff[1] = num;
1373*44704f69SBart Van Assche rsp_buff[0] = 0;
1374*44704f69SBart Van Assche pr2serr(">>> msense(10) but resp[0]=%d and not msense(6) "
1375*44704f69SBart Van Assche "response so fix length\n", num);
1376*44704f69SBart Van Assche } else
1377*44704f69SBart Van Assche resp_mode6 = true;
1378*44704f69SBart Van Assche }
1379*44704f69SBart Van Assche }
1380*44704f69SBart Van Assche cdbLenStr = resp_mode6 ? "6" : "10";
1381*44704f69SBart Van Assche if (op->do_raw || (1 == op->do_hex) || (op->do_hex > 2))
1382*44704f69SBart Van Assche ;
1383*44704f69SBart Van Assche else {
1384*44704f69SBart Van Assche if (resp_mode6 == op->do_six)
1385*44704f69SBart Van Assche printf("Mode parameter header from MODE SENSE(%s):\n",
1386*44704f69SBart Van Assche cdbLenStr);
1387*44704f69SBart Van Assche else
1388*44704f69SBart Van Assche printf(" >>> Mode parameter header from MODE SENSE(%s),\n"
1389*44704f69SBart Van Assche " decoded as %s byte response:\n",
1390*44704f69SBart Van Assche cdbLenStr, (resp_mode6 ? "6" : "10"));
1391*44704f69SBart Van Assche }
1392*44704f69SBart Van Assche rsp_buff_sz -= resid;
1393*44704f69SBart Van Assche if (rsp_buff_sz < 0) {
1394*44704f69SBart Van Assche pr2serr("MS(%s) resid=%d implies negative response length "
1395*44704f69SBart Van Assche "(%d)\n", cdbLenStr, resid, rsp_buff_sz);
1396*44704f69SBart Van Assche ret = SG_LIB_WILD_RESID;
1397*44704f69SBart Van Assche goto fini;
1398*44704f69SBart Van Assche }
1399*44704f69SBart Van Assche if (resp_mode6) {
1400*44704f69SBart Van Assche if (rsp_buff_sz < 4) {
1401*44704f69SBart Van Assche pr2serr("MS(6) resid=%d implies abridged header length "
1402*44704f69SBart Van Assche "(%d)\n", resid, rsp_buff_sz);
1403*44704f69SBart Van Assche ret = SG_LIB_WILD_RESID;
1404*44704f69SBart Van Assche goto fini;
1405*44704f69SBart Van Assche }
1406*44704f69SBart Van Assche headerlen = 4;
1407*44704f69SBart Van Assche medium_type = rsp_buff[1];
1408*44704f69SBart Van Assche specific = rsp_buff[2];
1409*44704f69SBart Van Assche longlba = false;
1410*44704f69SBart Van Assche } else { /* MODE SENSE(10) with resid */
1411*44704f69SBart Van Assche if (rsp_buff_sz < 8) {
1412*44704f69SBart Van Assche pr2serr("MS(10) resid=%d implies abridged header length "
1413*44704f69SBart Van Assche "(%d)\n", resid, rsp_buff_sz);
1414*44704f69SBart Van Assche ret = SG_LIB_WILD_RESID;
1415*44704f69SBart Van Assche goto fini;
1416*44704f69SBart Van Assche }
1417*44704f69SBart Van Assche headerlen = 8;
1418*44704f69SBart Van Assche medium_type = rsp_buff[2];
1419*44704f69SBart Van Assche specific = rsp_buff[3];
1420*44704f69SBart Van Assche longlba = !!(rsp_buff[4] & 1);
1421*44704f69SBart Van Assche }
1422*44704f69SBart Van Assche md_len = sg_msense_calc_length(rsp_buff, rsp_buff_sz, resp_mode6,
1423*44704f69SBart Van Assche &bd_len);
1424*44704f69SBart Van Assche if (md_len < 0) {
1425*44704f69SBart Van Assche pr2serr("MS(%s): sg_msense_calc_length() failed\n", cdbLenStr);
1426*44704f69SBart Van Assche ret = SG_LIB_CAT_MALFORMED;
1427*44704f69SBart Van Assche goto fini;
1428*44704f69SBart Van Assche }
1429*44704f69SBart Van Assche md_len = (md_len < rsp_buff_sz) ? md_len : rsp_buff_sz;
1430*44704f69SBart Van Assche if ((bd_len + headerlen) > md_len) {
1431*44704f69SBart Van Assche pr2serr("Invalid block descriptor length=%d, ignore\n", bd_len);
1432*44704f69SBart Van Assche bd_len = 0;
1433*44704f69SBart Van Assche }
1434*44704f69SBart Van Assche if (op->do_raw || (op->do_hex > 2)) {
1435*44704f69SBart Van Assche if (1 == op->do_raw)
1436*44704f69SBart Van Assche dStrRaw(rsp_buff, md_len);
1437*44704f69SBart Van Assche else if (op->do_raw > 1) {
1438*44704f69SBart Van Assche bp = rsp_buff + bd_len + headerlen;
1439*44704f69SBart Van Assche md_len -= bd_len + headerlen;
1440*44704f69SBart Van Assche spf = !!(bp[0] & 0x40);
1441*44704f69SBart Van Assche len = (spf ? (sg_get_unaligned_be16(bp + 2) + 4) :
1442*44704f69SBart Van Assche (bp[1] + 2));
1443*44704f69SBart Van Assche len = (len < md_len) ? len : md_len;
1444*44704f69SBart Van Assche for (k = 0; k < len; ++k)
1445*44704f69SBart Van Assche printf("%02x\n", bp[k]);
1446*44704f69SBart Van Assche } else
1447*44704f69SBart Van Assche hex2stdout(rsp_buff, md_len, -1);
1448*44704f69SBart Van Assche goto fini;
1449*44704f69SBart Van Assche }
1450*44704f69SBart Van Assche if (1 == op->do_hex) {
1451*44704f69SBart Van Assche hex2stdout(rsp_buff, md_len, 1);
1452*44704f69SBart Van Assche goto fini;
1453*44704f69SBart Van Assche } else if (op->do_hex > 1) {
1454*44704f69SBart Van Assche hex2stdout(rsp_buff, headerlen, 1);
1455*44704f69SBart Van Assche goto fini;
1456*44704f69SBart Van Assche }
1457*44704f69SBart Van Assche if ((PDT_DISK == inq_pdt) || (PDT_ZBC == inq_pdt))
1458*44704f69SBart Van Assche printf(" Mode data length=%d, medium type=0x%.2x, WP=%d,"
1459*44704f69SBart Van Assche " DpoFua=%d, longlba=%d\n", md_len, medium_type,
1460*44704f69SBart Van Assche !!(specific & 0x80), !!(specific & 0x10), (int)longlba);
1461*44704f69SBart Van Assche else
1462*44704f69SBart Van Assche printf(" Mode data length=%d, medium type=0x%.2x, specific"
1463*44704f69SBart Van Assche " param=0x%.2x, longlba=%d\n", md_len, medium_type,
1464*44704f69SBart Van Assche specific, (int)longlba);
1465*44704f69SBart Van Assche if (md_len > rsp_buff_sz) {
1466*44704f69SBart Van Assche printf("Only fetched %d bytes of response, truncate output\n",
1467*44704f69SBart Van Assche rsp_buff_sz);
1468*44704f69SBart Van Assche md_len = rsp_buff_sz;
1469*44704f69SBart Van Assche if (bd_len + headerlen > rsp_buff_sz)
1470*44704f69SBart Van Assche bd_len = rsp_buff_sz - headerlen;
1471*44704f69SBart Van Assche }
1472*44704f69SBart Van Assche if (! op->do_dbout) {
1473*44704f69SBart Van Assche printf(" Block descriptor length=%d\n", bd_len);
1474*44704f69SBart Van Assche if (bd_len > 0) {
1475*44704f69SBart Van Assche len = 8;
1476*44704f69SBart Van Assche density_code_off = 0;
1477*44704f69SBart Van Assche num = bd_len;
1478*44704f69SBart Van Assche if (longlba) {
1479*44704f69SBart Van Assche printf("> longlba direct access device block "
1480*44704f69SBart Van Assche "descriptors:\n");
1481*44704f69SBart Van Assche len = 16;
1482*44704f69SBart Van Assche density_code_off = 8;
1483*44704f69SBart Van Assche }
1484*44704f69SBart Van Assche else if ((PDT_DISK == inq_pdt) || (PDT_ZBC == inq_pdt)) {
1485*44704f69SBart Van Assche printf("> Direct access device block descriptors:\n");
1486*44704f69SBart Van Assche density_code_off = 4;
1487*44704f69SBart Van Assche }
1488*44704f69SBart Van Assche else
1489*44704f69SBart Van Assche printf("> General mode parameter block descriptors:\n");
1490*44704f69SBart Van Assche
1491*44704f69SBart Van Assche bp = rsp_buff + headerlen;
1492*44704f69SBart Van Assche while (num > 0) {
1493*44704f69SBart Van Assche printf(" Density code=0x%x\n",
1494*44704f69SBart Van Assche *(bp + density_code_off));
1495*44704f69SBart Van Assche hex2stdout(bp, len, 1);
1496*44704f69SBart Van Assche bp += len;
1497*44704f69SBart Van Assche num -= len;
1498*44704f69SBart Van Assche }
1499*44704f69SBart Van Assche printf("\n");
1500*44704f69SBart Van Assche }
1501*44704f69SBart Van Assche }
1502*44704f69SBart Van Assche bp = rsp_buff + bd_len + headerlen; /* start of mode page(s) */
1503*44704f69SBart Van Assche md_len -= bd_len + headerlen; /* length of mode page(s) */
1504*44704f69SBart Van Assche num_ua_pages = 0;
1505*44704f69SBart Van Assche for (k = 0; md_len > 0; ++k) { /* got mode page(s) */
1506*44704f69SBart Van Assche if ((k > 0) && (! op->do_all) &&
1507*44704f69SBart Van Assche (SPG_CODE_ALL != op->subpg_code)) {
1508*44704f69SBart Van Assche pr2serr("Unexpectedly received extra mode page responses, "
1509*44704f69SBart Van Assche "ignore\n");
1510*44704f69SBart Van Assche break;
1511*44704f69SBart Van Assche }
1512*44704f69SBart Van Assche uc = *bp;
1513*44704f69SBart Van Assche spf = !!(uc & 0x40);
1514*44704f69SBart Van Assche len = (spf ? (sg_get_unaligned_be16(bp + 2) + 4) : (bp[1] + 2));
1515*44704f69SBart Van Assche page_num = bp[0] & PG_CODE_MASK;
1516*44704f69SBart Van Assche if (0x0 == page_num) {
1517*44704f69SBart Van Assche ++num_ua_pages;
1518*44704f69SBart Van Assche if((num_ua_pages > 3) && (md_len > 0xa00)) {
1519*44704f69SBart Van Assche pr2serr(">>> Seen 3 unit attention pages (only one "
1520*44704f69SBart Van Assche "should be at end)\n and mpage length=%d, "
1521*44704f69SBart Van Assche "looks malformed, try '-f' option\n", md_len);
1522*44704f69SBart Van Assche break;
1523*44704f69SBart Van Assche }
1524*44704f69SBart Van Assche }
1525*44704f69SBart Van Assche if (op->do_hex) {
1526*44704f69SBart Van Assche if (spf)
1527*44704f69SBart Van Assche printf(">> page_code=0x%x, subpage_code=0x%x, page_cont"
1528*44704f69SBart Van Assche "rol=%d\n", page_num, bp[1], op->page_control);
1529*44704f69SBart Van Assche else
1530*44704f69SBart Van Assche printf(">> page_code=0x%x, page_control=%d\n", page_num,
1531*44704f69SBart Van Assche op->page_control);
1532*44704f69SBart Van Assche } else {
1533*44704f69SBart Van Assche descp = NULL;
1534*44704f69SBart Van Assche if ((0x18 == page_num) || (0x19 == page_num)) {
1535*44704f69SBart Van Assche t_proto = (spf ? bp[5] : bp[2]) & 0xf;
1536*44704f69SBart Van Assche descp = find_page_code_desc(page_num, (spf ? bp[1] : 0),
1537*44704f69SBart Van Assche inq_pdt, encserv, mchngr,
1538*44704f69SBart Van Assche t_proto);
1539*44704f69SBart Van Assche } else
1540*44704f69SBart Van Assche descp = find_page_code_desc(page_num, (spf ? bp[1] : 0),
1541*44704f69SBart Van Assche inq_pdt, encserv, mchngr, -1);
1542*44704f69SBart Van Assche if (NULL == descp) {
1543*44704f69SBart Van Assche if (spf)
1544*44704f69SBart Van Assche snprintf(ebuff, EBUFF_SZ, "0x%x, subpage_code: 0x%x",
1545*44704f69SBart Van Assche page_num, bp[1]);
1546*44704f69SBart Van Assche else
1547*44704f69SBart Van Assche snprintf(ebuff, EBUFF_SZ, "0x%x", page_num);
1548*44704f69SBart Van Assche }
1549*44704f69SBart Van Assche if (descp)
1550*44704f69SBart Van Assche printf(">> %s, page_control: %s\n", descp,
1551*44704f69SBart Van Assche pg_control_str_arr[op->page_control]);
1552*44704f69SBart Van Assche else
1553*44704f69SBart Van Assche printf(">> page_code: %s, page_control: %s\n", ebuff,
1554*44704f69SBart Van Assche pg_control_str_arr[op->page_control]);
1555*44704f69SBart Van Assche }
1556*44704f69SBart Van Assche num = (len > md_len) ? md_len : len;
1557*44704f69SBart Van Assche if ((k > 0) && (num > UNLIKELY_ABOVE_LEN)) {
1558*44704f69SBart Van Assche num = UNLIKELY_ABOVE_LEN;
1559*44704f69SBart Van Assche pr2serr(">>> page length (%d) > %d bytes, unlikely, trim\n"
1560*44704f69SBart Van Assche " Try '-f' option\n", len, num);
1561*44704f69SBart Van Assche }
1562*44704f69SBart Van Assche hex2stdout(bp, num , 1);
1563*44704f69SBart Van Assche bp += len;
1564*44704f69SBart Van Assche md_len -= len;
1565*44704f69SBart Van Assche }
1566*44704f69SBart Van Assche }
1567*44704f69SBart Van Assche
1568*44704f69SBart Van Assche fini:
1569*44704f69SBart Van Assche if (sg_fd >= 0)
1570*44704f69SBart Van Assche sg_cmds_close_device(sg_fd);
1571*44704f69SBart Van Assche if (free_rsp_buff)
1572*44704f69SBart Van Assche free(free_rsp_buff);
1573*44704f69SBart Van Assche if (0 == vb) {
1574*44704f69SBart Van Assche if (! sg_if_can2stderr("sg_modes failed: ", ret))
1575*44704f69SBart Van Assche pr2serr("Some error occurred, try again with '-v' or '-vv' for "
1576*44704f69SBart Van Assche "more information\n");
1577*44704f69SBart Van Assche }
1578*44704f69SBart Van Assche return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
1579*44704f69SBart Van Assche }
1580