You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3651 lines
98 KiB
3651 lines
98 KiB
15 years ago
|
/*
|
||
|
*
|
||
|
* $Id: k3bdevice.cpp 732002 2007-11-02 14:13:14Z trueg $
|
||
|
* Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
|
||
|
*
|
||
|
* This file is part of the K3b project.
|
||
|
* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 2 of the License, or
|
||
|
* (at your option) any later version.
|
||
|
* See the file "COPYING" for the exact licensing terms.
|
||
|
*/
|
||
|
|
||
|
#include <config.h>
|
||
|
|
||
|
|
||
|
#include "k3bdevice.h"
|
||
|
#include "k3bdeviceglobals.h"
|
||
|
#include "k3btrack.h"
|
||
|
#include "k3btoc.h"
|
||
|
#include "k3bdiskinfo.h"
|
||
|
#include "k3bmmc.h"
|
||
|
#include "k3bscsicommand.h"
|
||
|
#include "k3bcrc.h"
|
||
|
|
||
|
#include <qstringlist.h>
|
||
|
#include <qfile.h>
|
||
|
#include <qglobal.h>
|
||
|
#include <qvaluevector.h>
|
||
|
#include <qmutex.h>
|
||
|
|
||
|
#include <k3bdebug.h>
|
||
|
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/ioctl.h>
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <unistd.h>
|
||
|
#include <errno.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <math.h>
|
||
|
#include <stdarg.h>
|
||
|
|
||
|
|
||
|
#ifdef Q_OS_LINUX
|
||
|
|
||
|
#include <linux/version.h>
|
||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,70)
|
||
|
typedef unsigned char u8;
|
||
|
#endif
|
||
|
|
||
|
#undef __STRICT_ANSI__
|
||
|
#include <linux/cdrom.h>
|
||
|
#define __STRICT_ANSI__
|
||
|
|
||
|
#endif // Q_OS_LINUX
|
||
|
|
||
|
#ifdef Q_OS_FREEBSD
|
||
|
#include <stdio.h>
|
||
|
#include <camlib.h>
|
||
|
#define CD_FRAMESIZE_RAW 2352
|
||
|
#endif
|
||
|
|
||
|
#ifdef Q_OS_NETBSD
|
||
|
#include <sys/cdio.h>
|
||
|
#endif
|
||
|
|
||
|
#ifdef HAVE_RESMGR
|
||
|
extern "C" {
|
||
|
#include <resmgr.h>
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
//
|
||
|
// Very evil hacking: force the speed values to be acurate
|
||
|
// as long as "they" do not introduce other "broken" DVD
|
||
|
// speeds like 2.4 this works fine
|
||
|
//
|
||
|
static int fixupDvdWritingSpeed( int speed )
|
||
|
{
|
||
|
//
|
||
|
// Some writers report their speeds in 1000 bytes per second instead of 1024.
|
||
|
//
|
||
|
if( speed % 1385 == 0 )
|
||
|
return speed;
|
||
|
|
||
|
else if( speed % 1352 == 0 )
|
||
|
return speed*1385/1352;
|
||
|
|
||
|
// has to be 2.4x speed
|
||
|
else
|
||
|
return 3324;
|
||
|
}
|
||
|
|
||
|
|
||
|
const char* K3bDevice::Device::cdrdao_drivers[] =
|
||
|
{ "auto", "plextor", "plextor-scan", "cdd2600", "generic-mmc",
|
||
|
"generic-mmc-raw", "ricoh-mp6200", "sony-cdu920",
|
||
|
"sony-cdu948", "taiyo-yuden", "teac-cdr55", "toshiba",
|
||
|
"yamaha-cdr10x", 0
|
||
|
};
|
||
|
|
||
|
|
||
|
#if defined(Q_OS_LINUX) || defined(Q_OS_NETBSD)
|
||
|
int K3bDevice::openDevice( const char* name, bool write )
|
||
|
{
|
||
|
int fd = -1;
|
||
|
int flags = O_NONBLOCK;
|
||
|
if( write )
|
||
|
flags |= O_RDWR;
|
||
|
else
|
||
|
flags |= O_RDONLY;
|
||
|
|
||
|
#ifdef HAVE_RESMGR
|
||
|
// first try resmgr
|
||
|
fd = ::rsm_open_device( name, flags );
|
||
|
// k3bDebug() << "(K3bDevice::Device) resmgr open: " << fd << endl;
|
||
|
#endif
|
||
|
|
||
|
if( fd < 0 )
|
||
|
fd = ::open( name, flags );
|
||
|
|
||
|
if( fd < 0 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) could not open device "
|
||
|
<< name << ( write ? " for writing" : " for reading" ) << endl;
|
||
|
k3bDebug() << " (" << strerror(errno) << ")" << endl;
|
||
|
fd = -1;
|
||
|
|
||
|
// at least open it read-only (which is sufficient for kernels < 2.6.8 anyway)
|
||
|
if( write )
|
||
|
return openDevice( name, false );
|
||
|
}
|
||
|
|
||
|
return fd;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
class K3bDevice::Device::Private
|
||
|
{
|
||
|
public:
|
||
|
Private()
|
||
|
: supportedProfiles(0),
|
||
|
#ifdef Q_OS_LINUX
|
||
|
deviceFd(-1),
|
||
|
#endif
|
||
|
#ifdef Q_OS_NETBSD
|
||
|
deviceFd(-1),
|
||
|
#endif
|
||
|
#ifdef Q_OS_FREEBSD
|
||
|
cam(0),
|
||
|
#endif
|
||
|
openedReadWrite(false),
|
||
|
burnfree(false) {
|
||
|
}
|
||
|
|
||
|
int readCapabilities;
|
||
|
int writeCapabilities;
|
||
|
int supportedProfiles;
|
||
|
QStringList allNodes;
|
||
|
#ifdef Q_OS_LINUX
|
||
|
int deviceFd;
|
||
|
#endif
|
||
|
#ifdef Q_OS_NETBSD
|
||
|
int deviceFd;
|
||
|
#endif
|
||
|
#ifdef Q_OS_FREEBSD
|
||
|
struct cam_device *cam;
|
||
|
#endif
|
||
|
bool openedReadWrite;
|
||
|
bool burnfree;
|
||
|
|
||
|
QMutex mutex;
|
||
|
QMutex openCloseMutex;
|
||
|
};
|
||
|
|
||
|
|
||
|
K3bDevice::Device::Device( const QString& devname )
|
||
|
: m_bus(-1),
|
||
|
m_target(-1),
|
||
|
m_lun(-1),
|
||
|
m_writeModes(0)
|
||
|
{
|
||
|
d = new Private;
|
||
|
|
||
|
m_blockDevice = devname;
|
||
|
d->allNodes.append(devname);
|
||
|
|
||
|
m_cdrdaoDriver = "auto";
|
||
|
m_cdTextCapable = 0;
|
||
|
m_maxWriteSpeed = 0;
|
||
|
m_maxReadSpeed = 0;
|
||
|
d->burnfree = false;
|
||
|
m_dvdMinusTestwrite = true;
|
||
|
m_bufferSize = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
K3bDevice::Device::~Device()
|
||
|
{
|
||
|
close();
|
||
|
delete d;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::init( bool bCheckWritingModes )
|
||
|
{
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": init()" << endl;
|
||
|
|
||
|
//
|
||
|
// they all should read CD-ROM.
|
||
|
//
|
||
|
d->readCapabilities = MEDIA_CD_ROM;
|
||
|
d->writeCapabilities = 0;
|
||
|
d->supportedProfiles = 0;
|
||
|
|
||
|
if( !open() )
|
||
|
return false;
|
||
|
|
||
|
//
|
||
|
// inquiry
|
||
|
// use a 36 bytes buffer since not all devices return the full inquiry struct
|
||
|
//
|
||
|
ScsiCommand cmd( this );
|
||
|
unsigned char buf[36];
|
||
|
cmd.clear();
|
||
|
::memset( buf, 0, sizeof(buf) );
|
||
|
struct inquiry* inq = (struct inquiry*)buf;
|
||
|
cmd[0] = MMC_INQUIRY;
|
||
|
cmd[4] = sizeof(buf);
|
||
|
cmd[5] = 0;
|
||
|
if( cmd.transport( TR_DIR_READ, buf, sizeof(buf) ) ) {
|
||
|
kdError() << "(K3bDevice::Device) Unable to do inquiry." << endl;
|
||
|
close();
|
||
|
return false;
|
||
|
}
|
||
|
else {
|
||
|
m_vendor = QString::fromLatin1( (char*)(inq->vendor), 8 ).stripWhiteSpace();
|
||
|
m_description = QString::fromLatin1( (char*)(inq->product), 16 ).stripWhiteSpace();
|
||
|
m_version = QString::fromLatin1( (char*)(inq->revision), 4 ).stripWhiteSpace();
|
||
|
}
|
||
|
|
||
|
if( m_vendor.isEmpty() )
|
||
|
m_vendor = "UNKNOWN";
|
||
|
if( m_description.isEmpty() )
|
||
|
m_description = "UNKNOWN";
|
||
|
|
||
|
//
|
||
|
// We probe all features of the device. Since not all devices support the GET CONFIGURATION command
|
||
|
// we also query the mode page 2A and use the cdrom.h stuff to get as much information as possible
|
||
|
//
|
||
|
checkFeatures();
|
||
|
|
||
|
//
|
||
|
// Check the supported write modes (WRITINGMODE_TAO, WRITINGMODE_SAO, WRITINGMODE_RAW) by trying to set them
|
||
|
// We do this before checking mode page 2A in case some readers allow changin
|
||
|
// the write parameter page
|
||
|
//
|
||
|
if( bCheckWritingModes )
|
||
|
checkWritingModes();
|
||
|
|
||
|
//
|
||
|
// Most current drives support the 2A mode page
|
||
|
// Here we can get some more information (cdrecord -prcap does exactly this)
|
||
|
//
|
||
|
checkFor2AFeatures();
|
||
|
|
||
|
m_maxWriteSpeed = determineMaximalWriteSpeed();
|
||
|
|
||
|
//
|
||
|
// Check Just-Link via Ricoh mode page 0x30
|
||
|
//
|
||
|
if( !d->burnfree )
|
||
|
checkForJustLink();
|
||
|
|
||
|
//
|
||
|
// Support for some very old drives
|
||
|
//
|
||
|
checkForAncientWriters();
|
||
|
|
||
|
//
|
||
|
// If it can be written it can also be read
|
||
|
//
|
||
|
d->readCapabilities |= d->writeCapabilities;
|
||
|
|
||
|
close();
|
||
|
|
||
|
return furtherInit();
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::furtherInit()
|
||
|
{
|
||
|
#ifdef Q_OS_LINUX
|
||
|
|
||
|
//
|
||
|
// Since all CDR drives at least support WRITINGMODE_TAO, all CDRW drives should support
|
||
|
// mode page 2a and all DVD writer should support mode page 2a or the GET CONFIGURATION
|
||
|
// command this is redundant and may be removed for BSD ports or even completely
|
||
|
//
|
||
|
// We just keep it here because of the "should" in the sentence above. If someone can tell me
|
||
|
// that the linux driver does nothing more we can remove it completely.
|
||
|
//
|
||
|
open();
|
||
|
int drivetype = ::ioctl( handle(), CDROM_GET_CAPABILITY, CDSL_CURRENT );
|
||
|
if( drivetype < 0 ) {
|
||
|
k3bDebug() << "Error while retrieving capabilities." << endl;
|
||
|
close();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
d->readCapabilities |= DEVICE_CD_ROM;
|
||
|
|
||
|
if( drivetype & CDC_CD_R )
|
||
|
d->writeCapabilities |= MEDIA_CD_R;
|
||
|
if( drivetype & CDC_CD_RW )
|
||
|
d->writeCapabilities |= MEDIA_CD_RW;
|
||
|
if( drivetype & CDC_DVD_R )
|
||
|
d->writeCapabilities |= MEDIA_DVD_R;
|
||
|
if( drivetype & CDC_DVD )
|
||
|
d->readCapabilities |= MEDIA_DVD_ROM;
|
||
|
|
||
|
close();
|
||
|
|
||
|
#endif // Q_OS_LINUX
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
void K3bDevice::Device::checkForAncientWriters()
|
||
|
{
|
||
|
// TODO: add a boolean which determines if this device is non-MMC so we may warn the user at K3b startup about it
|
||
|
|
||
|
//
|
||
|
// There are a lot writers out there which behave like the TEAC R5XS
|
||
|
//
|
||
|
if( ( vendor().startsWith("TEAC") && ( description().startsWith("CD-R50S") ||
|
||
|
description().startsWith("CD-R55S") ) )
|
||
|
||
|
||
|
( vendor().startsWith("SAF") && ( description().startsWith("CD-R2006PLUS") ||
|
||
|
description().startsWith("CD-RW226") ||
|
||
|
description().startsWith("CD-R4012") ) )
|
||
|
||
|
||
|
( vendor().startsWith("JVC") && ( description().startsWith("XR-W2001") ||
|
||
|
description().startsWith("XR-W2010") ||
|
||
|
description().startsWith("R2626") ) )
|
||
|
||
|
||
|
( vendor().startsWith("PINNACLE") && ( description().startsWith("RCD-1000") ||
|
||
|
description().startsWith("RCD5020") ||
|
||
|
description().startsWith("RCD5040") ||
|
||
|
description().startsWith("RCD 4X4") ) )
|
||
|
||
|
||
|
( vendor().startsWith("Traxdata") && description().startsWith("CDR4120") ) ) {
|
||
|
m_writeModes = WRITINGMODE_TAO;
|
||
|
d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
|
||
|
d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
|
||
|
m_maxWriteSpeed = 4;
|
||
|
m_maxReadSpeed = 12;
|
||
|
m_bufferSize = 1024;
|
||
|
d->burnfree = false;
|
||
|
}
|
||
|
else if( vendor().startsWith("TEAC") ) {
|
||
|
if( description().startsWith("CD-R56S") ) {
|
||
|
m_writeModes |= TAO;
|
||
|
d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
|
||
|
d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
|
||
|
m_maxWriteSpeed = 6;
|
||
|
m_maxReadSpeed = 24;
|
||
|
m_bufferSize = 1302;
|
||
|
d->burnfree = false;
|
||
|
}
|
||
|
if( description().startsWith("CD-R58S") ) {
|
||
|
m_writeModes |= TAO;
|
||
|
d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
|
||
|
d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
|
||
|
m_maxWriteSpeed = 8;
|
||
|
m_maxReadSpeed = 24;
|
||
|
m_bufferSize = 4096;
|
||
|
d->burnfree = false;
|
||
|
}
|
||
|
}
|
||
|
else if( vendor().startsWith("MATSHITA") ) {
|
||
|
if( description().startsWith("CD-R CW-7501") ) {
|
||
|
m_writeModes = WRITINGMODE_TAO|WRITINGMODE_SAO;
|
||
|
d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
|
||
|
d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
|
||
|
m_maxWriteSpeed = 2;
|
||
|
m_maxReadSpeed = 4;
|
||
|
m_bufferSize = 1024;
|
||
|
d->burnfree = false;
|
||
|
}
|
||
|
if( description().startsWith("CD-R CW-7502") ) {
|
||
|
m_writeModes = WRITINGMODE_TAO|WRITINGMODE_SAO;
|
||
|
d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
|
||
|
d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
|
||
|
m_maxWriteSpeed = 4;
|
||
|
m_maxReadSpeed = 8;
|
||
|
m_bufferSize = 1024;
|
||
|
d->burnfree = false;
|
||
|
}
|
||
|
else if( description().startsWith("CD-R56S") ) {
|
||
|
m_writeModes |= WRITINGMODE_TAO;
|
||
|
d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
|
||
|
d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
|
||
|
m_maxWriteSpeed = 6;
|
||
|
m_maxReadSpeed = 24;
|
||
|
m_bufferSize = 1302;
|
||
|
d->burnfree = false;
|
||
|
}
|
||
|
}
|
||
|
else if( vendor().startsWith("HP") ) {
|
||
|
if( description().startsWith("CD-Writer 6020") ) {
|
||
|
m_writeModes = WRITINGMODE_TAO;
|
||
|
d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
|
||
|
d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
|
||
|
m_maxWriteSpeed = 2;
|
||
|
m_maxReadSpeed = 6;
|
||
|
m_bufferSize = 1024;
|
||
|
d->burnfree = false;
|
||
|
}
|
||
|
}
|
||
|
else if( vendor().startsWith( "PHILIPS" ) ) {
|
||
|
if( description().startsWith( "CDD2600" ) ) {
|
||
|
m_writeModes = WRITINGMODE_TAO|WRITINGMODE_SAO;
|
||
|
d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
|
||
|
d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
|
||
|
m_maxWriteSpeed = 2;
|
||
|
m_maxReadSpeed = 6;
|
||
|
m_bufferSize = 1024;
|
||
|
d->burnfree = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
K3bDevice::Interface K3bDevice::Device::interfaceType() const
|
||
|
{
|
||
|
if( m_bus != -1 && m_target != -1 && m_lun != -1 )
|
||
|
return SCSI;
|
||
|
else
|
||
|
return IDE;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::dao() const
|
||
|
{
|
||
|
return m_writeModes & WRITINGMODE_SAO;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::supportsRawWriting() const
|
||
|
{
|
||
|
return( writingModes() & (WRITINGMODE_RAW|WRITINGMODE_RAW_R16|WRITINGMODE_RAW_R96P|WRITINGMODE_RAW_R96R) );
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::writesCd() const
|
||
|
{
|
||
|
return ( d->writeCapabilities & MEDIA_CD_R ) && ( m_writeModes & WRITINGMODE_TAO );
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::burner() const
|
||
|
{
|
||
|
return ( writesCd() || writesDvd() );
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::writesCdrw() const
|
||
|
{
|
||
|
return d->writeCapabilities & MEDIA_CD_RW;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::writesDvd() const
|
||
|
{
|
||
|
return ( writesDvdPlus() || writesDvdMinus() );
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::writesDvdPlus() const
|
||
|
{
|
||
|
return d->writeCapabilities & (MEDIA_DVD_PLUS_R|MEDIA_DVD_PLUS_RW);
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::writesDvdMinus() const
|
||
|
{
|
||
|
return d->writeCapabilities & (MEDIA_DVD_R|MEDIA_DVD_RW);
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::readsDvd() const
|
||
|
{
|
||
|
return d->readCapabilities & MEDIA_DVD_ROM;
|
||
|
}
|
||
|
|
||
|
|
||
|
int K3bDevice::Device::type() const
|
||
|
{
|
||
|
int r = 0;
|
||
|
if( readCapabilities() & MEDIA_CD_ROM )
|
||
|
r |= DEVICE_CD_ROM;
|
||
|
if( writeCapabilities() & MEDIA_CD_R )
|
||
|
r |= DEVICE_CD_R;
|
||
|
if( writeCapabilities() & MEDIA_CD_RW )
|
||
|
r |= DEVICE_CD_RW;
|
||
|
if( readCapabilities() & MEDIA_DVD_ROM )
|
||
|
r |= DEVICE_DVD_ROM;
|
||
|
if( writeCapabilities() & MEDIA_DVD_RAM )
|
||
|
r |= DEVICE_DVD_RAM;
|
||
|
if( writeCapabilities() & MEDIA_DVD_R )
|
||
|
r |= DEVICE_DVD_R;
|
||
|
if( writeCapabilities() & MEDIA_DVD_RW )
|
||
|
r |= DEVICE_DVD_RW;
|
||
|
if( writeCapabilities() & MEDIA_DVD_R_DL )
|
||
|
r |= DEVICE_DVD_R_DL;
|
||
|
if( writeCapabilities() & MEDIA_DVD_PLUS_R )
|
||
|
r |= DEVICE_DVD_PLUS_R;
|
||
|
if( writeCapabilities() & MEDIA_DVD_PLUS_RW )
|
||
|
r |= DEVICE_DVD_PLUS_RW;
|
||
|
if( writeCapabilities() & MEDIA_DVD_PLUS_R_DL )
|
||
|
r |= DEVICE_DVD_PLUS_R_DL;
|
||
|
if( readCapabilities() & MEDIA_HD_DVD_ROM )
|
||
|
r |= DEVICE_HD_DVD_ROM;
|
||
|
if( writeCapabilities() & MEDIA_HD_DVD_R )
|
||
|
r |= DEVICE_HD_DVD_R;
|
||
|
if( writeCapabilities() & MEDIA_HD_DVD_RAM )
|
||
|
r |= DEVICE_HD_DVD_RAM;
|
||
|
if( readCapabilities() & MEDIA_BD_ROM )
|
||
|
r |= DEVICE_BD_ROM;
|
||
|
if( writeCapabilities() & MEDIA_BD_R )
|
||
|
r |= DEVICE_BD_R;
|
||
|
if( writeCapabilities() & MEDIA_BD_RE )
|
||
|
r |= DEVICE_BD_RE;
|
||
|
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
|
||
|
int K3bDevice::Device::readCapabilities() const
|
||
|
{
|
||
|
return d->readCapabilities;
|
||
|
}
|
||
|
|
||
|
|
||
|
int K3bDevice::Device::writeCapabilities() const
|
||
|
{
|
||
|
return d->writeCapabilities;
|
||
|
}
|
||
|
|
||
|
|
||
|
const QString& K3bDevice::Device::devicename() const
|
||
|
{
|
||
|
return blockDeviceName();
|
||
|
}
|
||
|
|
||
|
|
||
|
QString K3bDevice::Device::busTargetLun() const
|
||
|
{
|
||
|
return QString("%1,%2,%3").arg(m_bus).arg(m_target).arg(m_lun);
|
||
|
}
|
||
|
|
||
|
|
||
|
int K3bDevice::Device::cdTextCapable() const
|
||
|
{
|
||
|
if( cdrdaoDriver() == "auto" )
|
||
|
return 0;
|
||
|
else
|
||
|
return m_cdTextCapable;
|
||
|
}
|
||
|
|
||
|
|
||
|
void K3bDevice::Device::setCdTextCapability( bool b )
|
||
|
{
|
||
|
m_cdTextCapable = ( b ? 1 : 2 );
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::burnproof() const
|
||
|
{
|
||
|
return burnfree();
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::burnfree() const
|
||
|
{
|
||
|
return d->burnfree;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::isDVD() const
|
||
|
{
|
||
|
if( readsDvd() )
|
||
|
return( mediaType() & MEDIA_DVD_ALL );
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
int K3bDevice::Device::isEmpty() const
|
||
|
{
|
||
|
// if the device is already opened we do not close it
|
||
|
// to allow fast multiple method calls in a row
|
||
|
bool needToClose = !isOpen();
|
||
|
|
||
|
int ret = STATE_UNKNOWN;
|
||
|
if( !open() )
|
||
|
return STATE_UNKNOWN;
|
||
|
|
||
|
if( !testUnitReady() )
|
||
|
return STATE_NO_MEDIA;
|
||
|
|
||
|
unsigned char* data = 0;
|
||
|
unsigned int dataLen = 0;
|
||
|
|
||
|
if( readDiscInformation( &data, dataLen ) ) {
|
||
|
disc_info_t* inf = (disc_info_t*)data;
|
||
|
switch( inf->status ) {
|
||
|
case 0:
|
||
|
ret = STATE_EMPTY;
|
||
|
break;
|
||
|
case 1:
|
||
|
ret = STATE_INCOMPLETE;
|
||
|
break;
|
||
|
case 2:
|
||
|
ret = STATE_COMPLETE;
|
||
|
break;
|
||
|
default:
|
||
|
ret = STATE_UNKNOWN;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
delete [] data;
|
||
|
}
|
||
|
|
||
|
if( needToClose )
|
||
|
close();
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
int K3bDevice::Device::numSessions() const
|
||
|
{
|
||
|
//
|
||
|
// Session Info
|
||
|
// ============
|
||
|
// Byte 0-1: Data Length
|
||
|
// Byte 2: First Complete Session Number (Hex) - always 1
|
||
|
// Byte 3: Last Complete Session Number (Hex)
|
||
|
//
|
||
|
|
||
|
int ret = -1;
|
||
|
|
||
|
unsigned char* data = 0;
|
||
|
unsigned int len = 0;
|
||
|
|
||
|
if( mediaType() & MEDIA_CD_ALL ) {
|
||
|
//
|
||
|
// Althought disk_info should get the real value without ide-scsi
|
||
|
// I keep getting wrong values (the value is too high. I think the leadout
|
||
|
// gets counted as session sometimes :()
|
||
|
//
|
||
|
if( readTocPmaAtip( &data, len, 1, 0, 0 ) ) {
|
||
|
ret = data[3];
|
||
|
|
||
|
delete [] data;
|
||
|
}
|
||
|
else {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": could not get session info !" << endl;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if( readDiscInformation( &data, len ) ) {
|
||
|
ret = (int)( data[9]<<8 | data[4] );
|
||
|
|
||
|
// do only count complete sessions
|
||
|
if( (data[2]>>2) != 3 )
|
||
|
ret--;
|
||
|
|
||
|
delete [] data;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
int K3bDevice::Device::getDataMode( const K3b::Msf& sector ) const
|
||
|
{
|
||
|
bool needToClose = !isOpen();
|
||
|
|
||
|
int ret = Track::UNKNOWN;
|
||
|
|
||
|
if( !open() )
|
||
|
return ret;
|
||
|
|
||
|
// we use readCdMsf here since it's defined mandatory in MMC1 and
|
||
|
// we only use this method for CDs anyway
|
||
|
unsigned char data[2352];
|
||
|
bool readSuccess = readCdMsf( data, 2352,
|
||
|
0, // all sector types
|
||
|
false, // no dap
|
||
|
sector,
|
||
|
sector+1,
|
||
|
true, // SYNC
|
||
|
true, // HEADER
|
||
|
true, // SUBHEADER
|
||
|
true, // USER DATA
|
||
|
true, // EDC/ECC
|
||
|
0, // no c2 info
|
||
|
0 );
|
||
|
|
||
|
if( readSuccess ) {
|
||
|
if ( data[15] == 0x1 )
|
||
|
ret = Track::MODE1;
|
||
|
else if ( data[15] == 0x2 )
|
||
|
ret = Track::MODE2;
|
||
|
if ( ret == Track::MODE2 ) {
|
||
|
if ( data[16] == data[20] &&
|
||
|
data[17] == data[21] &&
|
||
|
data[18] == data[22] &&
|
||
|
data[19] == data[23] ) {
|
||
|
if ( data[18] & 0x20 )
|
||
|
ret = Track::XA_FORM2;
|
||
|
else
|
||
|
ret = Track::XA_FORM1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( needToClose )
|
||
|
close();
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
int K3bDevice::Device::getTrackDataMode( const K3bDevice::Track& track ) const
|
||
|
{
|
||
|
return getDataMode( track.firstSector() );
|
||
|
}
|
||
|
|
||
|
|
||
|
K3bDevice::Toc K3bDevice::Device::readToc() const
|
||
|
{
|
||
|
// if the device is already opened we do not close it
|
||
|
// to allow fast multiple method calls in a row
|
||
|
bool needToClose = !isOpen();
|
||
|
|
||
|
Toc toc;
|
||
|
|
||
|
if( !open() )
|
||
|
return toc;
|
||
|
|
||
|
int mt = mediaType();
|
||
|
|
||
|
//
|
||
|
// Use the profile if available because DVD-ROM units need to treat DVD+-R(W) media as DVD-ROM
|
||
|
// if supported at all
|
||
|
//
|
||
|
if( currentProfile() == MEDIA_DVD_ROM )
|
||
|
mt = MEDIA_DVD_ROM;
|
||
|
|
||
|
if( mt & (MEDIA_DVD_MINUS_ALL|MEDIA_DVD_PLUS_RW|MEDIA_DVD_ROM) ) {
|
||
|
if( !readFormattedToc( toc, mt ) ) {
|
||
|
K3b::Msf size;
|
||
|
if( readCapacity( size ) ) {
|
||
|
Track track;
|
||
|
track.m_firstSector = 0;
|
||
|
track.m_lastSector = size.lba();
|
||
|
track.m_session = 1;
|
||
|
track.m_type = Track::DATA;
|
||
|
track.m_mode = Track::DVD;
|
||
|
track.m_copyPermitted = ( mt != MEDIA_DVD_ROM );
|
||
|
track.m_preEmphasis = ( mt != MEDIA_DVD_ROM );
|
||
|
|
||
|
toc.append( track );
|
||
|
}
|
||
|
else
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
|
||
|
<< "READ CAPACITY for toc failed." << endl;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
else if( mt & (MEDIA_DVD_PLUS_R|MEDIA_DVD_PLUS_R_DL) ) {
|
||
|
//
|
||
|
// a DVD+R disk may have multiple sessions
|
||
|
// every session may contain up to 16 fragments
|
||
|
// if the disk is open there is one open session
|
||
|
// every closed session is viewed as a track whereas
|
||
|
// every fragment of the open session is viewed as a track
|
||
|
//
|
||
|
// We may use
|
||
|
// READ DISK INFORMATION
|
||
|
// READ TRACK INFORMATION: track number FFh, however, does not refer to the invisible track
|
||
|
// READ TOC/PMA/ATIP: form 0 refers to all closed sessions
|
||
|
// form 1 refers to the last closed session
|
||
|
//
|
||
|
readFormattedToc( toc, mt );
|
||
|
}
|
||
|
|
||
|
else if( mt & MEDIA_BD_ALL ) {
|
||
|
readFormattedToc( toc, mt );
|
||
|
}
|
||
|
|
||
|
else if( mt == MEDIA_DVD_RAM ) {
|
||
|
k3bDebug() << "(K3bDevice::readDvdToc) no dvdram support" << endl;
|
||
|
}
|
||
|
|
||
|
|
||
|
else if( mt & MEDIA_CD_ALL ) {
|
||
|
bool success = readRawToc( toc );
|
||
|
if( !success ) {
|
||
|
success = readFormattedToc( toc, mt );
|
||
|
|
||
|
#ifdef Q_OS_LINUX
|
||
|
if( !success ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) MMC READ TOC failed. falling back to cdrom.h." << endl;
|
||
|
readTocLinux(toc);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if( success )
|
||
|
fixupToc( toc );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( needToClose )
|
||
|
close();
|
||
|
|
||
|
return toc;
|
||
|
}
|
||
|
|
||
|
|
||
|
void K3bDevice::Device::readIsrcMcn( K3bDevice::Toc& toc ) const
|
||
|
{
|
||
|
// read MCN and ISRC of all tracks
|
||
|
QCString mcn;
|
||
|
if( readMcn( mcn ) ) {
|
||
|
toc.setMcn( mcn );
|
||
|
k3bDebug() << "(K3bDevice::Device) found MCN: " << mcn << endl;
|
||
|
}
|
||
|
else
|
||
|
k3bDebug() << "(K3bDevice::Device) no MCN found." << endl;
|
||
|
|
||
|
for( unsigned int i = 1; i <= toc.count(); ++i ) {
|
||
|
QCString isrc;
|
||
|
if( toc[i-1].type() == Track::AUDIO ) {
|
||
|
if( readIsrc( i, isrc ) ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) found ISRC for track " << i << ": " << isrc << endl;
|
||
|
toc[i-1].setIsrc( isrc );
|
||
|
}
|
||
|
else
|
||
|
k3bDebug() << "(K3bDevice::Device) no ISRC found for track " << i << endl;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::readFormattedToc( K3bDevice::Toc& toc, int mt ) const
|
||
|
{
|
||
|
// if the device is already opened we do not close it
|
||
|
// to allow fast multiple method calls in a row
|
||
|
bool needToClose = !isOpen();
|
||
|
|
||
|
bool success = false;
|
||
|
|
||
|
toc.clear();
|
||
|
|
||
|
unsigned int lastTrack = 0;
|
||
|
|
||
|
unsigned char* data = 0;
|
||
|
unsigned int dataLen = 0;
|
||
|
if( !(mt & MEDIA_CD_ALL) ) {
|
||
|
//
|
||
|
// on DVD-R(W) multisession disks only two sessions are represented as tracks in the readTocPmaAtip
|
||
|
// response (fabricated TOC). Thus, we use readDiscInformation for DVD media to get the proper number of tracks
|
||
|
//
|
||
|
if( readDiscInformation( &data, dataLen ) ) {
|
||
|
lastTrack = (int)( data[11]<<8 | data[6] );
|
||
|
|
||
|
delete [] data;
|
||
|
|
||
|
if( readTrackInformation( &data, dataLen, 1, lastTrack ) ) {
|
||
|
track_info_t* trackInfo = (track_info_t*)data;
|
||
|
|
||
|
if( trackInfo->blank ) {
|
||
|
lastTrack--;
|
||
|
}
|
||
|
|
||
|
delete [] data;
|
||
|
|
||
|
success = true;
|
||
|
}
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
else {
|
||
|
if( readTocPmaAtip( &data, dataLen, 0, 0, 1 ) ) {
|
||
|
|
||
|
if( dataLen < 4 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": formatted toc data too small." << endl;
|
||
|
}
|
||
|
else if( dataLen != ( (unsigned int)sizeof(toc_track_descriptor) * ((unsigned int)data[3]+1) ) + 4 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": invalid formatted toc data length: "
|
||
|
<< (dataLen-2) << endl;
|
||
|
}
|
||
|
else {
|
||
|
lastTrack = data[3];
|
||
|
toc_track_descriptor* td = (toc_track_descriptor*)&data[4];
|
||
|
for( unsigned int i = 0; i < lastTrack; ++i ) {
|
||
|
|
||
|
Track track;
|
||
|
unsigned int control = 0;
|
||
|
|
||
|
//
|
||
|
// In case READ TRACK INFORMATION fails:
|
||
|
// no session number info
|
||
|
// no track length and thus possibly incorrect last sector for
|
||
|
// multisession disks
|
||
|
//
|
||
|
track.m_firstSector = from4Byte( td[i].start_adr );
|
||
|
track.m_lastSector = from4Byte( td[i+1].start_adr ) - 1;
|
||
|
control = td[i].control;
|
||
|
|
||
|
track.m_type = (control & 0x4) ? Track::DATA : Track::AUDIO;
|
||
|
track.m_mode = getTrackDataMode( track );
|
||
|
track.m_copyPermitted = ( control & 0x2 );
|
||
|
track.m_preEmphasis = ( control & 0x1 );
|
||
|
|
||
|
toc.append( track );
|
||
|
}
|
||
|
|
||
|
success = true;
|
||
|
}
|
||
|
|
||
|
delete [] data;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Try to get information for all the tracks
|
||
|
//
|
||
|
for( unsigned int i = 0; i < lastTrack; ++i ) {
|
||
|
if( toc.count() < i+1 )
|
||
|
toc.append( Track() );
|
||
|
|
||
|
unsigned char* trackData = 0;
|
||
|
unsigned int trackDataLen = 0;
|
||
|
if( readTrackInformation( &trackData, trackDataLen, 1, i+1 ) ) {
|
||
|
track_info_t* trackInfo = (track_info_t*)trackData;
|
||
|
|
||
|
toc[i].m_firstSector = from4Byte( trackInfo->track_start );
|
||
|
|
||
|
if( i > 0 && toc[i-1].m_lastSector == 0 )
|
||
|
toc[i-1].m_lastSector = toc[i].m_firstSector - 1;
|
||
|
|
||
|
// There are drives that return 0 track length here!
|
||
|
// Some drives even return an invalid length here. :(
|
||
|
if( from4Byte( trackInfo->track_size ) > 0 )
|
||
|
toc[i].m_lastSector = toc[i].m_firstSector + from4Byte( trackInfo->track_size ) - 1;
|
||
|
|
||
|
if( trackInfo->nwa_v ) {
|
||
|
toc[i].m_nextWritableAddress = from4Byte( trackInfo->next_writable );
|
||
|
toc[i].m_freeBlocks = from4Byte( trackInfo->free_blocks );
|
||
|
}
|
||
|
|
||
|
toc[i].m_session = (int)(trackInfo->session_number_m<<8 & 0xf0 |
|
||
|
trackInfo->session_number_l & 0x0f); //FIXME: is this BCD?
|
||
|
|
||
|
int control = trackInfo->track_mode;
|
||
|
|
||
|
if( mt & MEDIA_CD_ALL ) {
|
||
|
toc[i].m_type = (control & 0x4) ? Track::DATA : Track::AUDIO;
|
||
|
toc[i].m_mode = getTrackDataMode( toc[i] );
|
||
|
}
|
||
|
else {
|
||
|
toc[i].m_type = Track::DATA;
|
||
|
toc[i].m_mode = Track::DVD;
|
||
|
}
|
||
|
toc[i].m_copyPermitted = ( control & 0x2 );
|
||
|
toc[i].m_preEmphasis = ( control & 0x1 );
|
||
|
|
||
|
delete [] trackData;
|
||
|
}
|
||
|
else if( !(mt & MEDIA_CD_ALL) ) {
|
||
|
success = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// this can only happen with DVD media
|
||
|
if( !toc.isEmpty() && toc.last().lastSector() == 0 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " no track length for the last non-empty track." << endl;
|
||
|
unsigned char* trackData = 0;
|
||
|
unsigned int trackDataLen = 0;
|
||
|
if( readTrackInformation( &trackData, trackDataLen, 1, lastTrack+1 ) ) {
|
||
|
track_info_t* trackInfo = (track_info_t*)trackData;
|
||
|
|
||
|
toc.last().m_lastSector = from4Byte( trackInfo->track_start ) - 1;
|
||
|
|
||
|
delete [] trackData;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if( needToClose )
|
||
|
close();
|
||
|
|
||
|
return success;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::readRawToc( K3bDevice::Toc& toc ) const
|
||
|
{
|
||
|
// if the device is already opened we do not close it
|
||
|
// to allow fast multiple method calls in a row
|
||
|
bool needToClose = !isOpen();
|
||
|
|
||
|
bool success = false;
|
||
|
|
||
|
toc.clear();
|
||
|
|
||
|
if( open() ) {
|
||
|
//
|
||
|
// Read Raw TOC (format: 0010b)
|
||
|
//
|
||
|
// For POINT from 01h-63h we get all the tracks
|
||
|
// POINT a1h gices us the last track number in the session in PMIN
|
||
|
// POINT a2h gives the start of the session lead-out in PMIN,PSEC,PFRAME
|
||
|
//
|
||
|
|
||
|
unsigned char* data = 0;
|
||
|
unsigned int dataLen = 0;
|
||
|
|
||
|
if( readTocPmaAtip( &data, dataLen, 2, false, 1 ) ) {
|
||
|
if( dataLen > 4 ) {
|
||
|
success = true;
|
||
|
|
||
|
toc_raw_track_descriptor* tr = (toc_raw_track_descriptor*)&data[4];
|
||
|
|
||
|
//
|
||
|
// debug the raw toc data
|
||
|
//
|
||
|
k3bDebug() << "Session | ADR | CONTROL| TNO | POINT | Min | Sec | Frame | Zero | PMIN | PSEC | PFRAME |" << endl;
|
||
|
for( unsigned int i = 0; i < (dataLen-4)/(int)sizeof(toc_raw_track_descriptor); ++i ) {
|
||
|
QString s;
|
||
|
s += QString( " %1 |" ).arg( (int)tr[i].session_number, 6 );
|
||
|
s += QString( " %1 |" ).arg( (int)tr[i].adr, 6 );
|
||
|
s += QString( " %1 |" ).arg( (int)tr[i].control, 6 );
|
||
|
s += QString( " %1 |" ).arg( (int)tr[i].tno, 6 );
|
||
|
s += QString( " %1 |" ).arg( (int)tr[i].point, 6, 16 );
|
||
|
s += QString( " %1 |" ).arg( (int)tr[i].min, 6 );
|
||
|
s += QString( " %1 |" ).arg( (int)tr[i].sec, 6 );
|
||
|
s += QString( " %1 |" ).arg( (int)tr[i].frame, 6 );
|
||
|
s += QString( " %1 |" ).arg( (int)tr[i].zero, 6, 16 );
|
||
|
s += QString( " %1 |" ).arg( (int)tr[i].p_min, 6 );
|
||
|
s += QString( " %1 |" ).arg( (int)tr[i].p_sec, 6 );
|
||
|
s += QString( " %1 |" ).arg( (int)tr[i].p_frame, 6 );
|
||
|
k3bDebug() << s << endl;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// First we try to determine if the raw toc data uses BCD values
|
||
|
//
|
||
|
int isBcd = rawTocDataWithBcdValues( data, dataLen );
|
||
|
if( isBcd == -1 ) {
|
||
|
delete [] data;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
K3b::Msf sessionLeadOut;
|
||
|
|
||
|
for( unsigned int i = 0; i < (dataLen-4)/(unsigned int)sizeof(toc_raw_track_descriptor); ++i ) {
|
||
|
if( tr[i].adr == 1 && tr[i].point <= 0x63 ) {
|
||
|
// track
|
||
|
K3bTrack track;
|
||
|
track.m_session = tr[i].session_number;
|
||
|
|
||
|
// :( We use 00:00:00 == 0 lba)
|
||
|
if( isBcd )
|
||
|
track.m_firstSector = K3b::Msf( K3bDevice::fromBcd(tr[i].p_min),
|
||
|
K3bDevice::fromBcd(tr[i].p_sec),
|
||
|
K3bDevice::fromBcd(tr[i].p_frame) ) - 150;
|
||
|
else
|
||
|
track.m_firstSector = K3b::Msf( tr[i].p_min, tr[i].p_sec, tr[i].p_frame ) - 150;
|
||
|
|
||
|
track.m_type = ( tr[i].control & 0x4 ? Track::DATA : Track::AUDIO );
|
||
|
track.m_mode = ( track.type() == Track::DATA ? getTrackDataMode(track) : Track::UNKNOWN );
|
||
|
track.m_copyPermitted = ( tr[i].control & 0x2 );
|
||
|
track.m_preEmphasis = ( tr[i].control & 0x1 );
|
||
|
|
||
|
//
|
||
|
// only do this within a session because otherwise we already set the last sector with the session leadout
|
||
|
//
|
||
|
if( !toc.isEmpty() )
|
||
|
if( toc[toc.count()-1].session() == track.session() )
|
||
|
toc[toc.count()-1].m_lastSector = track.firstSector() - 1;
|
||
|
|
||
|
toc.append(track);
|
||
|
}
|
||
|
else if( tr[i].point == 0xa2 ) {
|
||
|
//
|
||
|
// since the session is always reported before the tracks this is where we do this:
|
||
|
// set the previous session's last tracks's last sector to the first sector of the
|
||
|
// session leadout (which was reported before the tracks)
|
||
|
//
|
||
|
// This only happens on multisession CDs
|
||
|
//
|
||
|
if( !toc.isEmpty() )
|
||
|
toc[toc.count()-1].m_lastSector = sessionLeadOut - 1;
|
||
|
|
||
|
// this is save since the descriptors are reported in ascending order of the session number
|
||
|
// :( We use 00:00:00 == 0 lba)
|
||
|
if( isBcd )
|
||
|
sessionLeadOut = K3b::Msf( K3bDevice::fromBcd(tr[i].p_min),
|
||
|
K3bDevice::fromBcd(tr[i].p_sec),
|
||
|
K3bDevice::fromBcd(tr[i].p_frame) ) - 150;
|
||
|
else
|
||
|
sessionLeadOut = K3b::Msf( tr[i].p_min, tr[i].p_sec, tr[i].p_frame ) - 150;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
k3bDebug() << blockDeviceName() << ": setting last sector of last track to " << (sessionLeadOut-1).lba() << endl;
|
||
|
|
||
|
// set the last track's last sector
|
||
|
if( !toc.isEmpty() )
|
||
|
toc[toc.count()-1].m_lastSector = sessionLeadOut - 1;
|
||
|
}
|
||
|
else
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " empty raw toc." << endl;
|
||
|
|
||
|
delete [] data;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( needToClose )
|
||
|
close();
|
||
|
|
||
|
return success;
|
||
|
}
|
||
|
|
||
|
|
||
|
int K3bDevice::Device::rawTocDataWithBcdValues( unsigned char* data, unsigned int dataLen ) const
|
||
|
{
|
||
|
toc_raw_track_descriptor* tr = (toc_raw_track_descriptor*)&data[4];
|
||
|
|
||
|
bool notBcd = false;
|
||
|
bool notHex = false;
|
||
|
|
||
|
//
|
||
|
// in most cases this will already tell us if a drive does not provide bcd numbers
|
||
|
// (which should be all newer MMC drives)
|
||
|
//
|
||
|
for( unsigned int i = 0; i < (dataLen-4)/(unsigned int)sizeof(toc_raw_track_descriptor); ++i ) {
|
||
|
if( tr[i].adr == 1 && tr[i].point <= 0xa2) {
|
||
|
if( !K3bDevice::isValidBcd(tr[i].p_min) ||
|
||
|
!K3bDevice::isValidBcd(tr[i].p_sec) ||
|
||
|
!K3bDevice::isValidBcd(tr[i].p_frame) ) {
|
||
|
notBcd = true;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// we only need to check sec and frame since min needs to be <= 99
|
||
|
// and bcd values are never bigger than 99.
|
||
|
else if( (int)K3bDevice::fromBcd(tr[i].p_sec) >= 60 ||
|
||
|
(int)K3bDevice::fromBcd(tr[i].p_frame) >= 75 ) {
|
||
|
notBcd = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// all values are valid bcd values but we still don't know for sure if they are really
|
||
|
// used as bcd. So we also check the HEX values.
|
||
|
//
|
||
|
for( unsigned int i = 0; i < (dataLen-4)/(unsigned int)sizeof(toc_raw_track_descriptor); ++i ) {
|
||
|
if( tr[i].adr == 1 && tr[i].point <= 0xa2 ) {
|
||
|
if( (int)tr[i].p_min > 99 ||
|
||
|
(int)tr[i].p_sec >= 60 ||
|
||
|
(int)tr[i].p_frame >= 75 ) {
|
||
|
notHex = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// If all values are valid bcd and valid hex we check the start sectors of the tracks.
|
||
|
//
|
||
|
if( !notHex || !notBcd ) {
|
||
|
K3b::Msf sessionLeadOutHex, sessionLeadOutBcd;
|
||
|
K3b::Msf lastTrackHex, lastTrackBcd;
|
||
|
|
||
|
for( unsigned int i = 0; i < (dataLen-4)/(unsigned int)sizeof(toc_raw_track_descriptor); ++i ) {
|
||
|
|
||
|
if( tr[i].adr == 1 ) {
|
||
|
if( tr[i].point < 0x64 ) {
|
||
|
|
||
|
// check hex values
|
||
|
if( K3b::Msf( tr[i].p_min, tr[i].p_sec, tr[i].p_frame ) <
|
||
|
lastTrackHex )
|
||
|
notHex = true;
|
||
|
|
||
|
// check bcd values
|
||
|
if( K3b::Msf( K3bDevice::fromBcd(tr[i].p_min), K3bDevice::fromBcd(tr[i].p_sec), K3bDevice::fromBcd(tr[i].p_frame) ) <
|
||
|
lastTrackBcd )
|
||
|
notBcd = true;
|
||
|
|
||
|
lastTrackBcd = K3b::Msf( K3bDevice::fromBcd(tr[i].p_min), K3bDevice::fromBcd(tr[i].p_sec), K3bDevice::fromBcd(tr[i].p_frame) );
|
||
|
lastTrackHex = K3b::Msf( tr[i].p_min, tr[i].p_sec, tr[i].p_frame );
|
||
|
}
|
||
|
else if( tr[i].point == 0xa2 ) {
|
||
|
if( sessionLeadOutHex < lastTrackHex )
|
||
|
notHex = true;
|
||
|
if( sessionLeadOutBcd < lastTrackBcd )
|
||
|
notBcd = true;
|
||
|
|
||
|
sessionLeadOutHex = K3b::Msf( tr[i].p_min, tr[i].p_sec, tr[i].p_frame );
|
||
|
sessionLeadOutBcd = K3b::Msf( K3bDevice::fromBcd(tr[i].p_min), K3bDevice::fromBcd(tr[i].p_sec), K3bDevice::fromBcd(tr[i].p_frame) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// check the last track
|
||
|
if( sessionLeadOutHex < lastTrackHex )
|
||
|
notHex = true;
|
||
|
if( sessionLeadOutBcd < lastTrackBcd )
|
||
|
notBcd = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
if( !notBcd && !notHex ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) need to compare raw toc to formatted toc. :(" << endl;
|
||
|
//
|
||
|
// All values are valid bcd and valid HEX values so we compare with the formatted toc.
|
||
|
// This slows us down a lot but in most cases this should not be reached anyway.
|
||
|
//
|
||
|
// TODO: also check the bcd values
|
||
|
//
|
||
|
K3bDevice::Toc formattedToc;
|
||
|
if( readFormattedToc( formattedToc, MEDIA_CD_ROM ) ) {
|
||
|
for( unsigned int i = 0; i < (dataLen-4)/(unsigned int)sizeof(toc_raw_track_descriptor); ++i ) {
|
||
|
if( tr[i].adr == 1 && tr[i].point < 0x64 ) {
|
||
|
unsigned int track = (int)tr[i].point;
|
||
|
|
||
|
// FIXME: do bcd drive also encode the track number in bcd? If so test it, too.
|
||
|
|
||
|
if( track > formattedToc.count() ) {
|
||
|
notHex = true;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
K3b::Msf posHex( tr[i].p_min,
|
||
|
tr[i].p_sec,
|
||
|
tr[i].p_frame );
|
||
|
K3b::Msf posBcd( K3bDevice::fromBcd(tr[i].p_min),
|
||
|
K3bDevice::fromBcd(tr[i].p_sec),
|
||
|
K3bDevice::fromBcd(tr[i].p_frame) );
|
||
|
posHex -= 150;
|
||
|
posBcd -= 150;
|
||
|
if( posHex != formattedToc[track-1].firstSector() )
|
||
|
notHex = true;
|
||
|
if( posBcd != formattedToc[track-1].firstSector() )
|
||
|
notBcd = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( notBcd )
|
||
|
k3bDebug() << "(K3bDevice::Device) found invalid bcd values. No bcd toc." << endl;
|
||
|
if( notHex )
|
||
|
k3bDebug() << "(K3bDevice::Device) found invalid hex values. No hex toc." << endl;
|
||
|
|
||
|
if( notBcd == notHex ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) unable to determine if hex (" << notHex << ") or bcd (" << notBcd << ")." << endl;
|
||
|
if( !notHex ) {
|
||
|
k3bDebug() << "Assuming hex encoding in favor of newer drives and the more reliable raw toc." << endl;
|
||
|
return 0;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
else if( notBcd )
|
||
|
return 0;
|
||
|
else
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
K3bDevice::CdText K3bDevice::Device::readCdText() const
|
||
|
{
|
||
|
// if the device is already opened we do not close it
|
||
|
// to allow fast multiple method calls in a row
|
||
|
bool needToClose = !isOpen();
|
||
|
|
||
|
K3bDevice::CdText textData;
|
||
|
|
||
|
if( open() ) {
|
||
|
unsigned char* data = 0;
|
||
|
unsigned int dataLen = 0;
|
||
|
|
||
|
if( readTocPmaAtip( &data, dataLen, 5, false, 0 ) ) {
|
||
|
textData.setRawPackData( data, dataLen );
|
||
|
|
||
|
delete [] data;
|
||
|
}
|
||
|
|
||
|
if( needToClose )
|
||
|
close();
|
||
|
}
|
||
|
|
||
|
return textData;
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef Q_OS_LINUX
|
||
|
// fallback
|
||
|
bool K3bDevice::Device::readTocLinux( K3bDevice::Toc& toc ) const
|
||
|
{
|
||
|
// if the device is already opened we do not close it
|
||
|
// to allow fast multiple method calls in a row
|
||
|
bool needToClose = !isOpen();
|
||
|
|
||
|
bool success = true;
|
||
|
|
||
|
toc.clear();
|
||
|
|
||
|
struct cdrom_tochdr tochdr;
|
||
|
struct cdrom_tocentry tocentry;
|
||
|
|
||
|
usageLock();
|
||
|
if( open() ) {
|
||
|
//
|
||
|
// CDROMREADTOCHDR ioctl returns:
|
||
|
// cdth_trk0: First Track Number
|
||
|
// cdth_trk1: Last Track Number
|
||
|
//
|
||
|
if( ::ioctl( d->deviceFd, CDROMREADTOCHDR, &tochdr ) ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) could not get toc header !" << endl;
|
||
|
success = false;
|
||
|
}
|
||
|
else {
|
||
|
Track lastTrack;
|
||
|
for (int i = tochdr.cdth_trk0; i <= tochdr.cdth_trk1 + 1; i++) {
|
||
|
::memset(&tocentry,0,sizeof (struct cdrom_tocentry));
|
||
|
// get Lead-Out Information too
|
||
|
tocentry.cdte_track = (i<=tochdr.cdth_trk1) ? i : CDROM_LEADOUT;
|
||
|
tocentry.cdte_format = CDROM_LBA;
|
||
|
//
|
||
|
// CDROMREADTOCENTRY ioctl returns:
|
||
|
// cdte_addr.lba: Start Sector Number (LBA Format requested)
|
||
|
// cdte_ctrl: 4 ctrl bits
|
||
|
// 00x0b: 2 audio Channels(no pre-emphasis)
|
||
|
// 00x1b: 2 audio Channels(pre-emphasis)
|
||
|
// 10x0b: audio Channels(no pre-emphasis),reserved in cd-rw
|
||
|
// 10x1b: audio Channels(pre-emphasis),reserved in cd-rw
|
||
|
// 01x0b: data track, recorded uninterrupted
|
||
|
// 01x1b: data track, recorded incremental
|
||
|
// 11xxb: reserved
|
||
|
// xx0xb: digital copy prohibited
|
||
|
// xx1xb: digital copy permitted
|
||
|
// cdte_addr: 4 addr bits (type of Q-Subchannel data)
|
||
|
// 0000b: no Information
|
||
|
// 0001b: current position data
|
||
|
// 0010b: MCN
|
||
|
// 0011b: ISRC
|
||
|
// 0100b-1111b: reserved
|
||
|
// cdte_datamode: 0: Data Mode1
|
||
|
// 1: CD-I
|
||
|
// 2: CD-XA Mode2
|
||
|
//
|
||
|
|
||
|
if( ::ioctl( d->deviceFd, CDROMREADTOCENTRY, &tocentry ) ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) error reading tocentry " << i << endl;
|
||
|
success = false;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
int startSec = tocentry.cdte_addr.lba;
|
||
|
int control = tocentry.cdte_ctrl & 0x0f;
|
||
|
int mode = tocentry.cdte_datamode;
|
||
|
if( i > tochdr.cdth_trk0 ) {
|
||
|
Track track( lastTrack.firstSector(), startSec-1, lastTrack.type(), lastTrack.mode() );
|
||
|
track.m_preEmphasis = control & 0x1;
|
||
|
track.m_copyPermitted = control & 0x2;
|
||
|
toc.append( track );
|
||
|
}
|
||
|
int trackType = 0;
|
||
|
int trackMode = Track::UNKNOWN;
|
||
|
if( (control & 0x04 ) && (tocentry.cdte_track != CDROM_LEADOUT) ) {
|
||
|
trackType = Track::DATA;
|
||
|
if( mode == 1 )
|
||
|
trackMode = Track::MODE1;
|
||
|
else if( mode == 2 )
|
||
|
trackMode = Track::MODE2;
|
||
|
|
||
|
mode = getDataMode(startSec);
|
||
|
if( mode != Track::UNKNOWN )
|
||
|
trackMode = mode;
|
||
|
}
|
||
|
else
|
||
|
trackType = Track::AUDIO;
|
||
|
|
||
|
lastTrack = Track( startSec, startSec, trackType, trackMode );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( needToClose )
|
||
|
close();
|
||
|
}
|
||
|
else
|
||
|
success = false;
|
||
|
|
||
|
usageUnlock();
|
||
|
|
||
|
return success;
|
||
|
}
|
||
|
#endif // Q_OS_LINUX
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::fixupToc( K3bDevice::Toc& toc ) const
|
||
|
{
|
||
|
bool success = false;
|
||
|
|
||
|
//
|
||
|
// This is a very lame method of fixing the TOC of an Advanced Audio CD
|
||
|
// (a CD with two sessions: one with audio tracks and one with the data track)
|
||
|
// If a drive does not support reading raw toc or reading track info we only
|
||
|
// get every track's first sector. But between sessions there is a gap which is used
|
||
|
// for ms stuff. In this case it's 11400 sectors in size. When ripping ausio we would
|
||
|
// include these 11400 sectors which would result in a strange ending audio file.
|
||
|
//
|
||
|
if( numSessions() > 1 || toc.contentType() == MIXED ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) fixup multisession toc..." << endl;
|
||
|
|
||
|
//
|
||
|
// we need to update the last sector of every last track in every session
|
||
|
// for now we only update the track before the last session...
|
||
|
// This is the most often case: Advanced Audio CD
|
||
|
//
|
||
|
|
||
|
unsigned char* data = 0;
|
||
|
unsigned int dataLen = 0;
|
||
|
if( readTocPmaAtip( &data, dataLen, 1, false, 0 ) ) {
|
||
|
|
||
|
//
|
||
|
// data[6] - first track number in last complete session
|
||
|
// data[8-11] - start address of first track in last session
|
||
|
//
|
||
|
|
||
|
toc[(unsigned int)data[6]-2].m_lastSector = from4Byte( &data[8] ) - 11400 - 1;
|
||
|
|
||
|
delete [] data;
|
||
|
success = true;
|
||
|
}
|
||
|
else
|
||
|
k3bDebug() << "(K3bDevice::Device) FIXUP TOC failed." << endl;
|
||
|
}
|
||
|
|
||
|
return success;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::block( bool b ) const
|
||
|
{
|
||
|
//
|
||
|
// For some reason the Scsi Command does not work here.
|
||
|
// So we use the ioctl on Linux systems
|
||
|
//
|
||
|
#if defined(Q_OS_LINUX)
|
||
|
bool success = false;
|
||
|
bool needToClose = !isOpen();
|
||
|
usageLock();
|
||
|
if( open() ) {
|
||
|
success = ( ::ioctl( d->deviceFd, CDROM_LOCKDOOR, b ? 1 : 0 ) == 0 );
|
||
|
if( needToClose )
|
||
|
close();
|
||
|
}
|
||
|
usageUnlock();
|
||
|
if ( success )
|
||
|
return success;
|
||
|
#elif defined(Q_OS_NETBSD)
|
||
|
bool success = false;
|
||
|
bool needToClose = !isOpen();
|
||
|
int arg = b ? 1 : 0;
|
||
|
usageLock();
|
||
|
if( open() ) {
|
||
|
success = ( ::ioctl( d->deviceFd, DIOCLOCK, &arg ) == 0 );
|
||
|
if( needToClose )
|
||
|
close();
|
||
|
}
|
||
|
usageUnlock();
|
||
|
if ( success )
|
||
|
return success;
|
||
|
#endif
|
||
|
|
||
|
ScsiCommand cmd( this );
|
||
|
cmd[0] = MMC_PREVENT_ALLOW_MEDIUM_REMOVAL;
|
||
|
cmd[5] = 0; // Necessary to set the proper command length
|
||
|
if( b )
|
||
|
cmd[4] = 0x01;
|
||
|
int r = cmd.transport( TR_DIR_WRITE );
|
||
|
|
||
|
if( r )
|
||
|
k3bDebug() << "(K3bDevice::Device) MMC ALLOW MEDIA REMOVAL failed." << endl;
|
||
|
|
||
|
return ( r == 0 );
|
||
|
}
|
||
|
|
||
|
bool K3bDevice::Device::rewritable() const
|
||
|
{
|
||
|
unsigned char* data = 0;
|
||
|
unsigned int dataLen = 0;
|
||
|
|
||
|
if( readDiscInformation( &data, dataLen ) ) {
|
||
|
disc_info_t* inf = (disc_info_t*)data;
|
||
|
bool e = inf->erasable;
|
||
|
|
||
|
delete [] data;
|
||
|
|
||
|
return e;
|
||
|
}
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::eject() const
|
||
|
{
|
||
|
#ifdef Q_OS_NETBSD
|
||
|
bool success = false;
|
||
|
bool needToClose = !isOpen();
|
||
|
int arg = 0;
|
||
|
|
||
|
usageLock();
|
||
|
if( open() ) {
|
||
|
if ( ::ioctl( d->deviceFd, DIOCEJECT, &arg ) >= 0)
|
||
|
success = true;
|
||
|
if( needToClose )
|
||
|
close();
|
||
|
}
|
||
|
usageUnlock();
|
||
|
if ( success )
|
||
|
return success;
|
||
|
#elif defined(Q_OS_LINUX)
|
||
|
bool success = false;
|
||
|
bool needToClose = !isOpen();
|
||
|
|
||
|
usageLock();
|
||
|
if( open() ) {
|
||
|
if( ::ioctl( d->deviceFd, CDROMEJECT ) >= 0 )
|
||
|
success = true;
|
||
|
if( needToClose )
|
||
|
close();
|
||
|
}
|
||
|
usageUnlock();
|
||
|
if ( success )
|
||
|
return success;
|
||
|
#endif
|
||
|
|
||
|
ScsiCommand cmd( this );
|
||
|
cmd[0] = MMC_PREVENT_ALLOW_MEDIUM_REMOVAL;
|
||
|
cmd[5] = 0; // Necessary to set the proper command length
|
||
|
cmd.transport();
|
||
|
|
||
|
cmd[0] = MMC_START_STOP_UNIT;
|
||
|
cmd[5] = 0; // Necessary to set the proper command length
|
||
|
cmd[4] = 0x1; // Start unit
|
||
|
cmd.transport();
|
||
|
|
||
|
cmd[4] = 0x2; // LoEj = 1, Start = 0
|
||
|
|
||
|
return !cmd.transport();
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::load() const
|
||
|
{
|
||
|
#ifdef Q_OS_NETBSD
|
||
|
bool success = false;
|
||
|
bool needToClose = !isOpen();
|
||
|
int arg = 0;
|
||
|
|
||
|
usageLock();
|
||
|
if( open() ) {
|
||
|
if ( ::ioctl( d->deviceFd, CDIOCCLOSE, &arg ) >= 0)
|
||
|
success = true;
|
||
|
if( needToClose )
|
||
|
close();
|
||
|
}
|
||
|
usageUnlock();
|
||
|
if ( success )
|
||
|
return success;
|
||
|
#elif defined(Q_OS_LINUX)
|
||
|
bool success = false;
|
||
|
bool needToClose = !isOpen();
|
||
|
|
||
|
usageLock();
|
||
|
if( open() ) {
|
||
|
if( ::ioctl( d->deviceFd, CDROMCLOSETRAY ) >= 0 )
|
||
|
success = true;
|
||
|
if( needToClose )
|
||
|
close();
|
||
|
}
|
||
|
usageUnlock();
|
||
|
if ( success )
|
||
|
return success;
|
||
|
#endif
|
||
|
|
||
|
ScsiCommand cmd( this );
|
||
|
cmd[0] = MMC_START_STOP_UNIT;
|
||
|
cmd[4] = 0x3; // LoEj = 1, Start = 1
|
||
|
cmd[5] = 0; // Necessary to set the proper command length
|
||
|
return !cmd.transport();
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::setAutoEjectEnabled( bool enabled ) const
|
||
|
{
|
||
|
bool success = false;
|
||
|
#ifdef Q_OS_LINUX
|
||
|
|
||
|
bool needToClose = !isOpen();
|
||
|
usageLock();
|
||
|
if ( open() ) {
|
||
|
success = ( ::ioctl( d->deviceFd, CDROMEJECT_SW, enabled ? 1 : 0 ) == 0 );
|
||
|
if ( needToClose ) {
|
||
|
close();
|
||
|
}
|
||
|
}
|
||
|
usageUnlock();
|
||
|
#endif
|
||
|
return success;
|
||
|
}
|
||
|
|
||
|
|
||
|
void K3bDevice::Device::addDeviceNode( const QString& n )
|
||
|
{
|
||
|
if( !d->allNodes.contains( n ) )
|
||
|
d->allNodes.append( n );
|
||
|
}
|
||
|
|
||
|
|
||
|
const QStringList& K3bDevice::Device::deviceNodes() const
|
||
|
{
|
||
|
return d->allNodes;
|
||
|
}
|
||
|
|
||
|
|
||
|
K3bDevice::Device::Handle K3bDevice::Device::handle() const
|
||
|
{
|
||
|
#ifdef Q_OS_FREEBSD
|
||
|
return d->cam;
|
||
|
#else
|
||
|
return d->deviceFd;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::open( bool write ) const
|
||
|
{
|
||
|
if( d->openedReadWrite != write )
|
||
|
close();
|
||
|
|
||
|
QMutexLocker ml( &d->openCloseMutex );
|
||
|
|
||
|
d->openedReadWrite = write;
|
||
|
|
||
|
#ifdef Q_OS_FREEBSD
|
||
|
if( !d->cam ) {
|
||
|
d->cam = cam_open_pass (m_passDevice.latin1(), O_RDWR,0 /* NULL */);
|
||
|
k3bDebug() << "(K3bDevice::openDevice) open device " << m_passDevice
|
||
|
<< ((d->cam)?" succeeded.":" failed.") << endl;
|
||
|
}
|
||
|
|
||
|
return (d->cam != 0);
|
||
|
#endif
|
||
|
#if defined(Q_OS_LINUX) || defined(Q_OS_NETBSD)
|
||
|
if( d->deviceFd == -1 )
|
||
|
d->deviceFd = openDevice( QFile::encodeName(devicename()), write );
|
||
|
|
||
|
return ( d->deviceFd != -1 );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
void K3bDevice::Device::close() const
|
||
|
{
|
||
|
QMutexLocker ml( &d->openCloseMutex );
|
||
|
|
||
|
#ifdef Q_OS_FREEBSD
|
||
|
if( d->cam ) {
|
||
|
cam_close_device(d->cam);
|
||
|
d->cam = 0;
|
||
|
}
|
||
|
#endif
|
||
|
#if defined(Q_OS_LINUX) || defined(Q_OS_NETBSD)
|
||
|
if( d->deviceFd != -1 ) {
|
||
|
::close( d->deviceFd );
|
||
|
d->deviceFd = -1;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::isOpen() const
|
||
|
{
|
||
|
#ifdef Q_OS_FREEBSD
|
||
|
return d->cam;
|
||
|
#endif
|
||
|
#if defined(Q_OS_LINUX) || defined(Q_OS_NETBSD)
|
||
|
return ( d->deviceFd != -1 );
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
int K3bDevice::Device::supportedProfiles() const
|
||
|
{
|
||
|
return d->supportedProfiles;
|
||
|
}
|
||
|
|
||
|
|
||
|
int K3bDevice::Device::currentProfile() const
|
||
|
{
|
||
|
unsigned char profileBuf[8];
|
||
|
::memset( profileBuf, 0, 8 );
|
||
|
|
||
|
ScsiCommand cmd( this );
|
||
|
cmd[0] = MMC_GET_CONFIGURATION;
|
||
|
cmd[1] = 1;
|
||
|
cmd[8] = 8;
|
||
|
cmd[9] = 0; // Necessary to set the proper command length
|
||
|
|
||
|
if( cmd.transport( TR_DIR_READ, profileBuf, 8 ) ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
|
||
|
<< " GET_CONFIGURATION failed." << endl;
|
||
|
return MEDIA_UNKNOWN;
|
||
|
}
|
||
|
else {
|
||
|
short profile = from2Byte( &profileBuf[6] );
|
||
|
|
||
|
//
|
||
|
// Plextor drives might not set a current profile
|
||
|
// In that case we get the list of all current profiles
|
||
|
// and simply use the first one in that list.
|
||
|
//
|
||
|
if( profile == 0x00 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
|
||
|
<< " current profile 0. Checking current profile list instead." << endl;
|
||
|
unsigned char* data;
|
||
|
unsigned int len = 0;
|
||
|
if( getFeature( &data, len, FEATURE_PROFILE_LIST ) ) {
|
||
|
int featureLen( data[11] );
|
||
|
for( int j = 0; j < featureLen; j+=4 ) {
|
||
|
// use the first current profile we encounter
|
||
|
if( data[12+j+2] & 0x1 ) {
|
||
|
profile = from2Byte( &data[12+j] );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
delete[] data;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch (profile) {
|
||
|
case 0x00: return MEDIA_NONE;
|
||
|
case 0x08: return MEDIA_CD_ROM;
|
||
|
case 0x09: return MEDIA_CD_R;
|
||
|
case 0x0A: return MEDIA_CD_RW;
|
||
|
case 0x10: return MEDIA_DVD_ROM;
|
||
|
case 0x11: return MEDIA_DVD_R_SEQ;
|
||
|
case 0x12: return MEDIA_DVD_RAM;
|
||
|
case 0x13: return MEDIA_DVD_RW_OVWR;
|
||
|
case 0x14: return MEDIA_DVD_RW_SEQ;
|
||
|
case 0x15: return MEDIA_DVD_R_DL_SEQ;
|
||
|
case 0x16: return MEDIA_DVD_R_DL_JUMP;
|
||
|
case 0x1A: return MEDIA_DVD_PLUS_RW;
|
||
|
case 0x1B: return MEDIA_DVD_PLUS_R;
|
||
|
case 0x2B: return MEDIA_DVD_PLUS_R_DL;
|
||
|
case 0x40: return MEDIA_BD_ROM;
|
||
|
case 0x41: {
|
||
|
if( featureCurrent( FEATURE_BD_PSEUDO_OVERWRITE ) == 1 )
|
||
|
return MEDIA_BD_R_SRM_POW;
|
||
|
else
|
||
|
return MEDIA_BD_R_SRM;
|
||
|
}
|
||
|
case 0x42: return MEDIA_BD_R_RRM;
|
||
|
case 0x43: return MEDIA_BD_RE;
|
||
|
case 0x50: return MEDIA_HD_DVD_ROM;
|
||
|
case 0x51: return MEDIA_HD_DVD_R;
|
||
|
case 0x52: return MEDIA_HD_DVD_RAM;
|
||
|
default: return MEDIA_UNKNOWN;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
K3bDevice::DiskInfo K3bDevice::Device::diskInfo() const
|
||
|
{
|
||
|
DiskInfo inf;
|
||
|
|
||
|
// if the device is already opened we do not close it
|
||
|
// to allow fast multiple method calls in a row
|
||
|
bool needToClose = !isOpen();
|
||
|
|
||
|
if( open() ) {
|
||
|
|
||
|
unsigned char* data = 0;
|
||
|
unsigned int dataLen = 0;
|
||
|
|
||
|
//
|
||
|
// The first thing to do should be: checking if a media is loaded
|
||
|
// We cannot rely on the profile here since at least some Plextor
|
||
|
// drives return the NO MEDIUM profile for CD media
|
||
|
//
|
||
|
if( !testUnitReady() ) {
|
||
|
// no disk or tray open
|
||
|
inf.m_diskState = STATE_NO_MEDIA;
|
||
|
inf.m_mediaType = MEDIA_NONE;
|
||
|
inf.m_currentProfile = MEDIA_NONE;
|
||
|
}
|
||
|
else
|
||
|
inf.m_currentProfile = currentProfile();
|
||
|
|
||
|
if( inf.diskState() != STATE_NO_MEDIA ) {
|
||
|
|
||
|
if( readDiscInformation( &data, dataLen ) ) {
|
||
|
disc_info_t* dInf = (disc_info_t*)data;
|
||
|
//
|
||
|
// Copy the needed values from the disk_info struct
|
||
|
//
|
||
|
switch( dInf->status ) {
|
||
|
case 0:
|
||
|
inf.m_diskState = STATE_EMPTY;
|
||
|
break;
|
||
|
case 1:
|
||
|
inf.m_diskState = STATE_INCOMPLETE;
|
||
|
break;
|
||
|
case 2:
|
||
|
inf.m_diskState = STATE_COMPLETE;
|
||
|
break;
|
||
|
default:
|
||
|
inf.m_diskState = STATE_UNKNOWN;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
switch( dInf->border ) {
|
||
|
case 0:
|
||
|
inf.m_lastSessionState = STATE_EMPTY;
|
||
|
break;
|
||
|
case 1:
|
||
|
inf.m_lastSessionState = STATE_INCOMPLETE;
|
||
|
break;
|
||
|
case 2:
|
||
|
inf.m_lastSessionState = STATE_COMPLETE;
|
||
|
break;
|
||
|
default:
|
||
|
inf.m_lastSessionState = STATE_UNKNOWN;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
switch( dInf->bg_f_status&0x3 ) {
|
||
|
case 0x0:
|
||
|
inf.m_bgFormatState = BG_FORMAT_NONE;
|
||
|
break;
|
||
|
case 0x1:
|
||
|
inf.m_bgFormatState = BG_FORMAT_INCOMPLETE;
|
||
|
break;
|
||
|
case 0x2:
|
||
|
inf.m_bgFormatState = BG_FORMAT_IN_PROGRESS;
|
||
|
break;
|
||
|
case 0x3:
|
||
|
inf.m_bgFormatState = BG_FORMAT_COMPLETE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
inf.m_numTracks = (dInf->last_track_l & 0xff) | (dInf->last_track_m<<8 & 0xff00);
|
||
|
if( inf.diskState() == STATE_EMPTY )
|
||
|
inf.m_numTracks = 0;
|
||
|
|
||
|
// FIXME: I am not sure if this is accurate. Better test the last track's RT field
|
||
|
else if( inf.diskState() == STATE_INCOMPLETE )
|
||
|
inf.m_numTracks--; // do not count the invisible track
|
||
|
|
||
|
inf.m_rewritable = dInf->erasable;
|
||
|
|
||
|
//
|
||
|
// This is the Last Possible Lead-Out Start Address in HMSF format
|
||
|
// This is only valid for CD-R(W) and DVD+R media.
|
||
|
// For complete media this shall be filled with 0xff
|
||
|
//
|
||
|
if( dInf->lead_out_m != 0xff &&
|
||
|
dInf->lead_out_r != 0xff &&
|
||
|
dInf->lead_out_s != 0xff &&
|
||
|
dInf->lead_out_f != 0xff )
|
||
|
inf.m_capacity = K3b::Msf( dInf->lead_out_m + dInf->lead_out_r*60,
|
||
|
dInf->lead_out_s,
|
||
|
dInf->lead_out_f ) - 150;
|
||
|
|
||
|
//
|
||
|
// This is the position where the next Session shall be recorded in HMSF format
|
||
|
// This is only valid for CD-R(W) and DVD+R media.
|
||
|
// For complete media this shall be filled with 0xff
|
||
|
//
|
||
|
if( dInf->lead_in_m != 0xff &&
|
||
|
dInf->lead_in_r != 0xff &&
|
||
|
dInf->lead_in_s != 0xff &&
|
||
|
dInf->lead_in_f != 0xff )
|
||
|
inf.m_usedCapacity = K3b::Msf( dInf->lead_in_m + dInf->lead_in_r*60,
|
||
|
dInf->lead_in_s,
|
||
|
dInf->lead_in_f ) - 4500;
|
||
|
|
||
|
delete [] data;
|
||
|
}
|
||
|
else {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
|
||
|
<< " fabricating disk information for a stupid device." << endl;
|
||
|
Toc toc = readToc();
|
||
|
if( !toc.isEmpty() ) {
|
||
|
inf.m_diskState = STATE_COMPLETE;
|
||
|
inf.m_lastSessionState = STATE_COMPLETE;
|
||
|
inf.m_numTracks = toc.count();
|
||
|
inf.m_capacity = inf.m_usedCapacity = toc.length();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// The mediatype needs to be set
|
||
|
//
|
||
|
inf.m_mediaType = mediaType();
|
||
|
|
||
|
// At least some Plextor drives return profile NONE for CD media
|
||
|
// or CD_ROM for writable media
|
||
|
if( inf.m_mediaType & (MEDIA_UNKNOWN|MEDIA_NONE|MEDIA_CD_ROM) ) {
|
||
|
// probably it is a CD
|
||
|
if( inf.rewritable() )
|
||
|
inf.m_mediaType = MEDIA_CD_RW;
|
||
|
else if( inf.empty() || inf.appendable() )
|
||
|
inf.m_mediaType = MEDIA_CD_R;
|
||
|
else
|
||
|
inf.m_mediaType = MEDIA_CD_ROM;
|
||
|
}
|
||
|
|
||
|
if( inf.m_mediaType & MEDIA_DVD_ALL ) {
|
||
|
if( readDvdStructure( &data, dataLen ) ) {
|
||
|
// some debugging stuff
|
||
|
K3b::Msf sda, eda, ea0;
|
||
|
sda = ( data[4+5]<<16 | data[4+6] << 8 | data[4+7] );
|
||
|
eda = ( data[4+9]<<16 | data[4+10] << 8 | data[4+11] );
|
||
|
ea0 = ( data[4+13]<<16 | data[4+14] << 8 | data[4+15] );
|
||
|
|
||
|
k3bDebug() << "First sec data area: " << sda.toString()
|
||
|
<< " (LBA " << QString::number(sda.lba())
|
||
|
<< ") (" << QString::number(sda.mode1Bytes()) << endl;
|
||
|
k3bDebug() << "Last sec data area: " << eda.toString()
|
||
|
<< " (LBA " << QString::number(eda.lba())
|
||
|
<< ") (" << QString::number(eda.mode1Bytes()) << " Bytes)" << endl;
|
||
|
k3bDebug() << "Last sec layer 1: " << ea0.toString()
|
||
|
<< " (LBA " << QString::number(ea0.lba())
|
||
|
<< ") (" << QString::number(ea0.mode1Bytes()) << " Bytes)" << endl;
|
||
|
|
||
|
|
||
|
K3b::Msf da0 = ea0 - sda + 1;
|
||
|
K3b::Msf da1 = eda - ea0;
|
||
|
k3bDebug() << "Layer 1 length: " << da0.toString()
|
||
|
<< " (LBA " << QString::number(da0.lba())
|
||
|
<< ") (" << QString::number(da0.mode1Bytes()) << " Bytes)" << endl;
|
||
|
k3bDebug() << "Layer 2 length: " << da1.toString()
|
||
|
<< " (LBA " << QString::number(da1.lba())
|
||
|
<< ") (" << QString::number(da1.mode1Bytes()) << " Bytes)" << endl;
|
||
|
|
||
|
inf.m_numLayers = ((data[6]&0x60) == 0 ? 1 : 2);
|
||
|
|
||
|
bool otp = (data[4+2] & 0xF);
|
||
|
|
||
|
// ea0 is 0 if the medium does not use Opposite track path
|
||
|
if( otp && ea0 > 0 )
|
||
|
inf.m_firstLayerSize = da0;
|
||
|
else
|
||
|
inf.m_firstLayerSize = 0;
|
||
|
|
||
|
delete [] data;
|
||
|
}
|
||
|
else {
|
||
|
k3bDebug() << "(K3bDevice::Device) Unable to read DVD structure for num of layers." << endl;
|
||
|
inf.m_numLayers = ( (inf.m_mediaType & MEDIA_WRITABLE_DVD_DL) ? 2 : 1 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Number of sessions for non-empty disks
|
||
|
//
|
||
|
if( inf.diskState() != STATE_EMPTY ) {
|
||
|
int sessions = numSessions();
|
||
|
if( sessions >= 0 )
|
||
|
inf.m_numSessions = sessions;
|
||
|
else
|
||
|
k3bDebug() << "(K3bDevice::Device) could not get session info via READ TOC/PMA/ATIP." << endl;
|
||
|
}
|
||
|
else
|
||
|
inf.m_numSessions = 0;
|
||
|
|
||
|
inf.m_mediaId = mediaId( inf.mediaType() );
|
||
|
|
||
|
//
|
||
|
// Now we determine the size:
|
||
|
|
||
|
// for all empty and appendable media READ FORMAT CAPACITIES should return the proper unformatted size
|
||
|
// for complete disks we may use the READ_CAPACITY command or the start sector from the leadout
|
||
|
//
|
||
|
int media = inf.mediaType();
|
||
|
//
|
||
|
// Use the profile if available because DVD-ROM units need to treat DVD+-R(W) media as DVD-ROM
|
||
|
// if supported at all
|
||
|
//
|
||
|
if( inf.currentProfile() == MEDIA_DVD_ROM )
|
||
|
media = MEDIA_DVD_ROM;
|
||
|
|
||
|
switch( media ) {
|
||
|
case MEDIA_CD_R:
|
||
|
case MEDIA_CD_RW:
|
||
|
if( inf.m_capacity == 0 ) {
|
||
|
if( readTocPmaAtip( &data, dataLen, 0x4, true, 0 ) ) {
|
||
|
|
||
|
struct atip_descriptor* atip = (struct atip_descriptor*)data;
|
||
|
|
||
|
if( dataLen >= 11 ) {
|
||
|
inf.m_capacity = K3b::Msf( atip->lead_out_m, atip->lead_out_s, atip->lead_out_f ) - 150;
|
||
|
debugBitfield( &atip->lead_out_m, 3 );
|
||
|
k3bDebug() << blockDeviceName() << ": ATIP capacity: " << inf.m_capacity.toString() << endl;
|
||
|
}
|
||
|
|
||
|
delete [] data;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// for empty and appendable media capacity and usedCapacity should be filled in from
|
||
|
// diskinfo above. If not they are both still 0
|
||
|
//
|
||
|
if( inf.m_capacity != 0 &&
|
||
|
( inf.diskState() == STATE_EMPTY || inf.m_usedCapacity != 0 ) ) {
|
||
|
// done.
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
case MEDIA_CD_ROM:
|
||
|
if( inf.m_capacity > 0 && inf.m_usedCapacity == 0 )
|
||
|
inf.m_usedCapacity = inf.m_capacity;
|
||
|
|
||
|
if( inf.m_usedCapacity == 0 ) {
|
||
|
K3b::Msf readCap;
|
||
|
if( readCapacity( readCap ) ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) READ CAPACITY: " << readCap.toString()
|
||
|
<< " other capacity: " << inf.m_capacity.toString() << endl;
|
||
|
//
|
||
|
// READ CAPACITY returns the last written sector
|
||
|
// that means the size is actually readCap + 1
|
||
|
//
|
||
|
inf.m_usedCapacity = readCap + 1;
|
||
|
}
|
||
|
else {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
|
||
|
<< " Falling back to readToc for capacity." << endl;
|
||
|
inf.m_usedCapacity = readToc().length();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
case MEDIA_DVD_ROM: {
|
||
|
K3b::Msf readCap;
|
||
|
if( readCapacity( readCap ) ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) READ CAPACITY: " << readCap.toString()
|
||
|
<< " other capacity: " << inf.m_capacity.toString() << endl;
|
||
|
//
|
||
|
// READ CAPACITY returns the last written sector
|
||
|
// that means the size is actually readCap + 1
|
||
|
//
|
||
|
inf.m_usedCapacity = readCap + 1;
|
||
|
}
|
||
|
else {
|
||
|
//
|
||
|
// Only one track, use it's size
|
||
|
//
|
||
|
if( readTrackInformation( &data, dataLen, 0x1, 0x1 ) ) {
|
||
|
track_info_t* trackInfo = (track_info_t*)data;
|
||
|
inf.m_usedCapacity = from4Byte( trackInfo->track_size );
|
||
|
delete [] data;
|
||
|
}
|
||
|
else
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
|
||
|
<< "READ TRACK INFORMATION for DVD-ROM failed." << endl;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case MEDIA_DVD_PLUS_R:
|
||
|
case MEDIA_DVD_PLUS_R_DL:
|
||
|
if( inf.appendable() || inf.empty() ) {
|
||
|
//
|
||
|
// get remaining space via the invisible track
|
||
|
//
|
||
|
if( readTrackInformation( &data, dataLen, 0x1, /*0xff*/ inf.numTracks()+1 ) ) {
|
||
|
track_info_t* trackInfo = (track_info_t*)data;
|
||
|
inf.m_usedCapacity = from4Byte( trackInfo->track_start );
|
||
|
inf.m_capacity = from4Byte( trackInfo->track_start ) + from4Byte( trackInfo->track_size );
|
||
|
delete [] data;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if( readTrackInformation( &data, dataLen, 0x1, inf.numTracks() ) ) {
|
||
|
track_info_t* trackInfo = (track_info_t*)data;
|
||
|
inf.m_capacity = inf.m_usedCapacity
|
||
|
= from4Byte( trackInfo->track_start ) + from4Byte( trackInfo->track_size );
|
||
|
delete [] data;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case MEDIA_DVD_R:
|
||
|
case MEDIA_DVD_R_SEQ:
|
||
|
case MEDIA_DVD_R_DL:
|
||
|
case MEDIA_DVD_R_DL_JUMP:
|
||
|
case MEDIA_DVD_R_DL_SEQ:
|
||
|
//
|
||
|
// get data from the incomplete track (which is NOT the invisible track 0xff)
|
||
|
// This will fail in case the media is complete!
|
||
|
//
|
||
|
if( readTrackInformation( &data, dataLen, 0x1, inf.numTracks()+1 ) ) {
|
||
|
track_info_t* trackInfo = (track_info_t*)data;
|
||
|
inf.m_usedCapacity = from4Byte( trackInfo->track_start );
|
||
|
inf.m_capacity = from4Byte( trackInfo->free_blocks ) + from4Byte( trackInfo->track_start );
|
||
|
delete [] data;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get the "really" used space without border-out
|
||
|
//
|
||
|
if( !inf.empty() ) {
|
||
|
K3b::Msf readCap;
|
||
|
if( readCapacity( readCap ) ) {
|
||
|
//
|
||
|
// READ CAPACITY returns the last written sector
|
||
|
// that means the size is actually readCap + 1
|
||
|
//
|
||
|
inf.m_usedCapacity = readCap + 1;
|
||
|
}
|
||
|
else
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
|
||
|
<< " READ CAPACITY for DVD-R failed." << endl;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case MEDIA_DVD_RW_OVWR:
|
||
|
inf.m_numSessions = 1;
|
||
|
case MEDIA_DVD_RW:
|
||
|
case MEDIA_DVD_RW_SEQ:
|
||
|
// only one track on a DVD-RW media
|
||
|
if( readTrackInformation( &data, dataLen, 0x1, 0x1 ) ) {
|
||
|
track_info_t* trackInfo = (track_info_t*)data;
|
||
|
inf.m_capacity = from4Byte( trackInfo->track_size );
|
||
|
if( !inf.empty() ) {
|
||
|
if( readFormatCapacity( 0x10, inf.m_capacity ) )
|
||
|
k3bDebug() << blockDeviceName() << ": Format capacity 0x10: " << inf.m_capacity.toString() << endl;
|
||
|
|
||
|
inf.m_usedCapacity = from4Byte( trackInfo->track_size );
|
||
|
}
|
||
|
|
||
|
delete [] data;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case MEDIA_DVD_PLUS_RW: {
|
||
|
K3b::Msf currentMax;
|
||
|
int currentMaxFormat = 0;
|
||
|
if( readFormatCapacity( 0x26, inf.m_capacity, ¤tMax, ¤tMaxFormat ) ) {
|
||
|
if( currentMaxFormat == 0x1 ) { // unformatted or blank media
|
||
|
inf.m_usedCapacity = 0;
|
||
|
inf.m_capacity = currentMax;
|
||
|
}
|
||
|
else {
|
||
|
inf.m_usedCapacity = currentMax;
|
||
|
// Plextor drives tend to screw things up and report invalid values
|
||
|
// for the max format capacity of 1.4 GB DVD media
|
||
|
if ( inf.bgFormatState() == BG_FORMAT_COMPLETE ) {
|
||
|
inf.m_capacity = currentMax;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
|
||
|
<< " READ FORMAT CAPACITIES for DVD+RW failed." << endl;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case MEDIA_BD_R:
|
||
|
case MEDIA_BD_R_SRM:
|
||
|
case MEDIA_BD_R_SRM_POW:
|
||
|
case MEDIA_BD_R_RRM:
|
||
|
case MEDIA_BD_RE:
|
||
|
//
|
||
|
// get the invisible track's first sector
|
||
|
// or the next writable address of the last open track
|
||
|
//
|
||
|
if( readDiscInformation( &data, dataLen ) ) {
|
||
|
int lastTrack = (int)( data[11]<<8 | data[6] );
|
||
|
delete [] data;
|
||
|
|
||
|
if( readTrackInformation( &data, dataLen, 1, lastTrack ) ) {
|
||
|
|
||
|
// capacity: last track's start address + last track's size
|
||
|
inf.m_capacity = from4Byte( data+8 ) + from4Byte( data+24 );
|
||
|
|
||
|
if( data[6] & 0x80 )
|
||
|
inf.m_usedCapacity = from4Byte( data+8 );
|
||
|
else if( data[7] & 0x1 )
|
||
|
inf.m_usedCapacity = from4Byte( data+12 );
|
||
|
delete [] data;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case MEDIA_BD_ROM: {
|
||
|
K3b::Msf readCap;
|
||
|
if( readCapacity( readCap ) ) {
|
||
|
//
|
||
|
// READ CAPACITY returns the last written sector
|
||
|
// that means the size is actually readCap + 1
|
||
|
//
|
||
|
inf.m_usedCapacity = readCap + 1;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( needToClose )
|
||
|
close();
|
||
|
}
|
||
|
|
||
|
return inf;
|
||
|
}
|
||
|
|
||
|
|
||
|
int K3bDevice::Device::mediaType() const
|
||
|
{
|
||
|
int m = MEDIA_UNKNOWN;
|
||
|
|
||
|
if( testUnitReady() ) {
|
||
|
|
||
|
m = currentProfile();
|
||
|
|
||
|
if( m & (MEDIA_UNKNOWN|MEDIA_DVD_ROM|MEDIA_HD_DVD_ROM) ) {
|
||
|
//
|
||
|
// We prefere the mediatype as reported by the media since this way
|
||
|
// even ROM drives may report the correct type of writable media.
|
||
|
//
|
||
|
|
||
|
// 4 bytes header + 2048 bytes layer descriptor
|
||
|
unsigned char* data = 0;
|
||
|
unsigned int dataLen = 0;
|
||
|
if( readDvdStructure( &data, dataLen ) ) {
|
||
|
switch( data[4]&0xF0 ) {
|
||
|
case 0x00: m = MEDIA_DVD_ROM; break;
|
||
|
case 0x10: m = MEDIA_DVD_RAM; break;
|
||
|
case 0x20: m = MEDIA_DVD_R; break; // there seems to be no value for DVD-R DL, it reports DVD-R
|
||
|
case 0x30: m = MEDIA_DVD_RW; break;
|
||
|
case 0x40: m = MEDIA_HD_DVD_ROM; break;
|
||
|
case 0x50: m = MEDIA_HD_DVD_R; break;
|
||
|
case 0x60: m = MEDIA_HD_DVD_RAM; break;
|
||
|
case 0x90: m = MEDIA_DVD_PLUS_RW; break;
|
||
|
case 0xA0: m = MEDIA_DVD_PLUS_R; break;
|
||
|
case 0xE0: m = MEDIA_DVD_PLUS_R_DL; break;
|
||
|
default:
|
||
|
k3bDebug() << "(K3bDevice::Device) unknown dvd media type: " << QString::number(data[4]&0xF0, 8) << endl;
|
||
|
break; // unknown
|
||
|
}
|
||
|
|
||
|
delete [] data;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( m & (MEDIA_UNKNOWN|MEDIA_BD_ROM) ) {
|
||
|
//
|
||
|
// We prefere the mediatype as reported by the media since this way
|
||
|
// even ROM drives may report the correct type of writable media.
|
||
|
//
|
||
|
|
||
|
unsigned char* data = 0;
|
||
|
unsigned int dataLen = 0;
|
||
|
if( readDiscStructure( &data, dataLen, 1, 0 ) ) {
|
||
|
if( dataLen > 4+12 &&
|
||
|
data[4+8] == 'B' && data[4+9] == 'D' ) {
|
||
|
switch( data[4+10] ) {
|
||
|
case 'O': m = MEDIA_BD_ROM; break;
|
||
|
case 'W': m = MEDIA_BD_RE; break;
|
||
|
case 'R': m = MEDIA_BD_R; break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
delete [] data;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Only old CD or DVD devices do not report a current profile
|
||
|
// or report CD-ROM profile for all CD types
|
||
|
//
|
||
|
if( m & (MEDIA_UNKNOWN|MEDIA_CD_ROM) ) {
|
||
|
unsigned char* data = 0;
|
||
|
unsigned int dataLen = 0;
|
||
|
if( readTocPmaAtip( &data, dataLen, 4, false, 0 ) ) {
|
||
|
if( (data[6]>>6)&1 )
|
||
|
m = MEDIA_CD_RW;
|
||
|
else
|
||
|
m = MEDIA_CD_R;
|
||
|
|
||
|
delete [] data;
|
||
|
}
|
||
|
else
|
||
|
m = MEDIA_CD_ROM;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return m;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::readSectorsRaw( unsigned char *buf, int start, int count ) const
|
||
|
{
|
||
|
return readCd( buf, count*2352,
|
||
|
0, // all sector types
|
||
|
false, // no dap
|
||
|
start,
|
||
|
count,
|
||
|
true, // SYNC
|
||
|
true, // HEADER
|
||
|
true, // SUBHEADER
|
||
|
true, // USER DATA
|
||
|
true, // EDC/ECC
|
||
|
0, // no c2 info
|
||
|
0 );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void K3bDevice::Device::checkForJustLink()
|
||
|
{
|
||
|
unsigned char* ricoh = 0;
|
||
|
unsigned int ricohLen = 0;
|
||
|
if( modeSense( &ricoh, ricohLen, 0x30 ) ) {
|
||
|
|
||
|
//
|
||
|
// 8 byte mode header + 6 byte page data
|
||
|
//
|
||
|
|
||
|
if( ricohLen >= 14 ) {
|
||
|
ricoh_mode_page_30* rp = (ricoh_mode_page_30*)(ricoh+8);
|
||
|
d->burnfree = rp->BUEFS;
|
||
|
}
|
||
|
|
||
|
delete [] ricoh;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void K3bDevice::Device::checkFeatures()
|
||
|
{
|
||
|
unsigned char header[1024];
|
||
|
::memset( header, 0, 1024 );
|
||
|
|
||
|
ScsiCommand cmd( this );
|
||
|
cmd[0] = MMC_GET_CONFIGURATION;
|
||
|
cmd[1] = 2;
|
||
|
cmd[9] = 0; // Necessary to set the proper command length
|
||
|
|
||
|
|
||
|
//
|
||
|
// CD writing features
|
||
|
//
|
||
|
cmd[2] = FEATURE_CD_MASTERING>>8;
|
||
|
cmd[3] = FEATURE_CD_MASTERING;
|
||
|
cmd[8] = 8+8;
|
||
|
if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
|
||
|
unsigned int len = from4Byte( header );
|
||
|
if( len >= 12 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "CD Mastering" << endl;
|
||
|
#ifdef WORDS_BIGENDIAN
|
||
|
struct cd_mastering_feature {
|
||
|
unsigned char reserved1 : 1;
|
||
|
unsigned char BUF : 1; // Burnfree
|
||
|
unsigned char SAO : 1; // Session At Once writing
|
||
|
unsigned char raw_ms : 1; // Writing Multisession in Raw Writing Mode
|
||
|
unsigned char raw : 1; // Writing in WRITINGMODE_RAW mode
|
||
|
unsigned char testwrite : 1; // Simulation write support
|
||
|
unsigned char cd_rw : 1; // CD-RW support
|
||
|
unsigned char rw_sub : 1; // Write R-W sub channels with user data
|
||
|
unsigned char max_cue_length[3];
|
||
|
};
|
||
|
#else
|
||
|
struct cd_mastering_feature {
|
||
|
unsigned char rw_sub : 1; // Write R-W sub channels with user data
|
||
|
unsigned char cd_rw : 1; // CD-RW support
|
||
|
unsigned char testwrite : 1; // Simulation write support
|
||
|
unsigned char raw : 1; // Writing in WRITINGMODE_RAW mode
|
||
|
unsigned char raw_ms : 1; // Writing Multisession in Raw Writing Mode
|
||
|
unsigned char SAO : 1; // Session At Once writing
|
||
|
unsigned char BUF : 1; // Burnfree
|
||
|
unsigned char reserved1 : 1;
|
||
|
unsigned char max_cue_length[3];
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
struct cd_mastering_feature* p = (struct cd_mastering_feature*)&header[12];
|
||
|
if( p->BUF ) d->burnfree = true;
|
||
|
d->writeCapabilities |= MEDIA_CD_R;
|
||
|
if( p->cd_rw )
|
||
|
d->writeCapabilities |= MEDIA_CD_RW;
|
||
|
// if( p->WRITINGMODE_SAO ) m_writeModes |= WRITINGMODE_SAO;
|
||
|
// if( p->raw || p->raw_ms ) m_writeModes |= WRITINGMODE_RAW; // WRITINGMODE_RAW16 always supported when raw is supported?
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cmd[2] = FEATURE_CD_TRACK_AT_ONCE>>8;
|
||
|
cmd[3] = FEATURE_CD_TRACK_AT_ONCE;
|
||
|
cmd[8] = 8+8;
|
||
|
if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
|
||
|
unsigned int len = from4Byte( header );
|
||
|
if( len >= 12 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "CD Track At Once" << endl;
|
||
|
#ifdef WORDS_BIGENDIAN
|
||
|
struct cd_track_at_once_feature {
|
||
|
unsigned char reserved1 : 1;
|
||
|
unsigned char BUF : 1; // Burnfree
|
||
|
unsigned char reserved2 : 1;
|
||
|
unsigned char rw_raw : 1; // Writing R-W subcode in Raw mode
|
||
|
unsigned char rw_pack : 1; // Writing R-W subcode in Packet mode
|
||
|
unsigned char testwrite : 1; // Simulation write support
|
||
|
unsigned char cd_rw : 1; // CD-RW support
|
||
|
unsigned char rw_sub : 1; // Write R-W sub channels with user data
|
||
|
unsigned char reserved3;
|
||
|
unsigned char data_type[2];
|
||
|
};
|
||
|
#else
|
||
|
struct cd_track_at_once_feature {
|
||
|
unsigned char rw_sub : 1; // Write R-W sub channels with user data
|
||
|
unsigned char cd_rw : 1; // CD-RW support
|
||
|
unsigned char testwrite : 1; // Simulation write support
|
||
|
unsigned char rw_pack : 1; // Writing R-W subcode in Packet mode
|
||
|
unsigned char rw_raw : 1; // Writing R-W subcode in Raw mode
|
||
|
unsigned char reserved2 : 1;
|
||
|
unsigned char BUF : 1; // Burnfree
|
||
|
unsigned char reserved1 : 1;
|
||
|
unsigned char reserved3;
|
||
|
unsigned char data_type[2];
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
struct cd_track_at_once_feature* p = (struct cd_track_at_once_feature*)&header[12];
|
||
|
m_writeModes |= WRITINGMODE_TAO;
|
||
|
if( p->BUF ) d->burnfree = true;
|
||
|
d->writeCapabilities |= MEDIA_CD_R;
|
||
|
if( p->cd_rw )
|
||
|
d->writeCapabilities |= MEDIA_CD_RW;
|
||
|
|
||
|
// is the following correct? What exactly does rw_sub tell us?
|
||
|
// if( m_writeModes & WRITINGMODE_RAW ) {
|
||
|
// if( p->rw_raw ) m_writeModes |= WRITINGMODE_RAW_R96R;
|
||
|
// if( p->rw_pack ) m_writeModes |= WRITINGMODE_RAW_R96P;
|
||
|
// }
|
||
|
|
||
|
// // check the data types for 1, 2, and 3 (raw16, raw96p, and raw96r)
|
||
|
// debugBitfield( p->data_type, 2 );
|
||
|
// if( m_writeModes & WRITINGMODE_RAW ) {
|
||
|
// if( p->data_type[1] & 0x20 ) m_writeModes |= WRITINGMODE_RAW_R16;
|
||
|
// if( p->data_type[1] & 0x40 ) m_writeModes |= WRITINGMODE_RAW_R96P;
|
||
|
// if( p->data_type[1] & 0x80 ) m_writeModes |= WRITINGMODE_RAW_R96R;
|
||
|
// }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cmd[2] = FEATURE_CD_RW_MEDIA_WRITE_SUPPORT>>8;
|
||
|
cmd[3] = FEATURE_CD_RW_MEDIA_WRITE_SUPPORT;
|
||
|
cmd[8] = 8+8;
|
||
|
if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
|
||
|
unsigned int len = from4Byte( header );
|
||
|
if( len >= 12 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "CD-RW Media Write Support" << endl;
|
||
|
d->writeCapabilities |= (MEDIA_CD_R|MEDIA_CD_RW);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// DVD-ROM
|
||
|
//
|
||
|
// FIXME: since MMC5 the feature descr. is 8 bytes in length including a dvd dl read bit at byte 6
|
||
|
cmd[2] = FEATURE_DVD_READ>>8;
|
||
|
cmd[3] = FEATURE_DVD_READ;
|
||
|
cmd[8] = 8+8;
|
||
|
if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
|
||
|
unsigned int len = from4Byte( header );
|
||
|
if( len >= 12 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "DVD Read (MMC5)" << endl;
|
||
|
d->readCapabilities |= MEDIA_DVD_ROM;
|
||
|
if( header[8+6] & 0x1 )
|
||
|
d->readCapabilities |= MEDIA_WRITABLE_DVD_DL;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
// retry with pre-MMC5 length
|
||
|
cmd[8] = 8+4;
|
||
|
if( !cmd.transport( TR_DIR_READ, header, 12 ) ) {
|
||
|
unsigned int len = from4Byte( header );
|
||
|
if( len >= 8 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "DVD Read (pre-MMC5)" << endl;
|
||
|
d->readCapabilities |= MEDIA_DVD_ROM;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// DVD+R(W) writing features
|
||
|
//
|
||
|
cmd[2] = FEATURE_DVD_PLUS_R>>8;
|
||
|
cmd[3] = FEATURE_DVD_PLUS_R;
|
||
|
cmd[8] = 8+8;
|
||
|
if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
|
||
|
unsigned int len = from4Byte( header );
|
||
|
if( len >= 12 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "DVD+R" << endl;
|
||
|
d->readCapabilities |= MEDIA_DVD_PLUS_R;
|
||
|
if( header[12] & 0x1 )
|
||
|
d->writeCapabilities |= MEDIA_DVD_PLUS_R;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cmd[2] = FEATURE_DVD_PLUS_RW>>8;
|
||
|
cmd[3] = FEATURE_DVD_PLUS_RW;
|
||
|
cmd[8] = 8+8;
|
||
|
if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
|
||
|
unsigned int len = from4Byte( header );
|
||
|
if( len >= 12 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "DVD+RW" << endl;
|
||
|
#ifdef WORDS_BIGENDIAN
|
||
|
struct dvd_plus_rw_feature {
|
||
|
unsigned char reserved1 : 7;
|
||
|
unsigned char write : 1;
|
||
|
unsigned char reserved2 : 6;
|
||
|
unsigned char quick_start : 1;
|
||
|
unsigned char close_only : 1;
|
||
|
// and some stuff we do not use here...
|
||
|
};
|
||
|
#else
|
||
|
struct dvd_plus_rw_feature {
|
||
|
unsigned char write : 1;
|
||
|
unsigned char reserved1 : 7;
|
||
|
unsigned char close_only : 1;
|
||
|
unsigned char quick_start : 1;
|
||
|
unsigned char reserved2 : 6;
|
||
|
// and some stuff we do not use here...
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
struct dvd_plus_rw_feature* p = (struct dvd_plus_rw_feature*)&header[12];
|
||
|
d->readCapabilities |= MEDIA_DVD_PLUS_RW;
|
||
|
if( p->write )
|
||
|
d->writeCapabilities |= MEDIA_DVD_PLUS_RW;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// some older DVD-ROM drives claim to support DVD+R DL
|
||
|
if( d->writeCapabilities & MEDIA_DVD_PLUS_R ) {
|
||
|
cmd[2] = FEATURE_DVD_PLUS_RW_DUAL_LAYER>>8;
|
||
|
cmd[3] = FEATURE_DVD_PLUS_RW_DUAL_LAYER;
|
||
|
cmd[8] = 8+8;
|
||
|
if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
|
||
|
unsigned int len = from4Byte( header );
|
||
|
if( len >= 12 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "DVD+RW Double Layer" << endl;
|
||
|
d->readCapabilities |= MEDIA_DVD_PLUS_RW_DL;
|
||
|
if( header[12] & 0x1 )
|
||
|
d->writeCapabilities |= MEDIA_DVD_PLUS_RW_DL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cmd[2] = FEATURE_DVD_PLUS_R_DUAL_LAYER>>8;
|
||
|
cmd[3] = FEATURE_DVD_PLUS_R_DUAL_LAYER;
|
||
|
cmd[8] = 8+8;
|
||
|
if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
|
||
|
unsigned int len = from4Byte( header );
|
||
|
if( len >= 12 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "DVD+R Double Layer" << endl;
|
||
|
d->readCapabilities |= MEDIA_DVD_PLUS_R_DL;
|
||
|
if( header[12] & 0x1 )
|
||
|
d->writeCapabilities |= MEDIA_DVD_PLUS_R_DL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Blue Ray
|
||
|
//
|
||
|
// We do not care for the different BD classes and versions here
|
||
|
//
|
||
|
cmd[2] = FEATURE_BD_READ>>8;
|
||
|
cmd[3] = FEATURE_BD_READ;
|
||
|
cmd[8] = 8+32;
|
||
|
if( !cmd.transport( TR_DIR_READ, header, 40 ) ) {
|
||
|
unsigned int len = from4Byte( header );
|
||
|
if( len >= 36 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "BD Read" << endl;
|
||
|
if( header[8+8] || header[8+9] || header[8+10] || header[8+11] || header[8+12] || header[8+13] || header[8+14] || header[8+15] )
|
||
|
d->readCapabilities |= MEDIA_BD_RE;
|
||
|
if( header[8+16] || header[8+17] || header[8+18] || header[8+19] || header[8+20] || header[8+21] || header[8+22] || header[8+23] )
|
||
|
d->readCapabilities |= MEDIA_BD_R;
|
||
|
if( header[8+24] || header[8+25] || header[8+26] || header[8+27] || header[8+28] || header[8+29] || header[8+30] || header[8+31] )
|
||
|
d->readCapabilities |= MEDIA_BD_ROM;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cmd[2] = FEATURE_BD_WRITE>>8;
|
||
|
cmd[3] = FEATURE_BD_WRITE;
|
||
|
cmd[8] = 8+24;
|
||
|
if( !cmd.transport( TR_DIR_READ, header, 32 ) ) {
|
||
|
unsigned int len = from4Byte( header );
|
||
|
if( len >= 28 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "BD Write" << endl;
|
||
|
if( header[8+8] || header[8+9] || header[8+10] || header[8+11] || header[8+12] || header[8+13] || header[8+14] || header[8+15] )
|
||
|
d->writeCapabilities |= MEDIA_BD_RE;
|
||
|
if( header[8+16] || header[8+17] || header[8+18] || header[8+19] || header[8+20] || header[8+21] || header[8+22] || header[8+23] ) {
|
||
|
d->writeCapabilities |= MEDIA_BD_R;
|
||
|
m_writeModes |= WRITINGMODE_SRM;
|
||
|
|
||
|
cmd[2] = FEATURE_BD_PSEUDO_OVERWRITE>>8;
|
||
|
cmd[3] = FEATURE_BD_PSEUDO_OVERWRITE;
|
||
|
cmd[8] = 8+8;
|
||
|
if( !cmd.transport( TR_DIR_READ, header, 8+8 ) ) {
|
||
|
unsigned int len = from4Byte( header );
|
||
|
if( len >= 4+8 ) {
|
||
|
m_writeModes |= WRITINGMODE_SRM_POW;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cmd[2] = FEATURE_RANDOM_WRITABLE>>8;
|
||
|
cmd[3] = FEATURE_RANDOM_WRITABLE;
|
||
|
cmd[8] = 8+16;
|
||
|
if( !cmd.transport( TR_DIR_READ, header, 8+16 ) ) {
|
||
|
unsigned int len = from4Byte( header );
|
||
|
if( len >= 4+16 ) {
|
||
|
m_writeModes |= WRITINGMODE_RRM;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// DVD-R(W)
|
||
|
//
|
||
|
cmd[2] = FEATURE_DVD_R_RW_WRITE>>8;
|
||
|
cmd[3] = FEATURE_DVD_R_RW_WRITE;
|
||
|
cmd[8] = 16;
|
||
|
if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
|
||
|
unsigned int len = from4Byte( header );
|
||
|
if( len >= 12 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "DVD-R/-RW Write" << endl;
|
||
|
#ifdef WORDS_BIGENDIAN
|
||
|
struct dvd_r_rw_write_feature {
|
||
|
unsigned char reserved1 : 1;
|
||
|
unsigned char BUF : 1; // Burnfree
|
||
|
unsigned char reserved2 : 2;
|
||
|
unsigned char RDL : 1;
|
||
|
unsigned char testwrite : 1; // Simulation write support
|
||
|
unsigned char dvd_rw : 1; // DVD-RW Writing
|
||
|
unsigned char reserved3 : 1;
|
||
|
unsigned char reserved4[3];
|
||
|
};
|
||
|
#else
|
||
|
struct dvd_r_rw_write_feature {
|
||
|
unsigned char reserved3 : 1;
|
||
|
unsigned char dvd_rw : 1; // DVD-RW Writing
|
||
|
unsigned char testwrite : 1; // Simulation write support
|
||
|
unsigned char RDL : 1;
|
||
|
unsigned char reserved2 : 2;
|
||
|
unsigned char BUF : 1; // Burnfree
|
||
|
unsigned char reserved1 : 1;
|
||
|
unsigned char reserved4[3];
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
struct dvd_r_rw_write_feature* p = (struct dvd_r_rw_write_feature*)&header[12];
|
||
|
if( p->BUF ) d->burnfree = true;
|
||
|
d->writeCapabilities |= (MEDIA_DVD_R|MEDIA_DVD_R_SEQ);
|
||
|
if( p->dvd_rw )
|
||
|
d->writeCapabilities |= (MEDIA_DVD_RW|MEDIA_DVD_RW_SEQ);
|
||
|
if( p->RDL )
|
||
|
d->writeCapabilities |= (MEDIA_DVD_R_DL|MEDIA_DVD_R_DL_SEQ);
|
||
|
|
||
|
m_dvdMinusTestwrite = p->testwrite;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// DVD-RW restricted overwrite check
|
||
|
//
|
||
|
cmd[2] = FEATURE_RIGID_RESTRICTED_OVERWRITE>>8;
|
||
|
cmd[3] = FEATURE_RIGID_RESTRICTED_OVERWRITE;
|
||
|
cmd[8] = 16;
|
||
|
if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
|
||
|
unsigned int len = from4Byte( header );
|
||
|
if( len >= 12 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "Rigid Restricted Overwrite" << endl;
|
||
|
m_writeModes |= WRITINGMODE_RES_OVWR;
|
||
|
d->writeCapabilities |= (MEDIA_DVD_RW|MEDIA_DVD_RW_OVWR);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// DVD-R Dual Layer Layer
|
||
|
//
|
||
|
cmd[2] = FEATURE_LAYER_JUMP_RECORDING>>8;
|
||
|
cmd[3] = FEATURE_LAYER_JUMP_RECORDING;
|
||
|
cmd[8] = 12;
|
||
|
if( !cmd.transport( TR_DIR_READ, header, 12 ) ) {
|
||
|
// Now the jump feature is longer than 4 bytes but we don't need the link sizes.
|
||
|
unsigned int len = from4Byte( header );
|
||
|
if( len >= 8 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "Layer Jump Recording" << endl;
|
||
|
d->writeCapabilities |= (MEDIA_DVD_R_DL|MEDIA_DVD_R_DL_JUMP);
|
||
|
m_writeModes |= WRITINGMODE_LAYER_JUMP;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// HD-DVD-ROM
|
||
|
//
|
||
|
cmd[2] = FEATURE_HD_DVD_READ>>8;
|
||
|
cmd[3] = FEATURE_HD_DVD_READ;
|
||
|
cmd[8] = 16;
|
||
|
if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
|
||
|
unsigned int len = from4Byte( header );
|
||
|
if( len >= 12 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "HD-DVD Read" << endl;
|
||
|
d->readCapabilities |= MEDIA_HD_DVD_ROM;
|
||
|
if( header[8+4] & 0x1 )
|
||
|
d->readCapabilities |= MEDIA_HD_DVD_R;
|
||
|
if( header[8+6] & 0x1 )
|
||
|
d->readCapabilities |= MEDIA_HD_DVD_RAM;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// HD-DVD-R(AM)
|
||
|
//
|
||
|
cmd[2] = FEATURE_HD_DVD_WRITE>>8;
|
||
|
cmd[3] = FEATURE_HD_DVD_WRITE;
|
||
|
cmd[8] = 16;
|
||
|
if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
|
||
|
unsigned int len = from4Byte( header );
|
||
|
if( len >= 12 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " feature: " << "HD-DVD Write" << endl;
|
||
|
if( header[8+4] & 0x1 )
|
||
|
d->writeCapabilities |= MEDIA_HD_DVD_R;
|
||
|
if( header[8+6] & 0x1 )
|
||
|
d->writeCapabilities |= MEDIA_HD_DVD_RAM;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Get the profiles
|
||
|
//
|
||
|
// the max len of the returned data is 8 (header) + 4 (feature) + 255 (additional length)
|
||
|
//
|
||
|
cmd[2] = FEATURE_PROFILE_LIST>>8;
|
||
|
cmd[3] = FEATURE_PROFILE_LIST;
|
||
|
cmd[8] = 12; // get the number of returned profiles first
|
||
|
if( !cmd.transport( TR_DIR_READ, header, 12 ) ) {
|
||
|
unsigned int len = from4Byte( header ) + 4;
|
||
|
if( len >= 12 ) {
|
||
|
cmd[7] = len>>8;
|
||
|
cmd[8] = len;
|
||
|
if( !cmd.transport( TR_DIR_READ, header, len ) ) {
|
||
|
int featureLen( header[11] );
|
||
|
for( int j = 0; j < featureLen; j+=4 ) {
|
||
|
short profile = from2Byte( &header[12+j] );
|
||
|
|
||
|
switch (profile) {
|
||
|
case 0x08:
|
||
|
d->supportedProfiles |= MEDIA_CD_ROM;
|
||
|
break;
|
||
|
case 0x09:
|
||
|
d->supportedProfiles |= MEDIA_CD_R;
|
||
|
break;
|
||
|
case 0x0A:
|
||
|
d->supportedProfiles |= MEDIA_CD_RW;
|
||
|
break;
|
||
|
case 0x10:
|
||
|
d->supportedProfiles |= MEDIA_DVD_ROM;
|
||
|
// d->readCapabilities |= MEDIA_DVD_ROM;
|
||
|
break;
|
||
|
case 0x11:
|
||
|
d->supportedProfiles |= MEDIA_DVD_R_SEQ;
|
||
|
// d->writeCapabilities |= (MEDIA_DVD_R|MEDIA_DVD_R_SEQ);
|
||
|
break;
|
||
|
case 0x12:
|
||
|
d->supportedProfiles |= MEDIA_DVD_RAM;
|
||
|
// d->readCapabilities |= (MEDIA_DVD_RAM|MEDIA_DVD_ROM);
|
||
|
// d->writeCapabilities |= MEDIA_DVD_RAM;
|
||
|
break;
|
||
|
case 0x13:
|
||
|
d->supportedProfiles |= MEDIA_DVD_RW_OVWR;
|
||
|
// d->writeCapabilities |= (MEDIA_DVD_RW|MEDIA_DVD_RW_OVWR);
|
||
|
break;
|
||
|
case 0x14:
|
||
|
d->supportedProfiles |= MEDIA_DVD_RW_SEQ;
|
||
|
// d->writeCapabilities |= (MEDIA_DVD_RW|MEDIA_DVD_R|MEDIA_DVD_RW_SEQ|MEDIA_DVD_R_SEQ);
|
||
|
break;
|
||
|
case 0x15:
|
||
|
d->supportedProfiles |= MEDIA_DVD_R_DL_SEQ;
|
||
|
// d->writeCapabilities |= (MEDIA_DVD_R|MEDIA_DVD_R_DL|MEDIA_DVD_R_SEQ|MEDIA_DVD_R_DL_SEQ);
|
||
|
break;
|
||
|
case 0x16:
|
||
|
d->supportedProfiles |= MEDIA_DVD_R_DL_JUMP;
|
||
|
// d->writeCapabilities |= (MEDIA_DVD_R|MEDIA_DVD_R_DL||MEDIA_DVD_R_DL_JUMP);
|
||
|
break;
|
||
|
case 0x1A:
|
||
|
d->supportedProfiles |= MEDIA_DVD_PLUS_RW;
|
||
|
// d->writeCapabilities |= MEDIA_DVD_PLUS_RW;
|
||
|
break;
|
||
|
case 0x1B:
|
||
|
d->supportedProfiles |= MEDIA_DVD_PLUS_R;
|
||
|
// d->writeCapabilities |= MEDIA_DVD_PLUS_R;
|
||
|
break;
|
||
|
case 0x2A:
|
||
|
d->supportedProfiles |= MEDIA_DVD_PLUS_RW_DL;
|
||
|
// d->writeCapabilities |= MEDIA_DVD_PLUS_RW_DL;
|
||
|
break;
|
||
|
case 0x2B:
|
||
|
d->supportedProfiles |= MEDIA_DVD_PLUS_R_DL;
|
||
|
// d->writeCapabilities |= MEDIA_DVD_PLUS_R_DL;
|
||
|
break;
|
||
|
case 0x40:
|
||
|
d->supportedProfiles |= MEDIA_BD_ROM;
|
||
|
break;
|
||
|
case 0x41:
|
||
|
d->supportedProfiles |= MEDIA_BD_R_SRM;
|
||
|
break;
|
||
|
case 0x42:
|
||
|
d->supportedProfiles |= MEDIA_BD_R_RRM;
|
||
|
break;
|
||
|
case 0x43:
|
||
|
d->supportedProfiles |= MEDIA_BD_RE;
|
||
|
break;
|
||
|
case 0x50:
|
||
|
d->supportedProfiles |= MEDIA_HD_DVD_ROM;
|
||
|
break;
|
||
|
case 0x51:
|
||
|
d->supportedProfiles |= MEDIA_HD_DVD_R;
|
||
|
break;
|
||
|
case 0x52:
|
||
|
d->supportedProfiles |= MEDIA_HD_DVD_RAM;
|
||
|
break;
|
||
|
default:
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << " unknown profile: "
|
||
|
<< profile << endl;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// some older DVD-ROM drives claim to support DVD+R DL
|
||
|
if( !(d->supportedProfiles & MEDIA_DVD_PLUS_R) ) {
|
||
|
// remove DVD+R DL capability
|
||
|
// d->writeCapabilities &= ~MEDIA_DVD_PLUS_R_DL;
|
||
|
d->supportedProfiles &= ~MEDIA_DVD_PLUS_R_DL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void K3bDevice::Device::checkFor2AFeatures()
|
||
|
{
|
||
|
unsigned char* mm_cap_buffer = 0;
|
||
|
unsigned int mm_cap_len = 0;
|
||
|
|
||
|
if( modeSense( &mm_cap_buffer, mm_cap_len, 0x2A ) ) {
|
||
|
mm_cap_page_2A* mm_p = (mm_cap_page_2A*)(mm_cap_buffer+8);
|
||
|
if( mm_p->BUF )
|
||
|
d->burnfree = true;
|
||
|
|
||
|
if( mm_p->cd_r_write )
|
||
|
d->writeCapabilities |= MEDIA_CD_R;
|
||
|
else
|
||
|
d->writeCapabilities &= ~MEDIA_CD_R;
|
||
|
|
||
|
if( mm_p->cd_rw_write )
|
||
|
d->writeCapabilities |= MEDIA_CD_RW;
|
||
|
else
|
||
|
d->writeCapabilities &= ~MEDIA_CD_RW;
|
||
|
|
||
|
if( mm_p->dvd_r_write )
|
||
|
d->writeCapabilities |= MEDIA_DVD_R;
|
||
|
else
|
||
|
d->writeCapabilities &= ~MEDIA_DVD_R;
|
||
|
|
||
|
if( mm_p->dvd_rom_read || mm_p->dvd_r_read )
|
||
|
d->readCapabilities |= MEDIA_DVD_ROM;
|
||
|
|
||
|
m_maxReadSpeed = from2Byte(mm_p->max_read_speed);
|
||
|
m_bufferSize = from2Byte( mm_p->buffer_size );
|
||
|
|
||
|
delete [] mm_cap_buffer;
|
||
|
}
|
||
|
else {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": read mode page 2A failed!" << endl;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void K3bDevice::Device::checkWritingModes()
|
||
|
{
|
||
|
// if the device is already opened we do not close it
|
||
|
// to allow fast multiple method calls in a row
|
||
|
bool needToClose = !isOpen();
|
||
|
|
||
|
if( !open() )
|
||
|
return;
|
||
|
|
||
|
// header size is 8
|
||
|
unsigned char* buffer = 0;
|
||
|
unsigned int dataLen = 0;
|
||
|
|
||
|
if( !modeSense( &buffer, dataLen, 0x05 ) ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": modeSense 0x05 failed!" << endl
|
||
|
<< "(K3bDevice::Device) " << blockDeviceName() << ": Cannot check write modes." << endl;
|
||
|
}
|
||
|
else if( dataLen < 18 ) { // 8 bytes header + 10 bytes used modepage
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": Missing modepage 0x05 data." << endl
|
||
|
<< "(K3bDevice::Device) " << blockDeviceName() << ": Cannot check write modes." << endl;
|
||
|
}
|
||
|
else {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": dataLen: " << dataLen << endl;
|
||
|
|
||
|
wr_param_page_05* mp = (struct wr_param_page_05*)(buffer+8);
|
||
|
|
||
|
// reset some stuff to be on the safe side
|
||
|
mp->PS = 0;
|
||
|
mp->BUFE = 0;
|
||
|
mp->multi_session = 0;
|
||
|
mp->test_write = 0;
|
||
|
mp->LS_V = 0;
|
||
|
mp->copy = 0;
|
||
|
mp->fp = 0;
|
||
|
mp->host_appl_code= 0;
|
||
|
mp->session_format = 0;
|
||
|
mp->audio_pause_len[0] = 0;
|
||
|
mp->audio_pause_len[1] = 150;
|
||
|
|
||
|
// WRITINGMODE_TAO
|
||
|
mp->write_type = 0x01; // Track-at-once
|
||
|
mp->track_mode = 4; // MMC-4 says: 5, cdrecord uses 4 ?
|
||
|
mp->dbtype = 8; // Mode 1
|
||
|
|
||
|
// k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": modeselect WRITINGMODE_TAO data: " << endl;
|
||
|
// debugBitfield( buffer, dataLen );
|
||
|
|
||
|
|
||
|
//
|
||
|
// if a writer does not support WRITINGMODE_TAO it surely does not support WRITINGMODE_SAO or WRITINGMODE_RAW writing since WRITINGMODE_TAO is the minimal
|
||
|
// requirement
|
||
|
//
|
||
|
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": checking for TAO" << endl;
|
||
|
if( modeSelect( buffer, dataLen, 1, 0 ) ) {
|
||
|
m_writeModes |= WRITINGMODE_TAO;
|
||
|
d->writeCapabilities |= MEDIA_CD_R;
|
||
|
|
||
|
// WRITINGMODE_SAO
|
||
|
mp->write_type = 0x02; // Session-at-once
|
||
|
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": checking for SAO" << endl;
|
||
|
if( modeSelect( buffer, dataLen, 1, 0 ) )
|
||
|
m_writeModes |= WRITINGMODE_SAO;
|
||
|
|
||
|
// mp->dbtype = 1; // Raw data with P and Q Sub-channel (2368 bytes)
|
||
|
// if( modeSelect( buffer, dataLen, 1, 0 ) ) {
|
||
|
// m_writeModes |= WRITINGMODE_RAW_R16;
|
||
|
// }
|
||
|
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": checking for SAO_R96P" << endl;
|
||
|
mp->dbtype = 2; // Raw data with P-W Sub-channel (2448 bytes)
|
||
|
if( modeSelect( buffer, dataLen, 1, 0 ) ) {
|
||
|
m_writeModes |= WRITINGMODE_SAO_R96P;
|
||
|
}
|
||
|
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": checking for SAO_R96R" << endl;
|
||
|
mp->dbtype = 3; // Raw data with P-W raw Sub-channel (2448 bytes)
|
||
|
if( modeSelect( buffer, dataLen, 1, 0 ) ) {
|
||
|
m_writeModes |= WRITINGMODE_SAO_R96R;
|
||
|
}
|
||
|
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": checking for RAW_R16" << endl;
|
||
|
// WRITINGMODE_RAW
|
||
|
mp->write_type = 0x03; // WRITINGMODE_RAW
|
||
|
mp->dbtype = 1; // Raw data with P and Q Sub-channel (2368 bytes)
|
||
|
if( modeSelect( buffer, dataLen, 1, 0 ) ) {
|
||
|
m_writeModes |= WRITINGMODE_RAW;
|
||
|
m_writeModes |= WRITINGMODE_RAW_R16;
|
||
|
}
|
||
|
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": checking for RAW_R96P" << endl;
|
||
|
mp->dbtype = 2; // Raw data with P-W Sub-channel (2448 bytes)
|
||
|
if( modeSelect( buffer, dataLen, 1, 0 ) ) {
|
||
|
m_writeModes |= WRITINGMODE_RAW;
|
||
|
m_writeModes |= WRITINGMODE_RAW_R96P;
|
||
|
}
|
||
|
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": checking for RAW_R96R" << endl;
|
||
|
mp->dbtype = 3; // Raw data with P-W raw Sub-channel (2448 bytes)
|
||
|
if( modeSelect( buffer, dataLen, 1, 0 ) ) {
|
||
|
m_writeModes |= WRITINGMODE_RAW;
|
||
|
m_writeModes |= WRITINGMODE_RAW_R96R;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName() << ": modeSelect with WRITINGMODE_TAO failed. No writer" << endl;
|
||
|
}
|
||
|
|
||
|
|
||
|
delete [] buffer;
|
||
|
}
|
||
|
|
||
|
if( needToClose )
|
||
|
close();
|
||
|
}
|
||
|
|
||
|
|
||
|
int K3bDevice::Device::determineMaximalWriteSpeed() const
|
||
|
{
|
||
|
int ret = 0;
|
||
|
unsigned char* data = 0;
|
||
|
unsigned int dataLen = 0;
|
||
|
|
||
|
if( mediaType() & MEDIA_CD_ALL ) {
|
||
|
if( modeSense( &data, dataLen, 0x2A ) ) {
|
||
|
mm_cap_page_2A* mm = (mm_cap_page_2A*)&data[8];
|
||
|
|
||
|
// MMC1 used byte 18 and 19 for the max write speed
|
||
|
if( dataLen > 19 )
|
||
|
ret = from2Byte( mm->max_write_speed );
|
||
|
|
||
|
delete [] data;
|
||
|
|
||
|
if( ret > 0 )
|
||
|
return ret;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QValueList<int> list = determineSupportedWriteSpeeds();
|
||
|
if( !list.isEmpty() ) {
|
||
|
for( QValueList<int>::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
|
||
|
ret = QMAX( ret, *it );
|
||
|
}
|
||
|
|
||
|
if( ret > 0 )
|
||
|
return ret;
|
||
|
else
|
||
|
return m_maxWriteSpeed;
|
||
|
}
|
||
|
|
||
|
|
||
|
QValueList<int> K3bDevice::Device::determineSupportedWriteSpeeds() const
|
||
|
{
|
||
|
QValueList<int> ret;
|
||
|
|
||
|
if( burner() ) {
|
||
|
//
|
||
|
// Tests with all my drives resulted in 2A for CD and GET PERFORMANCE for DVD media
|
||
|
// as the valid method of speed detection.
|
||
|
//
|
||
|
if( mediaType() & MEDIA_CD_ALL ) {
|
||
|
if( !getSupportedWriteSpeedsVia2A( ret, false ) )
|
||
|
getSupportedWriteSpeedsViaGP( ret, false );
|
||
|
|
||
|
// restrict to max speed, although deprecated in MMC3 is still used everywhere and
|
||
|
// cdrecord also uses it as the max writing speed.
|
||
|
int max = 0;
|
||
|
unsigned char* data = 0;
|
||
|
unsigned int dataLen = 0;
|
||
|
if( modeSense( &data, dataLen, 0x2A ) ) {
|
||
|
mm_cap_page_2A* mm = (mm_cap_page_2A*)&data[8];
|
||
|
|
||
|
// MMC1 used byte 18 and 19 for the max write speed
|
||
|
if( dataLen > 19 )
|
||
|
max = from2Byte( mm->max_write_speed );
|
||
|
|
||
|
delete [] data;
|
||
|
|
||
|
if( max > 0 ) {
|
||
|
while( !ret.isEmpty() && ret.last() > max ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
|
||
|
<< " writing speed " << ret.last() << " higher than max " << max << endl;
|
||
|
ret.pop_back();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if( !getSupportedWriteSpeedsViaGP( ret, true ) )
|
||
|
getSupportedWriteSpeedsVia2A( ret, true );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::getSupportedWriteSpeedsVia2A( QValueList<int>& list, bool dvd ) const
|
||
|
{
|
||
|
unsigned char* data = 0;
|
||
|
unsigned int dataLen = 0;
|
||
|
if( modeSense( &data, dataLen, 0x2A ) ) {
|
||
|
mm_cap_page_2A* mm = (mm_cap_page_2A*)&data[8];
|
||
|
|
||
|
if( dataLen > 32 ) {
|
||
|
// we have descriptors
|
||
|
unsigned int numDesc = from2Byte( mm->num_wr_speed_des );
|
||
|
|
||
|
// Some CDs writer returns the number of bytes that contain
|
||
|
// the descriptors rather than the number of descriptors
|
||
|
// Ensure number of descriptors claimed actually fits in the data
|
||
|
// returned by the mode sense command.
|
||
|
if( numDesc > ((dataLen - 32 - 8) / 4) )
|
||
|
numDesc = (dataLen - 32 - 8) / 4;
|
||
|
|
||
|
cd_wr_speed_performance* wr = (cd_wr_speed_performance*)mm->wr_speed_des;
|
||
|
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
|
||
|
<< ": Number of supported write speeds via 2A: "
|
||
|
<< numDesc << endl;
|
||
|
|
||
|
|
||
|
for( unsigned int i = 0; i < numDesc; ++i ) {
|
||
|
int s = (int)from2Byte( wr[i].wr_speed_supp );
|
||
|
//
|
||
|
// some DVD writers report CD writing speeds here
|
||
|
// If that is the case we cannot rely on the reported speeds
|
||
|
// and need to use the values gained from GET PERFORMANCE.
|
||
|
//
|
||
|
if( dvd && s < 1352 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
|
||
|
<< " Invalid DVD speed: " << s << " KB/s" << endl;
|
||
|
list.clear();
|
||
|
break;
|
||
|
}
|
||
|
else {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
|
||
|
<< " : " << s << " KB/s" << endl;
|
||
|
|
||
|
if( dvd )
|
||
|
s = fixupDvdWritingSpeed( s );
|
||
|
|
||
|
// sort the list
|
||
|
QValueList<int>::iterator it = list.begin();
|
||
|
while( it != list.end() && *it < s )
|
||
|
++it;
|
||
|
list.insert( it, s );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
delete [] data;
|
||
|
}
|
||
|
|
||
|
return !list.isEmpty();
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::getSupportedWriteSpeedsViaGP( QValueList<int>& list, bool dvd ) const
|
||
|
{
|
||
|
unsigned char* data = 0;
|
||
|
unsigned int dataLen = 0;
|
||
|
if( getPerformance( &data, dataLen, 0x3, 0x0 ) ) {
|
||
|
int numDesc = (dataLen - 8)/16;
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
|
||
|
<< ": Number of supported write speeds via GET PERFORMANCE: "
|
||
|
<< numDesc << endl;
|
||
|
|
||
|
for( int i = 0; i < numDesc; ++i ) {
|
||
|
int s = from4Byte( &data[20+i*16] );
|
||
|
|
||
|
// Looks as if the code below does not make sense with most drives
|
||
|
// if( !( data[4+i*16] & 0x2 ) ) {
|
||
|
// k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
|
||
|
// << " No write speed: " << s << " KB/s" << endl;
|
||
|
// continue;
|
||
|
// }
|
||
|
|
||
|
if( dvd && s < 1352 ) {
|
||
|
//
|
||
|
// Does this ever happen?
|
||
|
//
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
|
||
|
<< " Invalid DVD speed: " << s << " KB/s" << endl;
|
||
|
}
|
||
|
else {
|
||
|
k3bDebug() << "(K3bDevice::Device) " << blockDeviceName()
|
||
|
<< " : " << s << " KB/s" << endl;
|
||
|
|
||
|
if( dvd )
|
||
|
s = fixupDvdWritingSpeed( s );
|
||
|
|
||
|
QValueList<int>::iterator it = list.begin();
|
||
|
while( it != list.end() && *it < s )
|
||
|
++it;
|
||
|
// the speed might already have been found in the 2a modepage
|
||
|
if( it == list.end() || *it != s )
|
||
|
list.insert( it, s );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
delete [] data;
|
||
|
}
|
||
|
|
||
|
return !list.isEmpty();
|
||
|
}
|
||
|
|
||
|
|
||
|
int K3bDevice::Device::getIndex( unsigned long lba ) const
|
||
|
{
|
||
|
// if the device is already opened we do not close it
|
||
|
// to allow fast multiple method calls in a row
|
||
|
bool needToClose = !isOpen();
|
||
|
|
||
|
if( !open() )
|
||
|
return -1;
|
||
|
|
||
|
int ret = -1;
|
||
|
|
||
|
//
|
||
|
// first try readCd
|
||
|
//
|
||
|
unsigned char readData[16];
|
||
|
::memset( readData, 0, 16 );
|
||
|
|
||
|
//
|
||
|
// The index is found in the Mode-1 Q which occupies at least 9 out of 10 successive CD frames
|
||
|
// It can be indentified by ADR == 1
|
||
|
//
|
||
|
// So if the current sector does not provide Mode-1 Q subchannel we try the previous.
|
||
|
//
|
||
|
|
||
|
if( readCd( readData,
|
||
|
16,
|
||
|
1, // CD-DA
|
||
|
0, // no DAP
|
||
|
lba,
|
||
|
1,
|
||
|
false,
|
||
|
false,
|
||
|
false,
|
||
|
false,
|
||
|
false,
|
||
|
0,
|
||
|
2 // Q-Subchannel
|
||
|
) ) {
|
||
|
// byte 0: 4 bits CONTROL (MSB) + 4 bits ADR (LSB)
|
||
|
if( (readData[0]&0x0f) == 0x1 )
|
||
|
ret = readData[2];
|
||
|
|
||
|
// search previous sector for Mode1 Q Subchannel
|
||
|
else if( readCd( readData,
|
||
|
16,
|
||
|
1, // CD-DA
|
||
|
0, // no DAP
|
||
|
lba-1,
|
||
|
1,
|
||
|
false,
|
||
|
false,
|
||
|
false,
|
||
|
false,
|
||
|
false,
|
||
|
0,
|
||
|
2 // Q-Subchannel
|
||
|
) ) {
|
||
|
if( (readData[0]&0x0f) == 0x1 )
|
||
|
ret = readData[2];
|
||
|
else
|
||
|
ret = -2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
else {
|
||
|
k3bDebug() << "(K3bDevice::Device::getIndex) readCd failed. Trying seek." << endl;
|
||
|
|
||
|
unsigned char* data = 0;
|
||
|
unsigned int dataLen = 0;
|
||
|
if( seek( lba ) && readSubChannel( &data, dataLen, 1, 0 ) ) {
|
||
|
// byte 5: 4 bits ADR (MSB) + 4 bits CONTROL (LSB)
|
||
|
if( dataLen > 7 && (data[5]>>4 & 0x0F) == 0x1 ) {
|
||
|
ret = data[7];
|
||
|
}
|
||
|
else if( seek( lba-1 ) && readSubChannel( &data, dataLen, 1, 0 ) ) {
|
||
|
if( dataLen > 7 && (data[5]>>4 & 0x0F) == 0x1 )
|
||
|
ret = data[7];
|
||
|
else
|
||
|
ret = -2;
|
||
|
}
|
||
|
|
||
|
delete [] data;
|
||
|
}
|
||
|
else
|
||
|
k3bDebug() << "(K3bDevice::Device::getIndex) seek or readSubChannel failed." << endl;
|
||
|
}
|
||
|
|
||
|
if( needToClose )
|
||
|
close();
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::searchIndex0( unsigned long startSec,
|
||
|
unsigned long endSec,
|
||
|
long& pregapStart ) const
|
||
|
{
|
||
|
// if the device is already opened we do not close it
|
||
|
// to allow fast multiple method calls in a row
|
||
|
bool needToClose = !isOpen();
|
||
|
|
||
|
if( !open() )
|
||
|
return false;
|
||
|
|
||
|
bool ret = false;
|
||
|
|
||
|
int lastIndex = getIndex( endSec );
|
||
|
if( lastIndex == 0 ) {
|
||
|
// there is a pregap
|
||
|
// let's find the position where the index turns to 0
|
||
|
// we jump in 1 sec steps backwards until we find an index > 0
|
||
|
unsigned long sector = endSec;
|
||
|
while( lastIndex == 0 && sector > startSec ) {
|
||
|
sector -= 75;
|
||
|
if( sector < startSec )
|
||
|
sector = startSec;
|
||
|
lastIndex = getIndex(sector);
|
||
|
}
|
||
|
|
||
|
if( lastIndex == 0 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) warning: no index != 0 found." << endl;
|
||
|
}
|
||
|
else {
|
||
|
// search forward to the first index = 0
|
||
|
while( getIndex( sector ) != 0 && sector < endSec )
|
||
|
sector++;
|
||
|
|
||
|
pregapStart = sector;
|
||
|
ret = true;
|
||
|
}
|
||
|
}
|
||
|
else if( lastIndex > 0 ) {
|
||
|
// no pregap
|
||
|
pregapStart = -1;
|
||
|
ret = true;
|
||
|
}
|
||
|
|
||
|
if( needToClose )
|
||
|
close();
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::indexScan( K3bDevice::Toc& toc ) const
|
||
|
{
|
||
|
// if the device is already opened we do not close it
|
||
|
// to allow fast multiple method calls in a row
|
||
|
bool needToClose = !isOpen();
|
||
|
|
||
|
if( !open() )
|
||
|
return false;
|
||
|
|
||
|
bool ret = true;
|
||
|
|
||
|
for( Toc::iterator it = toc.begin(); it != toc.end(); ++it ) {
|
||
|
Track& track = *it;
|
||
|
if( track.type() == Track::AUDIO ) {
|
||
|
track.m_indices.clear();
|
||
|
long index0 = -1;
|
||
|
if( searchIndex0( track.firstSector().lba(), track.lastSector().lba(), index0 ) ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) found index 0: " << index0 << endl;
|
||
|
}
|
||
|
if( index0 > 0 )
|
||
|
track.m_index0 = K3b::Msf( index0 - track.firstSector().lba() );
|
||
|
else
|
||
|
track.m_index0 = 0;
|
||
|
|
||
|
if( index0 > 0 )
|
||
|
searchIndexTransitions( track.firstSector().lba(), index0-1, track );
|
||
|
else
|
||
|
searchIndexTransitions( track.firstSector().lba(), track.lastSector().lba(), track );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( needToClose )
|
||
|
close();
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
void K3bDevice::Device::searchIndexTransitions( long start, long end, K3bDevice::Track& track ) const
|
||
|
{
|
||
|
k3bDebug() << "(K3bDevice::Device) searching for index transitions between "
|
||
|
<< start << " and " << end << endl;
|
||
|
int startIndex = getIndex( start );
|
||
|
int endIndex = getIndex( end );
|
||
|
|
||
|
if( startIndex < 0 || endIndex < 0 ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) could not retrieve index values." << endl;
|
||
|
}
|
||
|
else {
|
||
|
k3bDebug() << "(K3bDevice::Device) indices: " << start << " - " << startIndex
|
||
|
<< " and " << end << " - " << endIndex << endl;
|
||
|
|
||
|
if( startIndex != endIndex ) {
|
||
|
if( start+1 == end ) {
|
||
|
k3bDebug() << "(K3bDevice::Device) found index transition: " << endIndex << " " << end << endl;
|
||
|
track.m_indices.resize( endIndex );
|
||
|
// we save the index relative to the first sector
|
||
|
track.m_indices[endIndex-1] = K3b::Msf( end ) - track.firstSector();
|
||
|
}
|
||
|
else {
|
||
|
searchIndexTransitions( start, start+(end-start)/2, track );
|
||
|
searchIndexTransitions( start+(end-start)/2, end, track );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
int K3bDevice::Device::copyrightProtectionSystemType() const
|
||
|
{
|
||
|
unsigned char* dvdheader = 0;
|
||
|
unsigned int dataLen = 0;
|
||
|
if( readDvdStructure( &dvdheader, dataLen, 0x1 ) ) {
|
||
|
int ret = -1;
|
||
|
if( dataLen >= 6 )
|
||
|
ret = dvdheader[4];
|
||
|
delete [] dvdheader;
|
||
|
return ret;
|
||
|
}
|
||
|
else
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool K3bDevice::Device::getNextWritableAdress( unsigned int& lastSessionStart, unsigned int& nextWritableAdress ) const
|
||
|
{
|
||
|
bool success = false;
|
||
|
|
||
|
// FIXME: add CD media handling
|
||
|
int m = mediaType();
|
||
|
if( m & MEDIA_DVD_ALL ) {
|
||
|
// DVD+RW always returns complete
|
||
|
if( m & (K3bDevice::MEDIA_DVD_PLUS_RW|K3bDevice::MEDIA_DVD_RW_OVWR) )
|
||
|
return false;
|
||
|
|
||
|
unsigned char* data = 0;
|
||
|
unsigned int dataLen = 0;
|
||
|
|
||
|
if( readDiscInformation( &data, dataLen ) ) {
|
||
|
disc_info_t* inf = (disc_info_t*)data;
|
||
|
|
||
|
//
|
||
|
// The state of the last session has to be "empty" (0x0) or "incomplete" (0x1)
|
||
|
// The procedure here is taken from the dvd+rw-tools
|
||
|
//
|
||
|
if( !(inf->border & 0x2) ) {
|
||
|
// the incomplete track number is the first track in the last session (the empty session)
|
||
|
int nextTrack = inf->first_track_l|inf->first_track_m<<8;
|
||
|
|
||
|
unsigned char* trackData = 0;
|
||
|
unsigned int trackDataLen = 0;
|
||
|
|
||
|
// Read start address of the incomplete track
|
||
|
if( readTrackInformation( &trackData, trackDataLen, 0x1, nextTrack ) ) {
|
||
|
nextWritableAdress = from4Byte( &trackData[8] );
|
||
|
delete [] trackData;
|
||
|
|
||
|
// Read start address of the first track in the last session
|
||
|
if( readTocPmaAtip( &trackData, trackDataLen, 0x1, false, 0x0 ) ) {
|
||
|
lastSessionStart = from4Byte( &trackData[8] );
|
||
|
delete [] trackData;
|
||
|
success = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
delete [] data;
|
||
|
}
|
||
|
|
||
|
return success;
|
||
|
}
|
||
|
|
||
|
|
||
|
int K3bDevice::Device::nextWritableAddress() const
|
||
|
{
|
||
|
unsigned char* data = 0;
|
||
|
unsigned int dataLen = 0;
|
||
|
int nwa = -1;
|
||
|
|
||
|
if( readDiscInformation( &data, dataLen ) ) {
|
||
|
disc_info_t* inf = (disc_info_t*)data;
|
||
|
|
||
|
//
|
||
|
// The state of the last session has to be "empty" (0x0) or "incomplete" (0x1)
|
||
|
// The procedure here is taken from the dvd+rw-tools and wodim
|
||
|
//
|
||
|
if( !(inf->border & 0x2) ) {
|
||
|
// the incomplete track number is the first track in the last session (the empty session)
|
||
|
int nextTrack = inf->first_track_l|inf->first_track_m<<8;
|
||
|
|
||
|
unsigned char* trackData = 0;
|
||
|
unsigned int trackDataLen = 0;
|
||
|
|
||
|
// Read start address of the incomplete track
|
||
|
if( readTrackInformation( &trackData, trackDataLen, 0x1, nextTrack ) ) {
|
||
|
nwa = from4Byte( &trackData[8] );
|
||
|
delete [] trackData;
|
||
|
}
|
||
|
|
||
|
// Read start address of the invisible track
|
||
|
else if ( readTrackInformation( &trackData, trackDataLen, 0x1, 0xff ) ) {
|
||
|
nwa = from4Byte( &trackData[8] );
|
||
|
delete [] trackData;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
delete [] data;
|
||
|
}
|
||
|
|
||
|
return nwa;
|
||
|
}
|
||
|
|
||
|
|
||
|
QCString K3bDevice::Device::mediaId( int mediaType ) const
|
||
|
{
|
||
|
QCString id;
|
||
|
|
||
|
if( mediaType & MEDIA_CD_ALL ) {
|
||
|
// FIXME:
|
||
|
}
|
||
|
|
||
|
else if( mediaType & MEDIA_DVD_MINUS_ALL ) {
|
||
|
unsigned char* data = 0;
|
||
|
unsigned int dataLen = 0;
|
||
|
if( readDvdStructure( &data, dataLen, 0x0E ) ) {
|
||
|
if( data[4+16] == 3 && data[4+24] == 4 ) {
|
||
|
id.sprintf( "%6.6s%-6.6s", data+4+17, data+4+25 );
|
||
|
}
|
||
|
delete [] data;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
else if( mediaType & MEDIA_DVD_PLUS_ALL ) {
|
||
|
unsigned char* data = 0;
|
||
|
unsigned int dataLen = 0;
|
||
|
if( readDvdStructure( &data, dataLen, 0x11 ) ||
|
||
|
readDvdStructure( &data, dataLen, 0x0 ) ) {
|
||
|
id.sprintf( "%8.8s/%3.3s", data+23, data+31 );
|
||
|
delete [] data;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
else if( mediaType & MEDIA_BD_ALL ) {
|
||
|
unsigned char* data = 0;
|
||
|
unsigned int dataLen = 0;
|
||
|
if( readDiscStructure( &data, dataLen, 1, 0 ) ) {
|
||
|
if( data[4+0] == 'D' && data[4+1] == 'I' )
|
||
|
id.sprintf ("%6.6s/%-3.3s", data+4+100, data+4+106 );
|
||
|
delete [] data;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return id;
|
||
|
}
|
||
|
|
||
|
|
||
|
// int K3bDevice::Device::ioctl( int request, ... ) const
|
||
|
// {
|
||
|
// int r = -1;
|
||
|
// #if defined(Q_OS_LINUX) || defined(Q_OS_NETBSD)
|
||
|
// d->mutex.lock();
|
||
|
|
||
|
// va_list ap;
|
||
|
// va_start( ap, request );
|
||
|
// r = ::ioctl( d->deviceFd, request, ap );
|
||
|
// va_end( ap );
|
||
|
|
||
|
// d->mutex.unlock();
|
||
|
// #endif
|
||
|
// return r;
|
||
|
// }
|
||
|
|
||
|
|
||
|
void K3bDevice::Device::usageLock() const
|
||
|
{
|
||
|
d->mutex.lock();
|
||
|
}
|
||
|
|
||
|
|
||
|
void K3bDevice::Device::usageUnlock() const
|
||
|
{
|
||
|
d->mutex.unlock();
|
||
|
}
|