qCNC 0.82-alpha
/home/trilog/Desktop/project/gui/Final/src/lib/qextserialport/qextserialenumerator_osx.cpp
Go to the documentation of this file.
00001 
00002 
00003 
00004 #include "qextserialenumerator.h"
00005 #include <QDebug>
00006 #include <QMetaType>
00007 
00008 #include <IOKit/serial/IOSerialKeys.h>
00009 #include <CoreFoundation/CFNumber.h>
00010 #include <sys/param.h>
00011 
00012 QextSerialEnumerator::QextSerialEnumerator( )
00013 {
00014     if( !QMetaType::isRegistered( QMetaType::type("QextPortInfo") ) )
00015         qRegisterMetaType<QextPortInfo>("QextPortInfo");
00016 }
00017 
00018 QextSerialEnumerator::~QextSerialEnumerator( )
00019 {
00020     IONotificationPortDestroy( notificationPortRef );
00021 }
00022 
00023 // static
00024 QList<QextPortInfo> QextSerialEnumerator::getPorts()
00025 {
00026     QList<QextPortInfo> infoList;
00027     io_iterator_t serialPortIterator = 0;
00028     kern_return_t kernResult = KERN_FAILURE;
00029     CFMutableDictionaryRef matchingDictionary;
00030 
00031     // first try to get any serialbsd devices, then try any USBCDC devices
00032     if( !(matchingDictionary = IOServiceMatching(kIOSerialBSDServiceValue) ) ) {
00033         qWarning("IOServiceMatching returned a NULL dictionary.");
00034         return infoList;
00035     }
00036     CFDictionaryAddValue(matchingDictionary, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes));
00037 
00038     // then create the iterator with all the matching devices
00039     if( IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &serialPortIterator) != KERN_SUCCESS ) {
00040         qCritical() << "IOServiceGetMatchingServices failed, returned" << kernResult;
00041         return infoList;
00042     }
00043     iterateServicesOSX(serialPortIterator, infoList);
00044     IOObjectRelease(serialPortIterator);
00045     serialPortIterator = 0;
00046 
00047     if( !(matchingDictionary = IOServiceNameMatching("AppleUSBCDC")) ) {
00048         qWarning("IOServiceNameMatching returned a NULL dictionary.");
00049         return infoList;
00050     }
00051 
00052     if( IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDictionary, &serialPortIterator) != KERN_SUCCESS ) {
00053         qCritical() << "IOServiceGetMatchingServices failed, returned" << kernResult;
00054         return infoList;
00055     }
00056     iterateServicesOSX(serialPortIterator, infoList);
00057     IOObjectRelease(serialPortIterator);
00058 
00059     return infoList;
00060 }
00061 
00062 void QextSerialEnumerator::iterateServicesOSX(io_object_t service, QList<QextPortInfo> & infoList)
00063 {
00064     // Iterate through all modems found.
00065     io_object_t usbService;
00066     while( ( usbService = IOIteratorNext(service) ) )
00067     {
00068         QextPortInfo info;
00069         info.vendorID = 0;
00070         info.productID = 0;
00071         getServiceDetailsOSX( usbService, &info );
00072         infoList.append(info);
00073     }
00074 }
00075 
00076 bool QextSerialEnumerator::getServiceDetailsOSX( io_object_t service, QextPortInfo* portInfo )
00077 {
00078     bool retval = true;
00079     CFTypeRef bsdPathAsCFString = NULL;
00080     CFTypeRef productNameAsCFString = NULL;
00081     CFTypeRef vendorIdAsCFNumber = NULL;
00082     CFTypeRef productIdAsCFNumber = NULL;
00083     // check the name of the modem's callout device
00084     bsdPathAsCFString = IORegistryEntryCreateCFProperty(service, CFSTR(kIOCalloutDeviceKey),
00085                                                         kCFAllocatorDefault, 0);
00086 
00087     // wander up the hierarchy until we find the level that can give us the
00088     // vendor/product IDs and the product name, if available
00089     io_registry_entry_t parent;
00090     kern_return_t kernResult = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent);
00091     while( kernResult == KERN_SUCCESS && !vendorIdAsCFNumber && !productIdAsCFNumber )
00092     {
00093         if(!productNameAsCFString)
00094             productNameAsCFString = IORegistryEntrySearchCFProperty(parent,
00095                                                                     kIOServicePlane,
00096                                                                     CFSTR("Product Name"),
00097                                                                     kCFAllocatorDefault, 0);
00098         vendorIdAsCFNumber = IORegistryEntrySearchCFProperty(parent,
00099                                                              kIOServicePlane,
00100                                                              CFSTR(kUSBVendorID),
00101                                                              kCFAllocatorDefault, 0);
00102         productIdAsCFNumber = IORegistryEntrySearchCFProperty(parent,
00103                                                               kIOServicePlane,
00104                                                               CFSTR(kUSBProductID),
00105                                                               kCFAllocatorDefault, 0);
00106         io_registry_entry_t oldparent = parent;
00107         kernResult = IORegistryEntryGetParentEntry(parent, kIOServicePlane, &parent);
00108         IOObjectRelease(oldparent);
00109     }
00110 
00111     io_string_t ioPathName;
00112     IORegistryEntryGetPath( service, kIOServicePlane, ioPathName );
00113     portInfo->physName = ioPathName;
00114 
00115     if( bsdPathAsCFString )
00116     {
00117         char path[MAXPATHLEN];
00118         if( CFStringGetCString((CFStringRef)bsdPathAsCFString, path,
00119                                PATH_MAX, kCFStringEncodingUTF8) )
00120             portInfo->portName = path;
00121         CFRelease(bsdPathAsCFString);
00122     }
00123 
00124     if(productNameAsCFString)
00125     {
00126         char productName[MAXPATHLEN];
00127         if( CFStringGetCString((CFStringRef)productNameAsCFString, productName,
00128                                PATH_MAX, kCFStringEncodingUTF8) )
00129             portInfo->friendName = productName;
00130         CFRelease(productNameAsCFString);
00131     }
00132 
00133     if(vendorIdAsCFNumber)
00134     {
00135         SInt32 vID;
00136         if(CFNumberGetValue((CFNumberRef)vendorIdAsCFNumber, kCFNumberSInt32Type, &vID))
00137             portInfo->vendorID = vID;
00138         CFRelease(vendorIdAsCFNumber);
00139     }
00140 
00141     if(productIdAsCFNumber)
00142     {
00143         SInt32 pID;
00144         if(CFNumberGetValue((CFNumberRef)productIdAsCFNumber, kCFNumberSInt32Type, &pID))
00145             portInfo->productID = pID;
00146         CFRelease(productIdAsCFNumber);
00147     }
00148     IOObjectRelease(service);
00149     return retval;
00150 }
00151 
00152 // IOKit callbacks registered via setupNotifications()
00153 void deviceDiscoveredCallbackOSX( void *ctxt, io_iterator_t serialPortIterator );
00154 void deviceTerminatedCallbackOSX( void *ctxt, io_iterator_t serialPortIterator );
00155 
00156 void deviceDiscoveredCallbackOSX( void *ctxt, io_iterator_t serialPortIterator )
00157 {
00158     QextSerialEnumerator* qese = (QextSerialEnumerator*)ctxt;
00159     io_object_t serialService;
00160     while ((serialService = IOIteratorNext(serialPortIterator)))
00161         qese->onDeviceDiscoveredOSX(serialService);
00162 }
00163 
00164 void deviceTerminatedCallbackOSX( void *ctxt, io_iterator_t serialPortIterator )
00165 {
00166     QextSerialEnumerator* qese = (QextSerialEnumerator*)ctxt;
00167     io_object_t serialService;
00168     while ((serialService = IOIteratorNext(serialPortIterator)))
00169         qese->onDeviceTerminatedOSX(serialService);
00170 }
00171 
00172 /*
00173   A device has been discovered via IOKit.
00174   Create a QextPortInfo if possible, and emit the signal indicating that we've found it.
00175 */
00176 void QextSerialEnumerator::onDeviceDiscoveredOSX( io_object_t service )
00177 {
00178     QextPortInfo info;
00179     info.vendorID = 0;
00180     info.productID = 0;
00181     if( getServiceDetailsOSX( service, &info ) )
00182         emit deviceDiscovered( info );
00183 }
00184 
00185 /*
00186   Notification via IOKit that a device has been removed.
00187   Create a QextPortInfo if possible, and emit the signal indicating that it's gone.
00188 */
00189 void QextSerialEnumerator::onDeviceTerminatedOSX( io_object_t service )
00190 {
00191     QextPortInfo info;
00192     info.vendorID = 0;
00193     info.productID = 0;
00194     if( getServiceDetailsOSX( service, &info ) )
00195         emit deviceRemoved( info );
00196 }
00197 
00198 /*
00199   Create matching dictionaries for the devices we want to get notifications for,
00200   and add them to the current run loop.  Invoke the callbacks that will be responding
00201   to these notifications once to arm them, and discover any devices that
00202   are currently connected at the time notifications are setup.
00203 */
00204 void QextSerialEnumerator::setUpNotifications( )
00205 {
00206     kern_return_t kernResult;
00207     mach_port_t masterPort;
00208     CFRunLoopSourceRef notificationRunLoopSource;
00209     CFMutableDictionaryRef classesToMatch;
00210     CFMutableDictionaryRef cdcClassesToMatch;
00211     io_iterator_t portIterator;
00212 
00213     kernResult = IOMasterPort(MACH_PORT_NULL, &masterPort);
00214     if (KERN_SUCCESS != kernResult) {
00215         qDebug() << "IOMasterPort returned:" << kernResult;
00216         return;
00217     }
00218 
00219     classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue);
00220     if (classesToMatch == NULL)
00221         qDebug("IOServiceMatching returned a NULL dictionary.");
00222     else
00223         CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes));
00224 
00225     if( !(cdcClassesToMatch = IOServiceNameMatching("AppleUSBCDC") ) ) {
00226         qWarning("couldn't create cdc matching dict");
00227         return;
00228     }
00229 
00230     // Retain an additional reference since each call to IOServiceAddMatchingNotification consumes one.
00231     classesToMatch = (CFMutableDictionaryRef) CFRetain(classesToMatch);
00232     cdcClassesToMatch = (CFMutableDictionaryRef) CFRetain(cdcClassesToMatch);
00233 
00234     notificationPortRef = IONotificationPortCreate(masterPort);
00235     if(notificationPortRef == NULL) {
00236         qDebug("IONotificationPortCreate return a NULL IONotificationPortRef.");
00237         return;
00238     }
00239 
00240     notificationRunLoopSource = IONotificationPortGetRunLoopSource(notificationPortRef);
00241     if (notificationRunLoopSource == NULL) {
00242         qDebug("IONotificationPortGetRunLoopSource returned NULL CFRunLoopSourceRef.");
00243         return;
00244     }
00245 
00246     CFRunLoopAddSource(CFRunLoopGetCurrent(), notificationRunLoopSource, kCFRunLoopDefaultMode);
00247 
00248     kernResult = IOServiceAddMatchingNotification(notificationPortRef, kIOMatchedNotification, classesToMatch,
00249                                                   deviceDiscoveredCallbackOSX, this, &portIterator);
00250     if (kernResult != KERN_SUCCESS) {
00251         qDebug() << "IOServiceAddMatchingNotification return:" << kernResult;
00252         return;
00253     }
00254 
00255     // arm the callback, and grab any devices that are already connected
00256     deviceDiscoveredCallbackOSX( this, portIterator );
00257 
00258     kernResult = IOServiceAddMatchingNotification(notificationPortRef, kIOMatchedNotification, cdcClassesToMatch,
00259                                                   deviceDiscoveredCallbackOSX, this, &portIterator);
00260     if (kernResult != KERN_SUCCESS) {
00261         qDebug() << "IOServiceAddMatchingNotification return:" << kernResult;
00262         return;
00263     }
00264 
00265     // arm the callback, and grab any devices that are already connected
00266     deviceDiscoveredCallbackOSX( this, portIterator );
00267 
00268     kernResult = IOServiceAddMatchingNotification(notificationPortRef, kIOTerminatedNotification, classesToMatch,
00269                                                   deviceTerminatedCallbackOSX, this, &portIterator);
00270     if (kernResult != KERN_SUCCESS) {
00271         qDebug() << "IOServiceAddMatchingNotification return:" << kernResult;
00272         return;
00273     }
00274 
00275     // arm the callback, and clear any devices that are terminated
00276     deviceTerminatedCallbackOSX( this, portIterator );
00277 
00278     kernResult = IOServiceAddMatchingNotification(notificationPortRef, kIOTerminatedNotification, cdcClassesToMatch,
00279                                                   deviceTerminatedCallbackOSX, this, &portIterator);
00280     if (kernResult != KERN_SUCCESS) {
00281         qDebug() << "IOServiceAddMatchingNotification return:" << kernResult;
00282         return;
00283     }
00284 
00285     // arm the callback, and clear any devices that are terminated
00286     deviceTerminatedCallbackOSX( this, portIterator );
00287 }
00288 
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Defines