/* $NetBSD$ */
/*
* File "cd_disect.c" is part of the UDFclient toolkit.
* File $Id: cd_disect.c,v 1.67 2007/12/09 21:37:14 reinoud Exp $ $Name: $
*
* Copyright (c) 2003, 2004, 2005, 2006 Reinoud Zandijk <reinoud@netbsd.org>
* All rights reserved.
*
* The UDFclient toolkit is distributed under the Clarified Artistic Licence.
* A copy of the licence is included in the distribution as
* `LICENCE.clearified.artistic' and a copy of the licence can also be
* requested at the GNU foundantion's website.
*
* Visit the UDFclient toolkit homepage http://www.13thmonkey.org/udftoolkit/
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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 <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <assert.h>
#include <ctype.h>
#include <sys/types.h>
#include <inttypes.h>
#include "uscsilib.h"
#ifndef MAX
# define MAX(a,b) ((a)>(b)?(a):(b))
# define MIN(a,b) ((a)<(b)?(a):(b))
#endif
/*
* from inquiry preriph. device type; used to saveguard MMC specific
* operations. See spc2.
*/
#define DEVICE_TYPE_MMC 0x05
/* globals */
struct uscsi_dev dev;
/* helper functions */
static int read_cd_hex2(int val) {
int nl, nh;
nl = val & 15;
nh = val >> 4;
if (nl >= 'A') nl -= 'A' + 10;
if (nh >= 'A') nh -= 'A' + 10;
return (nh*16) + nl;
}
static int read_cd_bcd(int val) {
int nl, nh;
nl = (val & 15) - '0';
nh = (val >> 4) - '0';
if ((nl < 0 || nl > 9) || (nh < 0 || nh > 9)) return val;
return nh*10 + nl;
}
static int32_t cd_msf2lba(int h, int m, int s, int f) {
return 270000*h + 4500*m + 75*s + f - 150;
}
/* start of discect functions */
static char *print_disc_type(int type) {
switch (type) {
case 0x00 : return "CD-DA or CD-ROM Disc or non CD";
case 0x10 : return "CD-I Disc";
case 0x20 : return "CD-ROM XA Disc";
case 0xFF : return "Undefined";
}
return "Reserved";
}
static char *print_disc_state(int state) {
switch (state) {
case 0 : return "empty disc";
case 1 : return "incomplete (appendable)";
case 2 : return "full (not appendable)";
case 3 : return "random writable";
}
return "unknown disc state";
}
static char *print_session_state(int state) {
switch (state) {
case 0 : return "empty";
case 1 : return "incomplete";
case 2 : return "reserved/damaged";
case 3 : return "complete/closed disc";
}
return "unknown session_state";
}
static char *print_mmc_profile(int profile) {
static char scrap[100];
switch (profile) {
case 0x00 : return "Unknown[0] profile";
case 0x01 : return "Non removeable disc";
case 0x02 : return "Removable disc";
case 0x03 : return "Magneto Optical with sector erase";
case 0x04 : return "Magneto Optical write once";
case 0x05 : return "Advance Storage Magneto Optical";
case 0x08 : return "CD-ROM";
case 0x09 : return "CD-R recordable";
case 0x0a : return "CD-RW rewritable";
case 0x10 : return "DVD-ROM";
case 0x11 : return "DVD-R sequential";
case 0x12 : return "DVD-RAM rewritable";
case 0x13 : return "DVD-RW restricted overwrite";
case 0x14 : return "DVD-RW sequential";
case 0x1a : return "DVD+RW rewritable";
case 0x1b : return "DVD+R recordable";
case 0x20 : return "DDCD readonly";
case 0x21 : return "DDCD-R recordable";
case 0x22 : return "DDCD-RW rewritable";
case 0x2b : return "DVD+R double layer";
case 0x40 : return "BD-ROM";
case 0x41 : return "BD-R Sequential Recording (SRM)";
case 0x42 : return "BD-R Random Recording (RRM)";
case 0x43 : return "BD-RE rewritable";
}
sprintf(scrap, "Reserved profile 0x%02x", profile);
return scrap;
}
static char *print_write_type(int type) {
switch (type) {
case 0x00 : return "Packet/Incremental";
case 0x01 : return "Track-at-once";
case 0x02 : return "Session-at-one";
case 0x03 : return "Raw";
}
return "unknown write type";
}
static char *print_data_block_type(int type) {
static char scrap[100];
switch (type) {
case 0 : return "raw data 2352 bytes";
case 1 : return "raw data 2368 bytes with P and Q channel";
case 2 : return "raw data 2352 bytes (+96) P-W subchannel appended";
case 3 : return "raw data 2352 bytes (+96) raw P-W subchannel appended";
case 8 : return "ISO mode 1 with 2048 bytes data";
case 9 : return "ISO mode 2 with 2336 bytes data, formless";
case 10 : return "ISO mode 2 with 2048 bytes data (CDROM-XA, form 1), subheader from write parameters";
case 11 : return "ISO mode 2 with 2048 bytes data (CDROM-XA, form 1), 8 bytes for subheader first";
case 12 : return "ISO mode 2 with 2324 bytes data (CDROM-XA, form 2), subheader from write parameters";
case 13 : return "ISO mode 2 with 2332 bytes data (CDROM-XA, form 1 or 2 or mixed), 8 bytes for subheader first";
}
sprintf(scrap, "Unknown/reserved data block type 0x%02x", type);
return scrap;
}
static char *print_Q_control(int cntrl) {
static char scrap[100];
strcpy(scrap, "");
if ((cntrl & 12) == 4) {
strcat(scrap, "data track ");
if (cntrl & 1) strcat(scrap, "; incremental "); else strcat(scrap, "; uninterrupted");
} else {
strcat(scrap, "audio track");
if (cntrl & 1) strcat(scrap, "; pre-emphasis of 50/15 µs"); else strcat(scrap, "; no pre-emphasis");
}
if (cntrl & 2) strcat(scrap, "; copy prohibited");
return scrap;
}
static char *print_session_format(int format) {
static char scrap[100];
switch (format) {
case 0x00 : return "CD-DA, CD-ROM or other data discs";
case 0x10 : return "CD-I disc";
case 0x20 : return "CD-ROM XA disc or DDCD disc";
}
sprintf(scrap, "Unknown/reserved session format type 0x%02x", format);
return scrap;
}
/* why disc_type is not equal to session_format is not clear yet; its reported in the TOC/PMA/ATI format 010b */
static char *print_TOC_disc_type(int type) {
static char scrap[100];
switch (type) {
case 0x00 : return "CD-DA or CD Data disc with first track in Mode 1";
case 0x10 : return "CD-I disc";
case 0x20 : return "CD data XA disc with first track in Mode 2";
}
sprintf(scrap, "Unknown/reserved TOC disc type type 0x%02x", type);
return scrap;
}
static char *print_inactivity_time(int time) {
static char scrap[100];
switch (time) {
case 0x0 : return "Vendor specific";
case 0x1 : return "125 ms";
case 0x2 : return "250 ms";
case 0x3 : return "500 ms";
case 0x4 : return "1 sec";
case 0x5 : return "2 sec";
case 0x6 : return "4 sec";
case 0x7 : return "8 sec";
case 0x8 : return "16 sec";
case 0x9 : return "32 sec";
case 0xa : return "1 min";
case 0xb : return "2 min";
case 0xc : return "4 min";
case 0xd : return "8 min";
case 0xe : return "16 min";
case 0xf : return "32 min";
}
sprintf(scrap, "Unknown/reserved inactivity timeout 0x%02x", time);
return scrap;
}
static char *print_device_type(int type) {
static char scrap[100];
switch (type) {
case 0x00 : return "Direct-access device (e.g., magnetic disk)";
case 0x01 : return "Sequential-access device (e.g., magnetic tape)";
case 0x02 : return "Printer device";
case 0x03 : return "Processor device";
case 0x04 : return "Write-once device (e.g., some optical disks)";
case 0x05 : return "CD-ROM device";
case 0x06 : return "Scanner";
case 0x07 : return "Optical memory device (e.g., some optical disks)";
case 0x08 : return "Medium changer device (e.g., jukeboxes)";
case 0x09 : return "Communications device";
case 0x0a : /* fall trough */
case 0x0b : return "Defined by ASC IT8 (Graphic arts pre-press devices)";
case 0x0c : return "Storage array controller device (e.g., RAID)";
case 0x0d : return "Enclosure services device";
case 0x0e : return "Simplified direct-access device (e.g., magnetic disk)";
case 0x0f : return "Optical card reader/writer device";
case 0x10 : return "Reserved/used for Bridging Expanders";
case 0x11 : return "Object-based Storage Device";
}
sprintf(scrap, "Unknown/reserved device type 0x%02x", type);
return scrap;
}
static char *print_normal_string(uint8_t *buf, int len) {
static char scrap[100];
char *pos;
int i;
memset(scrap, 0, 100);
pos = scrap;
for (i = 0; i < len; i++) {
if (isprint(buf[i]))
*pos++ = buf[i];
}
*pos = (char) 0;
return scrap;
}
void dump_drive_identify(int *device_type) {
scsicmd cmd;
uint8_t buf[100];
int error;
/* go for SCB for a start */
*device_type = 0;
bzero(cmd, SCSI_CMD_LEN);
cmd[0] = 0x12; /* INQUIRY */
cmd[1] = 0; /* basic inquiry */
cmd[2] = 0; /* no page or operation code */
cmd[3] = 0; /* reserved/MSB result */
cmd[4] = 96; /* all but vendor specific */
cmd[5] = 0; /* control */
error = uscsi_command(SCSI_READCMD, &dev, cmd, 6, buf, 96, 30000, NULL);
if (error) {
fprintf(stderr, "Device Inquiry returned error : %s\n", strerror(error));
return;
}
*device_type = buf[0] & 0x1f;
printf("\n");
printf("\tDevice type\t\t: %s\n", print_device_type(*device_type));
printf("\tVendor identification\t: %s\n", print_normal_string(buf + 8, 8));
printf("\tProduct identification\t: %s\n", print_normal_string(buf + 16, 16));
printf("\tProduct revision level\t: %s\n", print_normal_string(buf + 32, 4));
printf("\t<Rest not dumped yet>\n");
printf("\n");
}
void dump_recorded_capacity(void) {
scsicmd cmd;
uint8_t buf[36];
uint32_t lba, blk_len;
int error;
bzero(cmd, SCSI_CMD_LEN);
cmd[0] = 0x25; /* CD READ RECORDED CAPACITY */
error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, buf, 8, 30000, NULL);
if (error) {
fprintf(stderr, "Read recorded capacity SCSI call returned : %s\n", strerror(error));
return;
}
lba = buf[3] | (buf[2]<<8) | (buf[1]<<16) | (buf[0]<<24);
blk_len = buf[7] | (buf[6]<<8) | (buf[5]<<16) | (buf[4]<<24);
printf("\nCD recorded capacity is LBA %d (%d blocks), blk_len %d\n",
lba, lba/4, blk_len);
}
void dump_feature(uint8_t *fpos) {
uint32_t feature, cnt, profile;
uint32_t feature_len, feature_ver, feature_cur, feature_pers;
uint32_t interface, blocking, log_blk_size, last_log_blk_addr;
uint32_t datablock_types, num_link_sizes, cuesheet_len;
uint32_t num_vol_levels, dcb_entry;
uint32_t century, year, day, month, hour, minute;
uint8_t *pos;
feature = fpos[1] | (fpos[0] << 8);
feature_ver = (fpos[2] >> 2) & 15;
feature_cur = (fpos[2] & 1);
feature_pers= (fpos[2] & 2);
feature_len = fpos[3];
printf("\t\tFeature 0x%04x (%2d bytes) version %2d; persistent %s; currently active %s\n",
feature, feature_len, feature_ver, feature_pers?"yes":" no", feature_cur?"yes":" no");
pos = &fpos[4];
switch (feature) {
case 0x0000:
printf("\t\tProfile list; supporting profiles\n");
for (cnt=0; cnt < feature_len; cnt += 4) {
profile = pos[1] | (pos[0] << 8);
printf("\t\t\t%s %s\n", pos[2] & 1 ?"ACTIVE ":"inactive", print_mmc_profile(profile));
pos += 4;
}
break;
case 0x0001:
interface = pos[3] | (pos[2] << 8) | (pos[1] << 16) | (pos[0] << 24);
printf("\t\tCore features : physical interface standard commands for `");
switch (interface) {
case 0 : printf("Unspecified"); break;
case 1 : printf("SCSI family"); break;
case 2 : printf("ATAPI"); break;
case 3 : printf("IEEE 1394 - 1995"); break;
case 4 : printf("IEEE 1394A"); break;
case 5 : printf("Fibre channel"); break;
case 0xffff : printf("Vendor specific"); break;
default : printf("<reserved (0x%04x)>", interface); break;
}
printf("'\n");
break;
case 0x0002:
printf("\t\tMorphing command set; %ssupport for ASYNC\n", (pos[0] & 1)?"":"no ");
break;
case 0x0003:
printf("\t\tRemovable medium features\n");
if (feature_ver > 1) {
printf("\t\t\tUnknown flags\n");
break;
}
if (pos[0] & 1) printf("\t\t\tDevice can be locked against removal\n");
printf("\t\t\tDevice will go into the %slocked state by default\n", (pos[0] & 4)?"":"un-");
printf("\t\t\tDevice %s eject the media with START/STOP with eject bit\n", (pos[0] & 8)?"can":"can't");
printf("\t\t\tLoading mechanism : ");
switch (pos[0] >> 5) {
case 0 : printf("Caddy"); break;
case 1 : printf("Tray"); break;
case 2 : printf("Pop-up"); break;
case 4 : printf("Embedded changer with seperate discs"); break;
case 5 : printf("Embedded changes using magazines"); break;
default : printf("Unknown/Reserved");
}
printf("\n");
break;
case 0x0004:
printf("\t\tWrite protect features :");
if (pos[0] & 2) printf(" SPWP");
if (pos[0] & 1) printf(" SSWPP");
printf("\n");
break;
case 0x0010:
printf("\t\tRandom readable\n");
log_blk_size = pos[3] | (pos[2] << 8) | (pos[1] << 16) | (pos[0] << 24);
blocking = pos[5] | (pos[4] << 8);
printf("\t\t\tLogical block size %u bytes\n", log_blk_size);
printf("\t\t\tDevice blocking number %d logical blocks\n", blocking);
if (pos[6] & 1) printf("\t\t\tHas RW error recovery mode page\n");
break;
case 0x001d:
printf("\t\tMulti-read; The Logical Unit can read all CD media types; based on OSTA MultiRead\n");
break;
case 0x001e:
printf("\t\tAbility to read CD specific structures\n");
printf("\t\t\tDevice does %ssupport C2 error pointers\n", (pos[0] & 2)?"":"NOT ");
printf("\t\t\tDevice does %ssupport CD-Text\n", (pos[0] & 1)?"":"NOT ");
break;
case 0x001f:
printf("\t\tAbility to read DVD specific structures\n");
break;
case 0x0020:
printf("\t\tRandom writable\n");
last_log_blk_addr = pos[3] | (pos[2] << 8) | (pos[1] << 16) | (pos[0] << 24);
log_blk_size = pos[7] | (pos[6] << 8) | (pos[5] << 16) | (pos[4] << 24);
blocking = pos[9] | (pos[8] << 8);
printf("\t\t\tLast writable logic block address is %u ", last_log_blk_addr);
printf("(approx %u Mb)\n", (uint32_t) ((uint64_t) last_log_blk_addr*log_blk_size/(1024*1024)));
printf("\t\t\tLogical block size %u bytes\n", log_blk_size);
printf("\t\t\tDevice blocking number %d logical blocks\n", blocking);
if (pos[10] & 1) printf("\t\t\tHas RW error recovery mode page\n");
break;
case 0x0021:
printf("\t\tIncremental streaming writable\n");
datablock_types = pos[1] | (pos[0] << 8);
num_link_sizes = pos[3];
printf("\t\t\tDevice is %scapable of `Zero loss linking'\n", (pos[2] & 1)?"":"NOT ");
printf("\t\t\tDevice supported data types (bitfield) 0x%04x\n", datablock_types);
printf("\t\t\tDevice supports %d link size%s :", num_link_sizes, (num_link_sizes>1)?"s":"");
for (cnt=0; cnt < num_link_sizes; cnt++) {
printf(" %d", pos[4+cnt]);
}
printf("\n");
break;
case 0x0022:
printf("\t\tCan erase/support for erasing media (OBSOLETE)\n");
break;
case 0x0023:
printf("\t\tSupport for formatting media\n");
break;
case 0x0024:
printf("\t\tHas defect handling; i.e. apparently defect-free space\n");
printf("\t\t\tDevice does %ssupport read `Space Area Information' (DVD)\n", (pos[0] & 128)?"":"NOT ");
break;
case 0x0025:
printf("\t\tSupport for writing any unrecorded logical block on write once media in random order\n");
log_blk_size = pos[3] | (pos[2] << 8) | (pos[1] << 16) | (pos[0] << 24);
blocking = pos[5] | (pos[4] << 8);
printf("\t\t\tLogical block size %u bytes\n", log_blk_size);
printf("\t\t\tDevice blocking number %d logical blocks\n", blocking);
if (pos[6] & 1) printf("\t\t\tHas RW error recovery mode page\n");
break;
case 0x0026:
printf("\t\tSupport for restricted overwrite; i.e. on blocking boundaries only\n");
break;
case 0x0027:
printf("\t\tCD-RW CAV Write; The ability to write high speed CD-RW media\n");
/* parameters all reserved */
break;
case 0x0028:
printf("\t\tMRW formatted media support\n");
printf("\t\t\tDevice can read MRW formatted media\n");
printf("\t\t\tDevice %swrite/format media in MRW format\n", (pos[0] & 1)?"can ":"can't");
break;
case 0x002a:
printf("\t\tDVD+RW media reading/writing support\n");
if (pos[0] & 1) {
printf("\t\t\tDevice can write and background format DVD+RW media\n");
if (pos[1] & 1) printf("\t\t\tDevice only supports read compatibility format stop\n");
} else {
printf("\t\t\tDevice can only recognise and read DVD+RW\n");
}
break;
case 0x002b:
printf("\t\tThe ability to read/write DVD+R recorded media\n");
if (pos[0] & 1) {
printf("\t\t\tDevice can write DVD+R media\n");
} else {
printf("\t\t\tDevice can only recognise and read DVD+R\n");
}
break;
case 0x002c:
printf("\t\tSupport for rigid restricted overwrite only (CD-RW)\n");
if (pos[0] & 8) printf("\t\t\tDevice can generate direct status data during formatting\n");
if (pos[0] & 4) printf("\t\t\tDevice can read the defect status data recorded on media\n");
if (pos[0] & 2) printf("\t\t\tDevice allows writing on immediate state sessions and quick formatting\n");
printf("\t\t\tDevice does %ssupport blanking of media\n", (pos[0] & 1)?"":"NOT ");
break;
case 0x002d:
printf("\t\tTrack at once recording support\n");
datablock_types = pos[3] | (pos[2] << 8);
if (pos[0] & 64) printf("\t\t\tDevice is capable of zero-loss linking\n");
if (pos[0] & 16) printf("\t\t\tDevice is capable of writing R-W Sub code in RAW mode\n");
if (pos[0] & 8) printf("\t\t\tDevice is capable of writing R-W Sub code in Packet mode\n");
printf("\t\t\tDevice does %ssupport test writes (i.e. laser off)\n", (pos[0] & 4)?"": "NOT ");
if (pos[0] & 2) printf("\t\t\tDevice supports overwriting a track using track at once\n");
if (pos[0] & 1) printf("\t\t\tDevice supports writing R-W Sub channels with user data\n");
printf("\t\t\tTrack at once data types supported (bitfield) 0x%04x\n", datablock_types);
break;
case 0x002e:
printf("\t\tSession at once or RAW writing support; CD Mastering\n");
cuesheet_len = pos[3] | (pos[2] << 8) | (pos[1] << 16);
if (pos[0] & 64) printf("\t\t\tDevice is capable of zero-loss linking\n");
printf("\t\t\tDevice can %swrite using the Session at Once write type\n", (pos[0] & 32)?"":"CANT ");
if (pos[0] & 16) printf("\t\t\tDevice is capable of writing multi-session in RAW mode\n");
printf("\t\t\tDevice can %swrite using the raw write type\n", (pos[0] & 8)?"":"CANT ");
printf("\t\t\tDevice does %ssupport test writes (i.e. laser off)\n", (pos[0] & 4)?"": "NOT ");
if (pos[0] & 2) printf("\t\t\tDevice supports overwriting previous recorded media\n");
if (pos[0] & 1) printf("\t\t\tDevice supports writing R-W Sub channels with user data\n");
printf("\t\t\tMaximum cue sheet length %d bytes\n", cuesheet_len);
break;
case 0x002f:
printf("\t\tDVD-R/-RW Write; The ability to write DVD specific structures\n");
if (pos[0] & 64) printf("\t\t\tDevice is capable of zero-loss linking\n");
printf("\t\t\tDevice does %ssupport test writes (i.e. laser off)\n", (pos[0] & 4)?"": "NOT ");
if (pos[0] & 2) printf("\t\t\tDevice supports overwriting previous recorded media\n");
break;
case 0x0030:
printf("\t\tCan read DDCD user data\n");
break;
case 0x0031:
printf("\t\tCan write and read DDCD-R media\n");
printf("\t\t\tDevice does %ssupport test writes (i.e. laser off)\n", (pos[0] & 4)?"": "NOT ");
break;
case 0x0032:
printf("\t\tCan write and read DDCD-RW media\n");
if (pos[0] & 2) printf("\t\t\tDevice allows writing on immediate state sessions and quick formatting\n");
printf("\t\t\tDevice does %ssupport blanking of media\n", (pos[0] & 1)?"":"NOT ");
break;
case 0x0037:
printf("\t\tCD-RW Media Write Support\n");
printf("\t\t\tCan %swrite media subtype 0\n", (pos[1] & 1) ? "": "NOT ");
printf("\t\t\tCan %swrite media subtype 1\n", (pos[1] & 2) ? "": "NOT ");
printf("\t\t\tCan %swrite media subtype 2\n", (pos[1] & 4) ? "": "NOT ");
printf("\t\t\tCan %swrite media subtype 3\n", (pos[1] & 8) ? "": "NOT ");
printf("\t\t\tCan %swrite media subtype 4\n", (pos[1] & 16) ? "": "NOT ");
printf("\t\t\tCan %swrite media subtype 5\n", (pos[1] & 32) ? "": "NOT ");
printf("\t\t\tCan %swrite media subtype 6\n", (pos[1] & 64) ? "": "NOT ");
printf("\t\t\tCan %swrite media subtype 7\n", (pos[1] & 128) ? "": "NOT ");
break;
case 0x003b:
printf("\t\tDVD+R Double Layer support\n");
printf("\t\t\tDrive can read DVD+R double layer discs\n");
printf("\t\t\tDrive can %swrite DVD+R double layer discs (only valid when active)\n", (pos[1] & 1) ? "" : "NOT ");
break;
case 0x0100:
printf("\t\tPower management support\n");
break;
case 0x0101:
printf("\t\tS.M.A.R.T. (Self-Monitoring Analysis and Reporting Technology support)\n");
printf("\t\t\tDevice does %sprovide Fault/Failure Reporting Mode Page\n", (pos[0] & 1)?"":"NOT ");
break;
case 0x0102:
printf("\t\tEmbedded changer feature\n");
printf("\t\t\tDevice is %scapable of switching media sides\n", (pos[0] & 16)?"":"NOT ");
printf("\t\t\tDevice can %sreport disc presence after a reset of magazine change\n", (pos[0] & 4)?"":"NOT ");
break;
case 0x0103:
printf("\t\tAbility to play audio CDs via the Logical Unit's own analog output\n");
num_vol_levels = pos[3] | (pos[2] << 8);
printf("\t\t\tDevice does %ssupport the SCAN command\n", (pos[0] & 4)?"":"NOT ");
printf("\t\t\tDevice %s mute seperate channels\n", (pos[0] & 4)?"can":"can't");
printf("\t\t\tDevice %s set volumes for each channel seperately\n", (pos[0] & 4)?"can":"can't");
printf("\t\t\tDevice has %d audio volume levels\n", num_vol_levels);
break;
case 0x0104:
printf("\t\tAbility for the device to accept new microcode via the interface\n");
break;
case 0x0105:
printf("\t\tAbility to respond to all commands within a specific time\n");
break;
case 0x0106:
printf("\t\tAbility to perform DVD CSS/CPPM authentication and RPC\n");
printf("\t\t\tSupporting CCS version %d\n", pos[3]);
break;
case 0x0107:
printf("\t\tAbility to read and write using Initiator requested performance parameters; realtime streaming\n");
printf("\t\t\tDevice does %shave the `read buffer capacity' command \n", (pos[0] & 16)?"":"NOT ");
printf("\t\t\tDevice CD speed can %sbe set up\n", (pos[0] & 8)?"":"NOT ");
printf("\t\t\tDevice write speed performance can %sbe queried\n", (pos[0] & 4)?"":"NOT ");
printf("\t\t\tDevice does %shave a `set streaming' command\n", (pos[0] & 2)?"":"NOT ");
printf("\t\t\tDevice does %ssupport stream recording operation\n", (pos[0] & 1)?"":"NOT ");
break;
case 0x0108:
printf("\t\tThis device has an unique serialnumber : \"");
for (cnt=0; cnt < feature_len; cnt++) {
printf("%c", pos[cnt]);
}
printf("\"\n");
break;
case 0x010a:
printf("\t\tThe ability to read and/or write Disc Control Blocks (DVD only)\n");
for (cnt=0; cnt < feature_len; cnt+=4, pos+=4) {
dcb_entry = pos[3] | (pos[2] << 8) | (pos[1] << 16);
printf("\t\t\t\tSupported Disc Control Blocks content descriptor %d\n", dcb_entry);
}
printf("\n");
break;
case 0x010b:
printf("\t\tThe Logical Unit supports DVD CPRM authentication\n");
printf("\t\t\tSupporting CPRM version %d\n", pos[3]);
break;
case 0x01ff:
printf("\t\tFirmware creation date report :\n");
century = pos[ 1] | (pos[ 0] << 8);
year = pos[ 3] | (pos[ 2] << 8);
month = pos[ 5] | (pos[ 4] << 8);
day = pos[ 7] | (pos[ 6] << 8);
hour = pos[ 9] | (pos[ 8] << 8);
minute = pos[11] | (pos[10] << 8);
printf("\t\t\tFirmware version date %d-%d-%2d%2d at time %02d:%02d\n", day, month, century, year, hour, minute);
break;
default:
break;
}
printf("\n");
}
void dump_drive_configuration(int feature_current) {
scsicmd cmd;
uint8_t features[1024+100], *fpos;
uint32_t feature, last_feature, features_len, feat_tbl_len;
uint32_t current_profile, pos;
uint32_t feature_cur, feature_len;
int error;
printf("Getting drive configuration\n");
feat_tbl_len = 1024;
last_feature = feature = 0;
do {
bzero(cmd, SCSI_CMD_LEN);
cmd[0] = 0x46; /* Get configuration */
cmd[1] = 0; /* RT=0 -> all independent of current setting */
cmd[2] = (last_feature) >> 8; /* MSB feature number */
cmd[3] = (last_feature) & 0xff; /* LSB feature number */
cmd[7] = (feat_tbl_len) >> 8; /* MSB buffersize */
cmd[8] = (feat_tbl_len) & 0xff; /* LSB buffersize */
cmd[9] = 0; /* control */
error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, features, feat_tbl_len, 10000, NULL);
if (error) {
fprintf(stderr, "While reading feature table : %s\n", strerror(error));
return;
}
features_len = features[3] | (features[2]<<8) | (features[1]<<16) | (features[0]<<24);
current_profile = features[7] | (features[6]<<8);
printf("\tFeatures table length %d bytes\n", features_len);
printf("\tCurrent profile 0x%04x `%s'\n", current_profile, print_mmc_profile(current_profile));
pos = 8;
while (pos < features_len) {
fpos = &features[pos];
feature = fpos[1] | (fpos[0] << 8);
feature_cur = (fpos[2] & 1);
feature_len = fpos[3];
if (feature_cur == feature_current) dump_feature(fpos);
last_feature = MAX(last_feature, feature);
if ((feature_len & 3) != 0) {
printf("\t\t*** drive returned bad feature length ***\n");
dump_feature(fpos);
feature_len = (feature_len + 3) & ~3;
}
pos += 4 + feature_len;
}
} while (features_len >= 0xffff);
}
#define max_di_len 1000
#define max_ti_len 128
void dump_disc_information(void) {
scsicmd cmd;
uint8_t di[max_di_len], ti[max_ti_len];
int di_len = max_di_len, ti_len = max_ti_len;
int disc_status, disc_type, last_session_state, eraseable;
int num_sessions;
int first_track, first_track_last_session, last_track_last_session;
int data_length, is_track_number, is_session_number, is_track_mode, is_data_mode;
int is_copy, is_damage, is_fixed_packet, is_packet_or_inc, is_blank, is_reserved;
int nwa_valid, lra_valid;
uint32_t track_start, next_writable_addr, free_blocks, packet_size, track_size, last_recorded_addr;
uint32_t disc_id, disc_barcode_l, disc_barcode_h;
uint64_t disc_barcode;
int track, speed, pos, num_opc_tables;
int printed, error;
bzero(di, di_len);
bzero(ti, ti_len);
/* read in fixed part */
di_len = 34;
bzero(cmd, SCSI_CMD_LEN);
cmd[0] = 0x51; /* Read disc information */
cmd[7] = 0; /* MSB allocation length */
cmd[8] = di_len; /* LSB allocation length */
cmd[9] = 0; /* control */
error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, di, di_len, 10000, NULL);
if (error) {
fprintf(stderr, "While reading disc information : %s\n", strerror(error));
return;
}
di_len = di[1] | (di[0]<<8);
bzero(cmd, SCSI_CMD_LEN);
cmd[0] = 0x51; /* Read disc information */
cmd[7] = di_len >> 8; /* MSB allocation length */
cmd[8] = di_len & 0xff; /* LSB allocation length */
cmd[9] = 0; /* control */
error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, di, di_len, 10000, NULL);
if (error) {
fprintf(stderr, "While reading disc information : %s\n", strerror(error));
return;
}
di_len = di[1] | (di[0]<<8);
printf("\tDrive returned %d bytes of data\n", di_len);
disc_type = di[ 8];
disc_status = di[ 2] & 3;
last_session_state = (di[ 2] >> 2) & 3;
eraseable = di[ 2] & 16;
first_track = di[ 3];
num_sessions = di[ 4] | (di[ 9] << 8);
first_track_last_session = di[ 5] | (di[10] << 8);
last_track_last_session = di[ 6] | (di[11] << 8);
disc_id = di[15] | (di[14]<<8) | (di[13]<<16) | (di[12]<<24);
disc_barcode_l = di[31] | (di[30]<<8) | (di[29]<<16) | (di[28]<<24);
disc_barcode_h = di[27] | (di[26]<<8) | (di[25]<<16) | (di[24]<<24);
disc_barcode = ((uint64_t) disc_barcode_h << 32) | disc_barcode_l;
num_opc_tables = di[33];
if (di_len < 33) num_opc_tables = 0;
printf("\tDisc type : %s\n", print_disc_type(disc_type));
printf("\tDisc status : %s\n", print_disc_state(disc_status));
printf("\tKind of disc : %s\n", eraseable ? "eraseable (rewritable disc)": "NOT eraseable (recordable disc)");
printf("\tFirst track number : %d\n", first_track);
printf("\tNumber of sessions : %d\n", num_sessions);
printf("\tDrive does %ssupport setting OPC information; num tables %d\n", num_opc_tables ? "": "NOT ", num_opc_tables);
printf("\tLast session information :\n");
printf("\t\tState : %s\n", print_session_state(last_session_state));
printf("\t\tFrom track %d to track %d (including hidden)\n", first_track_last_session, last_track_last_session);
printf("\tDisc is %sin 'Restricted Use Mode'\n", (di[7] & 32) ? "": "NOT ");
switch (di[7] & 3) {
case 0 : printf("\tBackground formatting not applicable"); break;
case 1 : printf("\tDisc formatting was interrupted and not running now"); break;
case 2 : printf("\tDisc is being formatted in the background"); break;
case 3 : printf("\tDisc is formatted"); break;
}
printf("\n");
printf("\tDisc has %s valid 'Disc ID' : (0x%08"PRIx32")\n", (di[7] & 128) ? "a ": "NO", disc_id);
printf("\tDisc has %s valid 'Disc Bar Code' : (0x%016"PRIx64")\n", (di[7] & 64) ? "a ": "NO", disc_barcode);
printf("\tDisc has %s valid 'Disc Application code' : (0x%x)\n", (di[7] & 16) ? "a" : "NO", di[32]);
printf("\tLast Session Lead-in Start Address not dumped\n");
printf("\tLast Possible Lead-out Start Address not dumped\n");
printf("\n");
if (di_len < 33) {
printf("\tDrive reported no recording speeds and OPC data\n");
} else {
printf("\tRecording speeds (in kB/sec) (OPC data not dumped) :\n\t\t");
pos = 34;
printed = 0;
while (pos < di_len) {
speed = di[pos+1] | (di[pos] << 8);
if (speed) {
printed = 1;
printf("%d", speed);
if (pos < di_len-8) printf(", ");
}
pos += 8;
}
if (!printed)
printf("\t\tAparently no speeds were returned");
printf("\n");
}
printf("\nReading track and session information; track by track\n");
for (track = first_track; track <= last_track_last_session; track++) {
ti_len = 39;
bzero(cmd, SCSI_CMD_LEN);
bzero(ti, ti_len);
cmd[0] = 0x52; /* Read track information */
cmd[1] = 1; /* indexed on track */
cmd[4] = track >> 8; /* track number 0-0xff ? */
cmd[5] = track & 0xff;
cmd[7] = ti_len >> 8;
cmd[8] = ti_len & 0xff;
cmd[9] = 0;
error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, ti, ti_len, 10000, NULL);
if (error) {
fprintf(stderr, "While reading track info : %s\n", strerror(error));
break;
}
data_length = ti[1] | (ti[0] << 8);
is_track_number = ti[2] | (ti[32] << 8);
is_session_number = ti[3] | (ti[33] << 8);
is_track_mode = ti[5] & 15;
is_copy = ti[5] & 16;
is_damage = ti[5] & 32;
is_data_mode = ti[6] & 15;
is_fixed_packet = ti[6] & 16;
is_packet_or_inc = ti[6] & 32;
is_blank = ti[6] & 64;
is_reserved = ti[6] & 128;
is_data_mode = ti[6] & 15;
nwa_valid = ti[7] & 1;
lra_valid = ti[7] & 2;
track_start = ti[11] | (ti[10]<<8) | (ti[ 9]<<16) | (ti[ 8]<<24);
next_writable_addr = ti[15] | (ti[14]<<8) | (ti[13]<<16) | (ti[12]<<24);
free_blocks = ti[19] | (ti[18]<<8) | (ti[17]<<16) | (ti[16]<<24);
packet_size = ti[23] | (ti[22]<<8) | (ti[21]<<16) | (ti[20]<<24);
track_size = ti[27] | (ti[26]<<8) | (ti[25]<<16) | (ti[24]<<24);
last_recorded_addr = ti[31] | (ti[30]<<8) | (ti[29]<<16) | (ti[28]<<24);
if (data_length <= 30) {
/* 8 bits track and session numbers returned; last_recordable is invalid */
is_track_number &= 0xff;
is_session_number &= 0xff;
last_recorded_addr = 0;
}
printf("\tTrack %d of session %d\n", is_track_number, is_session_number);
if (data_length < 27 && lra_valid) printf("\t\tLast recorded addres valid but not returned\n");
printf("\t\tStart at : %d\n", track_start);
printf("\t\tLength : %d\n", track_size);
printf("\t\tTrackmode : %s\n", print_Q_control(is_track_mode));
printf("\t\tDatamode : %s\n", (is_data_mode == 1) ? "mode 1" : "mode 2");
if (free_blocks) printf("\t\tFree blocks : %d\n", free_blocks);
if (packet_size) printf("\t\tPacket size : %d\n", packet_size);
if (nwa_valid) printf("\t\tNext writable : %d\n", next_writable_addr);
if (lra_valid) printf("\t\tLast recorded : %d\n", last_recorded_addr);
if (is_copy) printf("\t\tTrack is a copy\n");
if (is_reserved) printf("\t\tReserved or Complete track\n");
if (is_blank) printf("\t\tBlank track\n");
if (is_packet_or_inc) printf("\t\tPacket mode track\n");
if (is_fixed_packet) printf("\t\tFixed packet sizes track\n");
if (data_length > ti_len) {
printf("\t\tRest %d bytes undumed\n", ti_len - data_length);
}
}
}
#undef di_len
#undef ti_len
void dump_format_capabilities(void) {
scsicmd cmd;
uint8_t buf[512], *fcd;
uint32_t num_blks, param;
char *format_str, *nblks_str, *param_str;
int dscr_type, list_length, format_type;
int buf_len = 512, trans_len;
int error;
bzero(buf, buf_len);
trans_len = 12; /* only fixed header first */
bzero(cmd, SCSI_CMD_LEN);
cmd[0] = 0x23; /* Read format capabilities */
cmd[7] = trans_len >> 8; /* MSB allocation length */
cmd[8] = trans_len & 0xff; /* LSB allocation length */
cmd[9] = 0; /* control */
error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, buf, trans_len, 10000, NULL);
if (error) {
fprintf(stderr, "While reading format capabilities : %s\n", strerror(error));
return;
}
list_length = buf[ 3];
printf("\tCurrent/max capacity followed by additional capacity, reported length of %d bytes (8/entry)\n", list_length);
if (list_length % 8) {
printf("\t\tWarning: violating SCSI spec, capacity list length ought to be multiple of 8\n");
printf("\t\tInterpreting as including header of 4 bytes\n");
assert(list_length % 8 == 4);
list_length -= 4;
}
/* read in full capacity list */
trans_len = 12 + list_length; /* complete structure */
bzero(cmd, SCSI_CMD_LEN);
cmd[0] = 0x23; /* Read format capabilities */
cmd[7] = trans_len >> 8; /* MSB allocation length */
cmd[8] = trans_len & 0xff; /* LSB allocation length */
cmd[9] = 0; /* control */
error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, buf, trans_len, 10000, NULL);
if (error) {
fprintf(stderr, "While reading format capabilities : %s\n", strerror(error));
return;
}
fcd = buf + 4;
list_length -= 4; /* seems to include the first entry */
while (list_length > 0) {
num_blks = fcd[ 3] | (fcd[ 2] << 8) | (fcd[ 1] << 16) | (fcd[ 0] << 24);
dscr_type = fcd[ 4] & 3;
format_type = fcd[ 4] >> 2;
param = fcd[ 7] | (fcd[ 6] << 8) | (fcd[ 5] << 16);
format_str = nblks_str = param_str = "reserved";
switch (format_type) {
case 0x00 :
format_str = "full format capacity (non packet)";
nblks_str = "sectors";
param_str = "block length in bytes";
break;
case 0x01 :
format_str = "spare area expansion";
nblks_str = "extension in blocks";
param_str = "block length in bytes";
break;
/* 0x02 - 0x03 reserved */
case 0x04 :
format_str = "variable length zone'd format";
nblks_str = "zone length";
param_str = "zone number";
break;
case 0x05 :
format_str = "fixed length zone'd format";
nblks_str = "zone lenght";
param_str = "last zone number";
break;
/* 0x06 - 0x0f reserved */
case 0x10 :
format_str = "CD-RW/DVD-RW full packet format";
nblks_str = "adressable blocks";
param_str = "fixed packet size/ECC blocksize in sectors";
break;
case 0x11 :
format_str = "CD-RW/DVD-RW grow session";
nblks_str = "adressable blocks";
param_str = "fixed packet size/ECC blocksize in sectors";
break;
case 0x12 :
format_str = "CD-RW/DVD-RW add session";
nblks_str = "adressable blocks";
param_str = "maximum fixed packet size/ECC blocksize in sectors";
break;
case 0x13 :
format_str = "DVD-RW max growth of last complete session";
nblks_str = "adressable blocks";
param_str = "ECC blocksize in sectors";
break;
case 0x14 :
format_str = "DVD-RW max quick grow last session";
nblks_str = "adressable blocks";
param_str = "ECC blocksize in sectors";
break;
case 0x15 :
format_str = "DVD-RW quick full format";
nblks_str = "adressable blocks";
param_str = "ECC blocksize in sectors";
break;
/* 0x16 - 0x23 reserved */
case 0x24 :
format_str = "MRW format";
nblks_str = "Defect Management Area blocks";
param_str = "not used";
break;
/* 0x25 reserved */
case 0x26 :
format_str = "DVD+RW full format";
nblks_str = "sectors";
param_str = "not used";
break;
/* 0x27 - 0x2f reserved */
case 0x30 :
format_str = "BD-RE full format with spare area";
nblks_str = "blocks";
param_str = "total spare area size in clusters";
break;
case 0x31 :
format_str = "BD-RE full format without spare area";
nblks_str = "blocks";
param_str = "block length in bytes";
break;
/* 0x32 - 0x3f reserved */
default :
break;
}
printf("\n\tFormat type 0x%02x : %s\n", format_type, format_str);
switch (dscr_type) {
case 1 : printf("\t\tUnformatted media, maximum formatted capacity\n"); break;
case 2 : printf("\t\tFormatted media, current formatted capacity\n"); break;
case 3 : printf("\t\tNo media present or incomplete session, maximum formatted capacity\n"); break;
default : printf("\t\tUnspecified descriptor type\n"); break;
}
printf("\t\tNumber of blocks : %12d\t(%s)\n", num_blks, nblks_str);
printf("\t\tParameter : %12d\t(%s)\n", param, param_str);
fcd += 8;
list_length-=8;
}
printf("\n\tNote: last entry might be all zero due to deviation from SCSI standard\n");
}
void dump_formatted_toc(void) {
scsicmd cmd;
uint8_t toc[10000];
uint32_t lba;
int pos, toc_len, data_length;
int cur_track, addr, tno, cntrl;
int first_track, last_track;
int min, sec, frame;
int error;
printf("\nFormatted TOC\n");
/* only fixed part first */
toc_len = 4;
/* just TOC */
bzero(cmd, SCSI_CMD_LEN);
cmd[0] = 0x43; /* READ TOC/PMA/ATIP INFO */
cmd[1] = 2; /* return HMSF */
cmd[2] = 0; /* format 0; formatted TOC */
cmd[6] = 1; /* start at first track */
cmd[7] = toc_len >> 8;
cmd[8] = toc_len & 0xff;
cmd[9] = 0; /* control */
error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, toc, toc_len, 10000, NULL);
if (error) {
fprintf(stderr, "TOC reading of tracks failed : %s\n", strerror(error));
return;
}
first_track = read_cd_hex2(toc[2]);
last_track = read_cd_hex2(toc[3]);
printf("\tFirst track : %d\n", first_track);
printf("\tLast track : %d\n", last_track);
printf("\n");
/* complete formatted TOC */
data_length =toc[1] | (toc[0] << 8);
toc_len = data_length + 2;
cmd[7] = toc_len >> 8;
cmd[8] = toc_len & 0xff;
error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, toc, toc_len, 10000, NULL);
if (error) {
fprintf(stderr, "TOC reading of complete formatted TOC failed : %s\n", strerror(error));
return;
}
/* dump formatted TOC */
data_length =toc[1] | (toc[0] << 8);
pos = 4; cur_track = 0;
while (pos < data_length + 2) {
cntrl = (toc[pos + 1] ) & 15;
addr = (toc[pos + 1] >> 4) & 15;
tno = read_cd_bcd(toc[pos + 2]);
/* hour = read_cd_bsd(toc[pos + 4]); symetry, though mandatory zero ? */
min = read_cd_bcd(toc[pos + 5]);
sec = read_cd_bcd(toc[pos + 6]);
frame = read_cd_bcd(toc[pos + 7]);
lba = cd_msf2lba(0, min, sec, frame);
if (tno != cur_track) {
if (tno == 0xAA) {
printf("\tLead-out area start");
} else {
printf("\tTrack %d start\t", tno);
}
}
cur_track = tno;
printf("\t%02d:%02d:%02d (LBA %8d)\t", min, sec, frame, lba);
printf(" (%s), ", print_Q_control(cntrl));
printf("Q sub-channel ");
switch (addr) {
case 0x0 : printf("not suplied"); break;
case 0x1 : printf("encodes current position data"); break;
case 0x2 : printf("encodes media catalog number"); break;
case 0x3 : printf("encodes ISRC"); break;
default : printf("encoding marked for reserved type (%d)", addr);
}
printf("\n");
pos += 8;
}
}
void dump_raw_toc_pma_data(uint8_t *toc) {
int pos, data_length;
int cur_session, session, cntrl, addr, tno, point;
int min, sec, frame, pmin, psec, pframe, nwa, lba, extent;
data_length =toc[1] | (toc[0] << 8);
pos = 4; cur_session = 0;
while (pos < data_length + 2) {
session = read_cd_bcd(toc[pos+ 0]);
cntrl = toc[pos+1] & 15;
addr = toc[pos+1] >> 4;
tno = read_cd_bcd(toc[pos+ 2]);
point = read_cd_bcd(toc[pos+ 3]);
min = read_cd_bcd(toc[pos+ 4]);
sec = read_cd_bcd(toc[pos+ 5]);
frame = read_cd_bcd(toc[pos+ 6]);
pmin = read_cd_bcd(toc[pos+ 8]);
psec = read_cd_bcd(toc[pos+ 9]);
pframe = read_cd_bcd(toc[pos+10]);
if (session != cur_session) {
if (session) printf("\tSession %d\n", session);
cur_session = session;
}
/* if (tno == 0 && session) { */
if (1) {
switch (addr) {
case 1 :
printf("\t\t");
switch (point) {
case 0xa0 :
printf("Disc type %s\n\t\t", print_TOC_disc_type(psec));
printf("First track number %d\t\t\t", pmin);
break;
case 0xa1 :
printf("Last track number %d\t\t\t", pmin);
break;
case 0xa2 :
lba = cd_msf2lba(0, pmin, psec, pframe);
printf("Lead out %02d:%02d.%02d (LBA %8d)", pmin, psec, pframe, lba);
break;
default :
lba = cd_msf2lba(0, pmin, psec, pframe);
printf("Track start %02d:%02d.%02d (LBA %8d)", pmin, psec, pframe, lba);
}
if (min || sec || frame)
printf("\n\t\tATIME (%02d:%02d.%02d)\t\t\t", min, sec, frame);
break;
case 2 :
printf("\t\t");
printf("Media catalog number present\n");
break;
case 3 :
printf("\t\t");
printf("International Standard Recording Code (ISRC) present\n");
break;
case 5 :
printf("\t\t");
switch (point) {
case 0xb0 :
nwa = cd_msf2lba(0, min, sec, frame);
extent = cd_msf2lba(0,pmin, psec, pframe);
printf("Next writable at %02d:%02d.%02d (LBA %8d)", min, sec, frame, nwa);
printf("\n\t\t");
printf("Lead out max at %02d:%02d.%02d (LBA %8d)", pmin, psec, pframe, extent);
/* printf("\n\t\t"); */
/* printf("Disc surface used for %2d %%\t\t", (int) (100.0 * (float) nwa / (float) extent)); */
break;
case 0xc0 :
printf("Start first lead-in %02d:%02d.%02d (LBA %8d)", pmin, psec, pframe, cd_msf2lba(0, pmin, psec, pframe));
printf("\n\t\t");
printf("Optimum recording power %d\t\t", min);
break;
case 0xc1 :
printf("Copy of A1 point from ATIP\t\t");
break;
default :
printf("<undumped %d, %d, %02x pair>\t\t", addr, cntrl, point);
break;
}
break;
default :
printf("\t\t<undumped %d, %d, %02x pair>\t\t", addr, cntrl, point);
break;
}
printf("\t (");
if ((cntrl & 12) == 4) {
printf("data track ");
if (cntrl & 1) printf("; incremental "); else printf("; uninterrupted");
} else {
printf("audio track");
if (cntrl & 1) printf("; pre-emphasis of 50/15 µs"); else printf("; no pre-emphasis");
}
if (cntrl & 2) printf("; copy prohibited");
printf(")\n");
}
pos += 11;
}
}
void dump_raw_toc_info(void) {
scsicmd cmd;
uint8_t toc[10000];
int toc_len;
int data_length;
int first_session;
int error;
printf("\nRaw TOC\n");
printf("\tLba numbers are indicative only\n");
/* return only fixed part or multi-session info */
toc_len = 4;
bzero(cmd, SCSI_CMD_LEN);
cmd[0] = 0x43; /* READ TOC/PMA/ATIP INFO */
cmd[1] = 0; /* LBA's preferably (not relevant) */
cmd[2] = 1; /* format 1; multi-session info */
cmd[7] = (toc_len >> 8) & 0xff;
cmd[8] = (toc_len ) & 0xff;
cmd[9] = 0; /* control */
error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, toc, toc_len, 10000, NULL);
if (error) {
fprintf(stderr, "TOC reading of multi-session info failed : %s\n", strerror(error));
return;
}
/* XXX or just toc[2] ? */
first_session = read_cd_hex2(toc[2]);
/* return only fixed part of raw TOC */
toc_len = 4;
bzero(cmd, SCSI_CMD_LEN);
cmd[0] = 0x43; /* READ TOC/PMA/ATIP INFO */
cmd[1] = 2; /* officially no LBA's are defined */
cmd[2] = 2; /* format 2; raw TOC */
cmd[6] = first_session; /* start at first session */
cmd[7] = toc_len >> 8;
cmd[8] = toc_len & 0xff;
cmd[9] = 0; /* control */
error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, toc, toc_len, 10000, NULL);
if (error) {
fprintf(stderr, "TOC reading of fixed part of raw TOC failed : %s\n", strerror(error));
return;
}
/* read in complete raw TOC */
data_length =toc[1] | (toc[0] << 8);
toc_len = data_length; /* dont count length */
/* patch for ATAPI: make it even length */
if (toc_len & 1)
toc_len++;
cmd[7] = toc_len >> 8;
cmd[8] = toc_len & 0xff;
error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, toc, toc_len, 10000, NULL);
if (error) {
fprintf(stderr, "TOC reading of complete raw TOC failed : %s\n", strerror(error));
return;
}
dump_raw_toc_pma_data(toc);
printf("\nPMA\n");
printf("\tLba numbers are indicative only\n");
/* return only fixed part of PMA */
toc_len = 4;
bzero(cmd, SCSI_CMD_LEN);
cmd[0] = 0x43; /* READ TOC/PMA/ATIP INFO */
cmd[1] = 2; /* officially no LBA's are defined */
cmd[2] = 3; /* format 3; PMA */
cmd[6] = first_session; /* start at first session */
cmd[7] = toc_len >> 8;
cmd[8] = toc_len & 0xff;
cmd[9] = 0; /* control */
error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, toc, toc_len, 10000, NULL);
if (error) {
fprintf(stderr, "TOC reading of fixed part of PMA failed : %s\n", strerror(error));
return;
}
/* read in complete PMA */
data_length =toc[1] | (toc[0] << 8);
toc_len = data_length + 2;
/* patch for ATAPI: make it even length */
if (toc_len & 1)
toc_len++;
cmd[7] = toc_len >> 8;
cmd[8] = toc_len & 0xff;
error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, toc, toc_len, 10000, NULL);
if (error) {
fprintf(stderr, "TOC reading of complete PMA failed : %s\n", strerror(error));
return;
}
dump_raw_toc_pma_data(toc);
}
void dump_toc_pma_atip_info(void) {
dump_formatted_toc();
dump_raw_toc_info();
}
char *prchange(int val) {
if (val) return "[changable]";
return "[fixed] ";
}
void dump_parameter_page(uint8_t *pos, uint8_t *change) {
int page_code, page_length;
int write_type, track_mode, data_block_type, link_size;
int testwrite, linksize_valid, zerolinking;
int appl_code, session_format, packet_size, audio_pause;
int multi_session, fixed_packets, copybit;
uint32_t recovery_time_limit, inactiv_time_mult, S_per_MSF, F_per_MSF;
uint32_t idle_timer_value, standby_timer_value, interval_timer_value;
uint32_t group1_timeouts, group2_timeouts;
int i, j, c, per_emcdr;
page_code = pos[0] & 63;
page_length = pos[1];
printf("\tGot page 0x%02x (%spersistent) : %2d bytes for ", page_code, pos[0] & 128? "":"non ", page_length);
switch (page_code) {
case 0x01 :
printf("read/write error recovery page\n");
printf("\t\tError recovery behaviour %s %d\n", prchange(change[2]), pos[2]);
printf("\t\t\tAutomatic Write Reallocation Enabled (AWRE) %s %s\n", prchange(change[2] & 128), pos[2] & 128 ? "On":"Off");
printf("\t\t\tAutomatic Read Reallocation Enabled (ARRE) %s %s\n", prchange(change[2] & 64), pos[2] & 64 ? "On":"Off");
printf("\t\tError recovery parameter %s %d\n", prchange(change[2] & 63), pos[2] & 63);
printf("\t\t\tTransfer Block (TB) %s %s\n", prchange(change[2] & 32), pos[2] & 32 ? "On":"Off");
printf("\t\t\tRead Continuous (RC) %s %s\n", prchange(change[2] & 16), pos[2] & 16 ? "On":"Off");
printf("\t\t\t<Reserved> %s %s\n", prchange(change[2] & 8), pos[2] & 8 ? "On":"Off");
printf("\t\t\tPost Error (PER) %s %s\n", prchange(change[2] & 4), pos[2] & 4 ? "On":"Off");
printf("\t\t\tDisable Transfer on Error (DTE) %s %s\n", prchange(change[2] & 2), pos[2] & 2 ? "On":"Off");
printf("\t\t\tDisable Correction (DCR) %s %s\n", prchange(change[2] & 1), pos[2] & 1 ? "On":"Off");
printf("\t\tEnhanced Media Certification and Defect Reporting %s %d\n", prchange(change[7] & 3), pos[7] & 3);
per_emcdr = (pos[2] & 4) + (pos[7] & 3);
if (per_emcdr == 0)
printf("\t\t\tNo certification of medium on read operations and it will not report recovered errors\n");
if (per_emcdr >=1)
printf("\t\t\tCertification of medium on read and on verify operation enabled; ");
if (per_emcdr == 1)
printf("shall not report recovered error\n");
if (per_emcdr == 2)
printf("shall report recovered error or unrecovered error on verify operation.\n");
if (per_emcdr == 3)
printf("shall report recovered error or unrecovered error on read operation and verify operation\n");
if (per_emcdr == 4)
printf("shall report recovered error if higher lever error correction is used\n");
if (per_emcdr >= 5)
printf("shall report recovered error as RECOVERED ERROR/RECOVERED DATA - RECOMMEND REASSIGNMENT\n");
printf("\t\tRead retry count %s %d\n", prchange(change[3]), pos[3]);
printf("\t\tWrite retry count %s %d\n", prchange(change[8]), pos[8]);
recovery_time_limit = pos[11] + (pos[10] << 8);
printf("\t\tRecovery time limit %s %d\n", prchange(change[11]), recovery_time_limit);
printf("\n");
break;
case 0x03 :
printf("MRW mode page\n");
printf("\t\t`%s' LBA space selected\n", pos[3] & 1 ? "General Application Area" : "Defect Managed Area");
printf("\n");
break;
case 0x05 :
printf("write parameter page\n");
write_type = pos[2] & 15;
track_mode = pos[3] & 15;
data_block_type = pos[4] & 15;
testwrite = pos[2] & 16;
linksize_valid = pos[2] & 32;
zerolinking = pos[2] & 64;
multi_session = pos[3] >> 6;
fixed_packets = pos[3] & 32;
copybit = pos[3] & 16;
link_size = pos[5];
appl_code = pos[7] & 63;
session_format = pos[8];
packet_size = pos[13] + (pos[12] << 8) + (pos[11] << 16) + (pos[10] << 24);
audio_pause = pos[15] + (pos[14] << 8);
printf("\t\tWrite type %s %s\n", prchange(change[2] & 15), print_write_type(write_type));
printf("\t\tTrack mode %s %d (mode 1 Q subchannel control nibble)\n", prchange(change[3] & 15), track_mode);
printf("\t\tData block type %s %s\n", prchange(change[4] & 15), print_data_block_type(data_block_type));
printf("\t\tTestwriting %s %s\n", prchange(change[2] & 16), testwrite ? "On":"Off");
printf("\t\tLinksize %s %d sectors (%s)\n", prchange(change[5]), link_size, linksize_valid ? "valid": "not valid (7)");
printf("\t\tApplication code %s %d\n", prchange(change[7] & 63), appl_code);
printf("\t\tSession format %s %s\n", prchange(change[8]), print_session_format(session_format));
printf("\t\tUsing fixed packets %s %s\n", prchange(change[3] & 32), fixed_packets ? "On":"Off");
printf("\t\tCopybit %s %s\n", prchange(change[3] & 16), copybit ? "On":"Off");
printf("\t\tMulti session field %s %d\n", prchange(change[3] >> 6), multi_session);
printf("\t\tPacket size %s %d\n", prchange(change[13]), packet_size);
printf("\t\tAudio pause length %s %d\n", prchange(change[15]), audio_pause);
printf("\t\tMedia catalog number %s 0x", prchange(change[31]));
for (i = 31; i >= 16; i--) printf("%02x", pos[i]);
printf("\n");
printf("\t\tInitiational standard recording code %s 0x", prchange(change[47]));
for (i = 47; i >= 32; i--) printf("%02x", pos[i]);
printf("\n");
printf("\t\tSubheader byte 0 %s 0x%02x\n", prchange(change[48]), pos[48]);
printf("\t\tSubheader byte 1 %s 0x%02x\n", prchange(change[49]), pos[49]);
printf("\t\tSubheader byte 2 %s 0x%02x\n", prchange(change[50]), pos[50]);
printf("\t\tSubheader byte 3 %s 0x%02x\n", prchange(change[51]), pos[51]);
printf("\n");
break;
case 0x07 :
printf("verify error recovery mode page\n");
printf("\t\tNot dumped yet. (And not permitted for MM LUs)\n");
printf("\n");
break;
case 0x08 :
printf("caching mode page\n");
printf("\t\tWrite cache %s %s\n", prchange(change[2] & 4), pos[2] & 4 ? "enabled" : "disabled");
printf("\t\tRead cache %s %s\n", prchange(change[2] & 1), pos[2] & 1 ? "disabled" : "enabled");
printf("\n");
break;
case 0x0b :
printf("media types supported page\n");
printf("\t\tNot dumped yet. (And not permitted for MM LUs)\n");
printf("\n");
break;
case 0x0d :
printf("cd device parameters page\n");
inactiv_time_mult = pos[3] & 15;
S_per_MSF = pos[5] + (pos[4] << 8);
F_per_MSF = pos[7] + (pos[6] << 8);
printf("\t\tHold track inactivity time %s value %d (%s)\n", prchange(change[3] & 15), inactiv_time_mult, print_inactivity_time(inactiv_time_mult));
printf("\t\tSeconds per MSF %s %d\n", prchange(change[5]), S_per_MSF);
printf("\t\tFrames per MSF %s %d\n", prchange(change[7]), F_per_MSF);
printf("\n");
break;
case 0x0e :
printf("cd audio control page\n");
printf("\t\tStop on track crossing %s %s\n", prchange(change[ 2] & 2 ), pos[2] & 2 ? "On":"Off");
printf("\t\tCDDA output port 0 channel %s %d\n", prchange(change[ 8] & 31), pos[ 8] & 31);
printf("\t\tOutput port 0 volume %s %d\n", prchange(change[ 9] ), pos[ 9]);
printf("\t\tCDDA output port 1 channel %s %d\n", prchange(change[10] & 31), pos[10] & 31);
printf("\t\tOutput port 1 volume %s %d\n", prchange(change[11] ), pos[11]);
printf("\t\tCDDA output port 2 channel %s %d\n", prchange(change[12] & 31), pos[12] & 31);
printf("\t\tOutput port 2 volume %s %d\n", prchange(change[13] ), pos[13]);
printf("\t\tCDDA output port 3 channel %s %d\n", prchange(change[14] & 31), pos[14] & 31);
printf("\t\tOutput port 3 volume %s %d\n", prchange(change[15] ), pos[15]);
printf("\n");
break;
case 0x1a :
printf("power condition page\n");
idle_timer_value = pos[ 7] + (pos[ 6] << 8) + (pos[ 5] << 16) + (pos[ 4] << 24);
standby_timer_value = pos[11] + (pos[10] << 8) + (pos[ 9] << 16) + (pos[ 8] << 24);
printf("\t\tIdle timer activate %s %s\n", prchange(change[3] & 2 ), pos[3] & 2 ? "On":"Off");
printf("\t\tStandby timer active %s %s\n", prchange(change[3] & 1 ), pos[3] & 1 ? "On":"Off");
printf("\t\tIdle timer start value %s %d * 100ms\n", prchange(change[ 7] & 1 ), idle_timer_value);
printf("\t\tStandby timer start value %s %d * 100ms\n", prchange(change[11] & 1 ), standby_timer_value);
printf("\n");
break;
case 0x1c :
printf("informational exceptions control page (not checked yet)\n");
interval_timer_value = pos[ 7] + (pos[ 6] << 8) + (pos[ 5] << 16) + (pos[ 4] << 24);
printf("\t\tLogging %s %s\n", prchange(pos[2] & 1), pos[2] & 1 ? "standard" : "vendor specific");
printf("\t\tTest device failure notification generation %s %s\n", prchange(pos[2] & 4), pos[2] & 4 ? "On" : "Off");
printf("\t\tInterval timer if enabled %s %d * 100ms (?)\n", prchange(pos[7] & 1), interval_timer_value);
printf("\t\tRest undumped\n");
printf("\n");
break;
case 0x1d :
printf("timeout and protect page\n");
group1_timeouts = pos[7] + (pos[6] << 8);
group2_timeouts = pos[9] + (pos[8] << 8);
printf("\t\tTimeouts commands enabled %s %s\n", prchange(change[4] & 4), pos[4] & 4 ? "On":"Off");
printf("\t\tDisable device till powerd. %s %s\n", prchange(change[4] & 2), pos[4] & 2 ? "On":"Off");
printf("\t\tSoftware write protect %s %s\n", prchange(change[4] & 1), pos[4] & 1 ? "On":"Off");
if (page_length >= 8)
printf("\t\tGroup 1 minimum time outs %s %d\n", prchange(change[7]), group1_timeouts);
if (page_length >= 10)
printf("\t\tGroup 2 minimum time outs %s %d\n", prchange(change[9]), group2_timeouts);
printf("\n");
break;
case 0x2a :
printf("CD/DVD capabilities and mechanical status page\n");
printf("\t\tNot dumped yet\n");
printf("\n");
break;
default :
/* dump bytes in hex:char notation */
printf("Reserved/vendor specific\n");
printf("\t\t ");
for (j = 0; j < 16; j++) {
printf("%02x ", j);
}
printf(" ASCII\n");
for (i = 2; i < page_length+2; i+= 16) {
printf("\t\t%02x ", i-2);
for (j = 0; j < 16; j++) {
if (i+j < page_length+2) {
printf("%02x ", pos[i+j]);
} else {
printf(" ");
}
}
printf(": ");
for (j = 0; j < 16; j++) {
if (i+j < page_length+2) {
c = pos[i+j];
if (c < 32 || c == 127)
c = '.';
printf("%c", c);
} else {
printf(" ");
}
}
printf("\n");
}
printf("\n");
break;
}
}
#define max_blk_len 60000
void dump_parameter_pages(void) {
scsicmd cmd;
int blk_len = max_blk_len, val_length, changable_length;
uint8_t val[max_blk_len], changable[max_blk_len], zeros[256];
int voff, choff, vpage_code, vpage_length, chpage_code, chpage_length;
int error;
bzero(val, blk_len);
bzero(changable, blk_len);
bzero(zeros, 256);
bzero(cmd, SCSI_CMD_LEN);
cmd[0] = 0x5A; /* MODE SENSE (10) */
cmd[1] = 0; /* allow multiple blocks */
cmd[2] = 0x3F; /* return all current values */
cmd[7] = blk_len >> 8; /* MSB block length */
cmd[8] = blk_len & 0xff; /* LSB block length */
cmd[9] = 0; /* control */
error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, val, blk_len, 10000, NULL);
if (error) {
fprintf(stderr, "While reading all parameter pages : %s\n", strerror(error));
return;
}
val_length = val[1] | (val[0] << 8);
printf("\tSCSI mode sense for all pages returned %d bytes of data\n", val_length);
cmd[2] = 64 | 0x3F; /* get all changable values */
error = uscsi_command(SCSI_READCMD, &dev, cmd, 10, changable, blk_len, 10000, NULL);
if (error) {
fprintf(stderr, "While reading all changable parameter pages : %s\n", strerror(error));
}
changable_length = changable[1] | (changable[0] << 8);
printf("\tSCSI mode sense changable parameters list returned %d bytes of data\n", changable_length);
/* read the mode sense block and process page by page */
voff = 8;
while (voff < val_length) {
vpage_code = val[voff ] & 63;
vpage_length = val[voff + 1];
if (vpage_code) {
choff = 8; chpage_code = -1;
while (choff < changable_length) {
chpage_code = changable[choff ] & 63;
chpage_length = changable[choff + 1];
if (chpage_code == vpage_code) break;
choff += chpage_length + 2;
}
if (chpage_code == vpage_code) {
dump_parameter_page(val + voff, changable + choff);
} else {
printf("\tWarning: no matching changable bits page was found\n");
dump_parameter_page(val + voff, zeros);
}
}
voff += vpage_length + 2;
}
}
#undef max_blk_len
int main(int argc, char **argv) {
scsicmd cmd;
uint8_t buf[36];
struct uscsi_addr saddr;
int drive_type;
int error;
bzero(&dev, sizeof(dev));
if (argc != 2) {
printf("Usage : %s devicename\n", argv[0]);
return 1;
}
/* Open the device */
dev.dev_name = strdup(argv[1]);
printf("Opening device %s\n", dev.dev_name);
if (uscsi_open(&dev) != 0) {
exit(1);
}
error = uscsi_check_for_scsi(&dev);
if (error) {
fprintf(stderr, "sorry, not a SCSI device : %s\n", strerror(error));
exit(1);
}
error = uscsi_identify(&dev, &saddr);
if (error) {
fprintf(stderr, "SCSI identify returned : %s\n", strerror(error));
exit(1);
}
printf("\n\nDevice attachment identifies itself as : ");
switch (saddr.type) {
case USCSI_TYPE_SCSI :
printf("SCSI busnum = %d, target = %d, lun = %d\n",
saddr.addr.scsi.scbus, saddr.addr.scsi.target, saddr.addr.scsi.lun);
break;
case USCSI_TYPE_ATAPI :
printf("ATAPI busnum = %d, drive = %d\n",
saddr.addr.atapi.atbus, saddr.addr.atapi.drive);
break;
case USCSI_TYPE_UNKNOWN :
printf("Unknown attachment\n");
}
printf("\n\nTest unit ready\n");
bzero(cmd, SCSI_CMD_LEN);
cmd[0] = 0; /* test unit ready */
error = uscsi_command(SCSI_READCMD, &dev, cmd, 6, buf, 0, 10000, NULL);
if (error) perror("SCSI test unit ready returned : ");
printf("Device identifies itself as : ");
dump_drive_identify(&drive_type);
printf("\n\nCURRENT features\n");
dump_drive_configuration(1);
printf("\n\nCAPAPLE features\n");
dump_drive_configuration(0);
printf("\n\nRead recorded capacity\n");
dump_recorded_capacity();
if (drive_type == DEVICE_TYPE_MMC) {
printf("\n\nDisc information\n");
dump_disc_information();
}
printf("\n\nFormat capabilities\n");
dump_format_capabilities();
if (drive_type == DEVICE_TYPE_MMC) {
printf("\n\nReading TOC/PMA/ATIP\n");
dump_toc_pma_atip_info();
}
if (drive_type == DEVICE_TYPE_MMC) {
/* normally support mode sense 10 */
printf("\n\nReading SCSI 'mode sense' 10 parameter page\n");
dump_parameter_pages();
} else {
printf("\n\nNo checking for `mode sense' 6 parameter page yet\n");
}
uscsi_close(&dev);
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1