1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * HID driver for Saitek devices. |
4 | * |
5 | * PS1000 (USB gamepad): |
6 | * Fixes the HID report descriptor by removing a non-existent axis and |
7 | * clearing the constant bit on the input reports for buttons and d-pad. |
8 | * (This module is based on "hid-ortek".) |
9 | * Copyright (c) 2012 Andreas Hübner |
10 | * |
11 | * R.A.T.7, R.A.T.9, M.M.O.7 (USB gaming mice): |
12 | * Fixes the mode button which cycles through three constantly pressed |
13 | * buttons. All three press events are mapped to one button and the |
14 | * missing release event is generated immediately. |
15 | */ |
16 | |
17 | /* |
18 | */ |
19 | |
20 | #include <linux/device.h> |
21 | #include <linux/hid.h> |
22 | #include <linux/module.h> |
23 | #include <linux/kernel.h> |
24 | |
25 | #include "hid-ids.h" |
26 | |
27 | #define SAITEK_FIX_PS1000 0x0001 |
28 | #define SAITEK_RELEASE_MODE_RAT7 0x0002 |
29 | #define SAITEK_RELEASE_MODE_MMO7 0x0004 |
30 | |
31 | struct saitek_sc { |
32 | unsigned long quirks; |
33 | int mode; |
34 | }; |
35 | |
36 | static int saitek_probe(struct hid_device *hdev, |
37 | const struct hid_device_id *id) |
38 | { |
39 | unsigned long quirks = id->driver_data; |
40 | struct saitek_sc *ssc; |
41 | int ret; |
42 | |
43 | ssc = devm_kzalloc(dev: &hdev->dev, size: sizeof(*ssc), GFP_KERNEL); |
44 | if (ssc == NULL) { |
45 | hid_err(hdev, "can't alloc saitek descriptor\n" ); |
46 | return -ENOMEM; |
47 | } |
48 | |
49 | ssc->quirks = quirks; |
50 | ssc->mode = -1; |
51 | |
52 | hid_set_drvdata(hdev, data: ssc); |
53 | |
54 | ret = hid_parse(hdev); |
55 | if (ret) { |
56 | hid_err(hdev, "parse failed\n" ); |
57 | return ret; |
58 | } |
59 | |
60 | ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); |
61 | if (ret) { |
62 | hid_err(hdev, "hw start failed\n" ); |
63 | return ret; |
64 | } |
65 | |
66 | return 0; |
67 | } |
68 | |
69 | static __u8 *saitek_report_fixup(struct hid_device *hdev, __u8 *rdesc, |
70 | unsigned int *rsize) |
71 | { |
72 | struct saitek_sc *ssc = hid_get_drvdata(hdev); |
73 | |
74 | if ((ssc->quirks & SAITEK_FIX_PS1000) && *rsize == 137 && |
75 | rdesc[20] == 0x09 && rdesc[21] == 0x33 && |
76 | rdesc[94] == 0x81 && rdesc[95] == 0x03 && |
77 | rdesc[110] == 0x81 && rdesc[111] == 0x03) { |
78 | |
79 | hid_info(hdev, "Fixing up Saitek PS1000 report descriptor\n" ); |
80 | |
81 | /* convert spurious axis to a "noop" Logical Minimum (0) */ |
82 | rdesc[20] = 0x15; |
83 | rdesc[21] = 0x00; |
84 | |
85 | /* clear constant bit on buttons and d-pad */ |
86 | rdesc[95] = 0x02; |
87 | rdesc[111] = 0x02; |
88 | |
89 | } |
90 | return rdesc; |
91 | } |
92 | |
93 | static int saitek_raw_event(struct hid_device *hdev, |
94 | struct hid_report *report, u8 *raw_data, int size) |
95 | { |
96 | struct saitek_sc *ssc = hid_get_drvdata(hdev); |
97 | |
98 | if (ssc->quirks & SAITEK_RELEASE_MODE_RAT7 && size == 7) { |
99 | /* R.A.T.7 uses bits 13, 14, 15 for the mode */ |
100 | int mode = -1; |
101 | if (raw_data[1] & 0x01) |
102 | mode = 0; |
103 | else if (raw_data[1] & 0x02) |
104 | mode = 1; |
105 | else if (raw_data[1] & 0x04) |
106 | mode = 2; |
107 | |
108 | /* clear mode bits */ |
109 | raw_data[1] &= ~0x07; |
110 | |
111 | if (mode != ssc->mode) { |
112 | hid_dbg(hdev, "entered mode %d\n" , mode); |
113 | if (ssc->mode != -1) { |
114 | /* use bit 13 as the mode button */ |
115 | raw_data[1] |= 0x04; |
116 | } |
117 | ssc->mode = mode; |
118 | } |
119 | } else if (ssc->quirks & SAITEK_RELEASE_MODE_MMO7 && size == 8) { |
120 | |
121 | /* M.M.O.7 uses bits 8, 22, 23 for the mode */ |
122 | int mode = -1; |
123 | if (raw_data[1] & 0x80) |
124 | mode = 0; |
125 | else if (raw_data[2] & 0x01) |
126 | mode = 1; |
127 | else if (raw_data[2] & 0x02) |
128 | mode = 2; |
129 | |
130 | /* clear mode bits */ |
131 | raw_data[1] &= ~0x80; |
132 | raw_data[2] &= ~0x03; |
133 | |
134 | if (mode != ssc->mode) { |
135 | hid_dbg(hdev, "entered mode %d\n" , mode); |
136 | if (ssc->mode != -1) { |
137 | /* use bit 8 as the mode button, bits 22 |
138 | * and 23 do not represent buttons |
139 | * according to the HID report descriptor |
140 | */ |
141 | raw_data[1] |= 0x80; |
142 | } |
143 | ssc->mode = mode; |
144 | } |
145 | } |
146 | |
147 | return 0; |
148 | } |
149 | |
150 | static int saitek_event(struct hid_device *hdev, struct hid_field *field, |
151 | struct hid_usage *usage, __s32 value) |
152 | { |
153 | struct saitek_sc *ssc = hid_get_drvdata(hdev); |
154 | struct input_dev *input = field->hidinput->input; |
155 | |
156 | if (usage->type == EV_KEY && value && |
157 | (((ssc->quirks & SAITEK_RELEASE_MODE_RAT7) && |
158 | usage->code - BTN_MOUSE == 10) || |
159 | ((ssc->quirks & SAITEK_RELEASE_MODE_MMO7) && |
160 | usage->code - BTN_MOUSE == 15))) { |
161 | |
162 | input_report_key(dev: input, code: usage->code, value: 1); |
163 | |
164 | /* report missing release event */ |
165 | input_report_key(dev: input, code: usage->code, value: 0); |
166 | |
167 | return 1; |
168 | } |
169 | |
170 | return 0; |
171 | } |
172 | |
173 | static const struct hid_device_id saitek_devices[] = { |
174 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000), |
175 | .driver_data = SAITEK_FIX_PS1000 }, |
176 | { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT5), |
177 | .driver_data = SAITEK_RELEASE_MODE_RAT7 }, |
178 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7_OLD), |
179 | .driver_data = SAITEK_RELEASE_MODE_RAT7 }, |
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_RAT7_CONTAGION), |
183 | .driver_data = SAITEK_RELEASE_MODE_RAT7 }, |
184 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT9), |
185 | .driver_data = SAITEK_RELEASE_MODE_RAT7 }, |
186 | { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT9), |
187 | .driver_data = SAITEK_RELEASE_MODE_RAT7 }, |
188 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7), |
189 | .driver_data = SAITEK_RELEASE_MODE_MMO7 }, |
190 | { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_MMO7), |
191 | .driver_data = SAITEK_RELEASE_MODE_MMO7 }, |
192 | { } |
193 | }; |
194 | |
195 | MODULE_DEVICE_TABLE(hid, saitek_devices); |
196 | |
197 | static struct hid_driver saitek_driver = { |
198 | .name = "saitek" , |
199 | .id_table = saitek_devices, |
200 | .probe = saitek_probe, |
201 | .report_fixup = saitek_report_fixup, |
202 | .raw_event = saitek_raw_event, |
203 | .event = saitek_event, |
204 | }; |
205 | module_hid_driver(saitek_driver); |
206 | |
207 | MODULE_LICENSE("GPL" ); |
208 | |