hidraw support for udev; fix race condition

This commit is contained in:
Jack Andersen 2017-05-08 17:37:12 -10:00
parent ff89a9cccf
commit 7823aecc57
6 changed files with 109 additions and 32 deletions

View File

@ -60,6 +60,7 @@ std::shared_ptr<DeviceBase> DeviceSignature::DeviceNew(DeviceToken& token)
retval->m_hidDev = IHIDDeviceNew(token, *retval); retval->m_hidDev = IHIDDeviceNew(token, *retval);
if (!retval->m_hidDev) if (!retval->m_hidDev)
return nullptr; return nullptr;
retval->m_hidDev->_startThread();
return retval; return retval;
} }
@ -76,6 +77,7 @@ std::shared_ptr<DeviceBase> DeviceSignature::DeviceNew(DeviceToken& token)
retval->m_hidDev = IHIDDeviceNew(token, *retval); retval->m_hidDev = IHIDDeviceNew(token, *retval);
if (!retval->m_hidDev) if (!retval->m_hidDev)
return nullptr; return nullptr;
retval->m_hidDev->_startThread();
return retval; return retval;
} }

View File

@ -341,9 +341,13 @@ public:
: m_token(token), : m_token(token),
m_devImp(devImp), m_devImp(devImp),
m_devPath(token.getDevicePath()) m_devPath(token.getDevicePath())
{
}
void _startThread()
{ {
std::unique_lock<std::mutex> lk(m_initMutex); std::unique_lock<std::mutex> lk(m_initMutex);
DeviceType dType = token.getDeviceType(); DeviceType dType = m_token.getDeviceType();
if (dType == DeviceType::USB) if (dType == DeviceType::USB)
m_thread = std::thread(_threadProcUSBLL, this); m_thread = std::thread(_threadProcUSBLL, this);
else if (dType == DeviceType::Bluetooth) else if (dType == DeviceType::Bluetooth)

View File

@ -10,6 +10,8 @@
#include <stropts.h> #include <stropts.h>
#include <linux/usb/ch9.h> #include <linux/usb/ch9.h>
#include <linux/usbdevice_fs.h> #include <linux/usbdevice_fs.h>
#include <linux/input.h>
#include <linux/hidraw.h>
#include <fcntl.h> #include <fcntl.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <unistd.h> #include <unistd.h>
@ -147,7 +149,6 @@ class HIDDeviceUdev final : public IHIDDevice
close(fd); close(fd);
device->m_devFd = 0; device->m_devFd = 0;
udev_device_unref(udevDev); udev_device_unref(udevDev);
} }
static void _threadProcBTLL(HIDDeviceUdev* device) static void _threadProcBTLL(HIDDeviceUdev* device)
@ -171,20 +172,53 @@ class HIDDeviceUdev final : public IHIDDevice
static void _threadProcHID(HIDDeviceUdev* device) static void _threadProcHID(HIDDeviceUdev* device)
{ {
char errStr[256];
std::unique_lock<std::mutex> lk(device->m_initMutex); std::unique_lock<std::mutex> lk(device->m_initMutex);
udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.c_str()); udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.c_str());
/* Get device file */
const char* dp = udev_device_get_devnode(udevDev);
int fd = open(dp, O_RDWR | O_NONBLOCK);
if (fd < 0)
{
snprintf(errStr, 256, "Unable to open %s@%s: %s\n",
device->m_token.getProductName().c_str(), dp, strerror(errno));
device->m_devImp.deviceError(errStr);
lk.unlock();
device->m_initCond.notify_one();
udev_device_unref(udevDev);
return;
}
device->m_devFd = fd;
/* Return control to main thread */ /* Return control to main thread */
device->m_runningTransferLoop = true; device->m_runningTransferLoop = true;
lk.unlock(); lk.unlock();
device->m_initCond.notify_one(); device->m_initCond.notify_one();
/* Report input size */
size_t readSz = device->m_devImp.getInputBufferSize();
std::unique_ptr<uint8_t[]> readBuf(new uint8_t[readSz]);
/* Start transfer loop */ /* Start transfer loop */
device->m_devImp.initialCycle(); device->m_devImp.initialCycle();
while (device->m_runningTransferLoop) while (device->m_runningTransferLoop)
{
while (true)
{
ssize_t sz = read(fd, readBuf.get(), readSz);
if (sz < 0)
break;
device->m_devImp.receivedHIDReport(readBuf.get(), sz,
HIDReportType::Input, readBuf[0]);
}
device->m_devImp.transferCycle(); device->m_devImp.transferCycle();
}
device->m_devImp.finalCycle(); device->m_devImp.finalCycle();
/* Cleanup */
close(fd);
device->m_devFd = 0;
udev_device_unref(udevDev); udev_device_unref(udevDev);
} }
@ -193,10 +227,26 @@ class HIDDeviceUdev final : public IHIDDevice
m_runningTransferLoop = false; m_runningTransferLoop = false;
} }
bool _sendHIDReport(const uint8_t* data, size_t length, uint16_t message) bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
{ {
if (m_devFd) if (m_devFd)
{ {
if (tp == HIDReportType::Feature)
{
int ret = ioctl(m_devFd, HIDIOCSFEATURE(length), data);
if (ret < 0)
return false;
return true;
}
else if (tp == HIDReportType::Output)
{
ssize_t ret = write(m_devFd, data, length);
if (ret < 0)
return false;
return true;
}
/*
usbdevfs_ctrltransfer xfer = usbdevfs_ctrltransfer xfer =
{ {
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
@ -211,14 +261,25 @@ class HIDDeviceUdev final : public IHIDDevice
if (ret != (int)length) if (ret != (int)length)
return false; return false;
return true; return true;
*/
} }
return false; return false;
} }
size_t _recieveReport(const uint8_t *data, size_t length, uint16_t message) size_t _receiveHIDReport(uint8_t *data, size_t length, HIDReportType tp, uint32_t message)
{ {
if (m_devFd) if (m_devFd)
{ {
if (tp == HIDReportType::Feature)
{
data[0] = message;
int ret = ioctl(m_devFd, HIDIOCGFEATURE(length), data);
if (ret < 0)
return 0;
return length;
}
/*
usbdevfs_ctrltransfer xfer = usbdevfs_ctrltransfer xfer =
{ {
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
@ -230,6 +291,7 @@ class HIDDeviceUdev final : public IHIDDevice
(void*)data (void*)data
}; };
return ioctl(m_devFd, USBDEVFS_CONTROL, &xfer); return ioctl(m_devFd, USBDEVFS_CONTROL, &xfer);
*/
} }
return 0; return 0;
} }
@ -241,14 +303,17 @@ public:
m_devImp(devImp), m_devImp(devImp),
m_devPath(token.getDevicePath()) m_devPath(token.getDevicePath())
{ {
devImp.m_hidDev = this; }
void _startThread()
{
std::unique_lock<std::mutex> lk(m_initMutex); std::unique_lock<std::mutex> lk(m_initMutex);
DeviceToken::DeviceType dType = token.getDeviceType(); DeviceType dType = m_token.getDeviceType();
if (dType == DeviceToken::DeviceType::USB) if (dType == DeviceType::USB)
m_thread = std::thread(_threadProcUSBLL, this); m_thread = std::thread(_threadProcUSBLL, this);
else if (dType == DeviceToken::DeviceType::Bluetooth) else if (dType == DeviceType::Bluetooth)
m_thread = std::thread(_threadProcBTLL, this); m_thread = std::thread(_threadProcBTLL, this);
else if (dType == DeviceToken::DeviceType::GenericHID) else if (dType == DeviceType::HID)
m_thread = std::thread(_threadProcHID, this); m_thread = std::thread(_threadProcHID, this);
else else
{ {
@ -268,9 +333,9 @@ public:
}; };
IHIDDevice* IHIDDeviceNew(DeviceToken& token, DeviceBase& devImp) std::unique_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, DeviceBase& devImp)
{ {
return new HIDDeviceUdev(token, devImp); return std::make_unique<HIDDeviceUdev>(token, devImp);
} }
} }

