• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2004-2018 Douglas Gilbert.
3  * All rights reserved.
4  * Use of this source code is governed by a BSD-style
5  * license that can be found in the BSD_LICENSE file.
6  *
7  * SPDX-License-Identifier: BSD-2-Clause
8  */
9 
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stdarg.h>
15 #include <stdbool.h>
16 #include <string.h>
17 #include <ctype.h>
18 #include <getopt.h>
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 #include "sg_lib.h"
24 #include "sg_cmds_basic.h"
25 #include "sg_cmds_mmc.h"
26 #include "sg_unaligned.h"
27 #include "sg_pr2serr.h"
28 
29 /* A utility program originally written for the Linux OS SCSI subsystem.
30  *
31  * This program outputs information provided by a SCSI "Get Configuration"
32    command [0x46] which is only defined for CD/DVDs (in MMC-2,3,4,5,6).
33 
34 */
35 
36 static const char * version_str = "0.49 20180626";    /* mmc6r02 */
37 
38 #define MX_ALLOC_LEN 8192
39 #define NAME_BUFF_SZ 64
40 
41 #define ME "sg_get_config: "
42 
43 
44 static uint8_t resp_buffer[MX_ALLOC_LEN];
45 
46 static struct option long_options[] = {
47         {"brief", no_argument, 0, 'b'},
48         {"current", no_argument, 0, 'c'},
49         {"help", no_argument, 0, 'h'},
50         {"hex", no_argument, 0, 'H'},
51         {"inner-hex", no_argument, 0, 'i'},
52         {"list", no_argument, 0, 'l'},
53         {"raw", no_argument, 0, 'R'},
54         {"readonly", no_argument, 0, 'q'},
55         {"rt", required_argument, 0, 'r'},
56         {"starting", required_argument, 0, 's'},
57         {"verbose", no_argument, 0, 'v'},
58         {"version", no_argument, 0, 'V'},
59         {0, 0, 0, 0},
60 };
61 
62 
63 static void
usage()64 usage()
65 {
66     pr2serr("Usage:  sg_get_config [--brief] [--current] [--help] [--hex] "
67             "[--inner-hex]\n"
68             "                      [--list] [--raw] [--readonly] [--rt=RT]\n"
69             "                      [--starting=FC] [--verbose] [--version] "
70             "DEVICE\n"
71             "  where:\n"
72             "    --brief|-b       only give feature names of DEVICE "
73             "(don't decode)\n"
74             "    --current|-c     equivalent to '--rt=1' (show "
75             "current)\n"
76             "    --help|-h        print usage message then exit\n"
77             "    --hex|-H         output response in hex\n"
78             "    --inner-hex|-i    decode to feature name, then output "
79             "features in hex\n"
80             "    --list|-l        list all known features + profiles "
81             "(ignore DEVICE)\n"
82             "    --raw|-R         output in binary (to stdout)\n"
83             "    --readonly|-q    open DEVICE read-only (def: open it "
84             "read-write)\n"
85             "    --rt=RT|-r RT    default value is 0\n"
86             "                     0 -> all feature descriptors (regardless "
87             "of currency)\n"
88             "                     1 -> all current feature descriptors\n"
89             "                     2 -> only feature descriptor matching "
90             "'starting'\n"
91             "    --starting=FC|-s FC    starting from feature "
92             "code (FC) value\n"
93             "    --verbose|-v     verbose\n"
94             "    --version|-V     output version string\n\n"
95             "Get configuration information for MMC drive and/or media\n");
96 }
97 
98 struct val_desc_t {
99         int val;
100         const char * desc;
101 };
102 
103 static struct val_desc_t profile_desc_arr[] = {
104         {0x0, "No current profile"},
105         {0x1, "Non-removable disk (obs)"},
106         {0x2, "Removable disk"},
107         {0x3, "Magneto optical erasable"},
108         {0x4, "Optical write once"},
109         {0x5, "AS-MO"},
110         {0x8, "CD-ROM"},
111         {0x9, "CD-R"},
112         {0xa, "CD-RW"},
113         {0x10, "DVD-ROM"},
114         {0x11, "DVD-R sequential recording"},
115         {0x12, "DVD-RAM"},
116         {0x13, "DVD-RW restricted overwrite"},
117         {0x14, "DVD-RW sequential recording"},
118         {0x15, "DVD-R dual layer sequental recording"},
119         {0x16, "DVD-R dual layer jump recording"},
120         {0x17, "DVD-RW dual layer"},
121         {0x18, "DVD-Download disc recording"},
122         {0x1a, "DVD+RW"},
123         {0x1b, "DVD+R"},
124         {0x20, "DDCD-ROM"},
125         {0x21, "DDCD-R"},
126         {0x22, "DDCD-RW"},
127         {0x2a, "DVD+RW dual layer"},
128         {0x2b, "DVD+R dual layer"},
129         {0x40, "BD-ROM"},
130         {0x41, "BD-R SRM"},
131         {0x42, "BD-R RRM"},
132         {0x43, "BD-RE"},
133         {0x50, "HD DVD-ROM"},
134         {0x51, "HD DVD-R"},
135         {0x52, "HD DVD-RAM"},
136         {0x53, "HD DVD-RW"},
137         {0x58, "HD DVD-R dual layer"},
138         {0x5a, "HD DVD-RW dual layer"},
139         {0xffff, "Non-conforming profile"},
140         {-1, NULL},
141 };
142 
143 static const char *
get_profile_str(int profile_num,char * buff)144 get_profile_str(int profile_num, char * buff)
145 {
146     const struct val_desc_t * pdp;
147 
148     for (pdp = profile_desc_arr; pdp->desc; ++pdp) {
149         if (pdp->val == profile_num) {
150             strcpy(buff, pdp->desc);
151             return buff;
152         }
153     }
154     snprintf(buff, 64, "0x%x", profile_num);
155     return buff;
156 }
157 
158 static struct val_desc_t feature_desc_arr[] = {
159         {0x0, "Profile list"},
160         {0x1, "Core"},
161         {0x2, "Morphing"},
162         {0x3, "Removable media"},
163         {0x4, "Write Protect"},
164         {0x10, "Random readable"},
165         {0x1d, "Multi-read"},
166         {0x1e, "CD read"},
167         {0x1f, "DVD read"},
168         {0x20, "Random writable"},
169         {0x21, "Incremental streaming writable"},
170         {0x22, "Sector erasable"},
171         {0x23, "Formattable"},
172         {0x24, "Hardware defect management"},
173         {0x25, "Write once"},
174         {0x26, "Restricted overwrite"},
175         {0x27, "CD-RW CAV write"},
176         {0x28, "MRW"},          /* Mount Rainier reWritable */
177         {0x29, "Enhanced defect reporting"},
178         {0x2a, "DVD+RW"},
179         {0x2b, "DVD+R"},
180         {0x2c, "Rigid restricted overwrite"},
181         {0x2d, "CD track-at-once"},
182         {0x2e, "CD mastering (session at once)"},
183         {0x2f, "DVD-R/-RW write"},
184         {0x30, "Double density CD read"},
185         {0x31, "Double density CD-R write"},
186         {0x32, "Double density CD-RW write"},
187         {0x33, "Layer jump recording"},
188         {0x34, "LJ rigid restricted oberwrite"},
189         {0x35, "Stop long operation"},
190         {0x37, "CD-RW media write support"},
191         {0x38, "BD-R POW"},
192         {0x3a, "DVD+RW dual layer"},
193         {0x3b, "DVD+R dual layer"},
194         {0x40, "BD read"},
195         {0x41, "BD write"},
196         {0x42, "TSR (timely safe recording)"},
197         {0x50, "HD DVD read"},
198         {0x51, "HD DVD write"},
199         {0x52, "HD DVD-RW fragment recording"},
200         {0x80, "Hybrid disc"},
201         {0x100, "Power management"},
202         {0x101, "SMART"},
203         {0x102, "Embedded changer"},
204         {0x103, "CD audio external play"},
205         {0x104, "Microcode upgrade"},
206         {0x105, "Timeout"},
207         {0x106, "DVD CSS"},
208         {0x107, "Real time streaming"},
209         {0x108, "Drive serial number"},
210         {0x109, "Media serial number"},
211         {0x10a, "Disc control blocks"},
212         {0x10b, "DVD CPRM"},
213         {0x10c, "Firmware information"},
214         {0x10d, "AACS"},
215         {0x10e, "DVD CSS managed recording"},
216         {0x110, "VCPS"},
217         {0x113, "SecurDisc"},
218         {0x120, "BD CPS"},
219         {0x142, "OSSC"},
220 };
221 
222 static const char *
get_feature_str(int feature_num,char * buff)223 get_feature_str(int feature_num, char * buff)
224 {
225     int k, num;
226 
227     num = SG_ARRAY_SIZE(feature_desc_arr);
228     for (k = 0; k < num; ++k) {
229         if (feature_desc_arr[k].val == feature_num) {
230             strcpy(buff, feature_desc_arr[k].desc);
231             return buff;
232         }
233     }
234     snprintf(buff, 64, "0x%x", feature_num);
235     return buff;
236 }
237 
238 static void
dStrRaw(const char * str,int len)239 dStrRaw(const char * str, int len)
240 {
241     int k;
242 
243     for (k = 0; k < len; ++k)
244         printf("%c", str[k]);
245 }
246 
247 static void
decode_feature(int feature,uint8_t * bp,int len)248 decode_feature(int feature, uint8_t * bp, int len)
249 {
250     int k, num, n, profile;
251     char buff[128];
252     const char * cp;
253 
254     switch (feature) {
255     case 0:     /* Profile list */
256         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
257                ((bp[2] >> 2) & 0xf), !!(bp[2] & 2), !!(bp[2] & 1),
258                feature);
259         printf("    available profiles [more recent typically higher "
260                "in list]:\n");
261         for (k = 4; k < len; k += 4) {
262             profile = sg_get_unaligned_be16(bp + k);
263             printf("      profile: %s , currentP=%d\n",
264                    get_profile_str(profile, buff), !!(bp[k + 2] & 1));
265         }
266         break;
267     case 1:     /* Core */
268         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
269                ((bp[2] >> 2) & 0xf), !!(bp[2] & 2), !!(bp[2] & 1),
270                feature);
271         if (len < 8) {
272             printf("      additional length [%d] too short\n", len - 4);
273             break;
274         }
275         num = sg_get_unaligned_be32(bp + 4);
276         switch (num) {
277         case 0: cp = "unspecified"; break;
278         case 1: cp = "SCSI family"; break;
279         case 2: cp = "ATAPI"; break;
280         case 3: cp = "IEEE 1394 - 1995"; break;
281         case 4: cp = "IEEE 1394A"; break;
282         case 5: cp = "Fibre channel"; break;
283         case 6: cp = "IEEE 1394B"; break;
284         case 7: cp = "Serial ATAPI"; break;
285         case 8: cp = "USB (both 1 and 2)"; break;
286         case 0xffff: cp = "vendor unique"; break;
287         default:
288             snprintf(buff, sizeof(buff), "[0x%x]", num);
289             cp = buff;
290             break;
291         }
292         printf("      Physical interface standard: %s", cp);
293         if (len > 8)
294             printf(", INQ2=%d, DBE=%d\n", !!(bp[8] & 2), !!(bp[8] & 1));
295         else
296             printf("\n");
297         break;
298     case 2:     /* Morphing */
299         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
300                ((bp[2] >> 2) & 0xf), !!(bp[2] & 2), !!(bp[2] & 1),
301                feature);
302         if (len < 8) {
303             printf("      additional length [%d] too short\n", len - 4);
304             break;
305         }
306         printf("      OCEvent=%d, ASYNC=%d\n", !!(bp[4] & 2), !!(bp[4] & 1));
307         break;
308     case 3:     /* Removable medium */
309         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
310                ((bp[2] >> 2) & 0xf), !!(bp[2] & 2), !!(bp[2] & 1),
311                feature);
312         if (len < 8) {
313             printf("      additional length [%d] too short\n", len - 4);
314             break;
315         }
316         num = (bp[4] >> 5) & 0x7;
317         switch (num) {
318         case 0: cp = "Caddy/slot type"; break;
319         case 1: cp = "Tray type"; break;
320         case 2: cp = "Pop-up type"; break;
321         case 4: cp = "Embedded changer with individually changeable discs";
322             break;
323         case 5: cp = "Embedded changer using a magazine"; break;
324         default:
325             snprintf(buff, sizeof(buff), "[0x%x]", num);
326             cp = buff;
327             break;
328         }
329         printf("      Loading mechanism: %s\n", cp);
330         printf("      Load=%d, Eject=%d, Prevent jumper=%d, Lock=%d\n",
331                !!(bp[4] & 0x10), !!(bp[4] & 0x8), !!(bp[4] & 0x4),
332                !!(bp[4] & 0x1));
333         break;
334     case 4:     /* Write protect */
335         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
336                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
337                feature);
338         if (len < 8) {
339             printf("      additional length [%d] too short\n", len - 4);
340             break;
341         }
342         printf("      DWP=%d, WDCB=%d, SPWP=%d, SSWPP=%d\n", !!(bp[4] & 0x8),
343                !!(bp[4] & 0x4), !!(bp[4] & 0x2), !!(bp[4] & 0x1));
344         break;
345     case 0x10:     /* Random readable */
346         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
347                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
348                feature);
349         if (len < 12) {
350             printf("      additional length [%d] too short\n", len - 4);
351             break;
352         }
353         num = sg_get_unaligned_be32(bp + 4);
354         printf("      Logical block size=0x%x, blocking=0x%x, PP=%d\n",
355                num, sg_get_unaligned_be16(bp + 8), !!(bp[10] & 0x1));
356         break;
357     case 0x1d:     /* Multi-read */
358     case 0x22:     /* Sector erasable */
359     case 0x26:     /* Restricted overwrite */
360     case 0x27:     /* CDRW CAV write */
361     case 0x35:     /* Stop long operation */
362     case 0x38:     /* BD-R pseudo-overwrite (POW) */
363     case 0x42:     /* TSR (timely safe recording) */
364     case 0x100:    /* Power management */
365     case 0x109:    /* Media serial number */
366     case 0x110:    /* VCPS */
367     case 0x113:    /* SecurDisc */
368         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
369                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
370                feature);
371         break;
372     case 0x1e:     /* CD read */
373         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
374                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
375                feature);
376         if (len < 8) {
377             printf("      additional length [%d] too short\n", len - 4);
378             break;
379         }
380         printf("      DAP=%d, C2 flags=%d, CD-Text=%d\n", !!(bp[4] & 0x80),
381                !!(bp[4] & 0x2), !!(bp[4] & 0x1));
382         break;
383     case 0x1f:     /* DVD read */
384         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
385                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
386                feature);
387         if (len > 7)
388             printf("      MULTI110=%d, Dual-RW=%d, Dual-R=%d\n",
389                    !!(bp[4] & 0x1), !!(bp[6] & 0x2), !!(bp[6] & 0x1));
390         break;
391     case 0x20:     /* Random writable */
392         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
393                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
394                feature);
395         if (len < 16) {
396             printf("      additional length [%d] too short\n", len - 4);
397             break;
398         }
399         num = sg_get_unaligned_be32(bp + 4);
400         n = sg_get_unaligned_be32(bp + 8);
401         printf("      Last lba=0x%x, Logical block size=0x%x, blocking=0x%x,"
402                " PP=%d\n", num, n, sg_get_unaligned_be16(bp + 12),
403                !!(bp[14] & 0x1));
404         break;
405     case 0x21:     /* Incremental streaming writable */
406         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
407                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
408                feature);
409         if (len < 8) {
410             printf("      additional length [%d] too short\n", len - 4);
411             break;
412         }
413         printf("      Data block types supported=0x%x, TRIO=%d, ARSV=%d, "
414                "BUF=%d\n", sg_get_unaligned_be16(bp + 4), !!(bp[6] & 0x4),
415                !!(bp[6] & 0x2), !!(bp[6] & 0x1));
416         num = bp[7];
417         printf("      Number of link sizes=%d\n", num);
418         for (k = 0; k < num; ++k)
419             printf("        %d\n", bp[8 + k]);
420         break;
421     /* case 0x22:     Sector erasable -> see 0x1d entry */
422     case 0x23:     /* Formattable */
423         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
424                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
425                feature);
426         if (len > 4)
427             printf("      BD-RE: RENoSA=%d, Expand=%d, QCert=%d, Cert=%d, "
428                    "FRF=%d\n", !!(bp[4] & 0x8), !!(bp[4] & 0x4),
429                    !!(bp[4] & 0x2), !!(bp[4] & 0x1), !!(bp[5] & 0x80));
430         if (len > 8)
431             printf("      BD-R: RRM=%d\n", !!(bp[8] & 0x1));
432         break;
433     case 0x24:     /* Hardware defect management */
434         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
435                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
436                feature);
437         if (len > 4)
438             printf("      SSA=%d\n", !!(bp[4] & 0x80));
439         break;
440     case 0x25:     /* Write once */
441         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
442                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
443                feature);
444         if (len < 12) {
445             printf("      additional length [%d] too short\n", len - 4);
446             break;
447         }
448         num = sg_get_unaligned_be16(bp + 4);
449         printf("      Logical block size=0x%x, blocking=0x%x, PP=%d\n",
450                num, sg_get_unaligned_be16(bp + 8), !!(bp[10] & 0x1));
451         break;
452     /* case 0x26:     Restricted overwrite -> see 0x1d entry */
453     /* case 0x27:     CDRW CAV write -> see 0x1d entry */
454     case 0x28:     /* MRW  (Mount Rainier reWriteable) */
455         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
456                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
457                feature);
458         if (len > 4)
459             printf("      DVD+Write=%d, DVD+Read=%d, Write=%d\n",
460                    !!(bp[4] & 0x4), !!(bp[4] & 0x2), !!(bp[4] & 0x1));
461         break;
462     case 0x29:     /* Enhanced defect reporting */
463         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
464                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
465                feature);
466         if (len < 8) {
467             printf("      additional length [%d] too short\n", len - 4);
468             break;
469         }
470         printf("      DRT-DM=%d, number of DBI cache zones=0x%x, number of "
471                "entries=0x%x\n", !!(bp[4] & 0x1), bp[5],
472                sg_get_unaligned_be16(bp + 6));
473         break;
474     case 0x2a:     /* DVD+RW */
475         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
476                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
477                feature);
478         if (len < 8) {
479             printf("      additional length [%d] too short\n", len - 4);
480             break;
481         }
482         printf("      Write=%d, Quick start=%d, Close only=%d\n",
483                !!(bp[4] & 0x1), !!(bp[5] & 0x2), !!(bp[5] & 0x1));
484         break;
485     case 0x2b:     /* DVD+R */
486         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
487                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
488                feature);
489         if (len < 8) {
490             printf("      additional length [%d] too short\n", len - 4);
491             break;
492         }
493         printf("      Write=%d\n", !!(bp[4] & 0x1));
494         break;
495     case 0x2c:     /* Rigid restricted overwrite */
496         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
497                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
498                feature);
499         if (len < 8) {
500             printf("      additional length [%d] too short\n", len - 4);
501             break;
502         }
503         printf("      DSDG=%d, DSDR=%d, Intermediate=%d, Blank=%d\n",
504                !!(bp[4] & 0x8), !!(bp[4] & 0x4), !!(bp[4] & 0x2),
505                !!(bp[4] & 0x1));
506         break;
507     case 0x2d:     /* CD Track at once */
508         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
509                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
510                feature);
511         if (len < 8) {
512             printf("      additional length [%d] too short\n", len - 4);
513             break;
514         }
515         printf("      BUF=%d, R-W raw=%d, R-W pack=%d, Test write=%d\n",
516                !!(bp[4] & 0x40), !!(bp[4] & 0x10), !!(bp[4] & 0x8),
517                !!(bp[4] & 0x4));
518         printf("      CD-RW=%d, R-W sub-code=%d, Data type supported=%d\n",
519                !!(bp[4] & 0x2), !!(bp[4] & 0x1),
520                sg_get_unaligned_be16(bp + 6));
521         break;
522     case 0x2e:     /* CD mastering (session at once) */
523         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
524                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
525                feature);
526         if (len < 8) {
527             printf("      additional length [%d] too short\n", len - 4);
528             break;
529         }
530         printf("      BUF=%d, SAO=%d, Raw MS=%d, Raw=%d\n",
531                !!(bp[4] & 0x40), !!(bp[4] & 0x20), !!(bp[4] & 0x10),
532                !!(bp[4] & 0x8));
533         printf("      Test write=%d, CD-RW=%d, R-W=%d\n",
534                !!(bp[4] & 0x4), !!(bp[4] & 0x2), !!(bp[4] & 0x1));
535         printf("      Maximum cue sheet length=0x%x\n",
536                sg_get_unaligned_be24(bp + 5));
537         break;
538     case 0x2f:     /* DVD-R/-RW write */
539         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
540                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
541                feature);
542         if (len < 8) {
543             printf("      additional length [%d] too short\n", len - 4);
544             break;
545         }
546         printf("      BUF=%d, RDL=%d, Test write=%d, DVD-RW SL=%d\n",
547                !!(bp[4] & 0x40), !!(bp[4] & 0x8), !!(bp[4] & 0x4),
548                !!(bp[4] & 0x2));
549         break;
550     case 0x33:     /* Layer jump recording */
551         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
552                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
553                feature);
554         if (len < 8) {
555             printf("      additional length [%d] too short\n", len - 4);
556             break;
557         }
558         num = bp[7];
559         printf("      Number of link sizes=%d\n", num);
560         for (k = 0; k < num; ++k)
561             printf("        %d\n", bp[8 + k]);
562         break;
563     case 0x34:     /* Layer jump rigid restricted overwrite */
564         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
565                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
566                feature);
567         if (len < 8) {
568             printf("      additional length [%d] too short\n", len - 4);
569             break;
570         }
571         printf("      CLJB=%d\n", !!(bp[4] & 0x1));
572         printf("      Buffer block size=%d\n", bp[7]);
573         break;
574     /* case 0x35:     Stop long operation -> see 0x1d entry */
575     case 0x37:     /* CD-RW media write support */
576         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
577                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
578                feature);
579         if (len < 8) {
580             printf("      additional length [%d] too short\n", len - 4);
581             break;
582         }
583         printf("      CD-RW media sub-type support (bitmask)=0x%x\n", bp[5]);
584         break;
585     /* case 0x38:     BD-R pseudo-overwrite (POW) -> see 0x1d entry */
586     case 0x3a:     /* DVD+RW dual layer */
587         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
588                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
589                feature);
590         if (len < 8) {
591             printf("      additional length [%d] too short\n", len - 4);
592             break;
593         }
594         printf("      write=%d, quick_start=%d, close_only=%d\n",
595                !!(bp[4] & 0x1), !!(bp[5] & 0x2), !!(bp[5] & 0x1));
596         break;
597     case 0x3b:     /* DVD+R dual layer */
598         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
599                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
600                feature);
601         if (len < 8) {
602             printf("      additional length [%d] too short\n", len - 4);
603             break;
604         }
605         printf("      write=%d\n", !!(bp[4] & 0x1));
606         break;
607     case 0x40:     /* BD Read */
608         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
609                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
610                feature);
611         if (len < 32) {
612             printf("      additional length [%d] too short\n", len - 4);
613             break;
614         }
615         printf("      Bitmaps for BD-RE read support:\n");
616         printf("        Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
617                "Class 3=0x%x\n", sg_get_unaligned_be16(bp + 8),
618                sg_get_unaligned_be16(bp + 10),
619                sg_get_unaligned_be16(bp + 12),
620                sg_get_unaligned_be16(bp + 14));
621         printf("      Bitmaps for BD-R read support:\n");
622         printf("        Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
623                "Class 3=0x%x\n", sg_get_unaligned_be16(bp + 16),
624                sg_get_unaligned_be16(bp + 18),
625                sg_get_unaligned_be16(bp + 20),
626                sg_get_unaligned_be16(bp + 22));
627         printf("      Bitmaps for BD-ROM read support:\n");
628         printf("        Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
629                "Class 3=0x%x\n", sg_get_unaligned_be16(bp + 24),
630                sg_get_unaligned_be16(bp + 26),
631                sg_get_unaligned_be16(bp + 28),
632                sg_get_unaligned_be16(bp + 30));
633         break;
634     case 0x41:     /* BD Write */
635         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
636                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
637                feature);
638         if (len < 32) {
639             printf("      additional length [%d] too short\n", len - 4);
640             break;
641         }
642         printf("      SVNR=%d\n", !!(bp[4] & 0x1));
643         printf("      Bitmaps for BD-RE write support:\n");
644         printf("        Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
645                "Class 3=0x%x\n", sg_get_unaligned_be16(bp + 8),
646                sg_get_unaligned_be16(bp + 10),
647                sg_get_unaligned_be16(bp + 12),
648                sg_get_unaligned_be16(bp + 14));
649         printf("      Bitmaps for BD-R write support:\n");
650         printf("        Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
651                "Class 3=0x%x\n", sg_get_unaligned_be16(bp + 16),
652                sg_get_unaligned_be16(bp + 18),
653                sg_get_unaligned_be16(bp + 20),
654                sg_get_unaligned_be16(bp + 22));
655         printf("      Bitmaps for BD-ROM write support:\n");
656         printf("        Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
657                "Class 3=0x%x\n", sg_get_unaligned_be16(bp + 24),
658                sg_get_unaligned_be16(bp + 26),
659                sg_get_unaligned_be16(bp + 28),
660                sg_get_unaligned_be16(bp + 30));
661         break;
662     /* case 0x42:     TSR (timely safe recording) -> see 0x1d entry */
663     case 0x50:     /* HD DVD Read */
664         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
665                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
666                feature);
667         if (len < 8) {
668             printf("      additional length [%d] too short\n", len - 4);
669             break;
670         }
671         printf("      HD DVD-R=%d, HD DVD-RAM=%d\n", !!(bp[4] & 0x1),
672                !!(bp[6] & 0x1));
673         break;
674     case 0x51:     /* HD DVD Write */
675         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
676                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
677                feature);
678         if (len < 8) {
679             printf("      additional length [%d] too short\n", len - 4);
680             break;
681         }
682         printf("      HD DVD-R=%d, HD DVD-RAM=%d\n", !!(bp[4] & 0x1),
683                !!(bp[6] & 0x1));
684         break;
685     case 0x52:     /* HD DVD-RW fragment recording */
686         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
687                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
688                feature);
689         if (len < 8) {
690             printf("      additional length [%d] too short\n", len - 4);
691             break;
692         }
693         printf("      BGP=%d\n", !!(bp[4] & 0x1));
694         break;
695     case 0x80:     /* Hybrid disc */
696         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
697                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
698                feature);
699         if (len < 8) {
700             printf("      additional length [%d] too short\n", len - 4);
701             break;
702         }
703         printf("      RI=%d\n", !!(bp[4] & 0x1));
704         break;
705     /* case 0x100:    Power management -> see 0x1d entry */
706     case 0x101:    /* SMART */
707         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
708                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
709                feature);
710         if (len < 8) {
711             printf("      additional length [%d] too short\n", len - 4);
712             break;
713         }
714         printf("      PP=%d\n", !!(bp[4] & 0x1));
715         break;
716     case 0x102:    /* Embedded changer */
717         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
718                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
719                feature);
720         if (len < 8) {
721             printf("      additional length [%d] too short\n", len - 4);
722             break;
723         }
724         printf("      SCC=%d, SDP=%d, highest slot number=%d\n",
725                !!(bp[4] & 0x10), !!(bp[4] & 0x4), (bp[7] & 0x1f));
726         break;
727     case 0x103:    /* CD audio external play (obsolete) */
728         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
729                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
730                feature);
731         if (len < 8) {
732             printf("      additional length [%d] too short\n", len - 4);
733             break;
734         }
735         printf("      Scan=%d, SCM=%d, SV=%d, number of volume levels=%d\n",
736                !!(bp[4] & 0x4), !!(bp[4] & 0x2), !!(bp[4] & 0x1),
737                sg_get_unaligned_be16(bp + 6));
738         break;
739     case 0x104:    /* Firmware upgrade */
740         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
741                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
742                feature);
743         if (len < 4) {
744             printf("      additional length [%d] too short\n", len - 4);
745             break;
746         }
747         if (len > 4)
748             printf("      M5=%d\n", !!(bp[4] & 0x1));
749         break;
750     case 0x105:    /* Timeout */
751         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
752                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
753                feature);
754         if (len > 7) {
755             printf("      Group 3=%d, unit length=%d\n",
756                    !!(bp[4] & 0x1), sg_get_unaligned_be16(bp + 6));
757         }
758         break;
759     case 0x106:    /* DVD CSS */
760         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
761                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
762                feature);
763         if (len < 8) {
764             printf("      additional length [%d] too short\n", len - 4);
765             break;
766         }
767         printf("      CSS version=%d\n", bp[7]);
768         break;
769     case 0x107:    /* Real time streaming */
770         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
771                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
772                feature);
773         if (len < 8) {
774             printf("      additional length [%d] too short\n", len - 4);
775             break;
776         }
777         printf("      RBCB=%d, SCS=%d, MP2A=%d, WSPD=%d, SW=%d\n",
778                !!(bp[4] & 0x10), !!(bp[4] & 0x8), !!(bp[4] & 0x4),
779                !!(bp[4] & 0x2), !!(bp[4] & 0x1));
780         break;
781     case 0x108:    /* Drive serial number */
782         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
783                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
784                feature);
785         num = len - 4;
786         n = sizeof(buff) - 1;
787         n = ((num < n) ? num : n);
788         strncpy(buff, (const char *)(bp + 4), n);
789         buff[n] = '\0';
790         printf("      Drive serial number: %s\n", buff);
791         break;
792     /* case 0x109:    Media serial number -> see 0x1d entry */
793     case 0x10a:    /* Disc control blocks */
794         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
795                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
796                feature);
797         printf("      Disc control blocks:\n");
798         for (k = 4; k < len; k += 4) {
799             printf("        0x%x\n", sg_get_unaligned_be32(bp + k));
800         }
801         break;
802     case 0x10b:    /* DVD CPRM */
803         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
804                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
805                feature);
806         if (len < 8) {
807             printf("      additional length [%d] too short\n", len - 4);
808             break;
809         }
810         printf("      CPRM version=%d\n", bp[7]);
811         break;
812     case 0x10c:    /* firmware information */
813         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
814                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
815                feature);
816         if (len < 20) {
817             printf("      additional length [%d] too short\n", len - 4);
818             break;
819         }
820         printf("      %.2s%.2s/%.2s/%.2s %.2s:%.2s:%.2s\n", bp + 4,
821                bp + 6, bp + 8, bp + 10, bp + 12, bp + 14, bp + 16);
822         break;
823     case 0x10d:    /* AACS */
824         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
825                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
826                feature);
827         if (len < 8) {
828             printf("      additional length [%d] too short\n", len - 4);
829             break;
830         }
831         printf("      BNG=%d, Block count for binding nonce=%d\n",
832                !!(bp[4] & 0x1), bp[5]);
833         printf("      Number of AGIDs=%d, AACS version=%d\n",
834                (bp[6] & 0xf), bp[7]);
835         break;
836     case 0x10e:    /* DVD CSS managed recording */
837         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
838                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
839                feature);
840         if (len < 8) {
841             printf("      additional length [%d] too short\n", len - 4);
842             break;
843         }
844         printf("      Maximum number of scrambled extent information "
845                "entries=%d\n", bp[4]);
846         break;
847     /* case 0x110:    VCPS -> see 0x1d entry */
848     /* case 0x113:    SecurDisc -> see 0x1d entry */
849     case 0x120:    /* BD CPS */
850         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
851                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
852                feature);
853         if (len < 8) {
854             printf("      additional length [%d] too short\n", len - 4);
855             break;
856         }
857         printf("      BD CPS major:minor version number=%d:%d, max open "
858                "SACs=%d\n", ((bp[5] >> 4) & 0xf), (bp[5] & 0xf),
859                bp[6] & 0x3);
860         break;
861     case 0x142:    /* OSSC (Optical Security Subsystem Class) */
862         printf("    version=%d, persist=%d, current=%d [0x%x]\n",
863                ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
864                feature);
865         if (len < 8) {
866             printf("      additional length [%d] too short\n", len - 4);
867             break;
868         }
869         printf("    PSAU=%d, LOSPB=%d, ME=%d\n", !!(bp[4] & 0x80),
870                !!(bp[4] & 0x40), !!(bp[4] & 0x1));
871         num = bp[5];
872         printf("      Profile numbers:\n");
873         for (k = 6; (num > 0) && (k < len); --num, k += 2) {
874             printf("        %u\n", sg_get_unaligned_be16(bp + k));
875         }
876         break;
877     default:
878         pr2serr("    Unknown feature [0x%x], version=%d persist=%d, "
879                 "current=%d\n", feature, ((bp[2] >> 2) & 0xf),
880                 !!(bp[2] & 0x2), !!(bp[2] & 0x1));
881         hex2stderr(bp, len, 1);
882         break;
883     }
884 }
885 
886 static void
decode_config(uint8_t * resp,int max_resp_len,int len,bool brief,bool inner_hex)887 decode_config(uint8_t * resp, int max_resp_len, int len, bool brief,
888               bool inner_hex)
889 {
890     int k, curr_profile, extra_len, feature;
891     uint8_t * bp;
892     char buff[128];
893 
894     if (max_resp_len < len) {
895         pr2serr("<<<warning: response to long for buffer, resp_len=%d>>>\n",
896                 len);
897             len = max_resp_len;
898     }
899     if (len < 8) {
900         pr2serr("response length too short: %d\n", len);
901         return;
902     }
903     curr_profile = sg_get_unaligned_be16(resp + 6);
904     if (0 == curr_profile)
905         pr2serr("No current profile\n");
906     else
907         printf("Current profile: %s\n", get_profile_str(curr_profile, buff));
908     printf("Features%s:\n", (brief ? " (in brief)" : ""));
909     bp = resp + 8;
910     len -= 8;
911     for (k = 0; k < len; k += extra_len, bp += extra_len) {
912         extra_len = 4 + bp[3];
913         feature = sg_get_unaligned_be16(bp + 0);
914         printf("  %s feature\n", get_feature_str(feature, buff));
915         if (brief)
916             continue;
917         if (inner_hex) {
918             hex2stdout(bp, extra_len, 1);
919             continue;
920         }
921         if (0 != (extra_len % 4))
922             printf("    additional length [%d] not a multiple of 4, ignore\n",
923                    extra_len - 4);
924         else
925             decode_feature(feature, bp, extra_len);
926     }
927 }
928 
929 static void
list_known(bool brief)930 list_known(bool brief)
931 {
932     int k, num;
933 
934     num = SG_ARRAY_SIZE(feature_desc_arr);
935     printf("Known features:\n");
936     for (k = 0; k < num; ++k)
937         printf("  %s [0x%x]\n", feature_desc_arr[k].desc,
938                feature_desc_arr[k].val);
939     if (! brief) {
940         printf("Known profiles:\n");
941         num = SG_ARRAY_SIZE(profile_desc_arr);
942         for (k = 0; k < num; ++k)
943             printf("  %s [0x%x]\n", profile_desc_arr[k].desc,
944                    profile_desc_arr[k].val);
945     }
946 }
947 
948 
949 int
main(int argc,char * argv[])950 main(int argc, char * argv[])
951 {
952     bool brief = false;
953     bool inner_hex = false;
954     bool list = false;
955     bool do_raw = false;
956     bool readonly = false;
957     bool verbose_given = false;
958     bool version_given = false;
959     int sg_fd, res, c, len;
960     int peri_type = 0;
961     int rt = 0;
962     int starting = 0;
963     int verbose = 0;
964     int do_hex = 0;
965     const char * device_name = NULL;
966     char buff[64];
967     const char * cp;
968     struct sg_simple_inquiry_resp inq_resp;
969     int ret = 0;
970 
971     while (1) {
972         int option_index = 0;
973 
974         c = getopt_long(argc, argv, "bchHilqr:Rs:vV", long_options,
975                         &option_index);
976         if (c == -1)
977             break;
978 
979         switch (c) {
980         case 'b':
981             brief = true;
982             break;
983         case 'c':
984             rt = 1;
985             break;
986         case 'h':
987         case '?':
988             usage();
989             return 0;
990         case 'H':
991             ++do_hex;
992             break;
993         case 'i':
994             inner_hex = true;
995             break;
996         case 'l':
997             list = true;
998             break;
999         case 'q':
1000             readonly = true;
1001             break;
1002         case 'r':
1003             rt = sg_get_num(optarg);
1004             if ((rt < 0) || (rt > 3)) {
1005                 pr2serr("bad argument to '--rt'\n");
1006                 return SG_LIB_SYNTAX_ERROR;
1007             }
1008             break;
1009         case 'R':
1010             do_raw = true;
1011             break;
1012         case 's':
1013             starting = sg_get_num(optarg);
1014             if ((starting < 0) || (starting > 0xffff)) {
1015                 pr2serr("bad argument to '--starting'\n");
1016                 return SG_LIB_SYNTAX_ERROR;
1017             }
1018             break;
1019         case 'v':
1020             verbose_given = true;
1021             ++verbose;
1022             break;
1023         case 'V':
1024             version_given = true;
1025             break;
1026         default:
1027             pr2serr("unrecognised option code 0x%x ??\n", c);
1028             usage();
1029             return SG_LIB_SYNTAX_ERROR;
1030         }
1031     }
1032     if (optind < argc) {
1033         if (NULL == device_name) {
1034             device_name = argv[optind];
1035             ++optind;
1036         }
1037         if (optind < argc) {
1038             for (; optind < argc; ++optind)
1039                 pr2serr("Unexpected extra argument: %s\n", argv[optind]);
1040             usage();
1041             return SG_LIB_SYNTAX_ERROR;
1042         }
1043     }
1044 #ifdef DEBUG
1045     pr2serr("In DEBUG mode, ");
1046     if (verbose_given && version_given) {
1047         pr2serr("but override: '-vV' given, zero verbose and continue\n");
1048         verbose_given = false;
1049         version_given = false;
1050         verbose = 0;
1051     } else if (! verbose_given) {
1052         pr2serr("set '-vv'\n");
1053         verbose = 2;
1054     } else
1055         pr2serr("keep verbose=%d\n", verbose);
1056 #else
1057     if (verbose_given && version_given)
1058         pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
1059 #endif
1060     if (version_given) {
1061         pr2serr(ME "version: %s\n", version_str);
1062         return 0;
1063     }
1064 
1065     if (list) {
1066         list_known(brief);
1067         return 0;
1068     }
1069     if (NULL == device_name) {
1070         pr2serr("missing device name!\n");
1071         usage();
1072         return SG_LIB_SYNTAX_ERROR;
1073     }
1074     if ((sg_fd = sg_cmds_open_device(device_name, true /* ro */, verbose))
1075         < 0) {
1076         pr2serr(ME "error opening file: %s (ro): %s\n", device_name,
1077                 safe_strerror(-sg_fd));
1078         return sg_convert_errno(-sg_fd);
1079     }
1080     if (0 == sg_simple_inquiry(sg_fd, &inq_resp, true, verbose)) {
1081         if (! do_raw)
1082             printf("  %.8s  %.16s  %.4s\n", inq_resp.vendor, inq_resp.product,
1083                    inq_resp.revision);
1084         peri_type = inq_resp.peripheral_type;
1085         cp = sg_get_pdt_str(peri_type, sizeof(buff), buff);
1086         if (! do_raw) {
1087             if (strlen(cp) > 0)
1088                 printf("  Peripheral device type: %s\n", cp);
1089             else
1090                 printf("  Peripheral device type: 0x%x\n", peri_type);
1091         }
1092     } else {
1093         pr2serr(ME "%s doesn't respond to a SCSI INQUIRY\n", device_name);
1094         return SG_LIB_CAT_OTHER;
1095     }
1096     sg_cmds_close_device(sg_fd);
1097 
1098     sg_fd = sg_cmds_open_device(device_name, readonly, verbose);
1099     if (sg_fd < 0) {
1100         pr2serr(ME "open error (rw): %s\n", safe_strerror(-sg_fd));
1101         return sg_convert_errno(-sg_fd);
1102     }
1103     if (do_raw) {
1104         if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
1105             perror("sg_set_binary_mode");
1106             return SG_LIB_FILE_ERROR;
1107         }
1108     }
1109 
1110     res = sg_ll_get_config(sg_fd, rt, starting, resp_buffer,
1111                               sizeof(resp_buffer), true, verbose);
1112     ret = res;
1113     if (0 == res) {
1114         len = sg_get_unaligned_be32(resp_buffer + 0) + 4;
1115         if (do_hex) {
1116             if (len > (int)sizeof(resp_buffer))
1117                 len = sizeof(resp_buffer);
1118             hex2stdout(resp_buffer, len, 0);
1119         } else if (do_raw)
1120             dStrRaw((const char *)resp_buffer, len);
1121         else
1122             decode_config(resp_buffer, sizeof(resp_buffer), len, brief,
1123                           inner_hex);
1124     } else {
1125         char b[80];
1126 
1127         sg_get_category_sense_str(res, sizeof(b), b, verbose);
1128         pr2serr("Get Configuration command: %s\n", b);
1129         if (0 == verbose)
1130             pr2serr("    try '-v' option for more information\n");
1131     }
1132 
1133     res = sg_cmds_close_device(sg_fd);
1134     if (res < 0) {
1135         pr2serr("close error: %s\n", safe_strerror(-res));
1136         if (0 == ret)
1137             ret = sg_convert_errno(-ret);
1138     }
1139     if (0 == verbose) {
1140         if (! sg_if_can2stderr("sg_get_config failed: ", ret))
1141             pr2serr("Some error occurred, try again with '-v' or '-vv' for "
1142                     "more information\n");
1143     }
1144     return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
1145 }
1146