diff options
author | Benjamin Tissoires <benjamin.tissoires@redhat.com> | 2014-03-08 22:52:42 -0500 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2014-03-14 10:30:20 -0400 |
commit | e534a9352237e84263cecedff283387b144b3ed8 (patch) | |
tree | 59ef11db183e22e6192c7a5ab9587a62c0ad7dd1 | |
parent | 65ab2fc479fa8554e26d6d1726a4ebe6f29a73be (diff) |
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>
-rw-r--r-- | drivers/hid/hid-sony.c | 60 | ||||
-rw-r--r-- | drivers/hid/hidraw.c | 3 | ||||
-rw-r--r-- | drivers/hid/usbhid/hid-core.c | 7 | ||||
-rw-r--r-- | include/linux/hid.h | 2 |
4 files changed, 24 insertions, 48 deletions
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 @@ | |||
29 | #include <linux/hid.h> | 29 | #include <linux/hid.h> |
30 | #include <linux/module.h> | 30 | #include <linux/module.h> |
31 | #include <linux/slab.h> | 31 | #include <linux/slab.h> |
32 | #include <linux/usb.h> | ||
33 | #include <linux/leds.h> | 32 | #include <linux/leds.h> |
34 | #include <linux/power_supply.h> | 33 | #include <linux/power_supply.h> |
35 | #include <linux/spinlock.h> | 34 | #include <linux/spinlock.h> |
@@ -1007,45 +1006,6 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi, | |||
1007 | } | 1006 | } |
1008 | 1007 | ||
1009 | /* | 1008 | /* |
1010 | * The Sony Sixaxis does not handle HID Output Reports on the Interrupt EP | ||
1011 | * like it should according to usbhid/hid-core.c::usbhid_output_raw_report() | ||
1012 | * so we need to override that forcing HID Output Reports on the Control EP. | ||
1013 | * | ||
1014 | * There is also another issue about HID Output Reports via USB, the Sixaxis | ||
1015 | * does not want the report_id as part of the data packet, so we have to | ||
1016 | * discard buf[0] when sending the actual control message, even for numbered | ||
1017 | * reports, humpf! | ||
1018 | */ | ||
1019 | static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf, | ||
1020 | size_t count, unsigned char report_type) | ||
1021 | { | ||
1022 | struct usb_interface *intf = to_usb_interface(hid->dev.parent); | ||
1023 | struct usb_device *dev = interface_to_usbdev(intf); | ||
1024 | struct usb_host_interface *interface = intf->cur_altsetting; | ||
1025 | int report_id = buf[0]; | ||
1026 | int ret; | ||
1027 | |||
1028 | if (report_type == HID_OUTPUT_REPORT) { | ||
1029 | /* Don't send the Report ID */ | ||
1030 | buf++; | ||
1031 | count--; | ||
1032 | } | ||
1033 | |||
1034 | ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), | ||
1035 | HID_REQ_SET_REPORT, | ||
1036 | USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, | ||
1037 | ((report_type + 1) << 8) | report_id, | ||
1038 | interface->desc.bInterfaceNumber, buf, count, | ||
1039 | USB_CTRL_SET_TIMEOUT); | ||
1040 | |||
1041 | /* Count also the Report ID, in case of an Output report. */ | ||
1042 | if (ret > 0 && report_type == HID_OUTPUT_REPORT) | ||
1043 | ret++; | ||
1044 | |||
1045 | return ret; | ||
1046 | } | ||
1047 | |||
1048 | /* | ||
1049 | * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller | 1009 | * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller |
1050 | * to "operational". Without this, the ps3 controller will not report any | 1010 | * to "operational". Without this, the ps3 controller will not report any |
1051 | * events. | 1011 | * events. |
@@ -1305,11 +1265,8 @@ static void sixaxis_state_worker(struct work_struct *work) | |||
1305 | buf[10] |= sc->led_state[2] << 3; | 1265 | buf[10] |= sc->led_state[2] << 3; |
1306 | buf[10] |= sc->led_state[3] << 4; | 1266 | buf[10] |= sc->led_state[3] << 4; |
1307 | 1267 | ||
1308 | if (sc->quirks & SIXAXIS_CONTROLLER_USB) | 1268 | hid_hw_raw_request(sc->hdev, 0x01, buf, sizeof(buf), HID_OUTPUT_REPORT, |
1309 | hid_output_raw_report(sc->hdev, buf, sizeof(buf), HID_OUTPUT_REPORT); | 1269 | HID_REQ_SET_REPORT); |
1310 | else | ||
1311 | hid_hw_raw_request(sc->hdev, 0x01, buf, sizeof(buf), | ||
1312 | HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); | ||
1313 | } | 1270 | } |
1314 | 1271 | ||
1315 | static void dualshock4_state_worker(struct work_struct *work) | 1272 | 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) | |||
1659 | } | 1616 | } |
1660 | 1617 | ||
1661 | if (sc->quirks & SIXAXIS_CONTROLLER_USB) { | 1618 | if (sc->quirks & SIXAXIS_CONTROLLER_USB) { |
1662 | hdev->hid_output_raw_report = sixaxis_usb_output_raw_report; | 1619 | /* |
1620 | * The Sony Sixaxis does not handle HID Output Reports on the | ||
1621 | * Interrupt EP like it could, so we need to force HID Output | ||
1622 | * Reports to use HID_REQ_SET_REPORT on the Control EP. | ||
1623 | * | ||
1624 | * There is also another issue about HID Output Reports via USB, | ||
1625 | * the Sixaxis does not want the report_id as part of the data | ||
1626 | * packet, so we have to discard buf[0] when sending the actual | ||
1627 | * control message, even for numbered reports, humpf! | ||
1628 | */ | ||
1629 | hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP; | ||
1630 | hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID; | ||
1663 | ret = sixaxis_set_operational_usb(hdev); | 1631 | ret = sixaxis_set_operational_usb(hdev); |
1664 | sc->worker_initialized = 1; | 1632 | sc->worker_initialized = 1; |
1665 | INIT_WORK(&sc->state_worker, sixaxis_state_worker); | 1633 | 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, | |||
149 | goto out_free; | 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 | ret = hid_hw_output_report(dev, buf, count); | 154 | ret = hid_hw_output_report(dev, buf, count); |
154 | /* | 155 | /* |
155 | * compatibility with old implementation of USB-HID and I2C-HID: | 156 | * 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, | |||
894 | int ret, skipped_report_id = 0; | 894 | int ret, skipped_report_id = 0; |
895 | 895 | ||
896 | /* Byte 0 is the report number. Report data starts at byte 1.*/ | 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 | if (buf[0] == 0x0) { | 903 | if (buf[0] == 0x0) { |
899 | /* Don't send the Report ID */ | 904 | /* Don't send the Report ID */ |
900 | buf++; | 905 | 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 { | |||
287 | #define HID_QUIRK_NO_EMPTY_INPUT 0x00000100 | 287 | #define HID_QUIRK_NO_EMPTY_INPUT 0x00000100 |
288 | #define HID_QUIRK_NO_INIT_INPUT_REPORTS 0x00000200 | 288 | #define HID_QUIRK_NO_INIT_INPUT_REPORTS 0x00000200 |
289 | #define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000 | 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 | #define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000 | 292 | #define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000 |
291 | #define HID_QUIRK_NO_INIT_REPORTS 0x20000000 | 293 | #define HID_QUIRK_NO_INIT_REPORTS 0x20000000 |
292 | #define HID_QUIRK_NO_IGNORE 0x40000000 | 294 | #define HID_QUIRK_NO_IGNORE 0x40000000 |