View File

@ -325,9 +325,13 @@ public:
: m_token(token), : m_token(token),
m_devImp(devImp), m_devImp(devImp),
m_devPath(token.getDevicePath()) m_devPath(token.getDevicePath())
{
}
void _startThread()
{ {
std::unique_lock<std::mutex> lk(m_initMutex); std::unique_lock<std::mutex> lk(m_initMutex);
DeviceType dType = token.getDeviceType(); DeviceType dType = m_token.getDeviceType();
if (dType == DeviceType::USB) if (dType == DeviceType::USB)
m_thread = std::thread(_threadProcUSBLL, this); m_thread = std::thread(_threadProcUSBLL, this);
else if (dType == DeviceType::Bluetooth) else if (dType == DeviceType::Bluetooth)

View File

@ -34,11 +34,11 @@ class HIDListenerUdev final : public IHIDListener
/* Filter to USB/BT */ /* Filter to USB/BT */
const char* dt = udev_device_get_devtype(device); const char* dt = udev_device_get_devtype(device);
DeviceToken::DeviceType type; DeviceType type;
if (!strcmp(dt, "usb_device")) if (!strcmp(dt, "usb_device"))
type = DeviceToken::DeviceType::USB; type = DeviceType::USB;
else if (!strcmp(dt, "bluetooth_device")) else if (!strcmp(dt, "bluetooth_device"))
type = DeviceToken::DeviceType::Bluetooth; type = DeviceType::Bluetooth;
else else
return; return;
@ -78,31 +78,31 @@ class HIDListenerUdev final : public IHIDListener
if (producte) if (producte)
product = udev_list_entry_get_value(producte); product = udev_list_entry_get_value(producte);
if (!listener->m_finder._insertToken(DeviceToken(type, vid, pid, manuf, product, devPath))) if (!listener->m_finder._insertToken(
std::make_unique<DeviceToken>(type, vid, pid, manuf, product, devPath)))
{ {
/* Matched-insertion failed; see if generic HID interface is available */ /* Matched-insertion failed; see if generic HID interface is available */
udev_list_entry* devInterfaces = nullptr; udev_list_entry* devInterfaces = nullptr;
if (type == DeviceToken::DeviceType::USB) if (type == DeviceType::USB)
devInterfaces = udev_list_entry_get_by_name(attrs, "ID_USB_INTERFACES"); devInterfaces = udev_list_entry_get_by_name(attrs, "ID_USB_INTERFACES");
else if (type == DeviceToken::DeviceType::Bluetooth) else if (type == DeviceType::Bluetooth)
devInterfaces = udev_list_entry_get_by_name(attrs, "ID_BLUETOOTH_INTERFACES"); devInterfaces = udev_list_entry_get_by_name(attrs, "ID_BLUETOOTH_INTERFACES");
if (devInterfaces) if (devInterfaces)
{ {
const char* interfacesStr = udev_list_entry_get_value(devInterfaces); const char* interfacesStr = udev_list_entry_get_value(devInterfaces);
if (strstr(interfacesStr, ":030104") || /* HID / GenericDesktop / Joystick */ if (strstr(interfacesStr, ":03")) /* HID */
strstr(interfacesStr, ":030105")) /* HID / GenericDesktop / Gamepad */
{ {
udev_enumerate* hidEnum = udev_enumerate_new(UDEV_INST); udev_enumerate* hidEnum = udev_enumerate_new(UDEV_INST);
udev_enumerate_add_match_parent(hidEnum, device); udev_enumerate_add_match_parent(hidEnum, device);
udev_enumerate_add_match_subsystem(hidEnum, "hid"); udev_enumerate_add_match_subsystem(hidEnum, "hidraw");
udev_enumerate_scan_devices(hidEnum); udev_enumerate_scan_devices(hidEnum);
udev_list_entry* hidEnt = udev_enumerate_get_list_entry(hidEnum); udev_list_entry* hidEnt = udev_enumerate_get_list_entry(hidEnum);
if (hidEnt) if (hidEnt)
{ {
const char* hidPath = udev_list_entry_get_name(hidEnt); const char* hidPath = udev_list_entry_get_name(hidEnt);
if (!listener->m_finder._hasToken(hidPath)) if (!listener->m_finder._hasToken(hidPath))
listener->m_finder._insertToken(DeviceToken(DeviceToken::DeviceType::GenericHID, listener->m_finder._insertToken(std::make_unique<DeviceToken>(DeviceType::HID,
vid, pid, manuf, product, hidPath)); vid, pid, manuf, product, hidPath));
} }
udev_enumerate_unref(hidEnum); udev_enumerate_unref(hidEnum);
} }
@ -230,9 +230,9 @@ public:
}; };
IHIDListener* IHIDListenerNew(DeviceFinder& finder) std::unique_ptr<IHIDListener> IHIDListenerNew(DeviceFinder& finder)
{ {
return new HIDListenerUdev(finder); return std::make_unique<HIDListenerUdev>(finder);
} }
} }

View File

@ -10,11 +10,13 @@ namespace boo
class IHIDDevice class IHIDDevice
{ {
friend class DeviceBase; friend class DeviceBase;
friend struct DeviceSignature;
virtual void _deviceDisconnected()=0; virtual void _deviceDisconnected()=0;
virtual bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length)=0; virtual bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length)=0;
virtual size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length)=0; virtual size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length)=0;
virtual bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)=0; virtual bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)=0;
virtual size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message)=0; virtual size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message)=0;
virtual void _startThread()=0;
public: public:
inline virtual ~IHIDDevice() {} inline virtual ~IHIDDevice() {}
}; };