Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <dh.herrmann@gmail.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Tested-by: Antonio Ospite <ao2@ao2.it>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>

authored by

Benjamin Tissoires and committed by
Jiri Kosina
e534a935 65ab2fc4

+24 -48
+14 -46
drivers/hid/hid-sony.c
··· 29 29 #include <linux/hid.h> 30 30 #include <linux/module.h> 31 31 #include <linux/slab.h> 32 - #include <linux/usb.h> 33 32 #include <linux/leds.h> 34 33 #include <linux/power_supply.h> 35 34 #include <linux/spinlock.h> ··· 1006 1007 } 1007 1008 1008 1009 /* 1009 - * The Sony Sixaxis does not handle HID Output Reports on the Interrupt EP 1010 - * like it should according to usbhid/hid-core.c::usbhid_output_raw_report() 1011 - * so we need to override that forcing HID Output Reports on the Control EP. 1012 - * 1013 - * There is also another issue about HID Output Reports via USB, the Sixaxis 1014 - * does not want the report_id as part of the data packet, so we have to 1015 - * discard buf[0] when sending the actual control message, even for numbered 1016 - * reports, humpf! 1017 - */ 1018 - static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf, 1019 - size_t count, unsigned char report_type) 1020 - { 1021 - struct usb_interface *intf = to_usb_interface(hid->dev.parent); 1022 - struct usb_device *dev = interface_to_usbdev(intf); 1023 - struct usb_host_interface *interface = intf->cur_altsetting; 1024 - int report_id = buf[0]; 1025 - int ret; 1026 - 1027 - if (report_type == HID_OUTPUT_REPORT) { 1028 - /* Don't send the Report ID */ 1029 - buf++; 1030 - count--; 1031 - } 1032 - 1033 - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), 1034 - HID_REQ_SET_REPORT, 1035 - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 1036 - ((report_type + 1) << 8) | report_id, 1037 - interface->desc.bInterfaceNumber, buf, count, 1038 - USB_CTRL_SET_TIMEOUT); 1039 - 1040 - /* Count also the Report ID, in case of an Output report. */ 1041 - if (ret > 0 && report_type == HID_OUTPUT_REPORT) 1042 - ret++; 1043 - 1044 - return ret; 1045 - } 1046 - 1047 - /* 1048 1010 * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller 1049 1011 * to "operational". Without this, the ps3 controller will not report any 1050 1012 * events. ··· 1265 1305 buf[10] |= sc->led_state[2] << 3; 1266 1306 buf[10] |= sc->led_state[3] << 4; 1267 1307 1268 - if (sc->quirks & SIXAXIS_CONTROLLER_USB) 1269 - hid_output_raw_report(sc->hdev, buf, sizeof(buf), HID_OUTPUT_REPORT); 1270 - else 1271 - hid_hw_raw_request(sc->hdev, 0x01, buf, sizeof(buf), 1272 - HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); 1308 + hid_hw_raw_request(sc->hdev, 0x01, buf, sizeof(buf), HID_OUTPUT_REPORT, 1309 + HID_REQ_SET_REPORT); 1273 1310 } 1274 1311 1275 1312 static void dualshock4_state_worker(struct work_struct *work) ··· 1616 1659 } 1617 1660 1618 1661 if (sc->quirks & SIXAXIS_CONTROLLER_USB) { 1619 - hdev->hid_output_raw_report = sixaxis_usb_output_raw_report; 1662 + /* 1663 + * The Sony Sixaxis does not handle HID Output Reports on the 1664 + * Interrupt EP like it could, so we need to force HID Output 1665 + * Reports to use HID_REQ_SET_REPORT on the Control EP. 1666 + * 1667 + * There is also another issue about HID Output Reports via USB, 1668 + * the Sixaxis does not want the report_id as part of the data 1669 + * packet, so we have to discard buf[0] when sending the actual 1670 + * control message, even for numbered reports, humpf! 1671 + */ 1672 + hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP; 1673 + hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID; 1620 1674 ret = sixaxis_set_operational_usb(hdev); 1621 1675 sc->worker_initialized = 1; 1622 1676 INIT_WORK(&sc->state_worker, sixaxis_state_worker);
+2 -1
drivers/hid/hidraw.c
··· 149 149 goto out_free; 150 150 } 151 151 152 - if (report_type == HID_OUTPUT_REPORT) { 152 + if ((report_type == HID_OUTPUT_REPORT) && 153 + !(dev->quirks & HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP)) { 153 154 ret = hid_hw_output_report(dev, buf, count); 154 155 /* 155 156 * compatibility with old implementation of USB-HID and I2C-HID:
+6 -1
drivers/hid/usbhid/hid-core.c
··· 894 894 int ret, skipped_report_id = 0; 895 895 896 896 /* Byte 0 is the report number. Report data starts at byte 1.*/ 897 - buf[0] = reportnum; 897 + if ((rtype == HID_OUTPUT_REPORT) && 898 + (hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORT_ID)) 899 + buf[0] = 0; 900 + else 901 + buf[0] = reportnum; 902 + 898 903 if (buf[0] == 0x0) { 899 904 /* Don't send the Report ID */ 900 905 buf++;
+2
include/linux/hid.h
··· 287 287 #define HID_QUIRK_NO_EMPTY_INPUT 0x00000100 288 288 #define HID_QUIRK_NO_INIT_INPUT_REPORTS 0x00000200 289 289 #define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000 290 + #define HID_QUIRK_SKIP_OUTPUT_REPORT_ID 0x00020000 291 + #define HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP 0x00040000 290 292 #define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000 291 293 #define HID_QUIRK_NO_INIT_REPORTS 0x20000000 292 294 #define HID_QUIRK_NO_IGNORE 0x40000000