File indexing completed on 2024-04-21 04:49:41

0001 /*
0002     SPDX-FileCopyrightText: 1998-2009 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 #include "k3bdevice.h"
0006 #include "k3bdeviceglobals.h"
0007 #include "k3btrack.h"
0008 #include "k3btoc.h"
0009 #include "k3bdiskinfo.h"
0010 #include "k3bdiskinfo_p.h"
0011 #include "k3bmmc.h"
0012 #include "k3bscsicommand.h"
0013 #include "k3bcrc.h"
0014 
0015 #include "config-k3b.h"
0016 
0017 #include <Solid/Device>
0018 #include <Solid/OpticalDrive>
0019 #include <Solid/Block>
0020 #include <Solid/StorageAccess>
0021 #ifdef Q_OS_NETBSD
0022 #include <Solid/GenericInterface>
0023 #endif
0024 
0025 #include <qglobal.h>
0026 #include <QDebug>
0027 #include <QFile>
0028 #include <QMutex>
0029 #include <QStringList>
0030 
0031 #include <sys/types.h>
0032 #include <sys/ioctl.h>
0033 
0034 #include <stdio.h>
0035 #include <stdlib.h>
0036 #include <fcntl.h>
0037 #include <unistd.h>
0038 #include <errno.h>
0039 #include <sys/stat.h>
0040 #include <math.h>
0041 #include <stdarg.h>
0042 #include <limits.h>
0043 
0044 
0045 #ifdef Q_OS_LINUX
0046 
0047 #include <linux/version.h>
0048 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,70)
0049 typedef unsigned char u8;
0050 #endif
0051 
0052 #undef __STRICT_ANSI__
0053 #include <linux/cdrom.h>
0054 #define __STRICT_ANSI__
0055 
0056 #endif // Q_OS_LINUX
0057 
0058 #ifdef Q_OS_FREEBSD
0059 #include <stdio.h>
0060 #include <camlib.h>
0061 #define CD_FRAMESIZE_RAW 2352
0062 #endif
0063 
0064 #ifdef Q_OS_NETBSD
0065 #include <sys/cdio.h>
0066 #endif
0067 
0068 #ifdef HAVE_RESMGR
0069 extern "C" {
0070 #include <resmgr.h>
0071 }
0072 #endif
0073 
0074 #ifdef Q_OS_FREEBSD
0075 #define HANDLE_DEFAULT_VALUE 0
0076 #endif
0077 #ifdef Q_OS_WIN32
0078 #define HANDLE_DEFAULT_VALUE INVALID_HANDLE_VALUE
0079 #endif
0080 #ifdef Q_OS_LINUX
0081 #define HANDLE_DEFAULT_VALUE -1
0082 #endif
0083 #ifdef Q_OS_NETBSD
0084 #define HANDLE_DEFAULT_VALUE -1
0085 #endif
0086 
0087 //
0088 // Very evil hacking: force the speed values to be accurate
0089 // as long as "they" do not introduce other "broken" DVD
0090 // speeds like 2.4 this works fine
0091 //
0092 namespace {
0093     int fixupDvdWritingSpeed( int speed )
0094     {
0095         //
0096         // Some writers report their speeds in 1000 bytes per second instead of 1024.
0097         //
0098         if( speed % K3b::Device::SPEED_FACTOR_DVD == 0 )
0099             return speed;
0100 
0101         else if( speed % 1352 == 0 )
0102             return speed*K3b::Device::SPEED_FACTOR_DVD/1352;
0103 
0104         // has to be 2.4x speed
0105         else
0106             return 3324;
0107     }
0108 }
0109 
0110 class K3b::Device::Device::Private
0111 {
0112 public:
0113     Private()
0114         : deviceHandle(HANDLE_DEFAULT_VALUE),
0115           openedReadWrite(false),
0116           burnfree(false) {
0117     }
0118 
0119     Solid::Device solidDevice;
0120 
0121     QString vendor;
0122     QString description;
0123     QString version;
0124     int maxReadSpeed;
0125     int maxWriteSpeed;
0126     int currentWriteSpeed;
0127 
0128     bool dvdMinusTestwrite;
0129 
0130     int bufferSize;
0131 
0132     WritingModes writeModes;
0133 
0134     QString blockDevice;
0135     QString genericDevice;
0136 
0137     MediaTypes readCapabilities;
0138     MediaTypes writeCapabilities;
0139     MediaTypes supportedProfiles;
0140     Handle deviceHandle;
0141     bool openedReadWrite;
0142     bool burnfree;
0143 
0144     QMutex mutex;
0145     QMutex openCloseMutex;
0146 };
0147 
0148 #ifdef Q_OS_FREEBSD
0149 K3b::Device::Device::Handle K3b::Device::openDevice( const char* name, bool write )
0150 {
0151     K3b::Device::Device::Handle handle = cam_open_device (name, O_RDWR);
0152         qDebug() << "(K3b::Device::openDevice) open device " << name
0153                  << ((handle)?" succeeded.":" failed.") << Qt::endl;
0154     return handle;
0155 }
0156 #endif
0157 
0158 
0159 #if defined(Q_OS_LINUX) || defined(Q_OS_NETBSD)
0160 K3b::Device::Device::Handle K3b::Device::openDevice( const char* name, bool write )
0161 {
0162     K3b::Device::Device::Handle fd = HANDLE_DEFAULT_VALUE;
0163     int flags = O_NONBLOCK | O_CLOEXEC;
0164     if( write )
0165         flags |= O_RDWR;
0166     else
0167         flags |= O_RDONLY;
0168 
0169 #ifdef HAVE_RESMGR
0170     // first try resmgr
0171     fd = ::rsm_open_device( name, flags );
0172     //  qDebug() << "(K3b::Device::Device) resmgr open: " << fd;
0173 #endif
0174 
0175     if( fd < 0 )
0176         fd = ::open( name, flags );
0177 
0178     if( fd < 0 ) {
0179         qDebug() << "(K3b::Device::Device) could not open device "
0180                  << name << ( write ? " for writing" : " for reading" ) << Qt::endl;
0181         qDebug() << "                    (" << QString::fromLocal8Bit( ::strerror(errno) ) << ")";
0182         fd = HANDLE_DEFAULT_VALUE;
0183 
0184         // at least open it read-only (which is sufficient for kernels < 2.6.8 anyway)
0185         if( write )
0186             return openDevice( name, false );
0187     }
0188 
0189     return fd;
0190 }
0191 #endif
0192 
0193 #ifdef Q_OS_WIN32
0194 #define NAME_COUNT  25
0195 
0196 K3b::Device::Device::Handle K3b::Device::openDevice( const char* name, bool write )
0197 {
0198     bool status = false;
0199     K3b::Device::Device::Handle deviceHandle = HANDLE_DEFAULT_VALUE;
0200     char string[NAME_COUNT + 1];
0201     // check if name is already a device name
0202     if (name[0] == '\\')
0203         strncpy(string, name, NAME_COUNT);
0204     else
0205         _snprintf(string, NAME_COUNT, "\\\\.\\%s", name);
0206     deviceHandle = CreateFileA(string,
0207         GENERIC_READ | GENERIC_WRITE , // at least inquiry needs write access
0208         FILE_SHARE_READ | (write ? FILE_SHARE_WRITE : 0),
0209         NULL,
0210         OPEN_EXISTING,
0211         0,
0212         NULL);
0213 
0214     if( deviceHandle == INVALID_HANDLE_VALUE )
0215         deviceHandle = CreateFileA(string,
0216             GENERIC_READ,
0217             FILE_SHARE_READ,
0218             NULL,
0219             OPEN_EXISTING,
0220             0,
0221             NULL);
0222 
0223     if (deviceHandle == INVALID_HANDLE_VALUE) {
0224         int errorCode = GetLastError();
0225         qDebug() << "Error opening " << string << "Error:" << errorCode << Qt::endl;
0226         return HANDLE_DEFAULT_VALUE;
0227     }
0228 
0229     return deviceHandle;
0230 }
0231 #endif
0232 
0233 
0234 K3b::Device::Device::Device( const Solid::Device& dev )
0235 {
0236 #ifdef Q_OS_NETBSD
0237     const Solid::GenericInterface *gi = dev.as<Solid::GenericInterface>();
0238 #endif
0239     d = new Private;
0240     d->solidDevice = dev;
0241     // TODO: block device address might not be "stdio:/path"
0242 #ifndef Q_OS_NETBSD
0243     d->blockDevice = dev.as<Solid::Block>()->device();
0244 #else
0245     if (gi->propertyExists("block.netbsd.raw_device"))
0246         d->blockDevice = gi->property("block.netbsd.raw_device").toString();
0247     else
0248         d->blockDevice = dev.as<Solid::Block>()->device();
0249 #endif
0250     d->writeModes = {};
0251     d->maxWriteSpeed = 0;
0252     d->maxReadSpeed = 0;
0253     d->burnfree = false;
0254     d->dvdMinusTestwrite = true;
0255     d->bufferSize = 0;
0256 }
0257 
0258 
0259 K3b::Device::Device::~Device()
0260 {
0261     close();
0262     delete d;
0263 }
0264 
0265 
0266 QString K3b::Device::Device::vendor() const
0267 {
0268     return d->vendor;
0269 }
0270 
0271 
0272 QString K3b::Device::Device::description() const
0273 {
0274     return d->description;
0275 }
0276 
0277 
0278 QString K3b::Device::Device::version() const
0279 {
0280     return d->version;
0281 }
0282 
0283 
0284 bool K3b::Device::Device::dvdMinusTestwrite() const
0285 {
0286     return d->dvdMinusTestwrite;
0287 }
0288 
0289 
0290 int K3b::Device::Device::maxReadSpeed() const
0291 {
0292     return d->maxReadSpeed;
0293 }
0294 
0295 
0296 int K3b::Device::Device::currentWriteSpeed() const
0297 {
0298     return d->currentWriteSpeed;
0299 }
0300 
0301 
0302 int K3b::Device::Device::bufferSize() const
0303 {
0304     return d->bufferSize;
0305 }
0306 
0307 
0308 QString K3b::Device::Device::blockDeviceName() const
0309 {
0310     return d->blockDevice;
0311 }
0312 
0313 
0314 int K3b::Device::Device::maxWriteSpeed() const
0315 {
0316     return d->maxWriteSpeed;
0317 }
0318 
0319 
0320 void K3b::Device::Device::setCurrentWriteSpeed( int s )
0321 {
0322     d->currentWriteSpeed = s;
0323 }
0324 
0325 
0326 void K3b::Device::Device::setMaxReadSpeed( int s )
0327 {
0328     d->maxReadSpeed = s;
0329 }
0330 
0331 
0332 void K3b::Device::Device::setMaxWriteSpeed( int s )
0333 {
0334     d->maxWriteSpeed = s;
0335 }
0336 
0337 
0338 Solid::Device K3b::Device::Device::solidDevice() const
0339 {
0340     return d->solidDevice;
0341 }
0342 
0343 
0344 Solid::StorageAccess* K3b::Device::Device::solidStorage() const
0345 {
0346      QList<Solid::Device> storages = Solid::Device::listFromType( Solid::DeviceInterface::StorageAccess, d->solidDevice.udi() );
0347      if( storages.isEmpty() )
0348          return nullptr;
0349      else
0350          return storages.first().as<Solid::StorageAccess>();
0351 }
0352 
0353 
0354 bool K3b::Device::Device::init( bool bCheckWritingModes )
0355 {
0356     qDebug() << "(K3b::Device::Device) " << blockDeviceName() << ": init()";
0357 
0358     //
0359     // they all should read CD-ROM.
0360     //
0361     d->readCapabilities = MEDIA_CD_ROM;
0362     d->writeCapabilities = {};
0363     d->supportedProfiles = {};
0364 
0365     if( !open() )
0366         return false;
0367 
0368     //
0369     // inquiry
0370     // use a 36 bytes buffer since not all devices return the full inquiry struct
0371     //
0372     ScsiCommand cmd( this );
0373     unsigned char buf[36];
0374     cmd.clear();
0375     ::memset( buf, 0, sizeof(buf) );
0376     struct inquiry* inq = (struct inquiry*)buf;
0377     cmd[0] = MMC_INQUIRY;
0378     cmd[4] = sizeof(buf);
0379     cmd[5] = 0;
0380     if( cmd.transport( TR_DIR_READ, buf, sizeof(buf) ) ) {
0381         qCritical() << "(K3b::Device::Device) Unable to do inquiry." << Qt::endl;
0382         close();
0383         return false;
0384     }
0385     else {
0386         d->vendor = QString::fromLatin1( (char*)(inq->vendor), 8 ).trimmed();
0387         d->description = QString::fromLatin1( (char*)(inq->product), 16 ).trimmed();
0388         d->version = QString::fromLatin1( (char*)(inq->revision), 4 ).trimmed();
0389     }
0390 
0391     if( d->vendor.isEmpty() )
0392         d->vendor = "UNKNOWN";
0393     if( d->description.isEmpty() )
0394         d->description = "UNKNOWN";
0395 
0396     //
0397     // We probe all features of the device. Since not all devices support the GET CONFIGURATION command
0398     // we also query the mode page 2A and use the cdrom.h stuff to get as much information as possible
0399     //
0400     checkFeatures();
0401 
0402     //
0403     // Check the supported write modes (WRITINGMODE_TAO, WRITINGMODE_SAO, WRITINGMODE_RAW) by trying to set them
0404     // We do this before checking mode page 2A in case some readers allow changing
0405     // the write parameter page
0406     //
0407     if( bCheckWritingModes )
0408         checkWritingModes();
0409 
0410     //
0411     // Most current drives support the 2A mode page
0412     // Here we can get some more information (cdrecord -prcap does exactly this)
0413     //
0414     checkFor2AFeatures();
0415 
0416     d->maxWriteSpeed = determineMaximalWriteSpeed();
0417 
0418     //
0419     // Check Just-Link via Ricoh mode page 0x30
0420     //
0421     if( !d->burnfree )
0422         checkForJustLink();
0423 
0424     //
0425     // Support for some very old drives
0426     //
0427     checkForAncientWriters();
0428 
0429     //
0430     // If it can be written it can also be read
0431     //
0432     d->readCapabilities |= d->writeCapabilities;
0433 
0434     close();
0435 
0436     return furtherInit();
0437 }
0438 
0439 
0440 bool K3b::Device::Device::furtherInit()
0441 {
0442 #ifdef Q_OS_LINUX
0443 
0444     //
0445     // Since all CDR drives at least support WRITINGMODE_TAO, all CDRW drives should support
0446     // mode page 2a and all DVD writer should support mode page 2a or the GET CONFIGURATION
0447     // command this is redundant and may be removed for BSD ports or even completely
0448     //
0449     // We just keep it here because of the "should" in the sentence above. If someone can tell me
0450     // that the linux driver does nothing more we can remove it completely.
0451     //
0452     open();
0453     int drivetype = ::ioctl( handle(), CDROM_GET_CAPABILITY, CDSL_CURRENT );
0454     if( drivetype < 0 ) {
0455         qDebug() << "Error while retrieving capabilities.";
0456         close();
0457         return false;
0458     }
0459 
0460     d->readCapabilities |= MEDIA_CD_ROM;
0461 
0462     if( drivetype & CDC_CD_R )
0463         d->writeCapabilities |= MEDIA_CD_R;
0464     if( drivetype & CDC_CD_RW )
0465         d->writeCapabilities |= MEDIA_CD_RW;
0466     if( drivetype & CDC_DVD_R )
0467         d->writeCapabilities |= MEDIA_DVD_R;
0468     if( drivetype & CDC_DVD )
0469         d->readCapabilities |= MEDIA_DVD_ROM;
0470 
0471     close();
0472 
0473 #endif // Q_OS_LINUX
0474 #ifdef Q_OS_WIN32
0475     qDebug() << __FUNCTION__ << "to be implemented";
0476 #endif
0477     return true;
0478 }
0479 
0480 
0481 void K3b::Device::Device::checkForAncientWriters()
0482 {
0483     // TODO: add a boolean which determines if this device is non-MMC so we may warn the user at K3b startup about it
0484 
0485     //
0486     // There are a lot writers out there which behave like the TEAC R5XS
0487     //
0488     if( ( vendor().startsWith("TEAC") && ( description().startsWith("CD-R50S") ||
0489                                            description().startsWith("CD-R55S") ) )
0490         ||
0491         ( vendor().startsWith("SAF") && ( description().startsWith("CD-R2006PLUS") ||
0492                                           description().startsWith("CD-RW226") ||
0493                                           description().startsWith("CD-R4012") ) )
0494         ||
0495         ( vendor().startsWith("JVC") && ( description().startsWith("XR-W2001") ||
0496                                           description().startsWith("XR-W2010") ||
0497                                           description().startsWith("R2626") ) )
0498         ||
0499         ( vendor().startsWith("PINNACLE") && ( description().startsWith("RCD-1000") ||
0500                                                description().startsWith("RCD5020") ||
0501                                                description().startsWith("RCD5040") ||
0502                                                description().startsWith("RCD 4X4") ) )
0503         ||
0504         ( vendor().startsWith("Traxdata") && description().startsWith("CDR4120") ) ) {
0505         d->writeModes = WRITINGMODE_TAO;
0506         d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
0507         d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
0508         d->maxWriteSpeed = 4;
0509         d->maxReadSpeed = 12;
0510         d->bufferSize = 1024;
0511         d->burnfree = false;
0512     }
0513     else if( vendor().startsWith("TEAC") ) {
0514         if( description().startsWith("CD-R56S") ) {
0515             d->writeModes |= WRITINGMODE_TAO;
0516             d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
0517             d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
0518             d->maxWriteSpeed = 6;
0519             d->maxReadSpeed = 24;
0520             d->bufferSize = 1302;
0521             d->burnfree = false;
0522         }
0523         if( description().startsWith("CD-R58S") ) {
0524             d->writeModes |= WRITINGMODE_TAO;
0525             d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
0526             d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
0527             d->maxWriteSpeed = 8;
0528             d->maxReadSpeed = 24;
0529             d->bufferSize = 4096;
0530             d->burnfree = false;
0531         }
0532     }
0533     else if( vendor().startsWith("MATSHITA") ) {
0534         if( description().startsWith("CD-R   CW-7501") ) {
0535             d->writeModes = WRITINGMODE_TAO|WRITINGMODE_SAO;
0536             d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
0537             d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
0538             d->maxWriteSpeed = 2;
0539             d->maxReadSpeed = 4;
0540             d->bufferSize = 1024;
0541             d->burnfree = false;
0542         }
0543         if( description().startsWith("CD-R   CW-7502") ) {
0544             d->writeModes = WRITINGMODE_TAO|WRITINGMODE_SAO;
0545             d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
0546             d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
0547             d->maxWriteSpeed = 4;
0548             d->maxReadSpeed = 8;
0549             d->bufferSize = 1024;
0550             d->burnfree = false;
0551         }
0552         else if( description().startsWith("CD-R56S") ) {
0553             d->writeModes |= WRITINGMODE_TAO;
0554             d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
0555             d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
0556             d->maxWriteSpeed = 6;
0557             d->maxReadSpeed = 24;
0558             d->bufferSize = 1302;
0559             d->burnfree = false;
0560         }
0561     }
0562     else if( vendor().startsWith("HP") ) {
0563         if( description().startsWith("CD-Writer 6020") ) {
0564             d->writeModes = WRITINGMODE_TAO;
0565             d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
0566             d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
0567             d->maxWriteSpeed = 2;
0568             d->maxReadSpeed = 6;
0569             d->bufferSize = 1024;
0570             d->burnfree = false;
0571         }
0572     }
0573     else if( vendor().startsWith( "PHILIPS" ) ) {
0574         if( description().startsWith( "CDD2600" ) ) {
0575             d->writeModes = WRITINGMODE_TAO|WRITINGMODE_SAO;
0576             d->readCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
0577             d->writeCapabilities = MEDIA_CD_ROM|MEDIA_CD_R;
0578             d->maxWriteSpeed = 2;
0579             d->maxReadSpeed = 6;
0580             d->bufferSize = 1024;
0581             d->burnfree = false;
0582         }
0583     }
0584 }
0585 
0586 
0587 bool K3b::Device::Device::dao() const
0588 {
0589     return d->writeModes & WRITINGMODE_SAO;
0590 }
0591 
0592 
0593 bool K3b::Device::Device::supportsRawWriting() const
0594 {
0595     return( writingModes() & (WRITINGMODE_RAW|WRITINGMODE_RAW_R16|WRITINGMODE_RAW_R96P|WRITINGMODE_RAW_R96R) );
0596 }
0597 
0598 
0599 bool K3b::Device::Device::writesCd() const
0600 {
0601     return ( d->writeCapabilities & MEDIA_CD_R ) && ( d->writeModes & WRITINGMODE_TAO );
0602 }
0603 
0604 
0605 bool K3b::Device::Device::burner() const
0606 {
0607     return ( writesCd() || writesDvd() );
0608 }
0609 
0610 
0611 bool K3b::Device::Device::writesCdrw() const
0612 {
0613     return d->writeCapabilities & MEDIA_CD_RW;
0614 }
0615 
0616 
0617 bool K3b::Device::Device::writesDvd() const
0618 {
0619     return ( writesDvdPlus() || writesDvdMinus() );
0620 }
0621 
0622 
0623 bool K3b::Device::Device::writesDvdPlus() const
0624 {
0625     return d->writeCapabilities & (MEDIA_DVD_PLUS_R|MEDIA_DVD_PLUS_RW);
0626 }
0627 
0628 
0629 bool K3b::Device::Device::writesDvdMinus() const
0630 {
0631     return d->writeCapabilities & (MEDIA_DVD_R|MEDIA_DVD_RW);
0632 }
0633 
0634 
0635 bool K3b::Device::Device::readsDvd() const
0636 {
0637     return d->readCapabilities & MEDIA_DVD_ROM;
0638 }
0639 
0640 
0641 K3b::Device::DeviceTypes K3b::Device::Device::type() const
0642 {
0643     DeviceTypes r = {};
0644     if( readCapabilities() & MEDIA_CD_ROM )
0645         r |= DEVICE_CD_ROM;
0646     if( writeCapabilities() & MEDIA_CD_R )
0647         r |= DEVICE_CD_R;
0648     if( writeCapabilities() & MEDIA_CD_RW )
0649         r |= DEVICE_CD_RW;
0650     if( readCapabilities() & MEDIA_DVD_ROM )
0651         r |= DEVICE_DVD_ROM;
0652     if( writeCapabilities() & MEDIA_DVD_RAM )
0653         r |= DEVICE_DVD_RAM;
0654     if( writeCapabilities() & MEDIA_DVD_R )
0655         r |= DEVICE_DVD_R;
0656     if( writeCapabilities() & MEDIA_DVD_RW )
0657         r |= DEVICE_DVD_RW;
0658     if( writeCapabilities() & MEDIA_DVD_R_DL )
0659         r |= DEVICE_DVD_R_DL;
0660     if( writeCapabilities() & MEDIA_DVD_PLUS_R )
0661         r |= DEVICE_DVD_PLUS_R;
0662     if( writeCapabilities() & MEDIA_DVD_PLUS_RW )
0663         r |= DEVICE_DVD_PLUS_RW;
0664     if( writeCapabilities() & MEDIA_DVD_PLUS_R_DL )
0665         r |= DEVICE_DVD_PLUS_R_DL;
0666     if( readCapabilities() & MEDIA_HD_DVD_ROM )
0667         r |= DEVICE_HD_DVD_ROM;
0668     if( writeCapabilities() & MEDIA_HD_DVD_R )
0669         r |= DEVICE_HD_DVD_R;
0670     if( writeCapabilities() & MEDIA_HD_DVD_RAM )
0671         r |= DEVICE_HD_DVD_RAM;
0672     if( readCapabilities() & MEDIA_BD_ROM )
0673         r |= DEVICE_BD_ROM;
0674     if( writeCapabilities() & MEDIA_BD_R )
0675         r |= DEVICE_BD_R;
0676     if( writeCapabilities() & MEDIA_BD_RE )
0677         r |= DEVICE_BD_RE;
0678 
0679     return r;
0680 }
0681 
0682 
0683 K3b::Device::MediaTypes K3b::Device::Device::readCapabilities() const
0684 {
0685     return d->readCapabilities;
0686 }
0687 
0688 
0689 K3b::Device::MediaTypes K3b::Device::Device::writeCapabilities() const
0690 {
0691     return d->writeCapabilities;
0692 }
0693 
0694 
0695 K3b::Device::WritingModes K3b::Device::Device::writingModes() const
0696 {
0697     return d->writeModes;
0698 }
0699 
0700 
0701 bool K3b::Device::Device::burnproof() const
0702 {
0703     return burnfree();
0704 }
0705 
0706 
0707 bool K3b::Device::Device::burnfree() const
0708 {
0709     return d->burnfree;
0710 }
0711 
0712 
0713 bool K3b::Device::Device::isDVD() const
0714 {
0715     if( readsDvd() )
0716         return( mediaType() & MEDIA_DVD_ALL );
0717     else
0718         return false;
0719 }
0720 
0721 
0722 int K3b::Device::Device::isEmpty() const
0723 {
0724     // if the device is already opened we do not close it
0725     // to allow fast multiple method calls in a row
0726     bool needToClose = !isOpen();
0727 
0728     int ret = STATE_UNKNOWN;
0729     if( !open() )
0730         return STATE_UNKNOWN;
0731 
0732     if( !testUnitReady() )
0733         return STATE_NO_MEDIA;
0734 
0735     UByteArray data;
0736 
0737     if( readDiscInformation( data ) ) {
0738         disc_info_t* inf = (disc_info_t*)data.data();
0739         switch( inf->status ) {
0740         case 0:
0741             ret = STATE_EMPTY;
0742             break;
0743         case 1:
0744             ret = STATE_INCOMPLETE;
0745             break;
0746         case 2:
0747             ret = STATE_COMPLETE;
0748             break;
0749         default:
0750             ret = STATE_UNKNOWN;
0751             break;
0752         }
0753     }
0754 
0755     if( needToClose )
0756         close();
0757 
0758     return ret;
0759 }
0760 
0761 
0762 int K3b::Device::Device::numSessions() const
0763 {
0764     //
0765     // Session Info
0766     // ============
0767     // Byte 0-1: Data Length
0768     // Byte   2: First Complete Session Number (Hex) - always 1
0769     // Byte   3: Last Complete Session Number (Hex)
0770     //
0771 
0772     int ret = -1;
0773 
0774     UByteArray data;
0775 
0776     int m = mediaType();
0777     if( m & MEDIA_CD_ALL ) {
0778         //
0779         // Although disk_info should get the real value without ide-scsi
0780         // I keep getting wrong values (the value is too high. I think the leadout
0781         // gets counted as session sometimes :()
0782         //
0783         if( readTocPmaAtip( data, 1, 0, 0 ) ) {
0784             ret = data[3];
0785         }
0786         else {
0787             qDebug() << "(K3b::Device::Device) " << blockDeviceName() << ": could not get session info !";
0788         }
0789     }
0790     else if ( m & ( MEDIA_DVD_PLUS_RW|MEDIA_DVD_RW_OVWR|MEDIA_BD_RE ) ) {
0791         // fabricate value
0792         int e = isEmpty();
0793         return ( e == STATE_COMPLETE ? 1 : 0 );
0794     }
0795     else {
0796         if( readDiscInformation( data ) ) {
0797             ret = (int)( data[9]<<8 | data[4] );
0798 
0799             // do only count complete sessions
0800             if( (data[2]>>2) != 3 )
0801                 ret--;
0802         }
0803     }
0804 
0805     return ret;
0806 }
0807 
0808 
0809 K3b::Device::Track::DataMode K3b::Device::Device::getDataMode( const K3b::Msf& sector ) const
0810 {
0811     bool needToClose = !isOpen();
0812 
0813     Track::DataMode ret = Track::UNKNOWN;
0814 
0815     if( !open() )
0816         return ret;
0817 
0818     // we use readCdMsf here since it's defined mandatory in MMC1 and
0819     // we only use this method for CDs anyway
0820     unsigned char data[2352];
0821     bool readSuccess = readCdMsf( data, 2352,
0822                                   0,      // all sector types
0823                                   false,  // no dap
0824                                   sector,
0825                                   sector+1,
0826                                   true, // SYNC
0827                                   true, // HEADER
0828                                   true, // SUBHEADER
0829                                   true, // USER DATA
0830                                   true, // EDC/ECC
0831                                   0,    // no c2 info
0832                                   0 );
0833 
0834     if( readSuccess ) {
0835         if ( data[15] == 0x1 )
0836             ret = Track::MODE1;
0837         else if ( data[15] == 0x2 )
0838             ret = Track::MODE2;
0839         if ( ret == Track::MODE2 ) {
0840             if ( data[16] == data[20] &&
0841                  data[17] == data[21] &&
0842                  data[18] == data[22] &&
0843                  data[19] == data[23] ) {
0844                 if ( data[18] & 0x20 )
0845                     ret = Track::XA_FORM2;
0846                 else
0847                     ret = Track::XA_FORM1;
0848             }
0849         }
0850     }
0851 
0852     if( needToClose )
0853         close();
0854 
0855     return ret;
0856 }
0857 
0858 
0859 
0860 K3b::Device::Track::DataMode K3b::Device::Device::getTrackDataMode( const K3b::Device::Track& track ) const
0861 {
0862     return getDataMode( track.firstSector() );
0863 }
0864 
0865 
0866 K3b::Device::Toc K3b::Device::Device::readToc() const
0867 {
0868     // if the device is already opened we do not close it
0869     // to allow fast multiple method calls in a row
0870     bool needToClose = !isOpen();
0871 
0872     Toc toc;
0873 
0874     if( !open() )
0875         return toc;
0876 
0877     int mt = mediaType();
0878 
0879     //
0880     // Use the profile if available because DVD-ROM units need to treat DVD+-R(W) media as DVD-ROM
0881     // if supported at all
0882     //
0883     if( currentProfile() == MEDIA_DVD_ROM )
0884         mt = MEDIA_DVD_ROM;
0885 
0886     if( mt & (MEDIA_DVD_MINUS_ALL|MEDIA_DVD_PLUS_RW|MEDIA_DVD_ROM) ) {
0887         if( !readFormattedToc( toc, mt ) ) {
0888             K3b::Msf size;
0889             if( readCapacity( size ) ) {
0890                 Track track;
0891                 track.setFirstSector( 0 );
0892                 track.setLastSector( size.lba() );
0893                 track.setSession( 1 );
0894                 track.setType( Track::TYPE_DATA );
0895                 track.setMode( Track::DVD );
0896                 track.setCopyPermitted( mt != MEDIA_DVD_ROM );
0897                 track.setPreEmphasis( mt != MEDIA_DVD_ROM );
0898 
0899                 toc.append( track );
0900             }
0901             else
0902                 qDebug() << "(K3b::Device::Device) " << blockDeviceName()
0903                          << "READ CAPACITY for toc failed." << Qt::endl;
0904         }
0905     }
0906 
0907     else if( mt & (MEDIA_DVD_PLUS_R|MEDIA_DVD_PLUS_R_DL) ) {
0908         //
0909         // a DVD+R disk may have multiple sessions
0910         // every session may contain up to 16 fragments
0911         // if the disk is open there is one open session
0912         // every closed session is viewed as a track whereas
0913         // every fragment of the open session is viewed as a track
0914         //
0915         // We may use
0916         // READ DISK INFORMATION
0917         // READ TRACK INFORMATION: track number FFh, however, does not refer to the invisible track
0918         // READ TOC/PMA/ATIP: form 0 refers to all closed sessions
0919         //                    form 1 refers to the last closed session
0920         //
0921         readFormattedToc( toc, mt );
0922     }
0923 
0924     else if( mt & MEDIA_BD_ALL ) {
0925         readFormattedToc( toc, mt );
0926     }
0927 
0928     else if( mt == MEDIA_DVD_RAM ) {
0929         qDebug() << "(K3b::Device::readDvdToc) no dvdram support";
0930     }
0931 
0932 
0933     else if( mt & MEDIA_CD_ALL ) {
0934         bool success = readRawToc( toc );
0935         if( !success ) {
0936             success = readFormattedToc( toc, mt );
0937 
0938 #ifdef Q_OS_LINUX
0939             if( !success ) {
0940                 qDebug() << "(K3b::Device::Device) MMC READ TOC failed. falling back to cdrom.h.";
0941                 readTocLinux(toc);
0942             }
0943 #endif
0944 
0945             if( success )
0946                 fixupToc( toc );
0947         }
0948     }
0949 
0950     if( needToClose )
0951         close();
0952 
0953     return toc;
0954 }
0955 
0956 
0957 void K3b::Device::Device::readIsrcMcn( K3b::Device::Toc& toc ) const
0958 {
0959     // read MCN and ISRC of all tracks
0960     QByteArray mcn;
0961     if( readMcn( mcn ) ) {
0962         toc.setMcn( mcn );
0963         qDebug() << "(K3b::Device::Device) found MCN: " << mcn;
0964     }
0965     else
0966         qDebug() << "(K3b::Device::Device) no MCN found.";
0967 
0968     for( int i = 1; i <= toc.count(); ++i ) {
0969         QByteArray isrc;
0970         if( toc[i-1].type() == Track::TYPE_AUDIO ) {
0971             if( readIsrc( i, isrc ) ) {
0972                 qDebug() << "(K3b::Device::Device) found ISRC for track " << i << ": " << isrc;
0973                 toc[i-1].setIsrc( isrc );
0974             }
0975             else
0976                 qDebug() << "(K3b::Device::Device) no ISRC found for track " << i;
0977         }
0978     }
0979 }
0980 
0981 
0982 bool K3b::Device::Device::readFormattedToc( K3b::Device::Toc& toc, int mt ) const
0983 {
0984     // if the device is already opened we do not close it
0985     // to allow fast multiple method calls in a row
0986     bool needToClose = !isOpen();
0987 
0988     bool success = false;
0989 
0990     toc.clear();
0991 
0992     int lastTrack = 0;
0993 
0994     UByteArray data;
0995     if( !(mt & MEDIA_CD_ALL) ) {
0996         //
0997         // on DVD-R(W) multisession disks only two sessions are represented as tracks in the readTocPmaAtip
0998         // response (fabricated TOC). Thus, we use readDiscInformation for DVD media to get the proper number of tracks
0999         //
1000         if( readDiscInformation( data ) ) {
1001             lastTrack = (int)( data[11]<<8 | data[6] );
1002 
1003             if( readTrackInformation( data, 1, lastTrack ) ) {
1004                 track_info_t* trackInfo = (track_info_t*)data.data();
1005 
1006                 if( trackInfo->blank ) {
1007                     lastTrack--;
1008                 }
1009 
1010                 success = true;
1011             }
1012             else
1013                 return false;
1014         }
1015         else
1016             return false;
1017     }
1018     else {
1019         if( readTocPmaAtip( data, 0, 0, 1 ) ) {
1020 
1021             if( data.size() < 4 ) {
1022                 qDebug() << "(K3b::Device::Device) " << blockDeviceName() << ": formatted toc data too small.";
1023             }
1024             else if( data.size() != ( (int)sizeof(toc_track_descriptor) * ((int)data[3]+1) ) + 4 ) {
1025                 qDebug() << "(K3b::Device::Device) " << blockDeviceName() << ": invalid formatted toc data length: "
1026                          << (data.size()-2) << Qt::endl;
1027             }
1028             else {
1029                 lastTrack = data[3];
1030                 toc_track_descriptor* td = (toc_track_descriptor*)&data[4];
1031                 for( int i = 0; i < lastTrack; ++i ) {
1032 
1033                     Track track;
1034                     unsigned int control = 0;
1035 
1036                     //
1037                     // In case READ TRACK INFORMATION fails:
1038                     // no session number info
1039                     // no track length and thus possibly incorrect last sector for
1040                     // multisession disks
1041                     //
1042                     track.setFirstSector( from4Byte( td[i].start_adr ) );
1043                     track.setLastSector( from4Byte( td[i+1].start_adr ) - 1 );
1044                     control = td[i].control;
1045 
1046                     track.setType( (control & 0x4) ? Track::TYPE_DATA : Track::TYPE_AUDIO );
1047                     track.setMode( getTrackDataMode( track ) );
1048                     track.setCopyPermitted( control & 0x2 );
1049                     track.setPreEmphasis( control & 0x1 );
1050 
1051                     toc.append( track );
1052                 }
1053 
1054                 success = true;
1055             }
1056         }
1057     }
1058 
1059 
1060     //
1061     // Try to get information for all the tracks
1062     //
1063     for( int i = 0; i < lastTrack; ++i ) {
1064         if( toc.count() < i+1 )
1065             toc.append( Track() );
1066 
1067         UByteArray trackData;
1068         if( readTrackInformation( trackData, 1, i+1 ) ) {
1069             track_info_t* trackInfo = (track_info_t*)trackData.data();
1070 
1071             toc[i].setFirstSector( from4Byte( trackInfo->track_start ) );
1072 
1073             if( i > 0 && toc[i-1].lastSector() == 0 )
1074                 toc[i-1].setLastSector( toc[i].firstSector() - 1 );
1075 
1076             // There are drives that return 0 track length here!
1077             // Some drives even return an invalid length here. :(
1078             if( from4Byte( trackInfo->track_size ) > 0 )
1079                 toc[i].setLastSector( toc[i].firstSector() + from4Byte( trackInfo->track_size ) - 1 );
1080 
1081             if( trackInfo->nwa_v ) {
1082                 toc[i].setNextWritableAddress( from4Byte( trackInfo->next_writable ) );
1083                 toc[i].setFreeBlocks( from4Byte( trackInfo->free_blocks ) );
1084             }
1085 
1086             toc[i].setSession( (int)((trackInfo->session_number_m<<8 & 0xf0) |
1087                                      (trackInfo->session_number_l & 0x0f)) );  //FIXME: is this BCD?
1088 
1089             int control = trackInfo->track_mode;
1090 
1091             if( mt & MEDIA_CD_ALL ) {
1092                 toc[i].setType( (control & 0x4) ? Track::TYPE_DATA : Track::TYPE_AUDIO );
1093                 toc[i].setMode( getTrackDataMode( toc[i] ) );
1094             }
1095             else {
1096                 toc[i].setType( Track::TYPE_DATA );
1097                 toc[i].setMode( Track::DVD );
1098             }
1099             toc[i].setCopyPermitted( control & 0x2 );
1100             toc[i].setPreEmphasis( control & 0x1 );
1101         }
1102         else if( !(mt & MEDIA_CD_ALL) ) {
1103             success = false;
1104         }
1105     }
1106 
1107     // this can only happen with DVD media
1108     if( !toc.isEmpty() && toc.last().lastSector() == 0 ) {
1109         qDebug() << "(K3b::Device::Device) " << blockDeviceName() << " no track length for the last non-empty track.";
1110         UByteArray trackData;
1111         if( readTrackInformation( trackData, 1, lastTrack+1 ) ) {
1112             track_info_t* trackInfo = (track_info_t*)trackData.data();
1113 
1114             toc.last().setLastSector( from4Byte( trackInfo->track_start ) - 1 );
1115         }
1116     }
1117 
1118 
1119     if( needToClose )
1120         close();
1121 
1122     return success;
1123 }
1124 
1125 
1126 bool K3b::Device::Device::readRawToc( K3b::Device::Toc& toc ) const
1127 {
1128     // if the device is already opened we do not close it
1129     // to allow fast multiple method calls in a row
1130     bool needToClose = !isOpen();
1131 
1132     bool success = false;
1133 
1134     toc.clear();
1135 
1136     if( open() ) {
1137         //
1138         // Read Raw TOC (format: 0010b)
1139         //
1140         // For POINT from 01h-63h we get all the tracks
1141         // POINT a1h gices us the last track number in the session in PMIN
1142         // POINT a2h gives the start of the session lead-out in PMIN,PSEC,PFRAME
1143         //
1144 
1145         UByteArray data;
1146 
1147         if( readTocPmaAtip( data, 2, false, 1 ) ) {
1148             if( data.size() > 4 ) {
1149                 success = true;
1150 
1151                 toc_raw_track_descriptor* tr = (toc_raw_track_descriptor*)&data[4];
1152 
1153                 //
1154                 // debug the raw toc data
1155                 //
1156                 qDebug() << "Session |  ADR   | CONTROL|  TNO   | POINT  |  Min   |  Sec   | Frame  |  Zero  |  PMIN  |  PSEC  | PFRAME |";
1157                 for( int i = 0; i < (data.size()-4)/(int)sizeof(toc_raw_track_descriptor); ++i ) {
1158                     QString s;
1159                     s += QString( " %1 |" ).arg( (int)tr[i].session_number, 6 );
1160                     s += QString( " %1 |" ).arg( (int)tr[i].adr, 6 );
1161                     s += QString( " %1 |" ).arg( (int)tr[i].control, 6 );
1162                     s += QString( " %1 |" ).arg( (int)tr[i].tno, 6 );
1163                     s += QString( " %1 |" ).arg( (int)tr[i].point, 6, 16 );
1164                     s += QString( " %1 |" ).arg( (int)tr[i].min, 6 );
1165                     s += QString( " %1 |" ).arg( (int)tr[i].sec, 6 );
1166                     s += QString( " %1 |" ).arg( (int)tr[i].frame, 6 );
1167                     s += QString( " %1 |" ).arg( (int)tr[i].zero, 6, 16 );
1168                     s += QString( " %1 |" ).arg( (int)tr[i].p_min, 6 );
1169                     s += QString( " %1 |" ).arg( (int)tr[i].p_sec, 6 );
1170                     s += QString( " %1 |" ).arg( (int)tr[i].p_frame, 6 );
1171                     qDebug() << s;
1172                 }
1173 
1174                 //
1175                 // First we try to determine if the raw toc data uses BCD values
1176                 //
1177                 int isBcd = rawTocDataWithBcdValues( data );
1178                 if( isBcd == -1 ) {
1179                     return false;
1180                 }
1181 
1182                 K3b::Msf sessionLeadOut;
1183 
1184                 for( unsigned int i = 0; i < (data.size()-4)/(unsigned int)sizeof(toc_raw_track_descriptor); ++i ) {
1185                     if( tr[i].adr == 1 && tr[i].point <= 0x63 ) {
1186                         // track
1187                         Track track;
1188                         track.setSession( tr[i].session_number );
1189 
1190                         // :( We use 00:00:00 == 0 lba)
1191                         if( isBcd )
1192                             track.setFirstSector( K3b::Msf( K3b::Device::fromBcd(tr[i].p_min),
1193                                                             K3b::Device::fromBcd(tr[i].p_sec),
1194                                                             K3b::Device::fromBcd(tr[i].p_frame) ) - 150 );
1195                         else
1196                             track.setFirstSector( K3b::Msf( tr[i].p_min, tr[i].p_sec, tr[i].p_frame ) - 150 );
1197 
1198                         track.setType( tr[i].control & 0x4 ? Track::TYPE_DATA : Track::TYPE_AUDIO );
1199                         track.setMode( track.type() == Track::TYPE_DATA ? getTrackDataMode(track) : Track::UNKNOWN );
1200                         track.setCopyPermitted( tr[i].control & 0x2 );
1201                         track.setPreEmphasis( tr[i].control & 0x1 );
1202 
1203                         //
1204                         // only do this within a session because otherwise we already set the last sector with the session leadout
1205                         //
1206                         if( !toc.isEmpty() )
1207                             if( toc[toc.count()-1].session() == track.session() )
1208                                 toc[toc.count()-1].setLastSector( track.firstSector() - 1 );
1209 
1210                         toc.append(track);
1211                     }
1212                     else if( tr[i].point == 0xa2 ) {
1213                         //
1214                         // since the session is always reported before the tracks this is where we do this:
1215                         // set the previous session's last tracks's last sector to the first sector of the
1216                         // session leadout (which was reported before the tracks)
1217                         //
1218                         // This only happens on multisession CDs
1219                         //
1220                         if( !toc.isEmpty() )
1221                             toc[toc.count()-1].setLastSector( sessionLeadOut - 1 );
1222 
1223                         // this is save since the descriptors are reported in ascending order of the session number
1224                         // :( We use 00:00:00 == 0 lba)
1225                         if( isBcd )
1226                             sessionLeadOut = K3b::Msf( K3b::Device::fromBcd(tr[i].p_min),
1227                                                        K3b::Device::fromBcd(tr[i].p_sec),
1228                                                        K3b::Device::fromBcd(tr[i].p_frame) ) - 150;
1229                         else
1230                             sessionLeadOut = K3b::Msf( tr[i].p_min, tr[i].p_sec, tr[i].p_frame ) - 150;
1231                     }
1232                 }
1233 
1234                 qDebug() << blockDeviceName() << ": setting last sector of last track to " << (sessionLeadOut-1).lba();
1235 
1236                 // set the last track's last sector
1237                 if( !toc.isEmpty() )
1238                     toc[toc.count()-1].setLastSector( sessionLeadOut - 1 );
1239             }
1240             else
1241                 qDebug() << "(K3b::Device::Device) " << blockDeviceName() << " empty raw toc.";
1242         }
1243     }
1244 
1245     if( needToClose )
1246         close();
1247 
1248     return success;
1249 }
1250 
1251 
1252 int K3b::Device::Device::rawTocDataWithBcdValues( const UByteArray& data ) const
1253 {
1254     toc_raw_track_descriptor* tr = (toc_raw_track_descriptor*)&data[4];
1255 
1256     bool notBcd = false;
1257     bool notHex = false;
1258 
1259     //
1260     // in most cases this will already tell us if a drive does not provide bcd numbers
1261     // (which should be all newer MMC drives)
1262     //
1263     for( unsigned int i = 0; i < (data.size()-4)/(unsigned int)sizeof(toc_raw_track_descriptor); ++i ) {
1264         if( tr[i].adr == 1 && tr[i].point <= 0xa2) {
1265             if( !K3b::Device::isValidBcd(tr[i].p_min) ||
1266                 !K3b::Device::isValidBcd(tr[i].p_sec) ||
1267                 !K3b::Device::isValidBcd(tr[i].p_frame) ) {
1268                 notBcd = true;
1269                 break;
1270             }
1271 
1272             // we only need to check sec and frame since min needs to be <= 99
1273             // and bcd values are never bigger than 99.
1274             else if( (int)K3b::Device::fromBcd(tr[i].p_sec) >= 60 ||
1275                      (int)K3b::Device::fromBcd(tr[i].p_frame) >= 75 ) {
1276                 notBcd = true;
1277                 break;
1278             }
1279         }
1280     }
1281 
1282 
1283     //
1284     // all values are valid bcd values but we still don't know for sure if they are really
1285     // used as bcd. So we also check the HEX values.
1286     //
1287     for( unsigned int i = 0; i < (data.size()-4)/(unsigned int)sizeof(toc_raw_track_descriptor); ++i ) {
1288         if( tr[i].adr == 1 && tr[i].point <= 0xa2 ) {
1289             if( (int)tr[i].p_min > 99 ||
1290                 (int)tr[i].p_sec >= 60 ||
1291                 (int)tr[i].p_frame >= 75 ) {
1292                 notHex = true;
1293                 break;
1294             }
1295         }
1296     }
1297 
1298 
1299     //
1300     // If all values are valid bcd and valid hex we check the start sectors of the tracks.
1301     //
1302     if( !notHex || !notBcd ) {
1303         K3b::Msf sessionLeadOutHex, sessionLeadOutBcd;
1304         K3b::Msf lastTrackHex, lastTrackBcd;
1305 
1306         for( unsigned int i = 0; i < (data.size()-4)/(unsigned int)sizeof(toc_raw_track_descriptor); ++i ) {
1307 
1308             if( tr[i].adr == 1 ) {
1309                 if( tr[i].point < 0x64 ) {
1310 
1311                     // check hex values
1312                     if( K3b::Msf( tr[i].p_min, tr[i].p_sec, tr[i].p_frame ) <
1313                         lastTrackHex )
1314                         notHex = true;
1315 
1316                     // check bcd values
1317                     if( K3b::Msf( K3b::Device::fromBcd(tr[i].p_min), K3b::Device::fromBcd(tr[i].p_sec), K3b::Device::fromBcd(tr[i].p_frame) ) <
1318                         lastTrackBcd )
1319                         notBcd = true;
1320 
1321                     lastTrackBcd = K3b::Msf( K3b::Device::fromBcd(tr[i].p_min), K3b::Device::fromBcd(tr[i].p_sec), K3b::Device::fromBcd(tr[i].p_frame) );
1322                     lastTrackHex = K3b::Msf( tr[i].p_min, tr[i].p_sec, tr[i].p_frame );
1323                 }
1324                 else if( tr[i].point == 0xa2 ) {
1325                     if( sessionLeadOutHex < lastTrackHex )
1326                         notHex = true;
1327                     if( sessionLeadOutBcd < lastTrackBcd )
1328                         notBcd = true;
1329 
1330                     sessionLeadOutHex = K3b::Msf( tr[i].p_min, tr[i].p_sec, tr[i].p_frame );
1331                     sessionLeadOutBcd = K3b::Msf( K3b::Device::fromBcd(tr[i].p_min), K3b::Device::fromBcd(tr[i].p_sec), K3b::Device::fromBcd(tr[i].p_frame) );
1332                 }
1333             }
1334         }
1335 
1336         // check the last track
1337         if( sessionLeadOutHex < lastTrackHex )
1338             notHex = true;
1339         if( sessionLeadOutBcd < lastTrackBcd )
1340             notBcd = true;
1341     }
1342 
1343 
1344     if( !notBcd && !notHex ) {
1345         qDebug() << "(K3b::Device::Device) need to compare raw toc to formatted toc. :(";
1346         //
1347         // All values are valid bcd and valid HEX values so we compare with the formatted toc.
1348         // This slows us down a lot but in most cases this should not be reached anyway.
1349         //
1350         // TODO: also check the bcd values
1351         //
1352         K3b::Device::Toc formattedToc;
1353         if( readFormattedToc( formattedToc, MEDIA_CD_ROM ) ) {
1354             for( unsigned int i = 0; i < (data.size()-4)/(unsigned int)sizeof(toc_raw_track_descriptor); ++i ) {
1355                 if( tr[i].adr == 1 && tr[i].point < 0x64 ) {
1356                     unsigned int track = (int)tr[i].point;
1357 
1358                     // FIXME: do bcd drive also encode the track number in bcd? If so test it, too.
1359 
1360                     if( ( int )track > formattedToc.count() ) {
1361                         notHex = true;
1362                         break;
1363                     }
1364 
1365                     K3b::Msf posHex( tr[i].p_min,
1366                                      tr[i].p_sec,
1367                                      tr[i].p_frame );
1368                     K3b::Msf posBcd( K3b::Device::fromBcd(tr[i].p_min),
1369                                      K3b::Device::fromBcd(tr[i].p_sec),
1370                                      K3b::Device::fromBcd(tr[i].p_frame) );
1371                     posHex -= 150;
1372                     posBcd -= 150;
1373                     if( posHex != formattedToc[track-1].firstSector() )
1374                         notHex = true;
1375                     if( posBcd != formattedToc[track-1].firstSector() )
1376                         notBcd = true;
1377                 }
1378             }
1379         }
1380     }
1381 
1382     if( notBcd )
1383         qDebug() << "(K3b::Device::Device) found invalid bcd values. No bcd toc.";
1384     if( notHex )
1385         qDebug() << "(K3b::Device::Device) found invalid hex values. No hex toc.";
1386 
1387     if( notBcd == notHex ) {
1388         qDebug() << "(K3b::Device::Device) unable to determine if hex (" << notHex << ") or bcd (" << notBcd << ").";
1389         if( !notHex ) {
1390             qDebug() << "Assuming hex encoding in favor of newer drives and the more reliable raw toc.";
1391             return 0;
1392         }
1393         return -1;
1394     }
1395     else if( notBcd )
1396         return 0;
1397     else
1398         return 1;
1399 }
1400 
1401 
1402 K3b::Device::CdText K3b::Device::Device::readCdText() const
1403 {
1404     return CdText( readRawCdText() );
1405 }
1406 
1407 
1408 QByteArray K3b::Device::Device::readRawCdText( bool* success ) const
1409 {
1410     // if the device is already opened we do not close it
1411     // to allow fast multiple method calls in a row
1412     bool needToClose = !isOpen();
1413 
1414     QByteArray textData;
1415 
1416     if ( success )
1417         *success = false;
1418 
1419     if( open() ) {
1420         UByteArray data;
1421 
1422         if( readTocPmaAtip( data, 5, false, 0 ) ) {
1423             // we need more than the header and a multiple of 18 bytes to have valid CD-TEXT
1424             if( data.size() > 4 && data.size()%18 == 4 ) {
1425                 textData.append( QByteArray( reinterpret_cast<char*>(data.data()), data.size() ) );
1426                 if ( success )
1427                     *success = true;
1428             }
1429             else {
1430                 qDebug() << "invalid CD-TEXT length: " << data.size();
1431             }
1432         }
1433 
1434         if( needToClose )
1435             close();
1436     }
1437 
1438     return textData;
1439 }
1440 
1441 
1442 #ifdef Q_OS_LINUX
1443 // fallback
1444 bool K3b::Device::Device::readTocLinux( K3b::Device::Toc& toc ) const
1445 {
1446     // if the device is already opened we do not close it
1447     // to allow fast multiple method calls in a row
1448     bool needToClose = !isOpen();
1449 
1450     bool success = true;
1451 
1452     toc.clear();
1453 
1454     struct cdrom_tochdr tochdr;
1455     struct cdrom_tocentry tocentry;
1456 
1457     usageLock();
1458     if( open() ) {
1459         //
1460         // CDROMREADTOCHDR ioctl returns:
1461         // cdth_trk0: First Track Number
1462         // cdth_trk1: Last Track Number
1463         //
1464         if( ::ioctl( d->deviceHandle, CDROMREADTOCHDR, &tochdr ) ) {
1465             qDebug() << "(K3b::Device::Device) could not get toc header !";
1466             success = false;
1467         }
1468         else {
1469             Track lastTrack;
1470             for (int i = tochdr.cdth_trk0; i <= tochdr.cdth_trk1 + 1; i++) {
1471                 ::memset(&tocentry,0,sizeof (struct cdrom_tocentry));
1472                 // get Lead-Out Information too
1473                 tocentry.cdte_track = (i<=tochdr.cdth_trk1) ? i : CDROM_LEADOUT;
1474                 tocentry.cdte_format = CDROM_LBA;
1475                 //
1476                 // CDROMREADTOCENTRY ioctl returns:
1477                 // cdte_addr.lba: Start Sector Number (LBA Format requested)
1478                 // cdte_ctrl:     4 ctrl bits
1479                 //                   00x0b: 2 audio Channels(no pre-emphasis)
1480                 //                   00x1b: 2 audio Channels(pre-emphasis)
1481                 //                   10x0b: audio Channels(no pre-emphasis),reserved in cd-rw
1482                 //                   10x1b: audio Channels(pre-emphasis),reserved in cd-rw
1483                 //                   01x0b: data track, recorded uninterrupted
1484                 //                   01x1b: data track, recorded incremental
1485                 //                   11xxb: reserved
1486                 //                   xx0xb: digital copy prohibited
1487                 //                   xx1xb: digital copy permitted
1488                 // cdte_addr:     4 addr bits (type of Q-Subchannel data)
1489                 //                   0000b: no Information
1490                 //                   0001b: current position data
1491                 //                   0010b: MCN
1492                 //                   0011b: ISRC
1493                 //                   0100b-1111b:  reserved
1494                 // cdte_datamode:  0: Data Mode1
1495                 //                 1: CD-I
1496                 //                 2: CD-XA Mode2
1497                 //
1498 
1499                 if( ::ioctl( d->deviceHandle, CDROMREADTOCENTRY, &tocentry ) ) {
1500                     qDebug() << "(K3b::Device::Device) error reading tocentry " << i;
1501                     success = false;
1502                     break;
1503                 }
1504 
1505                 int startSec = tocentry.cdte_addr.lba;
1506                 int control  = tocentry.cdte_ctrl & 0x0f;
1507                 int mode     = tocentry.cdte_datamode;
1508                 if( i > tochdr.cdth_trk0 ) {
1509                     Track track( lastTrack.firstSector(), startSec-1, lastTrack.type(), lastTrack.mode() );
1510                     track.setPreEmphasis( control & 0x1 );
1511                     track.setCopyPermitted( control & 0x2 );
1512                     toc.append( track );
1513                 }
1514                 Track::TrackType trackType = Track::TYPE_UNKNOWN;
1515                 Track::DataMode trackMode = Track::UNKNOWN;
1516                 if( (control & 0x04 ) && (tocentry.cdte_track != CDROM_LEADOUT) ) {
1517                     trackType = Track::TYPE_DATA;
1518                     if( mode == 1 )
1519                         trackMode = Track::MODE1;
1520                     else if( mode == 2 )
1521                         trackMode = Track::MODE2;
1522 
1523                     Track::DataMode tm = getDataMode(startSec);
1524                     if( tm != Track::UNKNOWN )
1525                         trackMode = tm;
1526                 }
1527                 else
1528                     trackType = Track::TYPE_AUDIO;
1529 
1530                 lastTrack = Track( startSec, startSec, trackType, trackMode );
1531             }
1532         }
1533 
1534         if( needToClose )
1535             close();
1536     }
1537     else
1538         success = false;
1539 
1540     usageUnlock();
1541 
1542     return success;
1543 }
1544 #endif // Q_OS_LINUX
1545 
1546 
1547 bool K3b::Device::Device::fixupToc( K3b::Device::Toc& toc ) const
1548 {
1549     bool success = false;
1550 
1551     //
1552     // This is a very lame method of fixing the TOC of an Advanced Audio CD
1553     // (a CD with two sessions: one with audio tracks and one with the data track)
1554     // If a drive does not support reading raw toc or reading track info we only
1555     // get every track's first sector. But between sessions there is a gap which is used
1556     // for ms stuff. In this case it's 11400 sectors in size. When ripping ausio we would
1557     // include these 11400 sectors which would result in a strange ending audio file.
1558     //
1559     if( numSessions() > 1 || toc.contentType() == MIXED ) {
1560         qDebug() << "(K3b::Device::Device) fixup multisession toc...";
1561 
1562         //
1563         // we need to update the last sector of every last track in every session
1564         // for now we only update the track before the last session...
1565         // This is the most often case: Advanced Audio CD
1566         //
1567 
1568         UByteArray data;
1569         if( readTocPmaAtip( data, 1, false, 0 ) ) {
1570 
1571             //
1572             // data[6]    - first track number in last complete session
1573             // data[8-11] - start address of first track in last session
1574             //
1575 
1576             toc[(unsigned int)data[6]-2].setLastSector( from4Byte( &data[8] ) - 11400 - 1 );
1577 
1578             success = true;
1579         }
1580         else
1581             qDebug() << "(K3b::Device::Device) FIXUP TOC failed.";
1582     }
1583 
1584     return success;
1585 }
1586 
1587 
1588 bool K3b::Device::Device::block( bool b ) const
1589 {
1590     //
1591     // For some reason the Scsi Command does not work here.
1592     // So we use the ioctl on Linux systems
1593     //
1594 #if defined(Q_OS_LINUX)
1595     bool success = false;
1596     bool needToClose = !isOpen();
1597     usageLock();
1598     if( open() ) {
1599         success = ( ::ioctl( d->deviceHandle, CDROM_LOCKDOOR, b ? 1 : 0 ) == 0 );
1600         if( needToClose )
1601             close();
1602     }
1603     usageUnlock();
1604     if ( success )
1605         return success;
1606 #elif defined(Q_OS_NETBSD)
1607     bool success = false;
1608     bool needToClose = !isOpen();
1609     int arg = b ? 1 : 0;
1610     usageLock();
1611     if( open() ) {
1612         success = ( ::ioctl( d->deviceHandle, DIOCLOCK, &arg ) == 0 );
1613         if( needToClose )
1614             close();
1615     }
1616     usageUnlock();
1617     if ( success )
1618         return success;
1619 #endif
1620 
1621     ScsiCommand cmd( this );
1622     cmd[0] = MMC_PREVENT_ALLOW_MEDIUM_REMOVAL;
1623     cmd[5] = 0; // Necessary to set the proper command length
1624     if( b )
1625         cmd[4] = 0x01;
1626     int r = cmd.transport( TR_DIR_WRITE );
1627 
1628     if( r )
1629         qDebug() << "(K3b::Device::Device) MMC ALLOW MEDIA REMOVAL failed.";
1630 
1631     return ( r == 0 );
1632 }
1633 
1634 bool K3b::Device::Device::rewritable() const
1635 {
1636     UByteArray data;
1637 
1638     if( readDiscInformation( data ) ) {
1639         disc_info_t* inf = (disc_info_t*)data.data();
1640         bool e = inf->erasable;
1641 
1642         return e;
1643     }
1644     else
1645         return false;
1646 }
1647 
1648 
1649 bool K3b::Device::Device::eject() const
1650 {
1651 #ifdef Q_OS_NETBSD
1652     bool success = false;
1653     bool needToClose = !isOpen();
1654     int arg = 0;
1655 
1656     usageLock();
1657     if( open() ) {
1658         if ( ::ioctl( d->deviceHandle, DIOCEJECT, &arg ) >= 0)
1659             success = true;
1660         if( needToClose )
1661             close();
1662     }
1663     usageUnlock();
1664     if ( success )
1665         return success;
1666 #endif
1667 
1668     ScsiCommand cmd( this );
1669     cmd[0] = MMC_PREVENT_ALLOW_MEDIUM_REMOVAL;
1670     cmd[5] = 0; // Necessary to set the proper command length
1671     cmd.transport( TR_DIR_WRITE );
1672 
1673     cmd[0] = MMC_START_STOP_UNIT;
1674     cmd[5] = 0; // Necessary to set the proper command length
1675     cmd[4] = 0x2;      // eject medium LoEj = 1, Start = 0
1676     if (!cmd.transport( TR_DIR_WRITE )) {
1677         return true;
1678     }
1679 
1680 #if defined(Q_OS_LINUX)
1681     bool success = false;
1682     bool needToClose = !isOpen();
1683 
1684     usageLock();
1685     if( open() ) {
1686         if( ::ioctl( d->deviceHandle, CDROMEJECT ) >= 0 )
1687             success = true;
1688         if( needToClose )
1689             close();
1690     }
1691     usageUnlock();
1692     return success;
1693 #else
1694     return false;
1695 #endif
1696 }
1697 
1698 
1699 bool K3b::Device::Device::load() const
1700 {
1701 #ifdef Q_OS_NETBSD
1702     bool success = false;
1703     bool needToClose = !isOpen();
1704     int arg = 0;
1705 
1706     usageLock();
1707     if( open() ) {
1708         if ( ::ioctl( d->deviceHandle, CDIOCCLOSE, &arg ) >= 0)
1709             success = true;
1710         if( needToClose )
1711             close();
1712     }
1713     usageUnlock();
1714     if ( success )
1715         return success;
1716 #elif defined(Q_OS_LINUX)
1717     bool success = false;
1718     bool needToClose = !isOpen();
1719 
1720     usageLock();
1721     if( open() ) {
1722         if( ::ioctl( d->deviceHandle, CDROMCLOSETRAY ) >= 0 )
1723             success = true;
1724         if( needToClose )
1725             close();
1726     }
1727     usageUnlock();
1728     if ( success )
1729         return success;
1730 #endif
1731 
1732     ScsiCommand cmd( this );
1733     cmd[0] = MMC_START_STOP_UNIT;
1734     cmd[4] = 0x3;    // LoEj = 1, Start = 1
1735     cmd[5] = 0;      // Necessary to set the proper command length
1736     return !cmd.transport();
1737 }
1738 
1739 
1740 bool K3b::Device::Device::setAutoEjectEnabled( bool enabled ) const
1741 {
1742     bool success = false;
1743 #ifdef Q_OS_LINUX
1744 
1745     bool needToClose = !isOpen();
1746     usageLock();
1747     if ( open() ) {
1748         success = ( ::ioctl( d->deviceHandle, CDROMEJECT_SW, enabled ? 1 : 0 ) == 0 );
1749         if ( needToClose ) {
1750             close();
1751         }
1752     }
1753     usageUnlock();
1754 #endif
1755     return success;
1756 }
1757 
1758 
1759 K3b::Device::Device::Handle K3b::Device::Device::handle() const
1760 {
1761     return d->deviceHandle;
1762 }
1763 
1764 
1765 bool K3b::Device::Device::open( bool write ) const
1766 {
1767     if( d->openedReadWrite != write )
1768         close();
1769 
1770     QMutexLocker ml( &d->openCloseMutex );
1771 
1772     d->openedReadWrite = write;
1773 
1774     if( d->deviceHandle == HANDLE_DEFAULT_VALUE)
1775         d->deviceHandle = openDevice( QFile::encodeName(blockDeviceName()), write );
1776 
1777     return ( d->deviceHandle != HANDLE_DEFAULT_VALUE);
1778 }
1779 
1780 
1781 void K3b::Device::Device::close() const
1782 {
1783     QMutexLocker ml( &d->openCloseMutex );
1784 
1785     if( d->deviceHandle == HANDLE_DEFAULT_VALUE)
1786         return;
1787 
1788 #if defined(Q_OS_FREEBSD)
1789     cam_close_device(d->deviceHandle);
1790 #elif defined(Q_OS_WIN32)
1791     CloseHandle(d->deviceHandle);
1792 #else
1793     ::close( d->deviceHandle );
1794 #endif
1795     d->deviceHandle = HANDLE_DEFAULT_VALUE;
1796 }
1797 
1798 
1799 bool K3b::Device::Device::isOpen() const
1800 {
1801     return ( d->deviceHandle != HANDLE_DEFAULT_VALUE);
1802 }
1803 
1804 
1805 int K3b::Device::Device::supportedProfiles() const
1806 {
1807     return d->supportedProfiles;
1808 }
1809 
1810 
1811 int K3b::Device::Device::currentProfile() const
1812 {
1813     unsigned char profileBuf[8];
1814     ::memset( profileBuf, 0, 8 );
1815 
1816     ScsiCommand cmd( this );
1817     cmd[0] = MMC_GET_CONFIGURATION;
1818     cmd[1] = 1;
1819     cmd[8] = 8;
1820     cmd[9] = 0;      // Necessary to set the proper command length
1821 
1822     if( cmd.transport( TR_DIR_READ, profileBuf, 8 ) ) {
1823         qDebug() << "(K3b::Device::Device) " << blockDeviceName()
1824                  << " GET_CONFIGURATION failed." << Qt::endl;
1825         return MEDIA_UNKNOWN;
1826     }
1827     else {
1828         short profile = from2Byte( &profileBuf[6] );
1829 
1830         //
1831         // Plextor drives might not set a current profile
1832         // In that case we get the list of all current profiles
1833         // and simply use the first one in that list.
1834         //
1835         if( profile == 0x00 ) {
1836             qDebug() << "(K3b::Device::Device) " << blockDeviceName()
1837                      << " current profile 0. Checking current profile list instead." << Qt::endl;
1838             UByteArray data;
1839             if( getFeature( data, FEATURE_PROFILE_LIST ) ) {
1840                 int featureLen( data[11] );
1841                 for( int j = 0; j < featureLen; j+=4 ) {
1842                     // use the first current profile we encounter
1843                     if( data[12+j+2] & 0x1 ) {
1844                         profile = from2Byte( &data[12+j] );
1845                         break;
1846                     }
1847                 }
1848             }
1849         }
1850 
1851         switch (profile) {
1852         case 0x00: return MEDIA_NONE;
1853         case 0x08: return MEDIA_CD_ROM;
1854         case 0x09: return MEDIA_CD_R;
1855         case 0x0A: return MEDIA_CD_RW;
1856         case 0x10: return MEDIA_DVD_ROM;
1857         case 0x11: return MEDIA_DVD_R_SEQ;
1858         case 0x12: return MEDIA_DVD_RAM;
1859         case 0x13: return MEDIA_DVD_RW_OVWR;
1860         case 0x14: return MEDIA_DVD_RW_SEQ;
1861         case 0x15: return MEDIA_DVD_R_DL_SEQ;
1862         case 0x16: return MEDIA_DVD_R_DL_JUMP;
1863         case 0x1A: return MEDIA_DVD_PLUS_RW;
1864         case 0x1B: return MEDIA_DVD_PLUS_R;
1865         case 0x2B: return MEDIA_DVD_PLUS_R_DL;
1866         case 0x40: return MEDIA_BD_ROM;
1867         case 0x41: {
1868             if( featureCurrent( FEATURE_BD_PSEUDO_OVERWRITE ) == 1 )
1869                 return MEDIA_BD_R_SRM_POW;
1870             else
1871                 return MEDIA_BD_R_SRM;
1872         }
1873         case 0x42: return MEDIA_BD_R_RRM;
1874         case 0x43: return MEDIA_BD_RE;
1875         case 0x50: return MEDIA_HD_DVD_ROM;
1876         case 0x51: return MEDIA_HD_DVD_R;
1877         case 0x52: return MEDIA_HD_DVD_RAM;
1878         default: return MEDIA_UNKNOWN;
1879         }
1880     }
1881 }
1882 
1883 
1884 K3b::Device::DiskInfo K3b::Device::Device::diskInfo() const
1885 {
1886     DiskInfo inf;
1887 
1888     // if the device is already opened we do not close it
1889     // to allow fast multiple method calls in a row
1890     bool needToClose = !isOpen();
1891 
1892     if( open() ) {
1893 
1894         UByteArray data;
1895 
1896         //
1897         // The first thing to do should be: checking if a media is loaded
1898         // We cannot rely on the profile here since at least some Plextor
1899         // drives return the NO MEDIUM profile for CD media
1900         //
1901         if( !testUnitReady() ) {
1902             // no disk or tray open
1903             inf.d->diskState = STATE_NO_MEDIA;
1904             inf.d->mediaType = MEDIA_NONE;
1905             inf.d->currentProfile = MEDIA_NONE;
1906         }
1907         else
1908             inf.d->currentProfile = currentProfile();
1909 
1910         if( inf.diskState() != STATE_NO_MEDIA ) {
1911 
1912             if( readDiscInformation( data ) ) {
1913                 disc_info_t* dInf = (disc_info_t*)data.data();
1914                 //
1915                 // Copy the needed values from the disk_info struct
1916                 //
1917                 switch( dInf->status ) {
1918                 case 0:
1919                     inf.d->diskState = STATE_EMPTY;
1920                     break;
1921                 case 1:
1922                     inf.d->diskState = STATE_INCOMPLETE;
1923                     break;
1924                 case 2:
1925                     inf.d->diskState = STATE_COMPLETE;
1926                     break;
1927                 default:
1928                     inf.d->diskState = STATE_UNKNOWN;
1929                     break;
1930                 }
1931 
1932                 switch( dInf->border ) {
1933                 case 0:
1934                     inf.d->lastSessionState = STATE_EMPTY;
1935                     break;
1936                 case 1:
1937                     inf.d->lastSessionState = STATE_INCOMPLETE;
1938                     break;
1939                 case 2:
1940                     inf.d->lastSessionState = STATE_COMPLETE;
1941                     break;
1942                 default:
1943                     inf.d->lastSessionState = STATE_UNKNOWN;
1944                     break;
1945                 }
1946 
1947                 switch( dInf->bg_f_status&0x3 ) {
1948                 case 0x0:
1949                     inf.d->bgFormatState = BG_FORMAT_NONE;
1950                     break;
1951                 case 0x1:
1952                     inf.d->bgFormatState = BG_FORMAT_INCOMPLETE;
1953                     break;
1954                 case 0x2:
1955                     inf.d->bgFormatState = BG_FORMAT_IN_PROGRESS;
1956                     break;
1957                 case 0x3:
1958                     inf.d->bgFormatState = BG_FORMAT_COMPLETE;
1959                     break;
1960                 }
1961 
1962                 inf.d->numTracks = (dInf->last_track_l & 0xff) | (dInf->last_track_m<<8 & 0xff00);
1963                 if( inf.diskState() == STATE_EMPTY )
1964                     inf.d->numTracks = 0;
1965 
1966                 // FIXME: I am not sure if this is accurate. Better test the last track's RT field
1967                 else if( inf.diskState() == STATE_INCOMPLETE )
1968                     inf.d->numTracks--;  // do not count the invisible track
1969 
1970                 inf.d->rewritable = dInf->erasable;
1971 
1972                 //
1973                 // This is the Last Possible Lead-Out Start Address in HMSF format
1974                 // This is only valid for CD-R(W) and DVD+R media.
1975                 // For complete media this shall be filled with 0xff
1976                 //
1977                 if( dInf->lead_out_m != 0xff &&
1978                     dInf->lead_out_r != 0xff &&
1979                     dInf->lead_out_s != 0xff &&
1980                     dInf->lead_out_f != 0xff )
1981                     inf.d->capacity = K3b::Msf( dInf->lead_out_m + dInf->lead_out_r*60,
1982                                                 dInf->lead_out_s,
1983                                                 dInf->lead_out_f ) - 150;
1984 
1985                 //
1986                 // This is the position where the next Session shall be recorded in HMSF format
1987                 // This is only valid for CD-R(W) and DVD+R media.
1988                 // For complete media this shall be filled with 0xff
1989                 //
1990                 if( dInf->lead_in_m != 0xff &&
1991                     dInf->lead_in_r != 0xff &&
1992                     dInf->lead_in_s != 0xff &&
1993                     dInf->lead_in_f != 0xff )
1994                     inf.d->usedCapacity = K3b::Msf( dInf->lead_in_m + dInf->lead_in_r*60,
1995                                                     dInf->lead_in_s,
1996                                                     dInf->lead_in_f ) - 4500;
1997             }
1998             else {
1999                 qDebug() << "(K3b::Device::Device) " << blockDeviceName()
2000                          << " fabricating disk information for a stupid device." << Qt::endl;
2001                 Toc toc = readToc();
2002                 if( !toc.isEmpty() ) {
2003                     inf.d->diskState = STATE_COMPLETE;
2004                     inf.d->lastSessionState = STATE_COMPLETE;
2005                     inf.d->numTracks = toc.count();
2006                     inf.d->capacity = inf.d->usedCapacity = toc.length();
2007                 }
2008             }
2009 
2010 
2011             //
2012             // The mediatype needs to be set
2013             //
2014             inf.d->mediaType = mediaType();
2015 
2016             // At least some Plextor drives return profile NONE for CD media
2017             // or CD_ROM for writable media
2018             if( inf.d->mediaType & (MEDIA_UNKNOWN|MEDIA_NONE|MEDIA_CD_ROM) ) {
2019                 // probably it is a CD
2020                 if( inf.rewritable() )
2021                     inf.d->mediaType = MEDIA_CD_RW;
2022                 else if( inf.empty() || inf.appendable() )
2023                     inf.d->mediaType = MEDIA_CD_R;
2024                 else
2025                     inf.d->mediaType = MEDIA_CD_ROM;
2026             }
2027 
2028             if( inf.d->mediaType & MEDIA_DVD_ALL ) {
2029                 if( readDvdStructure( data ) ) {
2030                     // some debugging stuff
2031                     K3b::Msf sda, eda, ea0;
2032                     sda = ( data[4+5]<<16 | data[4+6] << 8 | data[4+7] );
2033                     eda = ( data[4+9]<<16 | data[4+10] << 8 | data[4+11] );
2034                     ea0 = ( data[4+13]<<16 | data[4+14] << 8 | data[4+15] );
2035 
2036                     qDebug() << "First sec data area: " << sda.toString()
2037                              << " (LBA " << QString::number(sda.lba())
2038                              << ") (" << QString::number(sda.mode1Bytes()) << Qt::endl;
2039                     qDebug() << "Last sec data area: " << eda.toString()
2040                              << " (LBA " << QString::number(eda.lba())
2041                              << ") (" << QString::number(eda.mode1Bytes()) << " Bytes)" << Qt::endl;
2042                     qDebug() << "Last sec layer 1: " << ea0.toString()
2043                              << " (LBA " << QString::number(ea0.lba())
2044                              << ") (" << QString::number(ea0.mode1Bytes()) << " Bytes)" << Qt::endl;
2045 
2046 
2047                     K3b::Msf da0 = ea0 - sda + 1;
2048                     K3b::Msf da1 = eda - ea0;
2049                     qDebug() << "Layer 1 length: " << da0.toString()
2050                              << " (LBA " << QString::number(da0.lba())
2051                              << ") (" << QString::number(da0.mode1Bytes()) << " Bytes)" << Qt::endl;
2052                     qDebug() << "Layer 2 length: " << da1.toString()
2053                              << " (LBA " << QString::number(da1.lba())
2054                              << ") (" << QString::number(da1.mode1Bytes()) << " Bytes)" << Qt::endl;
2055 
2056                     inf.d->numLayers = ((data[6]&0x60) == 0 ? 1 : 2);
2057 
2058                     bool otp = (data[4+2] & 0xF);
2059 
2060                     // ea0 is 0 if the medium does not use Opposite track path
2061                     if( otp && ea0 > 0 )
2062                         inf.d->firstLayerSize = da0;
2063                     else
2064                         inf.d->firstLayerSize = 0;
2065                 }
2066                 else {
2067                     qDebug() << "(K3b::Device::Device) Unable to read DVD structure for num of layers.";
2068                     inf.d->numLayers = ( (inf.d->mediaType & MEDIA_WRITABLE_DVD_DL) ? 2 : 1 );
2069                 }
2070             }
2071 
2072 
2073             //
2074             // Number of sessions for non-empty disks
2075             //
2076             if( inf.diskState() != STATE_EMPTY ) {
2077                 int sessions = numSessions();
2078                 if( sessions >= 0 )
2079                     inf.d->numSessions = sessions;
2080                 else
2081                     qDebug() << "(K3b::Device::Device) could not get session info via READ TOC/PMA/ATIP.";
2082             }
2083             else
2084                 inf.d->numSessions = 0;
2085 
2086             inf.d->mediaId = mediaId( inf.mediaType() );
2087 
2088             //
2089             // Now we determine the size:
2090 
2091             // for all empty and appendable media READ FORMAT CAPACITIES should return the proper unformatted size
2092             // for complete disks we may use the READ_CAPACITY command or the start sector from the leadout
2093             //
2094             int media = inf.mediaType();
2095             //
2096             // Use the profile if available because DVD-ROM units need to treat DVD+-R(W) media as DVD-ROM
2097             // if supported at all
2098             //
2099             if( inf.currentProfile() == MEDIA_DVD_ROM )
2100                 media = MEDIA_DVD_ROM;
2101 
2102             switch( media ) {
2103             case MEDIA_CD_R:
2104             case MEDIA_CD_RW:
2105                 if( inf.d->capacity == 0 ) {
2106                     if( readTocPmaAtip( data, 0x4, true, 0 ) ) {
2107 
2108                         struct atip_descriptor* atip = (struct atip_descriptor*)data.data();
2109 
2110                         if( data.size() >= 11 ) {
2111                             inf.d->capacity = K3b::Msf( atip->lead_out_m, atip->lead_out_s, atip->lead_out_f ) - 150;
2112                             debugBitfield( &atip->lead_out_m, 3 );
2113                             qDebug() << blockDeviceName() << ": ATIP capacity: " << inf.d->capacity.toString();
2114                         }
2115                     }
2116                 }
2117 
2118                 //
2119                 // for empty and appendable media capacity and usedCapacity should be filled in from
2120                 // diskinfo above. If not they are both still 0
2121                 //
2122                 if( inf.d->capacity != 0 &&
2123                     ( inf.diskState() == STATE_EMPTY || inf.d->usedCapacity != 0 ) ) {
2124                     // done.
2125                     break;
2126                 }
2127                 Q_FALLTHROUGH();
2128 
2129             default:
2130             case MEDIA_CD_ROM:
2131                 if( inf.d->capacity > 0 && inf.d->usedCapacity == 0 )
2132                     inf.d->usedCapacity = inf.d->capacity;
2133 
2134                 if( inf.d->usedCapacity == 0 ) {
2135                     K3b::Msf readCap;
2136                     if( readCapacity( readCap ) ) {
2137                         qDebug() << "(K3b::Device::Device) READ CAPACITY: " << readCap.toString()
2138                                  << " other capacity: " << inf.d->capacity.toString() << Qt::endl;
2139                         //
2140                         // READ CAPACITY returns the last written sector
2141                         // that means the size is actually readCap + 1
2142                         //
2143                         inf.d->usedCapacity = readCap + 1;
2144                     }
2145                     else {
2146                         qDebug() << "(K3b::Device::Device) " << blockDeviceName()
2147                                  << " Falling back to readToc for capacity." << Qt::endl;
2148                         inf.d->usedCapacity = readToc().length();
2149                     }
2150                 }
2151                 break;
2152 
2153             case MEDIA_DVD_ROM: {
2154                 K3b::Msf readCap;
2155                 if( readCapacity( readCap ) ) {
2156                     qDebug() << "(K3b::Device::Device) READ CAPACITY: " << readCap.toString()
2157                              << " other capacity: " << inf.d->capacity.toString() << Qt::endl;
2158                     //
2159                     // READ CAPACITY returns the last written sector
2160                     // that means the size is actually readCap + 1
2161                     //
2162                     inf.d->usedCapacity = readCap + 1;
2163                 }
2164                 else {
2165                     //
2166                     // Only one track, use it's size
2167                     //
2168                     if( readTrackInformation( data, 0x1, 0x1 ) ) {
2169                         track_info_t* trackInfo = (track_info_t*)data.data();
2170                         inf.d->usedCapacity = from4Byte( trackInfo->track_size );
2171                     }
2172                     else
2173                         qDebug() << "(K3b::Device::Device) " << blockDeviceName()
2174                                  << "READ TRACK INFORMATION for DVD-ROM failed." << Qt::endl;
2175                 }
2176 
2177                 break;
2178             }
2179 
2180             case MEDIA_DVD_PLUS_R:
2181             case MEDIA_DVD_PLUS_R_DL:
2182                 if( inf.appendable() || inf.empty() ) {
2183                     //
2184                     // get remaining space via the invisible track
2185                     //
2186                     if( readTrackInformation( data, 0x1, /*0xff*/ inf.numTracks()+1 ) ) {
2187                         track_info_t* trackInfo = (track_info_t*)data.data();
2188                         inf.d->usedCapacity = from4Byte( trackInfo->track_start );
2189                         inf.d->capacity = from4Byte( trackInfo->track_start ) + from4Byte( trackInfo->track_size );
2190                     }
2191                 }
2192                 else {
2193                     if( readTrackInformation( data, 0x1, inf.numTracks() ) ) {
2194                         track_info_t* trackInfo = (track_info_t*)data.data();
2195                         inf.d->capacity = inf.d->usedCapacity
2196                                         = from4Byte( trackInfo->track_start ) + from4Byte( trackInfo->track_size );
2197                     }
2198                 }
2199                 break;
2200 
2201             case MEDIA_DVD_R:
2202             case MEDIA_DVD_R_SEQ:
2203             case MEDIA_DVD_R_DL:
2204             case MEDIA_DVD_R_DL_JUMP:
2205             case MEDIA_DVD_R_DL_SEQ:
2206                 //
2207                 // get data from the incomplete track (which is NOT the invisible track 0xff)
2208                 // This will fail in case the media is complete!
2209                 //
2210                 if( readTrackInformation( data, 0x1, inf.numTracks()+1 ) ) {
2211                     track_info_t* trackInfo = (track_info_t*)data.data();
2212                     inf.d->usedCapacity = from4Byte( trackInfo->track_start );
2213                     inf.d->capacity = from4Byte( trackInfo->free_blocks ) + from4Byte( trackInfo->track_start );
2214                 }
2215 
2216                 //
2217                 // Get the "really" used space without border-out
2218                 //
2219                 if( !inf.empty() ) {
2220                     K3b::Msf readCap;
2221                     if( readCapacity( readCap ) ) {
2222                         //
2223                         // READ CAPACITY returns the last written sector
2224                         // that means the size is actually readCap + 1
2225                         //
2226                         inf.d->usedCapacity = readCap + 1;
2227                     }
2228                     else
2229                         qDebug() << "(K3b::Device::Device) " << blockDeviceName()
2230                                  << " READ CAPACITY for DVD-R failed." << Qt::endl;
2231                 }
2232 
2233                 break;
2234 
2235             case MEDIA_DVD_RW_OVWR:
2236                 inf.d->numSessions = 1;
2237                 Q_FALLTHROUGH();
2238             case MEDIA_DVD_RW:
2239             case MEDIA_DVD_RW_SEQ:
2240                 // only one track on a DVD-RW media
2241                 if( readTrackInformation( data, 0x1, 0x1 ) ) {
2242                     track_info_t* trackInfo = (track_info_t*)data.data();
2243                     inf.d->capacity = from4Byte( trackInfo->track_size );
2244                     if( !inf.empty() ) {
2245                         if( readFormatCapacity( 0x10, inf.d->capacity ) )
2246                             qDebug() << blockDeviceName() << ": Format capacity 0x10: " << inf.d->capacity.toString();
2247 
2248                         inf.d->usedCapacity = from4Byte( trackInfo->track_size );
2249                     }
2250                 }
2251                 break;
2252 
2253             case MEDIA_DVD_PLUS_RW: {
2254                 K3b::Msf currentMax;
2255                 int currentMaxFormat = 0;
2256                 if( readFormatCapacity( 0x26, inf.d->capacity, &currentMax, &currentMaxFormat ) ) {
2257                     if( currentMaxFormat == 0x1 ) { // unformatted or blank media
2258                         inf.d->usedCapacity = 0;
2259                         inf.d->capacity = currentMax;
2260                     }
2261                     else {
2262                         inf.d->usedCapacity = currentMax;
2263                         // Plextor drives tend to screw things up and report invalid values
2264                         // for the max format capacity of 1.4 GB DVD media
2265                         if ( inf.bgFormatState() == BG_FORMAT_COMPLETE ) {
2266                             inf.d->capacity = currentMax;
2267                         }
2268                     }
2269                 }
2270                 else
2271                     qDebug() << "(K3b::Device::Device) " << blockDeviceName()
2272                              << " READ FORMAT CAPACITIES for DVD+RW failed." << Qt::endl;
2273 
2274                 break;
2275             }
2276 
2277             case MEDIA_BD_R:
2278             case MEDIA_BD_R_SRM:
2279             case MEDIA_BD_R_SRM_POW:
2280             case MEDIA_BD_R_RRM:
2281                 //
2282                 // get the invisible track's first sector
2283                 // or the next writable address of the last open track
2284                 //
2285                 if( readDiscInformation( data ) ) {
2286                     int lastTrack = (int)( data[11]<<8 | data[6] );
2287 
2288                     if( readTrackInformation( data, 1, lastTrack ) ) {
2289 
2290                         // capacity: last track's start address + last track's size
2291                         inf.d->capacity = from4Byte( &data[8] ) + from4Byte( &data[24] );
2292 
2293                         if( data[6] & 0x80 )
2294                             inf.d->usedCapacity = from4Byte( &data[8] );
2295                         else if( data[7] & 0x1 )
2296                             inf.d->usedCapacity = from4Byte( &data[12] );
2297                         else
2298                             inf.d->usedCapacity = inf.d->capacity;
2299                     }
2300                 }
2301                 break;
2302 
2303             case MEDIA_BD_RE: {
2304                 K3b::Msf currentMax;
2305                 int currentMaxFormat = 0;
2306                 if ( readFormatCapacity( 0x00, inf.d->capacity, &currentMax, &currentMaxFormat ) ) {
2307                     if( currentMaxFormat == 0x1 ) { // unformatted or blank media
2308                         inf.d->usedCapacity = 0;
2309                         inf.d->capacity = currentMax;
2310                     }
2311                     else {
2312                         inf.d->usedCapacity = currentMax;
2313                     }
2314                 }
2315                 else
2316                     qDebug() << "(K3b::Device::Device) " << blockDeviceName()
2317                              << " READ FORMAT CAPACITIES for BD-RE failed." << Qt::endl;
2318                 break;
2319             }
2320 
2321             case MEDIA_BD_ROM: {
2322                 K3b::Msf readCap;
2323                 if( readCapacity( readCap ) ) {
2324                     //
2325                     // READ CAPACITY returns the last written sector
2326                     // that means the size is actually readCap + 1
2327                     //
2328                     inf.d->usedCapacity = readCap + 1;
2329                 }
2330 
2331                 break;
2332             }
2333             }
2334         }
2335 
2336         if( needToClose )
2337             close();
2338     }
2339 
2340     return inf;
2341 }
2342 
2343 
2344 K3b::Device::MediaType K3b::Device::Device::mediaType() const
2345 {
2346     K3b::Device::MediaType m = MEDIA_UNKNOWN;
2347 
2348     if( testUnitReady() ) {
2349 
2350         int p = currentProfile();
2351         if ( p != -1 )
2352             m = ( MediaType )p;
2353 
2354         if( m & (MEDIA_UNKNOWN|MEDIA_DVD_ROM|MEDIA_HD_DVD_ROM) ) {
2355             //
2356             // We prefer the mediatype as reported by the media since this way
2357             // even ROM drives may report the correct type of writable media.
2358             //
2359 
2360             // 4 bytes header + 2048 bytes layer descriptor
2361             UByteArray data;
2362             if( readDvdStructure( data ) ) {
2363                 switch( data[4]&0xF0 ) {
2364                 case 0x00: m = MEDIA_DVD_ROM; break;
2365                 case 0x10: m = MEDIA_DVD_RAM; break;
2366                 case 0x20: m = MEDIA_DVD_R; break; // there seems to be no value for DVD-R DL, it reports DVD-R
2367                 case 0x30: m = MEDIA_DVD_RW; break;
2368                 case 0x40: m = MEDIA_HD_DVD_ROM; break;
2369                 case 0x50: m = MEDIA_HD_DVD_R; break;
2370                 case 0x60: m = MEDIA_HD_DVD_RAM; break;
2371                 case 0x90: m = MEDIA_DVD_PLUS_RW; break;
2372                 case 0xA0: m = MEDIA_DVD_PLUS_R; break;
2373                 case 0xE0: m = MEDIA_DVD_PLUS_R_DL; break;
2374                 default:
2375                     qDebug() << "(K3b::Device::Device) unknown dvd media type: " << QString::number(data[4]&0xF0, 8);
2376                     break; // unknown
2377                 }
2378             }
2379         }
2380 
2381         if( m & (MEDIA_UNKNOWN|MEDIA_BD_ROM) ) {
2382             //
2383             // We prefer the mediatype as reported by the media since this way
2384             // even ROM drives may report the correct type of writable media.
2385             //
2386 
2387             UByteArray data;
2388             if( readDiscStructure( data, 1, 0 ) ) {
2389                 if( data.size() > 4+12 &&
2390                     data[4+8] == 'B' &&  data[4+9] == 'D' ) {
2391                     switch( data[4+10] ) {
2392                     case 'O': m = MEDIA_BD_ROM; break;
2393                     case 'W': m = MEDIA_BD_RE; break;
2394                     case 'R': m = MEDIA_BD_R; break;
2395                     }
2396                 }
2397             }
2398         }
2399 
2400         //
2401         // Only old CD or DVD devices do not report a current profile
2402         // or report CD-ROM profile for all CD types
2403         //
2404         if( m & (MEDIA_UNKNOWN|MEDIA_CD_ROM) ) {
2405             UByteArray data;
2406             if( readTocPmaAtip( data, 4, false, 0 ) ) {
2407                 if( (data[6]>>6)&1 )
2408                     m = MEDIA_CD_RW;
2409                 else
2410                     m = MEDIA_CD_R;
2411             }
2412             else
2413                 m = MEDIA_CD_ROM;
2414         }
2415     }
2416 
2417     return m;
2418 }
2419 
2420 
2421 bool K3b::Device::Device::readSectorsRaw( unsigned char *buf, int start, int count ) const
2422 {
2423     return readCd( buf, count*2352,
2424                    0,      // all sector types
2425                    false,  // no dap
2426                    start,
2427                    count,
2428                    true, // SYNC
2429                    true, // HEADER
2430                    true, // SUBHEADER
2431                    true, // USER DATA
2432                    true, // EDC/ECC
2433                    0,    // no c2 info
2434                    0 );
2435 }
2436 
2437 
2438 
2439 void K3b::Device::Device::checkForJustLink()
2440 {
2441     UByteArray ricoh;
2442     if( modeSense( ricoh, 0x30 ) ) {
2443 
2444         //
2445         // 8 byte mode header + 6 byte page data
2446         //
2447 
2448         if( ricoh.size() >= 14 ) {
2449             ricoh_mode_page_30* rp = (ricoh_mode_page_30*)(ricoh.data()+8);
2450             d->burnfree = rp->BUEFS;
2451         }
2452     }
2453 }
2454 
2455 
2456 void K3b::Device::Device::checkFeatures()
2457 {
2458     unsigned char header[1024];
2459     ::memset( header, 0, 1024 );
2460 
2461     ScsiCommand cmd( this );
2462     cmd[0] = MMC_GET_CONFIGURATION;
2463     cmd[1] = 2;
2464     cmd[9] = 0;      // Necessary to set the proper command length
2465 
2466 
2467     //
2468     // CD writing features
2469     //
2470     cmd[2] = FEATURE_CD_MASTERING>>8;
2471     cmd[3] = FEATURE_CD_MASTERING;
2472     cmd[8] = 8+8;
2473     if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
2474         unsigned int len = from4Byte( header );
2475         if( len >= 12 ) {
2476             qDebug() << "(K3b::Device::Device) " << blockDeviceName() << " feature: " << "CD Mastering";
2477 #ifdef WORDS_BIGENDIAN
2478             struct cd_mastering_feature {
2479                 unsigned char reserved1 : 1;
2480                 unsigned char BUF       : 1;  // Burnfree
2481                 unsigned char SAO       : 1;  // Session At Once writing
2482                 unsigned char raw_ms    : 1;  // Writing Multisession in Raw Writing Mode
2483                 unsigned char raw       : 1;  // Writing in WRITINGMODE_RAW mode
2484                 unsigned char testwrite : 1;  // Simulation write support
2485                 unsigned char cd_rw     : 1;  // CD-RW support
2486                 unsigned char rw_sub    : 1;  // Write R-W sub channels with user data
2487                 unsigned char max_cue_length[3];
2488             };
2489 #else
2490             struct cd_mastering_feature {
2491                 unsigned char rw_sub    : 1;  // Write R-W sub channels with user data
2492                 unsigned char cd_rw     : 1;  // CD-RW support
2493                 unsigned char testwrite : 1;  // Simulation write support
2494                 unsigned char raw       : 1;  // Writing in WRITINGMODE_RAW mode
2495                 unsigned char raw_ms    : 1;  // Writing Multisession in Raw Writing Mode
2496                 unsigned char SAO       : 1;  // Session At Once writing
2497                 unsigned char BUF       : 1;  // Burnfree
2498                 unsigned char reserved1 : 1;
2499                 unsigned char max_cue_length[3];
2500             };
2501 #endif
2502 
2503             struct cd_mastering_feature* p = (struct cd_mastering_feature*)&header[12];
2504             if( p->BUF ) d->burnfree = true;
2505             d->writeCapabilities |= MEDIA_CD_R;
2506             if( p->cd_rw )
2507                 d->writeCapabilities |= MEDIA_CD_RW;
2508 //       if( p->WRITINGMODE_SAO ) d->writeModes |= WRITINGMODE_SAO;
2509 //       if( p->raw || p->raw_ms ) d->writeModes |= WRITINGMODE_RAW;  // WRITINGMODE_RAW16 always supported when raw is supported?
2510         }
2511     }
2512 
2513     cmd[2] = FEATURE_CD_TRACK_AT_ONCE>>8;
2514     cmd[3] = FEATURE_CD_TRACK_AT_ONCE;
2515     cmd[8] = 8+8;
2516     if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
2517         unsigned int len = from4Byte( header );
2518         if( len >= 12 ) {
2519             qDebug() << "(K3b::Device::Device) " << blockDeviceName() << " feature: " << "CD Track At Once";
2520 #ifdef WORDS_BIGENDIAN
2521             struct cd_track_at_once_feature {
2522                 unsigned char reserved1 : 1;
2523                 unsigned char BUF       : 1;  // Burnfree
2524                 unsigned char reserved2 : 1;
2525                 unsigned char rw_raw    : 1;  // Writing R-W subcode in Raw mode
2526                 unsigned char rw_pack   : 1;  // Writing R-W subcode in Packet mode
2527                 unsigned char testwrite : 1;  // Simulation write support
2528                 unsigned char cd_rw     : 1;  // CD-RW support
2529                 unsigned char rw_sub    : 1;  // Write R-W sub channels with user data
2530                 unsigned char reserved3;
2531                 unsigned char data_type[2];
2532             };
2533 #else
2534             struct cd_track_at_once_feature {
2535                 unsigned char rw_sub    : 1;  // Write R-W sub channels with user data
2536                 unsigned char cd_rw     : 1;  // CD-RW support
2537                 unsigned char testwrite : 1;  // Simulation write support
2538                 unsigned char rw_pack   : 1;  // Writing R-W subcode in Packet mode
2539                 unsigned char rw_raw    : 1;  // Writing R-W subcode in Raw mode
2540                 unsigned char reserved2 : 1;
2541                 unsigned char BUF       : 1;  // Burnfree
2542                 unsigned char reserved1 : 1;
2543                 unsigned char reserved3;
2544                 unsigned char data_type[2];
2545             };
2546 #endif
2547 
2548             struct cd_track_at_once_feature* p = (struct cd_track_at_once_feature*)&header[12];
2549             d->writeModes |= WRITINGMODE_TAO;
2550             if( p->BUF ) d->burnfree = true;
2551             d->writeCapabilities |= MEDIA_CD_R;
2552             if( p->cd_rw )
2553                 d->writeCapabilities |= MEDIA_CD_RW;
2554 
2555             // is the following correct? What exactly does rw_sub tell us?
2556 //       if( d->writeModes & WRITINGMODE_RAW ) {
2557 //  if( p->rw_raw ) d->writeModes |= WRITINGMODE_RAW_R96R;
2558 //  if( p->rw_pack ) d->writeModes |= WRITINGMODE_RAW_R96P;
2559 //       }
2560 
2561 //       // check the data types for 1, 2, and 3 (raw16, raw96p, and raw96r)
2562 //        debugBitfield( p->data_type, 2 );
2563 //       if( d->writeModes & WRITINGMODE_RAW ) {
2564 //  if( p->data_type[1] & 0x20 ) d->writeModes |= WRITINGMODE_RAW_R16;
2565 //  if( p->data_type[1] & 0x40 ) d->writeModes |= WRITINGMODE_RAW_R96P;
2566 //  if( p->data_type[1] & 0x80 ) d->writeModes |= WRITINGMODE_RAW_R96R;
2567 //       }
2568         }
2569     }
2570 
2571     cmd[2] = FEATURE_CD_RW_MEDIA_WRITE_SUPPORT>>8;
2572     cmd[3] = FEATURE_CD_RW_MEDIA_WRITE_SUPPORT;
2573     cmd[8] = 8+8;
2574     if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
2575         unsigned int len = from4Byte( header );
2576         if( len >= 12 ) {
2577             qDebug() << "(K3b::Device::Device) " << blockDeviceName() << " feature: " << "CD-RW Media Write Support";
2578             d->writeCapabilities |= (MEDIA_CD_R|MEDIA_CD_RW);
2579         }
2580     }
2581 
2582 
2583     //
2584     // DVD-ROM
2585     //
2586     // FIXME: since MMC5 the feature descr. is 8 bytes in length including a dvd dl read bit at byte 6
2587     cmd[2] = FEATURE_DVD_READ>>8;
2588     cmd[3] = FEATURE_DVD_READ;
2589     cmd[8] = 8+8;
2590     if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
2591         unsigned int len = from4Byte( header );
2592         if( len >= 12 ) {
2593             qDebug() << "(K3b::Device::Device) " << blockDeviceName() << " feature: " << "DVD Read (MMC5)";
2594             d->readCapabilities |= MEDIA_DVD_ROM;
2595             if( header[8+6] & 0x1 )
2596                 d->readCapabilities |= MEDIA_WRITABLE_DVD_DL;
2597         }
2598     }
2599     else {
2600         // retry with pre-MMC5 length
2601         cmd[8] = 8+4;
2602         if( !cmd.transport( TR_DIR_READ, header, 12 ) ) {
2603             unsigned int len = from4Byte( header );
2604             if( len >= 8 ) {
2605                 qDebug() << "(K3b::Device::Device) " << blockDeviceName() << " feature: " << "DVD Read (pre-MMC5)";
2606                 d->readCapabilities |= MEDIA_DVD_ROM;
2607             }
2608         }
2609     }
2610 
2611     //
2612     // DVD+R(W) writing features
2613     //
2614     cmd[2] = FEATURE_DVD_PLUS_R>>8;
2615     cmd[3] = FEATURE_DVD_PLUS_R;
2616     cmd[8] = 8+8;
2617     if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
2618         unsigned int len = from4Byte( header );
2619         if( len >= 12 ) {
2620             qDebug() << "(K3b::Device::Device) " << blockDeviceName() << " feature: " << "DVD+R";
2621             d->readCapabilities |= MEDIA_DVD_PLUS_R;
2622             if( header[12] & 0x1 )
2623                 d->writeCapabilities |= MEDIA_DVD_PLUS_R;
2624         }
2625     }
2626 
2627     cmd[2] = FEATURE_DVD_PLUS_RW>>8;
2628     cmd[3] = FEATURE_DVD_PLUS_RW;
2629     cmd[8] = 8+8;
2630     if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
2631         unsigned int len = from4Byte( header );
2632         if( len >= 12 ) {
2633             qDebug() << "(K3b::Device::Device) " << blockDeviceName() << " feature: " << "DVD+RW";
2634 #ifdef WORDS_BIGENDIAN
2635             struct dvd_plus_rw_feature {
2636                 unsigned char reserved1   : 7;
2637                 unsigned char write       : 1;
2638                 unsigned char reserved2   : 6;
2639                 unsigned char quick_start : 1;
2640                 unsigned char close_only  : 1;
2641                 // and some stuff we do not use here...
2642             };
2643 #else
2644             struct dvd_plus_rw_feature {
2645                 unsigned char write       : 1;
2646                 unsigned char reserved1   : 7;
2647                 unsigned char close_only  : 1;
2648                 unsigned char quick_start : 1;
2649                 unsigned char reserved2   : 6;
2650                 // and some stuff we do not use here...
2651             };
2652 #endif
2653 
2654             struct dvd_plus_rw_feature* p = (struct dvd_plus_rw_feature*)&header[12];
2655             d->readCapabilities |= MEDIA_DVD_PLUS_RW;
2656             if( p->write )
2657                 d->writeCapabilities |= MEDIA_DVD_PLUS_RW;
2658         }
2659     }
2660 
2661 
2662     // some older DVD-ROM drives claim to support DVD+R DL
2663     if( d->writeCapabilities & MEDIA_DVD_PLUS_R ) {
2664         cmd[2] = FEATURE_DVD_PLUS_RW_DUAL_LAYER>>8;
2665         cmd[3] = FEATURE_DVD_PLUS_RW_DUAL_LAYER;
2666         cmd[8] = 8+8;
2667         if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
2668             unsigned int len = from4Byte( header );
2669             if( len >= 12 ) {
2670                 qDebug() << "(K3b::Device::Device) " << blockDeviceName() << " feature: " << "DVD+RW Double Layer";
2671                 d->readCapabilities |= MEDIA_DVD_PLUS_RW_DL;
2672                 if( header[12] & 0x1 )
2673                     d->writeCapabilities |= MEDIA_DVD_PLUS_RW_DL;
2674             }
2675         }
2676 
2677         cmd[2] = FEATURE_DVD_PLUS_R_DUAL_LAYER>>8;
2678         cmd[3] = FEATURE_DVD_PLUS_R_DUAL_LAYER;
2679         cmd[8] = 8+8;
2680         if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
2681             unsigned int len = from4Byte( header );
2682             if( len >= 12 ) {
2683                 qDebug() << "(K3b::Device::Device) " << blockDeviceName() << " feature: " << "DVD+R Double Layer";
2684                 d->readCapabilities |= MEDIA_DVD_PLUS_R_DL;
2685                 if( header[12] & 0x1 )
2686                     d->writeCapabilities |= MEDIA_DVD_PLUS_R_DL;
2687             }
2688         }
2689     }
2690 
2691 
2692     //
2693     // Blue Ray
2694     //
2695     // We do not care for the different BD classes and versions here
2696     //
2697     cmd[2] = FEATURE_BD_READ>>8;
2698     cmd[3] = FEATURE_BD_READ;
2699     cmd[8] = 8+32;
2700     if( !cmd.transport( TR_DIR_READ, header, 40 ) ) {
2701         unsigned int len = from4Byte( header );
2702         if( len >= 36 ) {
2703             qDebug() << "(K3b::Device::Device) " << blockDeviceName() << " feature: " << "BD Read";
2704             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] )
2705                 d->readCapabilities |= MEDIA_BD_RE;
2706             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] )
2707                 d->readCapabilities |= MEDIA_BD_R;
2708             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] )
2709                 d->readCapabilities |= MEDIA_BD_ROM;
2710         }
2711     }
2712 
2713     cmd[2] = FEATURE_BD_WRITE>>8;
2714     cmd[3] = FEATURE_BD_WRITE;
2715     cmd[8] = 8+24;
2716     if( !cmd.transport( TR_DIR_READ, header, 32 ) ) {
2717         unsigned int len = from4Byte( header );
2718         if( len >= 28 ) {
2719             qDebug() << "(K3b::Device::Device) " << blockDeviceName() << " feature: " << "BD Write";
2720             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] )
2721                 d->writeCapabilities |= MEDIA_BD_RE;
2722             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] ) {
2723                 d->writeCapabilities |= (MEDIA_BD_R|MEDIA_BD_R_SRM);
2724                 d->writeModes |= WRITINGMODE_SRM;
2725 
2726                 cmd[2] = FEATURE_BD_PSEUDO_OVERWRITE>>8;
2727                 cmd[3] = FEATURE_BD_PSEUDO_OVERWRITE;
2728                 cmd[8] = 8+8;
2729                 if( !cmd.transport( TR_DIR_READ, header, 8+8 ) ) {
2730                     unsigned int len = from4Byte( header );
2731                     if( len >= 4+8 ) {
2732                         d->writeCapabilities |= MEDIA_BD_R_SRM_POW;
2733                         d->writeModes |= WRITINGMODE_SRM_POW;
2734                     }
2735                 }
2736 
2737                 cmd[2] = FEATURE_RANDOM_WRITABLE>>8;
2738                 cmd[3] = FEATURE_RANDOM_WRITABLE;
2739                 cmd[8] = 8+16;
2740                 if( !cmd.transport( TR_DIR_READ, header, 8+16 ) ) {
2741                     unsigned int len = from4Byte( header );
2742                     if( len >= 4+16 ) {
2743                         d->writeCapabilities |= MEDIA_BD_R_RRM;
2744                         d->writeModes |= WRITINGMODE_RRM;
2745                     }
2746                 }
2747             }
2748         }
2749     }
2750 
2751 
2752 
2753     //
2754     // DVD-R(W)
2755     //
2756     cmd[2] = FEATURE_DVD_R_RW_WRITE>>8;
2757     cmd[3] = FEATURE_DVD_R_RW_WRITE;
2758     cmd[8] = 16;
2759     if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
2760         unsigned int len = from4Byte( header );
2761         if( len >= 12 ) {
2762             qDebug() << "(K3b::Device::Device) " << blockDeviceName() << " feature: " << "DVD-R/-RW Write";
2763 #ifdef WORDS_BIGENDIAN
2764             struct dvd_r_rw_write_feature {
2765                 unsigned char reserved1 : 1;
2766                 unsigned char BUF       : 1;  // Burnfree
2767                 unsigned char reserved2 : 2;
2768                 unsigned char RDL       : 1;
2769                 unsigned char testwrite : 1;  // Simulation write support
2770                 unsigned char dvd_rw    : 1;  // DVD-RW Writing
2771                 unsigned char reserved3 : 1;
2772                 unsigned char reserved4[3];
2773             };
2774 #else
2775             struct dvd_r_rw_write_feature {
2776                 unsigned char reserved3 : 1;
2777                 unsigned char dvd_rw    : 1;  // DVD-RW Writing
2778                 unsigned char testwrite : 1;  // Simulation write support
2779                 unsigned char RDL       : 1;
2780                 unsigned char reserved2 : 2;
2781                 unsigned char BUF       : 1;  // Burnfree
2782                 unsigned char reserved1 : 1;
2783                 unsigned char reserved4[3];
2784             };
2785 #endif
2786 
2787             struct dvd_r_rw_write_feature* p = (struct dvd_r_rw_write_feature*)&header[12];
2788             if( p->BUF ) d->burnfree = true;
2789             d->writeCapabilities |= (MEDIA_DVD_R|MEDIA_DVD_R_SEQ);
2790             if( p->dvd_rw )
2791                 d->writeCapabilities |= (MEDIA_DVD_RW|MEDIA_DVD_RW_SEQ);
2792             if( p->RDL )
2793                 d->writeCapabilities |= (MEDIA_DVD_R_DL|MEDIA_DVD_R_DL_SEQ);
2794 
2795             d->dvdMinusTestwrite = p->testwrite;
2796         }
2797     }
2798 
2799 
2800     //
2801     // DVD-RW restricted overwrite check
2802     //
2803     cmd[2] = FEATURE_RIGID_RESTRICTED_OVERWRITE>>8;
2804     cmd[3] = FEATURE_RIGID_RESTRICTED_OVERWRITE;
2805     cmd[8] = 16;
2806     if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
2807         unsigned int len = from4Byte( header );
2808         if( len >= 12 ) {
2809             qDebug() << "(K3b::Device::Device) " << blockDeviceName() << " feature: " << "Rigid Restricted Overwrite";
2810             d->writeModes |= WRITINGMODE_RES_OVWR;
2811             d->writeCapabilities |= (MEDIA_DVD_RW|MEDIA_DVD_RW_OVWR);
2812         }
2813     }
2814 
2815 
2816     //
2817     // DVD-R Dual Layer Layer
2818     //
2819     cmd[2] = FEATURE_LAYER_JUMP_RECORDING>>8;
2820     cmd[3] = FEATURE_LAYER_JUMP_RECORDING;
2821     cmd[8] = 12;
2822     if( !cmd.transport( TR_DIR_READ, header, 12 ) ) {
2823         // Now the jump feature is longer than 4 bytes but we don't need the link sizes.
2824         unsigned int len = from4Byte( header );
2825         if( len >= 8 ) {
2826             qDebug() << "(K3b::Device::Device) " << blockDeviceName() << " feature: " << "Layer Jump Recording";
2827             d->writeCapabilities |= (MEDIA_DVD_R_DL|MEDIA_DVD_R_DL_JUMP);
2828             d->writeModes |= WRITINGMODE_LAYER_JUMP;
2829         }
2830     }
2831 
2832 
2833     //
2834     // HD-DVD-ROM
2835     //
2836     cmd[2] = FEATURE_HD_DVD_READ>>8;
2837     cmd[3] = FEATURE_HD_DVD_READ;
2838     cmd[8] = 16;
2839     if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
2840         unsigned int len = from4Byte( header );
2841         if( len >= 12 ) {
2842             qDebug() << "(K3b::Device::Device) " << blockDeviceName() << " feature: " << "HD-DVD Read";
2843             d->readCapabilities |= MEDIA_HD_DVD_ROM;
2844             if( header[8+4] & 0x1 )
2845                 d->readCapabilities |= MEDIA_HD_DVD_R;
2846             if( header[8+6] & 0x1 )
2847                 d->readCapabilities |= MEDIA_HD_DVD_RAM;
2848         }
2849     }
2850 
2851 
2852     //
2853     // HD-DVD-R(AM)
2854     //
2855     cmd[2] = FEATURE_HD_DVD_WRITE>>8;
2856     cmd[3] = FEATURE_HD_DVD_WRITE;
2857     cmd[8] = 16;
2858     if( !cmd.transport( TR_DIR_READ, header, 16 ) ) {
2859         unsigned int len = from4Byte( header );
2860         if( len >= 12 ) {
2861             qDebug() << "(K3b::Device::Device) " << blockDeviceName() << " feature: " << "HD-DVD Write";
2862             if( header[8+4] & 0x1 )
2863                 d->writeCapabilities |= MEDIA_HD_DVD_R;
2864             if( header[8+6] & 0x1 )
2865                 d->writeCapabilities |= MEDIA_HD_DVD_RAM;
2866         }
2867     }
2868 
2869 
2870 
2871     //
2872     // Get the profiles
2873     //
2874     // the max len of the returned data is 8 (header) + 4 (feature) + 255 (additional length)
2875     //
2876     cmd[2] = FEATURE_PROFILE_LIST>>8;
2877     cmd[3] = FEATURE_PROFILE_LIST;
2878     cmd[8] = 12; // get the number of returned profiles first
2879     if( !cmd.transport( TR_DIR_READ, header, 12 ) ) {
2880         unsigned int len = from4Byte( header ) + 4;
2881         if( len >= 12 ) {
2882             cmd[7] = len>>8;
2883             cmd[8] = len;
2884             if( !cmd.transport( TR_DIR_READ, header, len ) ) {
2885                 int featureLen( header[11] );
2886                 for( int j = 0; j < featureLen; j+=4 ) {
2887                     short profile = from2Byte( &header[12+j] );
2888 
2889                     switch (profile) {
2890                     case 0x08:
2891                         d->supportedProfiles |= MEDIA_CD_ROM;
2892                         break;
2893                     case 0x09:
2894                         d->supportedProfiles |= MEDIA_CD_R;
2895                         break;
2896                     case 0x0A:
2897                         d->supportedProfiles |= MEDIA_CD_RW;
2898                         break;
2899                     case 0x10:
2900                         d->supportedProfiles |= MEDIA_DVD_ROM;
2901                         //      d->readCapabilities |= MEDIA_DVD_ROM;
2902                         break;
2903                     case 0x11:
2904                         d->supportedProfiles |= MEDIA_DVD_R_SEQ;
2905                         //      d->writeCapabilities |= (MEDIA_DVD_R|MEDIA_DVD_R_SEQ);
2906                         break;
2907                     case 0x12:
2908                         d->supportedProfiles |= MEDIA_DVD_RAM;
2909 //          d->readCapabilities |= (MEDIA_DVD_RAM|MEDIA_DVD_ROM);
2910 //          d->writeCapabilities |= MEDIA_DVD_RAM;
2911                         break;
2912                     case 0x13:
2913                         d->supportedProfiles |= MEDIA_DVD_RW_OVWR;
2914                         //      d->writeCapabilities |= (MEDIA_DVD_RW|MEDIA_DVD_RW_OVWR);
2915                         break;
2916                     case 0x14:
2917                         d->supportedProfiles |= MEDIA_DVD_RW_SEQ;
2918                         //      d->writeCapabilities |= (MEDIA_DVD_RW|MEDIA_DVD_R|MEDIA_DVD_RW_SEQ|MEDIA_DVD_R_SEQ);
2919                         break;
2920                     case 0x15:
2921                         d->supportedProfiles |= MEDIA_DVD_R_DL_SEQ;
2922                         //      d->writeCapabilities |= (MEDIA_DVD_R|MEDIA_DVD_R_DL|MEDIA_DVD_R_SEQ|MEDIA_DVD_R_DL_SEQ);
2923                         break;
2924                     case 0x16:
2925                         d->supportedProfiles |= MEDIA_DVD_R_DL_JUMP;
2926                         //      d->writeCapabilities |= (MEDIA_DVD_R|MEDIA_DVD_R_DL||MEDIA_DVD_R_DL_JUMP);
2927                         break;
2928                     case 0x1A:
2929                         d->supportedProfiles |= MEDIA_DVD_PLUS_RW;
2930                         //      d->writeCapabilities |= MEDIA_DVD_PLUS_RW;
2931                         break;
2932                     case 0x1B:
2933                         d->supportedProfiles |= MEDIA_DVD_PLUS_R;
2934                         //      d->writeCapabilities |= MEDIA_DVD_PLUS_R;
2935                         break;
2936                     case 0x2A:
2937                         d->supportedProfiles |= MEDIA_DVD_PLUS_RW_DL;
2938                         //      d->writeCapabilities |= MEDIA_DVD_PLUS_RW_DL;
2939                         break;
2940                     case 0x2B:
2941                         d->supportedProfiles |= MEDIA_DVD_PLUS_R_DL;
2942                         //      d->writeCapabilities |= MEDIA_DVD_PLUS_R_DL;
2943                         break;
2944                     case 0x40:
2945                         d->supportedProfiles |= MEDIA_BD_ROM;
2946                         break;
2947                     case 0x41:
2948                         d->supportedProfiles |= MEDIA_BD_R_SRM;
2949                         break;
2950                     case 0x42:
2951                         d->supportedProfiles |= MEDIA_BD_R_RRM;
2952                         break;
2953                     case 0x43:
2954                         d->supportedProfiles |= MEDIA_BD_RE;
2955                         break;
2956                     case 0x50:
2957                         d->supportedProfiles |= MEDIA_HD_DVD_ROM;
2958                         break;
2959                     case 0x51:
2960                         d->supportedProfiles |= MEDIA_HD_DVD_R;
2961                         break;
2962                     case 0x52:
2963                         d->supportedProfiles |= MEDIA_HD_DVD_RAM;
2964                         break;
2965                     default:
2966                         qDebug() << "(K3b::Device::Device) " << blockDeviceName() << " unknown profile: "
2967                                  << profile << Qt::endl;
2968                     }
2969                 }
2970 
2971                 // some older DVD-ROM drives claim to support DVD+R DL
2972                 if( !(d->supportedProfiles & MEDIA_DVD_PLUS_R) ) {
2973                     // remove DVD+R DL capability
2974                     //    d->writeCapabilities &= ~MEDIA_DVD_PLUS_R_DL;
2975                     d->supportedProfiles &= ~MEDIA_DVD_PLUS_R_DL;
2976                 }
2977             }
2978         }
2979     }
2980 }
2981 
2982 
2983 void K3b::Device::Device::checkFor2AFeatures()
2984 {
2985     UByteArray mm_cap_buffer;
2986 
2987     if( modeSense( mm_cap_buffer, 0x2A ) ) {
2988         mm_cap_page_2A* mm_p = (mm_cap_page_2A*)(mm_cap_buffer.data()+8);
2989         if( mm_p->BUF )
2990             d->burnfree = true;
2991 
2992         if( mm_p->cd_r_write )
2993             d->writeCapabilities |= MEDIA_CD_R;
2994         else
2995             d->writeCapabilities &= ~MEDIA_CD_R;
2996 
2997         if( mm_p->cd_rw_write )
2998             d->writeCapabilities |= MEDIA_CD_RW;
2999         else
3000             d->writeCapabilities &= ~MEDIA_CD_RW;
3001 
3002         if( mm_p->dvd_r_write )
3003             d->writeCapabilities |= MEDIA_DVD_R;
3004         else
3005             d->writeCapabilities &= ~MEDIA_DVD_R;
3006 
3007         if( mm_p->dvd_rom_read || mm_p->dvd_r_read )
3008             d->readCapabilities |= MEDIA_DVD_ROM;
3009 
3010         d->maxReadSpeed = from2Byte(mm_p->max_read_speed);
3011         d->bufferSize = from2Byte( mm_p->buffer_size );
3012     }
3013     else {
3014         qDebug() << "(K3b::Device::Device) " << blockDeviceName() << ": read mode page 2A failed!";
3015     }
3016 }
3017 
3018 
3019 void K3b::Device::Device::checkWritingModes()
3020 {
3021     // if the device is already opened we do not close it
3022     // to allow fast multiple method calls in a row
3023     bool needToClose = !isOpen();
3024 
3025     if( !open() )
3026         return;
3027 
3028     // header size is 8
3029     UByteArray buffer;
3030 
3031     if( !modeSense( buffer, 0x05 ) ) {
3032         qDebug() << "(K3b::Device::Device) " << blockDeviceName() << ": modeSense 0x05 failed!" << Qt::endl
3033                  << "(K3b::Device::Device) " << blockDeviceName() << ": Cannot check write modes." << Qt::endl;
3034     }
3035     else if( buffer.size() < 18 ) { // 8 bytes header + 10 bytes used modepage
3036         qDebug() << "(K3b::Device::Device) " << blockDeviceName() << ": Missing modepage 0x05 data." << Qt::endl
3037                  << "(K3b::Device::Device) " << blockDeviceName() << ": Cannot check write modes." << Qt::endl;
3038     }
3039     else {
3040         qDebug() << "(K3b::Device::Device) " << blockDeviceName() << ": buffer.size(): " << buffer.size();
3041 
3042         wr_param_page_05* mp = (struct wr_param_page_05*)(buffer.data()+8);
3043 
3044         // reset some stuff to be on the safe side
3045         mp->PS = 0;
3046         mp->BUFE = 0;
3047         mp->multi_session = 0;
3048         mp->test_write = 0;
3049         mp->LS_V = 0;
3050         mp->copy = 0;
3051         mp->fp = 0;
3052         mp->host_appl_code= 0;
3053         mp->session_format = 0;
3054         mp->audio_pause_len[0] = 0;
3055         mp->audio_pause_len[1] = 150;
3056 
3057         // WRITINGMODE_TAO
3058         mp->write_type = 0x01;  // Track-at-once
3059         mp->track_mode = 4;     // MMC-4 says: 5, cdrecord uses 4 ?
3060         mp->dbtype = 8;         // Mode 1
3061 
3062         //    qDebug() << "(K3b::Device::Device) " << blockDeviceName() << ": modeselect WRITINGMODE_TAO data: ";
3063         //    debugBitfield( buffer.data(), buffer.size() );
3064 
3065 
3066         //
3067         // 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
3068         // requirement
3069         //
3070 
3071         qDebug() << "(K3b::Device::Device) " << blockDeviceName() << ": checking for TAO";
3072         if( modeSelect( buffer, 1, 0 ) ) {
3073             d->writeModes |= WRITINGMODE_TAO;
3074             d->writeCapabilities |= MEDIA_CD_R;
3075 
3076             // WRITINGMODE_SAO
3077             mp->write_type = 0x02; // Session-at-once
3078 
3079             qDebug() << "(K3b::Device::Device) " << blockDeviceName() << ": checking for SAO";
3080             if( modeSelect( buffer, 1, 0 ) )
3081                 d->writeModes |= WRITINGMODE_SAO;
3082 
3083 //       mp->dbtype = 1;        // Raw data with P and Q Sub-channel (2368 bytes)
3084 //       if( modeSelect( buffer, 1, 0 ) ) {
3085 //  d->writeModes |= WRITINGMODE_RAW_R16;
3086 //       }
3087 
3088             qDebug() << "(K3b::Device::Device) " << blockDeviceName() << ": checking for SAO_R96P";
3089             mp->dbtype = 2;        // Raw data with P-W Sub-channel (2448 bytes)
3090             if( modeSelect( buffer, 1, 0 ) ) {
3091                 d->writeModes |= WRITINGMODE_SAO_R96P;
3092             }
3093 
3094             qDebug() << "(K3b::Device::Device) " << blockDeviceName() << ": checking for SAO_R96R";
3095             mp->dbtype = 3;        // Raw data with P-W raw Sub-channel (2448 bytes)
3096             if( modeSelect( buffer, 1, 0 ) ) {
3097                 d->writeModes |= WRITINGMODE_SAO_R96R;
3098             }
3099 
3100             qDebug() << "(K3b::Device::Device) " << blockDeviceName() << ": checking for RAW_R16";
3101             // WRITINGMODE_RAW
3102             mp->write_type = 0x03; // WRITINGMODE_RAW
3103             mp->dbtype = 1;        // Raw data with P and Q Sub-channel (2368 bytes)
3104             if( modeSelect( buffer, 1, 0 ) ) {
3105                 d->writeModes |= WRITINGMODE_RAW;
3106                 d->writeModes |= WRITINGMODE_RAW_R16;
3107             }
3108 
3109             qDebug() << "(K3b::Device::Device) " << blockDeviceName() << ": checking for RAW_R96P";
3110             mp->dbtype = 2;        // Raw data with P-W Sub-channel (2448 bytes)
3111             if( modeSelect( buffer, 1, 0 ) ) {
3112                 d->writeModes |= WRITINGMODE_RAW;
3113                 d->writeModes |= WRITINGMODE_RAW_R96P;
3114             }
3115 
3116             qDebug() << "(K3b::Device::Device) " << blockDeviceName() << ": checking for RAW_R96R";
3117             mp->dbtype = 3;        // Raw data with P-W raw Sub-channel (2448 bytes)
3118             if( modeSelect( buffer, 1, 0 ) ) {
3119                 d->writeModes |= WRITINGMODE_RAW;
3120                 d->writeModes |= WRITINGMODE_RAW_R96R;
3121             }
3122         }
3123         else {
3124             qDebug() << "(K3b::Device::Device) " << blockDeviceName() << ": modeSelect with WRITINGMODE_TAO failed. No writer";
3125         }
3126     }
3127 
3128     if( needToClose )
3129         close();
3130 }
3131 
3132 
3133 int K3b::Device::Device::getMaxWriteSpeedVia2A() const
3134 {
3135     int ret = 0;
3136 
3137     UByteArray data;
3138 
3139     if( modeSense( data, 0x2A ) ) {
3140         mm_cap_page_2A* mm = (mm_cap_page_2A*)&data[8];
3141 
3142         // MMC1 used byte 18 and 19 for the max write speed
3143         if( data.size() > 19 )
3144             ret = from2Byte( mm->max_write_speed );
3145     }
3146 
3147     return ret;
3148 }
3149 
3150 
3151 int K3b::Device::Device::determineMaximalWriteSpeed() const
3152 {
3153     int ret = 0;
3154 
3155     if( mediaType() & MEDIA_CD_ALL ) {
3156         ret = getMaxWriteSpeedVia2A();
3157         if ( ret > 0 )
3158             return ret;
3159     }
3160 
3161     QList<int> list = determineSupportedWriteSpeeds();
3162     if( !list.isEmpty() ) {
3163         for( QList<int>::const_iterator it = list.constBegin(); it != list.constEnd(); ++it )
3164             ret = qMax( ret, *it );
3165     }
3166 
3167     if( ret > 0 )
3168         return ret;
3169     else
3170         return d->maxWriteSpeed;
3171 }
3172 
3173 
3174 QList<int> K3b::Device::Device::determineSupportedWriteSpeeds() const
3175 {
3176     QList<int> ret;
3177 
3178     if( burner() ) {
3179         //
3180         // Tests with all my drives resulted in 2A for CD and GET PERFORMANCE for DVD media
3181         // as the valid method of speed detection.
3182         //
3183         MediaType m = mediaType();
3184         if( m & MEDIA_CD_ALL ) {
3185             if( !getSupportedWriteSpeedsVia2A( ret, m ) )
3186                 getSupportedWriteSpeedsViaGP( ret, m );
3187 
3188             // restrict to max speed, although deprecated in MMC3 is still used everywhere and
3189             // cdrecord also uses it as the max writing speed.
3190             int max = 0;
3191             UByteArray data;
3192             if( modeSense( data, 0x2A ) && data.size() >= 8 ) {
3193                 const mm_cap_page_2A* mm = (mm_cap_page_2A const*)&data.at(8);
3194 
3195                 // MMC1 used byte 18 and 19 for the max write speed
3196                 if( data.size() > 19 )
3197                     max = from2Byte( mm->max_write_speed );
3198 
3199                 if( max > 0 ) {
3200                     while( !ret.isEmpty() && ret.last() > max ) {
3201                         qDebug() << "(K3b::Device::Device) " << blockDeviceName()
3202                                  << " writing speed " << ret.last() << " higher than max " << max << Qt::endl;
3203                         ret.pop_back();
3204                     }
3205                 }
3206             }
3207         }
3208         else {
3209             if( !getSupportedWriteSpeedsViaGP( ret, m ) )
3210                 getSupportedWriteSpeedsVia2A( ret, m );
3211         }
3212 
3213         // construct writing speeds for old devices
3214         if ( ret.isEmpty() && K3b::Device::isCdMedia( m ) ) {
3215             int max = getMaxWriteSpeedVia2A();
3216             for ( int i = 1; i <= max/SPEED_FACTOR_CD; i *= 2 ) {
3217                 ret.append( i * SPEED_FACTOR_CD );
3218             }
3219         }
3220     }
3221 
3222     return ret;
3223 }
3224 
3225 
3226 bool K3b::Device::Device::getSupportedWriteSpeedsVia2A(QList<int>& list,
3227                                                     MediaType mediaType) const
3228 {
3229     UByteArray/* QVarLengthArray<unsigned char> */ data;
3230     if (modeSense(data, 0x2A)) {
3231         mm_cap_page_2A* mm = (mm_cap_page_2A*)&data[8];
3232 
3233         // 8 bytes of MODE SENSE(10) header
3234         // 32 bytes offset of speed descriptor list in Mode page 2Ah
3235         // (MMC-3, table 361. Pages of MMC-1 and MMC-2 are smaller.)
3236         if (data.size() > 32 + 8/* replyLen */) {
3237             // we have descriptors
3238             unsigned int numDesc = from2Byte(mm->num_wr_speed_des);
3239 
3240             // Ensure number of descriptors claimed actually fits in the data
3241             // returned by the mode sense command.
3242             if (static_cast<int>(numDesc) > ((data.size() - 32 - 8) / 4))
3243                 numDesc = (data.size() - 32 - 8) / 4;
3244 
3245             cd_wr_speed_performance* wr =
3246                 (cd_wr_speed_performance*)mm->wr_speed_des;
3247 
3248             qDebug() << "(K3b::Device::Device) " << blockDeviceName()
3249                      << ":  Number of supported write speeds via 2A: "
3250                      << numDesc << Qt::endl;
3251 
3252             for (unsigned int i = 0; i < numDesc; ++i) {
3253                 int s = (int)from2Byte(wr[i].wr_speed_supp);
3254                 //
3255                 // some DVD writers report CD writing speeds here
3256                 // If that is the case we cannot rely on the reported speeds
3257                 // and need to use the values gained from GET PERFORMANCE.
3258                 //
3259                 if( isDvdMedia( mediaType ) && s < 1352 ) {
3260                     qDebug() << "(K3b::Device::Device) " << blockDeviceName()
3261                              << " Invalid DVD speed: " << s << " KB/s" << Qt::endl;
3262                     list.clear();
3263                     break;
3264                 }
3265                 else {
3266                     qDebug() << "(K3b::Device::Device) " << blockDeviceName()
3267                              << " : " << s << " KB/s" << Qt::endl;
3268 
3269                     if( isDvdMedia( mediaType ) )
3270                         s = fixupDvdWritingSpeed( s );
3271 
3272                     // sort the list
3273                     QList<int>::iterator it = list.begin();
3274                     while( it != list.end() && *it < s )
3275                         ++it;
3276                     list.insert( it, s );
3277                 }
3278             }
3279         }
3280     }
3281 
3282     return !list.isEmpty();
3283 }
3284 
3285 
3286 bool K3b::Device::Device::getSupportedWriteSpeedsViaGP( QList<int>& list, MediaType mediaType ) const
3287 {
3288     UByteArray data;
3289     if( getPerformance( data, 0x3, 0x0 ) && data.size() >= 8 ) {
3290         int numDesc = (data.size() - 8)/16;
3291         qDebug() << "(K3b::Device::Device) " << blockDeviceName()
3292                  << ":  Number of supported write speeds via GET PERFORMANCE: "
3293                  << numDesc << Qt::endl;
3294 
3295         for( int i = 0; i < numDesc; ++i ) {
3296             int s = from4Byte( &data[20+i*16] );
3297 
3298             // Looks as if the code below does not make sense with most drives
3299 //       if( !( data[4+i*16] & 0x2 ) ) {
3300 //  qDebug() << "(K3b::Device::Device) " << blockDeviceName()
3301 //         << " No write speed: " << s << " KB/s" << Qt::endl;
3302 //  continue;
3303 //       }
3304 
3305             if( isDvdMedia( mediaType ) && s < 1352 ) {
3306                 //
3307                 // Does this ever happen?
3308                 //
3309                 qDebug() << "(K3b::Device::Device) " << blockDeviceName()
3310                          << " Invalid DVD speed: " << s << " KB/s" << Qt::endl;
3311             }
3312             else {
3313                 qDebug() << "(K3b::Device::Device) " << blockDeviceName()
3314                          << " : " << s << " KB/s" << Qt::endl;
3315 
3316                 if( isDvdMedia( mediaType ) )
3317                     s = fixupDvdWritingSpeed( s );
3318 
3319                 QList<int>::iterator it = list.begin();
3320                 while( it != list.end() && *it < s )
3321                     ++it;
3322                 // the speed might already have been found in the 2a modepage
3323                 if( it == list.end() || *it != s )
3324                     list.insert( it, s );
3325             }
3326         }
3327     }
3328 
3329     return !list.isEmpty();
3330 }
3331 
3332 
3333 int K3b::Device::Device::getIndex( unsigned long lba ) const
3334 {
3335     // if the device is already opened we do not close it
3336     // to allow fast multiple method calls in a row
3337     bool needToClose = !isOpen();
3338 
3339     if( !open() )
3340         return -1;
3341 
3342     int ret = -1;
3343 
3344     //
3345     // first try readCd
3346     //
3347     unsigned char readData[16];
3348     ::memset( readData, 0, 16 );
3349 
3350     //
3351     // The index is found in the Mode-1 Q which occupies at least 9 out of 10 successive CD frames
3352     // It can be identified by ADR == 1
3353     //
3354     // So if the current sector does not provide Mode-1 Q subchannel we try the previous.
3355     //
3356 
3357     if( readCd( readData,
3358                 16,
3359                 1, // CD-DA
3360                 0, // no DAP
3361                 lba,
3362                 1,
3363                 false,
3364                 false,
3365                 false,
3366                 false,
3367                 false,
3368                 0,
3369                 2 // Q-Subchannel
3370             ) ) {
3371         // byte 0: 4 bits CONTROL (MSB) + 4 bits ADR (LSB)
3372         if( (readData[0]&0x0f) == 0x1 )
3373             ret = readData[2];
3374 
3375         // search previous sector for Mode1 Q Subchannel
3376         else if( readCd( readData,
3377                          16,
3378                          1, // CD-DA
3379                          0, // no DAP
3380                          lba-1,
3381                          1,
3382                          false,
3383                          false,
3384                          false,
3385                          false,
3386                          false,
3387                          0,
3388                          2 // Q-Subchannel
3389                      ) ) {
3390             if( (readData[0]&0x0f) == 0x1 )
3391                 ret = readData[2];
3392             else
3393                 ret = -2;
3394         }
3395     }
3396 
3397     else {
3398         qDebug() << "(K3b::Device::Device::getIndex) readCd failed. Trying seek.";
3399 
3400         UByteArray data;
3401         if( seek( lba ) && readSubChannel( data, 1, 0 ) ) {
3402             // byte 5: 4 bits ADR (MSB) + 4 bits CONTROL (LSB)
3403             if( data.size() > 7 && (data[5]>>4 & 0x0F) == 0x1 ) {
3404                 ret = data[7];
3405             }
3406             else if( seek( lba-1 ) && readSubChannel( data, 1, 0 ) ) {
3407                 if( data.size() > 7 && (data[5]>>4 & 0x0F) == 0x1 )
3408                     ret = data[7];
3409                 else
3410                     ret = -2;
3411             }
3412         }
3413         else
3414             qDebug() << "(K3b::Device::Device::getIndex) seek or readSubChannel failed.";
3415     }
3416 
3417     if( needToClose )
3418         close();
3419 
3420     return ret;
3421 }
3422 
3423 
3424 bool K3b::Device::Device::searchIndex0( unsigned long startSec,
3425                                       unsigned long endSec,
3426                                       long& pregapStart ) const
3427 {
3428     // if the device is already opened we do not close it
3429     // to allow fast multiple method calls in a row
3430     bool needToClose = !isOpen();
3431 
3432     if( !open() )
3433         return false;
3434 
3435     bool ret = false;
3436 
3437     int lastIndex = getIndex( endSec );
3438     if( lastIndex == 0 ) {
3439         // there is a pregap
3440         // let's find the position where the index turns to 0
3441         // we jump in 1 sec steps backwards until we find an index > 0
3442         unsigned long sector = endSec;
3443         while( lastIndex == 0 && sector > startSec ) {
3444             sector -= 75;
3445             if( sector < startSec )
3446                 sector = startSec;
3447             lastIndex = getIndex(sector);
3448         }
3449 
3450         if( lastIndex == 0 ) {
3451             qDebug() << "(K3b::Device::Device) warning: no index != 0 found.";
3452         }
3453         else {
3454             // search forward to the first index = 0
3455             while( getIndex( sector ) != 0 && sector < endSec )
3456                 sector++;
3457 
3458             pregapStart = sector;
3459             ret = true;
3460         }
3461     }
3462     else if( lastIndex > 0 ) {
3463         // no pregap
3464         pregapStart = -1;
3465         ret = true;
3466     }
3467 
3468     if( needToClose )
3469         close();
3470 
3471     return ret;
3472 }
3473 
3474 
3475 bool K3b::Device::Device::indexScan( K3b::Device::Toc& toc ) const
3476 {
3477     // if the device is already opened we do not close it
3478     // to allow fast multiple method calls in a row
3479     bool needToClose = !isOpen();
3480 
3481     if( !open() )
3482         return false;
3483 
3484     bool ret = true;
3485 
3486     for( Toc::iterator it = toc.begin(); it != toc.end(); ++it ) {
3487         Track& track = *it;
3488         if( track.type() == Track::TYPE_AUDIO ) {
3489             track.setIndices( QList<K3b::Msf>() );
3490             long index0 = -1;
3491             if( searchIndex0( track.firstSector().lba(), track.lastSector().lba(), index0 ) ) {
3492                 qDebug() << "(K3b::Device::Device) found index 0: " << index0;
3493             }
3494             if( index0 > 0 )
3495                 track.setIndex0( K3b::Msf( index0 - track.firstSector().lba() ) );
3496             else
3497                 track.setIndex0( 0 );
3498 
3499             if( index0 > 0 )
3500                 searchIndexTransitions( track.firstSector().lba(), index0-1, track );
3501             else
3502                 searchIndexTransitions( track.firstSector().lba(), track.lastSector().lba(), track );
3503         }
3504     }
3505 
3506     if( needToClose )
3507         close();
3508 
3509     return ret;
3510 }
3511 
3512 
3513 void K3b::Device::Device::searchIndexTransitions( long start, long end, K3b::Device::Track& track ) const
3514 {
3515     qDebug() << "(K3b::Device::Device) searching for index transitions between "
3516              << start << " and " << end << Qt::endl;
3517     int startIndex = getIndex( start );
3518     int endIndex = getIndex( end );
3519 
3520     if( startIndex < 0 || endIndex < 0 ) {
3521         qDebug() << "(K3b::Device::Device) could not retrieve index values.";
3522     }
3523     else {
3524         qDebug() << "(K3b::Device::Device) indices: " << start << " - " << startIndex
3525                  << " and " << end << " - " << endIndex << Qt::endl;
3526 
3527         if( startIndex != endIndex ) {
3528             if( start+1 == end ) {
3529                 QList<K3b::Msf> indices = track.indices();
3530                 qDebug() << "(K3b::Device::Device) found index transition: " << endIndex << " " << end;
3531                 while ( indices.count() < endIndex )
3532                     indices.append( K3b::Msf() );
3533                 // we save the index relative to the first sector
3534                 if (endIndex > 0 && endIndex < indices.size() + 1)
3535                     indices[endIndex - 1] = K3b::Msf(end) - track.firstSector();
3536                 track.setIndices( indices ); // FIXME: better API
3537             }
3538             else {
3539                 searchIndexTransitions( start, start+(end-start)/2, track );
3540                 searchIndexTransitions( start+(end-start)/2, end, track );
3541             }
3542         }
3543     }
3544 }
3545 
3546 
3547 int K3b::Device::Device::copyrightProtectionSystemType() const
3548 {
3549     UByteArray dvdheader;
3550     if( readDvdStructure( dvdheader, 0x1 ) ) {
3551         int ret = -1;
3552         if( dvdheader.size() >= 6 )
3553             ret = dvdheader[4];
3554         return ret;
3555     }
3556     else
3557         return -1;
3558 }
3559 
3560 
3561 bool K3b::Device::Device::getNextWritableAdress( unsigned int& lastSessionStart, unsigned int& nextWritableAdress ) const
3562 {
3563     bool success = false;
3564 
3565     // FIXME: add CD media handling
3566     int m = mediaType();
3567     if( m & MEDIA_DVD_ALL ) {
3568         // DVD+RW always returns complete
3569         if( m & (K3b::Device::MEDIA_DVD_PLUS_RW|K3b::Device::MEDIA_DVD_RW_OVWR) )
3570             return false;
3571 
3572         UByteArray data;
3573 
3574         if( readDiscInformation( data ) ) {
3575             disc_info_t* inf = (disc_info_t*)data.data();
3576 
3577             //
3578             // The state of the last session has to be "empty" (0x0) or "incomplete" (0x1)
3579             // The procedure here is taken from the dvd+rw-tools
3580             //
3581             if( !(inf->border & 0x2) ) {
3582                 // the incomplete track number is the first track in the last session (the empty session)
3583                 int nextTrack;
3584                 if (m == MEDIA_BD_R_SRM_POW)
3585                     nextTrack = inf->last_track_l | inf->last_track_m << 8;
3586                 else
3587                     nextTrack  = inf->first_track_l | inf->first_track_m << 8;
3588 
3589                 UByteArray trackData;
3590 
3591                 // Read start address of the incomplete track
3592                 if( readTrackInformation( trackData, 0x1, nextTrack ) ) {
3593                     if (m == MEDIA_BD_R_SRM_POW && (trackData[7] & 1))
3594                         nextWritableAdress = from4Byte(&trackData[12]);
3595                     else
3596                         nextWritableAdress = from4Byte( &trackData[8] );
3597 
3598                     if (m == MEDIA_BD_R_SRM_POW) {
3599                         lastSessionStart = 0;
3600                         success = true;
3601                     } else {
3602                         // Read start address of the first track in the last session
3603                         if (readTocPmaAtip(trackData, 0x1, false, 0x0)) {
3604                             lastSessionStart = from4Byte(&trackData[8]);
3605                             success = true;
3606                         }
3607                     }
3608                 }
3609             }
3610         }
3611     }
3612 
3613     return success;
3614 }
3615 
3616 
3617 int K3b::Device::Device::nextWritableAddress() const
3618 {
3619     UByteArray data;
3620     int nwa = -1;
3621 
3622     if( readDiscInformation( data ) ) {
3623         disc_info_t* inf = (disc_info_t*)data.data();
3624 
3625         //
3626         // The state of the last session has to be "empty" (0x0) or "incomplete" (0x1)
3627         // The procedure here is taken from the dvd+rw-tools
3628         //
3629         if( !(inf->border & 0x2) ) {
3630             // the incomplete track number is the first track in the last session (the empty session)
3631             int nextTrack = inf->first_track_l|inf->first_track_m<<8;
3632 
3633             UByteArray trackData;
3634 
3635             // Read start address of the incomplete track
3636             if( readTrackInformation( trackData, 0x1, nextTrack ) ) {
3637                 nwa = from4Byte( &trackData[8] );
3638             }
3639 
3640             // Read start address of the invisible track
3641             else if ( readTrackInformation( trackData, 0x1, 0xff ) ) {
3642                 nwa = from4Byte( &trackData[8] );
3643             }
3644         }
3645     }
3646 
3647     return nwa;
3648 }
3649 
3650 
3651 QByteArray K3b::Device::Device::mediaId( int mediaType ) const
3652 {
3653     QString id;
3654 
3655     if( mediaType & MEDIA_CD_ALL ) {
3656         // FIXME:
3657     }
3658 
3659     else if( mediaType & MEDIA_DVD_MINUS_ALL ) {
3660         UByteArray data;
3661         if( readDvdStructure( data, 0x0E ) ) {
3662             if( data[4+16] == 3 && data[4+24] == 4 ) {
3663                 id = QString::asprintf( "%6.6s%-6.6s", data.data()+4+17, data.data()+4+25 );
3664             }
3665         }
3666     }
3667 
3668     else if( mediaType & MEDIA_DVD_PLUS_ALL ) {
3669         UByteArray data;
3670         if( readDvdStructure( data, 0x11 ) ||
3671             readDvdStructure( data, 0x0 ) ) {
3672             id = QString::asprintf( "%8.8s/%3.3s", data.data()+23, data.data()+31 );
3673         }
3674     }
3675 
3676     else if( mediaType & MEDIA_BD_ALL ) {
3677         UByteArray data;
3678         if( readDiscStructure( data, 1, 0 ) ) {
3679             if( data[4+0] == 'D' && data[4+1] == 'I' )
3680                 id = QString::asprintf ("%6.6s/%-3.3s", data.data()+4+100, data.data()+4+106 );
3681         }
3682     }
3683 
3684     return id.toLatin1();
3685 }
3686 
3687 
3688 // int K3b::Device::Device::ioctl( int request, ... ) const
3689 // {
3690 //     int r = -1;
3691 // #if defined(Q_OS_LINUX) || defined(Q_OS_NETBSD)
3692 //     d->mutex.lock();
3693 
3694 //     va_list ap;
3695 //     va_start( ap, request );
3696 //     r = ::ioctl( d->deviceFd, request, ap );
3697 //     va_end( ap );
3698 
3699 //     d->mutex.unlock();
3700 // #endif
3701 //     return r;
3702 // }
3703 
3704 
3705 void K3b::Device::Device::usageLock() const
3706 {
3707     d->mutex.lock();
3708 }
3709 
3710 
3711 void K3b::Device::Device::usageUnlock() const
3712 {
3713     d->mutex.unlock();
3714 }