From cd4dc0821bc97947f25c8483a4aa0711bff8619a Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Wed, 22 Jan 2014 13:49:41 -0500 Subject: [PATCH 01/25] HID: Add transport-driver callbacks to the hid_ll_driver struct Add raw_request and output_report callbacks to the hid_ll_driver struct. Signed-off-by: Frank Praznik Acked-by: David Herrmann Signed-off-by: Jiri Kosina --- include/linux/hid.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/include/linux/hid.h b/include/linux/hid.h index 31b9d299ef6c..003cc8e89831 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -700,8 +700,14 @@ struct hid_ll_driver { struct hid_report *report, int reqtype); int (*wait)(struct hid_device *hdev); - int (*idle)(struct hid_device *hdev, int report, int idle, int reqtype); + int (*raw_request) (struct hid_device *hdev, unsigned char reportnum, + __u8 *buf, size_t len, unsigned char rtype, + int reqtype); + + int (*output_report) (struct hid_device *hdev, __u8 *buf, size_t len); + + int (*idle)(struct hid_device *hdev, int report, int idle, int reqtype); }; #define PM_HINT_FULLON 1<<5 From 975a683271e690e7e467b274f22efadf1e696b5e Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Wed, 22 Jan 2014 13:49:42 -0500 Subject: [PATCH 02/25] HID: Add transport-driver functions to the USB HID interface. Add raw_request, set_raw_report and output_report transport-driver functions to the USB HID driver. Signed-off-by: Frank Praznik Acked-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 44df131d390a..f8ca312bae1b 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -884,6 +884,38 @@ static int usbhid_get_raw_report(struct hid_device *hid, return ret; } +static int usbhid_set_raw_report(struct hid_device *hid, unsigned int reportnum, + __u8 *buf, size_t count, unsigned char rtype) +{ + struct usbhid_device *usbhid = hid->driver_data; + struct usb_device *dev = hid_to_usb_dev(hid); + struct usb_interface *intf = usbhid->intf; + struct usb_host_interface *interface = intf->cur_altsetting; + int ret, skipped_report_id = 0; + + /* Byte 0 is the report number. Report data starts at byte 1.*/ + buf[0] = reportnum; + if (buf[0] == 0x0) { + /* Don't send the Report ID */ + buf++; + count--; + skipped_report_id = 1; + } + + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + HID_REQ_SET_REPORT, + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + ((rtype + 1) << 8) | reportnum, + interface->desc.bInterfaceNumber, buf, count, + USB_CTRL_SET_TIMEOUT); + /* count also the report id, if this was a numbered report. */ + if (ret > 0 && skipped_report_id) + ret++; + + return ret; +} + + static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count, unsigned char report_type) { @@ -936,6 +968,36 @@ static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t co return ret; } +static int usbhid_output_report(struct hid_device *hid, __u8 *buf, size_t count) +{ + struct usbhid_device *usbhid = hid->driver_data; + struct usb_device *dev = hid_to_usb_dev(hid); + int actual_length, skipped_report_id = 0, ret; + + if (!usbhid->urbout) + return -EIO; + + if (buf[0] == 0x0) { + /* Don't send the Report ID */ + buf++; + count--; + skipped_report_id = 1; + } + + ret = usb_interrupt_msg(dev, usbhid->urbout->pipe, + buf, count, &actual_length, + USB_CTRL_SET_TIMEOUT); + /* return the number of bytes transferred */ + if (ret == 0) { + ret = actual_length; + /* count also the report id */ + if (skipped_report_id) + ret++; + } + + return ret; +} + static void usbhid_restart_queues(struct usbhid_device *usbhid) { if (usbhid->urbout && !test_bit(HID_OUT_RUNNING, &usbhid->iofl)) @@ -1200,6 +1262,20 @@ static void usbhid_request(struct hid_device *hid, struct hid_report *rep, int r } } +static int usbhid_raw_request(struct hid_device *hid, unsigned char reportnum, + __u8 *buf, size_t len, unsigned char rtype, + int reqtype) +{ + switch (reqtype) { + case HID_REQ_GET_REPORT: + return usbhid_get_raw_report(hid, reportnum, buf, len, rtype); + case HID_REQ_SET_REPORT: + return usbhid_set_raw_report(hid, reportnum, buf, len, rtype); + default: + return -EIO; + } +} + static int usbhid_idle(struct hid_device *hid, int report, int idle, int reqtype) { @@ -1223,6 +1299,8 @@ static struct hid_ll_driver usb_hid_driver = { .power = usbhid_power, .request = usbhid_request, .wait = usbhid_wait_io, + .raw_request = usbhid_raw_request, + .output_report = usbhid_output_report, .idle = usbhid_idle, }; From 596cfdd80ab8ad11c750511da2c8c9a33f188ba0 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Wed, 22 Jan 2014 13:49:43 -0500 Subject: [PATCH 03/25] HID: Add the transport-driver function to the uhid driver Add the uhid_output_report transport-driver function to the uhid driver. Signed-off-by: Frank Praznik Acked-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index cedc6da93c19..f5a2b1931143 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -244,12 +244,39 @@ static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, return count; } +static int uhid_hid_output_report(struct hid_device *hid, __u8 *buf, + size_t count) +{ + struct uhid_device *uhid = hid->driver_data; + unsigned long flags; + struct uhid_event *ev; + + if (count < 1 || count > UHID_DATA_MAX) + return -EINVAL; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) + return -ENOMEM; + + ev->type = UHID_OUTPUT; + ev->u.output.size = count; + ev->u.output.rtype = UHID_OUTPUT_REPORT; + memcpy(ev->u.output.data, buf, count); + + spin_lock_irqsave(&uhid->qlock, flags); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + return count; +} + static struct hid_ll_driver uhid_hid_driver = { .start = uhid_hid_start, .stop = uhid_hid_stop, .open = uhid_hid_open, .close = uhid_hid_close, .parse = uhid_hid_parse, + .output_report = uhid_hid_output_report, }; #ifdef CONFIG_COMPAT From 0a7f364e812285246cd617a51194a3f8bd0e8daa Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Wed, 22 Jan 2014 13:49:44 -0500 Subject: [PATCH 04/25] HID: Add the transport-driver functions to the HIDP driver. Add raw_request, set_raw_report and output_report transport-driver functions to the HIDP driver. Signed-off-by: Frank Praznik Acked-by: David Herrmann Signed-off-by: Jiri Kosina --- net/bluetooth/hidp/core.c | 90 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 292e619db896..b062cee3f319 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -353,6 +353,71 @@ static int hidp_get_raw_report(struct hid_device *hid, return ret; } +static int hidp_set_raw_report(struct hid_device *hid, unsigned char reportnum, + unsigned char *data, size_t count, + unsigned char report_type) +{ + struct hidp_session *session = hid->driver_data; + int ret; + + switch (report_type) { + case HID_FEATURE_REPORT: + report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE; + break; + case HID_INPUT_REPORT: + report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_INPUT; + break; + case HID_OUTPUT_REPORT: + report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_OUPUT; + break; + default: + return -EINVAL; + } + + if (mutex_lock_interruptible(&session->report_mutex)) + return -ERESTARTSYS; + + /* Set up our wait, and send the report request to the device. */ + data[0] = reportnum; + set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); + ret = hidp_send_ctrl_message(session, report_type, data, count); + if (ret) + goto err; + + /* Wait for the ACK from the device. */ + while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags) && + !atomic_read(&session->terminate)) { + int res; + + res = wait_event_interruptible_timeout(session->report_queue, + !test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags) + || atomic_read(&session->terminate), + 10*HZ); + if (res == 0) { + /* timeout */ + ret = -EIO; + goto err; + } + if (res < 0) { + /* signal */ + ret = -ERESTARTSYS; + goto err; + } + } + + if (!session->output_report_success) { + ret = -EIO; + goto err; + } + + ret = count; + +err: + clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); + mutex_unlock(&session->report_mutex); + return ret; +} + static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count, unsigned char report_type) { @@ -411,6 +476,29 @@ static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, s return ret; } +static int hidp_raw_request(struct hid_device *hid, unsigned char reportnum, + __u8 *buf, size_t len, unsigned char rtype, + int reqtype) +{ + switch (reqtype) { + case HID_REQ_GET_REPORT: + return hidp_get_raw_report(hid, reportnum, buf, len, rtype); + case HID_REQ_SET_REPORT: + return hidp_set_raw_report(hid, reportnum, buf, len, rtype); + default: + return -EIO; + } +} + +static int hidp_output_report(struct hid_device *hid, __u8 *data, size_t count) +{ + struct hidp_session *session = hid->driver_data; + + return hidp_send_intr_message(session, + HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT, + data, count); +} + static void hidp_idle_timeout(unsigned long arg) { struct hidp_session *session = (struct hidp_session *) arg; @@ -727,6 +815,8 @@ static struct hid_ll_driver hidp_hid_driver = { .stop = hidp_stop, .open = hidp_open, .close = hidp_close, + .raw_request = hidp_raw_request, + .output_report = hidp_output_report, .hidinput_input_event = hidp_hidinput_event, }; From b69d6536794c93dda362ab79c9f559382e3465be Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 5 Feb 2014 16:33:16 -0500 Subject: [PATCH 05/25] HID: add inliners for ll_driver transport-layer callbacks Those callbacks are not mandatory, so it's better to add inliners to use them safely. Reviewed-by: David Herrmann Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- include/linux/hid.h | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/include/linux/hid.h b/include/linux/hid.h index 003cc8e89831..dddcad07c2d9 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -680,6 +680,8 @@ struct hid_driver { * shouldn't allocate anything to not leak memory * @request: send report request to device (e.g. feature report) * @wait: wait for buffered io to complete (send/recv reports) + * @raw_request: send raw report request to device (e.g. feature report) + * @output_report: send output report to device * @idle: send idle request to device */ struct hid_ll_driver { @@ -973,6 +975,49 @@ static inline void hid_hw_request(struct hid_device *hdev, hdev->ll_driver->request(hdev, report, reqtype); } +/** + * hid_hw_raw_request - send report request to device + * + * @hdev: hid device + * @reportnum: report ID + * @buf: in/out data to transfer + * @len: length of buf + * @rtype: HID report type + * @reqtype: HID_REQ_GET_REPORT or HID_REQ_SET_REPORT + * + * @return: count of data transfered, negative if error + * + * Same behavior as hid_hw_request, but with raw buffers instead. + */ +static inline int hid_hw_raw_request(struct hid_device *hdev, + unsigned char reportnum, __u8 *buf, + size_t len, unsigned char rtype, int reqtype) +{ + if (hdev->ll_driver->raw_request) + return hdev->ll_driver->raw_request(hdev, reportnum, buf, len, + rtype, reqtype); + + return -ENOSYS; +} + +/** + * hid_hw_output_report - send output report to device + * + * @hdev: hid device + * @buf: raw data to transfer + * @len: length of buf + * + * @return: count of data transfered, negative if error + */ +static inline int hid_hw_output_report(struct hid_device *hdev, __u8 *buf, + size_t len) +{ + if (hdev->ll_driver->output_report) + return hdev->ll_driver->output_report(hdev, buf, len); + + return -ENOSYS; +} + /** * hid_hw_idle - send idle request to device * From 0e40d35637d68f654b66f4562c9a914be7d06bd1 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 5 Feb 2014 16:33:17 -0500 Subject: [PATCH 06/25] HID: logitech-dj: remove hidinput_input_event hid-logitech-dj uses its own ->hidinput_input_event() instead of the generic binding in hid-input. Moving the handling of LEDs towards logi_dj_output_hidraw_report() allows two things: - remove hidinput_input_event in struct hid_device - hidraw user space programs can also set the LEDs Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-dj.c | 106 ++++++++++++++-------------------- 1 file changed, 42 insertions(+), 64 deletions(-) diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index f45279c3b11a..980ede54782d 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -44,14 +44,6 @@ static const char kbd_descriptor[] = { 0x19, 0xE0, /* USAGE_MINIMUM (Left Control) */ 0x29, 0xE7, /* USAGE_MAXIMUM (Right GUI) */ 0x81, 0x02, /* INPUT (Data,Var,Abs) */ - 0x95, 0x05, /* REPORT COUNT (5) */ - 0x05, 0x08, /* USAGE PAGE (LED page) */ - 0x19, 0x01, /* USAGE MINIMUM (1) */ - 0x29, 0x05, /* USAGE MAXIMUM (5) */ - 0x91, 0x02, /* OUTPUT (Data, Variable, Absolute) */ - 0x95, 0x01, /* REPORT COUNT (1) */ - 0x75, 0x03, /* REPORT SIZE (3) */ - 0x91, 0x01, /* OUTPUT (Constant) */ 0x95, 0x06, /* REPORT_COUNT (6) */ 0x75, 0x08, /* REPORT_SIZE (8) */ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ @@ -60,6 +52,18 @@ static const char kbd_descriptor[] = { 0x19, 0x00, /* USAGE_MINIMUM (no event) */ 0x2A, 0xFF, 0x00, /* USAGE_MAXIMUM (reserved) */ 0x81, 0x00, /* INPUT (Data,Ary,Abs) */ + 0x85, 0x0e, /* REPORT_ID (14) */ + 0x05, 0x08, /* USAGE PAGE (LED page) */ + 0x95, 0x05, /* REPORT COUNT (5) */ + 0x75, 0x01, /* REPORT SIZE (1) */ + 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ + 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ + 0x19, 0x01, /* USAGE MINIMUM (1) */ + 0x29, 0x05, /* USAGE MAXIMUM (5) */ + 0x91, 0x02, /* OUTPUT (Data, Variable, Absolute) */ + 0x95, 0x01, /* REPORT COUNT (1) */ + 0x75, 0x03, /* REPORT SIZE (3) */ + 0x91, 0x01, /* OUTPUT (Constant) */ 0xC0 }; @@ -544,10 +548,37 @@ static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf, size_t count, unsigned char report_type) { - /* Called by hid raw to send data */ - dbg_hid("%s\n", __func__); + struct dj_device *djdev = hid->driver_data; + struct dj_receiver_dev *djrcv_dev = djdev->dj_receiver_dev; + u8 *out_buf; + int ret; - return 0; + if (buf[0] != REPORT_TYPE_LEDS) + return -EINVAL; + + out_buf = kzalloc(DJREPORT_SHORT_LENGTH, GFP_ATOMIC); + if (!out_buf) + return -ENOMEM; + + if (count < DJREPORT_SHORT_LENGTH - 2) + count = DJREPORT_SHORT_LENGTH - 2; + + out_buf[0] = REPORT_ID_DJ_SHORT; + out_buf[1] = djdev->device_index; + memcpy(out_buf + 2, buf, count); + + /* + * hid-generic calls us with hid_output_raw_report(), but the LEDs + * are set through a SET_REPORT command. It works for USB-HID devices + * because usbhid either calls a SET_REPORT or directly send the output + * report depending if the device presents an urbout. + * Let be simple, send a SET_REPORT request. + */ + ret = hid_hw_raw_request(djrcv_dev->hdev, out_buf[0], out_buf, + DJREPORT_SHORT_LENGTH, report_type, HID_REQ_SET_REPORT); + + kfree(out_buf); + return ret; } static void rdcat(char *rdesc, unsigned int *rsize, const char *data, unsigned int size) @@ -613,58 +644,6 @@ static int logi_dj_ll_parse(struct hid_device *hid) return retval; } -static int logi_dj_ll_input_event(struct input_dev *dev, unsigned int type, - unsigned int code, int value) -{ - /* Sent by the input layer to handle leds and Force Feedback */ - struct hid_device *dj_hiddev = input_get_drvdata(dev); - struct dj_device *dj_dev = dj_hiddev->driver_data; - - struct dj_receiver_dev *djrcv_dev = - dev_get_drvdata(dj_hiddev->dev.parent); - struct hid_device *dj_rcv_hiddev = djrcv_dev->hdev; - struct hid_report_enum *output_report_enum; - - struct hid_field *field; - struct hid_report *report; - unsigned char *data; - int offset; - - dbg_hid("%s: %s, type:%d | code:%d | value:%d\n", - __func__, dev->phys, type, code, value); - - if (type != EV_LED) - return -1; - - offset = hidinput_find_field(dj_hiddev, type, code, &field); - - if (offset == -1) { - dev_warn(&dev->dev, "event field not found\n"); - return -1; - } - hid_set_field(field, offset, value); - - data = hid_alloc_report_buf(field->report, GFP_ATOMIC); - if (!data) { - dev_warn(&dev->dev, "failed to allocate report buf memory\n"); - return -1; - } - - hid_output_report(field->report, &data[0]); - - output_report_enum = &dj_rcv_hiddev->report_enum[HID_OUTPUT_REPORT]; - report = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT]; - hid_set_field(report->field[0], 0, dj_dev->device_index); - hid_set_field(report->field[0], 1, REPORT_TYPE_LEDS); - hid_set_field(report->field[0], 2, data[1]); - - hid_hw_request(dj_rcv_hiddev, report, HID_REQ_SET_REPORT); - - kfree(data); - - return 0; -} - static int logi_dj_ll_start(struct hid_device *hid) { dbg_hid("%s\n", __func__); @@ -683,7 +662,6 @@ static struct hid_ll_driver logi_dj_ll_driver = { .stop = logi_dj_ll_stop, .open = logi_dj_ll_open, .close = logi_dj_ll_close, - .hidinput_input_event = logi_dj_ll_input_event, }; From 2f0cd0300e8f73c1a02bd11a6185a5c8534f4b09 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 5 Feb 2014 16:33:18 -0500 Subject: [PATCH 07/25] HID: HIDp: remove hidp_hidinput_event hidp uses its own ->hidinput_input_event() instead of the generic binding in hid-input. Moving the handling of LEDs towards hidp_hidinput_event() allows two things: - remove hidinput_input_event definitively from struct hid_device - hidraw user space programs can also set the LEDs Reviewed-by: David Herrmann Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- net/bluetooth/hidp/core.c | 46 --------------------------------------- 1 file changed, 46 deletions(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index b062cee3f319..469e61b27dcf 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -223,51 +223,6 @@ static void hidp_input_report(struct hidp_session *session, struct sk_buff *skb) input_sync(dev); } -static int hidp_send_report(struct hidp_session *session, struct hid_report *report) -{ - unsigned char hdr; - u8 *buf; - int rsize, ret; - - buf = hid_alloc_report_buf(report, GFP_ATOMIC); - if (!buf) - return -EIO; - - hid_output_report(report, buf); - hdr = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT; - - rsize = ((report->size - 1) >> 3) + 1 + (report->id > 0); - ret = hidp_send_intr_message(session, hdr, buf, rsize); - - kfree(buf); - return ret; -} - -static int hidp_hidinput_event(struct input_dev *dev, unsigned int type, - unsigned int code, int value) -{ - struct hid_device *hid = input_get_drvdata(dev); - struct hidp_session *session = hid->driver_data; - struct hid_field *field; - int offset; - - BT_DBG("session %p type %d code %d value %d", - session, type, code, value); - - if (type != EV_LED) - return -1; - - offset = hidinput_find_field(hid, type, code, &field); - if (offset == -1) { - hid_warn(dev, "event field not found\n"); - return -1; - } - - hid_set_field(field, offset, value); - - return hidp_send_report(session, field->report); -} - static int hidp_get_raw_report(struct hid_device *hid, unsigned char report_number, unsigned char *data, size_t count, @@ -817,7 +772,6 @@ static struct hid_ll_driver hidp_hid_driver = { .close = hidp_close, .raw_request = hidp_raw_request, .output_report = hidp_output_report, - .hidinput_input_event = hidp_hidinput_event, }; /* This function sets up the hid device. It does not add it From b40272e4d0e6d07a0bf9409e5f95d622422cd73d Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 5 Feb 2014 16:33:19 -0500 Subject: [PATCH 08/25] HID: remove hidinput_input_event handler All the different transport drivers use now the generic event handling in hid-input. We can remove the handler definitively now. Reviewed-by: David Herrmann Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 4 +--- include/linux/hid.h | 4 ---- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index d50e7313b171..e5bb3c378292 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1263,9 +1263,7 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid) } input_set_drvdata(input_dev, hid); - if (hid->ll_driver->hidinput_input_event) - input_dev->event = hid->ll_driver->hidinput_input_event; - else if (hid->ll_driver->request || hid->hid_output_raw_report) + if (hid->ll_driver->request || hid->hid_output_raw_report) input_dev->event = hidinput_input_event; input_dev->open = hidinput_open; input_dev->close = hidinput_close; diff --git a/include/linux/hid.h b/include/linux/hid.h index dddcad07c2d9..38c307b8138a 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -675,7 +675,6 @@ struct hid_driver { * @stop: called on remove * @open: called by input layer on open * @close: called by input layer on close - * @hidinput_input_event: event input event (e.g. ff or leds) * @parse: this method is called only once to parse the device data, * shouldn't allocate anything to not leak memory * @request: send report request to device (e.g. feature report) @@ -693,9 +692,6 @@ struct hid_ll_driver { int (*power)(struct hid_device *hdev, int level); - int (*hidinput_input_event) (struct input_dev *idev, unsigned int type, - unsigned int code, int value); - int (*parse)(struct hid_device *hdev); void (*request)(struct hid_device *hdev, From e9d5da97a6a8da75f8a58b722bbd59ef474e0f47 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 5 Feb 2014 16:33:20 -0500 Subject: [PATCH 09/25] HID: HIDp: remove duplicated coded - Move hidp_output_report() above - Removed duplicated code in hidp_output_raw_report() Reviewed-by: David Herrmann Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- net/bluetooth/hidp/core.c | 68 +++++++-------------------------------- 1 file changed, 11 insertions(+), 57 deletions(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 469e61b27dcf..02670b30895a 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -373,62 +373,25 @@ static int hidp_set_raw_report(struct hid_device *hid, unsigned char reportnum, return ret; } -static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count, - unsigned char report_type) +static int hidp_output_report(struct hid_device *hid, __u8 *data, size_t count) { struct hidp_session *session = hid->driver_data; - int ret; + return hidp_send_intr_message(session, + HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT, + data, count); +} + +static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, + size_t count, unsigned char report_type) +{ if (report_type == HID_OUTPUT_REPORT) { - report_type = HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT; - return hidp_send_intr_message(session, report_type, - data, count); + return hidp_output_report(hid, data, count); } else if (report_type != HID_FEATURE_REPORT) { return -EINVAL; } - if (mutex_lock_interruptible(&session->report_mutex)) - return -ERESTARTSYS; - - /* Set up our wait, and send the report request to the device. */ - set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); - report_type = HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE; - ret = hidp_send_ctrl_message(session, report_type, data, count); - if (ret) - goto err; - - /* Wait for the ACK from the device. */ - while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags) && - !atomic_read(&session->terminate)) { - int res; - - res = wait_event_interruptible_timeout(session->report_queue, - !test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags) - || atomic_read(&session->terminate), - 10*HZ); - if (res == 0) { - /* timeout */ - ret = -EIO; - goto err; - } - if (res < 0) { - /* signal */ - ret = -ERESTARTSYS; - goto err; - } - } - - if (!session->output_report_success) { - ret = -EIO; - goto err; - } - - ret = count; - -err: - clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); - mutex_unlock(&session->report_mutex); - return ret; + return hidp_set_raw_report(hid, data[0], data, count, report_type); } static int hidp_raw_request(struct hid_device *hid, unsigned char reportnum, @@ -445,15 +408,6 @@ static int hidp_raw_request(struct hid_device *hid, unsigned char reportnum, } } -static int hidp_output_report(struct hid_device *hid, __u8 *data, size_t count) -{ - struct hidp_session *session = hid->driver_data; - - return hidp_send_intr_message(session, - HIDP_TRANS_DATA | HIDP_DATA_RTYPE_OUPUT, - data, count); -} - static void hidp_idle_timeout(unsigned long arg) { struct hidp_session *session = (struct hidp_session *) arg; From f9bcca405624c7f4a0cf4a1b78f8b3a312ca4dab Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 5 Feb 2014 16:33:21 -0500 Subject: [PATCH 10/25] HID: usbhid: remove duplicated code Well, no use to keep twice the same code. Reviewed-by: David Herrmann Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 64 ++++++----------------------------- 1 file changed, 11 insertions(+), 53 deletions(-) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index f8ca312bae1b..406497b120ea 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -915,59 +915,6 @@ static int usbhid_set_raw_report(struct hid_device *hid, unsigned int reportnum, return ret; } - -static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count, - unsigned char report_type) -{ - struct usbhid_device *usbhid = hid->driver_data; - struct usb_device *dev = hid_to_usb_dev(hid); - struct usb_interface *intf = usbhid->intf; - struct usb_host_interface *interface = intf->cur_altsetting; - int ret; - - if (usbhid->urbout && report_type != HID_FEATURE_REPORT) { - int actual_length; - int skipped_report_id = 0; - - if (buf[0] == 0x0) { - /* Don't send the Report ID */ - buf++; - count--; - skipped_report_id = 1; - } - ret = usb_interrupt_msg(dev, usbhid->urbout->pipe, - buf, count, &actual_length, - USB_CTRL_SET_TIMEOUT); - /* return the number of bytes transferred */ - if (ret == 0) { - ret = actual_length; - /* count also the report id */ - if (skipped_report_id) - ret++; - } - } else { - int skipped_report_id = 0; - int report_id = buf[0]; - if (buf[0] == 0x0) { - /* Don't send the Report ID */ - buf++; - count--; - skipped_report_id = 1; - } - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), - HID_REQ_SET_REPORT, - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, - ((report_type + 1) << 8) | report_id, - interface->desc.bInterfaceNumber, buf, count, - USB_CTRL_SET_TIMEOUT); - /* count also the report id, if this was a numbered report. */ - if (ret > 0 && skipped_report_id) - ret++; - } - - return ret; -} - static int usbhid_output_report(struct hid_device *hid, __u8 *buf, size_t count) { struct usbhid_device *usbhid = hid->driver_data; @@ -998,6 +945,17 @@ static int usbhid_output_report(struct hid_device *hid, __u8 *buf, size_t count) return ret; } +static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, + size_t count, unsigned char report_type) +{ + struct usbhid_device *usbhid = hid->driver_data; + + if (usbhid->urbout && report_type != HID_FEATURE_REPORT) + return usbhid_output_report(hid, buf, count); + + return usbhid_set_raw_report(hid, buf[0], buf, count, report_type); +} + static void usbhid_restart_queues(struct usbhid_device *usbhid) { if (usbhid->urbout && !test_bit(HID_OUT_RUNNING, &usbhid->iofl)) From cafebc058bf86e63fff5354864781d3de11e41d3 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 5 Feb 2014 16:33:22 -0500 Subject: [PATCH 11/25] HID: remove hid_get_raw_report in struct hid_device dev->hid_get_raw_report(X) and hid_hw_raw_request(X, HID_REQ_GET_REPORT) are strictly equivalent. Switch the hid subsystem to the hid_hw notation and remove the field .hid_get_raw_report in struct hid_device. Reviewed-by: David Herrmann Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 6 +++--- drivers/hid/hid-sony.c | 3 ++- drivers/hid/hidraw.c | 7 ++++--- drivers/hid/i2c-hid/i2c-hid.c | 1 - drivers/hid/uhid.c | 1 - drivers/hid/usbhid/hid-core.c | 1 - include/linux/hid.h | 3 --- net/bluetooth/hidp/core.c | 1 - 8 files changed, 9 insertions(+), 14 deletions(-) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index e5bb3c378292..5bd17b256856 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -350,9 +350,9 @@ static int hidinput_get_battery_property(struct power_supply *psy, ret = -ENOMEM; break; } - ret = dev->hid_get_raw_report(dev, dev->battery_report_id, - buf, 2, - dev->battery_report_type); + ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 2, + dev->battery_report_type, + HID_REQ_GET_REPORT); if (ret != 2) { ret = -ENODATA; diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 12354055d474..3930acbdee98 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -706,7 +706,8 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev) if (!buf) return -ENOMEM; - ret = hdev->hid_get_raw_report(hdev, 0xf2, buf, 17, HID_FEATURE_REPORT); + ret = hid_hw_raw_request(hdev, 0xf2, buf, 17, HID_FEATURE_REPORT, + HID_REQ_GET_REPORT); if (ret < 0) hid_err(hdev, "can't set operational mode\n"); diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index cb0137b3718d..4b2dc956c702 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -189,7 +189,7 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t dev = hidraw_table[minor]->hid; - if (!dev->hid_get_raw_report) { + if (!dev->ll_driver->raw_request) { ret = -ENODEV; goto out; } @@ -216,14 +216,15 @@ static ssize_t hidraw_get_report(struct file *file, char __user *buffer, size_t /* * Read the first byte from the user. This is the report number, - * which is passed to dev->hid_get_raw_report(). + * which is passed to hid_hw_raw_request(). */ if (copy_from_user(&report_number, buffer, 1)) { ret = -EFAULT; goto out_free; } - ret = dev->hid_get_raw_report(dev, report_number, buf, count, report_type); + ret = hid_hw_raw_request(dev, report_number, buf, count, report_type, + HID_REQ_GET_REPORT); if (ret < 0) goto out_free; diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index e914f2755491..f4ea7343e823 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -1005,7 +1005,6 @@ static int i2c_hid_probe(struct i2c_client *client, hid->driver_data = client; hid->ll_driver = &i2c_hid_ll_driver; - hid->hid_get_raw_report = i2c_hid_get_raw_report; hid->hid_output_raw_report = i2c_hid_output_raw_report; hid->dev.parent = &client->dev; ACPI_COMPANION_SET(&hid->dev, ACPI_COMPANION(&client->dev)); diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index f5a2b1931143..12439e1eeae2 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -404,7 +404,6 @@ static int uhid_dev_create(struct uhid_device *uhid, hid->uniq[63] = 0; hid->ll_driver = &uhid_hid_driver; - hid->hid_get_raw_report = uhid_hid_get_raw; hid->hid_output_raw_report = uhid_hid_output_raw; hid->bus = ev->u.create.bus; hid->vendor = ev->u.create.vendor; diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 406497b120ea..b9a770f4d7ae 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -1289,7 +1289,6 @@ static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id * usb_set_intfdata(intf, hid); hid->ll_driver = &usb_hid_driver; - hid->hid_get_raw_report = usbhid_get_raw_report; hid->hid_output_raw_report = usbhid_output_raw_report; hid->ff_init = hid_pidff_init; #ifdef CONFIG_USB_HIDDEV diff --git a/include/linux/hid.h b/include/linux/hid.h index 38c307b8138a..c56681a66b0b 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -508,9 +508,6 @@ struct hid_device { /* device report descriptor */ struct hid_usage *, __s32); void (*hiddev_report_event) (struct hid_device *, struct hid_report *); - /* handler for raw input (Get_Report) data, used by hidraw */ - int (*hid_get_raw_report) (struct hid_device *, unsigned char, __u8 *, size_t, unsigned char); - /* handler for raw output data, used by hidraw */ int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t, unsigned char); diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 02670b30895a..77c4badb3e9d 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -773,7 +773,6 @@ static int hidp_setup_hid(struct hidp_session *session, hid->dev.parent = &session->conn->hcon->dev; hid->ll_driver = &hidp_hid_driver; - hid->hid_get_raw_report = hidp_get_raw_report; hid->hid_output_raw_report = hidp_output_raw_report; /* True if device is blacklisted in drivers/hid/hid-core.c */ From 7e845d46b13e7730a3720e978c28117ce422edf9 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 5 Feb 2014 16:33:23 -0500 Subject: [PATCH 12/25] HID: introduce helper to access hid_output_raw_report() Add a helper to access hdev->hid_output_raw_report(). To convert the drivers, use the following snippets: for i in drivers/hid/*.c do sed -i.bak "s/[^ \t]*->hid_output_raw_report(/hid_output_raw_report(/g" $i done Then manually fix for checkpatch.pl Reviewed-by: David Herrmann Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-input.c | 2 +- drivers/hid/hid-lg.c | 6 ++++-- drivers/hid/hid-magicmouse.c | 2 +- drivers/hid/hid-sony.c | 6 +++--- drivers/hid/hid-thingm.c | 4 ++-- drivers/hid/hid-wacom.c | 16 +++++++--------- drivers/hid/hid-wiimote-core.c | 2 +- drivers/hid/hidraw.c | 2 +- include/linux/hid.h | 16 ++++++++++++++++ 9 files changed, 36 insertions(+), 20 deletions(-) diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 5bd17b256856..15959fbae268 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1184,7 +1184,7 @@ static void hidinput_led_worker(struct work_struct *work) hid_output_report(report, buf); /* synchronous output report */ - hid->hid_output_raw_report(hid, buf, len, HID_OUTPUT_REPORT); + hid_output_raw_report(hid, buf, len, HID_OUTPUT_REPORT); kfree(buf); } diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index 9fe9d4ac3114..76ed7e512dcf 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -692,7 +692,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) { unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); + ret = hid_output_raw_report(hdev, buf, sizeof(buf), + HID_FEATURE_REPORT); if (ret >= 0) { /* insert a little delay of 10 jiffies ~ 40ms */ @@ -704,7 +705,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) buf[1] = 0xB2; get_random_bytes(&buf[2], 2); - ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); + ret = hid_output_raw_report(hdev, buf, sizeof(buf), + HID_FEATURE_REPORT); } } diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 3b43d1cfa936..cb5db3afc690 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -538,7 +538,7 @@ static int magicmouse_probe(struct hid_device *hdev, * but there seems to be no other way of switching the mode. * Thus the super-ugly hacky success check below. */ - ret = hdev->hid_output_raw_report(hdev, feature, sizeof(feature), + ret = hid_output_raw_report(hdev, feature, sizeof(feature), HID_FEATURE_REPORT); if (ret != -EIO && ret != sizeof(feature)) { hid_err(hdev, "unable to request touch data (%d)\n", ret); diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 3930acbdee98..075089b37236 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -720,7 +720,8 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev) static int sixaxis_set_operational_bt(struct hid_device *hdev) { unsigned char buf[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 }; - return hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT); + return hid_output_raw_report(hdev, buf, sizeof(buf), + HID_FEATURE_REPORT); } static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds) @@ -942,8 +943,7 @@ static void sixaxis_state_worker(struct work_struct *work) buf[10] |= sc->led_state[2] << 3; buf[10] |= sc->led_state[3] << 4; - sc->hdev->hid_output_raw_report(sc->hdev, buf, sizeof(buf), - HID_OUTPUT_REPORT); + hid_output_raw_report(sc->hdev, buf, sizeof(buf), HID_OUTPUT_REPORT); } static void dualshock4_state_worker(struct work_struct *work) diff --git a/drivers/hid/hid-thingm.c b/drivers/hid/hid-thingm.c index 99342cfa0ea2..7dd3197f3b3e 100644 --- a/drivers/hid/hid-thingm.c +++ b/drivers/hid/hid-thingm.c @@ -48,8 +48,8 @@ static int blink1_send_command(struct blink1_data *data, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8]); - ret = data->hdev->hid_output_raw_report(data->hdev, buf, - BLINK1_CMD_SIZE, HID_FEATURE_REPORT); + ret = hid_output_raw_report(data->hdev, buf, BLINK1_CMD_SIZE, + HID_FEATURE_REPORT); return ret < 0 ? ret : 0; } diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c index 60c75dcbbdb8..c720db912edb 100644 --- a/drivers/hid/hid-wacom.c +++ b/drivers/hid/hid-wacom.c @@ -128,8 +128,7 @@ static void wacom_set_image(struct hid_device *hdev, const char *image, rep_data[0] = WAC_CMD_ICON_START_STOP; rep_data[1] = 0; - ret = hdev->hid_output_raw_report(hdev, rep_data, 2, - HID_FEATURE_REPORT); + ret = hid_output_raw_report(hdev, rep_data, 2, HID_FEATURE_REPORT); if (ret < 0) goto err; @@ -143,15 +142,14 @@ static void wacom_set_image(struct hid_device *hdev, const char *image, rep_data[j + 3] = p[(i << 6) + j]; rep_data[2] = i; - ret = hdev->hid_output_raw_report(hdev, rep_data, 67, + ret = hid_output_raw_report(hdev, rep_data, 67, HID_FEATURE_REPORT); } rep_data[0] = WAC_CMD_ICON_START_STOP; rep_data[1] = 0; - ret = hdev->hid_output_raw_report(hdev, rep_data, 2, - HID_FEATURE_REPORT); + ret = hid_output_raw_report(hdev, rep_data, 2, HID_FEATURE_REPORT); err: return; @@ -183,7 +181,7 @@ static void wacom_leds_set_brightness(struct led_classdev *led_dev, buf[3] = value; /* use fixed brightness for OLEDs */ buf[4] = 0x08; - hdev->hid_output_raw_report(hdev, buf, 9, HID_FEATURE_REPORT); + hid_output_raw_report(hdev, buf, 9, HID_FEATURE_REPORT); kfree(buf); } @@ -339,7 +337,7 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed) rep_data[0] = 0x03 ; rep_data[1] = 0x00; limit = 3; do { - ret = hdev->hid_output_raw_report(hdev, rep_data, 2, + ret = hid_output_raw_report(hdev, rep_data, 2, HID_FEATURE_REPORT); } while (ret < 0 && limit-- > 0); @@ -352,7 +350,7 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed) rep_data[1] = 0x00; limit = 3; do { - ret = hdev->hid_output_raw_report(hdev, + ret = hid_output_raw_report(hdev, rep_data, 2, HID_FEATURE_REPORT); } while (ret < 0 && limit-- > 0); @@ -378,7 +376,7 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed) rep_data[0] = 0x03; rep_data[1] = wdata->features; - ret = hdev->hid_output_raw_report(hdev, rep_data, 2, + ret = hid_output_raw_report(hdev, rep_data, 2, HID_FEATURE_REPORT); if (ret >= 0) wdata->high_speed = speed; diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index abb20db2b443..d7dc6c5bc244 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -35,7 +35,7 @@ static int wiimote_hid_send(struct hid_device *hdev, __u8 *buffer, if (!buf) return -ENOMEM; - ret = hdev->hid_output_raw_report(hdev, buf, count, HID_OUTPUT_REPORT); + ret = hid_output_raw_report(hdev, buf, count, HID_OUTPUT_REPORT); kfree(buf); return ret; diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 4b2dc956c702..f8708c93f85c 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -153,7 +153,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, goto out_free; } - ret = dev->hid_output_raw_report(dev, buf, count, report_type); + ret = hid_output_raw_report(dev, buf, count, report_type); out_free: kfree(buf); out: diff --git a/include/linux/hid.h b/include/linux/hid.h index c56681a66b0b..a837ede65ec6 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -1011,6 +1011,22 @@ static inline int hid_hw_output_report(struct hid_device *hdev, __u8 *buf, return -ENOSYS; } +/** + * hid_output_raw_report - send an output or a feature report to the device + * + * @hdev: hid device + * @buf: raw data to transfer + * @len: length of buf + * @report_type: HID_FEATURE_REPORT or HID_OUTPUT_REPORT + * + * @return: count of data transfered, negative if error + */ +static inline int hid_output_raw_report(struct hid_device *hdev, __u8 *buf, + size_t len, unsigned char report_type) +{ + return hdev->hid_output_raw_report(hdev, buf, len, report_type); +} + /** * hid_hw_idle - send idle request to device * From 6fad42d5fb42ffcf665634591ad4d9531536eb44 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 5 Feb 2014 16:33:24 -0500 Subject: [PATCH 13/25] HID: Add HID transport driver documentation Add David Herrmann's documentation for the new low-level HID transport driver functions. Signed-off-by: Frank Praznik Signed-off-by: David Herrmann Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- Documentation/hid/hid-transport.txt | 316 ++++++++++++++++++++++++++++ 1 file changed, 316 insertions(+) create mode 100644 Documentation/hid/hid-transport.txt diff --git a/Documentation/hid/hid-transport.txt b/Documentation/hid/hid-transport.txt new file mode 100644 index 000000000000..9dbbceaef4f3 --- /dev/null +++ b/Documentation/hid/hid-transport.txt @@ -0,0 +1,316 @@ + HID I/O Transport Drivers + =========================== + +The HID subsystem is independent of the underlying transport driver. Initially, +only USB was supported, but other specifications adopted the HID design and +provided new transport drivers. The kernel includes at least support for USB, +Bluetooth, I2C and user-space I/O drivers. + +1) HID Bus +========== + +The HID subsystem is designed as a bus. Any I/O subsystem may provide HID +devices and register them with the HID bus. HID core then loads generic device +drivers on top of it. The transport drivers are responsible of raw data +transport and device setup/management. HID core is responsible of +report-parsing, report interpretation and the user-space API. Device specifics +and quirks are handled by all layers depending on the quirk. + + +-----------+ +-----------+ +-----------+ +-----------+ + | Device #1 | | Device #i | | Device #j | | Device #k | + +-----------+ +-----------+ +-----------+ +-----------+ + \\ // \\ // + +------------+ +------------+ + | I/O Driver | | I/O Driver | + +------------+ +------------+ + || || + +------------------+ +------------------+ + | Transport Driver | | Transport Driver | + +------------------+ +------------------+ + \___ ___/ + \ / + +----------------+ + | HID Core | + +----------------+ + / | | \ + / | | \ + ____________/ | | \_________________ + / | | \ + / | | \ + +----------------+ +-----------+ +------------------+ +------------------+ + | Generic Driver | | MT Driver | | Custom Driver #1 | | Custom Driver #2 | + +----------------+ +-----------+ +------------------+ +------------------+ + +Example Drivers: + I/O: USB, I2C, Bluetooth-l2cap + Transport: USB-HID, I2C-HID, BT-HIDP + +Everything below "HID Core" is simplified in this graph as it is only of +interest to HID device drivers. Transport drivers do not need to know the +specifics. + +1.1) Device Setup +----------------- + +I/O drivers normally provide hotplug detection or device enumeration APIs to the +transport drivers. Transport drivers use this to find any suitable HID device. +They allocate HID device objects and register them with HID core. Transport +drivers are not required to register themselves with HID core. HID core is never +aware of which transport drivers are available and is not interested in it. It +is only interested in devices. + +Transport drivers attach a constant "struct hid_ll_driver" object with each +device. Once a device is registered with HID core, the callbacks provided via +this struct are used by HID core to communicate with the device. + +Transport drivers are responsible of detecting device failures and unplugging. +HID core will operate a device as long as it is registered regardless of any +device failures. Once transport drivers detect unplug or failure events, they +must unregister the device from HID core and HID core will stop using the +provided callbacks. + +1.2) Transport Driver Requirements +---------------------------------- + +The terms "asynchronous" and "synchronous" in this document describe the +transmission behavior regarding acknowledgements. An asynchronous channel must +not perform any synchronous operations like waiting for acknowledgements or +verifications. Generally, HID calls operating on asynchronous channels must be +running in atomic-context just fine. +On the other hand, synchronous channels can be implemented by the transport +driver in whatever way they like. They might just be the same as asynchronous +channels, but they can also provide acknowledgement reports, automatic +retransmission on failure, etc. in a blocking manner. If such functionality is +required on asynchronous channels, a transport-driver must implement that via +its own worker threads. + +HID core requires transport drivers to follow a given design. A Transport +driver must provide two bi-directional I/O channels to each HID device. These +channels must not necessarily be bi-directional in the hardware itself. A +transport driver might just provide 4 uni-directional channels. Or it might +multiplex all four on a single physical channel. However, in this document we +will describe them as two bi-directional channels as they have several +properties in common. + + - Interrupt Channel (intr): The intr channel is used for asynchronous data + reports. No management commands or data acknowledgements are sent on this + channel. Any unrequested incoming or outgoing data report must be sent on + this channel and is never acknowledged by the remote side. Devices usually + send their input events on this channel. Outgoing events are normally + not send via intr, except if high throughput is required. + - Control Channel (ctrl): The ctrl channel is used for synchronous requests and + device management. Unrequested data input events must not be sent on this + channel and are normally ignored. Instead, devices only send management + events or answers to host requests on this channel. + The control-channel is used for direct blocking queries to the device + independent of any events on the intr-channel. + Outgoing reports are usually sent on the ctrl channel via synchronous + SET_REPORT requests. + +Communication between devices and HID core is mostly done via HID reports. A +report can be of one of three types: + + - INPUT Report: Input reports provide data from device to host. This + data may include button events, axis events, battery status or more. This + data is generated by the device and sent to the host with or without + requiring explicit requests. Devices can choose to send data continuously or + only on change. + - OUTPUT Report: Output reports change device states. They are sent from host + to device and may include LED requests, rumble requests or more. Output + reports are never sent from device to host, but a host can retrieve their + current state. + Hosts may choose to send output reports either continuously or only on + change. + - FEATURE Report: Feature reports are used for specific static device features + and never reported spontaneously. A host can read and/or write them to access + data like battery-state or device-settings. + Feature reports are never sent without requests. A host must explicitly set + or retrieve a feature report. This also means, feature reports are never sent + on the intr channel as this channel is asynchronous. + +INPUT and OUTPUT reports can be sent as pure data reports on the intr channel. +For INPUT reports this is the usual operational mode. But for OUTPUT reports, +this is rarely done as OUTPUT reports are normally quite scarce. But devices are +free to make excessive use of asynchronous OUTPUT reports (for instance, custom +HID audio speakers make great use of it). + +Plain reports must not be sent on the ctrl channel, though. Instead, the ctrl +channel provides synchronous GET/SET_REPORT requests. Plain reports are only +allowed on the intr channel and are the only means of data there. + + - GET_REPORT: A GET_REPORT request has a report ID as payload and is sent + from host to device. The device must answer with a data report for the + requested report ID on the ctrl channel as a synchronous acknowledgement. + Only one GET_REPORT request can be pending for each device. This restriction + is enforced by HID core as several transport drivers don't allow multiple + simultaneous GET_REPORT requests. + Note that data reports which are sent as answer to a GET_REPORT request are + not handled as generic device events. That is, if a device does not operate + in continuous data reporting mode, an answer to GET_REPORT does not replace + the raw data report on the intr channel on state change. + GET_REPORT is only used by custom HID device drivers to query device state. + Normally, HID core caches any device state so this request is not necessary + on devices that follow the HID specs except during device initialization to + retrieve the current state. + GET_REPORT requests can be sent for any of the 3 report types and shall + return the current report state of the device. However, OUTPUT reports as + payload may be blocked by the underlying transport driver if the + specification does not allow them. + - SET_REPORT: A SET_REPORT request has a report ID plus data as payload. It is + sent from host to device and a device must update it's current report state + according to the given data. Any of the 3 report types can be used. However, + INPUT reports as payload might be blocked by the underlying transport driver + if the specification does not allow them. + A device must answer with a synchronous acknowledgement. However, HID core + does not require transport drivers to forward this acknowledgement to HID + core. + Same as for GET_REPORT, only one SET_REPORT can be pending at a time. This + restriction is enforced by HID core as some transport drivers do not support + multiple synchronous SET_REPORT requests. + +Other ctrl-channel requests are supported by USB-HID but are not available +(or deprecated) in most other transport level specifications: + + - GET/SET_IDLE: Only used by USB-HID and I2C-HID. + - GET/SET_PROTOCOL: Not used by HID core. + - RESET: Used by I2C-HID, not hooked up in HID core. + - SET_POWER: Used by I2C-HID, not hooked up in HID core. + +2) HID API +========== + +2.1) Initialization +------------------- + +Transport drivers normally use the following procedure to register a new device +with HID core: + + struct hid_device *hid; + int ret; + + hid = hid_allocate_device(); + if (IS_ERR(hid)) { + ret = PTR_ERR(hid); + goto err_<...>; + } + + strlcpy(hid->name, , 127); + strlcpy(hid->phys, , 63); + strlcpy(hid->uniq, , 63); + + hid->ll_driver = &custom_ll_driver; + hid->bus = ; + hid->vendor = ; + hid->product = ; + hid->version = ; + hid->country = ; + hid->dev.parent = ; + hid->driver_data = ; + + ret = hid_add_device(hid); + if (ret) + goto err_<...>; + +Once hid_add_device() is entered, HID core might use the callbacks provided in +"custom_ll_driver". Note that fields like "country" can be ignored by underlying +transport-drivers if not supported. + +To unregister a device, use: + + hid_destroy_device(hid); + +Once hid_destroy_device() returns, HID core will no longer make use of any +driver callbacks. + +2.2) hid_ll_driver operations +----------------------------- + +The available HID callbacks are: + - int (*start) (struct hid_device *hdev) + Called from HID device drivers once they want to use the device. Transport + drivers can choose to setup their device in this callback. However, normally + devices are already set up before transport drivers register them to HID core + so this is mostly only used by USB-HID. + + - void (*stop) (struct hid_device *hdev) + Called from HID device drivers once they are done with a device. Transport + drivers can free any buffers and deinitialize the device. But note that + ->start() might be called again if another HID device driver is loaded on the + device. + Transport drivers are free to ignore it and deinitialize devices after they + destroyed them via hid_destroy_device(). + + - int (*open) (struct hid_device *hdev) + Called from HID device drivers once they are interested in data reports. + Usually, while user-space didn't open any input API/etc., device drivers are + not interested in device data and transport drivers can put devices asleep. + However, once ->open() is called, transport drivers must be ready for I/O. + ->open() calls are nested for each client that opens the HID device. + + - void (*close) (struct hid_device *hdev) + Called from HID device drivers after ->open() was called but they are no + longer interested in device reports. (Usually if user-space closed any input + devices of the driver). + Transport drivers can put devices asleep and terminate any I/O of all + ->open() calls have been followed by a ->close() call. However, ->start() may + be called again if the device driver is interested in input reports again. + + - int (*parse) (struct hid_device *hdev) + Called once during device setup after ->start() has been called. Transport + drivers must read the HID report-descriptor from the device and tell HID core + about it via hid_parse_report(). + + - int (*power) (struct hid_device *hdev, int level) + Called by HID core to give PM hints to transport drivers. Usually this is + analogical to the ->open() and ->close() hints and redundant. + + - void (*request) (struct hid_device *hdev, struct hid_report *report, + int reqtype) + Send an HID request on the ctrl channel. "report" contains the report that + should be sent and "reqtype" the request type. Request-type can be + HID_REQ_SET_REPORT or HID_REQ_GET_REPORT. + This callback is optional. If not provided, HID core will assemble a raw + report following the HID specs and send it via the ->raw_request() callback. + The transport driver is free to implement this asynchronously. + + - int (*wait) (struct hid_device *hdev) + Used by HID core before calling ->request() again. A transport driver can use + it to wait for any pending requests to complete if only one request is + allowed at a time. + + - int (*raw_request) (struct hid_device *hdev, unsigned char reportnum, + __u8 *buf, size_t count, unsigned char rtype, + int reqtype) + Same as ->request() but provides the report as raw buffer. This request shall + be synchronous. A transport driver must not use ->wait() to complete such + requests. + + - int (*output_report) (struct hid_device *hdev, __u8 *buf, size_t len) + Send raw output report via intr channel. Used by some HID device drivers + which require high throughput for outgoing requests on the intr channel. This + must not cause SET_REPORT calls! This must be implemented as asynchronous + output report on the intr channel! + + - int (*idle) (struct hid_device *hdev, int report, int idle, int reqtype) + Perform SET/GET_IDLE request. Only used by USB-HID, do not implement! + +2.3) Data Path +-------------- + +Transport drivers are responsible of reading data from I/O devices. They must +handle any I/O-related state-tracking themselves. HID core does not implement +protocol handshakes or other management commands which can be required by the +given HID transport specification. + +Every raw data packet read from a device must be fed into HID core via +hid_input_report(). You must specify the channel-type (intr or ctrl) and report +type (input/output/feature). Under normal conditions, only input reports are +provided via this API. + +Responses to GET_REPORT requests via ->request() must also be provided via this +API. Responses to ->raw_request() are synchronous and must be intercepted by the +transport driver and not passed to hid_input_report(). +Acknowledgements to SET_REPORT requests are not of interest to HID core. + +---------------------------------------------------- +Written 2013, David Herrmann From 4a76d370f0c0508b5d6580d15eae3d40b47d837c Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 17 Feb 2014 14:15:48 +0100 Subject: [PATCH 14/25] HID: uhid: remove uhid_hid_get_raw() This function is now unused since cafebc058bf8 ("HID: remove hid_get_raw_report in struct hid_device"). Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 83 ---------------------------------------------- 1 file changed, 83 deletions(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 12439e1eeae2..89de2fef46b2 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -123,89 +123,6 @@ static int uhid_hid_parse(struct hid_device *hid) return hid_parse_report(hid, uhid->rd_data, uhid->rd_size); } -static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, - __u8 *buf, size_t count, unsigned char rtype) -{ - struct uhid_device *uhid = hid->driver_data; - __u8 report_type; - struct uhid_event *ev; - unsigned long flags; - int ret; - size_t uninitialized_var(len); - struct uhid_feature_answer_req *req; - - if (!uhid->running) - return -EIO; - - switch (rtype) { - case HID_FEATURE_REPORT: - report_type = UHID_FEATURE_REPORT; - break; - case HID_OUTPUT_REPORT: - report_type = UHID_OUTPUT_REPORT; - break; - case HID_INPUT_REPORT: - report_type = UHID_INPUT_REPORT; - break; - default: - return -EINVAL; - } - - ret = mutex_lock_interruptible(&uhid->report_lock); - if (ret) - return ret; - - ev = kzalloc(sizeof(*ev), GFP_KERNEL); - if (!ev) { - ret = -ENOMEM; - goto unlock; - } - - spin_lock_irqsave(&uhid->qlock, flags); - ev->type = UHID_FEATURE; - ev->u.feature.id = atomic_inc_return(&uhid->report_id); - ev->u.feature.rnum = rnum; - ev->u.feature.rtype = report_type; - - atomic_set(&uhid->report_done, 0); - uhid_queue(uhid, ev); - spin_unlock_irqrestore(&uhid->qlock, flags); - - ret = wait_event_interruptible_timeout(uhid->report_wait, - atomic_read(&uhid->report_done), 5 * HZ); - - /* - * Make sure "uhid->running" is cleared on shutdown before - * "uhid->report_done" is set. - */ - smp_rmb(); - if (!ret || !uhid->running) { - ret = -EIO; - } else if (ret < 0) { - ret = -ERESTARTSYS; - } else { - spin_lock_irqsave(&uhid->qlock, flags); - req = &uhid->report_buf.u.feature_answer; - - if (req->err) { - ret = -EIO; - } else { - ret = 0; - len = min(count, - min_t(size_t, req->size, UHID_DATA_MAX)); - memcpy(buf, req->data, len); - } - - spin_unlock_irqrestore(&uhid->qlock, flags); - } - - atomic_set(&uhid->report_done, 1); - -unlock: - mutex_unlock(&uhid->report_lock); - return ret ? ret : len; -} - static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, unsigned char report_type) { From 41abfb36005e06276a38fdda9ba4162d2a134c54 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 10 Feb 2014 12:58:46 -0500 Subject: [PATCH 15/25] HID: uHID: remove duplicated code uhid_hid_output_report() can be implemented through a simple call to uhid_hid_output_raw(). Signed-off-by: Benjamin Tissoires Reviewed-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 89de2fef46b2..49fbe97ebb16 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -164,27 +164,7 @@ static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, static int uhid_hid_output_report(struct hid_device *hid, __u8 *buf, size_t count) { - struct uhid_device *uhid = hid->driver_data; - unsigned long flags; - struct uhid_event *ev; - - if (count < 1 || count > UHID_DATA_MAX) - return -EINVAL; - - ev = kzalloc(sizeof(*ev), GFP_KERNEL); - if (!ev) - return -ENOMEM; - - ev->type = UHID_OUTPUT; - ev->u.output.size = count; - ev->u.output.rtype = UHID_OUTPUT_REPORT; - memcpy(ev->u.output.data, buf, count); - - spin_lock_irqsave(&uhid->qlock, flags); - uhid_queue(uhid, ev); - spin_unlock_irqrestore(&uhid->qlock, flags); - - return count; + return uhid_hid_output_raw(hid, buf, count, HID_OUTPUT_REPORT); } static struct hid_ll_driver uhid_hid_driver = { From 289a7162175ccabf2b8fa52314705df271b41e40 Mon Sep 17 00:00:00 2001 From: Jiri Kosina Date: Mon, 17 Feb 2014 14:49:34 +0100 Subject: [PATCH 16/25] HID: uhid: reintroduce uhid_hid_get_raw() This reverts commit 4a76d370f0c0508b5d6580d15eae3d40b47d837c. Removing it was a mistake, as we need a means to access GET_REPORT. Reported-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 49fbe97ebb16..b6de90392075 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -123,6 +123,89 @@ static int uhid_hid_parse(struct hid_device *hid) return hid_parse_report(hid, uhid->rd_data, uhid->rd_size); } +static int uhid_hid_get_raw(struct hid_device *hid, unsigned char rnum, + __u8 *buf, size_t count, unsigned char rtype) +{ + struct uhid_device *uhid = hid->driver_data; + __u8 report_type; + struct uhid_event *ev; + unsigned long flags; + int ret; + size_t uninitialized_var(len); + struct uhid_feature_answer_req *req; + + if (!uhid->running) + return -EIO; + + switch (rtype) { + case HID_FEATURE_REPORT: + report_type = UHID_FEATURE_REPORT; + break; + case HID_OUTPUT_REPORT: + report_type = UHID_OUTPUT_REPORT; + break; + case HID_INPUT_REPORT: + report_type = UHID_INPUT_REPORT; + break; + default: + return -EINVAL; + } + + ret = mutex_lock_interruptible(&uhid->report_lock); + if (ret) + return ret; + + ev = kzalloc(sizeof(*ev), GFP_KERNEL); + if (!ev) { + ret = -ENOMEM; + goto unlock; + } + + spin_lock_irqsave(&uhid->qlock, flags); + ev->type = UHID_FEATURE; + ev->u.feature.id = atomic_inc_return(&uhid->report_id); + ev->u.feature.rnum = rnum; + ev->u.feature.rtype = report_type; + + atomic_set(&uhid->report_done, 0); + uhid_queue(uhid, ev); + spin_unlock_irqrestore(&uhid->qlock, flags); + + ret = wait_event_interruptible_timeout(uhid->report_wait, + atomic_read(&uhid->report_done), 5 * HZ); + + /* + * Make sure "uhid->running" is cleared on shutdown before + * "uhid->report_done" is set. + */ + smp_rmb(); + if (!ret || !uhid->running) { + ret = -EIO; + } else if (ret < 0) { + ret = -ERESTARTSYS; + } else { + spin_lock_irqsave(&uhid->qlock, flags); + req = &uhid->report_buf.u.feature_answer; + + if (req->err) { + ret = -EIO; + } else { + ret = 0; + len = min(count, + min_t(size_t, req->size, UHID_DATA_MAX)); + memcpy(buf, req->data, len); + } + + spin_unlock_irqrestore(&uhid->qlock, flags); + } + + atomic_set(&uhid->report_done, 1); + +unlock: + mutex_unlock(&uhid->report_lock); + return ret ? ret : len; +} + static int uhid_hid_output_raw(struct hid_device *hid, __u8 *buf, size_t count, unsigned char report_type) { From 706daeffaf36590efc77142f6a209bfe51804f36 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 10 Feb 2014 12:58:47 -0500 Subject: [PATCH 17/25] HID: uHID: implement .raw_request uHID is missing a SET_REPORT protocol implementation, but as .hid_get_raw_report() as been removed from struct hid_device, there were no means to access GET_REPORT in uhid. Signed-off-by: Benjamin Tissoires Reviewed-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/uhid.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index b6de90392075..60acee422fdc 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -250,6 +250,21 @@ static int uhid_hid_output_report(struct hid_device *hid, __u8 *buf, return uhid_hid_output_raw(hid, buf, count, HID_OUTPUT_REPORT); } +static int uhid_raw_request(struct hid_device *hid, unsigned char reportnum, + __u8 *buf, size_t len, unsigned char rtype, + int reqtype) +{ + switch (reqtype) { + case HID_REQ_GET_REPORT: + return uhid_hid_get_raw(hid, reportnum, buf, len, rtype); + case HID_REQ_SET_REPORT: + /* TODO: implement proper SET_REPORT functionality */ + return -ENOSYS; + default: + return -EIO; + } +} + static struct hid_ll_driver uhid_hid_driver = { .start = uhid_hid_start, .stop = uhid_hid_stop, @@ -257,6 +272,7 @@ static struct hid_ll_driver uhid_hid_driver = { .close = uhid_hid_close, .parse = uhid_hid_parse, .output_report = uhid_hid_output_report, + .raw_request = uhid_raw_request, }; #ifdef CONFIG_COMPAT From 4fa5a7f76cc7b6ac87f57741edd2b124851d119f Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 10 Feb 2014 12:58:48 -0500 Subject: [PATCH 18/25] HID: core: implement generic .request() .request() can be emulated through .raw_request() we can implement this emulation in hid-core, and make .request not mandatory for transport layer drivers. Signed-off-by: Benjamin Tissoires Reviewed-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/hid-core.c | 45 +++++++++++++++++++++++++++++++++++++++++- include/linux/hid.h | 5 ++++- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 026ab0fc06f7..b6ae69711d2a 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1248,6 +1248,11 @@ void hid_output_report(struct hid_report *report, __u8 *data) } EXPORT_SYMBOL_GPL(hid_output_report); +static int hid_report_len(struct hid_report *report) +{ + return ((report->size - 1) >> 3) + 1 + (report->id > 0) + 7; +} + /* * Allocator for buffer that is going to be passed to hid_output_report() */ @@ -1258,7 +1263,7 @@ u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags) * of implement() working on 8 byte chunks */ - int len = ((report->size - 1) >> 3) + 1 + (report->id > 0) + 7; + int len = hid_report_len(report); return kmalloc(len, flags); } @@ -1314,6 +1319,44 @@ static struct hid_report *hid_get_report(struct hid_report_enum *report_enum, return report; } +/* + * Implement a generic .request() callback, using .raw_request() + * DO NOT USE in hid drivers directly, but through hid_hw_request instead. + */ +void __hid_request(struct hid_device *hid, struct hid_report *report, + int reqtype) +{ + char *buf; + int ret; + int len; + + if (!hid->ll_driver->raw_request) + return; + + buf = hid_alloc_report_buf(report, GFP_KERNEL); + if (!buf) + return; + + len = hid_report_len(report); + + if (reqtype == HID_REQ_SET_REPORT) + hid_output_report(report, buf); + + ret = hid->ll_driver->raw_request(hid, report->id, buf, len, + report->type, reqtype); + if (ret < 0) { + dbg_hid("unable to complete request: %d\n", ret); + goto out; + } + + if (reqtype == HID_REQ_GET_REPORT) + hid_input_report(hid, report->type, buf, ret, 0); + +out: + kfree(buf); +} +EXPORT_SYMBOL_GPL(__hid_request); + int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, int size, int interrupt) { diff --git a/include/linux/hid.h b/include/linux/hid.h index a837ede65ec6..09fbbd7fb784 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -753,6 +753,7 @@ struct hid_field *hidinput_get_led_field(struct hid_device *hid); unsigned int hidinput_count_leds(struct hid_device *hid); __s32 hidinput_calc_abs_res(const struct hid_field *field, __u16 code); void hid_output_report(struct hid_report *report, __u8 *data); +void __hid_request(struct hid_device *hid, struct hid_report *rep, int reqtype); u8 *hid_alloc_report_buf(struct hid_report *report, gfp_t flags); struct hid_device *hid_allocate_device(void); struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id); @@ -965,7 +966,9 @@ static inline void hid_hw_request(struct hid_device *hdev, struct hid_report *report, int reqtype) { if (hdev->ll_driver->request) - hdev->ll_driver->request(hdev, report, reqtype); + return hdev->ll_driver->request(hdev, report, reqtype); + + __hid_request(hdev, report, reqtype); } /** From 9b5a9ae88573884224a26fda0e3eb6a6ec48686d Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 10 Feb 2014 12:58:49 -0500 Subject: [PATCH 19/25] HID: i2c-hid: implement ll_driver transport-layer callbacks Add output_report and raw_request to i2c-hid. The current implementation of i2c_hid_output_raw_report decides by itself if it should use a direct send of the output report or use the data register (SET_REPORT). Split that by reimplement the logic in __i2c_hid_output_raw_report() which will be dropped soon. Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 69 ++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 9 deletions(-) diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index f4ea7343e823..b48f49d1f0dd 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -256,12 +256,21 @@ static int i2c_hid_get_report(struct i2c_client *client, u8 reportType, return 0; } -static int i2c_hid_set_report(struct i2c_client *client, u8 reportType, - u8 reportID, unsigned char *buf, size_t data_len) +/** + * i2c_hid_set_or_send_report: forward an incoming report to the device + * @client: the i2c_client of the device + * @reportType: 0x03 for HID_FEATURE_REPORT ; 0x02 for HID_OUTPUT_REPORT + * @reportID: the report ID + * @buf: the actual data to transfer, without the report ID + * @len: size of buf + * @use_data: true: use SET_REPORT HID command, false: send plain OUTPUT report + */ +static int i2c_hid_set_or_send_report(struct i2c_client *client, u8 reportType, + u8 reportID, unsigned char *buf, size_t data_len, bool use_data) { struct i2c_hid *ihid = i2c_get_clientdata(client); u8 *args = ihid->argsbuf; - const struct i2c_hid_cmd * hidcmd = &hid_set_report_cmd; + const struct i2c_hid_cmd *hidcmd; int ret; u16 dataRegister = le16_to_cpu(ihid->hdesc.wDataRegister); u16 outputRegister = le16_to_cpu(ihid->hdesc.wOutputRegister); @@ -278,6 +287,9 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType, i2c_hid_dbg(ihid, "%s\n", __func__); + if (!use_data && maxOutputLength == 0) + return -ENOSYS; + if (reportID >= 0x0F) { args[index++] = reportID; reportID = 0x0F; @@ -287,9 +299,10 @@ static int i2c_hid_set_report(struct i2c_client *client, u8 reportType, * use the data register for feature reports or if the device does not * support the output register */ - if (reportType == 0x03 || maxOutputLength == 0) { + if (use_data) { args[index++] = dataRegister & 0xFF; args[index++] = dataRegister >> 8; + hidcmd = &hid_set_report_cmd; } else { args[index++] = outputRegister & 0xFF; args[index++] = outputRegister >> 8; @@ -550,7 +563,7 @@ static int i2c_hid_get_raw_report(struct hid_device *hid, } static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf, - size_t count, unsigned char report_type) + size_t count, unsigned char report_type, bool use_data) { struct i2c_client *client = hid->driver_data; int report_id = buf[0]; @@ -564,9 +577,9 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf, count--; } - ret = i2c_hid_set_report(client, + ret = i2c_hid_set_or_send_report(client, report_type == HID_FEATURE_REPORT ? 0x03 : 0x02, - report_id, buf, count); + report_id, buf, count, use_data); if (report_id && ret >= 0) ret++; /* add report_id to the number of transfered bytes */ @@ -574,6 +587,42 @@ static int i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf, return ret; } +static int __i2c_hid_output_raw_report(struct hid_device *hid, __u8 *buf, + size_t count, unsigned char report_type) +{ + struct i2c_client *client = hid->driver_data; + struct i2c_hid *ihid = i2c_get_clientdata(client); + bool data = true; /* SET_REPORT */ + + if (report_type == HID_OUTPUT_REPORT) + data = le16_to_cpu(ihid->hdesc.wMaxOutputLength) == 0; + + return i2c_hid_output_raw_report(hid, buf, count, report_type, data); +} + +static int i2c_hid_output_report(struct hid_device *hid, __u8 *buf, + size_t count) +{ + return i2c_hid_output_raw_report(hid, buf, count, HID_OUTPUT_REPORT, + false); +} + +static int i2c_hid_raw_request(struct hid_device *hid, unsigned char reportnum, + __u8 *buf, size_t len, unsigned char rtype, + int reqtype) +{ + switch (reqtype) { + case HID_REQ_GET_REPORT: + return i2c_hid_get_raw_report(hid, reportnum, buf, len, rtype); + case HID_REQ_SET_REPORT: + if (buf[0] != reportnum) + return -EINVAL; + return i2c_hid_output_raw_report(hid, buf, len, rtype, true); + default: + return -EIO; + } +} + static void i2c_hid_request(struct hid_device *hid, struct hid_report *rep, int reqtype) { @@ -597,7 +646,7 @@ static void i2c_hid_request(struct hid_device *hid, struct hid_report *rep, break; case HID_REQ_SET_REPORT: hid_output_report(rep, buf); - i2c_hid_output_raw_report(hid, buf, len, rep->type); + i2c_hid_output_raw_report(hid, buf, len, rep->type, true); break; } @@ -761,6 +810,8 @@ static struct hid_ll_driver i2c_hid_ll_driver = { .close = i2c_hid_close, .power = i2c_hid_power, .request = i2c_hid_request, + .output_report = i2c_hid_output_report, + .raw_request = i2c_hid_raw_request, }; static int i2c_hid_init_irq(struct i2c_client *client) @@ -1005,7 +1056,7 @@ static int i2c_hid_probe(struct i2c_client *client, hid->driver_data = client; hid->ll_driver = &i2c_hid_ll_driver; - hid->hid_output_raw_report = i2c_hid_output_raw_report; + hid->hid_output_raw_report = __i2c_hid_output_raw_report; hid->dev.parent = &client->dev; ACPI_COMPANION_SET(&hid->dev, ACPI_COMPANION(&client->dev)); hid->bus = BUS_I2C; From 649f94790314b502bc4e905e005d89c3b693d60a Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 10 Feb 2014 12:58:50 -0500 Subject: [PATCH 20/25] HID: i2c-hid: use generic .request() implementation Having our own .request() implementation does not give anything, so use the generic binding. Signed-off-by: Benjamin Tissoires Reviewed-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index b48f49d1f0dd..5308656eec2e 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -623,36 +623,6 @@ static int i2c_hid_raw_request(struct hid_device *hid, unsigned char reportnum, } } -static void i2c_hid_request(struct hid_device *hid, struct hid_report *rep, - int reqtype) -{ - struct i2c_client *client = hid->driver_data; - char *buf; - int ret; - int len = i2c_hid_get_report_length(rep) - 2; - - buf = kzalloc(len, GFP_KERNEL); - if (!buf) - return; - - switch (reqtype) { - case HID_REQ_GET_REPORT: - ret = i2c_hid_get_raw_report(hid, rep->id, buf, len, rep->type); - if (ret < 0) - dev_err(&client->dev, "%s: unable to get report: %d\n", - __func__, ret); - else - hid_input_report(hid, rep->type, buf, ret, 0); - break; - case HID_REQ_SET_REPORT: - hid_output_report(rep, buf); - i2c_hid_output_raw_report(hid, buf, len, rep->type, true); - break; - } - - kfree(buf); -} - static int i2c_hid_parse(struct hid_device *hid) { struct i2c_client *client = hid->driver_data; @@ -809,7 +779,6 @@ static struct hid_ll_driver i2c_hid_ll_driver = { .open = i2c_hid_open, .close = i2c_hid_close, .power = i2c_hid_power, - .request = i2c_hid_request, .output_report = i2c_hid_output_report, .raw_request = i2c_hid_raw_request, }; From ddea1af9d9387665ee194ae1d98ec0d49c169313 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 10 Feb 2014 12:58:51 -0500 Subject: [PATCH 21/25] HID: usbhid: change return error of usbhid_output_report If there is no urbout when sending a output report, ENOSYS (Function not implemented) is a better error than EIO (I/O error). Signed-off-by: Benjamin Tissoires Reviewed-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/usbhid/hid-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index b9a770f4d7ae..0d1d87533f48 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -922,7 +922,7 @@ static int usbhid_output_report(struct hid_device *hid, __u8 *buf, size_t count) int actual_length, skipped_report_id = 0, ret; if (!usbhid->urbout) - return -EIO; + return -ENOSYS; if (buf[0] == 0x0) { /* Don't send the Report ID */ From bd27e202c640d19fbf948a56914c230dcbaee6d0 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 10 Feb 2014 12:58:53 -0500 Subject: [PATCH 22/25] HID: logitech-dj: remove hid_output_raw_report call hid-input do not use anymore hid_output_raw_report() to set the LEDs. Use the correct implementation now and make them working again. Signed-off-by: Benjamin Tissoires Reviewed-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/hid-logitech-dj.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 980ede54782d..486dbde2ba2d 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -193,9 +193,6 @@ static const u8 hid_reportid_size_map[NUMBER_OF_HID_REPORTS] = { static struct hid_ll_driver logi_dj_ll_driver; -static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf, - size_t count, - unsigned char report_type); static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev); static void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev, @@ -262,7 +259,6 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev, } dj_hiddev->ll_driver = &logi_dj_ll_driver; - dj_hiddev->hid_output_raw_report = logi_dj_output_hidraw_report; dj_hiddev->dev.parent = &djrcv_hdev->dev; dj_hiddev->bus = BUS_USB; @@ -544,9 +540,10 @@ static void logi_dj_ll_close(struct hid_device *hid) dbg_hid("%s:%s\n", __func__, hid->phys); } -static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf, - size_t count, - unsigned char report_type) +static int logi_dj_ll_raw_request(struct hid_device *hid, + unsigned char reportnum, __u8 *buf, + size_t count, unsigned char report_type, + int reqtype) { struct dj_device *djdev = hid->driver_data; struct dj_receiver_dev *djrcv_dev = djdev->dj_receiver_dev; @@ -567,15 +564,8 @@ static int logi_dj_output_hidraw_report(struct hid_device *hid, u8 * buf, out_buf[1] = djdev->device_index; memcpy(out_buf + 2, buf, count); - /* - * hid-generic calls us with hid_output_raw_report(), but the LEDs - * are set through a SET_REPORT command. It works for USB-HID devices - * because usbhid either calls a SET_REPORT or directly send the output - * report depending if the device presents an urbout. - * Let be simple, send a SET_REPORT request. - */ ret = hid_hw_raw_request(djrcv_dev->hdev, out_buf[0], out_buf, - DJREPORT_SHORT_LENGTH, report_type, HID_REQ_SET_REPORT); + DJREPORT_SHORT_LENGTH, report_type, reqtype); kfree(out_buf); return ret; @@ -662,6 +652,7 @@ static struct hid_ll_driver logi_dj_ll_driver = { .stop = logi_dj_ll_stop, .open = logi_dj_ll_open, .close = logi_dj_ll_close, + .raw_request = logi_dj_ll_raw_request, }; From b0dd72aafd785785bedbb6db932955807e454a65 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 10 Feb 2014 12:58:54 -0500 Subject: [PATCH 23/25] HID: replace hid_output_raw_report with hid_hw_raw_request for feature requests ret = hid_output_raw_report(A, B, C, HID_FEATURE_REPORT); is equivalent to ret = hid_hw_raw_request(A, B[0], B, C, HID_FEATURE_REPORT, HID_REQ_SET_REPORT); whatever the transport layer is. So use the new API where available Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-lg.c | 8 ++++---- drivers/hid/hid-magicmouse.c | 4 ++-- drivers/hid/hid-sony.c | 4 ++-- drivers/hid/hid-thingm.c | 4 ++-- drivers/hid/hid-wacom.c | 26 +++++++++++++++----------- 5 files changed, 25 insertions(+), 21 deletions(-) diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index 76ed7e512dcf..a976f48263f6 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -692,8 +692,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) { unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - ret = hid_output_raw_report(hdev, buf, sizeof(buf), - HID_FEATURE_REPORT); + ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf), + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); if (ret >= 0) { /* insert a little delay of 10 jiffies ~ 40ms */ @@ -705,8 +705,8 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id) buf[1] = 0xB2; get_random_bytes(&buf[2], 2); - ret = hid_output_raw_report(hdev, buf, sizeof(buf), - HID_FEATURE_REPORT); + ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf), + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); } } diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index cb5db3afc690..ecc2cbf300cc 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -538,8 +538,8 @@ static int magicmouse_probe(struct hid_device *hdev, * but there seems to be no other way of switching the mode. * Thus the super-ugly hacky success check below. */ - ret = hid_output_raw_report(hdev, feature, sizeof(feature), - HID_FEATURE_REPORT); + ret = hid_hw_raw_request(hdev, feature[0], feature, sizeof(feature), + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); if (ret != -EIO && ret != sizeof(feature)) { hid_err(hdev, "unable to request touch data (%d)\n", ret); goto err_stop_hw; diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 075089b37236..e3e89b6a41c2 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -720,8 +720,8 @@ static int sixaxis_set_operational_usb(struct hid_device *hdev) static int sixaxis_set_operational_bt(struct hid_device *hdev) { unsigned char buf[] = { 0xf4, 0x42, 0x03, 0x00, 0x00 }; - return hid_output_raw_report(hdev, buf, sizeof(buf), - HID_FEATURE_REPORT); + return hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf), + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); } static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds) diff --git a/drivers/hid/hid-thingm.c b/drivers/hid/hid-thingm.c index 7dd3197f3b3e..a97c78845f7b 100644 --- a/drivers/hid/hid-thingm.c +++ b/drivers/hid/hid-thingm.c @@ -48,8 +48,8 @@ static int blink1_send_command(struct blink1_data *data, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8]); - ret = hid_output_raw_report(data->hdev, buf, BLINK1_CMD_SIZE, - HID_FEATURE_REPORT); + ret = hid_hw_raw_request(data->hdev, buf[0], buf, BLINK1_CMD_SIZE, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); return ret < 0 ? ret : 0; } diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c index c720db912edb..902013ec041b 100644 --- a/drivers/hid/hid-wacom.c +++ b/drivers/hid/hid-wacom.c @@ -128,7 +128,8 @@ static void wacom_set_image(struct hid_device *hdev, const char *image, rep_data[0] = WAC_CMD_ICON_START_STOP; rep_data[1] = 0; - ret = hid_output_raw_report(hdev, rep_data, 2, HID_FEATURE_REPORT); + ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); if (ret < 0) goto err; @@ -142,14 +143,15 @@ static void wacom_set_image(struct hid_device *hdev, const char *image, rep_data[j + 3] = p[(i << 6) + j]; rep_data[2] = i; - ret = hid_output_raw_report(hdev, rep_data, 67, - HID_FEATURE_REPORT); + ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 67, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); } rep_data[0] = WAC_CMD_ICON_START_STOP; rep_data[1] = 0; - ret = hid_output_raw_report(hdev, rep_data, 2, HID_FEATURE_REPORT); + ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); err: return; @@ -181,7 +183,8 @@ static void wacom_leds_set_brightness(struct led_classdev *led_dev, buf[3] = value; /* use fixed brightness for OLEDs */ buf[4] = 0x08; - hid_output_raw_report(hdev, buf, 9, HID_FEATURE_REPORT); + hid_hw_raw_request(hdev, buf[0], buf, 9, HID_FEATURE_REPORT, + HID_REQ_SET_REPORT); kfree(buf); } @@ -337,8 +340,8 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed) rep_data[0] = 0x03 ; rep_data[1] = 0x00; limit = 3; do { - ret = hid_output_raw_report(hdev, rep_data, 2, - HID_FEATURE_REPORT); + ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); } while (ret < 0 && limit-- > 0); if (ret >= 0) { @@ -350,8 +353,9 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed) rep_data[1] = 0x00; limit = 3; do { - ret = hid_output_raw_report(hdev, - rep_data, 2, HID_FEATURE_REPORT); + ret = hid_hw_raw_request(hdev, rep_data[0], + rep_data, 2, HID_FEATURE_REPORT, + HID_REQ_SET_REPORT); } while (ret < 0 && limit-- > 0); if (ret >= 0) { @@ -376,8 +380,8 @@ static void wacom_set_features(struct hid_device *hdev, u8 speed) rep_data[0] = 0x03; rep_data[1] = wdata->features; - ret = hid_output_raw_report(hdev, rep_data, 2, - HID_FEATURE_REPORT); + ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2, + HID_FEATURE_REPORT, HID_REQ_SET_REPORT); if (ret >= 0) wdata->high_speed = speed; break; From 7e0bc880fce51ba3bd9128b8df9dc59567c5f73a Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 10 Feb 2014 12:58:55 -0500 Subject: [PATCH 24/25] HID: wiimote: replace hid_output_raw_report with hid_hw_output_report for output requests For BT transport layer, ret = hid_output_raw_report(A, B, C, HID_OUTPUT_REPORT); is equivalent to ret = hid_hw_output_report(A, B, C); So use the new API where available Signed-off-by: Benjamin Tissoires Reviewed-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/hid-wiimote-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-wiimote-core.c b/drivers/hid/hid-wiimote-core.c index d7dc6c5bc244..d00391418d1a 100644 --- a/drivers/hid/hid-wiimote-core.c +++ b/drivers/hid/hid-wiimote-core.c @@ -28,14 +28,14 @@ static int wiimote_hid_send(struct hid_device *hdev, __u8 *buffer, __u8 *buf; int ret; - if (!hdev->hid_output_raw_report) + if (!hdev->ll_driver->output_report) return -ENODEV; buf = kmemdup(buffer, count, GFP_KERNEL); if (!buf) return -ENOMEM; - ret = hid_output_raw_report(hdev, buf, count, HID_OUTPUT_REPORT); + ret = hid_hw_output_report(hdev, buf, count); kfree(buf); return ret; From 5318251744b2c8a288f91f4e53ed69f2a01d6412 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 10 Feb 2014 12:58:59 -0500 Subject: [PATCH 25/25] HID: core: check parameters when sending/receiving data from the device It is better to check them soon enough before triggering any kernel panic. Signed-off-by: Benjamin Tissoires Reviewed-by: David Herrmann Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 2 +- include/linux/hid.h | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 5308656eec2e..1a955317d05f 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -276,7 +276,7 @@ static int i2c_hid_set_or_send_report(struct i2c_client *client, u8 reportType, u16 outputRegister = le16_to_cpu(ihid->hdesc.wOutputRegister); u16 maxOutputLength = le16_to_cpu(ihid->hdesc.wMaxOutputLength); - /* hidraw already checked that data_len < HID_MAX_BUFFER_SIZE */ + /* hid_hw_* already checked that data_len < HID_MAX_BUFFER_SIZE */ u16 size = 2 /* size */ + (reportID ? 1 : 0) /* reportID */ + data_len /* buf */; diff --git a/include/linux/hid.h b/include/linux/hid.h index 09fbbd7fb784..60f3ff762376 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -989,6 +989,9 @@ static inline int hid_hw_raw_request(struct hid_device *hdev, unsigned char reportnum, __u8 *buf, size_t len, unsigned char rtype, int reqtype) { + if (len < 1 || len > HID_MAX_BUFFER_SIZE || !buf) + return -EINVAL; + if (hdev->ll_driver->raw_request) return hdev->ll_driver->raw_request(hdev, reportnum, buf, len, rtype, reqtype); @@ -1008,6 +1011,9 @@ static inline int hid_hw_raw_request(struct hid_device *hdev, static inline int hid_hw_output_report(struct hid_device *hdev, __u8 *buf, size_t len) { + if (len < 1 || len > HID_MAX_BUFFER_SIZE || !buf) + return -EINVAL; + if (hdev->ll_driver->output_report) return hdev->ll_driver->output_report(hdev, buf, len);