hidraw: allow opening multiple hidraw

For some devices we have to send reports on one endpoint
but read the reply on another. This commit allows us to
open and store multiple hidraws and adds API to specify
which hidraw to use for open, close, and read input report.

Signed-off-by: Thomas Hindoe Paaboel Andersen <phomes@gmail.com>
This commit is contained in:
Thomas Hindoe Paaboel Andersen
2018-08-26 17:09:17 +02:00
committed by Peter Hutterer
parent 38dce69838
commit a4e1e1690d
5 changed files with 105 additions and 42 deletions

View File

@@ -612,7 +612,7 @@ hidpp10drv_probe(struct ratbag_device *device)
goto err;
drv_data = zalloc(sizeof(*drv_data));
hidpp_device_init(&base, device->hidraw.fd);
hidpp_device_init(&base, device->hidraw[0].fd);
hidpp_device_set_log_handler(&base, hidpp10_log, HIDPP_LOG_PRIORITY_RAW, device);
typestr = ratbag_device_data_hidpp10_get_profile_type(device->data);

View File

@@ -1310,7 +1310,7 @@ hidpp20drv_probe(struct ratbag_device *device)
drv_data = zalloc(sizeof(*drv_data));
ratbag_set_drv_data(device, drv_data);
hidpp_device_init(&base, device->hidraw.fd);
hidpp_device_init(&base, device->hidraw[0].fd);
hidpp_device_set_log_handler(&base, hidpp20_log, HIDPP_LOG_PRIORITY_RAW, device);
device_idx = ratbag_device_data_hidpp20_get_index(device->data);

View File

