7 changed files with 792 additions and 6 deletions
@ -0,0 +1,207 @@
@@ -0,0 +1,207 @@
|
||||
/*!
|
||||
* \file qextserialenumerator.h |
||||
* \author Michal Policht |
||||
* \see QextSerialEnumerator |
||||
*/ |
||||
|
||||
#ifndef _QEXTSERIALENUMERATOR_H_ |
||||
#define _QEXTSERIALENUMERATOR_H_ |
||||
|
||||
|
||||
#include <QtCore/qglobal.h> |
||||
|
||||
#ifdef QEXTSERIALPORT_LIB |
||||
# define QEXTSERIALPORT_EXPORT Q_DECL_EXPORT |
||||
#else |
||||
# define QEXTSERIALPORT_EXPORT Q_DECL_IMPORT |
||||
#endif |
||||
|
||||
#include <QString> |
||||
#include <QList> |
||||
#include <QObject> |
||||
//#include "qextserialport_global.h"
|
||||
|
||||
#ifdef Q_OS_WIN |
||||
#include <windows.h> |
||||
#include <setupapi.h> |
||||
#include <dbt.h> |
||||
#endif /*Q_OS_WIN*/ |
||||
|
||||
#ifdef Q_OS_MAC |
||||
#include <IOKit/usb/IOUSBLib.h> |
||||
#endif |
||||
|
||||
/*!
|
||||
* Structure containing port information. |
||||
*/ |
||||
struct QextPortInfo { |
||||
QString portName; ///< Port name.
|
||||
QString physName; ///< Physical name.
|
||||
QString friendName; ///< Friendly name.
|
||||
QString enumName; ///< Enumerator name.
|
||||
int vendorID; ///< Vendor ID.
|
||||
int productID; ///< Product ID
|
||||
}; |
||||
|
||||
#ifdef Q_OS_WIN |
||||
#ifdef QT_GUI_LIB |
||||
#include <QWidget> |
||||
class QextSerialEnumerator; |
||||
|
||||
class QextSerialRegistrationWidget : public QWidget |
||||
{ |
||||
Q_OBJECT |
||||
public: |
||||
QextSerialRegistrationWidget( QextSerialEnumerator* qese ) { |
||||
this->qese = qese; |
||||
} |
||||
~QextSerialRegistrationWidget( ) { } |
||||
|
||||
protected: |
||||
QextSerialEnumerator* qese; |
||||
bool winEvent( MSG* message, long* result ); |
||||
}; |
||||
#endif // QT_GUI_LIB
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
/*!
|
||||
Provides list of ports available in the system. |
||||
|
||||
\section Usage |
||||
To poll the system for a list of connected devices, simply use getPorts(). Each |
||||
QextPortInfo structure will populated with information about the corresponding device. |
||||
|
||||
\b Example |
||||
\code |
||||
QList<QextPortInfo> ports = QextSerialEnumerator::getPorts(); |
||||
foreach( QextPortInfo port, ports ) { |
||||
// inspect port...
|
||||
} |
||||
\endcode |
||||
|
||||
To enable event-driven notification of device connection events, first call |
||||
setUpNotifications() and then connect to the deviceDiscovered() and deviceRemoved() |
||||
signals. Event-driven behavior is currently available only on Windows and OS X. |
||||
|
||||
\b Example |
||||
\code |
||||
QextSerialEnumerator* enumerator = new QextSerialEnumerator(); |
||||
connect(enumerator, SIGNAL(deviceDiscovered(const QextPortInfo &)), |
||||
myClass, SLOT(onDeviceDiscovered(const QextPortInfo &))); |
||||
connect(enumerator, SIGNAL(deviceRemoved(const QextPortInfo &)), |
||||
myClass, SLOT(onDeviceRemoved(const QextPortInfo &))); |
||||
\endcode |
||||
|
||||
\section Credits |
||||
Windows implementation is based on Zach Gorman's work from |
||||
<a href="http://www.codeproject.com">The Code Project</a> (http://www.codeproject.com/system/setupdi.asp).
|
||||
|
||||
OS X implementation, see |
||||
http://developer.apple.com/documentation/DeviceDrivers/Conceptual/AccessingHardware/AH_Finding_Devices/chapter_4_section_2.html
|
||||
|
||||
\author Michal Policht, Liam Staskawicz |
||||
*/ |
||||
class QEXTSERIALPORT_EXPORT QextSerialEnumerator : public QObject |
||||
{ |
||||
Q_OBJECT |
||||
public: |
||||
QextSerialEnumerator( ); |
||||
~QextSerialEnumerator( ); |
||||
|
||||
#ifdef Q_OS_WIN |
||||
LRESULT onDeviceChangeWin( WPARAM wParam, LPARAM lParam ); |
||||
private: |
||||
/*!
|
||||
* Get value of specified property from the registry. |
||||
* \param key handle to an open key. |
||||
* \param property property name. |
||||
* \return property value. |
||||
*/ |
||||
static QString getRegKeyValue(HKEY key, LPCTSTR property); |
||||
|
||||
/*!
|
||||
* Get specific property from registry. |
||||
* \param devInfo pointer to the device information set that contains the interface |
||||
* and its underlying device. Returned by SetupDiGetClassDevs() function. |
||||
* \param devData pointer to an SP_DEVINFO_DATA structure that defines the device instance. |
||||
* this is returned by SetupDiGetDeviceInterfaceDetail() function. |
||||
* \param property registry property. One of defined SPDRP_* constants. |
||||
* \return property string. |
||||
*/ |
||||
static QString getDeviceProperty(HDEVINFO devInfo, PSP_DEVINFO_DATA devData, DWORD property); |
||||
|
||||
/*!
|
||||
* Search for serial ports using setupapi. |
||||
* \param infoList list with result. |
||||
*/ |
||||
static void setupAPIScan(QList<QextPortInfo> & infoList); |
||||
void setUpNotificationWin( ); |
||||
static bool getDeviceDetailsWin( QextPortInfo* portInfo, HDEVINFO devInfo, |
||||
PSP_DEVINFO_DATA devData, WPARAM wParam = DBT_DEVICEARRIVAL ); |
||||
static void enumerateDevicesWin( const GUID & guidDev, QList<QextPortInfo>* infoList ); |
||||
bool matchAndDispatchChangedDevice(const QString & deviceID, const GUID & guid, WPARAM wParam); |
||||
#ifdef QT_GUI_LIB |
||||
QextSerialRegistrationWidget* notificationWidget; |
||||
#endif |
||||
#endif /*Q_OS_WIN*/ |
||||
|
||||
#ifdef Q_OS_UNIX |
||||
#ifdef Q_OS_MAC |
||||
private: |
||||
/*!
|
||||
* Search for serial ports using IOKit. |
||||
* \param infoList list with result. |
||||
*/ |
||||
static void scanPortsOSX(QList<QextPortInfo> & infoList); |
||||
static void iterateServicesOSX(io_object_t service, QList<QextPortInfo> & infoList); |
||||
static bool getServiceDetailsOSX( io_object_t service, QextPortInfo* portInfo ); |
||||
|
||||
void setUpNotificationOSX( ); |
||||
void onDeviceDiscoveredOSX( io_object_t service ); |
||||
void onDeviceTerminatedOSX( io_object_t service ); |
||||
friend void deviceDiscoveredCallbackOSX( void *ctxt, io_iterator_t serialPortIterator ); |
||||
friend void deviceTerminatedCallbackOSX( void *ctxt, io_iterator_t serialPortIterator ); |
||||
|
||||
IONotificationPortRef notificationPortRef; |
||||
|
||||
#else // Q_OS_MAC
|
||||
private: |
||||
/*!
|
||||
* Search for serial ports on unix. |
||||
* \param infoList list with result. |
||||
*/ |
||||
static void scanPortsNix(QList<QextPortInfo> & infoList); |
||||
#endif // Q_OS_MAC
|
||||
#endif /* Q_OS_UNIX */ |
||||
|
||||
public: |
||||
/*!
|
||||
Get list of ports. |
||||
\return list of ports currently available in the system. |
||||
*/ |
||||
static QList<QextPortInfo> getPorts(); |
||||
/*!
|
||||
Enable event-driven notifications of board discovery/removal. |
||||
*/ |
||||
void setUpNotifications( ); |
||||
|
||||
signals: |
||||
/*!
|
||||
A new device has been connected to the system. |
||||
|
||||
setUpNotifications() must be called first to enable event-driven device notifications. |
||||
Currently only implemented on Windows and OS X. |
||||
\param info The device that has been discovered. |
||||
*/ |
||||
void deviceDiscovered( const QextPortInfo & info ); |
||||
/*!
|
||||
A device has been disconnected from the system. |
||||
|
||||
setUpNotifications() must be called first to enable event-driven device notifications. |
||||
Currently only implemented on Windows and OS X. |
||||
\param info The device that was disconnected. |
||||
*/ |
||||
void deviceRemoved( const QextPortInfo & info ); |
||||
}; |
||||
|
||||
#endif /*_QEXTSERIALENUMERATOR_H_*/ |
@ -0,0 +1,288 @@
@@ -0,0 +1,288 @@
|
||||
|
||||
|
||||
|
||||
#include "qextserialenumerator.h" |
||||
#include <QDebug> |
||||
#include <QMetaType> |
||||
|
||||
#include <IOKit/serial/IOSerialKeys.h> |
||||
#include <CoreFoundation/CFNumber.h> |
||||
#include <sys/param.h> |
||||
|
||||
QextSerialEnumerator::QextSerialEnumerator( ) |
||||
{ |
||||
if( !QMetaType::isRegistered( QMetaType::type("QextPortInfo") ) ) |
||||
qRegisterMetaType<QextPortInfo>("QextPortInfo"); |
||||
} |
||||
|
||||
QextSerialEnumerator::~QextSerialEnumerator( ) |
||||
{ |
||||
IONotificationPortDestroy( notificationPortRef ); |
||||
} |
||||
|
||||
// static
|
||||
QList<QextPortInfo> QextSerialEnumerator::getPorts() |
||||
{ |
||||
QList<QextPortInfo> infoList; |
||||
io_iterator_t serialPortIterator = 0; |
||||
kern_return_t kernResult = KERN_FAILURE; |
||||
CFMutableDictionaryRef matchingDictionary; |
||||
|
||||
// first try to get any serialbsd devices, then try any USBCDC devices
|
||||
if( !(matchingDictionary = IOServiceMatching(kIOSerialBSDServiceValue) ) ) { |
||||
qWarning("IOServiceMatching returned a NULL dictionary."); |
||||
return infoList; |
||||
} |
||||
CFDictionaryAddValue(matchingDictionary, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes)); |
||||
|
||||
// then create the iterator with all the matching devices
|
||||
if( IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &serialPortIterator) != KERN_SUCCESS ) { |
||||
qCritical() << "IOServiceGetMatchingServices failed, returned" << kernResult; |
||||
return infoList; |
||||
} |
||||
iterateServicesOSX(serialPortIterator, infoList); |
||||
IOObjectRelease(serialPortIterator); |
||||
serialPortIterator = 0; |
||||
|
||||
if( !(matchingDictionary = IOServiceNameMatching("AppleUSBCDC")) ) { |
||||
qWarning("IOServiceNameMatching returned a NULL dictionary."); |
||||
return infoList; |
||||
} |
||||
|
||||
if( IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &serialPortIterator) != KERN_SUCCESS ) { |
||||
qCritical() << "IOServiceGetMatchingServices failed, returned" << kernResult; |
||||
return infoList; |
||||
} |
||||
iterateServicesOSX(serialPortIterator, infoList); |
||||
IOObjectRelease(serialPortIterator); |
||||
|
||||
return infoList; |
||||
} |
||||
|
||||
void QextSerialEnumerator::iterateServicesOSX(io_object_t service, QList<QextPortInfo> & infoList) |
||||
{ |
||||
// Iterate through all modems found.
|
||||
io_object_t usbService; |
||||
while( ( usbService = IOIteratorNext(service) ) ) |
||||
{ |
||||
QextPortInfo info; |
||||
info.vendorID = 0; |
||||
info.productID = 0; |
||||
getServiceDetailsOSX( usbService, &info ); |
||||
infoList.append(info); |
||||
} |
||||
} |
||||
|
||||
bool QextSerialEnumerator::getServiceDetailsOSX( io_object_t service, QextPortInfo* portInfo ) |
||||
{ |
||||
bool retval = true; |
||||
CFTypeRef bsdPathAsCFString = NULL; |
||||
CFTypeRef productNameAsCFString = NULL; |
||||
CFTypeRef vendorIdAsCFNumber = NULL; |
||||
CFTypeRef productIdAsCFNumber = NULL; |
||||
// check the name of the modem's callout device
|
||||
bsdPathAsCFString = IORegistryEntryCreateCFProperty(service, CFSTR(kIOCalloutDeviceKey), |
||||
kCFAllocatorDefault, 0); |
||||
|
||||
// wander up the hierarchy until we find the level that can give us the
|
||||
// vendor/product IDs and the product name, if available
|
||||
io_registry_entry_t parent; |
||||
kern_return_t kernResult = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent); |
||||
while( kernResult == KERN_SUCCESS && !vendorIdAsCFNumber && !productIdAsCFNumber ) |
||||
{ |
||||
if(!productNameAsCFString) |
||||
productNameAsCFString = IORegistryEntrySearchCFProperty(parent, |
||||
kIOServicePlane, |
||||
CFSTR("Product Name"), |
||||
kCFAllocatorDefault, 0); |
||||
vendorIdAsCFNumber = IORegistryEntrySearchCFProperty(parent, |
||||
kIOServicePlane, |
||||
CFSTR(kUSBVendorID), |
||||
kCFAllocatorDefault, 0); |
||||
productIdAsCFNumber = IORegistryEntrySearchCFProperty(parent, |
||||
kIOServicePlane, |
||||
CFSTR(kUSBProductID), |
||||
kCFAllocatorDefault, 0); |
||||
io_registry_entry_t oldparent = parent; |
||||
kernResult = IORegistryEntryGetParentEntry(parent, kIOServicePlane, &parent); |
||||
IOObjectRelease(oldparent); |
||||
} |
||||
|
||||
io_string_t ioPathName; |
||||
IORegistryEntryGetPath( service, kIOServicePlane, ioPathName ); |
||||
portInfo->physName = ioPathName; |
||||
|
||||
if( bsdPathAsCFString ) |
||||
{ |
||||
char path[MAXPATHLEN]; |
||||
if( CFStringGetCString((CFStringRef)bsdPathAsCFString, path, |
||||
PATH_MAX, kCFStringEncodingUTF8) ) |
||||
portInfo->portName = path; |
||||
CFRelease(bsdPathAsCFString); |
||||
} |
||||
|
||||
if(productNameAsCFString) |
||||
{ |
||||
char productName[MAXPATHLEN]; |
||||
if( CFStringGetCString((CFStringRef)productNameAsCFString, productName, |
||||
PATH_MAX, kCFStringEncodingUTF8) ) |
||||
portInfo->friendName = productName; |
||||
CFRelease(productNameAsCFString); |
||||
} |
||||
|
||||
if(vendorIdAsCFNumber) |
||||
{ |
||||
SInt32 vID; |
||||
if(CFNumberGetValue((CFNumberRef)vendorIdAsCFNumber, kCFNumberSInt32Type, &vID)) |
||||
portInfo->vendorID = vID; |
||||
CFRelease(vendorIdAsCFNumber); |
||||
} |
||||
|
||||
if(productIdAsCFNumber) |
||||
{ |
||||
SInt32 pID; |
||||
if(CFNumberGetValue((CFNumberRef)productIdAsCFNumber, kCFNumberSInt32Type, &pID)) |
||||
portInfo->productID = pID; |
||||
CFRelease(productIdAsCFNumber); |
||||
} |
||||
IOObjectRelease(service); |
||||
return retval; |
||||
} |
||||
|
||||
// IOKit callbacks registered via setupNotifications()
|
||||
void deviceDiscoveredCallbackOSX( void *ctxt, io_iterator_t serialPortIterator ); |
||||
void deviceTerminatedCallbackOSX( void *ctxt, io_iterator_t serialPortIterator ); |
||||
|
||||
void deviceDiscoveredCallbackOSX( void *ctxt, io_iterator_t serialPortIterator ) |
||||
{ |
||||
QextSerialEnumerator* qese = (QextSerialEnumerator*)ctxt; |
||||
io_object_t serialService; |
||||
while ((serialService = IOIteratorNext(serialPortIterator))) |
||||
qese->onDeviceDiscoveredOSX(serialService); |
||||
} |
||||
|
||||
void deviceTerminatedCallbackOSX( void *ctxt, io_iterator_t serialPortIterator ) |
||||
{ |
||||
QextSerialEnumerator* qese = (QextSerialEnumerator*)ctxt; |
||||
io_object_t serialService; |
||||
while ((serialService = IOIteratorNext(serialPortIterator))) |
||||
qese->onDeviceTerminatedOSX(serialService); |
||||
} |
||||
|
||||
/*
|
||||
A device has been discovered via IOKit. |
||||
Create a QextPortInfo if possible, and emit the signal indicating that we've found it. |
||||
*/ |
||||
void QextSerialEnumerator::onDeviceDiscoveredOSX( io_object_t service ) |
||||
{ |
||||
QextPortInfo info; |
||||
info.vendorID = 0; |
||||
info.productID = 0; |
||||
if( getServiceDetailsOSX( service, &info ) ) |
||||
emit deviceDiscovered( info ); |
||||
} |
||||
|
||||
/*
|
||||
Notification via IOKit that a device has been removed. |
||||
Create a QextPortInfo if possible, and emit the signal indicating that it's gone. |
||||
*/ |
||||
void QextSerialEnumerator::onDeviceTerminatedOSX( io_object_t service ) |
||||
{ |
||||
QextPortInfo info; |
||||
info.vendorID = 0; |
||||
info.productID = 0; |
||||
if( getServiceDetailsOSX( service, &info ) ) |
||||
emit deviceRemoved( info ); |
||||
} |
||||
|
||||
/*
|
||||
Create matching dictionaries for the devices we want to get notifications for, |
||||
and add them to the current run loop. Invoke the callbacks that will be responding |
||||
to these notifications once to arm them, and discover any devices that |
||||
are currently connected at the time notifications are setup. |
||||
*/ |
||||
void QextSerialEnumerator::setUpNotifications( ) |
||||
{ |
||||
kern_return_t kernResult; |
||||
mach_port_t masterPort; |
||||
CFRunLoopSourceRef notificationRunLoopSource; |
||||
CFMutableDictionaryRef classesToMatch; |
||||
CFMutableDictionaryRef cdcClassesToMatch; |
||||
io_iterator_t portIterator; |
||||
|
||||
kernResult = IOMasterPort(MACH_PORT_NULL, &masterPort); |
||||
if (KERN_SUCCESS != kernResult) { |
||||
qDebug() << "IOMasterPort returned:" << kernResult; |
||||
return; |
||||
} |
||||
|
||||
classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue); |
||||
if (classesToMatch == NULL) |
||||
qDebug("IOServiceMatching returned a NULL dictionary."); |
||||
else |
||||
CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes)); |
||||
|
||||
if( !(cdcClassesToMatch = IOServiceNameMatching("AppleUSBCDC") ) ) { |
||||
qWarning("couldn't create cdc matching dict"); |
||||
return; |
||||
} |
||||
|
||||
// Retain an additional reference since each call to IOServiceAddMatchingNotification consumes one.
|
||||
classesToMatch = (CFMutableDictionaryRef) CFRetain(classesToMatch); |
||||
cdcClassesToMatch = (CFMutableDictionaryRef) CFRetain(cdcClassesToMatch); |
||||
|
||||
notificationPortRef = IONotificationPortCreate(masterPort); |
||||
if(notificationPortRef == NULL) { |
||||
qDebug("IONotificationPortCreate return a NULL IONotificationPortRef."); |
||||
return; |
||||
} |
||||
|
||||
notificationRunLoopSource = IONotificationPortGetRunLoopSource(notificationPortRef); |
||||
if (notificationRunLoopSource == NULL) { |
||||
qDebug("IONotificationPortGetRunLoopSource returned NULL CFRunLoopSourceRef."); |
||||
return; |
||||
} |
||||
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), notificationRunLoopSource, kCFRunLoopDefaultMode); |
||||
|
||||
kernResult = IOServiceAddMatchingNotification(notificationPortRef, kIOMatchedNotification, classesToMatch, |
||||
deviceDiscoveredCallbackOSX, this, &portIterator); |
||||
if (kernResult != KERN_SUCCESS) { |
||||
qDebug() << "IOServiceAddMatchingNotification return:" << kernResult; |
||||
return; |
||||
} |
||||
|
||||
// arm the callback, and grab any devices that are already connected
|
||||
deviceDiscoveredCallbackOSX( this, portIterator ); |
||||
|
||||
kernResult = IOServiceAddMatchingNotification(notificationPortRef, kIOMatchedNotification, cdcClassesToMatch, |
||||
deviceDiscoveredCallbackOSX, this, &portIterator); |
||||
if (kernResult != KERN_SUCCESS) { |
||||
qDebug() << "IOServiceAddMatchingNotification return:" << kernResult; |
||||
return; |
||||
} |
||||
|
||||
// arm the callback, and grab any devices that are already connected
|
||||
deviceDiscoveredCallbackOSX( this, portIterator ); |
||||
|
||||
kernResult = IOServiceAddMatchingNotification(notificationPortRef, kIOTerminatedNotification, classesToMatch, |
||||
deviceTerminatedCallbackOSX, this, &portIterator); |
||||
if (kernResult != KERN_SUCCESS) { |
||||
qDebug() << "IOServiceAddMatchingNotification return:" << kernResult; |
||||
return; |
||||
} |
||||
|
||||
// arm the callback, and clear any devices that are terminated
|
||||
deviceTerminatedCallbackOSX( this, portIterator ); |
||||
|
||||
kernResult = IOServiceAddMatchingNotification(notificationPortRef, kIOTerminatedNotification, cdcClassesToMatch, |
||||
deviceTerminatedCallbackOSX, this, &portIterator); |
||||
if (kernResult != KERN_SUCCESS) { |
||||
qDebug() << "IOServiceAddMatchingNotification return:" << kernResult; |
||||
return; |
||||
} |
||||
|
||||
// arm the callback, and clear any devices that are terminated
|
||||
deviceTerminatedCallbackOSX( this, portIterator ); |
||||
} |
||||
|
@ -0,0 +1,75 @@
@@ -0,0 +1,75 @@
|
||||
|
||||
|
||||
|
||||
#include "qextserialenumerator.h" |
||||
#include <QDebug> |
||||
#include <QMetaType> |
||||
#include <QStringList> |
||||
#include <QDir> |
||||
|
||||
QextSerialEnumerator::QextSerialEnumerator( ) |
||||
{ |
||||
if( !QMetaType::isRegistered( QMetaType::type("QextPortInfo") ) ) |
||||
qRegisterMetaType<QextPortInfo>("QextPortInfo"); |
||||
} |
||||
|
||||
QextSerialEnumerator::~QextSerialEnumerator( ) |
||||
{ |
||||
} |
||||
|
||||
QList<QextPortInfo> QextSerialEnumerator::getPorts() |
||||
{ |
||||
QList<QextPortInfo> infoList; |
||||
#ifdef Q_OS_LINUX |
||||
QStringList portNamePrefixes, portNameList; |
||||
portNamePrefixes << "ttyS*"; // list normal serial ports first
|
||||
|
||||
QDir dir("/dev"); |
||||
portNameList = dir.entryList(portNamePrefixes, (QDir::System | QDir::Files), QDir::Name); |
||||
|
||||
// remove the values which are not serial ports for e.g. /dev/ttysa
|
||||
for (int i = 0; i < portNameList.size(); i++) { |
||||
bool ok; |
||||
QString current = portNameList.at(i); |
||||
// remove the ttyS part, and check, if the other part is a number
|
||||
current.remove(0,4).toInt(&ok, 10); |
||||
if (!ok) { |
||||
portNameList.removeAt(i); |
||||
i--; |
||||
} |
||||
} |
||||
|
||||
// get the non standard serial ports names
|
||||
// (USB-serial, bluetooth-serial, 18F PICs, and so on)
|
||||
// if you know an other name prefix for serial ports please let us know
|
||||
portNamePrefixes.clear(); |
||||
portNamePrefixes << "ttyACM*" << "ttyUSB*" << "rfcomm*"; |
||||
portNameList.append(dir.entryList(portNamePrefixes, (QDir::System | QDir::Files), QDir::Name)); |
||||
|
||||
foreach (QString str , portNameList) { |
||||
QextPortInfo inf; |
||||
inf.physName = "/dev/"+str; |
||||
inf.portName = str; |
||||
|
||||
if (str.contains("ttyS")) { |
||||
inf.friendName = "Serial port "+str.remove(0, 4); |
||||
} |
||||
else if (str.contains("ttyUSB")) { |
||||
inf.friendName = "USB-serial adapter "+str.remove(0, 6); |
||||
} |
||||
else if (str.contains("rfcomm")) { |
||||
inf.friendName = "Bluetooth-serial adapter "+str.remove(0, 6); |
||||
} |
||||
inf.enumName = "/dev"; // is there a more helpful name for this?
|
||||
infoList.append(inf); |
||||
} |
||||
#else |
||||
qCritical("Enumeration for POSIX systems (except Linux) is not implemented yet."); |
||||
#endif |
||||
return infoList; |
||||
} |
||||
|
||||
void QextSerialEnumerator::setUpNotifications( ) |
||||
{ |
||||
qCritical("Notifications for *Nix/FreeBSD are not implemented yet"); |
||||
} |
@ -0,0 +1,206 @@
@@ -0,0 +1,206 @@
|
||||
|
||||
|
||||
|
||||
#include "qextserialenumerator.h" |
||||
#include <QDebug> |
||||
#include <QMetaType> |
||||
|
||||
#include <objbase.h> |
||||
#include <initguid.h> |
||||
#include "qextserialport.h" |
||||
#include <QRegExp> |
||||
|
||||
QextSerialEnumerator::QextSerialEnumerator( ) |
||||
{ |
||||
if( !QMetaType::isRegistered( QMetaType::type("QextPortInfo") ) ) |
||||
qRegisterMetaType<QextPortInfo>("QextPortInfo"); |
||||
#if (defined QT_GUI_LIB) |
||||
notificationWidget = 0; |
||||
#endif // Q_OS_WIN
|
||||
} |
||||
|
||||
QextSerialEnumerator::~QextSerialEnumerator( ) |
||||
{ |
||||
#if (defined QT_GUI_LIB) |
||||
if( notificationWidget ) |
||||
delete notificationWidget; |
||||
#endif |
||||
} |
||||
|
||||
|
||||
|
||||
// see http://msdn.microsoft.com/en-us/library/ms791134.aspx for list of GUID classes
|
||||
#ifndef GUID_DEVCLASS_PORTS |
||||
DEFINE_GUID(GUID_DEVCLASS_PORTS, 0x4D36E978, 0xE325, 0x11CE, 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18 ); |
||||
#endif |
||||
|
||||
/* Gordon Schumacher's macros for TCHAR -> QString conversions and vice versa */ |
||||
#ifdef UNICODE |
||||
#define QStringToTCHAR(x) (wchar_t*) x.utf16() |
||||
#define PQStringToTCHAR(x) (wchar_t*) x->utf16() |
||||
#define TCHARToQString(x) QString::fromUtf16((ushort*)(x)) |
||||
#define TCHARToQStringN(x,y) QString::fromUtf16((ushort*)(x),(y)) |
||||
#else |
||||
#define QStringToTCHAR(x) x.local8Bit().constData() |
||||
#define PQStringToTCHAR(x) x->local8Bit().constData() |
||||
#define TCHARToQString(x) QString::fromLocal8Bit((x)) |
||||
#define TCHARToQStringN(x,y) QString::fromLocal8Bit((x),(y)) |
||||
#endif /*UNICODE*/ |
||||
|
||||
|
||||
//static
|
||||
QString QextSerialEnumerator::getRegKeyValue(HKEY key, LPCTSTR property) |
||||
{ |
||||
DWORD size = 0; |
||||
DWORD type; |
||||
RegQueryValueEx(key, property, NULL, NULL, NULL, & size); |
||||
BYTE* buff = new BYTE[size]; |
||||
QString result; |
||||
if( RegQueryValueEx(key, property, NULL, &type, buff, & size) == ERROR_SUCCESS ) |
||||
result = TCHARToQString(buff); |
||||
RegCloseKey(key); |
||||
delete [] buff; |
||||
return result; |
||||
} |
||||
|
||||
//static
|
||||
QString QextSerialEnumerator::getDeviceProperty(HDEVINFO devInfo, PSP_DEVINFO_DATA devData, DWORD property) |
||||
{ |
||||
DWORD buffSize = 0; |
||||
SetupDiGetDeviceRegistryProperty(devInfo, devData, property, NULL, NULL, 0, & buffSize); |
||||
BYTE* buff = new BYTE[buffSize]; |
||||
SetupDiGetDeviceRegistryProperty(devInfo, devData, property, NULL, buff, buffSize, NULL); |
||||
QString result = TCHARToQString(buff); |
||||
delete [] buff; |
||||
return result; |
||||
} |
||||
|
||||
QList<QextPortInfo> QextSerialEnumerator::getPorts() |
||||
{ |
||||
QList<QextPortInfo> ports; |
||||
enumerateDevicesWin(GUID_DEVCLASS_PORTS, &ports); |
||||
return ports; |
||||
} |
||||
|
||||
void QextSerialEnumerator::enumerateDevicesWin( const GUID & guid, QList<QextPortInfo>* infoList ) |
||||
{ |
||||
HDEVINFO devInfo; |
||||
if( (devInfo = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT)) != INVALID_HANDLE_VALUE) |
||||
{ |
||||
SP_DEVINFO_DATA devInfoData; |
||||
devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); |
||||
for(int i = 0; SetupDiEnumDeviceInfo(devInfo, i, &devInfoData); i++) |
||||
{ |
||||
QextPortInfo info; |
||||
info.productID = info.vendorID = 0; |
||||
getDeviceDetailsWin( &info, devInfo, &devInfoData ); |
||||
infoList->append(info); |
||||
} |
||||
SetupDiDestroyDeviceInfoList(devInfo); |
||||
} |
||||
} |
||||
|
||||
#ifdef QT_GUI_LIB |
||||
bool QextSerialRegistrationWidget::winEvent( MSG* message, long* result ) |
||||
{ |
||||
if ( message->message == WM_DEVICECHANGE ) { |
||||
qese->onDeviceChangeWin( message->wParam, message->lParam ); |
||||
*result = 1; |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
#endif |
||||
|
||||
void QextSerialEnumerator::setUpNotifications( ) |
||||
{ |
||||
#ifdef QT_GUI_LIB |
||||
if(notificationWidget) |
||||
return; |
||||
notificationWidget = new QextSerialRegistrationWidget(this); |
||||
|
||||
DEV_BROADCAST_DEVICEINTERFACE dbh; |
||||
ZeroMemory(&dbh, sizeof(dbh)); |
||||
dbh.dbcc_size = sizeof(dbh); |
||||
dbh.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; |
||||
CopyMemory(&dbh.dbcc_classguid, &GUID_DEVCLASS_PORTS, sizeof(GUID)); |
||||
if( RegisterDeviceNotification( notificationWidget->winId( ), &dbh, DEVICE_NOTIFY_WINDOW_HANDLE ) == NULL) |
||||
qWarning() << "RegisterDeviceNotification failed:" << GetLastError(); |
||||
// setting up notifications doesn't tell us about devices already connected
|
||||
// so get those manually
|
||||
foreach( QextPortInfo port, getPorts() ) |
||||
emit deviceDiscovered( port ); |
||||
#else |
||||
qWarning("QextSerialEnumerator: GUI not enabled - can't register for device notifications."); |
||||
#endif // QT_GUI_LIB
|
||||
} |
||||
|
||||
LRESULT QextSerialEnumerator::onDeviceChangeWin( WPARAM wParam, LPARAM lParam ) |
||||
{ |
||||
if ( DBT_DEVICEARRIVAL == wParam || DBT_DEVICEREMOVECOMPLETE == wParam ) |
||||
{ |
||||
PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)lParam; |
||||
if( pHdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE ) |
||||
{ |
||||
PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr; |
||||
// delimiters are different across APIs...change to backslash. ugh.
|
||||
QString deviceID = TCHARToQString(pDevInf->dbcc_name).toUpper().replace("#", "\\"); |
||||
|
||||
matchAndDispatchChangedDevice(deviceID, GUID_DEVCLASS_PORTS, wParam); |
||||
} |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
bool QextSerialEnumerator::matchAndDispatchChangedDevice(const QString & deviceID, const GUID & guid, WPARAM wParam) |
||||
{ |
||||
bool rv = false; |
||||
DWORD dwFlag = (DBT_DEVICEARRIVAL == wParam) ? DIGCF_PRESENT : DIGCF_ALLCLASSES; |
||||
HDEVINFO devInfo; |
||||
if( (devInfo = SetupDiGetClassDevs(&guid,NULL,NULL,dwFlag)) != INVALID_HANDLE_VALUE ) |
||||
{ |
||||
SP_DEVINFO_DATA spDevInfoData; |
||||
spDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA); |
||||
for(int i=0; SetupDiEnumDeviceInfo(devInfo, i, &spDevInfoData); i++) |
||||
{ |
||||
DWORD nSize=0 ; |
||||
TCHAR buf[MAX_PATH]; |
||||
if ( SetupDiGetDeviceInstanceId(devInfo, &spDevInfoData, buf, MAX_PATH, &nSize) && |
||||
deviceID.contains(TCHARToQString(buf))) // we found a match
|
||||
{ |
||||
rv = true; |
||||
QextPortInfo info; |
||||
info.productID = info.vendorID = 0; |
||||
getDeviceDetailsWin( &info, devInfo, &spDevInfoData, wParam ); |
||||
if( wParam == DBT_DEVICEARRIVAL ) |
||||
emit deviceDiscovered(info); |
||||
else if( wParam == DBT_DEVICEREMOVECOMPLETE ) |
||||
emit deviceRemoved(info); |
||||
break; |
||||
} |
||||
} |
||||
SetupDiDestroyDeviceInfoList(devInfo); |
||||
} |
||||
return rv; |
||||
} |
||||
|
||||
bool QextSerialEnumerator::getDeviceDetailsWin( QextPortInfo* portInfo, HDEVINFO devInfo, PSP_DEVINFO_DATA devData, WPARAM wParam ) |
||||
{ |
||||
portInfo->friendName = getDeviceProperty(devInfo, devData, SPDRP_FRIENDLYNAME); |
||||
if( wParam == DBT_DEVICEARRIVAL) |
||||
portInfo->physName = getDeviceProperty(devInfo, devData, SPDRP_PHYSICAL_DEVICE_OBJECT_NAME); |
||||
portInfo->enumName = getDeviceProperty(devInfo, devData, SPDRP_ENUMERATOR_NAME); |
||||
QString hardwareIDs = getDeviceProperty(devInfo, devData, SPDRP_HARDWAREID); |
||||
HKEY devKey = SetupDiOpenDevRegKey(devInfo, devData, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ); |
||||
portInfo->portName = QextSerialPort::fullPortNameWin( getRegKeyValue(devKey, TEXT("PortName")) ); |
||||
QRegExp idRx("VID_(\\w+)&PID_(\\w+)"); |
||||
if( hardwareIDs.toUpper().contains(idRx) ) |
||||
{ |
||||
bool dummy; |
||||
portInfo->vendorID = idRx.cap(1).toInt(&dummy, 16); |
||||
portInfo->productID = idRx.cap(2).toInt(&dummy, 16); |
||||
//qDebug() << "got vid:" << vid << "pid:" << pid;
|
||||
} |
||||
return true; |
||||
} |
||||
|
Loading…
Reference in new issue