/* * 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 #include #include #include #include #include #include #include #include #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("<<>>\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; }