only call IPlayerCallback::OnPlayBackSpeedChanged if the speed has actually changed
[vuplus_xbmc] / xbmc / peripherals / bus / osx / PeripheralBusUSB.cpp
1 /*
2  *      Copyright (C) 2005-2011 Team XBMC
3  *      http://xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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
19  *
20  */
21
22 #include "PeripheralBusUSB.h"
23 #include "peripherals/Peripherals.h"
24 #include "utils/log.h"
25
26 using namespace PERIPHERALS;
27
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>
33
34 typedef struct USBDevicePrivateData {
35   CPeripheralBusUSB     *refCon;
36   CStdString            deviceName;
37   io_object_t           notification;
38   PeripheralScanResult  result;
39 } USBDevicePrivateData;
40 #endif
41
42 CPeripheralBusUSB::CPeripheralBusUSB(CPeripherals *manager) :
43     CPeripheralBus(manager, PERIPHERAL_BUS_USB)
44 {
45   m_bNeedsPolling = false;
46
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);
52
53   m_notify_port = IONotificationPortCreate(kIOMasterPortDefault);
54   CFRunLoopSourceRef run_loop_source = IONotificationPortGetRunLoopSource(m_notify_port);
55   CFRunLoopAddSource(CFRunLoopGetCurrent(), run_loop_source, kCFRunLoopCommonModes);
56
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)
62   {
63     //call the callback to 'arm' the notification
64     DeviceAttachCallback(this, m_attach_iterator);
65   }
66 #endif
67 }
68
69 CPeripheralBusUSB::~CPeripheralBusUSB()
70 {
71 #ifdef TARGET_DARWIN_OSX
72   if (m_notify_port)
73   {
74     // remove the sleep notification port from the application runloop
75     CFRunLoopRemoveSource( CFRunLoopGetCurrent(),
76       IONotificationPortGetRunLoopSource(m_notify_port), kCFRunLoopDefaultMode );
77     IONotificationPortDestroy(m_notify_port);
78     m_notify_port = 0;
79   }
80   if (m_attach_iterator)
81   {
82     IOObjectRelease(m_attach_iterator);
83     m_attach_iterator = 0;
84   }
85 #endif
86 }
87
88 bool CPeripheralBusUSB::PerformDeviceScan(PeripheralScanResults &results)
89 {
90   results = m_scan_results;
91   return true;
92 }
93
94 #ifdef TARGET_DARWIN_OSX
95 const PeripheralType CPeripheralBusUSB::GetType(int iDeviceClass)
96 {
97   switch (iDeviceClass)
98   {
99   case kUSBHIDClass:
100     return PERIPHERAL_HID;
101   case kUSBCommClass:
102     return PERIPHERAL_NIC;
103   case kUSBMassStorageClass:
104     return PERIPHERAL_DISK;
105   //case USB_CLASS_PER_INTERFACE:
106   case kUSBAudioClass:
107   case kUSBPrintingClass:
108   //case USB_CLASS_PTP:
109   case kUSBHubClass:
110   case kUSBDataClass:
111   case kUSBVendorSpecificClass:
112   default:
113     return PERIPHERAL_UNKNOWN;
114   }
115 }
116
117 void CPeripheralBusUSB::DeviceDetachCallback(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
118
119   if (messageType == kIOMessageServiceIsTerminated)
120   {
121     IOReturn result;
122
123     USBDevicePrivateData *privateDataRef = (USBDevicePrivateData*)refCon;
124
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())
127     {
128       if (privateDataRef->result.m_strLocation == it->m_strLocation)
129         privateDataRef->refCon->m_scan_results.m_results.erase(it);
130       else
131         it++;
132     }
133     privateDataRef->refCon->ScanForDevices();
134     
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);
141   }
142 }
143
144 void CPeripheralBusUSB::DeviceAttachCallback(CPeripheralBusUSB* refCon, io_iterator_t iterator)
145 {
146   io_service_t usbDevice;
147   while ((usbDevice = IOIteratorNext(iterator)))
148   {
149     IOReturn  result;
150
151     io_name_t deviceName;
152     result = IORegistryEntryGetName(usbDevice, deviceName);
153                 if (result != KERN_SUCCESS)
154       deviceName[0] = '\0';
155
156     SInt32 deviceScore;
157     IOCFPlugInInterface **devicePlugin;
158     result = IOCreatePlugInInterfaceForService(usbDevice,
159       kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &devicePlugin, &deviceScore);
160     if (result != kIOReturnSuccess)
161     {
162       IOObjectRelease(usbDevice);
163       continue;
164     }
165
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)
171     {
172       IODestroyPlugInInterface(devicePlugin);
173       IOObjectRelease(usbDevice);
174       continue;
175     }
176
177     // get vendor/product ids
178     UInt16  vendorId;
179     UInt16  productId;
180     UInt32  locationId;
181     UInt8   bDeviceClass;
182
183     result = (*deviceInterface)->GetDeviceVendor( deviceInterface, &vendorId);
184     result = (*deviceInterface)->GetDeviceProduct(deviceInterface, &productId);
185     result = (*deviceInterface)->GetLocationID(   deviceInterface, &locationId);
186     result = (*deviceInterface)->GetDeviceClass(  deviceInterface, &bDeviceClass);
187
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)))
198     {
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)
205       {
206         IOObjectRelease(usbInterface);
207         continue;
208       }
209       IOUSBInterfaceInterface** interfaceInterface;
210       result = (*interfacePlugin)->QueryInterface(interfacePlugin,
211         CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (void**)&interfaceInterface);
212       if (result != kIOReturnSuccess)
213       {
214         IODestroyPlugInInterface(interfacePlugin);
215         IOObjectRelease(usbInterface);
216         continue;
217       }
218
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)
225       {
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);
234
235         if (bDeviceClass == kUSBCompositeClass)
236           privateDataRef->result.m_type = refCon->GetType(bInterfaceClass);
237         else
238           privateDataRef->result.m_type = refCon->GetType(bDeviceClass);
239
240         if (!refCon->m_scan_results.ContainsResult(privateDataRef->result))
241         {
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
249             
250           if (result == kIOReturnSuccess)
251           {
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());
255           }
256           else
257           {
258             delete privateDataRef;
259           }
260         }
261         else
262         {
263           delete privateDataRef;
264         }
265         // done with this device, only need one notification per device.
266         IODestroyPlugInInterface(interfacePlugin);
267         IOObjectRelease(usbInterface);
268         break;
269       }
270       IODestroyPlugInInterface(interfacePlugin);
271       IOObjectRelease(usbInterface);
272     }
273     IODestroyPlugInInterface(devicePlugin);
274     IOObjectRelease(usbDevice);
275   }
276   refCon->ScanForDevices();
277 }
278 #endif