]>
Commit | Line | Data |
---|---|---|
1e93674a | 1 | /* |
37c492c8 HB |
2 | * HID driver for Saitek devices. |
3 | * | |
4 | * PS1000 (USB gamepad): | |
1e93674a AH |
5 | * Fixes the HID report descriptor by removing a non-existent axis and |
6 | * clearing the constant bit on the input reports for buttons and d-pad. | |
7 | * (This module is based on "hid-ortek".) | |
1e93674a | 8 | * Copyright (c) 2012 Andreas Hübner |
37c492c8 | 9 | * |
7bb9d643 | 10 | * R.A.T.7, R.A.T.9, M.M.O.7 (USB gaming mice): |
37c492c8 HB |
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 | * | |
1e93674a AH |
15 | */ |
16 | ||
17 | /* | |
18 | * This program is free software; you can redistribute it and/or modify it | |
19 | * under the terms of the GNU General Public License as published by the Free | |
20 | * Software Foundation; either version 2 of the License, or (at your option) | |
21 | * any later version. | |
22 | */ | |
23 | ||
24 | #include <linux/device.h> | |
25 | #include <linux/hid.h> | |
26 | #include <linux/module.h> | |
27 | #include <linux/kernel.h> | |
28 | ||
29 | #include "hid-ids.h" | |
30 | ||
37c492c8 HB |
31 | #define SAITEK_FIX_PS1000 0x0001 |
32 | #define SAITEK_RELEASE_MODE_RAT7 0x0002 | |
33 | #define SAITEK_RELEASE_MODE_MMO7 0x0004 | |
34 | ||
35 | struct saitek_sc { | |
36 | unsigned long quirks; | |
37 | int mode; | |
38 | }; | |
39 | ||
40 | static 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 | ||
1e93674a AH |
73 | static __u8 *saitek_report_fixup(struct hid_device *hdev, __u8 *rdesc, |
74 | unsigned int *rsize) | |
75 | { | |
37c492c8 HB |
76 | struct saitek_sc *ssc = hid_get_drvdata(hdev); |
77 | ||
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) { | |
1e93674a AH |
82 | |
83 | hid_info(hdev, "Fixing up Saitek PS1000 report descriptor\n"); | |
84 | ||
85 | /* convert spurious axis to a "noop" Logical Minimum (0) */ | |
86 | rdesc[20] = 0x15; | |
87 | rdesc[21] = 0x00; | |
88 | ||
89 | /* clear constant bit on buttons and d-pad */ | |
90 | rdesc[95] = 0x02; | |
91 | rdesc[111] = 0x02; | |
92 | ||
93 | } | |
94 | return rdesc; | |
95 | } | |
96 | ||
37c492c8 HB |
97 | static 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 | ||
154 | static 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 | ||
1e93674a | 177 | static const struct hid_device_id saitek_devices[] = { |
37c492c8 HB |
178 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000), |
179 | .driver_data = SAITEK_FIX_PS1000 }, | |
8ffd341c HB |
180 | { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT5), |
181 | .driver_data = SAITEK_RELEASE_MODE_RAT7 }, | |
afe98939 DS |
182 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7_OLD), |
183 | .driver_data = SAITEK_RELEASE_MODE_RAT7 }, | |
37c492c8 HB |
184 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7), |
185 | .driver_data = SAITEK_RELEASE_MODE_RAT7 }, | |
43822c98 HM |
186 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT7_CONTAGION), |
187 | .driver_data = SAITEK_RELEASE_MODE_RAT7 }, | |
7d3ea5c1 MC |
188 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RAT9), |
189 | .driver_data = SAITEK_RELEASE_MODE_RAT7 }, | |
7bb9d643 VA |
190 | { HID_USB_DEVICE(USB_VENDOR_ID_MADCATZ, USB_DEVICE_ID_MADCATZ_RAT9), |
191 | .driver_data = SAITEK_RELEASE_MODE_RAT7 }, | |
37c492c8 HB |
192 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_MMO7), |
193 | .driver_data = SAITEK_RELEASE_MODE_MMO7 }, | |
1e93674a AH |
194 | { } |
195 | }; | |
196 | ||
197 | MODULE_DEVICE_TABLE(hid, saitek_devices); | |
198 | ||
199 | static struct hid_driver saitek_driver = { | |
200 | .name = "saitek", | |
201 | .id_table = saitek_devices, | |
37c492c8 HB |
202 | .probe = saitek_probe, |
203 | .report_fixup = saitek_report_fixup, | |
204 | .raw_event = saitek_raw_event, | |
205 | .event = saitek_event, | |
1e93674a | 206 | }; |
f425458e | 207 | module_hid_driver(saitek_driver); |
1e93674a | 208 | |
1e93674a | 209 | MODULE_LICENSE("GPL"); |