|
qCNC 0.82-alpha
|
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