6a3447568234c72d68be17d6eeed996da3157174
[vuplus_xbmc] / xbmc / peripherals / bus / linux / PeripheralBusUSBLibUdev.cpp
1 /*
2  *      Copyright (C) 2005-2013 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, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "PeripheralBusUSBLibUdev.h"
22 #include "peripherals/Peripherals.h"
23 extern "C" {
24 #include <libudev.h>
25 }
26 #include <poll.h>
27 #include "utils/log.h"
28
29 #ifndef USB_CLASS_PER_INTERFACE
30 #define USB_CLASS_PER_INTERFACE         0
31 #endif
32
33 #ifndef USB_CLASS_AUDIO
34 #define USB_CLASS_AUDIO                 1
35 #endif
36
37 #ifndef USB_CLASS_COMM
38 #define USB_CLASS_COMM                  2
39 #endif
40
41 #ifndef USB_CLASS_HID
42 #define USB_CLASS_HID                   3
43 #endif
44
45 #ifndef USB_CLASS_PHYSICAL
46 #define USB_CLASS_PHYSICAL              5
47 #endif
48
49 #ifndef USB_CLASS_PTP
50 #define USB_CLASS_PTP                   6
51 #endif
52
53 #ifndef USB_CLASS_PRINTER
54 #define USB_CLASS_PRINTER               7
55 #endif
56
57 #ifndef USB_CLASS_MASS_STORAGE
58 #define USB_CLASS_MASS_STORAGE          8
59 #endif
60
61 #ifndef USB_CLASS_HUB
62 #define USB_CLASS_HUB                   9
63 #endif
64
65 #ifndef USB_CLASS_DATA
66 #define USB_CLASS_DATA                  10
67 #endif
68
69 #ifndef USB_CLASS_APP_SPEC
70 #define USB_CLASS_APP_SPEC              0xfe
71 #endif
72
73 #ifndef USB_CLASS_VENDOR_SPEC
74 #define USB_CLASS_VENDOR_SPEC           0xff
75 #endif
76
77 using namespace PERIPHERALS;
78
79 CPeripheralBusUSB::CPeripheralBusUSB(CPeripherals *manager) :
80     CPeripheralBus("PeripBusUSBUdev", manager, PERIPHERAL_BUS_USB)
81 {
82   /* the Process() method in this class overrides the one in CPeripheralBus, so leave this set to true */
83   m_bNeedsPolling = true;
84
85   m_udev          = NULL;
86   m_udevMon       = NULL;
87
88   if (!(m_udev = udev_new()))
89   {
90     CLog::Log(LOGERROR, "%s - failed to allocate udev context", __FUNCTION__);
91     return;
92   }
93
94   /* set up a devices monitor that listen for any device change */
95   m_udevMon = udev_monitor_new_from_netlink(m_udev, "udev");
96   udev_monitor_enable_receiving(m_udevMon);
97
98   CLog::Log(LOGDEBUG, "%s - initialised udev monitor", __FUNCTION__);
99 }
100
101 CPeripheralBusUSB::~CPeripheralBusUSB(void)
102 {
103   StopThread(true);
104   udev_monitor_unref(m_udevMon);
105   udev_unref(m_udev);
106 }
107
108 bool CPeripheralBusUSB::PerformDeviceScan(PeripheralScanResults &results)
109 {
110   struct udev_enumerate *enumerate;
111   struct udev_list_entry *devices, *dev_list_entry;
112   struct udev_device *dev(NULL), *parent(NULL);
113   enumerate = udev_enumerate_new(m_udev);
114   udev_enumerate_scan_devices(enumerate);
115   devices = udev_enumerate_get_list_entry(enumerate);
116
117   bool bContinue(true);
118   CStdString strPath, strClass;
119   udev_list_entry_foreach(dev_list_entry, devices)
120   {
121     strPath = udev_list_entry_get_name(dev_list_entry);
122     if (strPath.IsEmpty())
123       bContinue = false;
124
125     if (bContinue)
126     {
127       if (!(parent = udev_device_new_from_syspath(m_udev, strPath)))
128         bContinue = false;
129     }
130
131     if (bContinue)
132     {
133       dev = udev_device_get_parent(udev_device_get_parent(parent));
134       if (!dev || !udev_device_get_sysattr_value(dev,"idVendor") || !udev_device_get_sysattr_value(dev, "idProduct"))
135         bContinue = false;
136     }
137
138     if (bContinue)
139     {
140       strClass = udev_device_get_sysattr_value(dev, "bDeviceClass");
141       if (strClass.IsEmpty())
142         bContinue = false;
143     }
144
145     if (bContinue)
146     {
147       int iClass = PeripheralTypeTranslator::HexStringToInt(strClass.c_str());
148       if (iClass == USB_CLASS_PER_INTERFACE)
149       {
150         //TODO just assume this is a HID device for now, since the only devices that we're currently
151         //     interested in are HID devices
152         iClass = USB_CLASS_HID;
153       }
154
155       PeripheralScanResult result(m_type);
156       result.m_iVendorId   = PeripheralTypeTranslator::HexStringToInt(udev_device_get_sysattr_value(dev, "idVendor"));
157       result.m_iProductId  = PeripheralTypeTranslator::HexStringToInt(udev_device_get_sysattr_value(dev, "idProduct"));
158       result.m_type        = GetType(iClass);
159       result.m_strLocation = udev_device_get_syspath(dev);
160       result.m_iSequence   = GetNumberOfPeripheralsWithId(result.m_iVendorId, result.m_iProductId);
161       if (!results.ContainsResult(result))
162         results.m_results.push_back(result);
163     }
164
165     bContinue = true;
166     if (parent)
167     {
168       /* unref the _parent_ device */
169       udev_device_unref(parent);
170       parent = NULL;
171     }
172   }
173   /* Free the enumerator object */
174   udev_enumerate_unref(enumerate);
175
176   return true;
177 }
178
179 const PeripheralType CPeripheralBusUSB::GetType(int iDeviceClass)
180 {
181   switch (iDeviceClass)
182   {
183   case USB_CLASS_HID:
184     return PERIPHERAL_HID;
185   case USB_CLASS_COMM:
186     return PERIPHERAL_NIC;
187   case USB_CLASS_MASS_STORAGE:
188     return PERIPHERAL_DISK;
189   case USB_CLASS_PER_INTERFACE:
190   case USB_CLASS_AUDIO:
191   case USB_CLASS_PRINTER:
192   case USB_CLASS_PTP:
193   case USB_CLASS_HUB:
194   case USB_CLASS_DATA:
195   case USB_CLASS_VENDOR_SPEC:
196   default:
197     return PERIPHERAL_UNKNOWN;
198   }
199 }
200
201 void CPeripheralBusUSB::Process(void)
202 {
203   bool bUpdated(false);
204   ScanForDevices();
205   while (!m_bStop)
206   {
207     bUpdated = WaitForUpdate();
208     if (bUpdated && !m_bStop)
209       ScanForDevices();
210   }
211
212   m_bIsStarted = false;
213 }
214
215 void CPeripheralBusUSB::Clear(void)
216 {
217   StopThread(false);
218
219   CPeripheralBus::Clear();
220 }
221
222 bool CPeripheralBusUSB::WaitForUpdate()
223 {
224   int udevFd = udev_monitor_get_fd(m_udevMon);
225
226   if (udevFd < 0)
227   {
228     CLog::Log(LOGERROR, "%s - get udev monitor", __FUNCTION__);
229     return false;
230   }
231
232   /* poll for udev changes */
233   struct pollfd pollFd;
234   pollFd.fd = udevFd;
235   pollFd.events = POLLIN;
236   int iPollResult;
237   while (!m_bStop && ((iPollResult = poll(&pollFd, 1, 100)) <= 0))
238     if (errno != EINTR && iPollResult != 0)
239       break;
240
241   /* the thread is being stopped, so just return false */
242   if (m_bStop)
243     return false;
244
245   /* we have to read the message from the queue, even though we're not actually using it */
246   struct udev_device *dev = udev_monitor_receive_device(m_udevMon);
247   if (dev)
248     udev_device_unref(dev);
249   else
250   {
251     CLog::Log(LOGERROR, "%s - failed to get device from udev_monitor_receive_device()", __FUNCTION__);
252     Clear();
253     return false;
254   }
255
256   return true;
257 }