@@ -1223,7 +1223,7 @@ static int
ratbag_hidraw_parse_report_descriptor(struct ratbag_device *device)
{
int rc, desc_size = 0;
struct ratbag_hidraw *hidraw = &device->hidraw;
struct ratbag_hidraw *hidraw = &device->hidraw[0];
struct hidraw_report_descriptor report_desc = {0};
unsigned int i, j;
unsigned int usage_page, usage;
@@ -1291,7 +1291,7 @@ ratbag_hidraw_parse_report_descriptor(struct ratbag_device *device)
}
static int
ratbag_open_hidraw_node(struct ratbag_device *device, struct udev_device *hidraw_udev)
ratbag_open_hidraw_node(struct ratbag_device *device, struct udev_device *hidraw_udev, int idx)
{
struct hidraw_devinfo info;
struct ratbag_device *tmp_device;
@@ -1300,15 +1300,17 @@ ratbag_open_hidraw_node(struct ratbag_device *device, struct udev_device *hidraw
const char *sysname;
size_t reports_size;
device->hidraw.fd = -1;
assert(idx >= 0 && idx < MAX_HIDRAW);
device->hidraw[idx].fd = -1;
sysname = udev_device_get_sysname(hidraw_udev);
if (!strneq("hidraw", sysname, 6))
return -ENODEV;
list_for_each(tmp_device, &device->ratbag->devices, link) {
if (tmp_device->hidraw.sysname &&
streq(tmp_device->hidraw.sysname, sysname)) {
if (tmp_device->hidraw[idx].sysname &&
streq(tmp_device->hidraw[idx].sysname, sysname)) {
return -ENODEV;
}
}
@@ -1339,7 +1341,7 @@ ratbag_open_hidraw_node(struct ratbag_device *device, struct udev_device *hidraw
device->name,
udev_device_get_devnode(hidraw_udev));
device->hidraw.fd = fd;
device->hidraw[idx].fd = fd;
/* parse first to count the number of reports */
res = ratbag_hidraw_parse_report_descriptor(device);
@@ -1348,19 +1350,19 @@ ratbag_open_hidraw_node(struct ratbag_device *device, struct udev_device *hidraw
"Error while parsing the report descriptor: '%s' (%d)\n",
strerror(-res),
res);
device->hidraw.fd = -1;
device->hidraw[idx].fd = -1;
goto err;
}
if (device->hidraw.num_reports)
reports_size = device->hidraw.num_reports * sizeof(struct ratbag_hid_report);
if (device->hidraw[idx].num_reports)
reports_size = device->hidraw[idx].num_reports * sizeof(struct ratbag_hid_report);
else
reports_size = sizeof(struct ratbag_hid_report);
device->hidraw.reports = zalloc(reports_size);
device->hidraw[idx].reports = zalloc(reports_size);
ratbag_hidraw_parse_report_descriptor(device);
device->hidraw.sysname = strdup_safe(sysname);
device->hidraw[idx].sysname = strdup_safe(sysname);
return 0;
err:
@@ -1372,7 +1374,8 @@ err:
static int
ratbag_find_hidraw_node(struct ratbag_device *device,
int (*match)(struct ratbag_device *device),
int use_usb_parent)
int use_usb_parent,
int match_index, int hidraw_index)
{
struct ratbag *ratbag = device->ratbag;
_cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL;
@@ -1382,7 +1385,7 @@ ratbag_find_hidraw_node(struct ratbag_device *device,
struct udev_device *parent_udev;
struct udev *udev = ratbag->udev;
int rc = -ENODEV;
int matched;
int matched, endpoint_index = 0;
assert(match);
@@ -1414,17 +1417,21 @@ ratbag_find_hidraw_node(struct ratbag_device *device,
if (!udev_device)
continue;
rc = ratbag_open_hidraw_node(device, udev_device);
if (match_index > 0 && match_index != endpoint_index++)
continue;
rc = ratbag_open_hidraw_node(device, udev_device, hidraw_index);
if (rc)
goto skip;
matched = match(device);
rc = matched ? 0 : -ENODEV;
if (matched == 1)
return rc;
skip:
ratbag_close_hidraw(device);
ratbag_close_hidraw_index(device, hidraw_index);
}
return rc;
@@ -1433,7 +1440,7 @@ skip:
int
ratbag_find_hidraw(struct ratbag_device *device, int (*match)(struct ratbag_device *device))
{
return ratbag_find_hidraw_node(device, match, true);
return ratbag_find_hidraw_node(device, match, true, -1, 0);
}
static int
@@ -1445,7 +1452,13 @@ hidraw_match_all(struct ratbag_device *device)
int
ratbag_open_hidraw(struct ratbag_device *device)
{
return ratbag_find_hidraw_node(device, hidraw_match_all, false);
return ratbag_find_hidraw_node(device, hidraw_match_all, false, -1, 0);
}
int
ratbag_open_hidraw_index(struct ratbag_device *device, int endpoint_index, int hidraw_index)
{
return ratbag_find_hidraw_node(device, hidraw_match_all, true, endpoint_index, hidraw_index);
}
static struct ratbag_hid_report *
@@ -1454,15 +1467,15 @@ ratbag_hidraw_get_report(struct ratbag_device *device, unsigned int report_id)
unsigned i;
if (report_id == 0) {
if (device->hidraw.reports[0].report_id == report_id)
return &device->hidraw.reports[0];
if (device->hidraw[0].reports[0].report_id == report_id)
return &device->hidraw[0].reports[0];
else
return NULL;
}
for (i = 0; i < device->hidraw.num_reports; i++) {
if (device->hidraw.reports[i].report_id == report_id)
return &device->hidraw.reports[i];
for (i = 0; i < device->hidraw[0].num_reports; i++) {
if (device->hidraw[0].reports[i].report_id == report_id)
return &device->hidraw[0].reports[i];
}
return NULL;
@@ -1504,20 +1517,28 @@ ratbag_hidraw_get_usage(struct ratbag_device *device, unsigned int report_id)
void
ratbag_close_hidraw(struct ratbag_device *device)
{
if (device->hidraw.fd < 0)
ratbag_close_hidraw_index(device, 0);
}
void
ratbag_close_hidraw_index(struct ratbag_device *device, int idx)
{
assert(idx >= 0 && idx < MAX_HIDRAW);
if (device->hidraw[idx].fd < 0)
return;
if (device->hidraw.sysname) {
free(device->hidraw.sysname);
device->hidraw.sysname = NULL;
if (device->hidraw[idx].sysname) {
free(device->hidraw[idx].sysname);
device->hidraw[idx].sysname = NULL;
}
ratbag_close_fd(device, device->hidraw.fd);
device->hidraw.fd = -1;
ratbag_close_fd(device, device->hidraw[idx].fd);
device->hidraw[idx].fd = -1;
if (device->hidraw.reports) {
free(device->hidraw.reports);
device->hidraw.reports = NULL;
if (device->hidraw[idx].reports) {
free(device->hidraw[idx].reports);
device->hidraw[idx].reports = NULL;
}
}
@@ -1528,7 +1549,7 @@ ratbag_hidraw_raw_request(struct ratbag_device *device, unsigned char reportnum,
uint8_t tmp_buf[HID_MAX_BUFFER_SIZE];
int rc;
if (len < 1 || len > HID_MAX_BUFFER_SIZE || !buf || device->hidraw.fd < 0)
if (len < 1 || len > HID_MAX_BUFFER_SIZE || !buf || device->hidraw[0].fd < 0)
return -EINVAL;
if (rtype != HID_FEATURE_REPORT)
@@ -1539,7 +1560,7 @@ ratbag_hidraw_raw_request(struct ratbag_device *device, unsigned char reportnum,
memset(tmp_buf, 0, len);
tmp_buf[0] = reportnum;
rc = ioctl(device->hidraw.fd, HIDIOCGFEATURE(len), tmp_buf);
rc = ioctl(device->hidraw[0].fd, HIDIOCGFEATURE(len), tmp_buf);
if (rc < 0)
return -errno;
@@ -1551,7 +1572,7 @@ ratbag_hidraw_raw_request(struct ratbag_device *device, unsigned char reportnum,
buf[0] = reportnum;
log_buf_raw(device->ratbag, "feature set: ", buf, len);
rc = ioctl(device->hidraw.fd, HIDIOCSFEATURE(len), buf);
rc = ioctl(device->hidraw[0].fd, HIDIOCSFEATURE(len), buf);
if (rc < 0)
return -errno;
@@ -1566,12 +1587,12 @@ ratbag_hidraw_output_report(struct ratbag_device *device, uint8_t *buf, size_t l
{
int rc;
if (len < 1 || len > HID_MAX_BUFFER_SIZE || !buf || device->hidraw.fd < 0)
if (len < 1 || len > HID_MAX_BUFFER_SIZE || !buf || device->hidraw[0].fd < 0)
return -EINVAL;
log_buf_raw(device->ratbag, "output report: ", buf, len);
rc = write(device->hidraw.fd, buf, len);
rc = write(device->hidraw[0].fd, buf, len);
if (rc < 0)
return -errno;
@@ -1584,14 +1605,22 @@ ratbag_hidraw_output_report(struct ratbag_device *device, uint8_t *buf, size_t l
int
ratbag_hidraw_read_input_report(struct ratbag_device *device, uint8_t *buf, size_t len)
{
return ratbag_hidraw_read_input_report_index(device, buf, len, 0);
}
int
ratbag_hidraw_read_input_report_index(struct ratbag_device *device, uint8_t *buf, size_t len, int hidrawno)
{
int rc;
struct pollfd fds;
if (len < 1 || !buf || device->hidraw.fd < 0)
assert(hidrawno >= 0 && hidrawno < MAX_HIDRAW);
if (len < 1 || !buf || device->hidraw[hidrawno].fd < 0)
return -EINVAL;
fds.fd = device->hidraw.fd;
fds.fd = device->hidraw[hidrawno].fd;
fds.events = POLLIN;
rc = poll(&fds, 1, 1000);
@@ -1601,7 +1630,7 @@ ratbag_hidraw_read_input_report(struct ratbag_device *device, uint8_t *buf, size
if (rc == 0)
return -ETIMEDOUT;
rc = read(device->hidraw.fd, buf, len);
rc = read(device->hidraw[hidrawno].fd, buf, len);
if (rc > 0)
log_buf_raw(device->ratbag, "input report: ", buf, rc);

View File

@@ -32,6 +32,7 @@
#define HID_INPUT_REPORT 0
#define HID_OUTPUT_REPORT 1
#define HID_FEATURE_REPORT 2
#define MAX_HIDRAW 2
struct ratbag_hid_report {
unsigned int report_id;
@@ -55,6 +56,18 @@ struct ratbag_hidraw {
*/
int ratbag_open_hidraw(struct ratbag_device *device);
/**
* Open a hidraw device associated with the device by its enumeration order to
* a specific internal hidraw index.
*
* @param device the ratbag device
* @param enumeration index of the endpoint
* @param interal index of hidraw array
*
* @return 0 on success or a negative errno on error
*/
int ratbag_open_hidraw_index(struct ratbag_device *device, int endpoint_index, int hidraw_index);
/**
* Find and open the hidraw device associated with the device by using the
* given matching function.
@@ -75,6 +88,14 @@ ratbag_find_hidraw(struct ratbag_device *device,
*/
void ratbag_close_hidraw(struct ratbag_device *device);
/**
* Close a hidraw device associated with the device by the internal index.
*
* @param device the ratbag device
* @param interal index of hidraw array
*/
void ratbag_close_hidraw_index(struct ratbag_device *device, int idx);
/**
* Send report request to device
*
@@ -115,6 +136,19 @@ int ratbag_hidraw_output_report(struct ratbag_device *device, uint8_t *buf, size
*/
int ratbag_hidraw_read_input_report(struct ratbag_device *device, uint8_t *buf, size_t len);
/**
* Read an input report from the device from a specific hidraw index
*
* @param device the ratbag device
* @param[out] buf resulting raw data
* @param len length of buf
* @param interal index of hidraw array
*
* @return count of data transfered, or a negative errno on error
*/
int ratbag_hidraw_read_input_report_index(struct ratbag_device *device, uint8_t *buf, size_t len, int hidrawno);
/**
* Tells if a given device has the specified report ID.
*

View File

@@ -129,7 +129,7 @@ struct ratbag_device {
void *userdata;
struct udev_device *udev_device;
struct ratbag_hidraw hidraw;
struct ratbag_hidraw hidraw[MAX_HIDRAW];
int refcount;
struct input_id ids;
struct ratbag_driver *driver;