You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
289 lines
0 B
289 lines
0 B
14 years ago
|
|
||
|
|
||
|
|
||
|
#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 );
|
||
|
}
|
||
|
|