2 * Copyright (C) 2005-2011 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
22 #include "PeripheralBusUSB.h"
23 #include "peripherals/Peripherals.h"
24 #include "utils/log.h"
26 using namespace PERIPHERALS;
28 #ifdef TARGET_DARWIN_OSX
29 #include <IOKit/IOCFPlugIn.h>
30 #include <IOKit/usb/IOUSBLib.h>
31 #include <IOKit/hid/IOHIDLib.h>
32 #include <IOKit/hid/IOHIDKeys.h>
34 typedef struct USBDevicePrivateData {
35 CPeripheralBusUSB *refCon;
36 CStdString deviceName;
37 io_object_t notification;
38 PeripheralScanResult result;
39 } USBDevicePrivateData;
42 CPeripheralBusUSB::CPeripheralBusUSB(CPeripherals *manager) :
43 CPeripheralBus(manager, PERIPHERAL_BUS_USB)
45 m_bNeedsPolling = false;
47 #ifdef TARGET_DARWIN_OSX
48 //set up the matching criteria for the devices we're interested in
49 //interested in instances of class IOUSBDevice and its subclasses
50 // match any usb device by not creating matching values in the dict
51 CFMutableDictionaryRef matching_dict = IOServiceMatching(kIOUSBDeviceClassName);
53 m_notify_port = IONotificationPortCreate(kIOMasterPortDefault);
54 CFRunLoopSourceRef run_loop_source = IONotificationPortGetRunLoopSource(m_notify_port);
55 CFRunLoopAddSource(CFRunLoopGetCurrent(), run_loop_source, kCFRunLoopCommonModes);
57 //add a notification callback for attach event
58 IOReturn result = IOServiceAddMatchingNotification(m_notify_port,
59 kIOFirstMatchNotification, matching_dict,
60 (IOServiceMatchingCallback)DeviceAttachCallback, this, &m_attach_iterator);
61 if (result == kIOReturnSuccess)
63 //call the callback to 'arm' the notification
64 DeviceAttachCallback(this, m_attach_iterator);
69 CPeripheralBusUSB::~CPeripheralBusUSB()
71 #ifdef TARGET_DARWIN_OSX
74 // remove the sleep notification port from the application runloop
75 CFRunLoopRemoveSource( CFRunLoopGetCurrent(),
76 IONotificationPortGetRunLoopSource(m_notify_port), kCFRunLoopDefaultMode );
77 IONotificationPortDestroy(m_notify_port);
80 if (m_attach_iterator)
82 IOObjectRelease(m_attach_iterator);
83 m_attach_iterator = 0;
88 bool CPeripheralBusUSB::PerformDeviceScan(PeripheralScanResults &results)
90 results = m_scan_results;
94 #ifdef TARGET_DARWIN_OSX
95 const PeripheralType CPeripheralBusUSB::GetType(int iDeviceClass)
100 return PERIPHERAL_HID;
102 return PERIPHERAL_NIC;
103 case kUSBMassStorageClass:
104 return PERIPHERAL_DISK;
105 //case USB_CLASS_PER_INTERFACE:
107 case kUSBPrintingClass:
108 //case USB_CLASS_PTP:
111 case kUSBVendorSpecificClass:
113 return PERIPHERAL_UNKNOWN;
117 void CPeripheralBusUSB::DeviceDetachCallback(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
119 if (messageType == kIOMessageServiceIsTerminated)
123 USBDevicePrivateData *privateDataRef = (USBDevicePrivateData*)refCon;
125 std::vector<PeripheralScanResult>::iterator it = privateDataRef->refCon->m_scan_results.m_results.begin();
126 while(it != privateDataRef->refCon->m_scan_results.m_results.end())
128 if (privateDataRef->result.m_strLocation == it->m_strLocation)
129 privateDataRef->refCon->m_scan_results.m_results.erase(it);
133 privateDataRef->refCon->ScanForDevices();
135 CLog::Log(LOGDEBUG, "HID Device Detach:%s, %s\n",
136 privateDataRef->deviceName.c_str(), privateDataRef->result.m_strLocation.c_str());
137 result = IOObjectRelease(privateDataRef->notification);
138 delete privateDataRef;
139 //release the service
140 result = IOObjectRelease(service);
144 void CPeripheralBusUSB::DeviceAttachCallback(CPeripheralBusUSB* refCon, io_iterator_t iterator)
146 io_service_t usbDevice;
147 while ((usbDevice = IOIteratorNext(iterator)))
151 io_name_t deviceName;
152 result = IORegistryEntryGetName(usbDevice, deviceName);
153 if (result != KERN_SUCCESS)
154 deviceName[0] = '\0';
157 IOCFPlugInInterface **devicePlugin;
158 result = IOCreatePlugInInterfaceForService(usbDevice,
159 kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &devicePlugin, &deviceScore);
160 if (result != kIOReturnSuccess)
162 IOObjectRelease(usbDevice);
166 IOUSBDeviceInterface **deviceInterface;
167 // Use the plugin interface to retrieve the device interface.
168 result = (*devicePlugin)->QueryInterface(devicePlugin,
169 CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*)&deviceInterface);
170 if (result != kIOReturnSuccess)
172 IODestroyPlugInInterface(devicePlugin);
173 IOObjectRelease(usbDevice);
177 // get vendor/product ids
183 result = (*deviceInterface)->GetDeviceVendor( deviceInterface, &vendorId);
184 result = (*deviceInterface)->GetDeviceProduct(deviceInterface, &productId);
185 result = (*deviceInterface)->GetLocationID( deviceInterface, &locationId);
186 result = (*deviceInterface)->GetDeviceClass( deviceInterface, &bDeviceClass);
188 // we only care about usb devices with an HID interface class
189 io_service_t usbInterface;
190 io_iterator_t interface_iterator;
191 IOUSBFindInterfaceRequest request;
192 request.bInterfaceClass = kUSBHIDInterfaceClass;
193 request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
194 request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
195 request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
196 result = (*deviceInterface)->CreateInterfaceIterator(deviceInterface, &request, &interface_iterator);
197 while ((usbInterface = IOIteratorNext(interface_iterator)))
199 SInt32 interfaceScore;
200 IOCFPlugInInterface **interfacePlugin;
201 //create intermediate plugin for interface access
202 result = IOCreatePlugInInterfaceForService(usbInterface,
203 kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &interfacePlugin, &interfaceScore);
204 if (result != kIOReturnSuccess)
206 IOObjectRelease(usbInterface);
209 IOUSBInterfaceInterface** interfaceInterface;
210 result = (*interfacePlugin)->QueryInterface(interfacePlugin,
211 CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (void**)&interfaceInterface);
212 if (result != kIOReturnSuccess)
214 IODestroyPlugInInterface(interfacePlugin);
215 IOObjectRelease(usbInterface);
219 // finally we can get to bInterfaceClass/bInterfaceProtocol
220 // we should also check for kHIDKeyboardInterfaceProtocol but
221 // some IR remotes that emulate an HID keyboard do not report this.
222 UInt8 bInterfaceClass;
223 result = (*interfaceInterface)->GetInterfaceClass(interfaceInterface, &bInterfaceClass);
224 if (bInterfaceClass == kUSBHIDInterfaceClass)
226 USBDevicePrivateData *privateDataRef;
227 privateDataRef = new USBDevicePrivateData;
228 // save the device info to our private data.
229 privateDataRef->refCon = refCon;
230 privateDataRef->deviceName = deviceName;
231 privateDataRef->result.m_iVendorId = vendorId;
232 privateDataRef->result.m_iProductId = productId;
233 privateDataRef->result.m_strLocation.Format("%d", locationId);
235 if (bDeviceClass == kUSBCompositeClass)
236 privateDataRef->result.m_type = refCon->GetType(bInterfaceClass);
238 privateDataRef->result.m_type = refCon->GetType(bDeviceClass);
240 if (!refCon->m_scan_results.ContainsResult(privateDataRef->result))
242 // register this usb device for an interest notification callback.
243 result = IOServiceAddInterestNotification(refCon->m_notify_port,
244 usbDevice, // service
245 kIOGeneralInterest, // interestType
246 (IOServiceInterestCallback)DeviceDetachCallback, // callback
247 privateDataRef, // refCon
248 &privateDataRef->notification); // notification
250 if (result == kIOReturnSuccess)
252 refCon->m_scan_results.m_results.push_back(privateDataRef->result);
253 CLog::Log(LOGDEBUG, "HID Device Attach:%s, %s\n",
254 deviceName, privateDataRef->result.m_strLocation.c_str());
258 delete privateDataRef;
263 delete privateDataRef;
265 // done with this device, only need one notification per device.
266 IODestroyPlugInInterface(interfacePlugin);
267 IOObjectRelease(usbInterface);
270 IODestroyPlugInInterface(interfacePlugin);
271 IOObjectRelease(usbInterface);
273 IODestroyPlugInInterface(devicePlugin);
274 IOObjectRelease(usbDevice);
276 refCon->ScanForDevices();