USB management fix added to OS X

Task-number: QTCREATORBUG-4933

Change-Id: I03275344fbb85ce2bf3c83a8117075a8d45b12b5
Reviewed-on: http://codereview.qt.nokia.com/34
Reviewed-by: Paweł Polański <jaggernod@gmail.com>
This commit is contained in:
Tom Sutcliffe
2011-05-19 14:41:03 +01:00
committed by Paweł Polański
parent 715cee76a9
commit ec97fc95a0
3 changed files with 145 additions and 4 deletions

View File

@@ -269,8 +269,8 @@ QWidget *S60DeployConfigurationWidget::createCommunicationChannel()
serialPortHBoxLayout->addWidget(m_serialPortsCombo); serialPortHBoxLayout->addWidget(m_serialPortsCombo);
serialPortHBoxLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored)); serialPortHBoxLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored));
#ifndef Q_OS_WIN #if !defined(Q_OS_WIN) && !defined(Q_OS_MACX)
// Update device list: on Linux only. // Update device list: only needed on linux.
QToolButton *updateSerialDevicesButton(new QToolButton); QToolButton *updateSerialDevicesButton(new QToolButton);
updateSerialDevicesButton->setIcon(qApp->style()->standardIcon(QStyle::SP_BrowserReload)); updateSerialDevicesButton->setIcon(qApp->style()->standardIcon(QStyle::SP_BrowserReload));
connect(updateSerialDevicesButton, SIGNAL(clicked()), connect(updateSerialDevicesButton, SIGNAL(clicked()),

View File

@@ -49,6 +49,17 @@
#include <QtCore/QWaitCondition> #include <QtCore/QWaitCondition>
#include <QtCore/QTimer> #include <QtCore/QTimer>
#ifdef Q_OS_MACX
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/serial/IOSerialKeys.h>
#include <IOKit/usb/USBSpec.h>
#if defined(MAC_OS_X_VERSION_10_3) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_3)
#include <IOKit/serial/ioss.h>
#endif
#include <IOKit/IOBSD.h>
#endif
namespace SymbianUtils { namespace SymbianUtils {
enum { debug = 0 }; enum { debug = 0 };
@@ -266,7 +277,14 @@ SYMBIANUTILS_EXPORT QDebug operator<<(QDebug d, const SymbianDevice &cd)
// ------------- SymbianDeviceManagerPrivate // ------------- SymbianDeviceManagerPrivate
struct SymbianDeviceManagerPrivate { struct SymbianDeviceManagerPrivate {
SymbianDeviceManagerPrivate() : m_initialized(false), m_devicesLock(QMutex::Recursive) {} SymbianDeviceManagerPrivate() :
m_initialized(false),
m_devicesLock(QMutex::Recursive)
{
#ifdef Q_OS_MACX
m_deviceListChangedNotification = 0;
#endif
}
bool m_initialized; bool m_initialized;
SymbianDeviceManager::SymbianDeviceList m_devices; SymbianDeviceManager::SymbianDeviceList m_devices;
@@ -274,6 +292,9 @@ struct SymbianDeviceManagerPrivate {
// The following 2 variables are needed to manage requests for a CODA port not coming from the main thread // The following 2 variables are needed to manage requests for a CODA port not coming from the main thread
int m_constructCodaPortEventType; int m_constructCodaPortEventType;
QMutex m_codaPortWaitMutex; QMutex m_codaPortWaitMutex;
#ifdef Q_OS_MACX
IONotificationPortRef m_deviceListChangedNotification;
#endif
}; };
class QConstructCodaPortEvent : public QEvent class QConstructCodaPortEvent : public QEvent
@@ -298,6 +319,10 @@ SymbianDeviceManager::SymbianDeviceManager(QObject *parent) :
SymbianDeviceManager::~SymbianDeviceManager() SymbianDeviceManager::~SymbianDeviceManager()
{ {
#ifdef Q_OS_MACX
if (d && d->m_deviceListChangedNotification)
IONotificationPortDestroy(d->m_deviceListChangedNotification);
#endif
delete d; delete d;
} }
@@ -551,10 +576,30 @@ void SymbianDeviceManager::update(bool emitSignals)
qDebug("<SerialDeviceLister::update\n%s\n", qPrintable(toString())); qDebug("<SerialDeviceLister::update\n%s\n", qPrintable(toString()));
} }
#ifdef Q_OS_MACX
QString CFStringToQString(CFStringRef cfstring)
{
QString result;
int len = CFStringGetLength(cfstring);
result.resize(len);
CFStringGetCharacters(cfstring, CFRangeMake(0, len), static_cast<UniChar *>(result.data()));
return result;
}
void deviceListChanged(void *refCon, io_iterator_t iter)
{
// The way we're structured, it's easier to rescan rather than take advantage of the finer-grained device addition and removal info that OS X gives us
io_object_t obj;
while ((obj = IOIteratorNext(iter))) IOObjectRelease(obj);
static_cast<SymbianDeviceManager *>(refCon)->update();
}
#endif
SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::serialPorts() const SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::serialPorts() const
{ {
SymbianDeviceList rc; SymbianDeviceList rc;
#ifdef Q_OS_WIN #if defined(Q_OS_WIN)
const QSettings registry(REGKEY_CURRENT_CONTROL_SET, QSettings::NativeFormat); const QSettings registry(REGKEY_CURRENT_CONTROL_SET, QSettings::NativeFormat);
const QString usbSerialRootKey = QLatin1String(USBSER) + QLatin1Char('/'); const QString usbSerialRootKey = QLatin1String(USBSER) + QLatin1Char('/');
const int count = registry.value(usbSerialRootKey + QLatin1String("Count")).toInt(); const int count = registry.value(usbSerialRootKey + QLatin1String("Count")).toInt();
@@ -575,6 +620,100 @@ SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::serialPorts() cons
rc.append(SymbianDevice(device.take())); rc.append(SymbianDevice(device.take()));
} }
} }
#elif defined(Q_OS_MACX)
CFMutableDictionaryRef classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue);
if (!classesToMatch) return rc;
CFDictionarySetValue(classesToMatch,
CFSTR(kIOSerialBSDTypeKey),
CFSTR(kIOSerialBSDAllTypes));
// Setup notifier if necessary
if (d->m_deviceListChangedNotification == 0) {
d->m_deviceListChangedNotification = IONotificationPortCreate(kIOMasterPortDefault);
if (d->m_deviceListChangedNotification) {
CFRunLoopSourceRef runloopSource = IONotificationPortGetRunLoopSource(d->m_deviceListChangedNotification);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runloopSource, kCFRunLoopDefaultMode);
// IOServiceAddMatchingNotification eats a reference each time we call it, so make sure it's still valid for the IOServiceGetMatchingServices later
CFRetain(classesToMatch);
CFRetain(classesToMatch);
io_iterator_t devicesAddedIterator;
io_iterator_t devicesRemovedIterator;
IOServiceAddMatchingNotification(d->m_deviceListChangedNotification, kIOMatchedNotification, classesToMatch, &deviceListChanged, (void*)this, &devicesAddedIterator);
IOServiceAddMatchingNotification(d->m_deviceListChangedNotification, kIOTerminatedNotification, classesToMatch, &deviceListChanged, (void*)this, &devicesRemovedIterator);
// Arm the iterators - we're not interested in the lists at this point, and the API rather expects that we will be
io_object_t obj;
while ((obj = IOIteratorNext(devicesAddedIterator))) IOObjectRelease(obj);
while ((obj = IOIteratorNext(devicesRemovedIterator))) IOObjectRelease(obj);
}
}
io_iterator_t matchingServices;
kern_return_t kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, &matchingServices);
if (kernResult != KERN_SUCCESS) {
if (debug)
qDebug("IOServiceGetMatchingServices returned %d", kernResult);
return rc;
}
io_object_t serialPort;
kernResult = KERN_FAILURE;
while ((serialPort = IOIteratorNext(matchingServices))) {
// Check if it's Bluetooth or USB, and if so we can apply additional filters to weed out things that definitely aren't valid ports
io_object_t parent, grandparent;
kernResult = IORegistryEntryGetParentEntry(serialPort, kIOServicePlane, &parent);
bool match = true;
DeviceCommunicationType deviceType = SerialPortCommunication;
CFStringRef name = NULL;
if (kernResult == KERN_SUCCESS) {
kernResult = IORegistryEntryGetParentEntry(parent, kIOServicePlane, &grandparent);
if (kernResult == KERN_SUCCESS) {
CFStringRef className = IOObjectCopyClass(grandparent);
if (CFStringCompare(className, CFSTR("IOBluetoothSerialClient"), 0) == 0) {
// CODA doesn't support bluetooth and TRK makes connections back to the PC so we're not going to support it - use CODA :)
match = false;
}
else if (CFStringCompare(className, CFSTR("AppleUSBCDCACMData"), 0) == 0) {
match = false;
CFNumberRef interfaceNumber = (CFNumberRef)IORegistryEntrySearchCFProperty(grandparent, kIOServicePlane, CFSTR(kUSBInterfaceNumber), kCFAllocatorDefault, kIORegistryIterateParents | kIORegistryIterateRecursively);
if (interfaceNumber) {
int val;
if (CFNumberGetValue(interfaceNumber, kCFNumberIntType, &val) && val == 4) match = true;
CFRelease(interfaceNumber);
}
if (match) {
CFStringRef deviceName = (CFStringRef)IORegistryEntrySearchCFProperty(grandparent, kIOServicePlane, CFSTR(kUSBProductString), kCFAllocatorDefault, kIORegistryIterateParents | kIORegistryIterateRecursively);
CFStringRef vendorName = (CFStringRef)IORegistryEntrySearchCFProperty(grandparent, kIOServicePlane, CFSTR(kUSBVendorString), kCFAllocatorDefault, kIORegistryIterateParents | kIORegistryIterateRecursively);
if (deviceName && vendorName) name = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@ %@"), vendorName, deviceName);
if (deviceName) CFRelease(deviceName);
if (vendorName) CFRelease(vendorName);
}
}
else {
// We don't expect TRK/CODA on any other type of serial port
match = false;
}
CFRelease(className);
IOObjectRelease(grandparent);
}
IOObjectRelease(parent);
}
if (match) {
CFStringRef devPath = (CFStringRef)IORegistryEntryCreateCFProperty(serialPort, CFSTR(kIODialinDeviceKey), kCFAllocatorDefault, 0);
if (name == NULL)
name = (CFStringRef)IORegistryEntryCreateCFProperty(serialPort, CFSTR(kIOTTYBaseNameKey), kCFAllocatorDefault, 0);
QScopedPointer<SymbianDeviceData> device(new SymbianDeviceData);
device->type = deviceType;
device->friendlyName = CFStringToQString(name);
device->portName = CFStringToQString(devPath);
CFRelease(devPath);
CFRelease(name);
rc.append(SymbianDevice(device.take()));
}
IOObjectRelease(serialPort);
}
IOObjectRelease(matchingServices);
#endif #endif
return rc; return rc;
} }

View File

@@ -39,3 +39,5 @@ contains(QT, gui) {
} else { } else {
message(Trk: Console ...) message(Trk: Console ...)
} }
macx:LIBS += -framework IOKit -framework CoreFoundation