165 lines
5.0 KiB
C++
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;
|
|
}
|