This repository has been archived on 2023-08-20. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
KableDistro/extra-sources/dvd+rw-tools-6.1/growisofs_mmc.cpp

1866 lines
49 KiB
C++

#ifndef _LARGEFILE_SOURCE
#define _LARGEFILE_SOURCE
#endif
#ifndef _LARGEFILE64_SOURCE
#define _LARGEFILE64_SOURCE
#endif
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
#endif
#if defined(__linux)
/* ... and "engage" glibc large file support */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#endif
#include "transport.hxx"
#include <time.h>
#include <sys/types.h>
#define SIGS_BLOCK 1
#define SIGS_UNBLOCK 0
extern "C" void sigs_mask (int);
extern "C" void atsignals (void (*)(void));
#define ONEX 1352 // 1385 * 1000 / 1024
static int media_written=0,next_track=1,
is_dao=0,quickgrown=0,do_reload=1;
static unsigned int stdnap=(256*1000)/ONEX, // measured in milliseconds
buf_size=512; // measured in KBs
static class __velocity__ {
private:
int value;
public:
int operator=(int v) { if (v>=ONEX) stdnap=(buf_size*500)/v;
else stdnap=(buf_size*500)/ONEX;
return value = v;
}
operator int() { return value; }
} velocity;
static void *ioctl_handle=(void *)-1;
#ifndef ioctl_fd
#define ioctl_fd ((long)ioctl_handle)
#endif
static unsigned int next_wr_addr=1; // it starts as boolean
static unsigned int dao_blocks=0;
static unsigned char formats[260],disc_info[32];
extern "C" {
extern int dvd_compat,test_write,no_reload,mmc_profile,_argc,
wrvfy,no_opc;
extern double speed_factor;
extern char *ioctl_device,**_argv;
}
extern "C"
int fumount (int fd)
{ Scsi_Command cmd;
return cmd.umount(fd);
}
extern "C"
int media_reload (char *name=NULL,struct stat *sb=NULL)
{ if (name==NULL)
{ Scsi_Command cmd(ioctl_handle);
pioneer_stop (cmd);
#if defined(RELOAD_NEVER_NEEDED)
#undef RELOAD_NEVER_NEEDED
#define RELOAD_NEVER_NEEDED 1
#else
#define RELOAD_NEVER_NEEDED 0
#endif
if (RELOAD_NEVER_NEEDED || no_reload>0)
{ cmd[0] = 0x1E; // ALLOW MEDIA REMOVAL
cmd[5] = 0;
cmd.transport ();
return (errno=0);
}
#if !RELOAD_NEVER_NEEDED
char str[12];
int n;
if ((n=fcntl (ioctl_fd,F_GETFD))<0) n=0;
fcntl (ioctl_fd,F_SETFD,n&~FD_CLOEXEC);
sprintf (str,"%ld",ioctl_fd);
execlp(_argv[0],no_reload<0?"-eject":"-reload",
str,ioctl_device,(void *)NULL);
}
else
{
{ Scsi_Command cmd;
if (!cmd.associate (name,sb)) return 1;
if (cmd.is_reload_needed())
{ fprintf (stderr,"%s: reloading tray\n",name);
cmd[0] = 0x1E; // ALLOW MEDIA REMOVAL
cmd[5] = 0;
if (cmd.transport ()) return 1;
while (1) // Pioneer DVR-x05 needs this...
{ cmd[0] = 0x1B; // START/STOP UNIT
cmd[1] = 0x1; // "IMMED"
cmd[4] = 0x2; // "Eject"
cmd[5] = 0;
if (cmd.transport() == 0x20407) // "OP IN PROGRESS"
{ poll (NULL,0,333);
continue;
}
break;
}
// yes, once again, non-"IMMED"...
cmd[0] = 0x1B; // START/STOP UNIT
cmd[4] = 0x2; // "Eject"
cmd[5] = 0;
if (cmd.transport()) return 1;
}
#if defined(__sun) || defined(sun)
else if (volmgt_running())
{ setuid(getuid());
execl("/usr/bin/volrmmount","volrmmount","-i",name,(void*)NULL);
return 0; // not normally reached
}
#endif
else return 0; // m-m-m-m! patched kernel:-)
}
if (no_reload>=0)
{ Scsi_Command cmd;
if (cmd.associate (name,sb))
{ cmd[0] = 0x1B; // START/STOP UNIT
cmd[1] = 0x1; // "IMMED"
cmd[4] = 0x3; // "Load"
cmd[5] = 0;
cmd.transport ();
}
errno=0; // ignore all errors on load
}
return 0;
#endif
}
return 1;
}
extern "C"
int get_mmc_profile (void *fd)
{ Scsi_Command cmd(fd);
unsigned char buf[8],inq[128];
int profile=0,once=1,blank=0,err;
unsigned int len;
// INQUIRY is considered to be "non-intrusive" in a sense that
// it won't interfere with any other operation nor clear sense
// data, which might be important to retain for security reasons.
cmd[0] = 0x12; // INQUIRY
cmd[4] = 36;
cmd[5] = 0;
if ((err=cmd.transport(READ,inq,36)))
sperror ("INQUIRY",err),
exit (FATAL_START(errno));
// make sure we're talking to MMC unit, for security reasons...
if ((inq[0]&0x1F) != 5)
fprintf (stderr,":-( not an MMC unit!\n"),
exit (FATAL_START(EINVAL));
do {
cmd[0] = 0x46;
cmd[8] = sizeof(buf);
cmd[9] = 0;
if ((err=cmd.transport(READ,buf,sizeof(buf))))
sperror ("GET CONFIGURATION",err),
fprintf (stderr,":-( non-MMC unit?\n"),
exit (FATAL_START(errno));
if ((profile = buf[6]<<8|buf[7]) || !once) break;
// no media?
cmd[0] = 0; // TEST UNIT READY
cmd[5] = 0;
if ((cmd.transport()&0xFFF00) != 0x23A00) break;
// try to load tray...
cmd[0] = 0x1B; // START/STOP UNIT
cmd[4] = 0x3; // "Load"
cmd[5] = 0;
if ((err=cmd.transport ()))
sperror ("LOAD TRAY",err),
exit (FATAL_START(errno));
#if 1
wait_for_unit (cmd);
#else
// consume sense data, most likely "MEDIA MAY HAVE CHANGED"
cmd[0] = 0; // TEST UNIT READY
cmd[5] = 0;
if ((err=cmd.transport ()) == -1)
sperror ("TEST UNIT READY",err),
exit (FATAL_START(errno));
#endif
} while (once--);
if (profile==0 || (profile&0x30)==0) // no or non-DVD media...
return profile;
cmd[0] = 0x51; // READ DISC INFORMATION
cmd[8] = sizeof(disc_info);
cmd[9] = 0;
if ((err=cmd.transport (READ,disc_info,sizeof(disc_info))))
sperror ("READ DISC INFORMATION",err),
exit (FATAL_START(errno));
// see if it's blank media
if ((disc_info[2]&3) == 0) blank=0x10000;
if (profile != 0x1A && profile != 0x13 && profile != 0x12)
return blank|profile;
cmd[0] = 0x23; // READ FORMAT CAPACITIES
cmd[8] = 12;
cmd[9] = 0;
if ((err=cmd.transport (READ,formats,12)))
sperror ("READ FORMAT CAPACITIES",err),
exit (FATAL_START(errno));
len = formats[3];
if (len&7 || len<16)
fprintf (stderr,":-( FORMAT allocaion length isn't sane"),
exit (FATAL_START(EINVAL));
cmd[0] = 0x23; // READ FORMAT CAPACITIES
cmd[7] = (4+len)>>8;
cmd[8] = (4+len)&0xFF;
cmd[9] = 0;
if ((err=cmd.transport (READ,formats,4+len)))
sperror ("READ FORMAT CAPACITIES",err),
exit (FATAL_START(errno));
if (len != formats[3])
fprintf (stderr,":-( parameter length inconsistency\n"),
exit(FATAL_START(EINVAL));
// see if it's not formatted
if ((formats[8]&3) != 2) blank = 0x10000;
return blank|profile;
}
static unsigned int get_2k_capacity (Scsi_Command &cmd)
{ unsigned char buf[32];
unsigned int ret=0;
unsigned int nwa,free_blocks;
int i,obligatory,len,err;
obligatory=0x00;
switch (mmc_profile&0xFFFF)
{ case 0x1A: // DVD+RW
obligatory=0x26;
case 0x13: // DVD-RW Restricted Overwrite
for (i=8,len=formats[3];i<len;i+=8)
if ((formats [4+i+4]>>2) == obligatory) break;
if (i==len)
{ fprintf (stderr,":-( can't locate obligatory format descriptor\n");
return 0;
}
ret = formats[4+i+0]<<24;
ret |= formats[4+i+1]<<16;
ret |= formats[4+i+2]<<8;
ret |= formats[4+i+3];
nwa = formats[4+5]<<16|formats[4+6]<<8|formats[4+7];
if (nwa>2048) ret *= nwa/2048;
else if (nwa<2048) ret /= 2048/nwa;
break;
case 0x12: // DVD-RAM
// As for the moment of this writing I don't format DVD-RAM.
// Therefore I just pull formatted capacity for now...
ret = formats[4+0]<<24;
ret |= formats[4+1]<<16;
ret |= formats[4+2]<<8;
ret |= formats[4+3];
nwa = formats[4+5]<<16|formats[4+6]<<8|formats[4+7];
if (nwa>2048) ret *= nwa/2048;
else if (nwa<2048) ret /= 2048/nwa;
break;
case 0x11: // DVD-R
case 0x14: // DVD-RW Sequential
case 0x15: // DVD-R Dual Layer Sequential
case 0x16: // DVD-R Dual Layer Jump
case 0x1B: // DVD+R
case 0x2B: // DVD+R Double Layer
cmd[0] = 0x52; // READ TRACK INFORMATION
cmd[1] = 1;
cmd[4] = next_track>>8;
cmd[5] = next_track&0xFF; // last track, set up earlier
cmd[8] = sizeof(buf);
cmd[9] = 0;
if ((err=cmd.transport (READ,buf,sizeof(buf))))
{ sperror ("READ TRACK INFORMATION",err);
return 0;
}
nwa = 0;
if (buf[7]&1) // NWA_V
{ nwa = buf[12]<<24;
nwa |= buf[13]<<16;
nwa |= buf[14]<<8;
nwa |= buf[15];
}
free_blocks = buf[16]<<24;
free_blocks |= buf[17]<<16;
free_blocks |= buf[18]<<8;
free_blocks |= buf[19];
ret = nwa + free_blocks;
break;
default:
break;
}
return ret;
}
extern "C"
off64_t get_capacity (void *fd)
{ Scsi_Command cmd(fd);
return (off64_t)get_2k_capacity(cmd)*2048;
}
extern "C"
double get_buffer_stats (void *fd)
{ Scsi_Command cmd(fd);
unsigned char bcap[12];
unsigned int bsize,bfree;
cmd[0] = 0x5C; // READ BUFFER CAPACITY
cmd[8] = sizeof(bcap);
cmd[9] = 0;
if (cmd.transport (READ,bcap,sizeof(bcap)))
return 0.0;
bsize = bcap[4]<<24|bcap[5]<<16|bcap[6]<<8|bcap[7];
bfree = bcap[8]<<24|bcap[9]<<16|bcap[10]<<8|bcap[11];
return (1.0 - (double)bfree/(double)bsize);
}
ssize_t poor_mans_pwrite64 (int fd,const void *_buff,size_t size,off64_t foff)
{ Scsi_Command cmd(ioctl_handle); /* screw first argument */
unsigned char bcap[12];
const unsigned char *buff=(const unsigned char *)_buff;
unsigned int lba,nbl,bsize,bfree;
int retries=0,errcode;
static int dao_toggle=-1;
if (foff&0x7FFF || size&0x7FFF) // 32K block size
return (errno=EINVAL,-1);
lba = foff>>11;
nbl = size>>11;
if (!media_written && next_wr_addr)
{ if ((lba+nbl) <= next_wr_addr)
return size;
else if (next_wr_addr > lba)
nbl -= (next_wr_addr-lba),
size -= (next_wr_addr-lba)<<11,
buff += (next_wr_addr-lba)<<11,
lba = next_wr_addr;
}
#if defined(__sun) || defined(sun)
else next_wr_addr = lba;
#endif
if (dao_toggle<0) dao_toggle=is_dao;
{ static unsigned int first_wr_addr=0;
if (!media_written)
{ sigs_mask (SIGS_BLOCK); first_wr_addr = lba; }
else if (lba >= (first_wr_addr+buf_size/2)) // measured in 2KBs!
{ sigs_mask (SIGS_UNBLOCK); }
}
if (dao_blocks!=0 && (lba+nbl)>dao_blocks)
nbl = dao_blocks-lba;
while (1)
{ cmd[0] = wrvfy?0x2E:0x2A; // WRITE [AND VERIFY] (10)
cmd[2] = (lba>>24)&0xff; // Logical Block Addrss
cmd[3] = (lba>>16)&0xff;
cmd[4] = (lba>>8)&0xff;
cmd[5] = lba&0xff;
cmd[7] = (nbl>>8)&0xff;
cmd[8] = nbl&0xff;
cmd[9] = 0;
#if 0
cmd[0] = 0xAA; // WRITE(12)
cmd[2] = (lba>>24)&0xff; // Logical Block Addrss
cmd[3] = (lba>>16)&0xff;
cmd[4] = (lba>>8)&0xff;
cmd[5] = lba&0xff;
cmd[8] = (nbl>>8)&0xff;
cmd[9] = nbl&0xff;
cmd[10] = 0x80; // "Streaming"
cmd[11] = 0;
#endif
//
// First writes can be long, especially in DAO mode...
// I wish I could complement this with "if (lba==0),"
// but some units might choose to fill the buffer before
// they take the first nap...
//
cmd.timeout(dao_toggle?180:60);
//
// It should also be noted that under Linux these values
// (if actually respected by kernel!) can turn out bogus.
// The problem is that I scale them to milliseconds as
// documentation requires/implies, while kernel treats
// them as "jiffies." I could/should have used HZ macro
// (or sysconf(_SC_CLK_TCK)), but recent kernels maintain
// own higher HZ value and disrespects the user-land one.
// Sending them down as milliseconds is just safer...
//
if (!(errcode=cmd.transport (WRITE,(void *)buff,size)))
break;
//--- WRITE failed ---//
#if defined(__sun) || defined(sun)
//
// Solaris can slice USB WRITEs to multiple ones. Here I try
// to find out which slice has failed. I expect we get here
// only when we re-enter the loop...
//
if (lba==next_wr_addr &&
errcode==0x52102) // "INVALID ADDRESS FOR WRITE"
{ unsigned char track[32];
cmd[0] = 0x52; // READ TRACK INFORMATION
cmd[1] = 1;
cmd[4] = next_track>>8;
cmd[5] = next_track&0xFF;
cmd[8] = sizeof(track);
cmd[9] = 0;
if (!cmd.transport (READ,track,sizeof(track)))
{ if (track[7]&1) // NWA_V
{ next_wr_addr = track[12]<<24;
next_wr_addr |= track[13]<<16;
next_wr_addr |= track[14]<<8;
next_wr_addr |= track[15];
if (lba<next_wr_addr && (lba+nbl)>next_wr_addr)
{ nbl -= next_wr_addr-lba,
size -= (next_wr_addr-lba)<<11,
buff += (next_wr_addr-lba)<<11,
lba = next_wr_addr;
continue;
}
}
}
}
#endif
if (errcode==0x20408) // "LONG WRITE IN PROGRESS"
{ // Apparently only Pioneer units do this...
if (velocity == 0)
{ if (handle_events(cmd) & (1<<6))
continue;
goto sync_cache;
}
cmd[0] = 0x5C; // READ BUFFER CAPACITY
cmd[8] = sizeof(bcap);
cmd[9] = 0;
if (cmd.transport (READ,bcap,sizeof(bcap)))
bfree=0, bsize=buf_size;
else
{ bsize = bcap[4]<<24|bcap[5]<<16|bcap[6]<<8|bcap[7];
bfree = bcap[8]<<24|bcap[9]<<16|bcap[10]<<8|bcap[11];
bsize /= 1024, bfree /= 1024; // xlate to KB
}
// check for sanity and drain 1/2 of the buffer
if (bsize < buf_size/2) bsize = buf_size/2;
else bsize /= 2;
if (bfree > bsize) bfree = bsize;
int msecs=0;
if ((msecs=(bsize-bfree)*1000) > velocity)
{ msecs /= velocity;
retries++;
if (dao_toggle) dao_toggle=-1;
}
else // lots of free buffer reported?
{ if (dao_toggle)
{ dao_toggle=-1;
if ((handle_events(cmd) & (1<<6)))
continue;
msecs = bsize*1000;
msecs /= velocity;
}
else if (!retries++) continue;
// Pioneer units seem to return bogus values at high
// velocities now and then... I reserve for 4 ECC
// blocks, which is ~6 ms at 16x. But note that some
// OSes, Linux 2.4 among then, will nap for 10 or 20.
msecs = (4*32*1000)/velocity;
}
// Retry values were increased because high-speed units
// start recordings at only portion of velocity and it
// takes more retries to write lead-in... Another common
// reason for stucks are recalibrations at zone edges...
if (retries > (dao_toggle?768:192))
fprintf (stderr,":-? the LUN appears to be stuck "
"writing LBA=%xh, keep retrying in %dms\n",
lba,msecs),
retries=1;
if (msecs>=0)
{ poll (NULL,0,msecs);
continue;
}
lba |= 0x80000000; // signal insane bfree...
}
else if (lba==0 && errcode==0x20401) // "IN PROCESS OF BECOMING READY"
{ if ((handle_events(cmd) & (1<<6)))
continue;
if (++retries > 3)
{ fprintf (stderr,":-! the LUN is still in process of becoming ready, "
"retrying in 5 secs...\n"),
retries=0;
sigs_mask (SIGS_UNBLOCK);
poll (NULL,0,5000);
}
else
sigs_mask (SIGS_UNBLOCK),
poll (NULL,0,stdnap);
continue;
}
else if (errcode==0x20404 || // "FORMAT IN PROGRESS"
errcode==0x20407 || // "OPERATION IN PROGRESS"
errcode==0x52C00) // "COMMAND SEQUENCE ERROR"
{ // Happens under automounter control? In general I recommend
// to disable it! This code is a tribute to users who disregard
// the instructions...
sync_cache:
cmd[0] = 0x35; // SYNC CACHE
cmd[9] = 0;
if (!cmd.transport())
{ if (++retries > 1)
{ fprintf (stderr,":-! the LUN appears to be stuck at %xh, "
"retrying in 5 secs...\n",lba),
retries=0;
sigs_mask (SIGS_UNBLOCK);
poll (NULL,0,5000);
}
else if (errcode==0x52C00)
fprintf (stderr,":-! \"COMMAND SEQUENCE ERROR\"@LBA=%xh. "
"Is media being read?\n",lba);
continue;
}
lba |= 0x40000000; // signal "can't SYNC CACHE"
}
{ char cmdname[64];
sprintf (cmdname,"WRITE@LBA=%xh",lba),
sperror (cmdname,errcode);
}
if (lba==0)
{ if (ASC(errcode)==0x30)
fprintf (stderr,":-( media is not formatted or unsupported.\n");
else if ((mmc_profile&0xFFFF)==0x14 /*&& ASC(errcode)==0x64*/)
fprintf (stderr,":-( attempt to re-run with "
"-dvd-compat -dvd-compat to engage DAO or "
"apply full blanking procedure\n");
}
#if 1 // safety net: leave DL sessions open and resumable...
if ((mmc_profile&0xFFFF)==0x2B && errcode!=0x52100) media_written=0;
#endif
return -1;
}
next_wr_addr = lba+nbl;
media_written = 1;
if (dao_toggle<0) dao_toggle=0;
return size;
}
static void plus_rw_format (Scsi_Command &cmd)
{ int err,i,len;
unsigned char descr[12];
if ((formats[4+4]&3) == 1) // Unformatted media
{ fprintf (stderr,"%s: pre-formatting blank DVD+RW...\n",ioctl_device);
for (i=8,len=formats[3];i<len;i+=8)
if ((formats [4+i+4]>>2) == 0x26) break;
if (i==len)
fprintf (stderr,":-( can't locate DVD+RW format descriptor\n"),
exit(FATAL_START(EMEDIUMTYPE));
memset (descr,0,sizeof(descr));
descr[1]=0x02; // "IMMED" flag
descr[3]=0x08; // "Descriptor Length" (LSB)
memcpy (descr+4,formats+4+i,4);
descr[8]=0x26<<2; // "Format type" 0x26
cmd[0] = 0x04; // FORMAT UNIT
cmd[1] = 0x11; // "FmtData" and "Format Code"
cmd[5] = 0;
if ((err=cmd.transport(WRITE,descr,sizeof(descr))))
sperror ("FORMAT UNIT",err),
exit(FATAL_START(errno));
wait_for_unit (cmd);
cmd[0] = 0x35; // FLUSH CACHE
cmd[9] = 0;
if ((err=cmd.transport()))
sperror ("FLUSH CACHE",err),
exit(FATAL_START(errno));
}
}
static int plus_rw_restart_format (Scsi_Command &cmd, off64_t size)
{ unsigned char descr[12];
unsigned int lead_out,blocks;
int err,i,len;
if (!dvd_compat && size!=0)
{ blocks = size/2048;
blocks += 15, blocks &= ~15;
lead_out = 0;
lead_out |= formats[4+0], lead_out <<= 8;
lead_out |= formats[4+1], lead_out <<= 8;
lead_out |= formats[4+2], lead_out <<= 8;
lead_out |= formats[4+3];
if (blocks<=lead_out) // no need to resume format...
return 0;
}
fprintf (stderr,"%s: restarting DVD+RW format...\n",ioctl_device);
for (i=8,len=formats[3];i<len;i+=8)
if ((formats [4+i+4]>>2) == 0x26) break;
if (i==len)
fprintf (stderr,":-( can't locate DVD+RW format descriptor\n"),
exit(FATAL_START(EMEDIUMTYPE));
memset (descr,0,sizeof(descr));
descr[1]=0x02; // "IMMED" flag
descr[3]=0x08; // "Descriptor Length" (LSB)
#if 1
memcpy (descr+4,formats+4+i,4);
#else
memset (descr+4,-1,4);
#endif
descr[8]=0x26<<2; // "Format type" 0x26
descr[11]=1; // "Restart Format"
cmd[0] = 0x04; // FORMAT UNIT
cmd[1] = 0x11; // "FmtData" and "Format Code"
cmd[5] = 0;
if ((err=cmd.transport(WRITE,descr,sizeof(descr))))
{ sperror ("RESTART FORMAT",err);
return 1;
}
wait_for_unit(cmd);
return 0;
}
extern "C"
int plusminus_r_C_parm (void *fd,char *C_parm)
{ unsigned int next_session, prev_session,err;
Scsi_Command cmd(fd);
unsigned char buf[36];
if ((disc_info[2]&3) != 1)
fprintf (stderr,":-( media is not appendable\n"),
exit(FATAL_START(EMEDIUMTYPE));
// allow to resume even --v-- incomplete sessions //
if (((disc_info[2]>>2)&3) > 1)
fprintf (stderr,":-( last session is not empty\n"),
exit(FATAL_START(EMEDIUMTYPE));
next_track = disc_info[5]|disc_info[10]<<8;
cmd[0] = 0x52; // READ TRACK INFORMATION
cmd[1] = 1;
cmd[4] = next_track>>8;
cmd[5] = next_track; // ask for last track
cmd[8] = sizeof(buf);
cmd[9] = 0;
if ((err=cmd.transport (READ,buf,sizeof(buf))))
{ sperror ("READ TRACK INFORMATION",err);
if (ASC(err)==0x24) // hp dvd200i returns 24 if media is full
fprintf (stderr,":-( media must be full already\n");
exit (FATAL_START(errno));
}
#if 0
if (buf[7]&1) // NWA_V
{ next_session = buf[12]<<24;
next_session |= buf[13]<<16;
next_session |= buf[14]<<8;
next_session |= buf[15];
}
else
fprintf (stderr,":-( Next Writable Address is invalid\n"),
exit(FATAL_START(EMEDIUMTYPE));
#else
next_session = buf[8]<<24; // Track Start Address
next_session |= buf[9]<<16;
next_session |= buf[10]<<8;
next_session |= buf[11];
#endif
//
// All manuals say the data is fabricated, presumably implying
// that one should use another command. But we stick to this one
// because kernel uses this very command to mount multi-session
// discs.
//
cmd[0] = 0x43; // READ TOC
cmd[2] = 1; // "Session info"
cmd[8] = 12;
cmd[9] = 0;
if ((err=cmd.transport (READ,buf,12)))
sperror ("READ SESSION INFO",err),
exit (FATAL_START(errno));
prev_session = buf[8]<<24;
prev_session |= buf[9]<<16;
prev_session |= buf[10]<<8;
prev_session |= buf[11];
sprintf (C_parm,"%d,%d",prev_session+16,next_session);
return next_session;
}
static unsigned char *pull_page2A (Scsi_Command &cmd)
{ unsigned char *page2A,header[12];
unsigned int len,bdlen;
int err;
cmd[0] = 0x5A; // MODE SENSE
cmd[1] = 0x08; // "Disable Block Descriptors"
cmd[2] = 0x2A; // "Capabilities and Mechanical Status"
cmd[8] = sizeof(header); // header only to start with
cmd[9] = 0;
if ((err=cmd.transport(READ,header,sizeof(header))))
sperror ("MODE SENSE#2A",err), exit(FATAL_START(errno));
len = (header[0]<<8|header[1])+2;
bdlen = header[6]<<8|header[7];
if (bdlen) // should never happen as we set "DBD" above
{ if (len < (8+bdlen+30))
fprintf (stderr,":-( LUN is impossible to bear with...\n"),
exit(FATAL_START(EINVAL));
}
else if (len < (8+2+(unsigned int)header[9]))// SANYO does this.
len = 8+2+header[9];
page2A = (unsigned char *)malloc(len);
if (page2A == NULL)
fprintf (stderr,":-( memory exhausted\n"), exit(FATAL_START(ENOMEM));
cmd[0] = 0x5A; // MODE SENSE
cmd[1] = 0x08; // "Disable Block Descriptors"
cmd[2] = 0x2A; // "Capabilities and Mechanical Status"
cmd[7] = len>>8;
cmd[8] = len; // real length this time
cmd[9] = 0;
if ((err=cmd.transport(READ,page2A,len)))
sperror ("MODE SENSE#2A",err),
exit(FATAL_START(errno));
len -= 2;
if (len < ((unsigned int)page2A[0]<<8|page2A[1])) // paranoia:-)
page2A[0] = len>>8, page2A[1] = len;
return page2A;
}
static int pull_velocity (Scsi_Command &cmd,unsigned char *d)
{ unsigned int len;
int v,err;
class autofree perf;
#if 0 // 8x AccessTek derivatives, such as OptoRite DD0405
if ((d[4]&2) == 0) return -1;
#endif
len = (d[0]<<24|d[1]<<16|d[2]<<8|d[3])-4;
if (len%16) return -1;
if (len==0) return -1; // LG GCA-4040N:-(
len += 8;
if (len == (8+16))
{ velocity = d[8+ 4]<<24|d[8+ 5]<<16|d[8+ 6]<<8|d[8+ 7];
v = d[8+12]<<24|d[8+13]<<16|d[8+14]<<8|d[8+15];
if (v>velocity) velocity=v; // CAV?
}
else // ZCLV
{ unsigned int n = (len-8)/16;
unsigned char *p;
perf = (unsigned char *)malloc (len);
if (perf == NULL)
fprintf (stderr,":-( memory exhausted\n"),
exit(FATAL_START(ENOMEM));
cmd[0]=0xAC; // GET PERFORMANCE
cmd[1]=4; // ask for "Overall Write Performance"
cmd[8]=n>>8;
cmd[9]=n; // real number of descriptors
cmd[10]=0; // ask for descriptor in effect
cmd[11]=0;
if ((err=cmd.transport(READ,perf,len)))
sperror ("GET CURRENT PERFORMANCE",err),
exit (FATAL_START(errno));
// Pick the highest speed...
for (p=perf+8,len-=8;len;p+=16,len-=16)
{ v=p[ 4]<<24|p[ 5]<<16|p[ 6]<<8|p[ 7];
if (v > velocity) velocity = v;
v=p[12]<<24|p[13]<<16|p[14]<<8|p[15];
if (v > velocity) velocity = v; // ZCAV?
}
}
return 0;
}
static int set_speed_B6h (Scsi_Command &cmd,unsigned int dvddash,
unsigned char *page2A)
{ unsigned int len;
int err;
unsigned char d[8+16];
class autofree perf;
cmd[0]=0xAC; // GET PERFORMACE
cmd[9]=1; // start with one descriptor
cmd[10]=0x3; // ask for "Write Speed Descriptor"
cmd[11]=0;
if ((err=cmd.transport(READ,d,sizeof(d))))
{ sperror ("GET PERFORMANCE",err);
fprintf (stderr,":-( falling down to SET CD SPEED\n");
return -1;
}
len = (d[0]<<24|d[1]<<16|d[2]<<8|d[3])-4;
if (len%16) // insane length
{ fprintf (stderr,":-( GET PERFORMANCE: insane Performance Data Length\n");
return -1;
}
perf = (unsigned char *)malloc(len+=8);
if (perf == NULL)
fprintf (stderr,":-( memory exhausted\n"),
exit(FATAL_START(ENOMEM));
if (len == sizeof(d))
memcpy (perf,d,sizeof(d));
else
{ unsigned int n=(len-8)/16;
cmd[0]=0xAC; // GET PERFORMANCE
cmd[8]=n>>8;
cmd[9]=n; // real number of descriptors
cmd[10]=0x3; // ask for "Write Speed Descriptor"
cmd[11]=0;
if ((err=cmd.transport(READ,perf,len)))
sperror ("GET PERFORMANCE",err),
exit (FATAL_START(errno));
}
int targetv=0,errp=0;
do {
memset (d,0,sizeof(d));
cmd[0]=0xAC; // GET PERFORMANCE
cmd[1]=4; // ask for "Overall Write performance"
cmd[9]=1;
cmd[10]=0; // ask for descriptor in effect
cmd[11]=0;
if (errp || (errp=cmd.transport(READ,d,sizeof(d))))
#if 0
sperror ("GET CURRENT PERFORMANCE",errp),
exit (FATAL_START(errno)); // well, if it passed above, we
// expect it to pass this too...
#else // Pioneer doesn't report current speed through GET PERFORMANCE:-(
{ emulated_err:
if (page2A == NULL) return -1;
unsigned int plen,hlen;
plen = (page2A[0]<<8|page2A[1]) + 2;
hlen = 8 + (page2A[6]<<8|page2A[7]);
unsigned char * const p = page2A + hlen;
if (plen<(hlen+32) || p[1]<(32-2))
return -1; // well, SET CD SPEED wouldn't work...
velocity = p[28]<<8|p[29];
}
#endif
else if ((errp = pull_velocity (cmd,d)) < 0)
goto emulated_err;
if (speed_factor != 0.0)
{ int i,j=len-8,v,v0,v1,minv,closesti,closestv=0;
unsigned char *wsdp=perf+8;
for (minv=0x7fffffff,i=0;i<j;i+=16)
{ v=wsdp[i+12]<<24|wsdp[i+13]<<16|wsdp[i+14]<<8|wsdp[i+15];
if (v<minv) minv=v;
} // minv will be treated as 1x
// New generation units offer minimum velocities other than 1x
if (minv>(15*ONEX)) speed_factor /= 16.0;
else if (minv>(11*ONEX)) speed_factor /= 12.0;
else if (minv>(7*ONEX)) speed_factor /= 8.0;
else if (minv>(5*ONEX)) speed_factor /= 6.0;
else if (minv>(3*ONEX)) speed_factor /= 4.0;
else if (!dvddash && minv>(2*ONEX))
// 2.4x is like 1x for DVD+, but it turned out that there're
// units, most notably "DVDRW IDE1004," burning DVD+ at
// lower speed, so I have to watch out for it...
speed_factor /= 2.4;
else if (minv>=(2*ONEX))
speed_factor /= 2.0;
v0=(int)(speed_factor*minv + 0.5);
for (closesti=0,minv=0x7fffffff,i=0;i<j;i+=16)
{ v1=wsdp[i+12]<<24|wsdp[i+13]<<16|wsdp[i+14]<<8|wsdp[i+15];
v=abs(v0-v1);
if (v<minv) minv=v, closesti=i, closestv=v1;
} // currenti is index of descriptor with *closest* velocity
targetv=closestv;
speed_factor=0.0;
if (velocity==targetv)
break; // already in shape, nothing to do...
unsigned char pd[28];
memset (pd,0,sizeof(pd)); // setup "Performance Descriptor"
pd[0]=*(wsdp+closesti); // copy "WRC" and other flags
#if 0
memset(pd+8,0xFF,4);
#else // I might have to copy the value from current descriptor...
unsigned int cap=get_2k_capacity(cmd)-1;
pd[8]=cap>>24;
pd[9]=cap>>16;
pd[10]=cap>>8;
pd[11]=cap;
#endif
memcpy(pd+12,wsdp+closesti+8,4); // copy "Read Speed"
memcpy(pd+20,wsdp+closesti+12,4); // copy "Write Speed"
pd[18]=pd[26]=1000>>8; // set both "Read Time" and
pd[19]=pd[27]=1000&0xFF; // "Write Time" to 1000ms
cmd[0]=0xB6; // SET STREAMING
cmd[10]=sizeof(pd);
cmd[11]=0;
if ((err=cmd.transport (WRITE,pd,sizeof(pd))))
sperror ("SET STREAMING",err),
exit (FATAL_START(errno));
// I pull the Page 2A in either case, because unit might
// have provided irrelevant information (Plextor). Not to
// mention cases when it simply failed to reply to GET
// CURRENT PERFORMANCE (Pioneer, LG).
unsigned int plen = (page2A[0]<<8|page2A[1]) + 2;
cmd[0] = 0x5A; // MODE SENSE
cmd[1] = 0x08; // "Disable Block Descriptors"
cmd[2] = 0x2A; // "Capabilities and Mechanical Status"
cmd[7] = plen>>8;
cmd[8] = plen; // real length this time
cmd[9] = 0;
if ((err=cmd.transport(READ,page2A,plen)))
sperror ("MODE SENSE#2A",err),
exit(FATAL_START(errno));
}
else if (targetv)
{ if (targetv!=velocity)
{ if (errp==0) // check Page 2A for speed then...
{ errp=-1; continue; }
if (velocity==0 || velocity>=ONEX)
{ fprintf(stderr,":-? Failed to change write speed: %d->%d\n",
(int)velocity,targetv);
if (velocity<targetv) velocity=targetv;
}
else
{ fprintf(stderr,":-( Failed to change write speed: %d->%d\n",
(int)velocity,targetv);
exit (FATAL_START(EINVAL));
}
}
break;
}
} while (targetv);
return targetv;
}
static int set_speed_BBh (Scsi_Command &cmd,unsigned char *page2A)
{ unsigned int plen,hlen;
int err;
plen = (page2A[0]<<8|page2A[1]) + 2;
hlen = 8 + (page2A[6]<<8|page2A[7]);
unsigned char * const p = page2A + hlen;
if (plen<(hlen+32) || p[1]<(32-2))
return -1;
int targetv=0;
do {
velocity = p[28]<<8|p[29];
if (speed_factor != 0.0)
{ int i,j,v,v0,v1,minv,closesti,closestv=0;
unsigned char *wsdp=p+32;
j=(p[30]<<8|p[31])*4;
for (minv=0x7fffffff,i=0;i<j;i+=4)
{ v=wsdp[i+2]<<8|wsdp[i+3];
if (v<minv) minv=v;
} // minv will be treated as 1x
v0=(int)(speed_factor*minv + 0.5);
for (closesti=0,minv=0x7fffffff,i=0;i<j;i+=4)
{ v1=wsdp[i+2]<<8|wsdp[i+3];
v=abs(v0-v1);
if (v<minv) minv=v, closesti=i, closestv=v1;
} // currenti is index of descriptor with *closest* velocity
targetv=closestv;
speed_factor=0.0;
if (velocity==targetv)
break; // already in shape, nothing to do...
cmd[0]=0xBB; // SET CD SPEED
cmd[1]=wsdp[closesti+1]; // Rotation Control
cmd[2]=cmd[3]=0xff; // Read Speed
cmd[4]=wsdp[closesti+2]; // Write Speed
cmd[5]=wsdp[closesti+3];
cmd[11]=0;
if ((err=cmd.transport()))
sperror ("SET CD SPEED",err),
exit (FATAL_START(errno));
cmd[0] = 0x5A; // MODE SENSE
cmd[1] = 0x08; // "Disable Block Descriptors"
cmd[2] = 0x2A; // "Capabilities and Mechanical Status"
cmd[7] = plen>>8;
cmd[8] = plen; // real length this time
cmd[9] = 0;
if ((err=cmd.transport(READ,page2A,plen)))
sperror ("MODE SENSE#2A",err),
exit(FATAL_START(errno));
}
else if (targetv)
{ if (targetv!=velocity)
fprintf (stderr,":-( Failed to change write speed: %d->%d\n",
(int)velocity,targetv),
exit (FATAL_START(EINVAL));
break;
}
} while (targetv);
return targetv;
}
static int plusminus_pages_setup (Scsi_Command &cmd,int profile)
{ unsigned int plen,hlen,dvddash;
unsigned char header[16],p32;
int err;
class autofree page2A;
switch (profile)
{ case 0x1A: // DVD+RW
p32 = 0;
dvddash = 0;
break;
case 0x1B: // DVD+R
//
// Even though MMC-4 draft explicitely exempts DVD+RW/+R media
// from those being affected by Page 05h settings, some
// firmwares apparently pay attention to Multi-session flag
// when finalizing DVD+R media. Well, we probably can't blame
// vendors as specification is still a work in progress, not to
// mention that it was published after DVD+R was introduced to
// the market.
//
p32 = dvd_compat?0x02:0xC0;
dvddash = 0;
break;
case 0x13: // DVD-RW Restricted Overwrite
p32 = 0xC0; // NB! Test Write in not option here
dvddash = 1;
break;
case 0x11: // DVD-R Sequential
case 0x14: // DVD-RW Sequential
case 0x15: // DVD-R Dual Layer Sequential
p32 = 0xC0;
if (next_track==1) do
{ if (dvd_compat >= (profile==0x14?2:256))
{ is_dao = 1,
fprintf (stderr,"%s: engaging DVD-%s DAO upon user request...\n",
ioctl_device,profile==0x14?"RW":"R");
break;
}
// Try to figure out if we have to go for DAO...
cmd[0] = 0x46; // GET CONFIGURATION
cmd[1] = 2; // ask for the only feature...
cmd[3] = 0x21; // the "Incremental Streaming Writable" one
cmd[8] = 16; // The feature should be there, right?
cmd[9] = 0;
if ((err=cmd.transport (READ,header,16)))
sperror ("GET FEATURE 21h",err),
exit(FATAL_START(errno));
hlen = header[0]<<24|header[1]<<16|header[2]<<8|header[3];
// See if Feature 21h is "current," if not, engage DAO...
if (hlen>=12 && (header[8+2]&1)==0)
{ is_dao = dvd_compat = 1;
fprintf (stderr,"%s: FEATURE 21h is not on, engaging DAO...\n",
ioctl_device);
break;
}
} while (0);
if (is_dao) p32 |=2; // DAO
if (profile==0x15 || // DVD-R DL Seq has no notion of multi-session
dvd_compat) p32 &= 0x3F; // Single-session
if (test_write)
p32 |= 0x10; // Test Write for debugging purposes
dvddash=1;
break;
case 0x16: // DVD-R Dual Layer Jump
p32 = 0xC0;
p32 |= 4; // Jump
if (test_write)
p32 |= 0x10; // Test Write for debugging purposes
dvddash=1;
break;
default:
p32 = 0;
dvddash = 0;
break;
}
if (dvddash || p32) page05_setup (cmd,profile,p32);
page2A = pull_page2A (cmd);
plen = (page2A[0]<<8|page2A[1]) + 2;
hlen = 8 + (page2A[6]<<8|page2A[7]);
unsigned char * const p = page2A + hlen;
if (plen<(hlen+14) || p[1]<(14-2)) // no "Buffer Size Supported"
buf_size = 512; // bogus value
else
buf_size = (p[12]<<8|p[13]);
if (buf_size<512) // bogus value
buf_size = 512;
// GET PERFORMANCE/SET STREAMING are listed as mandatory, so that
// this is actually the most likely path...
if (set_speed_B6h (cmd,dvddash,page2A) >= 0)
goto opc;
if (plen<(hlen+30) || p[1]<(30-2)) // no "Current Write Speed" present
{ if (dvddash)
{ fprintf (stderr,":-( \"Current Write Speed\" descriptor "
"is not present, faking 1x...\n"),
velocity = ONEX;
speed_factor = 0.0;
}
else
velocity = 0;
}
else do
{ velocity = p[28]<<8|p[29];
// Some units, most notably NEC and derivatives, were observed
// to report CD-R descriptors...
if (velocity < ONEX) // must be bogus
{ velocity=0;
break;
}
if (plen<(hlen+32) || p[1]<(32-2)) // no write descriptors
break;
int n = p[30]<<8|p[31],minv=1<<16;
unsigned char *p_= p+32;
for (;n--;p_+=4)
{ int v=p_[2]<<8|p_[3];
if (v==0) continue; // Lite-On returns zeros here...
if (v<ONEX) // must be bogus
velocity=0;
if (v < minv)
minv = v;
}
if (!dvddash && minv>2*ONEX)
// See corresponding comment in set_speed_B6h()
speed_factor /= 2.4;
} while (0);
if (speed_factor != 0.0 &&
(velocity==0 || set_speed_BBh (cmd,page2A)<0) )
fprintf (stderr,":-( can't control writing velocity, "
"-speed option is ignored\n");
opc:
if (!dvddash || test_write || no_opc) return 0;
// See if OPC is required...
cmd[0] = 0x51; // READ DISC INFORMATION
cmd[8] = 8;
cmd[9] = 0;
if ((err=cmd.transport (READ,header,8)))
sperror ("READ DISC INFORMATION",err),
exit(FATAL_START(errno));
if ((header[0]<<8|header[1]) <= 0x20)
{ cmd[0] = 0x54; // SEND OPC INFORMATION
cmd[1] = 1; // "Perform OPC"
cmd[9] = 0;
cmd.timeout(120); // NEC units can be slooo...w
if ((err=cmd.transport())) do
{ if (err==0x17301) // "POWER CALIBRATION AREA ALMOST FULL"
{ fprintf (stderr,":-! WARNING: Power Calibration Area "
"is almost full\n");
break;
}
sperror ("PERFORM OPC",err);
if ((err&0x0FF00)==0x07300) exit(FATAL_START(errno));
/*
* Th rest of errors are ignored, most notably observed:
* 0x52000, "INVALID COMMAND" Panasonic LD-F321
* 0x52700, "WRITE PROTECTED" Plextor PX-712A
* 0x52C00, "COMMAND SEQUENCE ERROR" Plextor PX-716UF
* 0x53002, "MEDIA NOT SUPPORTED" Toshiba TS-H542A
*/
} while (0);
}
handle_events(cmd);
return 0;
}
static int minus_rw_quickgrow (Scsi_Command &cmd,off64_t size)
{ unsigned char format[12];
unsigned int lead_out,blocks,type;
int i,len,err;
type = formats[4+4]&3;
if (type==2 && size!=0)
{ blocks = size/2048;
blocks += 15, blocks &= ~15;
lead_out = 0;
lead_out |= formats[4+0], lead_out <<= 8;
lead_out |= formats[4+1], lead_out <<= 8;
lead_out |= formats[4+2], lead_out <<= 8;
lead_out |= formats[4+3];
if (blocks<=lead_out) // no need to grow the session...
return 0;
}
// look for Quick Grow descriptor...
for (i=8,len=formats[3];i<len;i+=8)
if ((formats [4+i+4]>>2) == 0x13) break;
if (i==len) // no Quick Grow descriptor
{ if (type != 2)
quickgrown=1; // in reality quick formatted...
return 0;
}
else
{ blocks = 0;
blocks |= formats[i+0], blocks <<= 8;
blocks |= formats[i+1], blocks <<= 8;
blocks |= formats[i+2], blocks <<= 8;
blocks |= formats[i+3];
if (type==2 && blocks==0) // nowhere no grow...
return 0;
}
quickgrown=1;
fprintf (stderr,"%s: \"Quick Grow\" session...\n",ioctl_device);
memset (format,0,sizeof(format));
format [1] = 2; // "IMMED"
format [3] = 8; // "Length"
format [8] = 0x13<<2; // "Quick Grow"
format [11] = 16;
cmd[0] = 0x4; // FORMAT UNIT
cmd[1] = 0x11;
cmd[5] = 0;
if ((err=cmd.transport (WRITE,format,sizeof(format))))
{ sperror ("QUICK GROW",err);
return 1;
}
return wait_for_unit (cmd);
}
static int minus_r_reserve_track (Scsi_Command &cmd,off64_t size)
{ int err;
unsigned int blocks;
blocks = size/2048;
if (is_dao) dao_blocks = blocks;
else blocks += 15, blocks &= ~15;
fprintf (stderr,"%s: reserving %u blocks",ioctl_device,blocks);
if (is_dao && blocks<380000)
fprintf (stderr,"\b, warning for short DAO recording"),
poll (NULL,0,5000);
fprintf (stderr,"\n");
cmd[0] = 0x53; // RESERVE TRACK
cmd[5] = blocks>>24;
cmd[6] = blocks>>16;
cmd[7] = blocks>>8;
cmd[8] = blocks;
cmd[9] = 0;
if ((err=cmd.transport ()))
{ sperror ("RESERVE TRACK",err);
return 1;
}
return 0;
}
static void plus_r_dl_split (Scsi_Command &cmd,off64_t size)
{ int err;
unsigned int blocks,split;
unsigned char dvd_20[4+8];
cmd[0] = 0xAD; // READ DVD STRUCTURE
cmd[7] = 0x20; // "DVD+R Double Layer Boundary Information"
cmd[9] = sizeof(dvd_20);
cmd[11] = 0;
if ((err=cmd.transport(READ,dvd_20,sizeof(dvd_20))))
sperror ("READ DVD+R DL BOUNDARY INFORMATION",err),
exit (FATAL_START(errno));
if ((dvd_20[0]<<8|dvd_20[1]) < 10)
fprintf (stderr,":-( insane DVD+R DL BI structure length\n"),
exit (FATAL_START(EINVAL));
if (dvd_20[4]&0x80)
{ fprintf (stderr,":-? L0 Data Zone Capacity is set already\n");
return;
}
split = dvd_20[8]<<24|dvd_20[9]<<16|dvd_20[10]<<8|dvd_20[11];
blocks = size/2048;
blocks += 15, blocks &= ~15;
if (blocks <= split)
fprintf (stderr,":-( more than 50%% of space will be *wasted*!\n"
" use single layer media for this recording\n"),
exit (FATAL_START(EMEDIUMTYPE));
blocks /= 16;
blocks += 1;
blocks /= 2;
blocks *= 16;
fprintf (stderr,"%s: splitting layers at %u blocks\n",
ioctl_device,blocks);
memset (dvd_20,0,sizeof(dvd_20));
dvd_20[1] = sizeof(dvd_20)-2;
dvd_20[8] = blocks>>24;
dvd_20[9] = blocks>>16;
dvd_20[10] = blocks>>8;
dvd_20[11] = blocks;
cmd[0] = 0xBF; // SEND DVD STRUCTURE
cmd[7] = 0x20; // "DVD+R Double Layer Recording Information"
cmd[9] = sizeof(dvd_20);
cmd[11] = 0;
if ((err=cmd.transport(WRITE,dvd_20,sizeof(dvd_20))))
sperror ("SEND DVD+R DOUBLE LAYER RECORDING INFORMATION",err),
exit (FATAL_START(errno));
}
static int flush_cache (Scsi_Command &cmd, int even_sync=1)
{ int err;
cmd[0] = 0x35; // FLUSH CACHE
cmd[1] = 0x02; // "IMMED"
cmd[9] = 0;
if (!(err=cmd.transport()))
wait_for_unit (cmd);
else
sperror ("FLUSH CACHE",err);
if (even_sync) // Pioneer apparently needs this,
{ // non-IMMED FLUSH that is...
cmd[0] = 0x35; // FLUSH CACHE
cmd[9] = 0;
if (is_dao) cmd.timeout (15*60);
if ((err=cmd.transport()))
{ sperror ("SYNCHRONOUS FLUSH CACHE",err);
return err;
}
}
return 0;
}
//
// atexit/signal handlers
//
extern "C"
void no_r_finalize ()
{ while (media_written)
{ Scsi_Command cmd(ioctl_handle);
fprintf (stderr,"%s: flushing cache\n",ioctl_device);
if (flush_cache (cmd)) break;
pioneer_stop (cmd);
media_written = 0;
errno = 0;
}
_exit (errno);
}
extern "C"
void plus_rw_finalize ()
{ sigs_mask(SIGS_BLOCK);
while (media_written)
{ Scsi_Command cmd(ioctl_handle);
int err;
fprintf (stderr,"%s: flushing cache\n",ioctl_device);
if (flush_cache (cmd)) break;
if (!dvd_compat)
{ fprintf (stderr,"%s: stopping de-icing\n",ioctl_device);
cmd[0] = 0x5B; // CLOSE TRACK/SESSION
cmd[1] = 0x01; // "IMMED"
cmd[2] = 0; // "Stop De-Icing"
cmd[9] = 0;
if ((err=cmd.transport()))
sperror ("STOP DE-ICING",err);
if (wait_for_unit (cmd)) break;
}
fprintf (stderr,"%s: writing lead-out\n",ioctl_device);
cmd[0] = 0x5B; // CLOSE TRACK/SESSION
cmd[1] = 0x01; // "IMMED"
cmd[2] = 0x02; // "Close session"
cmd[9] = 0;
if ((err=cmd.transport()))
sperror ("CLOSE SESSION",err);
if (wait_for_unit (cmd)) break;
media_written = 0;
next_wr_addr = 0;
errno = 0;
if (do_reload && media_reload())
perror (":-( unable to reload tray");// not actually reached
}
_exit (errno);
}
extern "C"
void plus_r_finalize ()
{ sigs_mask(SIGS_BLOCK);
while (media_written)
{ Scsi_Command cmd(ioctl_handle);
int mode,err;
fprintf (stderr,"%s: flushing cache\n",ioctl_device);
if (flush_cache(cmd)) break;
fprintf (stderr,"%s: closing track\n",ioctl_device);
cmd[0] = 0x5B; // CLOSE TRACK/SESSION
cmd[1] = 0x01; // "IMMED"
cmd[2] = 0x01; // "Close Track"
cmd[4] = next_track>>8;
cmd[5] = next_track;
cmd[9] = 0;
if ((err=cmd.transport()))
sperror ("CLOSE TRACK",err);
if (wait_for_unit (cmd)) break;
if (dvd_compat)
// HP dvd520n insists on "finalize with minimum radius"
// with DVD+R DL ---------------vv
mode = ((mmc_profile&0xFFFF)==0x2B)?0x05:0x06,
fprintf (stderr,"%s: closing disc\n",ioctl_device);
else
mode = 0x02,
fprintf (stderr,"%s: closing session\n",ioctl_device);
while (1) // Pioneer DVR-K04RA
{ cmd[0] = 0x5B; // CLOSE TRACK/SESSION
cmd[1] = 0x01; // "IMMED"
cmd[2] = mode; // "Close session"
cmd[9] = 0;
err = cmd.transport();
if (err == 0x20407) // "OP IN PROGRESS"
{ poll (NULL,0,stdnap);
continue;
}
else if (err)
sperror (dvd_compat?"CLOSE DISC":"CLOSE SESSION",err);
break;
}
if (wait_for_unit (cmd)) break;
media_written = 0;
errno = 0;
if (do_reload && media_reload())
perror (":-( unable to reload tray");// not actually reached
}
_exit (errno);
}
extern "C"
void minus_rw_finalize ()
{ sigs_mask(SIGS_BLOCK);
while (media_written)
{ Scsi_Command cmd(ioctl_handle);
int err;
fprintf (stderr,"%s: flushing cache\n",ioctl_device);
if (flush_cache(cmd)) break;
if (quickgrown)
{ fprintf (stderr,"%s: writing lead-out\n",ioctl_device);
cmd[0] = 0x5B; // CLOSE TRACK/SESSION
cmd[1] = 0x01; // "IMMED"
cmd[2] = 0x02; // "Close Session"
cmd[9] = 0;
if ((err=cmd.transport()))
sperror ("CLOSE SESSION",err);
if (wait_for_unit (cmd)) break;
quickgrown=0;
}
media_written = 0;
next_wr_addr = 0;
errno = 0;
if (do_reload)
{ if (media_reload())
perror (":-( unable to reload tray");// not actually reached
}
else return; // Restricted Overwrite is just ugly!
}
_exit (errno);
}
extern "C"
void minus_r_finalize ()
{ sigs_mask(SIGS_BLOCK);
while (media_written)
{ Scsi_Command cmd(ioctl_handle);
int err;
fprintf (stderr,"%s: flushing cache\n",ioctl_device);
if (flush_cache(cmd,!is_dao)) break;
if (!is_dao)
{ fprintf (stderr,"%s: updating RMA\n",ioctl_device);
cmd[0] = 0x5B; // CLOSE TRACK/SESSION
cmd[1] = 0x01; // "IMMED"
cmd[2] = 0x01; // "Close Track"
cmd[4] = next_track>>8;
cmd[5] = next_track;
cmd[9] = 0;
if ((err=cmd.transport()))
sperror ("CLOSE TRACK",err);
if (wait_for_unit (cmd)) break;
fprintf (stderr,"%s: closing %s\n",ioctl_device,
dvd_compat?"disc":"session");
while (1) // Pioneer DVR-K04RA
{ cmd[0] = 0x5B; // CLOSE TRACK/SESSION
cmd[1] = 0x01; // "IMMED"
cmd[2] = 0x02; // "Close Session"
cmd[9] = 0;
err = cmd.transport();
if (err == 0x20407) // "OP IN PROGRESS"
{ poll (NULL,0,stdnap);
continue;
}
else if (err)
sperror (dvd_compat?"CLOSE DISC":"CLOSE SESSION",err);
break;
}
if (wait_for_unit (cmd)) break;
}
media_written = 0;
errno = 0;
if (do_reload && media_reload())
perror (":-( unable to reload tray");// not actually reached
}
_exit (errno);
}
extern "C"
void ram_reload ()
{ sigs_mask(SIGS_BLOCK);
if (media_written && do_reload) media_reload();
_exit (errno);
}
//
// poor_mans_setup takes care of a lot of things.
// It's invoked right before first write and if necessary/applicable
// prepares Page 05, reserves track, sets up atexit and signal handlers.
//
typedef ssize_t (*pwrite64_t)(int,const void *,size_t,off64_t);
extern "C"
pwrite64_t poor_mans_setup (void *fd,off64_t leadout)
{ Scsi_Command cmd(ioctl_handle=fd);
int err,profile=mmc_profile&0xFFFF;
// We might have loaded media ourselves, in which case we
// should lock the door...
cmd[0] = 0x1E; // PREVENT/ALLOW MEDIA REMOVAL
cmd[4] = 1; // "Prevent"
cmd[5] = 0;
if ((err=cmd.transport ()))
sperror ("PREVENT MEDIA REMOVAL",err),
exit (FATAL_START(errno));
handle_events(cmd);
switch (profile)
{ case 0x1A: // DVD+RW
switch (disc_info[7]&3) // Background formatting
{ case 0: // blank
plus_rw_format (cmd);
break;
case 1: // suspended
plus_rw_restart_format (cmd,leadout);
break;
case 2: // in progress
case 3: // complete
break;
}
plusminus_pages_setup (cmd,profile);
atexit (plus_rw_finalize);
atsignals (plus_rw_finalize);
break;
case 0x1B: // DVD+R
case 0x2B: // DVD+R Double Layer
plusminus_pages_setup(cmd,profile);
if (profile==0x2B && next_track==1 && dvd_compat && leadout)
plus_r_dl_split (cmd,leadout);
atexit (plus_r_finalize);
if (next_wr_addr)
{ atsignals (no_r_finalize);
next_wr_addr=(unsigned int)-1;
}
else atsignals (plus_r_finalize);
break;
case 0x13: // DVD-RW Restricted Overwrite
plusminus_pages_setup (cmd,profile);
//
// A side note. Quick Grow can't be performed earlier,
// as then reading is not possible.
//
minus_rw_quickgrow (cmd,leadout);
atexit (minus_rw_finalize);
atsignals (minus_rw_finalize);
break;
case 0x11: // DVD-R Sequential
case 0x14: // DVD-RW Sequential
case 0x15: // DVD-R Dual Layer Sequential
plusminus_pages_setup (cmd,profile);
if ((profile==0x15) && leadout)
{ unsigned char dvd_10[4+16];
unsigned int layer_size,data_size=leadout/2048;
cmd[0] = 0xAD; // READ DVD STRUCTURE
cmd[7] = 0x10;
cmd[9] = sizeof(dvd_10);
cmd[11] = 0;
if ((err=cmd.transport(READ,dvd_10,sizeof(dvd_10))))
sperror("READ DVD STRUCTURE #10h",err),
exit(FATAL_START(errno));
layer_size = dvd_10[4+13]<<16,
layer_size |= dvd_10[4+14]<<8,
layer_size |= dvd_10[4+15];
if (data_size <= layer_size)
fprintf (stderr,":-( more than 50%% of space will be *wasted*!\n"
" use single layer media for this recording\n"),
exit(FATAL_START(EMEDIUMTYPE));
}
if (is_dao && leadout)
minus_r_reserve_track(cmd,leadout);
atexit (minus_r_finalize);
if (next_wr_addr)
{ atsignals (no_r_finalize);
next_wr_addr=(unsigned int)-1;
}
else atsignals (minus_r_finalize);
break;
case 0x12: // DVD-RAM
// exclusively for speed settings...
plusminus_pages_setup (cmd,profile);
atexit (ram_reload);
atsignals (ram_reload);
break;
case 0x16: // DVD-R Dual Layer Jump not supported for reasons
// discussed at web-page...
default:
fprintf (stderr,":-( mounted media[%X] is not supported\n",profile),
exit(FATAL_START(EMEDIUMTYPE));
break;
}
if (velocity)
fprintf (stderr,"%s: \"Current Write Speed\" is %.1fx1385KBps.\n",
ioctl_device,velocity/(double)ONEX);
if (next_wr_addr==(unsigned int)-1) do
{ unsigned char track[32];
next_wr_addr = 0;
cmd[0] = 0x52; // READ TRACK INFORMATION
cmd[1] = 1;
cmd[4] = next_track>>8;
cmd[5] = next_track&0xFF; // last track, set up earlier
cmd[8] = sizeof(track);
cmd[9] = 0;
if (cmd.transport (READ,track,sizeof(track)))
break;
if (track[7]&1) // NWA_V
{ next_wr_addr = track[12]<<24;
next_wr_addr |= track[13]<<16;
next_wr_addr |= track[14]<<8;
next_wr_addr |= track[15];
}
if (next_wr_addr != // track start
(unsigned int)(track[8]<<24|track[9]<<16|track[10]<<8|track[11]))
fprintf (stderr,":-? resuming track#%d from LBA#%d\n",
next_track,next_wr_addr);
} while (0);
else next_wr_addr = 0;
return poor_mans_pwrite64;
}
extern "C"
int poor_man_rewritable (void *fd, void *buf)
{ Scsi_Command cmd(fd);
int err,profile=mmc_profile&0xFFFF;
if (profile!=0x13 && // not DVD-RW Restricted Overwrite
profile!=0x12 && // nor DVD-RAM
profile!=0x1A && // nor DVD+RW
profile!=0x2A) // nor DVD+RW Double Layer
return 0;
if (profile==0x13) // DVD-RW Restricted Overwrite
{ // Yet another restriction of Restricted Overwrite mode?
// Pioneer DVR-x05 can't read a bit otherwise...
do_reload=0;
minus_rw_finalize (); // with do_reload==0!
do_reload=1;
}
else if (profile==0x1A || // DVD+RW
profile==0x2A) // DVD+RW Double Layer
{ fprintf (stderr,"%s: flushing cache\n",ioctl_device);
if (flush_cache(cmd)) exit (errno);
}
cmd[0] = 0x28; // READ(10)
cmd[5] = 16; // LBA#16, volume descriptor set
cmd[8] = 16; // 32KB
cmd[9] = 0;
if ((err=cmd.transport (READ,buf,16*2048)))
sperror ("READ@LBA=10h",err),
exit(errno);
return 1;
}