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