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

0001 /*
0002     SPDX-FileCopyrightText: 2003-2009 Sebastian Trueg <trueg@k3b.org>
0003     SPDX-FileCopyrightText: 2010 Michal Malek <michalm@jabster.pl>
0004     SPDX-FileCopyrightText: 1998-2009 Sebastian Trueg <trueg@k3b.org>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "k3bdevicemanager.h"
0010 #include "k3bdevice.h"
0011 #include "k3bdeviceglobals.h"
0012 #include "k3bscsicommand.h"
0013 #include "k3bmmc.h"
0014 
0015 #include <config-k3b.h>
0016 
0017 #include <KConfig>
0018 #include <KConfigGroup>
0019 
0020 #include <Solid/DeviceNotifier>
0021 #include <Solid/DeviceInterface>
0022 #include <Solid/OpticalDrive>
0023 #include <Solid/Block>
0024 #include <Solid/Device>
0025 #ifdef Q_OS_NETBSD
0026 #include <Solid/GenericInterface>
0027 #endif
0028 
0029 #include <QDebug>
0030 #include <QString>
0031 #include <QStringList>
0032 #include <QFile>
0033 #include <QFileInfo>
0034 #include <QRegExp>
0035 #include <QTemporaryFile>
0036 
0037 #include <iostream>
0038 #include <limits.h>
0039 #include <assert.h>
0040 
0041 #ifdef Q_OS_FREEBSD
0042 #include <sys/param.h>
0043 #include <sys/ucred.h>
0044 #include <osreldate.h>
0045 #endif
0046 
0047 #include <stdio.h>
0048 #include <stdlib.h>
0049 #include <fcntl.h>
0050 #include <unistd.h>
0051 #include <errno.h>
0052 #include <sys/stat.h>
0053 #include <sys/ioctl.h>
0054 
0055 #ifdef HAVE_RESMGR
0056 #include <resmgr.h>
0057 #endif
0058 
0059 #ifdef Q_OS_LINUX
0060 #include <sys/utsname.h>
0061 /* Fix definitions for 2.5 kernels */
0062 #include <linux/version.h>
0063 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,70)
0064 typedef unsigned char u8;
0065 #endif
0066 
0067 #undef __STRICT_ANSI__
0068 #include <asm/types.h>
0069 #define __STRICT_ANSI__
0070 
0071 #include <scsi/scsi.h>
0072 #include <linux/major.h>
0073 
0074 
0075 #ifndef SCSI_DISK_MAJOR
0076 #define SCSI_DISK_MAJOR(M) ((M) == SCSI_DISK0_MAJOR ||                  \
0077                             ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR) || \
0078                             ((M) >= SCSI_DISK8_MAJOR && (M) <= SCSI_DISK15_MAJOR))
0079 #endif
0080 
0081 #ifndef SCSI_BLK_MAJOR
0082 #define SCSI_BLK_MAJOR(M)                       \
0083     (SCSI_DISK_MAJOR(M)                         \
0084      || (M) == SCSI_CDROM_MAJOR)
0085 #endif
0086 
0087 #ifndef SCSI_GENERIC_MAJOR
0088 #define SCSI_GENERIC_MAJOR 21
0089 #endif
0090 
0091 #endif // Q_OS_LINUX
0092 
0093 
0094 #ifdef Q_OS_FREEBSD
0095 #include <cam/cam.h>
0096 #include <cam/scsi/scsi_pass.h>
0097 #include <camlib.h>
0098 #endif
0099 
0100 #ifdef Q_OS_NETBSD
0101 #include <sys/scsiio.h>
0102 #endif
0103 
0104 
0105 
0106 class K3b::Device::DeviceManager::Private
0107 {
0108 public:
0109     QList<Device*> allDevices;
0110     QList<Device*> cdReader;
0111     QList<Device*> cdWriter;
0112     QList<Device*> dvdReader;
0113     QList<Device*> dvdWriter;
0114     QList<Device*> bdReader;
0115     QList<Device*> bdWriter;
0116 
0117     bool checkWritingModes;
0118 };
0119 
0120 
0121 
0122 K3b::Device::DeviceManager::DeviceManager( QObject* parent )
0123     : QObject( parent ),
0124       d( new Private() )
0125 {
0126     connect( Solid::DeviceNotifier::instance(), SIGNAL(deviceAdded(QString)),
0127              this, SLOT(slotSolidDeviceAdded(QString)) );
0128     connect( Solid::DeviceNotifier::instance(), SIGNAL(deviceRemoved(QString)),
0129              this, SLOT(slotSolidDeviceRemoved(QString)) );
0130 }
0131 
0132 
0133 K3b::Device::DeviceManager::~DeviceManager()
0134 {
0135     qDeleteAll( d->allDevices );
0136     delete d;
0137 }
0138 
0139 
0140 void K3b::Device::DeviceManager::setCheckWritingModes( bool b )
0141 {
0142     d->checkWritingModes = b;
0143 }
0144 
0145 
0146 K3b::Device::Device* K3b::Device::DeviceManager::deviceByName( const QString& name )
0147 {
0148     return findDevice( name );
0149 }
0150 
0151 
0152 K3b::Device::Device* K3b::Device::DeviceManager::findDevice( const QString& devicename )
0153 {
0154     if( devicename.isEmpty() ) {
0155         qDebug() << "(K3b::Device::DeviceManager) request for empty device!";
0156         return 0;
0157     }
0158 
0159     foreach( Device* dev, d->allDevices ) {
0160         if( dev->blockDeviceName() == devicename )
0161             return dev;
0162     }
0163 
0164     return 0;
0165 }
0166 
0167 
0168 K3b::Device::Device* K3b::Device::DeviceManager::findDeviceByUdi( const QString& udi )
0169 {
0170     foreach( Device* dev, d->allDevices ) {
0171         if ( dev->solidDevice().udi() == udi )
0172             return dev;
0173     }
0174     return 0;
0175 }
0176 
0177 
0178 QList<K3b::Device::Device*> K3b::Device::DeviceManager::cdWriter() const
0179 {
0180     return d->cdWriter;
0181 }
0182 
0183 QList<K3b::Device::Device*> K3b::Device::DeviceManager::cdReader() const
0184 {
0185     return d->cdReader;
0186 }
0187 
0188 QList<K3b::Device::Device*> K3b::Device::DeviceManager::dvdWriter() const
0189 {
0190     return d->dvdWriter;
0191 }
0192 
0193 QList<K3b::Device::Device*> K3b::Device::DeviceManager::dvdReader() const
0194 {
0195     return d->dvdReader;
0196 }
0197 
0198 QList<K3b::Device::Device*> K3b::Device::DeviceManager::blueRayReader() const
0199 {
0200     return d->bdReader;
0201 }
0202 
0203 QList<K3b::Device::Device*> K3b::Device::DeviceManager::blueRayWriters() const
0204 {
0205     return d->bdWriter;
0206 }
0207 
0208 QList<K3b::Device::Device*> K3b::Device::DeviceManager::burningDevices() const
0209 {
0210     return cdWriter();
0211 }
0212 
0213 
0214 QList<K3b::Device::Device*> K3b::Device::DeviceManager::readingDevices() const
0215 {
0216     return cdReader();
0217 }
0218 
0219 
0220 QList<K3b::Device::Device*> K3b::Device::DeviceManager::allDevices() const
0221 {
0222     return d->allDevices;
0223 }
0224 
0225 
0226 int K3b::Device::DeviceManager::scanBus()
0227 {
0228     int cnt = 0;
0229 
0230     QList<Solid::Device> dl = Solid::Device::listFromType( Solid::DeviceInterface::OpticalDrive );
0231     Q_FOREACH( const Solid::Device& solidDev, dl ) {
0232         if ( checkDevice( solidDev ) ) {
0233             ++cnt;
0234         }
0235     }
0236 
0237     return cnt;
0238 }
0239 
0240 
0241 K3b::Device::Device* K3b::Device::DeviceManager::checkDevice( const Solid::Device& dev )
0242 {
0243     if ( dev.is<Solid::OpticalDrive>() ) {
0244         return addDevice( dev );
0245     }
0246     else {
0247         return 0;
0248     }
0249 }
0250 
0251 
0252 void K3b::Device::DeviceManager::printDevices()
0253 {
0254     qDebug() << "Devices:" << Qt::endl
0255              << "------------------------------" << Qt::endl;
0256     Q_FOREACH( Device* dev, d->allDevices ) {
0257         qDebug() << "Blockdevice:    " << dev->blockDeviceName() << Qt::endl
0258                  << "Vendor:         " << dev->vendor() << Qt::endl
0259                  << "Description:    " << dev->description() << Qt::endl
0260                  << "Version:        " << dev->version() << Qt::endl
0261                  << "Write speed:    " << dev->maxWriteSpeed() << Qt::endl
0262                  << "Profiles:       " << mediaTypeString( dev->supportedProfiles() ) << Qt::endl
0263                  << "Read Cap:       " << mediaTypeString( dev->readCapabilities() ) << Qt::endl
0264                  << "Write Cap:      " << mediaTypeString( dev->writeCapabilities() ) << Qt::endl
0265                  << "Writing modes:  " << writingModeString( dev->writingModes() ) << Qt::endl
0266                  << "------------------------------" << Qt::endl;
0267     }
0268 }
0269 
0270 
0271 void K3b::Device::DeviceManager::clear()
0272 {
0273     // clear current devices
0274     d->cdReader.clear();
0275     d->cdWriter.clear();
0276     d->dvdReader.clear();
0277     d->dvdWriter.clear();
0278     d->bdReader.clear();
0279     d->bdWriter.clear();
0280 
0281     // to make sure no one crashes lets keep the devices around until the changed
0282     // signals return
0283     QList<Device*> devicesToDelete( d->allDevices );
0284     d->allDevices.clear();
0285 
0286     emit changed( this );
0287     emit changed();
0288 
0289     qDeleteAll( devicesToDelete );
0290 }
0291 
0292 
0293 bool K3b::Device::DeviceManager::readConfig( const KConfigGroup& c )
0294 {
0295     //
0296     // New configuration format since K3b 0.11.94
0297     // for details see saveConfig()
0298     //
0299 
0300     //
0301     // Iterate over all devices and check if we have a config entry
0302     //
0303     for( QList<K3b::Device::Device*>::iterator it = d->allDevices.begin(); it != d->allDevices.end(); ++it ) {
0304         K3b::Device::Device* dev = *it;
0305 
0306         QString configEntryName = dev->vendor() + ' ' + dev->description();
0307         QStringList list = c.readEntry( configEntryName, QStringList() );
0308         if( !list.isEmpty() ) {
0309             qDebug() << "(K3b::Device::DeviceManager) found config entry for devicetype: " << configEntryName;
0310 
0311             dev->setMaxReadSpeed( list[0].toInt() );
0312             if( list.count() > 1 )
0313                 dev->setMaxWriteSpeed( list[1].toInt() );
0314         }
0315     }
0316 
0317     return true;
0318 }
0319 
0320 
0321 bool K3b::Device::DeviceManager::saveConfig( KConfigGroup c )
0322 {
0323     //
0324     // New configuration format since K3b 0.11.94
0325     //
0326     // We save a device search path which contains all device nodes
0327     // where devices could be found including the old search path.
0328     // This way also for example a manually added USB device will be
0329     // found between sessions.
0330     // Then we do not save the device settings (writing speed, cdrdao driver)
0331     // for every single device but for every device type.
0332     // This also makes sure device settings are kept between sessions
0333     //
0334 
0335     Q_FOREACH( Device* dev, d->allDevices ) {
0336 
0337         // save the device type settings
0338         QString configEntryName = dev->vendor() + ' ' + dev->description();
0339         QStringList list;
0340         list << QString::number(dev->maxReadSpeed())
0341              << QString::number(dev->maxWriteSpeed());
0342 
0343         c.writeEntry( configEntryName, list );
0344     }
0345 
0346     return true;
0347 }
0348 
0349 
0350 K3b::Device::Device* K3b::Device::DeviceManager::addDevice( const Solid::Device& solidDevice )
0351 {
0352     if( const Solid::Block* blockDevice = solidDevice.as<Solid::Block>() ) {
0353 #ifndef Q_OS_NETBSD
0354         if( !findDevice( blockDevice->device() ) )
0355 #else
0356         if( !findDevice( solidDevice.as<Solid::GenericInterface>()->propertyExists("block.netbsd.raw_device") ? solidDevice.as<Solid::GenericInterface>()->property("block.netbsd.raw_device").toString() : blockDevice->device() ) )
0357 #endif
0358             return addDevice( new K3b::Device::Device( solidDevice ) );
0359         else
0360             qDebug() << "(K3b::Device::DeviceManager) dev " << blockDevice->device()  << " already found";
0361     }
0362     return 0;
0363 }
0364 
0365 
0366 K3b::Device::Device* K3b::Device::DeviceManager::addDevice( K3b::Device::Device* device )
0367 {
0368     const QString devicename = device->blockDeviceName();
0369 
0370     if( !device->init() ) {
0371         qDebug() << "Could not initialize device " << devicename;
0372         delete device;
0373         return 0;
0374     }
0375 
0376     if( device ) {
0377         d->allDevices.append( device );
0378 
0379         // not every drive is able to read CDs
0380         // there are some 1st generation DVD writer that cannot
0381         if( device->type() & K3b::Device::DEVICE_CD_ROM )
0382             d->cdReader.append( device );
0383         if( device->readsDvd() )
0384             d->dvdReader.append( device );
0385         if( device->writesCd() )
0386             d->cdWriter.append( device );
0387         if( device->writesDvd() )
0388             d->dvdWriter.append( device );
0389         if( device->readCapabilities() & MEDIA_BD_ALL )
0390             d->bdReader.append( device );
0391         if( device->writeCapabilities() & MEDIA_BD_ALL )
0392             d->bdWriter.append( device );
0393 
0394         if( device->writesCd() ) {
0395             // default to max write speed
0396             qDebug() << "(K3b::Device::DeviceManager) setting current write speed of device "
0397                      << device->blockDeviceName()
0398                      << " to " << device->maxWriteSpeed();
0399             device->setCurrentWriteSpeed( device->maxWriteSpeed() );
0400         }
0401 
0402         emit changed( this );
0403         emit changed();
0404     }
0405 
0406     return device;
0407 }
0408 
0409 
0410 void K3b::Device::DeviceManager::removeDevice( const Solid::Device& dev )
0411 {
0412     if( const Solid::Block* blockDevice = dev.as<Solid::Block>() ) {
0413         if( Device* device = findDevice( blockDevice->device() ) ) {
0414             d->cdReader.removeAll( device );
0415             d->dvdReader.removeAll( device );
0416             d->bdReader.removeAll( device );
0417             d->cdWriter.removeAll( device );
0418             d->dvdWriter.removeAll( device );
0419             d->bdWriter.removeAll( device );
0420             d->allDevices.removeAll( device );
0421 
0422             emit changed( this );
0423             emit changed();
0424 
0425             delete device;
0426         }
0427     }
0428 }
0429 
0430 
0431 void K3b::Device::DeviceManager::slotSolidDeviceAdded( const QString& udi )
0432 {
0433     qDebug() << udi;
0434     checkDevice( Solid::Device( udi ) );
0435 }
0436 
0437 
0438 void K3b::Device::DeviceManager::slotSolidDeviceRemoved( const QString& udi )
0439 {
0440     qDebug() << udi;
0441     Solid::Device solidDev( udi );
0442     if ( solidDev.isDeviceInterface( Solid::DeviceInterface::OpticalDrive ) ) {
0443         if ( solidDev.is<Solid::OpticalDrive>() ) {
0444             removeDevice( solidDev );
0445         }
0446     }
0447 }
0448 
0449 #include "moc_k3bdevicemanager.cpp"