blob: 5e7f37df6a6b9fe6a63cd966276fc42b216bd536 [file] [log] [blame]
/*
* Copyright (c) 2004-2005 Douglas Gilbert.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "sg_include.h"
#include "sg_lib.h"
#include "sg_cmds.h"
/* A utility program 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).
*/
static char * version_str = "0.16 20050324";
#define SENSE_BUFF_LEN 32 /* Arbitrary, could be larger */
#define DEF_TIMEOUT 60000 /* 60,000 millisecs == 60 seconds */
#define GET_CONFIG_CMD 0x46
#define GET_CONFIG_CMD_LEN 10
#define MX_ALLOC_LEN 8192
#define NAME_BUFF_SZ 64
#define EBUFF_SZ 256
#define ME "sg_get_config: "
static unsigned char resp_buffer[MX_ALLOC_LEN];
static char ebuff[EBUFF_SZ];
static struct option long_options[] = {
{"brief", 0, 0, '1'},
{"help", 0, 0, 'h'},
{"hex", 0, 0, 'H'},
{"inner-hex", 0, 0, 'i'},
{"list", 0, 0, 'l'},
{"rt", 1, 0, 'r'},
{"starting", 1, 0, 's'},
{"verbose", 0, 0, 'v'},
{"version", 0, 0, 'V'},
{0, 0, 0, 0},
};
/* Returns 0 when successful, SG_LIB_CAT_INVALID_OP if command not
supported, SG_LIB_CAT_ILLEGAL_REQ if field in cdb not supported,
else -1 */
static int sg_ll_get_config(int sg_fd, int rt, int starting, void * resp,
int mx_resp_len, int noisy, int verbose)
{
int res, k;
unsigned char gcCmdBlk[GET_CONFIG_CMD_LEN] = {GET_CONFIG_CMD, 0, 0, 0,
0, 0, 0, 0, 0, 0};
unsigned char sense_b[SENSE_BUFF_LEN];
struct sg_io_hdr io_hdr;
if ((rt < 0) || (rt > 3)) {
fprintf(stderr, "Bad rt value: %d\n", rt);
return -1;
}
gcCmdBlk[1] = (rt & 0x3);
if ((starting < 0) || (starting > 0xffff)) {
fprintf(stderr, "Bad starting field number: 0x%x\n", starting);
return -1;
}
gcCmdBlk[2] = (unsigned char)((starting >> 8) & 0xff);
gcCmdBlk[3] = (unsigned char)(starting & 0xff);
if ((mx_resp_len < 0) || (mx_resp_len > 0xffff)) {
fprintf(stderr, "Bad mx_resp_len: 0x%x\n", starting);
return -1;
}
gcCmdBlk[7] = (unsigned char)((mx_resp_len >> 8) & 0xff);
gcCmdBlk[8] = (unsigned char)(mx_resp_len & 0xff);
if (verbose) {
fprintf(stderr, " Get Configuration cdb: ");
for (k = 0; k < GET_CONFIG_CMD_LEN; ++k)
fprintf(stderr, "%02x ", gcCmdBlk[k]);
fprintf(stderr, "\n");
}
memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
io_hdr.interface_id = 'S';
io_hdr.cmd_len = sizeof(gcCmdBlk);
io_hdr.mx_sb_len = sizeof(sense_b);
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
io_hdr.dxfer_len = mx_resp_len;
io_hdr.dxferp = resp;
io_hdr.cmdp = gcCmdBlk;
io_hdr.sbp = sense_b;
io_hdr.timeout = DEF_TIMEOUT;
if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
perror("SG_IO (get config) error");
return -1;
}
res = sg_err_category3(&io_hdr);
switch (res) {
case SG_LIB_CAT_RECOVERED:
sg_chk_n_print3("Get config, continuing", &io_hdr);
/* fall through */
case SG_LIB_CAT_CLEAN:
if (verbose && io_hdr.resid)
fprintf(stderr, " get config: resid=%d\n", io_hdr.resid);
return 0;
case SG_LIB_CAT_INVALID_OP:
case SG_LIB_CAT_ILLEGAL_REQ:
if (verbose > 1)
sg_chk_n_print3("get config error", &io_hdr);
return res;
default:
if (verbose | noisy) {
snprintf(ebuff, EBUFF_SZ, "get config error, rt=%d, "
"starting=0x%x ", rt, starting);
sg_chk_n_print3(ebuff, &io_hdr);
}
return -1;
}
}
static void usage()
{
fprintf(stderr,
"Usage: 'sg_get_config [--brief] [--help] [--hex] [--inner-hex] "
"[--list]\n"
" [--rt=<num>] [--starting=<num>] "
"[--verbose]\n"
" [--version] <device>'\n"
" where --brief | -b only give feature names of <device> "
"(don't decode)\n"
" --help | -h output usage message\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"
" --rt=<num> | -r <num>\n"
" 0 -> all feature descriptors (regardless "
"of currency)\n"
" 1 -> all current feature descriptors\n"
" 2 -> only feature descriptor matching "
"'starting'\n"
" --starting=<num> | -s <num> starting from feature "
"<num>\n"
" --verbose | -v verbose\n"
" --version | -V output version string\n");
}
static const char * scsi_ptype_strs[] = {
/* 0 */ "disk",
"tape",
"printer",
"processor",
"write once optical disk",
/* 5 */ "cd/dvd",
"scanner",
"optical memory device",
"medium changer",
"communications",
/* 0xa */ "graphics [0xa]",
"graphics [0xb]",
"storage array controller",
"enclosure services device",
"simplified direct access device",
"optical card reader/writer device",
/* 0x10 */ "bridge controller commands",
"object based storage",
"automation/driver interface",
"0x13", "0x14", "0x15", "0x16", "0x17", "0x18",
"0x19", "0x1a", "0x1b", "0x1c", "0x1d",
"well known logical unit",
"no physical device on this lu",
};
static const char * get_ptype_str(int scsi_ptype)
{
int num = sizeof(scsi_ptype_strs) / sizeof(scsi_ptype_strs[0]);
return (scsi_ptype < num) ? scsi_ptype_strs[scsi_ptype] : "";
}
struct code_desc {
int code;
const char * desc;
};
static struct code_desc profile_desc_arr[] = {
{0x0, "No current profile"},
{0x1, "Non-removable disk"},
{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 restricted recording"},
{0x15, "DVD-R dual layer sequental recording"},
{0x16, "DVD-R dual layer layer jump recording"},
{0x1a, "DVD+RW"},
{0x1b, "DVD+R"},
{0x20, "DDCD-ROM"},
{0x21, "DDCD-R"},
{0x22, "DDCD-RW"},
{0x2b, "DVD+R double layer"},
{0x40, "BD-ROM"},
{0x41, "BD-R sequential recording (SRM)"},
{0x42, "BD-R random recording (RRM)"},
{0x43, "BD-RE"},
{0xffff, "Non-conforming profile"},
};
static const char * get_profile_str(int profile_num, char * buff)
{
int k, num;
num = sizeof(profile_desc_arr) / sizeof(profile_desc_arr[0]);
for (k = 0; k < num; ++k) {
if (profile_desc_arr[k].code == profile_num) {
strcpy(buff, profile_desc_arr[k].desc);
return buff;
}
}
snprintf(buff, 64, "0x%x", profile_num);
return buff;
}
static struct code_desc 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"},
{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"},
{0x37, "CD-RW media write support"},
{0x38, "BD-R pseudo-overwrite (POW)"},
{0x3b, "DVD+R double layer"},
{0x40, "BD read"},
{0x41, "BD write"},
{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, "Logical unit serial number"},
{0x109, "Media serial number"},
{0x10a, "Disc control blocks"},
{0x10b, "DVD CPRM"},
{0x10c, "Firmware information"},
{0x110, "VCPS"},
{0x120, "BD CPS"},
};
static const char * get_feature_str(int feature_num, char * buff)
{
int k, num;
num = sizeof(feature_desc_arr) / sizeof(feature_desc_arr[0]);
for (k = 0; k < num; ++k) {
if (feature_desc_arr[k].code == feature_num) {
strcpy(buff, feature_desc_arr[k].desc);
return buff;
}
}
snprintf(buff, 64, "0x%x", feature_num);
return buff;
}
static void decode_feature(int feature, unsigned char * ucp, int len)
{
int k, num, n, profile;
char buff[128];
const char * cp;
cp = "";
switch (feature) {
case 0: /* Profile list */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 2), !!(ucp[2] & 1),
feature);
printf(" available profiles [ordered from most advanced to "
"least]:\n");
for (k = 4; k < len; k += 4) {
profile = (ucp[k] << 8) + ucp[k + 1];
printf(" profile: %s , currentP=%d\n",
get_profile_str(profile, buff), !!(ucp[k + 2] & 1));
}
break;
case 1: /* Core */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 2), !!(ucp[2] & 1),
feature);
if (len < 8) {
printf(" additional length [%d] too short\n", len - 4);
break;
}
num = (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7];
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(", DBE=%d\n", !!(ucp[8] & 1));
else
printf("\n");
break;
case 2: /* Morphing */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 2), !!(ucp[2] & 1),
feature);
if (len < 8) {
printf(" additional length [%d] too short\n", len - 4);
break;
}
printf(" OCEvent=%d, ASYNC=%d\n", !!(ucp[4] & 2),
!!(ucp[4] & 1));
break;
case 3: /* Removable medium */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 2), !!(ucp[2] & 1),
feature);
if (len < 8) {
printf(" additional length [%d] too short\n", len - 4);
break;
}
num = (ucp[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(" Eject=%d, Prevent jumper=%d, Lock=%d\n",
!!(ucp[4] & 0x8), !!(ucp[4] & 0x4), !!(ucp[4] & 0x1));
break;
case 4: /* Write protect */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[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", !!(ucp[4] & 0x8),
!!(ucp[4] & 0x4), !!(ucp[4] & 0x2), !!(ucp[4] & 0x1));
break;
case 0x10: /* Random readable */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
feature);
if (len < 12) {
printf(" additional length [%d] too short\n", len - 4);
break;
}
num = (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7];
printf(" Logical block size=0x%x, blocking=0x%x, PP=%d\n",
num, ((ucp[8] << 8) + ucp[9]), !!(ucp[10] & 0x1));
break;
case 0x1d: /* Multi-read */
case 0x1f: /* DVD read */
case 0x22: /* Sector erasable */
case 0x26: /* Restricted overwrite */
case 0x27: /* CDRW CAV write */
case 0x38: /* BD-R pseudo-overwrite (POW) */
case 0x100: /* Power management */
case 0x104: /* Firmware upgrade */
case 0x109: /* Media serial number */
case 0x110: /* VCPS */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
feature);
break;
case 0x1e: /* CD read */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[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", !!(ucp[4] & 0x80),
!!(ucp[4] & 0x2), !!(ucp[4] & 0x1));
break;
case 0x20: /* Random writable */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
feature);
if (len < 16) {
printf(" additional length [%d] too short\n", len - 4);
break;
}
num = (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7];
n = (ucp[8] << 24) + (ucp[9] << 16) + (ucp[10] << 8) + ucp[11];
printf(" Last lba=0x%x, Logical block size=0x%x, blocking=0x%x,"
" PP=%d\n", num, n, ((ucp[12] << 8) + ucp[13]),
!!(ucp[14] & 0x1));
break;
case 0x21: /* Incremental streaming writable */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
feature);
if (len < 8) {
printf(" additional length [%d] too short\n", len - 4);
break;
}
printf(" Data block types supported=0x%x, ARSV=%d, BUF=%d\n",
((ucp[4] << 8) + ucp[5]), !!(ucp[6] & 0x2), !!(ucp[6] & 0x1));
num = ucp[7];
printf(" Number of link sizes=%d\n", num);
for (k = 0; k < num; ++k)
printf(" %d\n", ucp[8 + k]);
break;
case 0x23: /* Formattable */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
feature);
if (len > 4)
printf(" BD-RE: RENoSA=%d, Expand=%d, QCert=%d, Cert=%d\n",
!!(ucp[4] & 0x8), !!(ucp[4] & 0x4), !!(ucp[4] & 0x2),
!!(ucp[4] & 0x1));
if (len > 8)
printf(" BD-R: RRM=%d\n", !!(ucp[8] & 0x1));
break;
case 0x24: /* Hardware defect management */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
feature);
if (len > 4)
printf(" SSA=%d\n", !!(ucp[4] & 0x80));
break;
case 0x25: /* Write once */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
feature);
if (len < 12) {
printf(" additional length [%d] too short\n", len - 4);
break;
}
num = (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7];
printf(" Logical block size=0x%x, blocking=0x%x, PP=%d\n",
num, ((ucp[8] << 8) + ucp[9]), !!(ucp[10] & 0x1));
break;
case 0x28: /* MRW */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
feature);
if (len > 4)
printf(" Write=%d\n", !!(ucp[4] & 0x1));
break;
case 0x29: /* Enhanced defect reporting */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[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", !!(ucp[4] & 0x1), ucp[5],
((ucp[6] << 8) + ucp[7]));
break;
case 0x2a: /* DVD+RW */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[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",
!!(ucp[4] & 0x1), !!(ucp[5] & 0x2), !!(ucp[5] & 0x1));
break;
case 0x2b: /* DVD+R */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
feature);
if (len < 8) {
printf(" additional length [%d] too short\n", len - 4);
break;
}
printf(" Write=%d\n", !!(ucp[4] & 0x1));
break;
case 0x2c: /* Rigid restricted overwrite */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[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",
!!(ucp[4] & 0x8), !!(ucp[4] & 0x4), !!(ucp[4] & 0x2),
!!(ucp[4] & 0x1));
break;
case 0x2d: /* CD Track at once */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[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",
!!(ucp[4] & 0x40), !!(ucp[4] & 0x10), !!(ucp[4] & 0x8),
!!(ucp[4] & 0x4));
printf(" CD-RW=%d, R-W sub-code=%d\n",
!!(ucp[4] & 0x2), !!(ucp[4] & 0x1));
break;
case 0x2e: /* CD mastering (session at once) */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[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",
!!(ucp[4] & 0x40), !!(ucp[4] & 0x20), !!(ucp[4] & 0x10),
!!(ucp[4] & 0x8));
printf(" Test write=%d, CD-RW=%d, R-W=%d\n",
!!(ucp[4] & 0x4), !!(ucp[4] & 0x2), !!(ucp[4] & 0x1));
printf(" Maximum cue sheet length=0x%x\n",
(ucp[5] << 16) + (ucp[6] << 8) + ucp[7]);
break;
case 0x2f: /* DVD-R/-RW write */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
feature);
if (len < 8) {
printf(" additional length [%d] too short\n", len - 4);
break;
}
printf(" BUF=%d, Dual-R=%d, Test write=%d, DVD-RW=%d\n",
!!(ucp[4] & 0x40), !!(ucp[4] & 0x8), !!(ucp[4] & 0x4),
!!(ucp[4] & 0x2));
break;
case 0x37: /* CD-RW media write support */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[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", ucp[5]);
break;
case 0x3b: /* DVD+R double layer */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
feature);
if (len < 8) {
printf(" additional length [%d] too short\n", len - 4);
break;
}
printf(" Write=%d\n", !!(ucp[4] & 0x1));
break;
case 0x40: /* BD Read */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[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", (ucp[8] << 8) + ucp[9],
(ucp[10] << 8) + ucp[11],
(ucp[12] << 8) + ucp[13],
(ucp[14] << 8) + ucp[15]);
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", (ucp[16] << 8) + ucp[17],
(ucp[18] << 8) + ucp[19],
(ucp[20] << 8) + ucp[21],
(ucp[22] << 8) + ucp[23]);
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", (ucp[24] << 8) + ucp[25],
(ucp[26] << 8) + ucp[27],
(ucp[28] << 8) + ucp[29],
(ucp[30] << 8) + ucp[31]);
break;
case 0x41: /* BD Write */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
feature);
if (len < 32) {
printf(" additional length [%d] too short\n", len - 4);
break;
}
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", (ucp[8] << 8) + ucp[9],
(ucp[10] << 8) + ucp[11],
(ucp[12] << 8) + ucp[13],
(ucp[14] << 8) + ucp[15]);
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", (ucp[16] << 8) + ucp[17],
(ucp[18] << 8) + ucp[19],
(ucp[20] << 8) + ucp[21],
(ucp[22] << 8) + ucp[23]);
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", (ucp[24] << 8) + ucp[25],
(ucp[26] << 8) + ucp[27],
(ucp[28] << 8) + ucp[29],
(ucp[30] << 8) + ucp[31]);
break;
case 0x101: /* SMART */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
feature);
if (len < 8) {
printf(" additional length [%d] too short\n", len - 4);
break;
}
printf(" PP=%d\n", !!(ucp[4] & 0x1));
break;
case 0x102: /* Embedded changer */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[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",
!!(ucp[4] & 0x10), !!(ucp[4] & 0x4), (ucp[7] & 0x1f));
break;
case 0x103: /* CD audio external play */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[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",
!!(ucp[4] & 0x4), !!(ucp[4] & 0x2), !!(ucp[4] & 0x1),
(ucp[6] << 8) + ucp[7]);
break;
case 0x105: /* Timeout */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
feature);
if (len > 7) {
printf(" Group 3=%d, unit length=%d\n",
!!(ucp[4] & 0x1), (ucp[6] << 8) + ucp[7]);
}
break;
case 0x106: /* DVD CSS */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
feature);
if (len < 8) {
printf(" additional length [%d] too short\n", len - 4);
break;
}
printf(" CSS version=%d\n", ucp[7]);
break;
case 0x107: /* Real time streaming */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[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",
!!(ucp[4] & 0x10), !!(ucp[4] & 0x8), !!(ucp[4] & 0x4),
!!(ucp[4] & 0x2), !!(ucp[4] & 0x1));
break;
case 0x108: /* Logical unit serial number */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
feature);
num = len - 4;
n = sizeof(buff) - 1;
n = ((num < n) ? num : n);
strncpy(buff, (const char *)(ucp + 4), n);
buff[n] = '\0';
printf(" Logical unit serial number: %s\n", buff);
break;
case 0x10a: /* Disc control blocks */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
feature);
printf(" Disc control blocks:\n");
for (k = 4; k < len; k += 4) {
printf(" 0x%x\n", (ucp[k] << 24) + (ucp[k + 1] << 16) +
(ucp[k + 2] << 8) + ucp[k + 3]);
}
break;
case 0x10b: /* DVD CPRM */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[2] & 0x1),
feature);
if (len < 8) {
printf(" additional length [%d] too short\n", len - 4);
break;
}
printf(" CPRM version=%d\n", ucp[7]);
break;
case 0x10c: /* firmware information */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[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", ucp + 4,
ucp + 6, ucp + 8, ucp + 10, ucp + 12, ucp + 14, ucp + 16);
break;
case 0x120: /* BD CPS */
printf(" version=%d, persist=%d, current=%d [0x%x]\n",
((ucp[2] >> 2) & 0xf), !!(ucp[2] & 0x2), !!(ucp[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", ((ucp[5] >> 4) & 0xf), (ucp[5] & 0xf),
ucp[6] & 0x3);
break;
default:
printf(" Unknown feature [0x%x], version=%d persist=%d, "
"current=%d\n", feature, ((ucp[2] >> 2) & 0xf),
!!(ucp[2] & 0x2), !!(ucp[2] & 0x1));
dStrHex((const char *)ucp, len, 1);
break;
}
}
static void decode_config(unsigned char * resp, int max_resp_len, int len,
int brief, int inner_hex)
{
int k, curr_profile, extra, feature;
unsigned char * ucp;
char buff[128];
if (max_resp_len < len) {
printf("<<<warning: response to long for buffer, resp_len=%d>>>\n",
len);
len = max_resp_len;
}
if (len < 8) {
printf("response length too short: %d\n", len);
return;
}
curr_profile = (resp[6] << 8) + resp[7];
if (0 == curr_profile)
printf("No current profile\n");
else
printf("Current profile: %s\n", get_profile_str(curr_profile, buff));
printf("Features%s:\n", (brief ? " (in brief)" : ""));
ucp = resp + 8;
len -= 8;
for (k = 0; k < len; k += extra, ucp += extra) {
extra = 4 + ucp[3];
feature = (ucp[0] << 8) + ucp[1];
printf(" %s feature\n", get_feature_str(feature, buff));
if (brief)
continue;
if (inner_hex) {
dStrHex((const char *)ucp, extra, 1);
continue;
}
if (0 != (extra % 4))
printf(" additional length [%d] not a multiple of 4, ignore\n",
extra - 4);
else
decode_feature(feature, ucp, extra);
}
}
static void list_known(int brief)
{
int k, num;
num = sizeof(feature_desc_arr) / sizeof(feature_desc_arr[0]);
printf("Known features:\n");
for (k = 0; k < num; ++k)
printf(" %s [0x%x]\n", feature_desc_arr[k].desc,
feature_desc_arr[k].code);
if (! brief) {
printf("Known profiles:\n");
num = sizeof(profile_desc_arr) / sizeof(profile_desc_arr[0]);
for (k = 0; k < num; ++k)
printf(" %s [0x%x]\n", profile_desc_arr[k].desc,
profile_desc_arr[k].code);
}
}
int main(int argc, char * argv[])
{
int sg_fd, res, c, len;
int peri_type = 0;
int brief = 0;
int hex = 0;
int inner_hex = 0;
int list = 0;
int rt = 0;
int starting = 0;
int verbose = 0;
char device_name[256];
const char * cp;
struct sg_simple_inquiry_resp inq_resp;
int ret = 1;
memset(device_name, 0, sizeof device_name);
while (1) {
int option_index = 0;
c = getopt_long(argc, argv, "bhHilr:s:vV", long_options,
&option_index);
if (c == -1)
break;
switch (c) {
case 'b':
brief = 1;
break;
case 'h':
case '?':
usage();
return 0;
case 'H':
hex = 1;
break;
case 'i':
inner_hex = 1;
break;
case 'l':
list = 1;
break;
case 'r':
rt = sg_get_num(optarg);
if ((rt < 0) || (rt > 3)) {
fprintf(stderr, "bad argument to '--rt'\n");
return 1;
}
break;
case 's':
starting = sg_get_num(optarg);
if ((starting < 0) || (starting > 0xffff)) {
fprintf(stderr, "bad argument to '--starting'\n");
return 1;
}
break;
case 'v':
++verbose;
break;
case 'V':
fprintf(stderr, ME "version: %s\n", version_str);
return 0;
default:
fprintf(stderr, "unrecognised switch code 0x%x ??\n", c);
usage();
return 1;
}
}
if (optind < argc) {
if ('\0' == device_name[0]) {
strncpy(device_name, argv[optind], sizeof(device_name) - 1);
device_name[sizeof(device_name) - 1] = '\0';
++optind;
}
if (optind < argc) {
for (; optind < argc; ++optind)
fprintf(stderr, "Unexpected extra argument: %s\n",
argv[optind]);
usage();
return 1;
}
}
if (list) {
list_known(brief);
return 0;
}
if (0 == device_name[0]) {
fprintf(stderr, "missing device name!\n");
usage();
return 1;
}
if ((sg_fd = open(device_name, O_RDONLY | O_NONBLOCK)) < 0) {
snprintf(ebuff, EBUFF_SZ, ME "error opening file: %s (ro)",
device_name);
perror(ebuff);
return 1;
}
if (0 == sg_simple_inquiry(sg_fd, &inq_resp, 1, verbose)) {
printf(" %.8s %.16s %.4s\n", inq_resp.vendor, inq_resp.product,
inq_resp.revision);
peri_type = inq_resp.peripheral_type;
cp = get_ptype_str(peri_type);
if (strlen(cp) > 0)
printf(" Peripheral device type: %s\n", cp);
else
printf(" Peripheral device type: 0x%x\n", peri_type);
} else {
printf(ME "%s doesn't respond to a SCSI INQUIRY\n", device_name);
return 1;
}
close(sg_fd);
sg_fd = open(device_name, O_RDWR | O_NONBLOCK);
if (sg_fd < 0) {
perror(ME "open error (rw)");
return 1;
}
res = sg_ll_get_config(sg_fd, rt, starting, resp_buffer,
sizeof(resp_buffer), 1, verbose);
if (0 == res) {
ret = 0;
len = (resp_buffer[0] << 24) + (resp_buffer[1] << 16) +
(resp_buffer[2] << 8) + resp_buffer[3] + 4;
if (hex) {
if (len > (int)sizeof(resp_buffer))
len = sizeof(resp_buffer);
dStrHex((const char *)resp_buffer, len, 0);
} else
decode_config(resp_buffer, sizeof(resp_buffer), len, brief,
inner_hex);
} else if (SG_LIB_CAT_INVALID_OP == res)
fprintf(stderr, "Get Configuration command not supported\n");
else if (SG_LIB_CAT_ILLEGAL_REQ == res)
fprintf(stderr, "field in Get Configuration command illegal\n");
else
fprintf(stderr, "Get Configuration command failed\n");
res = close(sg_fd);
if (res < 0) {
perror(ME "close error");
return 1;
}
return ret;
}