|
|
|
/*
|
|
|
|
*
|
|
|
|
* $Id: k3bdevicemanager.cpp 676188 2007-06-16 08:55:00Z trueg $
|
|
|
|
* Copyright (C) 2003-2007 Sebastian Trueg <trueg@k3b.org>
|
|
|
|
*
|
|
|
|
* This file is part of the K3b project.
|
|
|
|
* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
* See the file "COPYING" for the exact licensing terms.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include "k3bdevicemanager.h"
|
|
|
|
#include "k3bdevice.h"
|
|
|
|
#include "k3bdeviceglobals.h"
|
|
|
|
#include "k3bscsicommand.h"
|
|
|
|
#include "k3bmmc.h"
|
|
|
|
#include "k3bdebug.h"
|
|
|
|
|
|
|
|
#include <tqstring.h>
|
|
|
|
#include <tqstringlist.h>
|
|
|
|
#include <tqptrlist.h>
|
|
|
|
#include <tqfile.h>
|
|
|
|
#include <tqfileinfo.h>
|
|
|
|
#include <tqregexp.h>
|
|
|
|
|
|
|
|
#include <kprocess.h>
|
|
|
|
#include <kapplication.h>
|
|
|
|
#include <kconfig.h>
|
|
|
|
#include <ktempfile.h>
|
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#ifdef Q_OS_FREEBSD
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/ucred.h>
|
|
|
|
#include <osreldate.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
|
|
|
#ifdef HAVE_RESMGR
|
|
|
|
#include <resmgr.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef Q_OS_LINUX
|
|
|
|
|
|
|
|
/* Fix definitions for 2.5 kernels */
|
|
|
|
#include <linux/version.h>
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,70)
|
|
|
|
typedef unsigned char u8;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#undef __STRICT_ANSI__
|
|
|
|
#include <asm/types.h>
|
|
|
|
#define __STRICT_ANSI__
|
|
|
|
|
|
|
|
#include <scsi/scsi.h>
|
|
|
|
#include <linux/major.h>
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef SCSI_DISK_MAJOR
|
|
|
|
#define SCSI_DISK_MAJOR(M) ((M) == SCSI_DISK0_MAJOR || \
|
|
|
|
((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR) || \
|
|
|
|
((M) >= SCSI_DISK8_MAJOR && (M) <= SCSI_DISK15_MAJOR))
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef SCSI_BLK_MAJOR
|
|
|
|
#define SCSI_BLK_MAJOR(M) \
|
|
|
|
(SCSI_DISK_MAJOR(M) \
|
|
|
|
|| (M) == SCSI_CDROM_MAJOR)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef SCSI_GENERIC_MAJOR
|
|
|
|
#define SCSI_GENERIC_MAJOR 21
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#endif // Q_OS_LINUX
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef Q_OS_FREEBSD
|
|
|
|
#include <cam/cam.h>
|
|
|
|
#include <cam/scsi/scsi_pass.h>
|
|
|
|
#include <camlib.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef Q_OS_NETBSD
|
|
|
|
#include <sys/scsiio.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class K3bDevice::DeviceManager::Private
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
TQPtrList<K3bDevice::Device> allDevices;
|
|
|
|
TQPtrList<K3bDevice::Device> cdReader;
|
|
|
|
TQPtrList<K3bDevice::Device> cdWriter;
|
|
|
|
TQPtrList<K3bDevice::Device> dvdReader;
|
|
|
|
TQPtrList<K3bDevice::Device> dvdWriter;
|
|
|
|
TQPtrList<K3bDevice::Device> bdReader;
|
|
|
|
TQPtrList<K3bDevice::Device> bdWriter;
|
|
|
|
|
|
|
|
bool checkWritingModes;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
K3bDevice::DeviceManager::DeviceManager( TQObject* parent, const char* name )
|
|
|
|
: TQObject( parent, name )
|
|
|
|
{
|
|
|
|
d = new Private;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
K3bDevice::DeviceManager::~DeviceManager()
|
|
|
|
{
|
|
|
|
d->allDevices.setAutoDelete( true );
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void K3bDevice::DeviceManager::setCheckWritingModes( bool b )
|
|
|
|
{
|
|
|
|
d->checkWritingModes = b;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
K3bDevice::Device* K3bDevice::DeviceManager::deviceByName( const TQString& name )
|
|
|
|
{
|
|
|
|
return findDevice( name );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
K3bDevice::Device* K3bDevice::DeviceManager::findDevice( int bus, int id, int lun )
|
|
|
|
{
|
|
|
|
TQPtrListIterator<K3bDevice::Device> it( d->allDevices );
|
|
|
|
while( it.current() )
|
|
|
|
{
|
|
|
|
if( it.current()->scsiBus() == bus &&
|
|
|
|
it.current()->scsiId() == id &&
|
|
|
|
it.current()->scsiLun() == lun )
|
|
|
|
return it.current();
|
|
|
|
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
K3bDevice::Device* K3bDevice::DeviceManager::findDevice( const TQString& devicename )
|
|
|
|
{
|
|
|
|
if( devicename.isEmpty() ) {
|
|
|
|
k3bDebug() << "(K3bDevice::DeviceManager) request for empty device!" << endl;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
TQPtrListIterator<K3bDevice::Device> it( d->allDevices );
|
|
|
|
while( it.current() ) {
|
|
|
|
if( it.current()->deviceNodes().contains(devicename) )
|
|
|
|
return it.current();
|
|
|
|
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const TQPtrList<K3bDevice::Device>& K3bDevice::DeviceManager::cdWriter() const
|
|
|
|
{
|
|
|
|
return d->cdWriter;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TQPtrList<K3bDevice::Device>& K3bDevice::DeviceManager::cdReader() const
|
|
|
|
{
|
|
|
|
return d->cdReader;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TQPtrList<K3bDevice::Device>& K3bDevice::DeviceManager::dvdWriter() const
|
|
|
|
{
|
|
|
|
return d->dvdWriter;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TQPtrList<K3bDevice::Device>& K3bDevice::DeviceManager::dvdReader() const
|
|
|
|
{
|
|
|
|
return d->dvdReader;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TQPtrList<K3bDevice::Device>& K3bDevice::DeviceManager::blueRayReader() const
|
|
|
|
{
|
|
|
|
return d->bdReader;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TQPtrList<K3bDevice::Device>& K3bDevice::DeviceManager::blueRayWriters() const
|
|
|
|
{
|
|
|
|
return d->bdWriter;
|
|
|
|
}
|
|
|
|
|
|
|
|
const TQPtrList<K3bDevice::Device>& K3bDevice::DeviceManager::burningDevices() const
|
|
|
|
{
|
|
|
|
return cdWriter();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const TQPtrList<K3bDevice::Device>& K3bDevice::DeviceManager::readingDevices() const
|
|
|
|
{
|
|
|
|
return cdReader();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const TQPtrList<K3bDevice::Device>& K3bDevice::DeviceManager::allDevices() const
|
|
|
|
{
|
|
|
|
return d->allDevices;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int K3bDevice::DeviceManager::scanBus()
|
|
|
|
{
|
|
|
|
unsigned int numDevs = d->allDevices.count();
|
|
|
|
|
|
|
|
#ifdef Q_OS_LINUX
|
|
|
|
LinuxDeviceScan();
|
|
|
|
#endif
|
|
|
|
#ifdef Q_OS_FREEBSD
|
|
|
|
BSDDeviceScan();
|
|
|
|
#endif
|
|
|
|
#ifdef Q_OS_NETBSD
|
|
|
|
NetBSDDeviceScan();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return d->allDevices.count() - numDevs;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void K3bDevice::DeviceManager::LinuxDeviceScan()
|
|
|
|
{
|
|
|
|
#ifdef HAVE_RESMGR
|
|
|
|
//
|
|
|
|
// Resmgr device scan
|
|
|
|
//
|
|
|
|
char** resmgrDevices = rsm_list_devices( 0 );
|
|
|
|
if( resmgrDevices ) {
|
|
|
|
for( int i = 0; resmgrDevices[i]; ++i ) {
|
|
|
|
addDevice( resmgrDevices[i] );
|
|
|
|
free( resmgrDevices[i] );
|
|
|
|
}
|
|
|
|
|
|
|
|
free( resmgrDevices );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
TQFile info("/proc/sys/dev/cdrom/info");
|
|
|
|
TQString line,devstring;
|
|
|
|
if( info.open(IO_ReadOnly) ) {
|
|
|
|
info.readLine(line,80); // CD-ROM information, Id: cdrom.c 3.12 2000/10/18
|
|
|
|
info.readLine(line,80); //
|
|
|
|
|
|
|
|
TQRegExp re("[\t\n:]+");
|
|
|
|
while( info.readLine( line, 80 ) > 0 ) {
|
|
|
|
if( line.contains("drive name") > 0 ) {
|
|
|
|
int i = 1;
|
|
|
|
TQString dev;
|
|
|
|
while( !(dev = line.section(re, i, i)).isEmpty() ) {
|
|
|
|
if( addDevice( TQString("/dev/%1").arg(dev) ) ) {
|
|
|
|
devstring += dev + "|";
|
|
|
|
}
|
|
|
|
// according to the LINUX ALLOCATED DEVICES document (http://www.lanana.org/docs/device-list/),
|
|
|
|
// the official device names for SCSI-CDROM's (block major 11) are /dev/sr*, the
|
|
|
|
// prefix /dev/scd instead of /dev/sr has been used as well, and might make more sense.
|
|
|
|
// Since there should be one and only one device node (and maybe several symbolic links) for
|
|
|
|
// each physical device the next line should be better
|
|
|
|
// else if ( dev.startsWith("sr") )
|
|
|
|
if ( dev.startsWith("sr") ) {
|
|
|
|
if( addDevice(TQString("/dev/%1").arg(dev.replace(TQRegExp("r"),"cd"))) )
|
|
|
|
devstring += dev + "|";
|
|
|
|
}
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
info.close();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
kdError() << "(K3bDevice::DeviceManager) could not open /proc/sys/dev/cdrom/info" << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Scan the generic devices if we have scsi devices
|
|
|
|
//
|
|
|
|
k3bDebug() << "(K3bDevice::DeviceManager) SCANNING FOR GENERIC DEVICES." << endl;
|
|
|
|
for( int i = 0; i < 16; i++ ) {
|
|
|
|
TQString sgDev = resolveSymLink( TQString("/dev/sg%1").arg(i) );
|
|
|
|
int bus = -1, id = -1, lun = -1;
|
|
|
|
if( determineBusIdLun( sgDev, bus, id, lun ) ) {
|
|
|
|
if( Device* dev = findDevice( bus, id, lun ) ) {
|
|
|
|
dev->m_genericDevice = sgDev;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// FIXME: also scan /dev/scsi/hostX.... for devfs without symlinks
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void K3bDevice::DeviceManager::NetBSDDeviceScan()
|
|
|
|
{
|
|
|
|
// Generate entries for /dev/cd* devices
|
|
|
|
// Note: As there are only 10 possible /dev/(r)cd devices,
|
|
|
|
// only these will be found.
|
|
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
// Whole disk mask (According to cd(4), the AMD64, i386 and BeBox ports use
|
|
|
|
// 'd' as whole-disk partition, the rest uses 'c'.)
|
|
|
|
|
|
|
|
#if defined(__i386__) || defined (__amd64__) || defined (__bebox__)
|
|
|
|
static const char slicename = 'd';
|
|
|
|
#else
|
|
|
|
static const char slicename = 'c';
|
|
|
|
#endif
|
|
|
|
|
|
|
|
char devicename[11]; // /dev/rcdXd + trailing zero
|
|
|
|
|
|
|
|
for (i = 0; i < 10; i++ ) // cd(4) claims there are max. 10 CD devices.
|
|
|
|
{
|
|
|
|
snprintf(devicename,11,"/dev/rcd%d%c",i, slicename);
|
|
|
|
addDevice(TQString(devicename));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void K3bDevice::DeviceManager::BSDDeviceScan()
|
|
|
|
{
|
|
|
|
// Unfortunately uses lots of FBSD-specific data structures
|
|
|
|
#ifndef Q_OS_FREEBSD
|
|
|
|
// bool bsdspecificcode = false;
|
|
|
|
// assert(bsdspecificcode);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef Q_OS_FREEBSD
|
|
|
|
union ccb ccb;
|
|
|
|
int fd;
|
|
|
|
int need_close = 0;
|
|
|
|
int skip_device = 0;
|
|
|
|
int bus, target, lun;
|
|
|
|
TQString dev1, dev2;
|
|
|
|
|
|
|
|
if ((fd = open(XPT_DEVICE, O_RDWR)) == -1)
|
|
|
|
{
|
|
|
|
k3bDebug() << "couldn't open %s " << XPT_DEVICE << endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&ccb, 0, sizeof(ccb));
|
|
|
|
|
|
|
|
ccb.ccb_h.func_code = XPT_DEV_MATCH;
|
|
|
|
char buffer[100*sizeof(struct dev_match_result)];
|
|
|
|
ccb.cdm.match_buf_len = 100*sizeof(struct dev_match_result);
|
|
|
|
ccb.cdm.matches = (struct dev_match_result *)buffer;
|
|
|
|
ccb.cdm.num_matches = 0;
|
|
|
|
ccb.cdm.num_patterns = 0;
|
|
|
|
ccb.cdm.pattern_buf_len = 0;
|
|
|
|
do {
|
|
|
|
if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) {
|
|
|
|
k3bDebug() << "(BSDDeviceScan) error sending CAMIOCOMMAND ioctl: " << errno << endl;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ccb.ccb_h.status != CAM_RETQ_CMP)
|
|
|
|
|| ((ccb.cdm.status != CAM_DEV_MATCH_LAST) && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) {
|
|
|
|
k3bDebug() << "(BSDDeviceScan) got CAM error " << ccb.ccb_h.status << ", CDM error %d" << ccb.cdm.status << endl;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
k3bDebug() << "(BSDDeviceScan) number of matches " << (int)ccb.cdm.num_matches << endl;
|
|
|
|
for (int i = 0; i < (int)ccb.cdm.num_matches; i++) {
|
|
|
|
switch (ccb.cdm.matches[i].type) {
|
|
|
|
case DEV_MATCH_DEVICE: {
|
|
|
|
struct device_match_result *dev_result = &ccb.cdm.matches[i].result.device_result;
|
|
|
|
|
|
|
|
if (dev_result->flags & DEV_RESULT_UNCONFIGURED)
|
|
|
|
{
|
|
|
|
skip_device = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
skip_device = 0;
|
|
|
|
if (need_close)
|
|
|
|
{
|
|
|
|
TQString pass = dev1;
|
|
|
|
TQString dev = "/dev/" + dev2;
|
|
|
|
if (dev2.startsWith("pass"))
|
|
|
|
{
|
|
|
|
pass = dev2;
|
|
|
|
dev = "/dev/" + dev1;
|
|
|
|
}
|
|
|
|
#if __FreeBSD_version < 500100
|
|
|
|
dev += "c";
|
|
|
|
#endif
|
|
|
|
if (!dev1.isEmpty() && !dev2.isEmpty() && dev.startsWith("/dev/cd"))
|
|
|
|
{
|
|
|
|
Device* device = new Device(dev.latin1());
|
|
|
|
device->m_bus = bus;
|
|
|
|
device->m_target = target;
|
|
|
|
device->m_lun = lun;
|
|
|
|
device->m_passDevice = "/dev/" + pass;
|
|
|
|
k3bDebug() << "(BSDDeviceScan) add device " << dev << ":" << bus << ":" << target << ":" << lun << endl;
|
|
|
|
addDevice(device);
|
|
|
|
}
|
|
|
|
need_close = 0;
|
|
|
|
dev1="";
|
|
|
|
dev2="";
|
|
|
|
}
|
|
|
|
bus = dev_result->path_id;
|
|
|
|
target = dev_result->target_id;
|
|
|
|
lun = dev_result->target_lun;
|
|
|
|
|
|
|
|
need_close = 1;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case DEV_MATCH_PERIPH: {
|
|
|
|
struct periph_match_result *periph_result = &ccb.cdm.matches[i].result.periph_result;
|
|
|
|
|
|
|
|
if (skip_device != 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (need_close > 1)
|
|
|
|
dev1 = periph_result->periph_name + TQString::number(periph_result->unit_number);
|
|
|
|
else
|
|
|
|
dev2 = periph_result->periph_name + TQString::number(periph_result->unit_number);
|
|
|
|
|
|
|
|
need_close++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case DEV_MATCH_BUS : {
|
|
|
|
// bool cannotmatchbus = false;
|
|
|
|
// assert(cannotmatchbus);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} while ((ccb.ccb_h.status == CAM_RETQ_CMP)
|
|
|
|
&& (ccb.cdm.status == CAM_DEV_MATCH_MORE));
|
|
|
|
|
|
|
|
if (need_close)
|
|
|
|
{
|
|
|
|
TQString pass = dev1;
|
|
|
|
TQString dev = "/dev/" + dev2;
|
|
|
|
if (dev2.startsWith("pass"))
|
|
|
|
{
|
|
|
|
pass = dev2;
|
|
|
|
dev = "/dev/" + dev1;
|
|
|
|
}
|
|
|
|
#if __FreeBSD_version < 500100
|
|
|
|
dev += "c";
|
|
|
|
#endif
|
|
|
|
if (!dev1.isEmpty() && !dev2.isEmpty() && dev.startsWith("/dev/cd"))
|
|
|
|
{
|
|
|
|
Device* device = new Device(dev.latin1());
|
|
|
|
device->m_bus = bus;
|
|
|
|
device->m_target = target;
|
|
|
|
device->m_lun = lun;
|
|
|
|
device->m_passDevice = "/dev/" + pass;
|
|
|
|
k3bDebug() << "(BSDDeviceScan) add device " << dev << ":" << bus << ":" << target << ":" << lun << endl;
|
|
|
|
addDevice(device);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
close(fd);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void K3bDevice::DeviceManager::printDevices()
|
|
|
|
{
|
|
|
|
k3bDebug() << "Devices:" << endl
|
|
|
|
<< "------------------------------" << endl;
|
|
|
|
TQPtrListIterator<Device> it( allDevices() );
|
|
|
|
for( ; *it; ++it ) {
|
|
|
|
Device* dev = *it;
|
|
|
|
k3bDebug() << "Blockdevice: " << dev->blockDeviceName() << endl
|
|
|
|
<< "Generic device: " << dev->genericDevice() << endl
|
|
|
|
<< "Vendor: " << dev->vendor() << endl
|
|
|
|
<< "Description: " << dev->description() << endl
|
|
|
|
<< "Version: " << dev->version() << endl
|
|
|
|
<< "Write speed: " << dev->maxWriteSpeed() << endl
|
|
|
|
<< "Profiles: " << mediaTypeString( dev->supportedProfiles() ) << endl
|
|
|
|
<< "Read Cap: " << mediaTypeString( dev->readCapabilities() ) << endl
|
|
|
|
<< "Write Cap: " << mediaTypeString( dev->writeCapabilities() ) << endl
|
|
|
|
<< "Writing modes: " << writingModeString( dev->writingModes() ) << endl
|
|
|
|
<< "Reader aliases: " << dev->deviceNodes().join(", ") << endl
|
|
|
|
<< "------------------------------" << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void K3bDevice::DeviceManager::clear()
|
|
|
|
{
|
|
|
|
// clear current devices
|
|
|
|
d->cdReader.clear();
|
|
|
|
d->cdWriter.clear();
|
|
|
|
d->dvdReader.clear();
|
|
|
|
d->dvdWriter.clear();
|
|
|
|
d->bdReader.clear();
|
|
|
|
d->bdWriter.clear();
|
|
|
|
|
|
|
|
// to make sure no one crashes lets keep the devices around until the changed
|
|
|
|
// signals return
|
|
|
|
TQPtrList<K3bDevice::Device> tmp = d->allDevices;
|
|
|
|
tmp.setAutoDelete( true );
|
|
|
|
|
|
|
|
d->allDevices.clear();
|
|
|
|
|
|
|
|
emit changed( this );
|
|
|
|
emit changed();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool K3bDevice::DeviceManager::readConfig( KConfig* c )
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// New configuration format since K3b 0.11.94
|
|
|
|
// for details see saveConfig()
|
|
|
|
//
|
|
|
|
|
|
|
|
if( !c->hasGroup( "Devices" ) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
c->setGroup( "Devices" );
|
|
|
|
|
|
|
|
TQStringList deviceSearchPath = c->readListEntry( "device_search_path" );
|
|
|
|
for( TQStringList::const_iterator it = deviceSearchPath.constBegin();
|
|
|
|
it != deviceSearchPath.constEnd(); ++it )
|
|
|
|
addDevice( *it );
|
|
|
|
|
|
|
|
//
|
|
|
|
// Iterate over all devices and check if we have a config entry
|
|
|
|
//
|
|
|
|
for( TQPtrListIterator<K3bDevice::Device> it( d->allDevices ); *it; ++it ) {
|
|
|
|
K3bDevice::Device* dev = *it;
|
|
|
|
|
|
|
|
TQString configEntryName = dev->vendor() + " " + dev->description();
|
|
|
|
TQStringList list = c->readListEntry( configEntryName );
|
|
|
|
if( !list.isEmpty() ) {
|
|
|
|
k3bDebug() << "(K3bDevice::DeviceManager) found config entry for devicetype: " << configEntryName << endl;
|
|
|
|
|
|
|
|
dev->setMaxReadSpeed( list[0].toInt() );
|
|
|
|
if( list.count() > 1 )
|
|
|
|
dev->setMaxWriteSpeed( list[1].toInt() );
|
|
|
|
if( list.count() > 2 )
|
|
|
|
dev->setCdrdaoDriver( list[2] );
|
|
|
|
if( list.count() > 3 )
|
|
|
|
dev->setCdTextCapability( list[3] == "yes" );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool K3bDevice::DeviceManager::saveConfig( KConfig* c )
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// New configuration format since K3b 0.11.94
|
|
|
|
//
|
|
|
|
// We save a device search path which contains all device nodes
|
|
|
|
// where devices could be found including the old search path.
|
|
|
|
// This way also for example a manually added USB device will be
|
|
|
|
// found between sessions.
|
|
|
|
// Then we do not save the device settings (writing speed, cdrdao driver)
|
|
|
|
// for every single device but for every device type.
|
|
|
|
// This also makes sure device settings are kept between sessions
|
|
|
|
//
|
|
|
|
|
|
|
|
c->setGroup( "Devices" );
|
|
|
|
TQStringList deviceSearchPath = c->readListEntry( "device_search_path" );
|
|
|
|
// remove duplicate entries (caused by buggy old implementations)
|
|
|
|
TQStringList saveDeviceSearchPath;
|
|
|
|
for( TQStringList::const_iterator it = deviceSearchPath.constBegin(); it != deviceSearchPath.constEnd(); ++it )
|
|
|
|
if( !saveDeviceSearchPath.contains( *it ) )
|
|
|
|
saveDeviceSearchPath.append( *it );
|
|
|
|
|
|
|
|
for( TQPtrListIterator<K3bDevice::Device> it( d->allDevices ); *it; ++it ) {
|
|
|
|
K3bDevice::Device* dev = *it;
|
|
|
|
|
|
|
|
// update device search path
|
|
|
|
if( !saveDeviceSearchPath.contains( dev->blockDeviceName() ) )
|
|
|
|
saveDeviceSearchPath.append( dev->blockDeviceName() );
|
|
|
|
|
|
|
|
// save the device type settings
|
|
|
|
TQString configEntryName = dev->vendor() + " " + dev->description();
|
|
|
|
TQStringList list;
|
|
|
|
list << TQString::number(dev->maxReadSpeed())
|
|
|
|
<< TQString::number(dev->maxWriteSpeed())
|
|
|
|
<< dev->cdrdaoDriver();
|
|
|
|
|
|
|
|
if( dev->cdrdaoDriver() != "auto" )
|
|
|
|
list << ( dev->cdTextCapable() == 1 ? "yes" : "no" );
|
|
|
|
else
|
|
|
|
list << "auto";
|
|
|
|
|
|
|
|
c->writeEntry( configEntryName, list );
|
|
|
|
}
|
|
|
|
|
|
|
|
c->writeEntry( "device_search_path", saveDeviceSearchPath );
|
|
|
|
|
|
|
|
c->sync();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool K3bDevice::DeviceManager::testForCdrom( const TQString& devicename )
|
|
|
|
{
|
|
|
|
#ifdef Q_OS_FREEBSD
|
|
|
|
Q_UNUSED(devicename);
|
|
|
|
return true;
|
|
|
|
#endif
|
|
|
|
#if defined(Q_OS_LINUX) || defined(Q_OS_NETBSD)
|
|
|
|
bool ret = false;
|
|
|
|
int cdromfd = K3bDevice::openDevice( devicename.ascii() );
|
|
|
|
if (cdromfd < 0) {
|
|
|
|
k3bDebug() << "could not open device " << devicename << " (" << strerror(errno) << ")" << endl;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// stat the device
|
|
|
|
struct stat cdromStat;
|
|
|
|
if( ::fstat( cdromfd, &cdromStat ) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if( !S_ISBLK( cdromStat.st_mode) ) {
|
|
|
|
k3bDebug() << devicename << " is no block device" << endl;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
k3bDebug() << devicename << " is block device (" << (int)(cdromStat.st_rdev & 0xFF) << ")" << endl;
|
|
|
|
#if defined(Q_OS_NETBSD)
|
|
|
|
}
|
|
|
|
{
|
|
|
|
#endif
|
|
|
|
// inquiry
|
|
|
|
// use a 36 bytes buffer since not all devices return the full inquiry struct
|
|
|
|
unsigned char buf[36];
|
|
|
|
struct inquiry* inq = (struct inquiry*)buf;
|
|
|
|
::memset( buf, 0, sizeof(buf) );
|
|
|
|
|
|
|
|
ScsiCommand cmd( cdromfd );
|
|
|
|
cmd[0] = MMC_INTQUIRY;
|
|
|
|
cmd[4] = sizeof(buf);
|
|
|
|
cmd[5] = 0;
|
|
|
|
|
|
|
|
if( cmd.transport( TR_DIR_READ, buf, sizeof(buf) ) ) {
|
|
|
|
k3bDebug() << "(K3bDevice::Device) Unable to do inquiry. " << devicename << " is not a cdrom device" << endl;
|
|
|
|
}
|
|
|
|
else if( (inq->p_device_type&0x1f) != 0x5 ) {
|
|
|
|
k3bDebug() << devicename << " seems not to be a cdrom device: " << strerror(errno) << endl;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ret = true;
|
|
|
|
k3bDebug() << devicename << " seems to be cdrom" << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
::close( cdromfd );
|
|
|
|
return ret;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
K3bDevice::Device* K3bDevice::DeviceManager::addDevice( const TQString& devicename )
|
|
|
|
{
|
|
|
|
#ifdef Q_OS_FREEBSD
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
K3bDevice::Device* device = 0;
|
|
|
|
|
|
|
|
// resolve all symlinks
|
|
|
|
TQString resolved = resolveSymLink( devicename );
|
|
|
|
k3bDebug() << devicename << " resolved to " << resolved << endl;
|
|
|
|
|
|
|
|
if ( K3bDevice::Device* oldDev = findDevice(resolved) ) {
|
|
|
|
k3bDebug() << "(K3bDevice::DeviceManager) dev " << resolved << " already found" << endl;
|
|
|
|
oldDev->addDeviceNode( devicename );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !testForCdrom(resolved) ) {
|
|
|
|
#ifdef HAVE_RESMGR
|
|
|
|
// With resmgr we might only be able to open the symlink name.
|
|
|
|
if( testForCdrom(devicename) ) {
|
|
|
|
resolved = devicename;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
int bus = -1, target = -1, lun = -1;
|
|
|
|
bool scsi = determineBusIdLun( resolved, bus, target, lun );
|
|
|
|
if(scsi) {
|
|
|
|
if ( K3bDevice::Device* oldDev = findDevice(bus, target, lun) ) {
|
|
|
|
k3bDebug() << "(K3bDevice::DeviceManager) dev " << resolved << " already found" << endl;
|
|
|
|
oldDev->addDeviceNode( devicename );
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
device = new K3bDevice::Device(resolved);
|
|
|
|
if( scsi ) {
|
|
|
|
device->m_bus = bus;
|
|
|
|
device->m_target = target;
|
|
|
|
device->m_lun = lun;
|
|
|
|
}
|
|
|
|
|
|
|
|
return addDevice(device);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
K3bDevice::Device* K3bDevice::DeviceManager::addDevice( K3bDevice::Device* device )
|
|
|
|
{
|
|
|
|
const TQString devicename = device->devicename();
|
|
|
|
|
|
|
|
if( !device->init() ) {
|
|
|
|
k3bDebug() << "Could not initialize device " << devicename << endl;
|
|
|
|
delete device;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( device ) {
|
|
|
|
d->allDevices.append( device );
|
|
|
|
|
|
|
|
// not every drive is able to read CDs
|
|
|
|
// there are some 1st generation DVD writer that cannot
|
|
|
|
if( device->type() & K3bDevice::DEVICE_CD_ROM )
|
|
|
|
d->cdReader.append( device );
|
|
|
|
if( device->readsDvd() )
|
|
|
|
d->dvdReader.append( device );
|
|
|
|
if( device->writesCd() )
|
|
|
|
d->cdWriter.append( device );
|
|
|
|
if( device->writesDvd() )
|
|
|
|
d->dvdWriter.append( device );
|
|
|
|
if( device->readCapabilities() & MEDIA_BD_ALL )
|
|
|
|
d->bdReader.append( device );
|
|
|
|
if( device->writeCapabilities() & MEDIA_BD_ALL )
|
|
|
|
d->bdWriter.append( device );
|
|
|
|
|
|
|
|
if( device->writesCd() ) {
|
|
|
|
// default to max write speed
|
|
|
|
k3bDebug() << "(K3bDevice::DeviceManager) setting current write speed of device "
|
|
|
|
<< device->blockDeviceName()
|
|
|
|
<< " to " << device->maxWriteSpeed() << endl;
|
|
|
|
device->setCurrentWriteSpeed( device->maxWriteSpeed() );
|
|
|
|
}
|
|
|
|
|
|
|
|
emit changed( this );
|
|
|
|
emit changed();
|
|
|
|
}
|
|
|
|
|
|
|
|
return device;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void K3bDevice::DeviceManager::removeDevice( const TQString& dev )
|
|
|
|
{
|
|
|
|
if( Device* device = findDevice( dev ) ) {
|
|
|
|
d->cdReader.removeRef( device );
|
|
|
|
d->dvdReader.removeRef( device );
|
|
|
|
d->bdReader.removeRef( device );
|
|
|
|
d->cdWriter.removeRef( device );
|
|
|
|
d->dvdWriter.removeRef( device );
|
|
|
|
d->bdWriter.removeRef( device );
|
|
|
|
d->allDevices.removeRef( device );
|
|
|
|
|
|
|
|
emit changed( this );
|
|
|
|
emit changed();
|
|
|
|
|
|
|
|
delete device;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool K3bDevice::DeviceManager::determineBusIdLun( const TQString& dev, int& bus, int& id, int& lun )
|
|
|
|
{
|
|
|
|
#ifdef Q_OS_FREEBSD
|
|
|
|
Q_UNUSED(dev);
|
|
|
|
Q_UNUSED(bus);
|
|
|
|
Q_UNUSED(id);
|
|
|
|
Q_UNUSED(lun);
|
|
|
|
return false;
|
|
|
|
/* NOTREACHED */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef Q_OS_NETBSD
|
|
|
|
int cdromfd = K3bDevice::openDevice ( dev.ascii() );
|
|
|
|
if (cdromfd < 0) {
|
|
|
|
int local_errno = errno; // For all we know, k3bDebug() destroys errno
|
|
|
|
k3bDebug() << "could not open device " << dev << " (" << strerror(local_errno) << ")" << endl;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct scsi_addr my_addr;
|
|
|
|
|
|
|
|
if (::ioctl(cdromfd, SCIOCIDENTIFY, &my_addr))
|
|
|
|
{
|
|
|
|
int local_errno = errno; // For all we know, k3bDebug() destroys errno
|
|
|
|
k3bDebug() << "ioctl(SCIOCIDENTIFY) failed on device " << dev << " (" << strerror(local_errno) << ")" << endl;
|
|
|
|
|
|
|
|
::close(cdromfd);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (my_addr.type == TYPE_ATAPI)
|
|
|
|
{
|
|
|
|
// XXX Re-map atapibus, so it doesn't conflict with "real" scsi
|
|
|
|
// busses
|
|
|
|
|
|
|
|
bus = 15;
|
|
|
|
id = my_addr.addr.atapi.drive + 2 * my_addr.addr.atapi.atbus;
|
|
|
|
lun = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bus = my_addr.addr.scsi.scbus;
|
|
|
|
id = my_addr.addr.scsi.target;
|
|
|
|
lun = my_addr.addr.scsi.lun;
|
|
|
|
}
|
|
|
|
|
|
|
|
::close(cdromfd);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef Q_OS_LINUX
|
|
|
|
int ret = false;
|
|
|
|
int cdromfd = K3bDevice::openDevice( dev.ascii() );
|
|
|
|
if (cdromfd < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct stat cdromStat;
|
|
|
|
if ( ::fstat( cdromfd, &cdromStat ) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if( SCSI_BLK_MAJOR( cdromStat.st_rdev>>8 ) ||
|
|
|
|
SCSI_GENERIC_MAJOR == (cdromStat.st_rdev>>8) ) {
|
|
|
|
struct ScsiIdLun
|
|
|
|
{
|
|
|
|
int id;
|
|
|
|
int lun;
|
|
|
|
};
|
|
|
|
ScsiIdLun idLun;
|
|
|
|
|
|
|
|
// in kernel 2.2 SCSI_IOCTL_GET_IDLUN does not contain the bus id
|
|
|
|
if ( (::ioctl( cdromfd, SCSI_IOCTL_GET_IDLUN, &idLun ) < 0) ||
|
|
|
|
(::ioctl( cdromfd, SCSI_IOCTL_GET_BUS_NUMBER, &bus ) < 0) ) {
|
|
|
|
k3bDebug() << "Need a filename that resolves to a SCSI device" << endl;
|
|
|
|
ret = false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
id = idLun.id & 0xff;
|
|
|
|
lun = (idLun.id >> 8) & 0xff;
|
|
|
|
k3bDebug() << "bus: " << bus << ", id: " << id << ", lun: " << lun << endl;
|
|
|
|
ret = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
::close(cdromfd);
|
|
|
|
return ret;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TQString K3bDevice::DeviceManager::resolveSymLink( const TQString& path )
|
|
|
|
{
|
|
|
|
char resolved[PATH_MAX];
|
|
|
|
if( !realpath( TQFile::encodeName(path), resolved ) )
|
|
|
|
{
|
|
|
|
k3bDebug() << "Could not resolve " << path << endl;
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TQString::fromLatin1( resolved );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#include "k3bdevicemanager.moc"
|