#include #include #include #include #include #include #include #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; }