From 4fa5a7f76cc7b6ac87f57741edd2b124851d119f Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 10 Feb 2014 12:58:48 -0500 Subject: 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 --- include/linux/hid.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'include') 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); } /** -- cgit v1.2.3 From 5318251744b2c8a288f91f4e53ed69f2a01d6412 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Mon, 10 Feb 2014 12:58:59 -0500 Subject: 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(-) (limited to 'include') 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); -- cgit v1.2.3 From 3c86726cfe38952f0366f86acfbbb025813ec1c2 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 20 Feb 2014 15:24:49 -0500 Subject: HID: make .raw_request mandatory SET_REPORT and GET_REPORT are mandatory in the HID specification. Make the corresponding API in hid-core mandatory too, which removes the need to test against it in some various places. Signed-off-by: Benjamin Tissoires Reviewed-by: David Herrmann Signed-off-by: Jiri Kosina --- Documentation/hid/hid-transport.txt | 3 ++- drivers/hid/hid-core.c | 11 ++++++++--- drivers/hid/hid-input.c | 4 +--- include/linux/hid.h | 5 +---- 4 files changed, 12 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/Documentation/hid/hid-transport.txt b/Documentation/hid/hid-transport.txt index 9dbbceaef4f3..3dcba9fd4a3a 100644 --- a/Documentation/hid/hid-transport.txt +++ b/Documentation/hid/hid-transport.txt @@ -283,7 +283,8 @@ The available HID callbacks are: 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. + requests. This request is mandatory and hid core will reject the device if + it is missing. - 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 diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index b6ae69711d2a..0b57babe3f9f 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1330,9 +1330,6 @@ void __hid_request(struct hid_device *hid, struct hid_report *report, int ret; int len; - if (!hid->ll_driver->raw_request) - return; - buf = hid_alloc_report_buf(report, GFP_KERNEL); if (!buf) return; @@ -2471,6 +2468,14 @@ int hid_add_device(struct hid_device *hdev) if (hid_ignore(hdev)) return -ENODEV; + /* + * Check for the mandatory transport channel. + */ + if (!hdev->ll_driver->raw_request) { + hid_err(hdev, "transport driver missing .raw_request()\n"); + return -EINVAL; + } + /* * Read the device report descriptor once and use as template * for the driver-specific modifications. diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 310b96779e8e..f5aef792f13b 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1266,9 +1266,7 @@ static struct hid_input *hidinput_allocate(struct hid_device *hid) } input_set_drvdata(input_dev, hid); - if (hid->ll_driver->request || hid->ll_driver->output_report || - hid->ll_driver->raw_request) - input_dev->event = hidinput_input_event; + input_dev->event = hidinput_input_event; input_dev->open = hidinput_open; input_dev->close = hidinput_close; input_dev->setkeycode = hidinput_setkeycode; diff --git a/include/linux/hid.h b/include/linux/hid.h index 60f3ff762376..5eb282e0dff7 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -992,11 +992,8 @@ static inline int hid_hw_raw_request(struct hid_device *hdev, 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, + return hdev->ll_driver->raw_request(hdev, reportnum, buf, len, rtype, reqtype); - - return -ENOSYS; } /** -- cgit v1.2.3 From e534a9352237e84263cecedff283387b144b3ed8 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Sat, 8 Mar 2014 22:52:42 -0500 Subject: HID: sony: do not rely on hid_output_raw_report hid_out_raw_report is going to be obsoleted as it is not part of the unified HID low level transport documentation (Documentation/hid/hid-transport.txt) To do so, we need to introduce two new quirks: * HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP: this quirks prevents the transport driver to use the interrupt channel to send output report (and thus force to use HID_REQ_SET_REPORT command) * HID_QUIRK_SKIP_OUTPUT_REPORT_ID: this one forces usbhid to not include the report ID in the buffer it sends to the device through HID_REQ_SET_REPORT in case of an output report This also fixes a regression introduced in commit 3a75b24949a8 (HID: hidraw: replace hid_output_raw_report() calls by appropriates ones). The hidraw API was not able to communicate with the PS3 SixAxis controllers in USB mode. Reviewed-by: David Herrmann Signed-off-by: Benjamin Tissoires Tested-by: Antonio Ospite Signed-off-by: Jiri Kosina --- drivers/hid/hid-sony.c | 60 ++++++++++--------------------------------- drivers/hid/hidraw.c | 3 ++- drivers/hid/usbhid/hid-core.c | 7 ++++- include/linux/hid.h | 2 ++ 4 files changed, 24 insertions(+), 48 deletions(-) (limited to 'include') diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index b5fe65e70dc4..4884bb567bf8 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -1006,45 +1005,6 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi, return 0; } -/* - * The Sony Sixaxis does not handle HID Output Reports on the Interrupt EP - * like it should according to usbhid/hid-core.c::usbhid_output_raw_report() - * so we need to override that forcing HID Output Reports on the Control EP. - * - * There is also another issue about HID Output Reports via USB, the Sixaxis - * does not want the report_id as part of the data packet, so we have to - * discard buf[0] when sending the actual control message, even for numbered - * reports, humpf! - */ -static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf, - size_t count, unsigned char report_type) -{ - struct usb_interface *intf = to_usb_interface(hid->dev.parent); - struct usb_device *dev = interface_to_usbdev(intf); - struct usb_host_interface *interface = intf->cur_altsetting; - int report_id = buf[0]; - int ret; - - if (report_type == HID_OUTPUT_REPORT) { - /* Don't send the Report ID */ - buf++; - count--; - } - - 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, in case of an Output report. */ - if (ret > 0 && report_type == HID_OUTPUT_REPORT) - ret++; - - return ret; -} - /* * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller * to "operational". Without this, the ps3 controller will not report any @@ -1305,11 +1265,8 @@ static void sixaxis_state_worker(struct work_struct *work) buf[10] |= sc->led_state[2] << 3; buf[10] |= sc->led_state[3] << 4; - if (sc->quirks & SIXAXIS_CONTROLLER_USB) - hid_output_raw_report(sc->hdev, buf, sizeof(buf), HID_OUTPUT_REPORT); - else - hid_hw_raw_request(sc->hdev, 0x01, buf, sizeof(buf), - HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); + hid_hw_raw_request(sc->hdev, 0x01, buf, sizeof(buf), HID_OUTPUT_REPORT, + HID_REQ_SET_REPORT); } static void dualshock4_state_worker(struct work_struct *work) @@ -1659,7 +1616,18 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) } if (sc->quirks & SIXAXIS_CONTROLLER_USB) { - hdev->hid_output_raw_report = sixaxis_usb_output_raw_report; + /* + * The Sony Sixaxis does not handle HID Output Reports on the + * Interrupt EP like it could, so we need to force HID Output + * Reports to use HID_REQ_SET_REPORT on the Control EP. + * + * There is also another issue about HID Output Reports via USB, + * the Sixaxis does not want the report_id as part of the data + * packet, so we have to discard buf[0] when sending the actual + * control message, even for numbered reports, humpf! + */ + hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP; + hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID; ret = sixaxis_set_operational_usb(hdev); sc->worker_initialized = 1; INIT_WORK(&sc->state_worker, sixaxis_state_worker); diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 2cc484c0017b..ffa648ce002e 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -149,7 +149,8 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer, goto out_free; } - if (report_type == HID_OUTPUT_REPORT) { + if ((report_type == HID_OUTPUT_REPORT) && + !(dev->quirks & HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP)) { ret = hid_hw_output_report(dev, buf, count); /* * compatibility with old implementation of USB-HID and I2C-HID: diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 0d1d87533f48..3bc7cad48fe0 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -894,7 +894,12 @@ static int usbhid_set_raw_report(struct hid_device *hid, unsigned int reportnum, int ret, skipped_report_id = 0; /* Byte 0 is the report number. Report data starts at byte 1.*/ - buf[0] = reportnum; + if ((rtype == HID_OUTPUT_REPORT) && + (hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORT_ID)) + buf[0] = 0; + else + buf[0] = reportnum; + if (buf[0] == 0x0) { /* Don't send the Report ID */ buf++; diff --git a/include/linux/hid.h b/include/linux/hid.h index 5eb282e0dff7..3fe444f4a36f 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -287,6 +287,8 @@ struct hid_item { #define HID_QUIRK_NO_EMPTY_INPUT 0x00000100 #define HID_QUIRK_NO_INIT_INPUT_REPORTS 0x00000200 #define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000 +#define HID_QUIRK_SKIP_OUTPUT_REPORT_ID 0x00020000 +#define HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP 0x00040000 #define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000 #define HID_QUIRK_NO_INIT_REPORTS 0x20000000 #define HID_QUIRK_NO_IGNORE 0x40000000 -- cgit v1.2.3 From 6fd182028c43baf1c7d017d52b0134ecadbdc447 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Sat, 8 Mar 2014 22:52:43 -0500 Subject: HID: remove hid_output_raw_report transport implementations Nobody calls hid_output_raw_report anymore, and nobody should. We can now remove the various implementation in the different transport drivers and the declarations. Reviewed-by: David Herrmann Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 14 -------------- drivers/hid/uhid.c | 1 - drivers/hid/usbhid/hid-core.c | 12 ------------ include/linux/hid.h | 19 ------------------- net/bluetooth/hidp/core.c | 14 -------------- 5 files changed, 60 deletions(-) (limited to 'include') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 1a955317d05f..2de2b8e22462 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -587,19 +587,6 @@ 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) { @@ -1025,7 +1012,6 @@ 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->dev.parent = &client->dev; ACPI_COMPANION_SET(&hid->dev, ACPI_COMPANION(&client->dev)); hid->bus = BUS_I2C; diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 60acee422fdc..7ed79be2686a 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -400,7 +400,6 @@ static int uhid_dev_create(struct uhid_device *uhid, hid->uniq[63] = 0; hid->ll_driver = &uhid_hid_driver; - hid->hid_output_raw_report = uhid_hid_output_raw; hid->bus = ev->u.create.bus; hid->vendor = ev->u.create.vendor; hid->product = ev->u.create.product; diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 3bc7cad48fe0..7b88f4cb9902 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -950,17 +950,6 @@ 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)) @@ -1294,7 +1283,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_output_raw_report = usbhid_output_raw_report; hid->ff_init = hid_pidff_init; #ifdef CONFIG_USB_HIDDEV hid->hiddev_connect = hiddev_connect; diff --git a/include/linux/hid.h b/include/linux/hid.h index 3fe444f4a36f..01a90b8d53bb 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -510,9 +510,6 @@ struct hid_device { /* device report descriptor */ struct hid_usage *, __s32); void (*hiddev_report_event) (struct hid_device *, struct hid_report *); - /* handler for raw output data, used by hidraw */ - int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t, unsigned char); - /* debugging support via debugfs */ unsigned short debug; struct dentry *debug_dir; @@ -1019,22 +1016,6 @@ 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 * diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 98e4840935e2..514ddb5aef96 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -382,18 +382,6 @@ static int hidp_output_report(struct hid_device *hid, __u8 *data, size_t count) 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) { - return hidp_output_report(hid, data, count); - } else if (report_type != HID_FEATURE_REPORT) { - return -EINVAL; - } - - return hidp_set_raw_report(hid, data[0], data, count, report_type); -} - static int hidp_raw_request(struct hid_device *hid, unsigned char reportnum, __u8 *buf, size_t len, unsigned char rtype, int reqtype) @@ -776,8 +764,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_output_raw_report = hidp_output_raw_report; - /* True if device is blacklisted in drivers/hid/hid-core.c */ if (hid_ignore(hid)) { hid_destroy_device(session->hid); -- cgit v1.2.3