/*
* Copyright (c) 2006, 2008 Reinoud Zandijk
* 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.
*
* 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.
*
*/
/*
* not be to impatient... give the drive some time to start or it
* might break off
*/
start_time = getmtime();
sleep(10);
progress = 0;
while (progress < 0x10000) {
/* we need a command that is NOT going to stop the formatting */
bzero(cmd, SCSI_CMD_LEN);
cmd[0] = 0; /* test unit ready */
uscsi_command(SCSI_READCMD, mydev,
cmd, 6, buffer, 0, 10000, &sense);
/*
* asc may be `not-ready' or `no-sense'. ascq for format in
* progress is 4 too
*/
asc = sense.asc;
ascq = sense.ascq;
if (((asc == 0) && (ascq == 4)) || (asc == 4)) {
/* drive not ready : operation/format in progress */
if (sense.skey_valid) {
progress = sense.sense_key;
} else {
/* finished */
progress = 0x10000;
}
}
/* check if drive is ready again, ifso break out loop */
if ((asc == 0) && (ascq == 0)) {
progress = 0x10000;
}
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;
}
/* format a CD-RW disc */
/* old style format 7 */
static int
uscsi_format_cdrw_mode7(struct uscsi_dev *mydev, uint32_t blocks)
{
scsicmd cmd;
struct uscsi_sense sense;
uint8_t buffer[16];
int error;
/* 8 bytes of format descriptor */
/* (s)ession bit 1<<7, (g)row bit 1<<6 */
/* SG action */
/* 00 format disc with number of user data blocks */
/* 10 create new session with number of data blocks */
/* x1 grow session to be number of data blocks */
/* this will take a while .... */
error = uscsi_command(SCSI_WRITECMD, mydev,
cmd, 6, buffer, sizeof(buffer), UINT_MAX, &sense);
if (error)
return error;
uscsi_waitop(mydev);
return 0;
}
static int
uscsi_format_disc(struct uscsi_dev *mydev, int immed, int format_type,
uint32_t blocks, uint32_t param, int certification, int cmplist)
{
scsicmd cmd;
struct uscsi_sense sense;
uint8_t buffer[16], fmt_flags;
int error;
fmt_flags = 0x80; /* valid info flag */
if (immed)
fmt_flags |= 2;
if (certification == 0)
fmt_flags |= 32;
if (cmplist)
cmplist = 8;
#if 0
if (mmc_profile != 0x43) {
/* certification specifier only valid for BD-RE */
certification = 0;
}
#endif
/* this will take a while .... */
error = uscsi_command(SCSI_WRITECMD, mydev,
cmd, 12, NULL, 0, UINT_MAX, NULL);
if (error)
return error;
uscsi_waitop(mydev);
return 0;
}
static int
usage(char *program)
{
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [options] devicename\n", program);
fprintf(stderr,
"-B blank cd-rw disc before formatting\n"
"-F format cd-rw disc\n"
"-O CD-RW formatting 'old-style' for old CD-RW drives\n"
"-M select MRW format\n"
"-R restart MRW & DVD+RW format\n"
"-G grow last CD-RW/DVD-RW session\n"
"-S grow spare space DVD-RAM/BD-RE\n"
"-s format DVD+MRW/BD-RE with extra spare space\n"
"-w wait until completion of background format\n"
"-p explicitly set packet format\n"
"-c num media certification for DVD-RAM/BD-RE : "
"0 no, 1 full, 2 quick\n"
"-r recompile defect list for DVD-RAM (cmplist)\n"
"-h -H -I help/inquiry formats\n"
"-X format expert format selector form 'fmt:blks:param' with -c\n"
"-b blockingnr in sectors (for CD-RW)\n"
"-D verbose SCSI command errors\n"
);
return 1;
}
int
main(int argc, char *argv[])
{
struct uscsi_addr saddr;
uint32_t blks[256], params[256];
uint32_t format_type, format_blks, format_param, blockingnr;
uint8_t allow[256];
uint8_t caps[512];
uint32_t caps_len = sizeof(caps);
char *progname;
int blank, format, mrw, background;
int inquiry, spare, oldtimer;
int expert;
int restart_format, grow_session, grow_spare, packet_wr;
int mmc_profile, flag, error, display_usage;
int certification, cmplist;
int wait_until_finished;
progname = strdup(argv[0]);
if (argc == 1) {
return usage(progname);
}
blank = 0;
format = 0;
mrw = 0;
restart_format = 0;
grow_session = 0;
grow_spare = 0;
wait_until_finished = 0;
packet_wr = 0;
certification = 1;
cmplist = 0;
inquiry = 0;
spare = 0;
inquiry = 0;
oldtimer = 0;
expert = 0;
display_usage = 0;
blockingnr = 32;
uscsilib_verbose = 0;
while ((flag = getopt(argc, argv, "BFMRGSwpsc:rhHIX:Ob:D")) != -1) {
switch (flag) {
case 'B' :
blank = 1;
break;
case 'F' :
format = 1;
break;
case 'M' :
mrw = 1;
break;
case 'R' :
restart_format = 1;
break;
case 'G' :
grow_session = 1;
break;
case 'S' :
grow_spare = 1;
break;
case 'w' :
wait_until_finished = 1;
break;
case 'p' :
packet_wr = 1;
break;
case 's' :
spare = 1;
break;
case 'c' :
certification = atoi(optarg);
break;
case 'r' :
cmplist = 1;
break;
case 'h' :
case 'H' :
display_usage = 1;
break;
case 'I' :
inquiry = 1;
break;
case 'X' :
/* TODO parse expert mode string */
printf("-X not implemented yet\n");
expert = 1;
exit(1);
break;
case 'O' :
/* oldtimer CD-RW format */
oldtimer = 1;
format = 1;
break;
case 'b' :
blockingnr = atoi(optarg);
break;
case 'D' :
uscsilib_verbose = 1;
break;
default :
return usage(progname);
}
}
argv += optind;
argc -= optind;
if (!blank && !format && !grow_session && !grow_spare &&
!expert && !inquiry && !display_usage) {
fprintf(stderr, "%s : at least one of -B, -F, -G, -h, -H -S, "
"-X or -I needs to be specified\n\n", progname);
return usage(progname);
}
if (format + grow_session + grow_spare + expert > 1) {
fprintf(stderr, "%s : at most one of -F, -G, -S or -X "
"needs to be specified\n\n", progname);
return usage(progname);
}
if (argc != 1) return usage(progname);
/* Open the device */
dev.dev_name = strdup(*argv);
printf("Opening device %s\n", dev.dev_name);
error = uscsi_open(&dev);
if (error) {
fprintf(stderr, "Device failed to open : %s\n",
strerror(error));
exit(1);
}
error = uscsi_check_for_scsi(&dev);
if (error) {
fprintf(stderr, "sorry, not a SCSI/ATAPI device : %s\n",
strerror(error));
exit(1);
}
error = uscsi_identify(&dev, &saddr);
if (error) {
fprintf(stderr, "SCSI/ATAPI identify returned : %s\n",
strerror(error));
exit(1);
}
format_type = 0;
/* expert format section */
if (expert) {
}
if (!format && !grow_spare && !grow_session) {
/* we're done */
if (display_usage)
usage(progname);
uscsi_close(&dev);
exit(0);
}
/* normal format section */
if (format) {
/* get current mmc profile of disc */
if (oldtimer && mmc_profile != 0x0a) {
printf("Oldtimer flag only defined for CD-RW; "
"ignored\n");
}
switch (mmc_profile) {
case 0x12 : /* DVD-RAM */
format_type = 0x00;
break;
case 0x0a : /* CD-RW */
format_type = mrw ? 0x24 : 0x10;
packet_wr = 1;
break;
case 0x13 : /* DVD-RW restricted overwrite */
case 0x14 : /* DVD-RW sequential */
format_type = 0x10;
/*
* Some drives suddenly stop supporting this format
* type when packet_wr = 1
*/
packet_wr = 0;
break;
case 0x1a : /* DVD+RW */
format_type = mrw ? 0x24 : 0x26;
break;
case 0x43 : /* BD-RE */
format_type = spare ? 0x30 : 0x31;
break;
default :
fprintf(stderr, "Can't format discs of type %s\n",
print_mmc_profile(mmc_profile));
uscsi_close(&dev);
exit(1);
}
}
if (grow_spare) {
switch (mmc_profile) {
case 0x12 : /* DVD-RAM */
case 0x43 : /* BD-RE */
format_type = 0x01;
break;
default :
fprintf(stderr,
"Can't grow spare area for discs of type %s\n",
print_mmc_profile(mmc_profile));
uscsi_close(&dev);
exit(1);
}
}
if (grow_session) {
switch (mmc_profile) {
case 0x0a : /* CD-RW */
format_type = 0x11;
break;
case 0x13 : /* DVD-RW restricted overwrite */
case 0x14 : /* DVD-RW sequential ? */
format_type = 0x13;
break;
default :
uscsi_close(&dev);
fprintf(stderr,
"Can't grow session for discs of type %s\n",
print_mmc_profile(mmc_profile));
exit(1);
}
}
/* check if format type is allowed */
format_blks = blks[format_type];
format_param = params[format_type];
if (!allow[format_type]) {
if (!inquiry)
process_format_caps(caps, caps_len, 1, allow,
blks, params);
printf("\n");
fflush(stdout);
fprintf(stderr,
"Drive indicates it can't format with deduced format "
"type 0x%02x\n", format_type);
uscsi_close(&dev);
exit(1);
}
if (restart_format && !((mmc_profile == 0x1a) || (format_type == 0x24)))
{
fprintf(stderr,
"Format restarting only for MRW formats or DVD+RW "
"formats\n");
uscsi_close(&dev);
exit(1);
}
if (restart_format && !wait_until_finished) {
printf( "Warning : format restarting without waiting for it be "
"finished is prolly not handy\n");
}
/* explicitly select packet write just in case */
if (packet_wr) {
printf("Explicitly setting packet type and blocking number\n");
error = uscsi_set_packet_parameters(&dev, blockingnr);
if (error) {
fprintf(stderr,
"Can't set packet writing and blocking number: "
"%s\n", strerror(error));
uscsi_close(&dev);
exit(1);
}
}
/* determine if formatting is done in the background */
background = 0;
if (format_type == 0x24) background = 1;
if (format_type == 0x26) background = 1;
/* special case format type 0x24 : MRW */
if (format_type == 0x24) {
format_blks = spare ? 0xffff0000 : 0xffffffff;
format_param = restart_format;
}
/* special case format type 0x26 : DVD+RW */
if (format_type == 0x26) {
format_param = restart_format;
}
/* verbose to the user */
DEBUG(
printf("Actual format selected: "
"format_type 0x%02x, blks %d, param %d, "
"certification %d, cmplist %d\n",
format_type, format_blks, format_param,
certification, cmplist);
);
printf("\nFormatting.... "); fflush(stdout);
/* what now? */
if (error) {
printf("fail\n"); fflush(stdout);
fprintf(stderr, "Formatting failed because of : %s\n",
strerror(error));
} else {
if (background) {
printf("background formatting in progress\n");
if (wait_until_finished) {
printf("Waiting for completion ... ");
uscsi_waitop(&dev);
}
/* explicitly do NOT close disc ... (for now) */
return 0;
} else {
printf("success!\n\n");
}
}