1
0
mirror of https://github.com/clementine-player/Clementine synced 2024-12-14 18:35:16 +01:00

Clean up Mac MTP detection code.

This commit is contained in:
John Maguire 2010-09-01 21:43:23 +00:00
parent cce7fadc89
commit 08f257d3ee

View File

@ -14,6 +14,8 @@
#import <Foundation/NSString.h>
#import <Foundation/NSURL.h>
#include <boost/scope_exit.hpp>
#include <libmtp.h>
#include <QtDebug>
@ -269,6 +271,31 @@ void MacDeviceLister::DiskRemovedCallback(DADiskRef disk, void* context) {
}
}
bool DeviceRequest(IOUSBDeviceInterface** dev,
quint8 direction,
quint8 type,
quint8 recipient,
quint8 request_code,
quint16 value,
quint16 index,
quint16 length,
QByteArray* data) {
IOUSBDevRequest req;
req.bmRequestType = USBmakebmRequestType(direction, type, recipient);
req.bRequest = request_code;
req.wValue = value;
req.wIndex = index;
req.wLength = length;
data->resize(256);
req.pData = data->data();
kern_return_t err = (*dev)->DeviceRequest(dev, &req);
if (err != kIOReturnSuccess) {
return false;
}
data->resize(req.wLenDone);
return true;
}
void MacDeviceLister::USBDeviceAddedCallback(void* refcon, io_iterator_t it) {
MacDeviceLister* me = reinterpret_cast<MacDeviceLister*>(refcon);
@ -287,6 +314,11 @@ void MacDeviceLister::USBDeviceAddedCallback(void* refcon, io_iterator_t it) {
device.vendor_id = [vendor_id unsignedShortValue];
device.product_id = [product_id unsignedShortValue];
if (device.vendor_id == 0x5ac) {
// I think we can safely skip Apple products.
continue;
}
qDebug() << device.vendor
<< device.vendor_id
<< device.product
@ -328,100 +360,59 @@ void MacDeviceLister::USBDeviceAddedCallback(void* refcon, io_iterator_t it) {
continue;
}
// Automatically close & release usb device at scope exit.
BOOST_SCOPE_EXIT((dev)) {
(*dev)->USBDeviceClose(dev);
(*dev)->Release(dev);
} BOOST_SCOPE_EXIT_END
// Request the string descriptor at 0xee.
// This is a magic string that indicates whether this device supports MTP.
// Fetch string descriptor length.
UInt8 desc[256]; // Max descriptor length.
IOUSBDevRequest req;
req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
req.bRequest = kUSBRqGetDescriptor;
req.wValue = (kUSBStringDesc << 8) | 0xee; // 0xee is the MTP descriptor.
req.wIndex = 0x0409; // English
req.wLength = 2;
req.pData = &desc;
err = (*dev)->DeviceRequest(dev, &req);
if (err != kIOReturnSuccess) {
QByteArray data;
bool ret = DeviceRequest(
dev, kUSBIn, kUSBStandard, kUSBDevice, kUSBRqGetDescriptor,
(kUSBStringDesc << 8) | 0xee, 0x0409, 2, &data);
if (!ret)
continue;
}
UInt8 string_len = desc[0];
if (string_len == 0) {
UInt8 string_len = data[0];
ret = DeviceRequest(
dev, kUSBIn, kUSBStandard, kUSBDevice, kUSBRqGetDescriptor,
(kUSBStringDesc << 8) | 0xee, 0x0409, string_len, &data);
if (!ret)
continue;
}
// Fetch actual string descriptor.
req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
req.bRequest = kUSBRqGetDescriptor;
req.wValue = (kUSBStringDesc << 8) | 0xee;
req.wIndex = 0x0409;
req.wLength = string_len;
req.pData = &desc;
err = (*dev)->DeviceRequest(dev, &req);
if (err != kIOReturnSuccess) {
continue;
}
// The device actually returned something. That's a good sign.
// Because this was designed by MS, the characters are in UTF-16 (LE?).
CFStringRef str = CFStringCreateWithCharacters(NULL, (const UniChar*)(desc + 2), (req.wLenDone-2) / 2);
char buf[256];
CFStringGetCString(str, buf, 256, kCFStringEncodingNonLossyASCII);
CFRelease(str);
QString str = QString::fromUtf16(reinterpret_cast<ushort*>(data.data() + 2), (data.size() / 2) - 2);
if (QString(buf).startsWith("MSFT100")) {
if (str.startsWith("MSFT100")) {
// We got the OS descriptor!
char vendor_code = desc[16];
req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBVendor, kUSBDevice);
req.bRequest = vendor_code;
req.wValue = 0;
req.wIndex = 4;
req.wLength = 256; // Magic number!
req.pData = &desc;
err = (*dev)->DeviceRequest(dev, &req);
if (err != kIOReturnSuccess) {
qDebug() << "Getting vendor OS descriptor failed";
char vendor_code = data[16];
ret = DeviceRequest(
dev, kUSBIn, kUSBVendor, kUSBDevice, vendor_code, 0, 4, 256, &data);
if (!ret || data.at(0) != 0x28)
continue;
}
// Moar magic!
if (desc[0] != 0x28) {
continue;
}
if (desc[0x12] != 'M' || desc[0x13] != 'T' || desc[0x14] != 'P') {
if (QString::fromAscii(data.data() + 0x12, 3) != "MTP") {
// Not quite.
continue;
}
req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBVendor, kUSBDevice);
req.bRequest = vendor_code;
req.wValue = 0;
req.wIndex = 5;
req.wLength = 256;
req.pData = &desc;
err = (*dev)->DeviceRequest(dev, &req);
if (err != kIOReturnSuccess || desc[0] != 0x28) {
ret = DeviceRequest(
dev, kUSBIn, kUSBVendor, kUSBDevice, vendor_code, 0, 5, 256, &data);
if (!ret || data.at(0) != 0x28) {
continue;
}
if (desc[0x12] != 'M' || desc[0x13] != 'T' || desc[0x14] != 'P') {
// :-(
if (QString::fromAscii(data.data() + 0x12, 3) != "MTP") {
// Not quite.
continue;
}
// Hurray! We made it!
qDebug() << "New MTP device detected!";
}
(*dev)->USBDeviceClose(dev);
(*dev)->Release(dev);
}
CFRelease(class_name);