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