diff options
Diffstat (limited to 'src/sg_get_config.c')
-rw-r--r-- | src/sg_get_config.c | 1145 |
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; +} |