aboutsummaryrefslogtreecommitdiff
path: root/src/sg_get_config.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sg_get_config.c')
-rw-r--r--src/sg_get_config.c1145
1 files changed, 1145 insertions, 0 deletions
diff --git a/src/sg_get_config.c b/src/sg_get_config.c
new file mode 100644
index 00000000..ad3bce9e
--- /dev/null
+++ b/src/sg_get_config.c
@@ -0,0 +1,1145 @@
+/*
+ * Copyright (c) 2004-2018 Douglas Gilbert.
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the BSD_LICENSE file.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "sg_lib.h"
+#include "sg_cmds_basic.h"
+#include "sg_cmds_mmc.h"
+#include "sg_unaligned.h"
+#include "sg_pr2serr.h"
+
+/* A utility program originally written for the Linux OS SCSI subsystem.
+ *
+ * This program outputs information provided by a SCSI "Get Configuration"
+ command [0x46] which is only defined for CD/DVDs (in MMC-2,3,4,5,6).
+
+*/
+
+static const char * version_str = "0.49 20180626"; /* mmc6r02 */
+
+#define MX_ALLOC_LEN 8192
+#define NAME_BUFF_SZ 64
+
+#define ME "sg_get_config: "
+
+
+static uint8_t resp_buffer[MX_ALLOC_LEN];
+
+static struct option long_options[] = {
+ {"brief", no_argument, 0, 'b'},
+ {"current", no_argument, 0, 'c'},
+ {"help", no_argument, 0, 'h'},
+ {"hex", no_argument, 0, 'H'},
+ {"inner-hex", no_argument, 0, 'i'},
+ {"list", no_argument, 0, 'l'},
+ {"raw", no_argument, 0, 'R'},
+ {"readonly", no_argument, 0, 'q'},
+ {"rt", required_argument, 0, 'r'},
+ {"starting", required_argument, 0, 's'},
+ {"verbose", no_argument, 0, 'v'},
+ {"version", no_argument, 0, 'V'},
+ {0, 0, 0, 0},
+};
+
+
+static void
+usage()
+{
+ pr2serr("Usage: sg_get_config [--brief] [--current] [--help] [--hex] "
+ "[--inner-hex]\n"
+ " [--list] [--raw] [--readonly] [--rt=RT]\n"
+ " [--starting=FC] [--verbose] [--version] "
+ "DEVICE\n"
+ " where:\n"
+ " --brief|-b only give feature names of DEVICE "
+ "(don't decode)\n"
+ " --current|-c equivalent to '--rt=1' (show "
+ "current)\n"
+ " --help|-h print usage message then exit\n"
+ " --hex|-H output response in hex\n"
+ " --inner-hex|-i decode to feature name, then output "
+ "features in hex\n"
+ " --list|-l list all known features + profiles "
+ "(ignore DEVICE)\n"
+ " --raw|-R output in binary (to stdout)\n"
+ " --readonly|-q open DEVICE read-only (def: open it "
+ "read-write)\n"
+ " --rt=RT|-r RT default value is 0\n"
+ " 0 -> all feature descriptors (regardless "
+ "of currency)\n"
+ " 1 -> all current feature descriptors\n"
+ " 2 -> only feature descriptor matching "
+ "'starting'\n"
+ " --starting=FC|-s FC starting from feature "
+ "code (FC) value\n"
+ " --verbose|-v verbose\n"
+ " --version|-V output version string\n\n"
+ "Get configuration information for MMC drive and/or media\n");
+}
+
+struct val_desc_t {
+ int val;
+ const char * desc;
+};
+
+static struct val_desc_t profile_desc_arr[] = {
+ {0x0, "No current profile"},
+ {0x1, "Non-removable disk (obs)"},
+ {0x2, "Removable disk"},
+ {0x3, "Magneto optical erasable"},
+ {0x4, "Optical write once"},
+ {0x5, "AS-MO"},
+ {0x8, "CD-ROM"},
+ {0x9, "CD-R"},
+ {0xa, "CD-RW"},
+ {0x10, "DVD-ROM"},
+ {0x11, "DVD-R sequential recording"},
+ {0x12, "DVD-RAM"},
+ {0x13, "DVD-RW restricted overwrite"},
+ {0x14, "DVD-RW sequential recording"},
+ {0x15, "DVD-R dual layer sequental recording"},
+ {0x16, "DVD-R dual layer jump recording"},
+ {0x17, "DVD-RW dual layer"},
+ {0x18, "DVD-Download disc recording"},
+ {0x1a, "DVD+RW"},
+ {0x1b, "DVD+R"},
+ {0x20, "DDCD-ROM"},
+ {0x21, "DDCD-R"},
+ {0x22, "DDCD-RW"},
+ {0x2a, "DVD+RW dual layer"},
+ {0x2b, "DVD+R dual layer"},
+ {0x40, "BD-ROM"},
+ {0x41, "BD-R SRM"},
+ {0x42, "BD-R RRM"},
+ {0x43, "BD-RE"},
+ {0x50, "HD DVD-ROM"},
+ {0x51, "HD DVD-R"},
+ {0x52, "HD DVD-RAM"},
+ {0x53, "HD DVD-RW"},
+ {0x58, "HD DVD-R dual layer"},
+ {0x5a, "HD DVD-RW dual layer"},
+ {0xffff, "Non-conforming profile"},
+ {-1, NULL},
+};
+
+static const char *
+get_profile_str(int profile_num, char * buff)
+{
+ const struct val_desc_t * pdp;
+
+ for (pdp = profile_desc_arr; pdp->desc; ++pdp) {
+ if (pdp->val == profile_num) {
+ strcpy(buff, pdp->desc);
+ return buff;
+ }
+ }
+ snprintf(buff, 64, "0x%x", profile_num);
+ return buff;
+}
+
+static struct val_desc_t feature_desc_arr[] = {
+ {0x0, "Profile list"},
+ {0x1, "Core"},
+ {0x2, "Morphing"},
+ {0x3, "Removable media"},
+ {0x4, "Write Protect"},
+ {0x10, "Random readable"},
+ {0x1d, "Multi-read"},
+ {0x1e, "CD read"},
+ {0x1f, "DVD read"},
+ {0x20, "Random writable"},
+ {0x21, "Incremental streaming writable"},
+ {0x22, "Sector erasable"},
+ {0x23, "Formattable"},
+ {0x24, "Hardware defect management"},
+ {0x25, "Write once"},
+ {0x26, "Restricted overwrite"},
+ {0x27, "CD-RW CAV write"},
+ {0x28, "MRW"}, /* Mount Rainier reWritable */
+ {0x29, "Enhanced defect reporting"},
+ {0x2a, "DVD+RW"},
+ {0x2b, "DVD+R"},
+ {0x2c, "Rigid restricted overwrite"},
+ {0x2d, "CD track-at-once"},
+ {0x2e, "CD mastering (session at once)"},
+ {0x2f, "DVD-R/-RW write"},
+ {0x30, "Double density CD read"},
+ {0x31, "Double density CD-R write"},
+ {0x32, "Double density CD-RW write"},
+ {0x33, "Layer jump recording"},
+ {0x34, "LJ rigid restricted oberwrite"},
+ {0x35, "Stop long operation"},
+ {0x37, "CD-RW media write support"},
+ {0x38, "BD-R POW"},
+ {0x3a, "DVD+RW dual layer"},
+ {0x3b, "DVD+R dual layer"},
+ {0x40, "BD read"},
+ {0x41, "BD write"},
+ {0x42, "TSR (timely safe recording)"},
+ {0x50, "HD DVD read"},
+ {0x51, "HD DVD write"},
+ {0x52, "HD DVD-RW fragment recording"},
+ {0x80, "Hybrid disc"},
+ {0x100, "Power management"},
+ {0x101, "SMART"},
+ {0x102, "Embedded changer"},
+ {0x103, "CD audio external play"},
+ {0x104, "Microcode upgrade"},
+ {0x105, "Timeout"},
+ {0x106, "DVD CSS"},
+ {0x107, "Real time streaming"},
+ {0x108, "Drive serial number"},
+ {0x109, "Media serial number"},
+ {0x10a, "Disc control blocks"},
+ {0x10b, "DVD CPRM"},
+ {0x10c, "Firmware information"},
+ {0x10d, "AACS"},
+ {0x10e, "DVD CSS managed recording"},
+ {0x110, "VCPS"},
+ {0x113, "SecurDisc"},
+ {0x120, "BD CPS"},
+ {0x142, "OSSC"},
+};
+
+static const char *
+get_feature_str(int feature_num, char * buff)
+{
+ int k, num;
+
+ num = SG_ARRAY_SIZE(feature_desc_arr);
+ for (k = 0; k < num; ++k) {
+ if (feature_desc_arr[k].val == feature_num) {
+ strcpy(buff, feature_desc_arr[k].desc);
+ return buff;
+ }
+ }
+ snprintf(buff, 64, "0x%x", feature_num);
+ return buff;
+}
+
+static void
+dStrRaw(const char * str, int len)
+{
+ int k;
+
+ for (k = 0; k < len; ++k)
+ printf("%c", str[k]);
+}
+
+static void
+decode_feature(int feature, uint8_t * bp, int len)
+{
+ int k, num, n, profile;
+ char buff[128];
+ const char * cp;
+
+ switch (feature) {
+ case 0: /* Profile list */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 2), !!(bp[2] & 1),
+ feature);
+ printf(" available profiles [more recent typically higher "
+ "in list]:\n");
+ for (k = 4; k < len; k += 4) {
+ profile = sg_get_unaligned_be16(bp + k);
+ printf(" profile: %s , currentP=%d\n",
+ get_profile_str(profile, buff), !!(bp[k + 2] & 1));
+ }
+ break;
+ case 1: /* Core */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 2), !!(bp[2] & 1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ num = sg_get_unaligned_be32(bp + 4);
+ switch (num) {
+ case 0: cp = "unspecified"; break;
+ case 1: cp = "SCSI family"; break;
+ case 2: cp = "ATAPI"; break;
+ case 3: cp = "IEEE 1394 - 1995"; break;
+ case 4: cp = "IEEE 1394A"; break;
+ case 5: cp = "Fibre channel"; break;
+ case 6: cp = "IEEE 1394B"; break;
+ case 7: cp = "Serial ATAPI"; break;
+ case 8: cp = "USB (both 1 and 2)"; break;
+ case 0xffff: cp = "vendor unique"; break;
+ default:
+ snprintf(buff, sizeof(buff), "[0x%x]", num);
+ cp = buff;
+ break;
+ }
+ printf(" Physical interface standard: %s", cp);
+ if (len > 8)
+ printf(", INQ2=%d, DBE=%d\n", !!(bp[8] & 2), !!(bp[8] & 1));
+ else
+ printf("\n");
+ break;
+ case 2: /* Morphing */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 2), !!(bp[2] & 1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" OCEvent=%d, ASYNC=%d\n", !!(bp[4] & 2), !!(bp[4] & 1));
+ break;
+ case 3: /* Removable medium */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 2), !!(bp[2] & 1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ num = (bp[4] >> 5) & 0x7;
+ switch (num) {
+ case 0: cp = "Caddy/slot type"; break;
+ case 1: cp = "Tray type"; break;
+ case 2: cp = "Pop-up type"; break;
+ case 4: cp = "Embedded changer with individually changeable discs";
+ break;
+ case 5: cp = "Embedded changer using a magazine"; break;
+ default:
+ snprintf(buff, sizeof(buff), "[0x%x]", num);
+ cp = buff;
+ break;
+ }
+ printf(" Loading mechanism: %s\n", cp);
+ printf(" Load=%d, Eject=%d, Prevent jumper=%d, Lock=%d\n",
+ !!(bp[4] & 0x10), !!(bp[4] & 0x8), !!(bp[4] & 0x4),
+ !!(bp[4] & 0x1));
+ break;
+ case 4: /* Write protect */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" DWP=%d, WDCB=%d, SPWP=%d, SSWPP=%d\n", !!(bp[4] & 0x8),
+ !!(bp[4] & 0x4), !!(bp[4] & 0x2), !!(bp[4] & 0x1));
+ break;
+ case 0x10: /* Random readable */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 12) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ num = sg_get_unaligned_be32(bp + 4);
+ printf(" Logical block size=0x%x, blocking=0x%x, PP=%d\n",
+ num, sg_get_unaligned_be16(bp + 8), !!(bp[10] & 0x1));
+ break;
+ case 0x1d: /* Multi-read */
+ case 0x22: /* Sector erasable */
+ case 0x26: /* Restricted overwrite */
+ case 0x27: /* CDRW CAV write */
+ case 0x35: /* Stop long operation */
+ case 0x38: /* BD-R pseudo-overwrite (POW) */
+ case 0x42: /* TSR (timely safe recording) */
+ case 0x100: /* Power management */
+ case 0x109: /* Media serial number */
+ case 0x110: /* VCPS */
+ case 0x113: /* SecurDisc */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ break;
+ case 0x1e: /* CD read */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" DAP=%d, C2 flags=%d, CD-Text=%d\n", !!(bp[4] & 0x80),
+ !!(bp[4] & 0x2), !!(bp[4] & 0x1));
+ break;
+ case 0x1f: /* DVD read */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len > 7)
+ printf(" MULTI110=%d, Dual-RW=%d, Dual-R=%d\n",
+ !!(bp[4] & 0x1), !!(bp[6] & 0x2), !!(bp[6] & 0x1));
+ break;
+ case 0x20: /* Random writable */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 16) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ num = sg_get_unaligned_be32(bp + 4);
+ n = sg_get_unaligned_be32(bp + 8);
+ printf(" Last lba=0x%x, Logical block size=0x%x, blocking=0x%x,"
+ " PP=%d\n", num, n, sg_get_unaligned_be16(bp + 12),
+ !!(bp[14] & 0x1));
+ break;
+ case 0x21: /* Incremental streaming writable */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" Data block types supported=0x%x, TRIO=%d, ARSV=%d, "
+ "BUF=%d\n", sg_get_unaligned_be16(bp + 4), !!(bp[6] & 0x4),
+ !!(bp[6] & 0x2), !!(bp[6] & 0x1));
+ num = bp[7];
+ printf(" Number of link sizes=%d\n", num);
+ for (k = 0; k < num; ++k)
+ printf(" %d\n", bp[8 + k]);
+ break;
+ /* case 0x22: Sector erasable -> see 0x1d entry */
+ case 0x23: /* Formattable */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len > 4)
+ printf(" BD-RE: RENoSA=%d, Expand=%d, QCert=%d, Cert=%d, "
+ "FRF=%d\n", !!(bp[4] & 0x8), !!(bp[4] & 0x4),
+ !!(bp[4] & 0x2), !!(bp[4] & 0x1), !!(bp[5] & 0x80));
+ if (len > 8)
+ printf(" BD-R: RRM=%d\n", !!(bp[8] & 0x1));
+ break;
+ case 0x24: /* Hardware defect management */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len > 4)
+ printf(" SSA=%d\n", !!(bp[4] & 0x80));
+ break;
+ case 0x25: /* Write once */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 12) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ num = sg_get_unaligned_be16(bp + 4);
+ printf(" Logical block size=0x%x, blocking=0x%x, PP=%d\n",
+ num, sg_get_unaligned_be16(bp + 8), !!(bp[10] & 0x1));
+ break;
+ /* case 0x26: Restricted overwrite -> see 0x1d entry */
+ /* case 0x27: CDRW CAV write -> see 0x1d entry */
+ case 0x28: /* MRW (Mount Rainier reWriteable) */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len > 4)
+ printf(" DVD+Write=%d, DVD+Read=%d, Write=%d\n",
+ !!(bp[4] & 0x4), !!(bp[4] & 0x2), !!(bp[4] & 0x1));
+ break;
+ case 0x29: /* Enhanced defect reporting */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" DRT-DM=%d, number of DBI cache zones=0x%x, number of "
+ "entries=0x%x\n", !!(bp[4] & 0x1), bp[5],
+ sg_get_unaligned_be16(bp + 6));
+ break;
+ case 0x2a: /* DVD+RW */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" Write=%d, Quick start=%d, Close only=%d\n",
+ !!(bp[4] & 0x1), !!(bp[5] & 0x2), !!(bp[5] & 0x1));
+ break;
+ case 0x2b: /* DVD+R */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" Write=%d\n", !!(bp[4] & 0x1));
+ break;
+ case 0x2c: /* Rigid restricted overwrite */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" DSDG=%d, DSDR=%d, Intermediate=%d, Blank=%d\n",
+ !!(bp[4] & 0x8), !!(bp[4] & 0x4), !!(bp[4] & 0x2),
+ !!(bp[4] & 0x1));
+ break;
+ case 0x2d: /* CD Track at once */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" BUF=%d, R-W raw=%d, R-W pack=%d, Test write=%d\n",
+ !!(bp[4] & 0x40), !!(bp[4] & 0x10), !!(bp[4] & 0x8),
+ !!(bp[4] & 0x4));
+ printf(" CD-RW=%d, R-W sub-code=%d, Data type supported=%d\n",
+ !!(bp[4] & 0x2), !!(bp[4] & 0x1),
+ sg_get_unaligned_be16(bp + 6));
+ break;
+ case 0x2e: /* CD mastering (session at once) */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" BUF=%d, SAO=%d, Raw MS=%d, Raw=%d\n",
+ !!(bp[4] & 0x40), !!(bp[4] & 0x20), !!(bp[4] & 0x10),
+ !!(bp[4] & 0x8));
+ printf(" Test write=%d, CD-RW=%d, R-W=%d\n",
+ !!(bp[4] & 0x4), !!(bp[4] & 0x2), !!(bp[4] & 0x1));
+ printf(" Maximum cue sheet length=0x%x\n",
+ sg_get_unaligned_be24(bp + 5));
+ break;
+ case 0x2f: /* DVD-R/-RW write */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" BUF=%d, RDL=%d, Test write=%d, DVD-RW SL=%d\n",
+ !!(bp[4] & 0x40), !!(bp[4] & 0x8), !!(bp[4] & 0x4),
+ !!(bp[4] & 0x2));
+ break;
+ case 0x33: /* Layer jump recording */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ num = bp[7];
+ printf(" Number of link sizes=%d\n", num);
+ for (k = 0; k < num; ++k)
+ printf(" %d\n", bp[8 + k]);
+ break;
+ case 0x34: /* Layer jump rigid restricted overwrite */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" CLJB=%d\n", !!(bp[4] & 0x1));
+ printf(" Buffer block size=%d\n", bp[7]);
+ break;
+ /* case 0x35: Stop long operation -> see 0x1d entry */
+ case 0x37: /* CD-RW media write support */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" CD-RW media sub-type support (bitmask)=0x%x\n", bp[5]);
+ break;
+ /* case 0x38: BD-R pseudo-overwrite (POW) -> see 0x1d entry */
+ case 0x3a: /* DVD+RW dual layer */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" write=%d, quick_start=%d, close_only=%d\n",
+ !!(bp[4] & 0x1), !!(bp[5] & 0x2), !!(bp[5] & 0x1));
+ break;
+ case 0x3b: /* DVD+R dual layer */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" write=%d\n", !!(bp[4] & 0x1));
+ break;
+ case 0x40: /* BD Read */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 32) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" Bitmaps for BD-RE read support:\n");
+ printf(" Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
+ "Class 3=0x%x\n", sg_get_unaligned_be16(bp + 8),
+ sg_get_unaligned_be16(bp + 10),
+ sg_get_unaligned_be16(bp + 12),
+ sg_get_unaligned_be16(bp + 14));
+ printf(" Bitmaps for BD-R read support:\n");
+ printf(" Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
+ "Class 3=0x%x\n", sg_get_unaligned_be16(bp + 16),
+ sg_get_unaligned_be16(bp + 18),
+ sg_get_unaligned_be16(bp + 20),
+ sg_get_unaligned_be16(bp + 22));
+ printf(" Bitmaps for BD-ROM read support:\n");
+ printf(" Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
+ "Class 3=0x%x\n", sg_get_unaligned_be16(bp + 24),
+ sg_get_unaligned_be16(bp + 26),
+ sg_get_unaligned_be16(bp + 28),
+ sg_get_unaligned_be16(bp + 30));
+ break;
+ case 0x41: /* BD Write */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 32) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" SVNR=%d\n", !!(bp[4] & 0x1));
+ printf(" Bitmaps for BD-RE write support:\n");
+ printf(" Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
+ "Class 3=0x%x\n", sg_get_unaligned_be16(bp + 8),
+ sg_get_unaligned_be16(bp + 10),
+ sg_get_unaligned_be16(bp + 12),
+ sg_get_unaligned_be16(bp + 14));
+ printf(" Bitmaps for BD-R write support:\n");
+ printf(" Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
+ "Class 3=0x%x\n", sg_get_unaligned_be16(bp + 16),
+ sg_get_unaligned_be16(bp + 18),
+ sg_get_unaligned_be16(bp + 20),
+ sg_get_unaligned_be16(bp + 22));
+ printf(" Bitmaps for BD-ROM write support:\n");
+ printf(" Class 0=0x%x, Class 1=0x%x, Class 2=0x%x, "
+ "Class 3=0x%x\n", sg_get_unaligned_be16(bp + 24),
+ sg_get_unaligned_be16(bp + 26),
+ sg_get_unaligned_be16(bp + 28),
+ sg_get_unaligned_be16(bp + 30));
+ break;
+ /* case 0x42: TSR (timely safe recording) -> see 0x1d entry */
+ case 0x50: /* HD DVD Read */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" HD DVD-R=%d, HD DVD-RAM=%d\n", !!(bp[4] & 0x1),
+ !!(bp[6] & 0x1));
+ break;
+ case 0x51: /* HD DVD Write */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" HD DVD-R=%d, HD DVD-RAM=%d\n", !!(bp[4] & 0x1),
+ !!(bp[6] & 0x1));
+ break;
+ case 0x52: /* HD DVD-RW fragment recording */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" BGP=%d\n", !!(bp[4] & 0x1));
+ break;
+ case 0x80: /* Hybrid disc */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" RI=%d\n", !!(bp[4] & 0x1));
+ break;
+ /* case 0x100: Power management -> see 0x1d entry */
+ case 0x101: /* SMART */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" PP=%d\n", !!(bp[4] & 0x1));
+ break;
+ case 0x102: /* Embedded changer */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" SCC=%d, SDP=%d, highest slot number=%d\n",
+ !!(bp[4] & 0x10), !!(bp[4] & 0x4), (bp[7] & 0x1f));
+ break;
+ case 0x103: /* CD audio external play (obsolete) */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" Scan=%d, SCM=%d, SV=%d, number of volume levels=%d\n",
+ !!(bp[4] & 0x4), !!(bp[4] & 0x2), !!(bp[4] & 0x1),
+ sg_get_unaligned_be16(bp + 6));
+ break;
+ case 0x104: /* Firmware upgrade */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 4) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ if (len > 4)
+ printf(" M5=%d\n", !!(bp[4] & 0x1));
+ break;
+ case 0x105: /* Timeout */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len > 7) {
+ printf(" Group 3=%d, unit length=%d\n",
+ !!(bp[4] & 0x1), sg_get_unaligned_be16(bp + 6));
+ }
+ break;
+ case 0x106: /* DVD CSS */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" CSS version=%d\n", bp[7]);
+ break;
+ case 0x107: /* Real time streaming */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" RBCB=%d, SCS=%d, MP2A=%d, WSPD=%d, SW=%d\n",
+ !!(bp[4] & 0x10), !!(bp[4] & 0x8), !!(bp[4] & 0x4),
+ !!(bp[4] & 0x2), !!(bp[4] & 0x1));
+ break;
+ case 0x108: /* Drive serial number */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ num = len - 4;
+ n = sizeof(buff) - 1;
+ n = ((num < n) ? num : n);
+ strncpy(buff, (const char *)(bp + 4), n);
+ buff[n] = '\0';
+ printf(" Drive serial number: %s\n", buff);
+ break;
+ /* case 0x109: Media serial number -> see 0x1d entry */
+ case 0x10a: /* Disc control blocks */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ printf(" Disc control blocks:\n");
+ for (k = 4; k < len; k += 4) {
+ printf(" 0x%x\n", sg_get_unaligned_be32(bp + k));
+ }
+ break;
+ case 0x10b: /* DVD CPRM */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" CPRM version=%d\n", bp[7]);
+ break;
+ case 0x10c: /* firmware information */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 20) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" %.2s%.2s/%.2s/%.2s %.2s:%.2s:%.2s\n", bp + 4,
+ bp + 6, bp + 8, bp + 10, bp + 12, bp + 14, bp + 16);
+ break;
+ case 0x10d: /* AACS */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" BNG=%d, Block count for binding nonce=%d\n",
+ !!(bp[4] & 0x1), bp[5]);
+ printf(" Number of AGIDs=%d, AACS version=%d\n",
+ (bp[6] & 0xf), bp[7]);
+ break;
+ case 0x10e: /* DVD CSS managed recording */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" Maximum number of scrambled extent information "
+ "entries=%d\n", bp[4]);
+ break;
+ /* case 0x110: VCPS -> see 0x1d entry */
+ /* case 0x113: SecurDisc -> see 0x1d entry */
+ case 0x120: /* BD CPS */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" BD CPS major:minor version number=%d:%d, max open "
+ "SACs=%d\n", ((bp[5] >> 4) & 0xf), (bp[5] & 0xf),
+ bp[6] & 0x3);
+ break;
+ case 0x142: /* OSSC (Optical Security Subsystem Class) */
+ printf(" version=%d, persist=%d, current=%d [0x%x]\n",
+ ((bp[2] >> 2) & 0xf), !!(bp[2] & 0x2), !!(bp[2] & 0x1),
+ feature);
+ if (len < 8) {
+ printf(" additional length [%d] too short\n", len - 4);
+ break;
+ }
+ printf(" PSAU=%d, LOSPB=%d, ME=%d\n", !!(bp[4] & 0x80),
+ !!(bp[4] & 0x40), !!(bp[4] & 0x1));
+ num = bp[5];
+ printf(" Profile numbers:\n");
+ for (k = 6; (num > 0) && (k < len); --num, k += 2) {
+ printf(" %u\n", sg_get_unaligned_be16(bp + k));
+ }
+ break;
+ default:
+ pr2serr(" Unknown feature [0x%x], version=%d persist=%d, "
+ "current=%d\n", feature, ((bp[2] >> 2) & 0xf),
+ !!(bp[2] & 0x2), !!(bp[2] & 0x1));
+ hex2stderr(bp, len, 1);
+ break;
+ }
+}
+
+static void
+decode_config(uint8_t * resp, int max_resp_len, int len, bool brief,
+ bool inner_hex)
+{
+ int k, curr_profile, extra_len, feature;
+ uint8_t * bp;
+ char buff[128];
+
+ if (max_resp_len < len) {
+ pr2serr("<<<warning: response to long for buffer, resp_len=%d>>>\n",
+ len);
+ len = max_resp_len;
+ }
+ if (len < 8) {
+ pr2serr("response length too short: %d\n", len);
+ return;
+ }
+ curr_profile = sg_get_unaligned_be16(resp + 6);
+ if (0 == curr_profile)
+ pr2serr("No current profile\n");
+ else
+ printf("Current profile: %s\n", get_profile_str(curr_profile, buff));
+ printf("Features%s:\n", (brief ? " (in brief)" : ""));
+ bp = resp + 8;
+ len -= 8;
+ for (k = 0; k < len; k += extra_len, bp += extra_len) {
+ extra_len = 4 + bp[3];
+ feature = sg_get_unaligned_be16(bp + 0);
+ printf(" %s feature\n", get_feature_str(feature, buff));
+ if (brief)
+ continue;
+ if (inner_hex) {
+ hex2stdout(bp, extra_len, 1);
+ continue;
+ }
+ if (0 != (extra_len % 4))
+ printf(" additional length [%d] not a multiple of 4, ignore\n",
+ extra_len - 4);
+ else
+ decode_feature(feature, bp, extra_len);
+ }
+}
+
+static void
+list_known(bool brief)
+{
+ int k, num;
+
+ num = SG_ARRAY_SIZE(feature_desc_arr);
+ printf("Known features:\n");
+ for (k = 0; k < num; ++k)
+ printf(" %s [0x%x]\n", feature_desc_arr[k].desc,
+ feature_desc_arr[k].val);
+ if (! brief) {
+ printf("Known profiles:\n");
+ num = SG_ARRAY_SIZE(profile_desc_arr);
+ for (k = 0; k < num; ++k)
+ printf(" %s [0x%x]\n", profile_desc_arr[k].desc,
+ profile_desc_arr[k].val);
+ }
+}
+
+
+int
+main(int argc, char * argv[])
+{
+ bool brief = false;
+ bool inner_hex = false;
+ bool list = false;
+ bool do_raw = false;
+ bool readonly = false;
+ bool verbose_given = false;
+ bool version_given = false;
+ int sg_fd, res, c, len;
+ int peri_type = 0;
+ int rt = 0;
+ int starting = 0;
+ int verbose = 0;
+ int do_hex = 0;
+ const char * device_name = NULL;
+ char buff[64];
+ const char * cp;
+ struct sg_simple_inquiry_resp inq_resp;
+ int ret = 0;
+
+ while (1) {
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "bchHilqr:Rs:vV", long_options,
+ &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'b':
+ brief = true;
+ break;
+ case 'c':
+ rt = 1;
+ break;
+ case 'h':
+ case '?':
+ usage();
+ return 0;
+ case 'H':
+ ++do_hex;
+ break;
+ case 'i':
+ inner_hex = true;
+ break;
+ case 'l':
+ list = true;
+ break;
+ case 'q':
+ readonly = true;
+ break;
+ case 'r':
+ rt = sg_get_num(optarg);
+ if ((rt < 0) || (rt > 3)) {
+ pr2serr("bad argument to '--rt'\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'R':
+ do_raw = true;
+ break;
+ case 's':
+ starting = sg_get_num(optarg);
+ if ((starting < 0) || (starting > 0xffff)) {
+ pr2serr("bad argument to '--starting'\n");
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ break;
+ case 'v':
+ verbose_given = true;
+ ++verbose;
+ break;
+ case 'V':
+ version_given = true;
+ break;
+ default:
+ pr2serr("unrecognised option code 0x%x ??\n", c);
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+ if (optind < argc) {
+ if (NULL == device_name) {
+ device_name = argv[optind];
+ ++optind;
+ }
+ if (optind < argc) {
+ for (; optind < argc; ++optind)
+ pr2serr("Unexpected extra argument: %s\n", argv[optind]);
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ }
+#ifdef DEBUG
+ pr2serr("In DEBUG mode, ");
+ if (verbose_given && version_given) {
+ pr2serr("but override: '-vV' given, zero verbose and continue\n");
+ verbose_given = false;
+ version_given = false;
+ verbose = 0;
+ } else if (! verbose_given) {
+ pr2serr("set '-vv'\n");
+ verbose = 2;
+ } else
+ pr2serr("keep verbose=%d\n", verbose);
+#else
+ if (verbose_given && version_given)
+ pr2serr("Not in DEBUG mode, so '-vV' has no special action\n");
+#endif
+ if (version_given) {
+ pr2serr(ME "version: %s\n", version_str);
+ return 0;
+ }
+
+ if (list) {
+ list_known(brief);
+ return 0;
+ }
+ if (NULL == device_name) {
+ pr2serr("missing device name!\n");
+ usage();
+ return SG_LIB_SYNTAX_ERROR;
+ }
+ if ((sg_fd = sg_cmds_open_device(device_name, true /* ro */, verbose))
+ < 0) {
+ pr2serr(ME "error opening file: %s (ro): %s\n", device_name,
+ safe_strerror(-sg_fd));
+ return sg_convert_errno(-sg_fd);
+ }
+ if (0 == sg_simple_inquiry(sg_fd, &inq_resp, true, verbose)) {
+ if (! do_raw)
+ printf(" %.8s %.16s %.4s\n", inq_resp.vendor, inq_resp.product,
+ inq_resp.revision);
+ peri_type = inq_resp.peripheral_type;
+ cp = sg_get_pdt_str(peri_type, sizeof(buff), buff);
+ if (! do_raw) {
+ if (strlen(cp) > 0)
+ printf(" Peripheral device type: %s\n", cp);
+ else
+ printf(" Peripheral device type: 0x%x\n", peri_type);
+ }
+ } else {
+ pr2serr(ME "%s doesn't respond to a SCSI INQUIRY\n", device_name);
+ return SG_LIB_CAT_OTHER;
+ }
+ sg_cmds_close_device(sg_fd);
+
+ sg_fd = sg_cmds_open_device(device_name, readonly, verbose);
+ if (sg_fd < 0) {
+ pr2serr(ME "open error (rw): %s\n", safe_strerror(-sg_fd));
+ return sg_convert_errno(-sg_fd);
+ }
+ if (do_raw) {
+ if (sg_set_binary_mode(STDOUT_FILENO) < 0) {
+ perror("sg_set_binary_mode");
+ return SG_LIB_FILE_ERROR;
+ }
+ }
+
+ res = sg_ll_get_config(sg_fd, rt, starting, resp_buffer,
+ sizeof(resp_buffer), true, verbose);
+ ret = res;
+ if (0 == res) {
+ len = sg_get_unaligned_be32(resp_buffer + 0) + 4;
+ if (do_hex) {
+ if (len > (int)sizeof(resp_buffer))
+ len = sizeof(resp_buffer);
+ hex2stdout(resp_buffer, len, 0);
+ } else if (do_raw)
+ dStrRaw((const char *)resp_buffer, len);
+ else
+ decode_config(resp_buffer, sizeof(resp_buffer), len, brief,
+ inner_hex);
+ } else {
+ char b[80];
+
+ sg_get_category_sense_str(res, sizeof(b), b, verbose);
+ pr2serr("Get Configuration command: %s\n", b);
+ if (0 == verbose)
+ pr2serr(" try '-v' option for more information\n");
+ }
+
+ res = sg_cmds_close_device(sg_fd);
+ if (res < 0) {
+ pr2serr("close error: %s\n", safe_strerror(-res));
+ if (0 == ret)
+ ret = sg_convert_errno(-ret);
+ }
+ if (0 == verbose) {
+ if (! sg_if_can2stderr("sg_get_config failed: ", ret))
+ pr2serr("Some error occurred, try again with '-v' or '-vv' for "
+ "more information\n");
+ }
+ return (ret >= 0) ? ret : SG_LIB_CAT_OTHER;
+}