aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Brinkmann <hbrinkmann@braincalibration.de>2014-05-20 14:28:00 -0400
committerJiri Kosina <jkosina@suse.cz>2014-05-20 15:28:42 -0400
commit37c492c8f6a99870551fe2964b2dfce5d9e87375 (patch)
treeebd0971a7181fbb0d7cdb03f5cabb613d7b86fca
parentf362e690e5530dd8ace289da991bda558731678e (diff)
HID: quirk for Saitek RAT7 and MMO7 mices' mode button
Some saitek mice implement a tristate button (for switching button mappings in the original driver) by keeping one of three (non-physical) buttons constantly pressed. This breaks X and probably other userspace software. This patch implements a quirk for the R.A.T.7 and M.M.O.7, tracking the mode and generating presses of a single button if it changes. Also the missing release event is generated instantly. Signed-off-by: Harald Brinkmann <hbrinkmann@braincalibration.de> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r--drivers/hid/Kconfig5
-rw-r--r--drivers/hid/hid-core.c4
-rw-r--r--drivers/hid/hid-ids.h2
-rw-r--r--drivers/hid/hid-saitek.c154
4 files changed, 157 insertions, 8 deletions
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 7af9d0b5dea1..00e01383bf30 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -608,7 +608,10 @@ config HID_SAITEK
608 Support for Saitek devices that are not fully compliant with the 608 Support for Saitek devices that are not fully compliant with the
609 HID standard. 609 HID standard.
610 610
611 Currently only supports the PS1000 controller. 611 Supported devices:
612 - PS1000 Dual Analog Pad
613 - R.A.T.7 Gaming Mouse
614 - M.M.O.7 Gaming Mouse
612 615
613config HID_SAMSUNG 616config HID_SAMSUNG
614 tristate "Samsung InfraRed remote control or keyboards" 617 tristate "Samsung InfraRed remote control or keyboards"
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 10a2c0866459..3d9c6f8efc2a 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1867,7 +1867,11 @@ static const struct hid_device_id hid_have_special_driver[] = {
1867 { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK_PRO) }, 1867 { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK_PRO) },
1868 { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) }, 1868 { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) },
1869#endif 1869#endif
1870#if IS_ENABLED(CONFIG_HID_SAITEK)
1870 { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) }, 1871 { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) },
1872 { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7) },
1873 { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7) },
1874#endif
1871 { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, 1875 { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
1872 { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, 1876 { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
1873 { HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) }, 1877 { HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index c8af7202c28d..91db867d89fa 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -761,6 +761,8 @@
761#define USB_VENDOR_ID_SAITEK 0x06a3 761#define USB_VENDOR_ID_SAITEK 0x06a3
762#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 762#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17
763#define USB_DEVICE_ID_SAITEK_PS1000 0x0621 763#define USB_DEVICE_ID_SAITEK_PS1000 0x0621
764#define USB_DEVICE_ID_SAITEK_RAT7 0x0cd7
765#define USB_DEVICE_ID_SAITEK_MMO7 0x0cd0
764 766
765#define USB_VENDOR_ID_SAMSUNG 0x0419 767#define USB_VENDOR_ID_SAMSUNG 0x0419
766#define USB_DEVICE_ID_SAMSUNG_IR_REMOTE 0x0001 768#define USB_DEVICE_ID_SAMSUNG_IR_REMOTE 0x0001
diff --git a/drivers/hid/hid-saitek.c b/drivers/hid/hid-saitek.c
index 37961c7e397d..69cca1476a0c 100644
--- a/drivers/hid/hid-saitek.c
+++ b/drivers/hid/hid-saitek.c
@@ -1,10 +1,17 @@
1/* 1/*
2 * HID driver for Saitek devices, currently only the PS1000 (USB gamepad). 2 * HID driver for Saitek devices.
3 *
4 * PS1000 (USB gamepad):
3 * Fixes the HID report descriptor by removing a non-existent axis and 5 * Fixes the HID report descriptor by removing a non-existent axis and
4 * clearing the constant bit on the input reports for buttons and d-pad. 6 * clearing the constant bit on the input reports for buttons and d-pad.
5 * (This module is based on "hid-ortek".) 7 * (This module is based on "hid-ortek".)
6 *
7 * Copyright (c) 2012 Andreas Hübner 8 * Copyright (c) 2012 Andreas Hübner
9 *
10 * R.A.T.7, M.M.O.7 (USB gaming mice):
11 * Fixes the mode button which cycles through three constantly pressed
12 * buttons. All three press events are mapped to one button and the
13 * missing release event is generated immediately.
14 *
8 */ 15 */
9 16
10/* 17/*
@@ -21,12 +28,57 @@
21 28
22#include "hid-ids.h" 29#include "hid-ids.h"
23 30
31#define SAITEK_FIX_PS1000 0x0001
32#define SAITEK_RELEASE_MODE_RAT7 0x0002
33#define SAITEK_RELEASE_MODE_MMO7 0x0004
34
35struct saitek_sc {
36 unsigned long quirks;
37 int mode;
38};
39
40static int saitek_probe(struct hid_device *hdev,
41 const struct hid_device_id *id)
42{
43 unsigned long quirks = id->driver_data;
44 struct saitek_sc *ssc;
45 int ret;
46
47 ssc = devm_kzalloc(&hdev->dev, sizeof(*ssc), GFP_KERNEL);
48 if (ssc == NULL) {
49 hid_err(hdev, "can't alloc saitek descriptor\n");
50 return -ENOMEM;
51 }
52
53 ssc->quirks = quirks;
54 ssc->mode = -1;
55
56 hid_set_drvdata(hdev, ssc);
57
58 ret = hid_parse(hdev);
59 if (ret) {
60 hid_err(hdev, "parse failed\n");
61 return ret;
62 }
63
64 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
65 if (ret) {
66 hid_err(hdev, "hw start failed\n");
67 return ret;
68 }
69
70 return 0;
71}
72
24static __u8 *saitek_report_fixup(struct hid_device *hdev, __u8 *rdesc, 73static __u8 *saitek_report_fixup(struct hid_device *hdev, __u8 *rdesc,
25 unsigned int *rsize) 74 unsigned int *rsize)
26{ 75{
27 if (*rsize == 137 && rdesc[20] == 0x09 && rdesc[21] == 0x33 76 struct saitek_sc *ssc = hid_get_drvdata(hdev);
28 && rdesc[94] == 0x81 && rdesc[95] == 0x03 77
29 && rdesc[110] == 0x81 && rdesc[111] == 0x03) { 78 if ((ssc->quirks & SAITEK_FIX_PS1000) && *rsize == 137 &&
79 rdesc[20] == 0x09 && rdesc[21] == 0x33 &&
80 rdesc[94] == 0x81 && rdesc[95] == 0x03 &&
81 rdesc[110] == 0x81 && rdesc[111] == 0x03) {
30 82
31 hid_info(hdev, "Fixing up Saitek PS1000 report descriptor\n"); 83 hid_info(hdev, "Fixing up Saitek PS1000 report descriptor\n");
32 84
@@ -42,8 +94,93 @@ static __u8 *saitek_report_fixup(struct hid_device *hdev, __u8 *rdesc,
42 return rdesc; 94 return rdesc;
43} 95}
44 96
97static int saitek_raw_event(struct hid_device *hdev,
98 struct hid_report *report, u8 *raw_data, int size)
99{
100 struct saitek_sc *ssc = hid_get_drvdata(hdev);
101
102 if (ssc->quirks & SAITEK_RELEASE_MODE_RAT7 && size == 7) {
103 /* R.A.T.7 uses bits 13, 14, 15 for the mode */
104 int mode = -1;
105 if (raw_data[1] & 0x01)
106 mode = 0;
107 else if (raw_data[1] & 0x02)
108 mode = 1;
109 else if (raw_data[1] & 0x04)
110 mode = 2;
111
112 /* clear mode bits */
113 raw_data[1] &= ~0x07;
114
115 if (mode != ssc->mode) {
116 hid_dbg(hdev, "entered mode %d\n", mode);
117 if (ssc->mode != -1) {
118 /* use bit 13 as the mode button */
119 raw_data[1] |= 0x04;
120 }
121 ssc->mode = mode;
122 }
123 } else if (ssc->quirks & SAITEK_RELEASE_MODE_MMO7 && size == 8) {
124
125 /* M.M.O.7 uses bits 8, 22, 23 for the mode */
126 int mode = -1;
127 if (raw_data[1] & 0x80)
128 mode = 0;
129 else if (raw_data[2] & 0x01)
130 mode = 1;
131 else if (raw_data[2] & 0x02)
132 mode = 2;
133
134 /* clear mode bits */
135 raw_data[1] &= ~0x80;
136 raw_data[2] &= ~0x03;
137
138 if (mode != ssc->mode) {
139 hid_dbg(hdev, "entered mode %d\n", mode);
140 if (ssc->mode != -1) {
141 /* use bit 8 as the mode button, bits 22
142 * and 23 do not represent buttons
143 * according to the HID report descriptor
144 */
145 raw_data[1] |= 0x80;
146 }
147 ssc->mode = mode;
148 }
149 }
150
151 return 0;
152}
153
154static int saitek_event(struct hid_device *hdev, struct hid_field *field,
155 struct hid_usage *usage, __s32 value)
156{
157 struct saitek_sc *ssc = hid_get_drvdata(hdev);
158 struct input_dev *input = field->hidinput->input;
159
160 if (usage->type == EV_KEY && value &&
161 (((ssc->quirks & SAITEK_RELEASE_MODE_RAT7) &&
162 usage->code - BTN_MOUSE == 10) ||
163 ((ssc->quirks & SAITEK_RELEASE_MODE_MMO7) &&
164 usage->code - BTN_MOUSE == 15))) {
165
166 input_report_key(input, usage->code, 1);
167
168 /* report missing release event */
169 input_report_key(input, usage->code, 0);
170
171 return 1;
172 }
173
174 return 0;
175}
176
45static const struct hid_device_id saitek_devices[] = { 177static const struct hid_device_id saitek_devices[] = {
46 { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000)}, 178 { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000),
179 .driver_data = SAITEK_FIX_PS1000 },
180 { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7),
181 .driver_data = SAITEK_RELEASE_MODE_RAT7 },
182 { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7),
183 .driver_data = SAITEK_RELEASE_MODE_MMO7 },
47 { } 184 { }
48}; 185};
49 186
@@ -52,7 +189,10 @@ MODULE_DEVICE_TABLE(hid, saitek_devices);
52static struct hid_driver saitek_driver = { 189static struct hid_driver saitek_driver = {
53 .name = "saitek", 190 .name = "saitek",
54 .id_table = saitek_devices, 191 .id_table = saitek_devices,
55 .report_fixup = saitek_report_fixup 192 .probe = saitek_probe,
193 .report_fixup = saitek_report_fixup,
194 .raw_event = saitek_raw_event,
195 .event = saitek_event,
56}; 196};
57module_hid_driver(saitek_driver); 197module_hid_driver(saitek_driver);
58 198