From 48607a2d0774ffe8ce87d5d611d025eb985d1f6f Mon Sep 17 00:00:00 2001 From: Denis Shienkov Date: Wed, 2 Jul 2014 12:28:11 +0400 Subject: Refactor of the QSerialPortInfo for OS X The current implementation of the qserialportinfo_mac.cpp module is a little confused and complicated. It is reasonable to re-write it using some ready wrappers of CFTypeRef, CFStringRef and others objects from the private qcore_mac_p.h file. Thus, it will allow to bypass of manual releasing of the allocated resources (using the RAII idiom where it is possible), and also to simplify a code. Tested on OS X 10.8.4 with the on-board, USB PL2303, USB FTDI, USB Android serial ports and with the USB ZTE Modem using Qt4. Change-Id: Ibbac03a17fdd41bbaf530f863e7c690a15708bd2 Reviewed-by: Thiago Macieira Reviewed-by: Denis Shienkov --- src/serialport/qserialportinfo_mac.cpp | 312 ++++++++------------- .../qt4support/include/private/qcore_mac_p.h | 148 ++++++++++ 2 files changed, 267 insertions(+), 193 deletions(-) create mode 100644 src/serialport/qt4support/include/private/qcore_mac_p.h diff --git a/src/serialport/qserialportinfo_mac.cpp b/src/serialport/qserialportinfo_mac.cpp index d540b15..03f95e7 100644 --- a/src/serialport/qserialportinfo_mac.cpp +++ b/src/serialport/qserialportinfo_mac.cpp @@ -45,6 +45,8 @@ #include "qserialportinfo_p.h" #include "qserialport_unix_p.h" +#include "private/qcore_mac_p.h" + #include #include @@ -60,233 +62,157 @@ QT_BEGIN_NAMESPACE -QList QSerialPortInfo::availablePorts() +static QCFType searchProperty(io_registry_entry_t ioRegistryEntry, + const QCFString &propertyKey) { - QList serialPortInfoList; - - static const int propertyCount = 7; - - - ::CFMutableDictionaryRef matching = ::IOServiceMatching(kIOSerialBSDServiceValue); - if (!matching) - return serialPortInfoList; - - ::CFDictionaryAddValue(matching, - CFSTR(kIOSerialBSDTypeKey), - CFSTR(kIOSerialBSDAllTypes)); - - io_iterator_t iter = 0; - kern_return_t kr = ::IOServiceGetMatchingServices(kIOMasterPortDefault, - matching, - &iter); - - if (kr != kIOReturnSuccess) - return serialPortInfoList; - - io_registry_entry_t service; - - while ((service = ::IOIteratorNext(iter))) { - - ::CFTypeRef device = 0; - ::CFTypeRef portName = 0; - ::CFTypeRef description = 0; - ::CFTypeRef manufacturer = 0; - ::CFTypeRef serialNumber = 0; - ::CFTypeRef vendorIdentifier = 0; - ::CFTypeRef productIdentifier = 0; - - int matchingPropertiesCounter = 0; - - io_registry_entry_t entry = service; - - do { + return ::IORegistryEntrySearchCFProperty( + ioRegistryEntry, kIOServicePlane, propertyKey, kCFAllocatorDefault, 0); +} - if (!device) { - device = - ::IORegistryEntrySearchCFProperty(entry, - kIOServicePlane, - CFSTR(kIOCalloutDeviceKey), - kCFAllocatorDefault, - 0); - if (device) - ++matchingPropertiesCounter; - } +static QString searchStringProperty(io_registry_entry_t ioRegistryEntry, + const QCFString &propertyKey) +{ + const QCFString result(searchProperty(ioRegistryEntry, propertyKey).as()); + return QCFString::toQString(result); +} - if (!portName) { - portName = - ::IORegistryEntrySearchCFProperty(entry, - kIOServicePlane, - CFSTR(kIOTTYDeviceKey), - kCFAllocatorDefault, - 0); - if (portName) - ++matchingPropertiesCounter; - } +static quint16 searchShortIntProperty(io_registry_entry_t ioRegistryEntry, + const QCFString &propertyKey, + bool &ok) +{ + const QCFType result(searchProperty(ioRegistryEntry, propertyKey)); + quint16 value = 0; + ok = result.as() + && (::CFNumberGetValue(result.as(), kCFNumberShortType, &value) > 0); + return value; +} - if (!description) { - description = - ::IORegistryEntrySearchCFProperty(entry, - kIOServicePlane, - CFSTR(kIOPropertyProductNameKey), - kCFAllocatorDefault, - 0); - if (!description) - description = - ::IORegistryEntrySearchCFProperty(entry, - kIOServicePlane, - CFSTR(kUSBProductString), - kCFAllocatorDefault, - 0); - if (!description) - description = - ::IORegistryEntrySearchCFProperty(entry, - kIOServicePlane, - CFSTR("BTName"), - kCFAllocatorDefault, - 0); - - if (description) - ++matchingPropertiesCounter; - } +static bool isCompleteInfo(const QSerialPortInfo &portInfo) +{ + return !portInfo.portName().isEmpty() + && !portInfo.systemLocation().isEmpty() + && !portInfo.manufacturer().isEmpty() + && !portInfo.description().isEmpty() + && !portInfo.serialNumber().isEmpty() + && portInfo.hasProductIdentifier() + && portInfo.hasVendorIdentifier(); +} - if (!manufacturer) { - manufacturer = - ::IORegistryEntrySearchCFProperty(entry, - kIOServicePlane, - CFSTR(kUSBVendorString), - kCFAllocatorDefault, - 0); - if (manufacturer) - ++matchingPropertiesCounter; +static QString devicePortName(io_registry_entry_t ioRegistryEntry) +{ + return searchStringProperty(ioRegistryEntry, QCFString(kIOTTYDeviceKey)); +} - } +static QString deviceSystemLocation(io_registry_entry_t ioRegistryEntry) +{ + return searchStringProperty(ioRegistryEntry, QCFString(kIOCalloutDeviceKey)); +} - if (!serialNumber) { - serialNumber = - ::IORegistryEntrySearchCFProperty(entry, - kIOServicePlane, - CFSTR(kUSBSerialNumberString), - kCFAllocatorDefault, - 0); - if (serialNumber) - ++matchingPropertiesCounter; +static QString deviceDescription(io_registry_entry_t ioRegistryEntry) +{ + QString result = searchStringProperty(ioRegistryEntry, QCFString(kIOPropertyProductNameKey)); + if (result.isEmpty()) + result = searchStringProperty(ioRegistryEntry, QCFString(kUSBProductString)); + if (result.isEmpty()) + result = searchStringProperty(ioRegistryEntry, QCFString("BTName")); + return result; +} - } +static QString deviceManufacturer(io_registry_entry_t ioRegistryEntry) +{ + return searchStringProperty(ioRegistryEntry, QCFString(kUSBVendorString)); +} +static QString deviceSerialNumber(io_registry_entry_t ioRegistryEntry) +{ + return searchStringProperty(ioRegistryEntry, QCFString(kUSBSerialNumberString)); +} - if (!vendorIdentifier) { - vendorIdentifier = - ::IORegistryEntrySearchCFProperty(entry, - kIOServicePlane, - CFSTR(kUSBVendorID), - kCFAllocatorDefault, - 0); - if (vendorIdentifier) - ++matchingPropertiesCounter; +static quint16 deviceVendorIdentifier(io_registry_entry_t ioRegistryEntry, bool &ok) +{ + return searchShortIntProperty(ioRegistryEntry, QCFString(kUSBVendorID), ok); +} - } +static quint16 deviceProductIdentifier(io_registry_entry_t ioRegistryEntry, bool &ok) +{ + return searchShortIntProperty(ioRegistryEntry, QCFString(kUSBProductID), ok); +} - if (!productIdentifier) { - productIdentifier = - ::IORegistryEntrySearchCFProperty(entry, - kIOServicePlane, - CFSTR(kUSBProductID), - kCFAllocatorDefault, - 0); - if (productIdentifier) - ++matchingPropertiesCounter; +static io_registry_entry_t parentSerialPortService(io_registry_entry_t currentSerialPortService) +{ + io_registry_entry_t result = 0; + ::IORegistryEntryGetParentEntry(currentSerialPortService, kIOServicePlane, &result); + ::IOObjectRelease(currentSerialPortService); + return result; +} - } +QList QSerialPortInfo::availablePorts() +{ + CFMutableDictionaryRef serialPortDictionary = ::IOServiceMatching(kIOSerialBSDServiceValue); + if (!serialPortDictionary) + return QList(); - if (matchingPropertiesCounter == propertyCount) - break; + ::CFDictionaryAddValue(serialPortDictionary, + CFSTR(kIOSerialBSDTypeKey), + CFSTR(kIOSerialBSDAllTypes)); - kr = ::IORegistryEntryGetParentEntry(entry, kIOServicePlane, &entry); + io_iterator_t serialPortIterator = 0; + if (::IOServiceGetMatchingServices(kIOMasterPortDefault, serialPortDictionary, + &serialPortIterator) != KERN_SUCCESS) { + return QList(); + } - } while (kr == kIOReturnSuccess); + QList serialPortInfoList; - (void) ::IOObjectRelease(entry); + forever { + io_registry_entry_t serialPortService = ::IOIteratorNext(serialPortIterator); + if (!serialPortService) + break; - if (matchingPropertiesCounter > 0) { + QSerialPortInfo serialPortInfo; - QSerialPortInfo serialPortInfo; - QByteArray buffer(MAXPATHLEN, 0); + forever { + if (serialPortInfo.portName().isEmpty()) + serialPortInfo.d_ptr->portName = devicePortName(serialPortService); - if (device) { - if (::CFStringGetCString(CFStringRef(device), - buffer.data(), - buffer.size(), - kCFStringEncodingUTF8)) { - serialPortInfo.d_ptr->device = QString::fromUtf8(buffer); - } - ::CFRelease(device); - } + if (serialPortInfo.systemLocation().isEmpty()) + serialPortInfo.d_ptr->device = deviceSystemLocation(serialPortService); - if (portName) { - if (::CFStringGetCString(CFStringRef(portName), - buffer.data(), - buffer.size(), - kCFStringEncodingUTF8)) { - serialPortInfo.d_ptr->portName = QString::fromUtf8(buffer); - } - ::CFRelease(portName); - } + if (serialPortInfo.description().isEmpty()) + serialPortInfo.d_ptr->description = deviceDescription(serialPortService); - if (description) { - if (::CFStringGetCString(CFStringRef(description), - buffer.data(), - buffer.size(), - kCFStringEncodingUTF8)) { - serialPortInfo.d_ptr->description = QString::fromUtf8(buffer); - } - ::CFRelease(description); - } + if (serialPortInfo.manufacturer().isEmpty()) + serialPortInfo.d_ptr->manufacturer = deviceManufacturer(serialPortService); - if (manufacturer) { - if (::CFStringGetCString(CFStringRef(manufacturer), - buffer.data(), - buffer.size(), - kCFStringEncodingUTF8)) { - serialPortInfo.d_ptr->manufacturer = QString::fromUtf8(buffer); - } - ::CFRelease(manufacturer); - } + if (serialPortInfo.serialNumber().isEmpty()) + serialPortInfo.d_ptr->serialNumber = deviceSerialNumber(serialPortService); - if (serialNumber) { - if (::CFStringGetCString(CFStringRef(serialNumber), - buffer.data(), - buffer.size(), - kCFStringEncodingUTF8)) { - serialPortInfo.d_ptr->serialNumber = QString::fromUtf8(buffer); - } - ::CFRelease(serialNumber); + if (!serialPortInfo.hasVendorIdentifier()) { + serialPortInfo.d_ptr->vendorIdentifier = + deviceVendorIdentifier(serialPortService, + serialPortInfo.d_ptr->hasVendorIdentifier); } - quint16 value = 0; - - if (vendorIdentifier) { - serialPortInfo.d_ptr->hasVendorIdentifier = ::CFNumberGetValue(CFNumberRef(vendorIdentifier), kCFNumberIntType, &value); - if (serialPortInfo.d_ptr->hasVendorIdentifier) - serialPortInfo.d_ptr->vendorIdentifier = value; - - ::CFRelease(vendorIdentifier); + if (!serialPortInfo.hasProductIdentifier()) { + serialPortInfo.d_ptr->productIdentifier = + deviceProductIdentifier(serialPortService, + serialPortInfo.d_ptr->hasProductIdentifier); } - if (productIdentifier) { - serialPortInfo.d_ptr->hasProductIdentifier = ::CFNumberGetValue(CFNumberRef(productIdentifier), kCFNumberIntType, &value); - if (serialPortInfo.d_ptr->hasProductIdentifier) - serialPortInfo.d_ptr->productIdentifier = value; - - ::CFRelease(productIdentifier); + if (isCompleteInfo(serialPortInfo)) { + ::IOObjectRelease(serialPortService); + break; } - serialPortInfoList.append(serialPortInfo); + serialPortService = parentSerialPortService(serialPortService); + if (!serialPortService) + break; } - (void) ::IOObjectRelease(service); + serialPortInfoList.append(serialPortInfo); } - (void) ::IOObjectRelease(iter); + ::IOObjectRelease(serialPortIterator); return serialPortInfoList; } diff --git a/src/serialport/qt4support/include/private/qcore_mac_p.h b/src/serialport/qt4support/include/private/qcore_mac_p.h new file mode 100644 index 0000000..b893025 --- /dev/null +++ b/src/serialport/qt4support/include/private/qcore_mac_p.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCORE_MAC_P_H +#define QCORE_MAC_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef __IMAGECAPTURE__ +# define __IMAGECAPTURE__ +#endif + +#if defined(QT_BOOTSTRAPPED) +#include +#else +#include +#endif + +#include "qglobal.h" + +#ifdef Q_OS_MACX +#include +#endif + +#ifdef __OBJC__ +#include +#endif + +#include "qstring.h" + +#if defined( __OBJC__) && defined(QT_NAMESPACE) +#define QT_NAMESPACE_ALIAS_OBJC_CLASS(__KLASS__) @compatibility_alias __KLASS__ QT_MANGLE_NAMESPACE(__KLASS__) +#else +#define QT_NAMESPACE_ALIAS_OBJC_CLASS(__KLASS__) +#endif + +QT_BEGIN_NAMESPACE + +/* + Helper class that automates refernce counting for CFtypes. + After constructing the QCFType object, it can be copied like a + value-based type. + + Note that you must own the object you are wrapping. + This is typically the case if you get the object from a Core + Foundation function with the word "Create" or "Copy" in it. If + you got the object from a "Get" function, either retain it or use + constructFromGet(). One exception to this rule is the + HIThemeGet*Shape functions, which in reality are "Copy" functions. +*/ +template +class Q_CORE_EXPORT QCFType +{ +public: + inline QCFType(const T &t = 0) : type(t) {} + inline QCFType(const QCFType &helper) : type(helper.type) { if (type) CFRetain(type); } + inline ~QCFType() { if (type) CFRelease(type); } + inline operator T() { return type; } + inline QCFType operator =(const QCFType &helper) + { + if (helper.type) + CFRetain(helper.type); + CFTypeRef type2 = type; + type = helper.type; + if (type2) + CFRelease(type2); + return *this; + } + inline T *operator&() { return &type; } + template X as() const { return reinterpret_cast(type); } + static QCFType constructFromGet(const T &t) + { + CFRetain(t); + return QCFType(t); + } +protected: + T type; +}; + +class Q_CORE_EXPORT QCFString : public QCFType +{ +public: + inline QCFString(const QString &str) : QCFType(0), string(str) {} + inline QCFString(const CFStringRef cfstr = 0) : QCFType(cfstr) {} + inline QCFString(const QCFType &other) : QCFType(other) {} + operator QString() const; + operator CFStringRef() const; + static QString toQString(CFStringRef cfstr); + static CFStringRef toCFStringRef(const QString &str); +#ifdef __OBJC__ + static QString toQString(const NSString *nsstr); + static NSString *toNSString(const QString &string); +#endif + +private: + QString string; +}; + +QT_END_NAMESPACE + +#endif // QCORE_MAC_P_H -- cgit v1.2.1