VulcanoLE/src/VulcanoLE/API/HIDHelper.cpp

165 lines
5.0 KiB
C++

#include <cstring>
#include <cstdio>
#include <fcntl.h>
#include <execution>
#include <sys/ioctl.h>
#include <VulcanoLE/API/HIDHelper.h>
#include <VUtils/Logging.h>
#define RV_CTRL_INTERFACE 1
#define RV_LED_INTERFACE 3
#define RV_VENDOR 0x1e7d
HIDHelper::HIDHelper() = default;
HIDHelper::~HIDHelper() = default;
int HIDHelper::openDevices() {
int p = 0;
while (m_products[ p ]) {
unsigned short product_id = m_products[ p ];
// For LED device, use hidapi-libusb, since we need to disconnect it
// from the default kernel driver.
struct hid_device_info *devs = nullptr;
m_ledDevice = nullptr;
devs = hid_enumerate(RV_VENDOR, product_id);
if (!findLED(devs, product_id)) {
if (devs) {
hid_free_enumeration(devs);
devs = nullptr;
};
if (m_ledDevice) hid_close(m_ledDevice);
p++;
continue;
}
// For CTRL device, use native HIDRAW access. After sending the init
// sequence, we will close it.
struct udev *udev = udev_new();
struct udev_enumerate *enumerate = udev_enumerate_new(udev);
udev_enumerate_add_match_subsystem(enumerate, "hidraw");
udev_enumerate_scan_devices(enumerate);
struct udev_list_entry *entries = udev_enumerate_get_list_entry(enumerate);
struct udev_list_entry *cur;
udev_list_entry_foreach(cur, entries) {
struct udev_device *usb_dev = nullptr;
struct udev_device *raw_dev = nullptr;
if (ctrlDeviceWork(cur, usb_dev, raw_dev, udev, product_id)) {
break;
}
}
if (ctrl_device < 1) {
ERR("open_device(%04hx, %04hx): No CTRL device found", RV_VENDOR, product_id);
if (devs) {
hid_free_enumeration(devs);
devs = nullptr;
};
if (m_ledDevice) hid_close(m_ledDevice);
p++;
continue;
}
return 0;
}
return -1;
}
bool HIDHelper::ctrlDeviceWork(
udev_list_entry *cur,
udev_device *usb_dev,
udev_device *raw_dev,
udev *udev,
unsigned int product_id
) {
const char *sysfs_path = udev_list_entry_get_name(cur);
if (!sysfs_path)
return false;
raw_dev = udev_device_new_from_syspath(udev, sysfs_path);
const char *dev_path = udev_device_get_devnode(raw_dev);
if (!dev_path)
return false;
usb_dev = udev_device_get_parent_with_subsystem_devtype(
raw_dev,
"usb",
"usb_interface");
if (!usb_dev)
return false;
const char *info = udev_device_get_sysattr_value(usb_dev, "uevent");
if (!info)
return false;
const char *itf = udev_device_get_sysattr_value(usb_dev, "bInterfaceNumber");
if (!itf)
return false;
// We're looking for vid/pid and interface number
if (atoi(itf) == RV_CTRL_INTERFACE) {
char searchstr[64];
snprintf(searchstr, 64, "PRODUCT=%hx/%hx", RV_VENDOR, product_id);
if (strstr(info, searchstr) != nullptr) {
ctrl_device = open(dev_path, O_RDWR);
if (!ctrl_device) {
ERR("open_device(%04hx, %04hx): Unable to open CTRL device at %s", RV_VENDOR, product_id,
dev_path);
return false;
}
LOG("open_device(%04hx, %04hx): CTRL interface at %s", RV_VENDOR, product_id, dev_path);
return true;
}
}
if (raw_dev) udev_device_unref(raw_dev);
return false;
}
bool HIDHelper::findLED(hid_device_info *devs, unsigned int product_id) {
struct hid_device_info *dev = nullptr;
dev = devs;
while (dev) {
if (dev->interface_number == RV_LED_INTERFACE) {
LOG("open_device(%04hx, %04hx): LED interface at USB path %s", RV_VENDOR, product_id, dev->path);
m_ledDevice = hid_open_path(dev->path);
if (!m_ledDevice || hid_set_nonblocking(m_ledDevice, 1) < 0) {
ERR("open_device(%04hx, %04hx): Unable to open LED interface %s", RV_VENDOR, product_id,
dev->path);
return false;
}
} else {
DBG("open_device(%04hx, %04hx): ignoring non-LED interface #%d", RV_VENDOR, product_id,
dev->interface_number);
}
dev = dev->next;
}
if (!m_ledDevice) {
DBG("open_device(%04hx, %04hx): No LED device found", RV_VENDOR, product_id);
return false;
}
return true;
}
int HIDHelper::get_feature_report(unsigned char *buf, int size) const {
int res = ioctl(ctrl_device, HIDIOCGFEATURE(size), buf);
if (res < 0) {
return 0;
}
return size;
}
int HIDHelper::send_feature_report(unsigned char *buf, int size) const {
int res = ioctl(ctrl_device, HIDIOCSFEATURE(size), buf);
if (res < 0) {
return 0;
}
return size;
}
void HIDHelper::close_ctrl_device() {
if (ctrl_device) close(ctrl_device);
ctrl_device = 0;
}