diff options
Diffstat (limited to 'xbmc/platform/linux/peripherals')
5 files changed, 420 insertions, 0 deletions
diff --git a/xbmc/platform/linux/peripherals/CMakeLists.txt b/xbmc/platform/linux/peripherals/CMakeLists.txt new file mode 100644 index 0000000..626aa40 --- /dev/null +++ b/xbmc/platform/linux/peripherals/CMakeLists.txt @@ -0,0 +1,11 @@ +if(UDEV_FOUND) + list(APPEND SOURCES PeripheralBusUSBLibUdev.cpp) + list(APPEND HEADERS PeripheralBusUSBLibUdev.h) +elseif(LIBUSB_FOUND) + list(APPEND SOURCES PeripheralBusUSBLibUSB.cpp) + list(APPEND HEADERS PeripheralBusUSBLibUSB.h) +endif() + +if(SOURCES) + core_add_library(platform_linux_peripherals) +endif() diff --git a/xbmc/platform/linux/peripherals/PeripheralBusUSBLibUSB.cpp b/xbmc/platform/linux/peripherals/PeripheralBusUSBLibUSB.cpp new file mode 100644 index 0000000..5c788bb --- /dev/null +++ b/xbmc/platform/linux/peripherals/PeripheralBusUSBLibUSB.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "PeripheralBusUSBLibUSB.h" + +#include "peripherals/Peripherals.h" +#include "utils/StringUtils.h" +#include "utils/log.h" + +#include <usb.h> + +using namespace PERIPHERALS; + +CPeripheralBusUSB::CPeripheralBusUSB(CPeripherals& manager) : + CPeripheralBus("PeripBusUSB", manager, PERIPHERAL_BUS_USB) +{ + usb_init(); + usb_find_busses(); + m_busses = usb_get_busses(); + CLog::Log(LOGDEBUG, "{} - using libusb peripheral scanning", __FUNCTION__); +} + +bool CPeripheralBusUSB::PerformDeviceScan(PeripheralScanResults &results) +{ + struct usb_bus *bus; + usb_find_devices(); + for (bus = m_busses; bus; bus = bus->next) + { + struct usb_device *dev; + for (dev = bus->devices; dev; dev = dev->next) + { + PeripheralScanResult result(m_type); + result.m_iVendorId = dev->descriptor.idVendor; + result.m_iProductId = dev->descriptor.idProduct; + result.m_type = (dev->descriptor.bDeviceClass == USB_CLASS_PER_INTERFACE && dev->descriptor.bNumConfigurations > 0 && + dev->config[0].bNumInterfaces > 0 && dev->config[0].interface[0].num_altsetting > 0) ? + GetType(dev->config[0].interface[0].altsetting[0].bInterfaceClass) : + GetType(dev->descriptor.bDeviceClass); +#ifdef TARGET_FREEBSD + result.m_strLocation = std::to_string(dev->filename); +#else + result.m_strLocation = StringUtils::Format("/bus{}/dev{}", bus->dirname, dev->filename); +#endif + result.m_iSequence = GetNumberOfPeripheralsWithId(result.m_iVendorId, result.m_iProductId); + if (!results.ContainsResult(result)) + results.m_results.push_back(result); + } + } + + return true; +} + +const PeripheralType CPeripheralBusUSB::GetType(int iDeviceClass) +{ + switch (iDeviceClass) + { + case USB_CLASS_HID: + return PERIPHERAL_HID; + case USB_CLASS_COMM: + return PERIPHERAL_NIC; + case USB_CLASS_MASS_STORAGE: + return PERIPHERAL_DISK; + case USB_CLASS_PER_INTERFACE: + case USB_CLASS_AUDIO: + case USB_CLASS_PRINTER: + case USB_CLASS_PTP: + case USB_CLASS_HUB: + case USB_CLASS_DATA: + case USB_CLASS_VENDOR_SPEC: + default: + return PERIPHERAL_UNKNOWN; + } +} diff --git a/xbmc/platform/linux/peripherals/PeripheralBusUSBLibUSB.h b/xbmc/platform/linux/peripherals/PeripheralBusUSBLibUSB.h new file mode 100644 index 0000000..d2e7e48 --- /dev/null +++ b/xbmc/platform/linux/peripherals/PeripheralBusUSBLibUSB.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "peripherals/bus/PeripheralBus.h" +#include "peripherals/devices/Peripheral.h" + +struct usb_bus; + +namespace PERIPHERALS +{ + class CPeripherals; + + class CPeripheralBusUSB : public CPeripheralBus + { + public: + explicit CPeripheralBusUSB(CPeripherals& manager); + + /*! + * @see PeripheralBus::PerformDeviceScan() + */ + bool PerformDeviceScan(PeripheralScanResults &results); + + protected: + static const PeripheralType GetType(int iDeviceClass); + struct usb_bus *m_busses; + }; +} diff --git a/xbmc/platform/linux/peripherals/PeripheralBusUSBLibUdev.cpp b/xbmc/platform/linux/peripherals/PeripheralBusUSBLibUdev.cpp new file mode 100644 index 0000000..aeb5e71 --- /dev/null +++ b/xbmc/platform/linux/peripherals/PeripheralBusUSBLibUdev.cpp @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "PeripheralBusUSBLibUdev.h" +#include "peripherals/Peripherals.h" +extern "C" { +#include <libudev.h> +} +#include <cassert> +#include <poll.h> +#include "utils/log.h" + +#ifndef USB_CLASS_PER_INTERFACE +#define USB_CLASS_PER_INTERFACE 0 +#endif + +#ifndef USB_CLASS_AUDIO +#define USB_CLASS_AUDIO 1 +#endif + +#ifndef USB_CLASS_COMM +#define USB_CLASS_COMM 2 +#endif + +#ifndef USB_CLASS_HID +#define USB_CLASS_HID 3 +#endif + +#ifndef USB_CLASS_PHYSICAL +#define USB_CLASS_PHYSICAL 5 +#endif + +#ifndef USB_CLASS_PTP +#define USB_CLASS_PTP 6 +#endif + +#ifndef USB_CLASS_PRINTER +#define USB_CLASS_PRINTER 7 +#endif + +#ifndef USB_CLASS_MASS_STORAGE +#define USB_CLASS_MASS_STORAGE 8 +#endif + +#ifndef USB_CLASS_HUB +#define USB_CLASS_HUB 9 +#endif + +#ifndef USB_CLASS_DATA +#define USB_CLASS_DATA 10 +#endif + +#ifndef USB_CLASS_APP_SPEC +#define USB_CLASS_APP_SPEC 0xfe +#endif + +#ifndef USB_CLASS_VENDOR_SPEC +#define USB_CLASS_VENDOR_SPEC 0xff +#endif + +using namespace PERIPHERALS; + +CPeripheralBusUSB::CPeripheralBusUSB(CPeripherals& manager) : + CPeripheralBus("PeripBusUSBUdev", manager, PERIPHERAL_BUS_USB) +{ + /* the Process() method in this class overrides the one in CPeripheralBus, so leave this set to true */ + m_bNeedsPolling = true; + + m_udev = NULL; + m_udevMon = NULL; +} + +CPeripheralBusUSB::~CPeripheralBusUSB(void) +{ + StopThread(true); +} + +bool CPeripheralBusUSB::PerformDeviceScan(PeripheralScanResults &results) +{ + // We don't want this one to be called from outside world + assert(IsCurrentThread()); + + struct udev_enumerate *enumerate; + struct udev_list_entry *devices, *dev_list_entry; + struct udev_device *dev(NULL), *parent(NULL); + enumerate = udev_enumerate_new(m_udev); + udev_enumerate_scan_devices(enumerate); + devices = udev_enumerate_get_list_entry(enumerate); + + bool bContinue(true); + std::string strPath, strClass; + udev_list_entry_foreach(dev_list_entry, devices) + { + strPath = udev_list_entry_get_name(dev_list_entry); + if (strPath.empty()) + bContinue = false; + + if (bContinue) + { + if (!(parent = udev_device_new_from_syspath(m_udev, strPath.c_str()))) + bContinue = false; + } + + if (bContinue) + { + dev = udev_device_get_parent(udev_device_get_parent(parent)); + if (!dev || !udev_device_get_sysattr_value(dev,"idVendor") || !udev_device_get_sysattr_value(dev, "idProduct")) + bContinue = false; + } + + if (bContinue) + { + strClass = udev_device_get_sysattr_value(dev, "bDeviceClass"); + if (strClass.empty()) + bContinue = false; + } + + if (bContinue) + { + int iClass = PeripheralTypeTranslator::HexStringToInt(strClass.c_str()); + if (iClass == USB_CLASS_PER_INTERFACE) + { + //! @todo just assume this is a HID device for now, since the only devices that we're currently + //! interested in are HID devices + iClass = USB_CLASS_HID; + } + + PeripheralScanResult result(m_type); + result.m_iVendorId = PeripheralTypeTranslator::HexStringToInt(udev_device_get_sysattr_value(dev, "idVendor")); + result.m_iProductId = PeripheralTypeTranslator::HexStringToInt(udev_device_get_sysattr_value(dev, "idProduct")); + result.m_type = GetType(iClass); + result.m_strLocation = udev_device_get_syspath(dev); + result.m_iSequence = GetNumberOfPeripheralsWithId(result.m_iVendorId, result.m_iProductId); + if (!results.ContainsResult(result)) + results.m_results.push_back(result); + } + + bContinue = true; + if (parent) + { + /* unref the _parent_ device */ + udev_device_unref(parent); + parent = NULL; + } + } + /* Free the enumerator object */ + udev_enumerate_unref(enumerate); + + return true; +} + +PeripheralType CPeripheralBusUSB::GetType(int iDeviceClass) +{ + switch (iDeviceClass) + { + case USB_CLASS_HID: + return PERIPHERAL_HID; + case USB_CLASS_COMM: + return PERIPHERAL_NIC; + case USB_CLASS_MASS_STORAGE: + return PERIPHERAL_DISK; + case USB_CLASS_PER_INTERFACE: + case USB_CLASS_AUDIO: + case USB_CLASS_PRINTER: + case USB_CLASS_PTP: + case USB_CLASS_HUB: + case USB_CLASS_DATA: + case USB_CLASS_VENDOR_SPEC: + default: + return PERIPHERAL_UNKNOWN; + } +} + +void CPeripheralBusUSB::Process(void) +{ + if (!(m_udev = udev_new())) + { + CLog::Log(LOGERROR, "{} - failed to allocate udev context", __FUNCTION__); + return; + } + + /* set up a devices monitor that listen for any device change */ + m_udevMon = udev_monitor_new_from_netlink(m_udev, "udev"); + + /* filter to only receive usb events */ + if (udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "usb", nullptr) < 0) + { + CLog::Log(LOGERROR, "Could not limit filter on USB only"); + } + + CLog::Log(LOGDEBUG, "{} - initialised udev monitor", __FUNCTION__); + + udev_monitor_enable_receiving(m_udevMon); + bool bUpdated(false); + ScanForDevices(); + while (!m_bStop) + { + bUpdated = WaitForUpdate(); + if (bUpdated && !m_bStop) + ScanForDevices(); + } + udev_monitor_unref(m_udevMon); + udev_unref(m_udev); +} + +void CPeripheralBusUSB::Clear(void) +{ + StopThread(false); + + CPeripheralBus::Clear(); +} + +bool CPeripheralBusUSB::WaitForUpdate() +{ + int udevFd = udev_monitor_get_fd(m_udevMon); + + if (udevFd < 0) + { + CLog::Log(LOGERROR, "{} - get udev monitor", __FUNCTION__); + return false; + } + + /* poll for udev changes */ + struct pollfd pollFd; + pollFd.fd = udevFd; + pollFd.events = POLLIN; + int iPollResult; + while (!m_bStop && ((iPollResult = poll(&pollFd, 1, 100)) <= 0)) + if (errno != EINTR && iPollResult != 0) + break; + + /* the thread is being stopped, so just return false */ + if (m_bStop) + return false; + + /* we have to read the message from the queue, even though we're not actually using it */ + struct udev_device *dev = udev_monitor_receive_device(m_udevMon); + if (dev) + udev_device_unref(dev); + else + { + CLog::Log(LOGERROR, "{} - failed to get device from udev_monitor_receive_device()", + __FUNCTION__); + Clear(); + return false; + } + + return true; +} diff --git a/xbmc/platform/linux/peripherals/PeripheralBusUSBLibUdev.h b/xbmc/platform/linux/peripherals/PeripheralBusUSBLibUdev.h new file mode 100644 index 0000000..72cb437 --- /dev/null +++ b/xbmc/platform/linux/peripherals/PeripheralBusUSBLibUdev.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "peripherals/bus/PeripheralBus.h" +#include "peripherals/devices/Peripheral.h" + +struct udev; +struct udev_monitor; + +namespace PERIPHERALS +{ + class CPeripherals; + + class CPeripheralBusUSB : public CPeripheralBus + { + public: + explicit CPeripheralBusUSB(CPeripherals& manager); + ~CPeripheralBusUSB(void) override; + + void Clear(void) override; + + /*! + * @see PeripheralBus::PerformDeviceScan() + */ + bool PerformDeviceScan(PeripheralScanResults &results) override; + + protected: + static PeripheralType GetType(int iDeviceClass); + + void Process(void) override; + bool WaitForUpdate(void); + + struct udev * m_udev; + struct udev_monitor *m_udevMon; + }; +} |