From 75a576a5c2ba45c5aeab0b4a630126a621d18d08 Mon Sep 17 00:00:00 2001 From: John Maguire Date: Thu, 2 Sep 2010 21:20:27 +0000 Subject: [PATCH] MTP support for Mac... Currently crashes the entire USB bus so I wouldn't use it. --- src/devices/macdevicelister.h | 9 +++- src/devices/macdevicelister.mm | 81 ++++++++++++++++++++++++++++++---- src/devices/mtpconnection.cpp | 20 ++++++++- src/devices/mtpconnection.h | 4 +- src/devices/mtpdevice.cpp | 2 +- src/devices/mtploader.cpp | 8 ++-- src/devices/mtploader.h | 5 ++- 7 files changed, 110 insertions(+), 19 deletions(-) diff --git a/src/devices/macdevicelister.h b/src/devices/macdevicelister.h index 17bf697d5..744cac74f 100644 --- a/src/devices/macdevicelister.h +++ b/src/devices/macdevicelister.h @@ -30,9 +30,13 @@ class MacDeviceLister : public DeviceLister { struct MTPDevice { QString vendor; - quint16 vendor_id; QString product; + quint16 vendor_id; quint16 product_id; + + int quirks; + int bus; + int address; }; public slots: @@ -48,10 +52,13 @@ class MacDeviceLister : public DeviceLister { static void DiskUnmountCallback( DADiskRef disk, DADissenterRef dissenter, void* context); + void FoundMTPDevice(const MTPDevice& device, const QString& serial); + DASessionRef loop_session_; CFRunLoopRef run_loop_; QMap current_devices_; + QMap mtp_devices_; static QSet sMTPDeviceList; }; diff --git a/src/devices/macdevicelister.mm b/src/devices/macdevicelister.mm index 2358cb8d1..ad7904b0d 100644 --- a/src/devices/macdevicelister.mm +++ b/src/devices/macdevicelister.mm @@ -81,6 +81,7 @@ void MacDeviceLister::Init() { d.vendor_id = device.vendor_id; d.product = QString::fromAscii(device.product); d.product_id = device.product_id; + d.quirks = device.device_flags; } } } @@ -227,6 +228,11 @@ QString GetSerialForDevice(io_object_t device) { "USB/" + GetUSBRegistryEntryString(device, CFSTR(kUSBSerialNumberString))); } +QString GetSerialForMTPDevice(io_object_t device) { + return QString( + "MTP/" + GetUSBRegistryEntryString(device, CFSTR(kUSBSerialNumberString))); +} + QString FindDeviceProperty(const QString& bsd_name, CFStringRef property) { DASessionRef session = DASessionCreate(kCFAllocatorDefault); DADiskRef disk = DADiskCreateFromBSDName( @@ -340,6 +346,11 @@ void MacDeviceLister::USBDeviceAddedCallback(void* refcon, io_iterator_t it) { io_object_t object; while ((object = IOIteratorNext(it))) { CFStringRef class_name = IOObjectCopyClass(object); + BOOST_SCOPE_EXIT((class_name)(object)) { + CFRelease(class_name); + IOObjectRelease(object); + } BOOST_SCOPE_EXIT_END + if (CFStringCompare(class_name, CFSTR(kIOUSBDeviceClassName), 0) == kCFCompareEqualTo) { NSString* vendor = (NSString*)GetPropertyForDevice(object, CFSTR(kUSBVendorString)); NSString* product = (NSString*)GetPropertyForDevice(object, CFSTR(kUSBProductString)); @@ -351,6 +362,10 @@ void MacDeviceLister::USBDeviceAddedCallback(void* refcon, io_iterator_t it) { device.product = QString::fromUtf8([product UTF8String]); device.vendor_id = [vendor_id unsignedShortValue]; device.product_id = [product_id unsignedShortValue]; + device.quirks = 0; + + device.bus = -1; + device.address = -1; if (device.vendor_id == 0x5ac) { // I think we can safely skip Apple products. @@ -369,12 +384,17 @@ void MacDeviceLister::USBDeviceAddedCallback(void* refcon, io_iterator_t it) { // Failed to get bus or address number. continue; } + device.bus = bus; + device.address = [addr intValue]; // First check the libmtp device list. - if (sMTPDeviceList.contains(device)) { + QSet::const_iterator it = sMTPDeviceList.find(device); + if (it != sMTPDeviceList.end()) { qDebug() << "Matched device to libmtp list!"; - // emit. - return; + // Fill in quirks flags from libmtp. + device.quirks = it->quirks; + me->FoundMTPDevice(device, GetSerialForMTPDevice(object)); + continue; } IOCFPlugInInterface** plugin_interface = NULL; @@ -457,16 +477,31 @@ void MacDeviceLister::USBDeviceAddedCallback(void* refcon, io_iterator_t it) { continue; } // Hurray! We made it! - qDebug() << "New MTP device detected!"; + me->FoundMTPDevice(device, GetSerialForMTPDevice(object)); } } - - CFRelease(class_name); - IOObjectRelease(object); } } +void MacDeviceLister::FoundMTPDevice(const MTPDevice& device, const QString& serial) { + qDebug() << "New MTP device detected!"; + mtp_devices_[serial] = device; + emit DeviceAdded(serial); +} + +bool IsMTPSerial(const QString& serial) { + return serial.startsWith("MTP"); +} + QString MacDeviceLister::MakeFriendlyName(const QString& serial) { + if (IsMTPSerial(serial)) { + const MTPDevice& device = mtp_devices_[serial]; + if (device.vendor.isEmpty()) { + return device.product; + } else { + return device.vendor + " " + device.product; + } + } QString bsd_name = current_devices_[serial]; DASessionRef session = DASessionCreate(kCFAllocatorDefault); DADiskRef disk = DADiskCreateFromBSDName( @@ -487,6 +522,19 @@ QString MacDeviceLister::MakeFriendlyName(const QString& serial) { } QList MacDeviceLister::MakeDeviceUrls(const QString& serial) { + if (IsMTPSerial(serial)) { + const MTPDevice& device = mtp_devices_[serial]; + QString str; + str.sprintf("gphoto2://usb-%d-%d/", device.bus, device.address); + QUrl url(str); + url.addQueryItem("vendor", device.vendor); + url.addQueryItem("vendor_id", QString::number(device.vendor_id)); + url.addQueryItem("product", device.product); + url.addQueryItem("product_id", QString::number(device.product_id)); + url.addQueryItem("quirks", QString::number(device.quirks)); + return QList() << url; + } + QString bsd_name = current_devices_[serial]; DASessionRef session = DASessionCreate(kCFAllocatorDefault); DADiskRef disk = DADiskCreateFromBSDName( @@ -506,10 +554,13 @@ QList MacDeviceLister::MakeDeviceUrls(const QString& serial) { } QStringList MacDeviceLister::DeviceUniqueIDs() { - return current_devices_.keys(); + return current_devices_.keys() + mtp_devices_.keys(); } QVariantList MacDeviceLister::DeviceIcons(const QString& serial) { + if (IsMTPSerial(serial)) { + return QVariantList(); + } QString bsd_name = current_devices_[serial]; DASessionRef session = DASessionCreate(kCFAllocatorDefault); DADiskRef disk = DADiskCreateFromBSDName( @@ -538,14 +589,23 @@ QVariantList MacDeviceLister::DeviceIcons(const QString& serial) { } QString MacDeviceLister::DeviceManufacturer(const QString& serial){ + if (IsMTPSerial(serial)) { + return mtp_devices_[serial].vendor; + } return FindDeviceProperty(current_devices_[serial], CFSTR(kUSBVendorString)); } QString MacDeviceLister::DeviceModel(const QString& serial){ + if (IsMTPSerial(serial)) { + return mtp_devices_[serial].product; + } return FindDeviceProperty(current_devices_[serial], CFSTR(kUSBProductString)); } quint64 MacDeviceLister::DeviceCapacity(const QString& serial){ + if (IsMTPSerial(serial)) { + return 10000000; + } QString bsd_name = current_devices_[serial]; DASessionRef session = DASessionCreate(kCFAllocatorDefault); DADiskRef disk = DADiskCreateFromBSDName( @@ -566,6 +626,9 @@ quint64 MacDeviceLister::DeviceCapacity(const QString& serial){ } quint64 MacDeviceLister::DeviceFreeSpace(const QString& serial){ + if (IsMTPSerial(serial)) { + return 10000000; + } QString bsd_name = current_devices_[serial]; DASessionRef session = DASessionCreate(kCFAllocatorDefault); DADiskRef disk = DADiskCreateFromBSDName( @@ -597,6 +660,8 @@ quint64 MacDeviceLister::DeviceFreeSpace(const QString& serial){ QVariantMap MacDeviceLister::DeviceHardwareInfo(const QString& serial){return QVariantMap();} void MacDeviceLister::UnmountDevice(const QString& serial) { + if (IsMTPSerial(serial)) return; + QString bsd_name = current_devices_[serial]; DADiskRef disk = DADiskCreateFromBSDName( kCFAllocatorDefault, loop_session_, bsd_name.toAscii().constData()); diff --git a/src/devices/mtpconnection.cpp b/src/devices/mtpconnection.cpp index e3d19fbfc..69dfa9e40 100644 --- a/src/devices/mtpconnection.cpp +++ b/src/devices/mtpconnection.cpp @@ -19,9 +19,10 @@ #include #include -MtpConnection::MtpConnection(const QString& hostname) +MtpConnection::MtpConnection(const QUrl& url) : device_(NULL) { + QString hostname = url.host(); // Parse the URL QRegExp host_re("^usb-(\\d+)-(\\d+)$"); @@ -33,6 +34,23 @@ MtpConnection::MtpConnection(const QString& hostname) const unsigned int bus_location = host_re.cap(1).toInt(); const unsigned int device_num = host_re.cap(2).toInt(); + if (url.hasQueryItem("vendor")) { + LIBMTP_raw_device_t* raw_device = (LIBMTP_raw_device_t*)malloc(sizeof(LIBMTP_raw_device_t)); + raw_device->device_entry.vendor = url.queryItemValue("vendor").toAscii().data(); + raw_device->device_entry.product = url.queryItemValue("product").toAscii().data(); + raw_device->device_entry.vendor_id = url.queryItemValue("vendor_id").toUShort(); + raw_device->device_entry.product_id = url.queryItemValue("product_id").toUShort(); + raw_device->device_entry.device_flags = url.queryItemValue("quirks").toUInt(); + + raw_device->bus_location = bus_location; + raw_device->devnum = device_num; + + qDebug() << "\\o/" << url; + + device_ = LIBMTP_Open_Raw_Device(raw_device); + return; + } + // Get a list of devices from libmtp and figure out which one is ours int count = 0; LIBMTP_raw_device_t* raw_devices = NULL; diff --git a/src/devices/mtpconnection.h b/src/devices/mtpconnection.h index 34ab7840d..62476777d 100644 --- a/src/devices/mtpconnection.h +++ b/src/devices/mtpconnection.h @@ -17,13 +17,13 @@ #ifndef MTPCONNECTION_H #define MTPCONNECTION_H -#include +#include #include class MtpConnection { public: - MtpConnection(const QString& hostname); + MtpConnection(const QUrl& url); ~MtpConnection(); bool is_valid() const { return device_; } diff --git a/src/devices/mtpdevice.cpp b/src/devices/mtpdevice.cpp index 03e9632c0..37b0c4027 100644 --- a/src/devices/mtpdevice.cpp +++ b/src/devices/mtpdevice.cpp @@ -47,7 +47,7 @@ void MtpDevice::Init() { InitBackendDirectory("/", first_time_, false); model_->Init(); - loader_ = new MtpLoader(url_.host(), manager_->task_manager(), backend_, + loader_ = new MtpLoader(url_, manager_->task_manager(), backend_, shared_from_this()); loader_->moveToThread(loader_thread_); diff --git a/src/devices/mtploader.cpp b/src/devices/mtploader.cpp index bef3f2170..9b00082da 100644 --- a/src/devices/mtploader.cpp +++ b/src/devices/mtploader.cpp @@ -23,11 +23,11 @@ #include -MtpLoader::MtpLoader(const QString& hostname, TaskManager* task_manager, +MtpLoader::MtpLoader(const QUrl& url, TaskManager* task_manager, LibraryBackend* backend, boost::shared_ptr device) : QObject(NULL), device_(device), - hostname_(hostname), + url_(url), task_manager_(task_manager), backend_(backend) { @@ -50,7 +50,7 @@ void MtpLoader::LoadDatabase() { } bool MtpLoader::TryLoad() { - MtpConnection dev(hostname_); + MtpConnection dev(url_); if (!dev.is_valid()) { emit Error(tr("Error connecting MTP device")); return false; @@ -65,7 +65,7 @@ bool MtpLoader::TryLoad() { Song song; song.InitFromMTP(track); song.set_directory_id(1); - song.set_filename("mtp://" + hostname_ + "/" + song.filename()); + song.set_filename("mtp://" + url_.host() + "/" + song.filename()); songs << song; tracks = tracks->next; diff --git a/src/devices/mtploader.h b/src/devices/mtploader.h index 40dc599f0..452547172 100644 --- a/src/devices/mtploader.h +++ b/src/devices/mtploader.h @@ -18,6 +18,7 @@ #define MTPLOADER_H #include +#include #include @@ -29,7 +30,7 @@ class MtpLoader : public QObject { Q_OBJECT public: - MtpLoader(const QString& hostname, TaskManager* task_manager, + MtpLoader(const QUrl& url, TaskManager* task_manager, LibraryBackend* backend, boost::shared_ptr device); ~MtpLoader(); @@ -48,7 +49,7 @@ private: boost::shared_ptr device_; QThread* original_thread_; - QString hostname_; + QUrl url_; TaskManager* task_manager_; LibraryBackend* backend_; };