mirror of https://github.com/AxioDL/boo.git
initial udev implementation for linux-hosted hid devices
This commit is contained in:
parent
cf0eaad388
commit
8442c492d4
16
libBoo.pri
16
libBoo.pri
|
@ -1,5 +1,3 @@
|
|||
!contains(CONFIG,c++11):CONFIG += C++11
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/include/boo.hpp \
|
||||
$$PWD/include/IGraphicsContext.hpp \
|
||||
|
@ -18,8 +16,8 @@ HEADERS += \
|
|||
$$PWD/include/inputdev/CDeviceToken.hpp \
|
||||
$$PWD/include/inputdev/CDeviceBase.hpp \
|
||||
$$PWD/include/inputdev/DeviceClasses.hpp \
|
||||
$$PWD/src/inputdev/IHIDDevice.hpp \
|
||||
$$PWD/src/inputdev/IHIDListener.hpp
|
||||
$$PWD/include/inputdev/IHIDListener.hpp \
|
||||
$$PWD/src/inputdev/IHIDDevice.hpp
|
||||
|
||||
unix:!macx:HEADERS += \
|
||||
$$PWD/include/x11/CGLXContext.hpp
|
||||
|
@ -41,12 +39,12 @@ SOURCES += \
|
|||
$$PWD/src/inputdev/CDualshockPad.cpp \
|
||||
$$PWD/src/inputdev/CGenericPad.cpp \
|
||||
$$PWD/src/inputdev/CDeviceBase.cpp \
|
||||
$$PWD/src/inputdev/DeviceClasses.cpp
|
||||
$$PWD/src/inputdev/DeviceClasses.cpp \
|
||||
$$PWD/src/inputdev/CHIDListenerUdev.cpp \
|
||||
$$PWD/src/inputdev/CHIDDeviceUdev.cpp
|
||||
|
||||
unix:!macx:SOURCES += \
|
||||
$$PWD/src/x11/CGLXContext.cpp \
|
||||
$$PWD/src/inputdev/CHIDDeviceUdev.cpp \
|
||||
$$PWD/src/inputdev/CHIDListenerUdev.cpp
|
||||
$$PWD/src/x11/CGLXContext.cpp
|
||||
|
||||
macx:SOURCES += \
|
||||
$$PWD/src/mac/CCGLContext.cpp \
|
||||
|
@ -63,3 +61,5 @@ win32:SOURCES += \
|
|||
$$PWD/src/inputdev/CHIDListenerWin32.cpp
|
||||
|
||||
INCLUDEPATH += $$PWD/include
|
||||
|
||||
unix:!macx:LIBS += -ludev
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
CONFIG -= Qt
|
||||
CONFIG += app c++11
|
||||
#QMAKE_CXXFLAGS -= -std=c++0x
|
||||
QMAKE_CXXFLAGS += -std=c++11
|
||||
|
||||
unix:!macx:CONFIG += link_pkgconfig
|
||||
unix:!macx:PKGCONFIG += x11
|
||||
#unix:!macx:CONFIG += link_pkgconfig
|
||||
#unix:!macx:PKGCONFIG += x11
|
||||
|
||||
include(libBoo.pri)
|
||||
include(test/test.pri)
|
||||
|
|
|
@ -1 +1,182 @@
|
|||
#include "IHIDDevice.hpp"
|
||||
#include "inputdev/CDeviceToken.hpp"
|
||||
#include "inputdev/CDeviceBase.hpp"
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#include <libudev.h>
|
||||
#include <stropts.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usbdevice_fs.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
udev* BooGetUdev();
|
||||
|
||||
#define MAX_REPORT_SIZE 65536
|
||||
|
||||
/* Reference: http://tali.admingilde.org/linux-docbook/usb/ch07s06.html
|
||||
*/
|
||||
|
||||
class CHIDDeviceUdev final : public IHIDDevice
|
||||
{
|
||||
CDeviceToken& m_token;
|
||||
CDeviceBase& m_devImp;
|
||||
|
||||
int m_devFd = 0;
|
||||
unsigned m_usbIntfInPipe = 0;
|
||||
unsigned m_usbIntfOutPipe = 0;
|
||||
bool m_runningTransferLoop = false;
|
||||
|
||||
const std::string& m_devPath;
|
||||
std::mutex m_initMutex;
|
||||
std::condition_variable m_initCond;
|
||||
std::thread* m_thread;
|
||||
|
||||
bool _sendInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length)
|
||||
{
|
||||
if (m_devFd)
|
||||
{
|
||||
usbdevfs_bulktransfer xfer =
|
||||
{
|
||||
m_usbIntfOutPipe | USB_DIR_OUT,
|
||||
(unsigned)length,
|
||||
0,
|
||||
(void*)data
|
||||
};
|
||||
int ret = ioctl(m_devFd, USBDEVFS_BULK, &xfer);
|
||||
if (ret != length)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t _receiveInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length)
|
||||
{
|
||||
if (m_devFd)
|
||||
{
|
||||
usbdevfs_bulktransfer xfer =
|
||||
{
|
||||
m_usbIntfInPipe | USB_DIR_IN,
|
||||
(unsigned)length,
|
||||
0,
|
||||
data
|
||||
};
|
||||
return ioctl(m_devFd, USBDEVFS_BULK, &xfer);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _threadProcLL(CHIDDeviceUdev* device)
|
||||
{
|
||||
unsigned i;
|
||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||
udev_device* hidDev = udev_device_new_from_syspath(BooGetUdev(), device->m_devPath.c_str());
|
||||
|
||||
/* Get the HID element's parent (USB interrupt transfer-interface) */
|
||||
udev_device* usbDev = udev_device_get_parent_with_subsystem_devtype(hidDev, "usb", "usb_device");
|
||||
const char* dp = udev_device_get_devnode(usbDev);
|
||||
device->m_devFd = open(dp, O_RDONLY);
|
||||
usb_device_descriptor devDesc = {0};
|
||||
read(device->m_devFd, &devDesc, 1);
|
||||
read(device->m_devFd, &devDesc.bDescriptorType, devDesc.bLength-1);
|
||||
if (devDesc.bNumConfigurations)
|
||||
{
|
||||
usb_config_descriptor confDesc = {0};
|
||||
read(device->m_devFd, &confDesc, 1);
|
||||
read(device->m_devFd, &confDesc.bDescriptorType, confDesc.bLength-1);
|
||||
if (confDesc.bNumInterfaces)
|
||||
{
|
||||
usb_interface_descriptor intfDesc = {0};
|
||||
read(device->m_devFd, &intfDesc, 1);
|
||||
read(device->m_devFd, &intfDesc.bDescriptorType, intfDesc.bLength-1);
|
||||
for (i=0 ; i<intfDesc.bNumEndpoints+1 ; ++i)
|
||||
{
|
||||
usb_endpoint_descriptor endpDesc = {0};
|
||||
read(device->m_devFd, &endpDesc, 1);
|
||||
read(device->m_devFd, &endpDesc.bDescriptorType, endpDesc.bLength-1);
|
||||
if ((endpDesc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)
|
||||
{
|
||||
if ((endpDesc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
|
||||
device->m_usbIntfInPipe = endpDesc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
|
||||
else if ((endpDesc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
|
||||
device->m_usbIntfOutPipe = endpDesc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Request that kernel disconnects existing driver */
|
||||
usbdevfs_ioctl disconnectReq = {
|
||||
0,
|
||||
USBDEVFS_DISCONNECT,
|
||||
NULL
|
||||
};
|
||||
ioctl(device->m_devFd, USBDEVFS_IOCTL, &disconnectReq);
|
||||
|
||||
/* Return control to main thread */
|
||||
device->m_runningTransferLoop = true;
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
|
||||
/* Start transfer loop */
|
||||
while (device->m_runningTransferLoop)
|
||||
device->m_devImp.transferCycle();
|
||||
device->m_devImp.finalCycle();
|
||||
|
||||
/* Cleanup */
|
||||
close(device->m_devFd);
|
||||
device->m_devFd = NULL;
|
||||
udev_device_unref(hidDev);
|
||||
|
||||
}
|
||||
|
||||
static void _threadProcHL(CHIDDeviceUdev* device)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void _deviceDisconnected()
|
||||
{
|
||||
m_runningTransferLoop = false;
|
||||
}
|
||||
|
||||
bool _sendReport(const uint8_t* data, size_t length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
CHIDDeviceUdev(CDeviceToken& token, CDeviceBase& devImp, bool lowLevel)
|
||||
: m_token(token),
|
||||
m_devImp(devImp),
|
||||
m_devPath(token.getDevicePath())
|
||||
{
|
||||
devImp.m_hidDev = this;
|
||||
std::unique_lock<std::mutex> lk(m_initMutex);
|
||||
if (lowLevel)
|
||||
m_thread = new std::thread(_threadProcLL, this);
|
||||
else
|
||||
m_thread = new std::thread(_threadProcHL, this);
|
||||
m_initCond.wait(lk);
|
||||
}
|
||||
|
||||
~CHIDDeviceUdev()
|
||||
{
|
||||
m_runningTransferLoop = false;
|
||||
m_thread->detach();
|
||||
delete m_thread;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp, bool lowLevel)
|
||||
{
|
||||
return new CHIDDeviceUdev(token, devImp, lowLevel);
|
||||
}
|
||||
|
|
|
@ -1 +1,166 @@
|
|||
#include "IHIDListener.hpp"
|
||||
#include "inputdev/IHIDListener.hpp"
|
||||
#include "inputdev/CDeviceFinder.hpp"
|
||||
#include <libudev.h>
|
||||
#include <string.h>
|
||||
#include <thread>
|
||||
|
||||
static udev* UDEV_INST = NULL;
|
||||
udev* BooGetUdev()
|
||||
{
|
||||
if (!UDEV_INST)
|
||||
UDEV_INST = udev_new();
|
||||
return UDEV_INST;
|
||||
}
|
||||
|
||||
class CHIDListenerUdev final : public IHIDListener
|
||||
{
|
||||
CDeviceFinder& m_finder;
|
||||
|
||||
udev_monitor* m_udevMon;
|
||||
std::thread* m_udevThread;
|
||||
bool m_udevRunning;
|
||||
bool m_scanningEnabled;
|
||||
|
||||
static void deviceConnected(CHIDListenerUdev* listener,
|
||||
udev_device* device)
|
||||
{
|
||||
if (!listener->m_scanningEnabled)
|
||||
return;
|
||||
|
||||
const char* devPath = udev_device_get_syspath(device);
|
||||
if (listener->m_finder._hasToken(devPath))
|
||||
return;
|
||||
|
||||
udev_device* devCast = udev_device_get_parent_with_subsystem_devtype(device, "bluetooth", "bluetooth_device");
|
||||
if (!devCast)
|
||||
devCast = udev_device_get_parent_with_subsystem_devtype(device, "usb", "usb_device");
|
||||
if (!devCast)
|
||||
return;
|
||||
|
||||
int vid = 0, pid = 0;
|
||||
udev_list_entry* attrs = udev_device_get_properties_list_entry(devCast);
|
||||
|
||||
udev_list_entry* vide = udev_list_entry_get_by_name(attrs, "ID_VENDOR_ID");
|
||||
if (vide)
|
||||
vid = strtol(udev_list_entry_get_value(vide), NULL, 16);
|
||||
|
||||
udev_list_entry* pide = udev_list_entry_get_by_name(attrs, "ID_MODEL_ID");
|
||||
if (pide)
|
||||
pid = strtol(udev_list_entry_get_value(pide), NULL, 16);
|
||||
|
||||
const char* manuf = NULL;
|
||||
udev_list_entry* manufe = udev_list_entry_get_by_name(attrs, "ID_VENDOR");
|
||||
if (manufe)
|
||||
manuf = udev_list_entry_get_value(manufe);
|
||||
|
||||
const char* product = NULL;
|
||||
udev_list_entry* producte = udev_list_entry_get_by_name(attrs, "ID_MODEL");
|
||||
if (producte)
|
||||
product = udev_list_entry_get_value(producte);
|
||||
|
||||
listener->m_finder._insertToken(CDeviceToken(vid, pid, manuf, product, devPath));
|
||||
}
|
||||
|
||||
static void deviceDisconnected(CHIDListenerUdev* listener,
|
||||
udev_device* device)
|
||||
{
|
||||
const char* devPath = udev_device_get_syspath(device);
|
||||
listener->m_finder._removeToken(devPath);
|
||||
}
|
||||
|
||||
static void _udevProc(CHIDListenerUdev* listener)
|
||||
{
|
||||
udev_monitor_enable_receiving(listener->m_udevMon);
|
||||
int fd = udev_monitor_get_fd(listener->m_udevMon);
|
||||
while (listener->m_udevRunning)
|
||||
{
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(fd, &fds);
|
||||
select(fd+1, &fds, NULL, NULL, NULL);
|
||||
udev_device* dev = udev_monitor_receive_device(listener->m_udevMon);
|
||||
if (dev)
|
||||
{
|
||||
const char* action = udev_device_get_action(dev);
|
||||
if (!strcmp(action, "add"))
|
||||
{
|
||||
deviceConnected(listener, dev);
|
||||
}
|
||||
else if (!strcmp(action, "remove"))
|
||||
{
|
||||
deviceDisconnected(listener, dev);
|
||||
}
|
||||
udev_device_unref(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
CHIDListenerUdev(CDeviceFinder& finder)
|
||||
: m_finder(finder)
|
||||
{
|
||||
|
||||
/* Setup hiddev and hotplug events */
|
||||
m_udevMon = udev_monitor_new_from_netlink(BooGetUdev(), "udev");
|
||||
if (!m_udevMon)
|
||||
throw std::runtime_error("unable to init udev_monitor");
|
||||
udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "hid", NULL);
|
||||
udev_monitor_filter_update(m_udevMon);
|
||||
|
||||
/* Initial HID Device Add */
|
||||
m_scanningEnabled = true;
|
||||
scanNow();
|
||||
m_scanningEnabled = false;
|
||||
|
||||
/* Start hotplug thread */
|
||||
m_udevRunning = true;
|
||||
m_udevThread = new std::thread(_udevProc, this);
|
||||
|
||||
}
|
||||
|
||||
~CHIDListenerUdev()
|
||||
{
|
||||
m_udevRunning = false;
|
||||
m_udevThread->join();
|
||||
udev_monitor_unref(m_udevMon);
|
||||
}
|
||||
|
||||
/* Automatic device scanning */
|
||||
bool startScanning()
|
||||
{
|
||||
m_scanningEnabled = true;
|
||||
return true;
|
||||
}
|
||||
bool stopScanning()
|
||||
{
|
||||
m_scanningEnabled = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Manual device scanning */
|
||||
bool scanNow()
|
||||
{
|
||||
udev_enumerate* uenum = udev_enumerate_new(BooGetUdev());
|
||||
udev_enumerate_add_match_subsystem(uenum, "hid");
|
||||
udev_enumerate_scan_devices(uenum);
|
||||
udev_list_entry* uenumList = udev_enumerate_get_list_entry(uenum);
|
||||
udev_list_entry* uenumItem;
|
||||
udev_list_entry_foreach(uenumItem, uenumList)
|
||||
{
|
||||
const char* devPath = udev_list_entry_get_name(uenumItem);
|
||||
udev_device* dev = udev_device_new_from_syspath(UDEV_INST, devPath);
|
||||
if (dev)
|
||||
deviceConnected(this, dev);
|
||||
udev_device_unref(dev);
|
||||
}
|
||||
udev_enumerate_unref(uenum);
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
IHIDListener* IHIDListenerNew(CDeviceFinder& finder)
|
||||
{
|
||||
return new CHIDListenerUdev(finder);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#else
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <boo.hpp>
|
||||
|
||||
class CDolphinSmashAdapterCallback : public IDolphinSmashAdapterCallback
|
||||
|
@ -61,6 +62,7 @@ int main(int argc, char** argv)
|
|||
#if __APPLE__
|
||||
CFRunLoopRun();
|
||||
#else
|
||||
while (true) {sleep(1);}
|
||||
#endif
|
||||
|
||||
delete ctx;
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
SOURCES += \
|
||||
$$PWD/main.cpp
|
||||
$$PWD/main.cpp
|
||||
|
||||
CONFIG += c++11
|
Loading…
Reference in New Issue