12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2606bd0a8SJiri Slaby /*
3606bd0a8SJiri Slaby * HID driver for some logitech "special" devices
4606bd0a8SJiri Slaby *
5606bd0a8SJiri Slaby * Copyright (c) 1999 Andreas Gal
6606bd0a8SJiri Slaby * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
7606bd0a8SJiri Slaby * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
8606bd0a8SJiri Slaby * Copyright (c) 2006-2007 Jiri Kosina
9606bd0a8SJiri Slaby * Copyright (c) 2008 Jiri Slaby
102c6118e4SHendrik Iben * Copyright (c) 2010 Hendrik Iben
11606bd0a8SJiri Slaby */
12606bd0a8SJiri Slaby
13606bd0a8SJiri Slaby /*
14606bd0a8SJiri Slaby */
15606bd0a8SJiri Slaby
16606bd0a8SJiri Slaby #include <linux/device.h>
17606bd0a8SJiri Slaby #include <linux/hid.h>
18606bd0a8SJiri Slaby #include <linux/module.h>
1932c88cbcSSimon Wood #include <linux/random.h>
2032c88cbcSSimon Wood #include <linux/sched.h>
2154bfe3f0SPaul Sbarra #include <linux/usb.h>
2232c88cbcSSimon Wood #include <linux/wait.h>
23606bd0a8SJiri Slaby
2454bfe3f0SPaul Sbarra #include "usbhid/usbhid.h"
25606bd0a8SJiri Slaby #include "hid-ids.h"
26606bd0a8SJiri Slaby #include "hid-lg.h"
27a54dc779SMichal Malý #include "hid-lg4ff.h"
28606bd0a8SJiri Slaby
29606bd0a8SJiri Slaby #define LG_RDESC 0x001
30606bd0a8SJiri Slaby #define LG_BAD_RELATIVE_KEYS 0x002
31606bd0a8SJiri Slaby #define LG_DUPLICATE_USAGES 0x004
32606bd0a8SJiri Slaby #define LG_EXPANDED_KEYMAP 0x010
33606bd0a8SJiri Slaby #define LG_IGNORE_DOUBLED_WHEEL 0x020
34606bd0a8SJiri Slaby #define LG_WIRELESS 0x040
35606bd0a8SJiri Slaby #define LG_INVERT_HWHEEL 0x080
36606bd0a8SJiri Slaby #define LG_NOGET 0x100
37606bd0a8SJiri Slaby #define LG_FF 0x200
38606bd0a8SJiri Slaby #define LG_FF2 0x400
3924985cf6SJiri Kosina #define LG_RDESC_REL_ABS 0x800
4074f292caSGary Stein #define LG_FF3 0x1000
4132c88cbcSSimon Wood #define LG_FF4 0x2000
42606bd0a8SJiri Slaby
4354bfe3f0SPaul Sbarra /* Size of the original descriptors of the Driving Force (and Pro) wheels */
4454bfe3f0SPaul Sbarra #define DF_RDESC_ORIG_SIZE 130
45dc0a4f0cSMichael Bauer #define DFP_RDESC_ORIG_SIZE 97
467f505470SSimon Wood #define FV_RDESC_ORIG_SIZE 130
47270baef1SSimon Wood #define MOMO_RDESC_ORIG_SIZE 87
48348cbaa8SSimon Wood #define MOMO2_RDESC_ORIG_SIZE 87
49560bea30SSimon Wood #define FFG_RDESC_ORIG_SIZE 85
50e41b3cdaSJarrad Whitaker #define FG_RDESC_ORIG_SIZE 82
51dc0a4f0cSMichael Bauer
5254bfe3f0SPaul Sbarra /* Fixed report descriptors for Logitech Driving Force (and Pro)
5354bfe3f0SPaul Sbarra * wheel controllers
54dc0a4f0cSMichael Bauer *
5554bfe3f0SPaul Sbarra * The original descriptors hide the separate throttle and brake axes in
56dc0a4f0cSMichael Bauer * a custom vendor usage page, providing only a combined value as
57dc0a4f0cSMichael Bauer * GenericDesktop.Y.
5854bfe3f0SPaul Sbarra * These descriptors remove the combined Y axis and instead report
59dc0a4f0cSMichael Bauer * separate throttle (Y) and brake (RZ).
60dc0a4f0cSMichael Bauer */
6154bfe3f0SPaul Sbarra static __u8 df_rdesc_fixed[] = {
6254bfe3f0SPaul Sbarra 0x05, 0x01, /* Usage Page (Desktop), */
63bfb61abfSAntonio Ospite 0x09, 0x04, /* Usage (Joystick), */
6454bfe3f0SPaul Sbarra 0xA1, 0x01, /* Collection (Application), */
6554bfe3f0SPaul Sbarra 0xA1, 0x02, /* Collection (Logical), */
6654bfe3f0SPaul Sbarra 0x95, 0x01, /* Report Count (1), */
6754bfe3f0SPaul Sbarra 0x75, 0x0A, /* Report Size (10), */
6854bfe3f0SPaul Sbarra 0x14, /* Logical Minimum (0), */
6954bfe3f0SPaul Sbarra 0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
7054bfe3f0SPaul Sbarra 0x34, /* Physical Minimum (0), */
7154bfe3f0SPaul Sbarra 0x46, 0xFF, 0x03, /* Physical Maximum (1023), */
7254bfe3f0SPaul Sbarra 0x09, 0x30, /* Usage (X), */
7354bfe3f0SPaul Sbarra 0x81, 0x02, /* Input (Variable), */
7454bfe3f0SPaul Sbarra 0x95, 0x0C, /* Report Count (12), */
7554bfe3f0SPaul Sbarra 0x75, 0x01, /* Report Size (1), */
7654bfe3f0SPaul Sbarra 0x25, 0x01, /* Logical Maximum (1), */
7754bfe3f0SPaul Sbarra 0x45, 0x01, /* Physical Maximum (1), */
7854bfe3f0SPaul Sbarra 0x05, 0x09, /* Usage (Buttons), */
7954bfe3f0SPaul Sbarra 0x19, 0x01, /* Usage Minimum (1), */
8054bfe3f0SPaul Sbarra 0x29, 0x0c, /* Usage Maximum (12), */
8154bfe3f0SPaul Sbarra 0x81, 0x02, /* Input (Variable), */
8254bfe3f0SPaul Sbarra 0x95, 0x02, /* Report Count (2), */
8354bfe3f0SPaul Sbarra 0x06, 0x00, 0xFF, /* Usage Page (Vendor: 65280), */
8454bfe3f0SPaul Sbarra 0x09, 0x01, /* Usage (?: 1), */
8554bfe3f0SPaul Sbarra 0x81, 0x02, /* Input (Variable), */
8654bfe3f0SPaul Sbarra 0x05, 0x01, /* Usage Page (Desktop), */
8754bfe3f0SPaul Sbarra 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
8854bfe3f0SPaul Sbarra 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
8954bfe3f0SPaul Sbarra 0x95, 0x01, /* Report Count (1), */
9054bfe3f0SPaul Sbarra 0x75, 0x08, /* Report Size (8), */
9154bfe3f0SPaul Sbarra 0x81, 0x02, /* Input (Variable), */
9254bfe3f0SPaul Sbarra 0x25, 0x07, /* Logical Maximum (7), */
9354bfe3f0SPaul Sbarra 0x46, 0x3B, 0x01, /* Physical Maximum (315), */
9454bfe3f0SPaul Sbarra 0x75, 0x04, /* Report Size (4), */
9554bfe3f0SPaul Sbarra 0x65, 0x14, /* Unit (Degrees), */
9654bfe3f0SPaul Sbarra 0x09, 0x39, /* Usage (Hat Switch), */
9754bfe3f0SPaul Sbarra 0x81, 0x42, /* Input (Variable, Null State), */
9854bfe3f0SPaul Sbarra 0x75, 0x01, /* Report Size (1), */
9954bfe3f0SPaul Sbarra 0x95, 0x04, /* Report Count (4), */
10054bfe3f0SPaul Sbarra 0x65, 0x00, /* Unit (none), */
10154bfe3f0SPaul Sbarra 0x06, 0x00, 0xFF, /* Usage Page (Vendor: 65280), */
10254bfe3f0SPaul Sbarra 0x09, 0x01, /* Usage (?: 1), */
10354bfe3f0SPaul Sbarra 0x25, 0x01, /* Logical Maximum (1), */
10454bfe3f0SPaul Sbarra 0x45, 0x01, /* Physical Maximum (1), */
10554bfe3f0SPaul Sbarra 0x81, 0x02, /* Input (Variable), */
1065a9b571bSPaul Sbarra 0x05, 0x01, /* Usage Page (Desktop), */
1075a9b571bSPaul Sbarra 0x95, 0x01, /* Report Count (1), */
10854bfe3f0SPaul Sbarra 0x75, 0x08, /* Report Size (8), */
10954bfe3f0SPaul Sbarra 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
11054bfe3f0SPaul Sbarra 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
1115a9b571bSPaul Sbarra 0x09, 0x31, /* Usage (Y), */
1125a9b571bSPaul Sbarra 0x81, 0x02, /* Input (Variable), */
1135a9b571bSPaul Sbarra 0x09, 0x35, /* Usage (Rz), */
11454bfe3f0SPaul Sbarra 0x81, 0x02, /* Input (Variable), */
11554bfe3f0SPaul Sbarra 0xC0, /* End Collection, */
11654bfe3f0SPaul Sbarra 0xA1, 0x02, /* Collection (Logical), */
11754bfe3f0SPaul Sbarra 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
11854bfe3f0SPaul Sbarra 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
11954bfe3f0SPaul Sbarra 0x95, 0x07, /* Report Count (7), */
12054bfe3f0SPaul Sbarra 0x75, 0x08, /* Report Size (8), */
12154bfe3f0SPaul Sbarra 0x09, 0x03, /* Usage (?: 3), */
12254bfe3f0SPaul Sbarra 0x91, 0x02, /* Output (Variable), */
12354bfe3f0SPaul Sbarra 0xC0, /* End Collection, */
12454bfe3f0SPaul Sbarra 0xC0 /* End Collection */
12554bfe3f0SPaul Sbarra };
12654bfe3f0SPaul Sbarra
127dc0a4f0cSMichael Bauer static __u8 dfp_rdesc_fixed[] = {
128dc0a4f0cSMichael Bauer 0x05, 0x01, /* Usage Page (Desktop), */
129bfb61abfSAntonio Ospite 0x09, 0x04, /* Usage (Joystick), */
130dc0a4f0cSMichael Bauer 0xA1, 0x01, /* Collection (Application), */
131dc0a4f0cSMichael Bauer 0xA1, 0x02, /* Collection (Logical), */
132dc0a4f0cSMichael Bauer 0x95, 0x01, /* Report Count (1), */
133dc0a4f0cSMichael Bauer 0x75, 0x0E, /* Report Size (14), */
134dc0a4f0cSMichael Bauer 0x14, /* Logical Minimum (0), */
135dc0a4f0cSMichael Bauer 0x26, 0xFF, 0x3F, /* Logical Maximum (16383), */
136dc0a4f0cSMichael Bauer 0x34, /* Physical Minimum (0), */
137dc0a4f0cSMichael Bauer 0x46, 0xFF, 0x3F, /* Physical Maximum (16383), */
138dc0a4f0cSMichael Bauer 0x09, 0x30, /* Usage (X), */
139dc0a4f0cSMichael Bauer 0x81, 0x02, /* Input (Variable), */
140dc0a4f0cSMichael Bauer 0x95, 0x0E, /* Report Count (14), */
141dc0a4f0cSMichael Bauer 0x75, 0x01, /* Report Size (1), */
142dc0a4f0cSMichael Bauer 0x25, 0x01, /* Logical Maximum (1), */
143dc0a4f0cSMichael Bauer 0x45, 0x01, /* Physical Maximum (1), */
144dc0a4f0cSMichael Bauer 0x05, 0x09, /* Usage Page (Button), */
145dc0a4f0cSMichael Bauer 0x19, 0x01, /* Usage Minimum (01h), */
146dc0a4f0cSMichael Bauer 0x29, 0x0E, /* Usage Maximum (0Eh), */
147dc0a4f0cSMichael Bauer 0x81, 0x02, /* Input (Variable), */
148dc0a4f0cSMichael Bauer 0x05, 0x01, /* Usage Page (Desktop), */
149dc0a4f0cSMichael Bauer 0x95, 0x01, /* Report Count (1), */
150dc0a4f0cSMichael Bauer 0x75, 0x04, /* Report Size (4), */
151dc0a4f0cSMichael Bauer 0x25, 0x07, /* Logical Maximum (7), */
152dc0a4f0cSMichael Bauer 0x46, 0x3B, 0x01, /* Physical Maximum (315), */
153dc0a4f0cSMichael Bauer 0x65, 0x14, /* Unit (Degrees), */
154dc0a4f0cSMichael Bauer 0x09, 0x39, /* Usage (Hat Switch), */
155dc0a4f0cSMichael Bauer 0x81, 0x42, /* Input (Variable, Nullstate), */
156dc0a4f0cSMichael Bauer 0x65, 0x00, /* Unit, */
157dc0a4f0cSMichael Bauer 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
158dc0a4f0cSMichael Bauer 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
159dc0a4f0cSMichael Bauer 0x75, 0x08, /* Report Size (8), */
160dc0a4f0cSMichael Bauer 0x81, 0x01, /* Input (Constant), */
161dc0a4f0cSMichael Bauer 0x09, 0x31, /* Usage (Y), */
162dc0a4f0cSMichael Bauer 0x81, 0x02, /* Input (Variable), */
163dc0a4f0cSMichael Bauer 0x09, 0x35, /* Usage (Rz), */
164dc0a4f0cSMichael Bauer 0x81, 0x02, /* Input (Variable), */
165dc0a4f0cSMichael Bauer 0x81, 0x01, /* Input (Constant), */
166dc0a4f0cSMichael Bauer 0xC0, /* End Collection, */
167dc0a4f0cSMichael Bauer 0xA1, 0x02, /* Collection (Logical), */
168dc0a4f0cSMichael Bauer 0x09, 0x02, /* Usage (02h), */
169dc0a4f0cSMichael Bauer 0x95, 0x07, /* Report Count (7), */
170dc0a4f0cSMichael Bauer 0x91, 0x02, /* Output (Variable), */
171dc0a4f0cSMichael Bauer 0xC0, /* End Collection, */
172dc0a4f0cSMichael Bauer 0xC0 /* End Collection */
173dc0a4f0cSMichael Bauer };
174dc0a4f0cSMichael Bauer
1757f505470SSimon Wood static __u8 fv_rdesc_fixed[] = {
1767f505470SSimon Wood 0x05, 0x01, /* Usage Page (Desktop), */
177bfb61abfSAntonio Ospite 0x09, 0x04, /* Usage (Joystick), */
1787f505470SSimon Wood 0xA1, 0x01, /* Collection (Application), */
1797f505470SSimon Wood 0xA1, 0x02, /* Collection (Logical), */
1807f505470SSimon Wood 0x95, 0x01, /* Report Count (1), */
1817f505470SSimon Wood 0x75, 0x0A, /* Report Size (10), */
1827f505470SSimon Wood 0x15, 0x00, /* Logical Minimum (0), */
1837f505470SSimon Wood 0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
1847f505470SSimon Wood 0x35, 0x00, /* Physical Minimum (0), */
1857f505470SSimon Wood 0x46, 0xFF, 0x03, /* Physical Maximum (1023), */
1867f505470SSimon Wood 0x09, 0x30, /* Usage (X), */
1877f505470SSimon Wood 0x81, 0x02, /* Input (Variable), */
1887f505470SSimon Wood 0x95, 0x0C, /* Report Count (12), */
1897f505470SSimon Wood 0x75, 0x01, /* Report Size (1), */
1907f505470SSimon Wood 0x25, 0x01, /* Logical Maximum (1), */
1917f505470SSimon Wood 0x45, 0x01, /* Physical Maximum (1), */
1927f505470SSimon Wood 0x05, 0x09, /* Usage Page (Button), */
1937f505470SSimon Wood 0x19, 0x01, /* Usage Minimum (01h), */
1947f505470SSimon Wood 0x29, 0x0C, /* Usage Maximum (0Ch), */
1957f505470SSimon Wood 0x81, 0x02, /* Input (Variable), */
1967f505470SSimon Wood 0x95, 0x02, /* Report Count (2), */
1977f505470SSimon Wood 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
1987f505470SSimon Wood 0x09, 0x01, /* Usage (01h), */
1997f505470SSimon Wood 0x81, 0x02, /* Input (Variable), */
2007f505470SSimon Wood 0x09, 0x02, /* Usage (02h), */
2017f505470SSimon Wood 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
2027f505470SSimon Wood 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
2037f505470SSimon Wood 0x95, 0x01, /* Report Count (1), */
2047f505470SSimon Wood 0x75, 0x08, /* Report Size (8), */
2057f505470SSimon Wood 0x81, 0x02, /* Input (Variable), */
2067f505470SSimon Wood 0x05, 0x01, /* Usage Page (Desktop), */
2077f505470SSimon Wood 0x25, 0x07, /* Logical Maximum (7), */
2087f505470SSimon Wood 0x46, 0x3B, 0x01, /* Physical Maximum (315), */
2097f505470SSimon Wood 0x75, 0x04, /* Report Size (4), */
2107f505470SSimon Wood 0x65, 0x14, /* Unit (Degrees), */
2117f505470SSimon Wood 0x09, 0x39, /* Usage (Hat Switch), */
2127f505470SSimon Wood 0x81, 0x42, /* Input (Variable, Null State), */
2137f505470SSimon Wood 0x75, 0x01, /* Report Size (1), */
2147f505470SSimon Wood 0x95, 0x04, /* Report Count (4), */
2157f505470SSimon Wood 0x65, 0x00, /* Unit, */
2167f505470SSimon Wood 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
2177f505470SSimon Wood 0x09, 0x01, /* Usage (01h), */
2187f505470SSimon Wood 0x25, 0x01, /* Logical Maximum (1), */
2197f505470SSimon Wood 0x45, 0x01, /* Physical Maximum (1), */
2207f505470SSimon Wood 0x81, 0x02, /* Input (Variable), */
2217f505470SSimon Wood 0x05, 0x01, /* Usage Page (Desktop), */
2227f505470SSimon Wood 0x95, 0x01, /* Report Count (1), */
2237f505470SSimon Wood 0x75, 0x08, /* Report Size (8), */
2247f505470SSimon Wood 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
2257f505470SSimon Wood 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
2267f505470SSimon Wood 0x09, 0x31, /* Usage (Y), */
2277f505470SSimon Wood 0x81, 0x02, /* Input (Variable), */
2287f505470SSimon Wood 0x09, 0x32, /* Usage (Z), */
2297f505470SSimon Wood 0x81, 0x02, /* Input (Variable), */
2307f505470SSimon Wood 0xC0, /* End Collection, */
2317f505470SSimon Wood 0xA1, 0x02, /* Collection (Logical), */
2327f505470SSimon Wood 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
2337f505470SSimon Wood 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
2347f505470SSimon Wood 0x95, 0x07, /* Report Count (7), */
2357f505470SSimon Wood 0x75, 0x08, /* Report Size (8), */
2367f505470SSimon Wood 0x09, 0x03, /* Usage (03h), */
2377f505470SSimon Wood 0x91, 0x02, /* Output (Variable), */
2387f505470SSimon Wood 0xC0, /* End Collection, */
2397f505470SSimon Wood 0xC0 /* End Collection */
2407f505470SSimon Wood };
2417f505470SSimon Wood
242270baef1SSimon Wood static __u8 momo_rdesc_fixed[] = {
243270baef1SSimon Wood 0x05, 0x01, /* Usage Page (Desktop), */
244bfb61abfSAntonio Ospite 0x09, 0x04, /* Usage (Joystick), */
245270baef1SSimon Wood 0xA1, 0x01, /* Collection (Application), */
246270baef1SSimon Wood 0xA1, 0x02, /* Collection (Logical), */
247270baef1SSimon Wood 0x95, 0x01, /* Report Count (1), */
248270baef1SSimon Wood 0x75, 0x0A, /* Report Size (10), */
249270baef1SSimon Wood 0x15, 0x00, /* Logical Minimum (0), */
250270baef1SSimon Wood 0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
251270baef1SSimon Wood 0x35, 0x00, /* Physical Minimum (0), */
252270baef1SSimon Wood 0x46, 0xFF, 0x03, /* Physical Maximum (1023), */
253270baef1SSimon Wood 0x09, 0x30, /* Usage (X), */
254270baef1SSimon Wood 0x81, 0x02, /* Input (Variable), */
255270baef1SSimon Wood 0x95, 0x08, /* Report Count (8), */
256270baef1SSimon Wood 0x75, 0x01, /* Report Size (1), */
257270baef1SSimon Wood 0x25, 0x01, /* Logical Maximum (1), */
258270baef1SSimon Wood 0x45, 0x01, /* Physical Maximum (1), */
259270baef1SSimon Wood 0x05, 0x09, /* Usage Page (Button), */
260270baef1SSimon Wood 0x19, 0x01, /* Usage Minimum (01h), */
261270baef1SSimon Wood 0x29, 0x08, /* Usage Maximum (08h), */
262270baef1SSimon Wood 0x81, 0x02, /* Input (Variable), */
263270baef1SSimon Wood 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
264270baef1SSimon Wood 0x75, 0x0E, /* Report Size (14), */
265270baef1SSimon Wood 0x95, 0x01, /* Report Count (1), */
266270baef1SSimon Wood 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
267270baef1SSimon Wood 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
268270baef1SSimon Wood 0x09, 0x00, /* Usage (00h), */
269270baef1SSimon Wood 0x81, 0x02, /* Input (Variable), */
270270baef1SSimon Wood 0x05, 0x01, /* Usage Page (Desktop), */
271270baef1SSimon Wood 0x75, 0x08, /* Report Size (8), */
272270baef1SSimon Wood 0x09, 0x31, /* Usage (Y), */
273270baef1SSimon Wood 0x81, 0x02, /* Input (Variable), */
274270baef1SSimon Wood 0x09, 0x32, /* Usage (Z), */
275270baef1SSimon Wood 0x81, 0x02, /* Input (Variable), */
276270baef1SSimon Wood 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
277270baef1SSimon Wood 0x09, 0x01, /* Usage (01h), */
278270baef1SSimon Wood 0x81, 0x02, /* Input (Variable), */
279270baef1SSimon Wood 0xC0, /* End Collection, */
280270baef1SSimon Wood 0xA1, 0x02, /* Collection (Logical), */
281270baef1SSimon Wood 0x09, 0x02, /* Usage (02h), */
282270baef1SSimon Wood 0x95, 0x07, /* Report Count (7), */
283270baef1SSimon Wood 0x91, 0x02, /* Output (Variable), */
284270baef1SSimon Wood 0xC0, /* End Collection, */
285270baef1SSimon Wood 0xC0 /* End Collection */
286270baef1SSimon Wood };
287dc0a4f0cSMichael Bauer
288348cbaa8SSimon Wood static __u8 momo2_rdesc_fixed[] = {
289348cbaa8SSimon Wood 0x05, 0x01, /* Usage Page (Desktop), */
290bfb61abfSAntonio Ospite 0x09, 0x04, /* Usage (Joystick), */
291348cbaa8SSimon Wood 0xA1, 0x01, /* Collection (Application), */
292348cbaa8SSimon Wood 0xA1, 0x02, /* Collection (Logical), */
293348cbaa8SSimon Wood 0x95, 0x01, /* Report Count (1), */
294348cbaa8SSimon Wood 0x75, 0x0A, /* Report Size (10), */
295348cbaa8SSimon Wood 0x15, 0x00, /* Logical Minimum (0), */
296348cbaa8SSimon Wood 0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
297348cbaa8SSimon Wood 0x35, 0x00, /* Physical Minimum (0), */
298348cbaa8SSimon Wood 0x46, 0xFF, 0x03, /* Physical Maximum (1023), */
299348cbaa8SSimon Wood 0x09, 0x30, /* Usage (X), */
300348cbaa8SSimon Wood 0x81, 0x02, /* Input (Variable), */
301348cbaa8SSimon Wood 0x95, 0x0A, /* Report Count (10), */
302348cbaa8SSimon Wood 0x75, 0x01, /* Report Size (1), */
303348cbaa8SSimon Wood 0x25, 0x01, /* Logical Maximum (1), */
304348cbaa8SSimon Wood 0x45, 0x01, /* Physical Maximum (1), */
305348cbaa8SSimon Wood 0x05, 0x09, /* Usage Page (Button), */
306348cbaa8SSimon Wood 0x19, 0x01, /* Usage Minimum (01h), */
307348cbaa8SSimon Wood 0x29, 0x0A, /* Usage Maximum (0Ah), */
308348cbaa8SSimon Wood 0x81, 0x02, /* Input (Variable), */
309348cbaa8SSimon Wood 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
310348cbaa8SSimon Wood 0x09, 0x00, /* Usage (00h), */
311348cbaa8SSimon Wood 0x95, 0x04, /* Report Count (4), */
312348cbaa8SSimon Wood 0x81, 0x02, /* Input (Variable), */
313348cbaa8SSimon Wood 0x95, 0x01, /* Report Count (1), */
314348cbaa8SSimon Wood 0x75, 0x08, /* Report Size (8), */
315348cbaa8SSimon Wood 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
316348cbaa8SSimon Wood 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
317348cbaa8SSimon Wood 0x09, 0x01, /* Usage (01h), */
318348cbaa8SSimon Wood 0x81, 0x02, /* Input (Variable), */
319348cbaa8SSimon Wood 0x05, 0x01, /* Usage Page (Desktop), */
320348cbaa8SSimon Wood 0x09, 0x31, /* Usage (Y), */
321348cbaa8SSimon Wood 0x81, 0x02, /* Input (Variable), */
322348cbaa8SSimon Wood 0x09, 0x32, /* Usage (Z), */
323348cbaa8SSimon Wood 0x81, 0x02, /* Input (Variable), */
324348cbaa8SSimon Wood 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
325348cbaa8SSimon Wood 0x09, 0x00, /* Usage (00h), */
326348cbaa8SSimon Wood 0x81, 0x02, /* Input (Variable), */
327348cbaa8SSimon Wood 0xC0, /* End Collection, */
328348cbaa8SSimon Wood 0xA1, 0x02, /* Collection (Logical), */
329348cbaa8SSimon Wood 0x09, 0x02, /* Usage (02h), */
330348cbaa8SSimon Wood 0x95, 0x07, /* Report Count (7), */
331348cbaa8SSimon Wood 0x91, 0x02, /* Output (Variable), */
332348cbaa8SSimon Wood 0xC0, /* End Collection, */
333348cbaa8SSimon Wood 0xC0 /* End Collection */
334348cbaa8SSimon Wood };
335348cbaa8SSimon Wood
336560bea30SSimon Wood static __u8 ffg_rdesc_fixed[] = {
337560bea30SSimon Wood 0x05, 0x01, /* Usage Page (Desktop), */
338560bea30SSimon Wood 0x09, 0x04, /* Usage (Joystik), */
339560bea30SSimon Wood 0xA1, 0x01, /* Collection (Application), */
340560bea30SSimon Wood 0xA1, 0x02, /* Collection (Logical), */
341560bea30SSimon Wood 0x95, 0x01, /* Report Count (1), */
342560bea30SSimon Wood 0x75, 0x0A, /* Report Size (10), */
343560bea30SSimon Wood 0x15, 0x00, /* Logical Minimum (0), */
344560bea30SSimon Wood 0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
345560bea30SSimon Wood 0x35, 0x00, /* Physical Minimum (0), */
346560bea30SSimon Wood 0x46, 0xFF, 0x03, /* Physical Maximum (1023), */
347560bea30SSimon Wood 0x09, 0x30, /* Usage (X), */
348560bea30SSimon Wood 0x81, 0x02, /* Input (Variable), */
349560bea30SSimon Wood 0x95, 0x06, /* Report Count (6), */
350560bea30SSimon Wood 0x75, 0x01, /* Report Size (1), */
351560bea30SSimon Wood 0x25, 0x01, /* Logical Maximum (1), */
352560bea30SSimon Wood 0x45, 0x01, /* Physical Maximum (1), */
353560bea30SSimon Wood 0x05, 0x09, /* Usage Page (Button), */
354560bea30SSimon Wood 0x19, 0x01, /* Usage Minimum (01h), */
355560bea30SSimon Wood 0x29, 0x06, /* Usage Maximum (06h), */
356560bea30SSimon Wood 0x81, 0x02, /* Input (Variable), */
357560bea30SSimon Wood 0x95, 0x01, /* Report Count (1), */
358560bea30SSimon Wood 0x75, 0x08, /* Report Size (8), */
359560bea30SSimon Wood 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
360560bea30SSimon Wood 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
361560bea30SSimon Wood 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
362560bea30SSimon Wood 0x09, 0x01, /* Usage (01h), */
363560bea30SSimon Wood 0x81, 0x02, /* Input (Variable), */
364560bea30SSimon Wood 0x05, 0x01, /* Usage Page (Desktop), */
365560bea30SSimon Wood 0x81, 0x01, /* Input (Constant), */
366560bea30SSimon Wood 0x09, 0x31, /* Usage (Y), */
367560bea30SSimon Wood 0x81, 0x02, /* Input (Variable), */
368560bea30SSimon Wood 0x09, 0x32, /* Usage (Z), */
369560bea30SSimon Wood 0x81, 0x02, /* Input (Variable), */
370560bea30SSimon Wood 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
371560bea30SSimon Wood 0x09, 0x01, /* Usage (01h), */
372560bea30SSimon Wood 0x81, 0x02, /* Input (Variable), */
373560bea30SSimon Wood 0xC0, /* End Collection, */
374560bea30SSimon Wood 0xA1, 0x02, /* Collection (Logical), */
375560bea30SSimon Wood 0x09, 0x02, /* Usage (02h), */
376560bea30SSimon Wood 0x95, 0x07, /* Report Count (7), */
377560bea30SSimon Wood 0x91, 0x02, /* Output (Variable), */
378560bea30SSimon Wood 0xC0, /* End Collection, */
379560bea30SSimon Wood 0xC0 /* End Collection */
380560bea30SSimon Wood };
381560bea30SSimon Wood
382e41b3cdaSJarrad Whitaker static __u8 fg_rdesc_fixed[] = {
383e41b3cdaSJarrad Whitaker 0x05, 0x01, /* Usage Page (Desktop), */
384e41b3cdaSJarrad Whitaker 0x09, 0x04, /* Usage (Joystik), */
385e41b3cdaSJarrad Whitaker 0xA1, 0x01, /* Collection (Application), */
386e41b3cdaSJarrad Whitaker 0xA1, 0x02, /* Collection (Logical), */
387e41b3cdaSJarrad Whitaker 0x15, 0x00, /* Logical Minimum (0), */
388e41b3cdaSJarrad Whitaker 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
389e41b3cdaSJarrad Whitaker 0x35, 0x00, /* Physical Minimum (0), */
390e41b3cdaSJarrad Whitaker 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
391e41b3cdaSJarrad Whitaker 0x75, 0x08, /* Report Size (8), */
392e41b3cdaSJarrad Whitaker 0x95, 0x01, /* Report Count (1), */
393e41b3cdaSJarrad Whitaker 0x09, 0x30, /* Usage (X), */
394e41b3cdaSJarrad Whitaker 0x81, 0x02, /* Input (Variable), */
395e41b3cdaSJarrad Whitaker 0xA4, /* Push, */
396e41b3cdaSJarrad Whitaker 0x25, 0x01, /* Logical Maximum (1), */
397e41b3cdaSJarrad Whitaker 0x45, 0x01, /* Physical Maximum (1), */
398e41b3cdaSJarrad Whitaker 0x75, 0x01, /* Report Size (1), */
399e41b3cdaSJarrad Whitaker 0x95, 0x02, /* Report Count (2), */
400e41b3cdaSJarrad Whitaker 0x81, 0x01, /* Input (Constant), */
401e41b3cdaSJarrad Whitaker 0x95, 0x06, /* Report Count (6), */
402e41b3cdaSJarrad Whitaker 0x05, 0x09, /* Usage Page (Button), */
403e41b3cdaSJarrad Whitaker 0x19, 0x01, /* Usage Minimum (01h), */
404e41b3cdaSJarrad Whitaker 0x29, 0x06, /* Usage Maximum (06h), */
405e41b3cdaSJarrad Whitaker 0x81, 0x02, /* Input (Variable), */
406e41b3cdaSJarrad Whitaker 0x05, 0x01, /* Usage Page (Desktop), */
407e41b3cdaSJarrad Whitaker 0xB4, /* Pop, */
408e41b3cdaSJarrad Whitaker 0x81, 0x02, /* Input (Constant), */
409e41b3cdaSJarrad Whitaker 0x09, 0x31, /* Usage (Y), */
410e41b3cdaSJarrad Whitaker 0x81, 0x02, /* Input (Variable), */
411e41b3cdaSJarrad Whitaker 0x09, 0x32, /* Usage (Z), */
412e41b3cdaSJarrad Whitaker 0x81, 0x02, /* Input (Variable), */
413e41b3cdaSJarrad Whitaker 0xC0, /* End Collection, */
414e41b3cdaSJarrad Whitaker 0xA1, 0x02, /* Collection (Logical), */
415e41b3cdaSJarrad Whitaker 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
416e41b3cdaSJarrad Whitaker 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
417e41b3cdaSJarrad Whitaker 0x75, 0x08, /* Report Size (8), */
418e41b3cdaSJarrad Whitaker 0x95, 0x04, /* Report Count (4), */
419e41b3cdaSJarrad Whitaker 0x09, 0x02, /* Usage (02h), */
420e41b3cdaSJarrad Whitaker 0xB1, 0x02, /* Feature (Variable), */
421e41b3cdaSJarrad Whitaker 0xC0, /* End Collection, */
422e41b3cdaSJarrad Whitaker 0xC0 /* End Collection, */
423e41b3cdaSJarrad Whitaker };
424e41b3cdaSJarrad Whitaker
425606bd0a8SJiri Slaby /*
426606bd0a8SJiri Slaby * Certain Logitech keyboards send in report #3 keys which are far
427606bd0a8SJiri Slaby * above the logical maximum described in descriptor. This extends
428606bd0a8SJiri Slaby * the original value of 0x28c of logical maximum to 0x104d
429606bd0a8SJiri Slaby */
lg_report_fixup(struct hid_device * hdev,__u8 * rdesc,unsigned int * rsize)43073e4008dSNikolai Kondrashov static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
43173e4008dSNikolai Kondrashov unsigned int *rsize)
432606bd0a8SJiri Slaby {
43325751553SAxel Lin struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
434606bd0a8SJiri Slaby
4354ab25786SJiri Kosina if ((drv_data->quirks & LG_RDESC) && *rsize >= 91 && rdesc[83] == 0x26 &&
436606bd0a8SJiri Slaby rdesc[84] == 0x8c && rdesc[85] == 0x02) {
4374291ee30SJoe Perches hid_info(hdev,
4384291ee30SJoe Perches "fixing up Logitech keyboard report descriptor\n");
439606bd0a8SJiri Slaby rdesc[84] = rdesc[89] = 0x4d;
440606bd0a8SJiri Slaby rdesc[85] = rdesc[90] = 0x10;
441606bd0a8SJiri Slaby }
4424ab25786SJiri Kosina if ((drv_data->quirks & LG_RDESC_REL_ABS) && *rsize >= 51 &&
44324985cf6SJiri Kosina rdesc[32] == 0x81 && rdesc[33] == 0x06 &&
44424985cf6SJiri Kosina rdesc[49] == 0x81 && rdesc[50] == 0x06) {
4454291ee30SJoe Perches hid_info(hdev,
4464291ee30SJoe Perches "fixing up rel/abs in Logitech report descriptor\n");
44724985cf6SJiri Kosina rdesc[33] = rdesc[50] = 0x02;
44824985cf6SJiri Kosina }
449dc0a4f0cSMichael Bauer
450dc0a4f0cSMichael Bauer switch (hdev->product) {
45154bfe3f0SPaul Sbarra
452e41b3cdaSJarrad Whitaker case USB_DEVICE_ID_LOGITECH_WINGMAN_FG:
453e41b3cdaSJarrad Whitaker if (*rsize == FG_RDESC_ORIG_SIZE) {
454e41b3cdaSJarrad Whitaker hid_info(hdev,
455e41b3cdaSJarrad Whitaker "fixing up Logitech Wingman Formula GP report descriptor\n");
456e41b3cdaSJarrad Whitaker rdesc = fg_rdesc_fixed;
457e41b3cdaSJarrad Whitaker *rsize = sizeof(fg_rdesc_fixed);
458e41b3cdaSJarrad Whitaker } else {
459e41b3cdaSJarrad Whitaker hid_info(hdev,
460e41b3cdaSJarrad Whitaker "rdesc size test failed for formula gp\n");
461e41b3cdaSJarrad Whitaker }
462e41b3cdaSJarrad Whitaker break;
463e41b3cdaSJarrad Whitaker
464e41b3cdaSJarrad Whitaker
465560bea30SSimon Wood case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
466560bea30SSimon Wood if (*rsize == FFG_RDESC_ORIG_SIZE) {
467560bea30SSimon Wood hid_info(hdev,
468560bea30SSimon Wood "fixing up Logitech Wingman Formula Force GP report descriptor\n");
469560bea30SSimon Wood rdesc = ffg_rdesc_fixed;
470560bea30SSimon Wood *rsize = sizeof(ffg_rdesc_fixed);
471560bea30SSimon Wood }
472560bea30SSimon Wood break;
473560bea30SSimon Wood
47454bfe3f0SPaul Sbarra /* Several wheels report as this id when operating in emulation mode. */
47554bfe3f0SPaul Sbarra case USB_DEVICE_ID_LOGITECH_WHEEL:
4767363b25aSSimon Wood if (*rsize == DF_RDESC_ORIG_SIZE) {
47754bfe3f0SPaul Sbarra hid_info(hdev,
47854bfe3f0SPaul Sbarra "fixing up Logitech Driving Force report descriptor\n");
47954bfe3f0SPaul Sbarra rdesc = df_rdesc_fixed;
48054bfe3f0SPaul Sbarra *rsize = sizeof(df_rdesc_fixed);
48154bfe3f0SPaul Sbarra }
48254bfe3f0SPaul Sbarra break;
48354bfe3f0SPaul Sbarra
484270baef1SSimon Wood case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
485270baef1SSimon Wood if (*rsize == MOMO_RDESC_ORIG_SIZE) {
486270baef1SSimon Wood hid_info(hdev,
487270baef1SSimon Wood "fixing up Logitech Momo Force (Red) report descriptor\n");
488270baef1SSimon Wood rdesc = momo_rdesc_fixed;
489270baef1SSimon Wood *rsize = sizeof(momo_rdesc_fixed);
490270baef1SSimon Wood }
491270baef1SSimon Wood break;
492270baef1SSimon Wood
493348cbaa8SSimon Wood case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
494348cbaa8SSimon Wood if (*rsize == MOMO2_RDESC_ORIG_SIZE) {
495348cbaa8SSimon Wood hid_info(hdev,
496348cbaa8SSimon Wood "fixing up Logitech Momo Racing Force (Black) report descriptor\n");
497348cbaa8SSimon Wood rdesc = momo2_rdesc_fixed;
498348cbaa8SSimon Wood *rsize = sizeof(momo2_rdesc_fixed);
499348cbaa8SSimon Wood }
500348cbaa8SSimon Wood break;
501348cbaa8SSimon Wood
5027f505470SSimon Wood case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL:
5037f505470SSimon Wood if (*rsize == FV_RDESC_ORIG_SIZE) {
5047f505470SSimon Wood hid_info(hdev,
5057f505470SSimon Wood "fixing up Logitech Formula Vibration report descriptor\n");
5067f505470SSimon Wood rdesc = fv_rdesc_fixed;
5077f505470SSimon Wood *rsize = sizeof(fv_rdesc_fixed);
5087f505470SSimon Wood }
5097f505470SSimon Wood break;
5107f505470SSimon Wood
511dc0a4f0cSMichael Bauer case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
512dc0a4f0cSMichael Bauer if (*rsize == DFP_RDESC_ORIG_SIZE) {
513dc0a4f0cSMichael Bauer hid_info(hdev,
514dc0a4f0cSMichael Bauer "fixing up Logitech Driving Force Pro report descriptor\n");
515dc0a4f0cSMichael Bauer rdesc = dfp_rdesc_fixed;
516dc0a4f0cSMichael Bauer *rsize = sizeof(dfp_rdesc_fixed);
517dc0a4f0cSMichael Bauer }
518dc0a4f0cSMichael Bauer break;
519b5836246SSimon Wood
520b5836246SSimon Wood case USB_DEVICE_ID_LOGITECH_WII_WHEEL:
521b5836246SSimon Wood if (*rsize >= 101 && rdesc[41] == 0x95 && rdesc[42] == 0x0B &&
522b5836246SSimon Wood rdesc[47] == 0x05 && rdesc[48] == 0x09) {
523b5836246SSimon Wood hid_info(hdev, "fixing up Logitech Speed Force Wireless report descriptor\n");
524b5836246SSimon Wood rdesc[41] = 0x05;
525b5836246SSimon Wood rdesc[42] = 0x09;
526b5836246SSimon Wood rdesc[47] = 0x95;
527b5836246SSimon Wood rdesc[48] = 0x0B;
528b5836246SSimon Wood }
529b5836246SSimon Wood break;
530dc0a4f0cSMichael Bauer }
531dc0a4f0cSMichael Bauer
53273e4008dSNikolai Kondrashov return rdesc;
533606bd0a8SJiri Slaby }
534606bd0a8SJiri Slaby
535606bd0a8SJiri Slaby #define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
536606bd0a8SJiri Slaby EV_KEY, (c))
537606bd0a8SJiri Slaby
lg_ultrax_remote_mapping(struct hid_input * hi,struct hid_usage * usage,unsigned long ** bit,int * max)538606bd0a8SJiri Slaby static int lg_ultrax_remote_mapping(struct hid_input *hi,
539606bd0a8SJiri Slaby struct hid_usage *usage, unsigned long **bit, int *max)
540606bd0a8SJiri Slaby {
541606bd0a8SJiri Slaby if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
542606bd0a8SJiri Slaby return 0;
543606bd0a8SJiri Slaby
544606bd0a8SJiri Slaby set_bit(EV_REP, hi->input->evbit);
545606bd0a8SJiri Slaby switch (usage->hid & HID_USAGE) {
546606bd0a8SJiri Slaby /* Reported on Logitech Ultra X Media Remote */
547606bd0a8SJiri Slaby case 0x004: lg_map_key_clear(KEY_AGAIN); break;
548606bd0a8SJiri Slaby case 0x00d: lg_map_key_clear(KEY_HOME); break;
549606bd0a8SJiri Slaby case 0x024: lg_map_key_clear(KEY_SHUFFLE); break;
550606bd0a8SJiri Slaby case 0x025: lg_map_key_clear(KEY_TV); break;
551606bd0a8SJiri Slaby case 0x026: lg_map_key_clear(KEY_MENU); break;
552606bd0a8SJiri Slaby case 0x031: lg_map_key_clear(KEY_AUDIO); break;
553606bd0a8SJiri Slaby case 0x032: lg_map_key_clear(KEY_TEXT); break;
554606bd0a8SJiri Slaby case 0x033: lg_map_key_clear(KEY_LAST); break;
555606bd0a8SJiri Slaby case 0x047: lg_map_key_clear(KEY_MP3); break;
556606bd0a8SJiri Slaby case 0x048: lg_map_key_clear(KEY_DVD); break;
557606bd0a8SJiri Slaby case 0x049: lg_map_key_clear(KEY_MEDIA); break;
558606bd0a8SJiri Slaby case 0x04a: lg_map_key_clear(KEY_VIDEO); break;
559606bd0a8SJiri Slaby case 0x04b: lg_map_key_clear(KEY_ANGLE); break;
560606bd0a8SJiri Slaby case 0x04c: lg_map_key_clear(KEY_LANGUAGE); break;
561606bd0a8SJiri Slaby case 0x04d: lg_map_key_clear(KEY_SUBTITLE); break;
562606bd0a8SJiri Slaby case 0x051: lg_map_key_clear(KEY_RED); break;
563606bd0a8SJiri Slaby case 0x052: lg_map_key_clear(KEY_CLOSE); break;
564606bd0a8SJiri Slaby
565606bd0a8SJiri Slaby default:
566606bd0a8SJiri Slaby return 0;
567606bd0a8SJiri Slaby }
568606bd0a8SJiri Slaby return 1;
569606bd0a8SJiri Slaby }
570606bd0a8SJiri Slaby
lg_wireless_mapping(struct hid_input * hi,struct hid_usage * usage,unsigned long ** bit,int * max)571606bd0a8SJiri Slaby static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
572606bd0a8SJiri Slaby unsigned long **bit, int *max)
573606bd0a8SJiri Slaby {
574606bd0a8SJiri Slaby if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
575606bd0a8SJiri Slaby return 0;
576606bd0a8SJiri Slaby
577606bd0a8SJiri Slaby switch (usage->hid & HID_USAGE) {
578606bd0a8SJiri Slaby case 0x1001: lg_map_key_clear(KEY_MESSENGER); break;
579606bd0a8SJiri Slaby case 0x1003: lg_map_key_clear(KEY_SOUND); break;
580606bd0a8SJiri Slaby case 0x1004: lg_map_key_clear(KEY_VIDEO); break;
581606bd0a8SJiri Slaby case 0x1005: lg_map_key_clear(KEY_AUDIO); break;
582606bd0a8SJiri Slaby case 0x100a: lg_map_key_clear(KEY_DOCUMENTS); break;
58318392212SLorenzo Castelli /* The following two entries are Playlist 1 and 2 on the MX3200 */
58418392212SLorenzo Castelli case 0x100f: lg_map_key_clear(KEY_FN_1); break;
58518392212SLorenzo Castelli case 0x1010: lg_map_key_clear(KEY_FN_2); break;
586606bd0a8SJiri Slaby case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG); break;
587606bd0a8SJiri Slaby case 0x1012: lg_map_key_clear(KEY_NEXTSONG); break;
588606bd0a8SJiri Slaby case 0x1013: lg_map_key_clear(KEY_CAMERA); break;
589606bd0a8SJiri Slaby case 0x1014: lg_map_key_clear(KEY_MESSENGER); break;
590606bd0a8SJiri Slaby case 0x1015: lg_map_key_clear(KEY_RECORD); break;
591606bd0a8SJiri Slaby case 0x1016: lg_map_key_clear(KEY_PLAYER); break;
592606bd0a8SJiri Slaby case 0x1017: lg_map_key_clear(KEY_EJECTCD); break;
593606bd0a8SJiri Slaby case 0x1018: lg_map_key_clear(KEY_MEDIA); break;
594606bd0a8SJiri Slaby case 0x1019: lg_map_key_clear(KEY_PROG1); break;
595606bd0a8SJiri Slaby case 0x101a: lg_map_key_clear(KEY_PROG2); break;
596606bd0a8SJiri Slaby case 0x101b: lg_map_key_clear(KEY_PROG3); break;
59718392212SLorenzo Castelli case 0x101c: lg_map_key_clear(KEY_CYCLEWINDOWS); break;
598606bd0a8SJiri Slaby case 0x101f: lg_map_key_clear(KEY_ZOOMIN); break;
599606bd0a8SJiri Slaby case 0x1020: lg_map_key_clear(KEY_ZOOMOUT); break;
600606bd0a8SJiri Slaby case 0x1021: lg_map_key_clear(KEY_ZOOMRESET); break;
601606bd0a8SJiri Slaby case 0x1023: lg_map_key_clear(KEY_CLOSE); break;
602606bd0a8SJiri Slaby case 0x1027: lg_map_key_clear(KEY_MENU); break;
603606bd0a8SJiri Slaby /* this one is marked as 'Rotate' */
604606bd0a8SJiri Slaby case 0x1028: lg_map_key_clear(KEY_ANGLE); break;
605606bd0a8SJiri Slaby case 0x1029: lg_map_key_clear(KEY_SHUFFLE); break;
606606bd0a8SJiri Slaby case 0x102a: lg_map_key_clear(KEY_BACK); break;
607606bd0a8SJiri Slaby case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS); break;
60818392212SLorenzo Castelli case 0x102d: lg_map_key_clear(KEY_WWW); break;
60918392212SLorenzo Castelli /* The following two are 'Start/answer call' and 'End/reject call'
61018392212SLorenzo Castelli on the MX3200 */
61118392212SLorenzo Castelli case 0x1031: lg_map_key_clear(KEY_OK); break;
61218392212SLorenzo Castelli case 0x1032: lg_map_key_clear(KEY_CANCEL); break;
613606bd0a8SJiri Slaby case 0x1041: lg_map_key_clear(KEY_BATTERY); break;
614606bd0a8SJiri Slaby case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR); break;
615606bd0a8SJiri Slaby case 0x1043: lg_map_key_clear(KEY_SPREADSHEET); break;
616606bd0a8SJiri Slaby case 0x1044: lg_map_key_clear(KEY_PRESENTATION); break;
617606bd0a8SJiri Slaby case 0x1045: lg_map_key_clear(KEY_UNDO); break;
618606bd0a8SJiri Slaby case 0x1046: lg_map_key_clear(KEY_REDO); break;
619606bd0a8SJiri Slaby case 0x1047: lg_map_key_clear(KEY_PRINT); break;
620606bd0a8SJiri Slaby case 0x1048: lg_map_key_clear(KEY_SAVE); break;
621606bd0a8SJiri Slaby case 0x1049: lg_map_key_clear(KEY_PROG1); break;
622606bd0a8SJiri Slaby case 0x104a: lg_map_key_clear(KEY_PROG2); break;
623606bd0a8SJiri Slaby case 0x104b: lg_map_key_clear(KEY_PROG3); break;
624606bd0a8SJiri Slaby case 0x104c: lg_map_key_clear(KEY_PROG4); break;
625606bd0a8SJiri Slaby
626606bd0a8SJiri Slaby default:
627606bd0a8SJiri Slaby return 0;
628606bd0a8SJiri Slaby }
629606bd0a8SJiri Slaby return 1;
630606bd0a8SJiri Slaby }
631606bd0a8SJiri Slaby
lg_input_mapping(struct hid_device * hdev,struct hid_input * hi,struct hid_field * field,struct hid_usage * usage,unsigned long ** bit,int * max)632606bd0a8SJiri Slaby static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
633606bd0a8SJiri Slaby struct hid_field *field, struct hid_usage *usage,
634606bd0a8SJiri Slaby unsigned long **bit, int *max)
635606bd0a8SJiri Slaby {
636606bd0a8SJiri Slaby /* extended mapping for certain Logitech hardware (Logitech cordless
637606bd0a8SJiri Slaby desktop LX500) */
638606bd0a8SJiri Slaby static const u8 e_keymap[] = {
639606bd0a8SJiri Slaby 0,216, 0,213,175,156, 0, 0, 0, 0,
640606bd0a8SJiri Slaby 144, 0, 0, 0, 0, 0, 0, 0, 0,212,
641606bd0a8SJiri Slaby 174,167,152,161,112, 0, 0, 0,154, 0,
642606bd0a8SJiri Slaby 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
643606bd0a8SJiri Slaby 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
644606bd0a8SJiri Slaby 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
645606bd0a8SJiri Slaby 0, 0, 0, 0, 0,183,184,185,186,187,
646606bd0a8SJiri Slaby 188,189,190,191,192,193,194, 0, 0, 0
647606bd0a8SJiri Slaby };
64825751553SAxel Lin struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
649606bd0a8SJiri Slaby unsigned int hid = usage->hid;
650606bd0a8SJiri Slaby
651606bd0a8SJiri Slaby if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER &&
652606bd0a8SJiri Slaby lg_ultrax_remote_mapping(hi, usage, bit, max))
653606bd0a8SJiri Slaby return 1;
654606bd0a8SJiri Slaby
6558577dbf9SMichal Malý if ((drv_data->quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
656606bd0a8SJiri Slaby return 1;
657606bd0a8SJiri Slaby
658606bd0a8SJiri Slaby if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
659606bd0a8SJiri Slaby return 0;
660606bd0a8SJiri Slaby
661606bd0a8SJiri Slaby hid &= HID_USAGE;
662606bd0a8SJiri Slaby
663606bd0a8SJiri Slaby /* Special handling for Logitech Cordless Desktop */
664606bd0a8SJiri Slaby if (field->application == HID_GD_MOUSE) {
6658577dbf9SMichal Malý if ((drv_data->quirks & LG_IGNORE_DOUBLED_WHEEL) &&
666606bd0a8SJiri Slaby (hid == 7 || hid == 8))
667606bd0a8SJiri Slaby return -1;
668606bd0a8SJiri Slaby } else {
6698577dbf9SMichal Malý if ((drv_data->quirks & LG_EXPANDED_KEYMAP) &&
670606bd0a8SJiri Slaby hid < ARRAY_SIZE(e_keymap) &&
671606bd0a8SJiri Slaby e_keymap[hid] != 0) {
672606bd0a8SJiri Slaby hid_map_usage(hi, usage, bit, max, EV_KEY,
673606bd0a8SJiri Slaby e_keymap[hid]);
674606bd0a8SJiri Slaby return 1;
675606bd0a8SJiri Slaby }
676606bd0a8SJiri Slaby }
677606bd0a8SJiri Slaby
678606bd0a8SJiri Slaby return 0;
679606bd0a8SJiri Slaby }
680606bd0a8SJiri Slaby
lg_input_mapped(struct hid_device * hdev,struct hid_input * hi,struct hid_field * field,struct hid_usage * usage,unsigned long ** bit,int * max)681606bd0a8SJiri Slaby static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
682606bd0a8SJiri Slaby struct hid_field *field, struct hid_usage *usage,
683606bd0a8SJiri Slaby unsigned long **bit, int *max)
684606bd0a8SJiri Slaby {
68525751553SAxel Lin struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
686606bd0a8SJiri Slaby
6878577dbf9SMichal Malý if ((drv_data->quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
688606bd0a8SJiri Slaby (field->flags & HID_MAIN_ITEM_RELATIVE))
689606bd0a8SJiri Slaby field->flags &= ~HID_MAIN_ITEM_RELATIVE;
690606bd0a8SJiri Slaby
6918577dbf9SMichal Malý if ((drv_data->quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY ||
692606bd0a8SJiri Slaby usage->type == EV_REL || usage->type == EV_ABS))
693606bd0a8SJiri Slaby clear_bit(usage->code, *bit);
694606bd0a8SJiri Slaby
69594b3f712SSimon Wood /* Ensure that Logitech wheels are not given a default fuzz/flat value */
69694b3f712SSimon Wood if (usage->type == EV_ABS && (usage->code == ABS_X ||
69794b3f712SSimon Wood usage->code == ABS_Y || usage->code == ABS_Z ||
69894b3f712SSimon Wood usage->code == ABS_RZ)) {
69994b3f712SSimon Wood switch (hdev->product) {
70029fae1c8SSimon Wood case USB_DEVICE_ID_LOGITECH_G29_WHEEL:
701e41b3cdaSJarrad Whitaker case USB_DEVICE_ID_LOGITECH_WINGMAN_FG:
702560bea30SSimon Wood case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
70394b3f712SSimon Wood case USB_DEVICE_ID_LOGITECH_WHEEL:
70494b3f712SSimon Wood case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
70594b3f712SSimon Wood case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
70694b3f712SSimon Wood case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
70794b3f712SSimon Wood case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
70894b3f712SSimon Wood case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
70994b3f712SSimon Wood case USB_DEVICE_ID_LOGITECH_WII_WHEEL:
71094b3f712SSimon Wood case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
711bd04363dSElias Vanderstuyft case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL:
71294b3f712SSimon Wood field->application = HID_GD_MULTIAXIS;
71394b3f712SSimon Wood break;
71494b3f712SSimon Wood default:
71594b3f712SSimon Wood break;
71694b3f712SSimon Wood }
71794b3f712SSimon Wood }
71894b3f712SSimon Wood
719606bd0a8SJiri Slaby return 0;
720606bd0a8SJiri Slaby }
721606bd0a8SJiri Slaby
lg_event(struct hid_device * hdev,struct hid_field * field,struct hid_usage * usage,__s32 value)722606bd0a8SJiri Slaby static int lg_event(struct hid_device *hdev, struct hid_field *field,
723606bd0a8SJiri Slaby struct hid_usage *usage, __s32 value)
724606bd0a8SJiri Slaby {
72525751553SAxel Lin struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
726606bd0a8SJiri Slaby
7278577dbf9SMichal Malý if ((drv_data->quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
728606bd0a8SJiri Slaby input_event(field->hidinput->input, usage->type, usage->code,
729606bd0a8SJiri Slaby -value);
730606bd0a8SJiri Slaby return 1;
731606bd0a8SJiri Slaby }
7322b24a960SMichal Malý if (drv_data->quirks & LG_FF4) {
7332b24a960SMichal Malý return lg4ff_adjust_input_event(hdev, field, usage, value, drv_data);
7342b24a960SMichal Malý }
735606bd0a8SJiri Slaby
736606bd0a8SJiri Slaby return 0;
737606bd0a8SJiri Slaby }
738606bd0a8SJiri Slaby
lg_raw_event(struct hid_device * hdev,struct hid_report * report,u8 * rd,int size)739c832f86eSSimon Wood static int lg_raw_event(struct hid_device *hdev, struct hid_report *report,
740c832f86eSSimon Wood u8 *rd, int size)
741c832f86eSSimon Wood {
742c832f86eSSimon Wood struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
743c832f86eSSimon Wood
744c832f86eSSimon Wood if (drv_data->quirks & LG_FF4)
745c832f86eSSimon Wood return lg4ff_raw_event(hdev, report, rd, size, drv_data);
746c832f86eSSimon Wood
747c832f86eSSimon Wood return 0;
748c832f86eSSimon Wood }
749c832f86eSSimon Wood
lg_probe(struct hid_device * hdev,const struct hid_device_id * id)750606bd0a8SJiri Slaby static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
751606bd0a8SJiri Slaby {
752*93020953SGreg Kroah-Hartman struct usb_interface *iface;
753*93020953SGreg Kroah-Hartman __u8 iface_num;
754606bd0a8SJiri Slaby unsigned int connect_mask = HID_CONNECT_DEFAULT;
7558577dbf9SMichal Malý struct lg_drv_data *drv_data;
756606bd0a8SJiri Slaby int ret;
757606bd0a8SJiri Slaby
758*93020953SGreg Kroah-Hartman if (!hid_is_usb(hdev))
759*93020953SGreg Kroah-Hartman return -EINVAL;
760*93020953SGreg Kroah-Hartman
761*93020953SGreg Kroah-Hartman iface = to_usb_interface(hdev->dev.parent);
762*93020953SGreg Kroah-Hartman iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
763*93020953SGreg Kroah-Hartman
7642078665aSBenjamin Tissoires /* G29 only work with the 1st interface */
7652078665aSBenjamin Tissoires if ((hdev->product == USB_DEVICE_ID_LOGITECH_G29_WHEEL) &&
7662078665aSBenjamin Tissoires (iface_num != 0)) {
76729fae1c8SSimon Wood dbg_hid("%s: ignoring ifnum %d\n", __func__, iface_num);
76829fae1c8SSimon Wood return -ENODEV;
76929fae1c8SSimon Wood }
77029fae1c8SSimon Wood
7718577dbf9SMichal Malý drv_data = kzalloc(sizeof(struct lg_drv_data), GFP_KERNEL);
7728577dbf9SMichal Malý if (!drv_data) {
7738577dbf9SMichal Malý hid_err(hdev, "Insufficient memory, cannot allocate driver data\n");
7748577dbf9SMichal Malý return -ENOMEM;
7758577dbf9SMichal Malý }
7768577dbf9SMichal Malý drv_data->quirks = id->driver_data;
777606bd0a8SJiri Slaby
7788577dbf9SMichal Malý hid_set_drvdata(hdev, (void *)drv_data);
7798577dbf9SMichal Malý
7808577dbf9SMichal Malý if (drv_data->quirks & LG_NOGET)
781606bd0a8SJiri Slaby hdev->quirks |= HID_QUIRK_NOGET;
782606bd0a8SJiri Slaby
783606bd0a8SJiri Slaby ret = hid_parse(hdev);
784606bd0a8SJiri Slaby if (ret) {
7854291ee30SJoe Perches hid_err(hdev, "parse failed\n");
786606bd0a8SJiri Slaby goto err_free;
787606bd0a8SJiri Slaby }
788606bd0a8SJiri Slaby
7898577dbf9SMichal Malý if (drv_data->quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4))
790606bd0a8SJiri Slaby connect_mask &= ~HID_CONNECT_FF;
791606bd0a8SJiri Slaby
792606bd0a8SJiri Slaby ret = hid_hw_start(hdev, connect_mask);
793606bd0a8SJiri Slaby if (ret) {
7944291ee30SJoe Perches hid_err(hdev, "hw start failed\n");
795606bd0a8SJiri Slaby goto err_free;
796606bd0a8SJiri Slaby }
797606bd0a8SJiri Slaby
7987362cd22SMichal Malý /* Setup wireless link with Logitech Wii wheel */
7997362cd22SMichal Malý if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
80047af1cdbSColin Ian King static const unsigned char cbuf[] = {
80147af1cdbSColin Ian King 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
80247af1cdbSColin Ian King };
803061232f0SBenjamin Tissoires u8 *buf = kmemdup(cbuf, sizeof(cbuf), GFP_KERNEL);
80432c88cbcSSimon Wood
805061232f0SBenjamin Tissoires if (!buf) {
806061232f0SBenjamin Tissoires ret = -ENOMEM;
8075f924277SAlan Stern goto err_stop;
808061232f0SBenjamin Tissoires }
809061232f0SBenjamin Tissoires
810061232f0SBenjamin Tissoires ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(cbuf),
811b0dd72aaSBenjamin Tissoires HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
81232c88cbcSSimon Wood if (ret >= 0) {
81332c88cbcSSimon Wood /* insert a little delay of 10 jiffies ~ 40ms */
81432c88cbcSSimon Wood wait_queue_head_t wait;
81532c88cbcSSimon Wood init_waitqueue_head (&wait);
816e0393062SNicholas Mc Guire wait_event_interruptible_timeout(wait, 0,
817e0393062SNicholas Mc Guire msecs_to_jiffies(40));
81832c88cbcSSimon Wood
81932c88cbcSSimon Wood /* Select random Address */
82032c88cbcSSimon Wood buf[1] = 0xB2;
82132c88cbcSSimon Wood get_random_bytes(&buf[2], 2);
82232c88cbcSSimon Wood
823061232f0SBenjamin Tissoires ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(cbuf),
824b0dd72aaSBenjamin Tissoires HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
82532c88cbcSSimon Wood }
826061232f0SBenjamin Tissoires kfree(buf);
82732c88cbcSSimon Wood }
82832c88cbcSSimon Wood
8298577dbf9SMichal Malý if (drv_data->quirks & LG_FF)
8302dbf635eSMichal Malý ret = lgff_init(hdev);
831f4e1eb96SMichal Malý else if (drv_data->quirks & LG_FF2)
8322dbf635eSMichal Malý ret = lg2ff_init(hdev);
833f4e1eb96SMichal Malý else if (drv_data->quirks & LG_FF3)
8342dbf635eSMichal Malý ret = lg3ff_init(hdev);
835f4e1eb96SMichal Malý else if (drv_data->quirks & LG_FF4)
8362dbf635eSMichal Malý ret = lg4ff_init(hdev);
8372dbf635eSMichal Malý
8382dbf635eSMichal Malý if (ret)
8395f924277SAlan Stern goto err_stop;
840606bd0a8SJiri Slaby
841606bd0a8SJiri Slaby return 0;
8425f924277SAlan Stern
8435f924277SAlan Stern err_stop:
8445f924277SAlan Stern hid_hw_stop(hdev);
845606bd0a8SJiri Slaby err_free:
8468577dbf9SMichal Malý kfree(drv_data);
847606bd0a8SJiri Slaby return ret;
848606bd0a8SJiri Slaby }
849606bd0a8SJiri Slaby
lg_remove(struct hid_device * hdev)85030bb75d7SMichal Malý static void lg_remove(struct hid_device *hdev)
85130bb75d7SMichal Malý {
85225751553SAxel Lin struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
8538577dbf9SMichal Malý if (drv_data->quirks & LG_FF4)
85430bb75d7SMichal Malý lg4ff_deinit(hdev);
85530bb75d7SMichal Malý hid_hw_stop(hdev);
8568577dbf9SMichal Malý kfree(drv_data);
85730bb75d7SMichal Malý }
85830bb75d7SMichal Malý
859606bd0a8SJiri Slaby static const struct hid_device_id lg_devices[] = {
860606bd0a8SJiri Slaby { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER),
861606bd0a8SJiri Slaby .driver_data = LG_RDESC | LG_WIRELESS },
862606bd0a8SJiri Slaby
863606bd0a8SJiri Slaby { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER),
864606bd0a8SJiri Slaby .driver_data = LG_BAD_RELATIVE_KEYS },
865606bd0a8SJiri Slaby
866606bd0a8SJiri Slaby { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP),
867606bd0a8SJiri Slaby .driver_data = LG_DUPLICATE_USAGES },
868606bd0a8SJiri Slaby
869606bd0a8SJiri Slaby { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD),
870606bd0a8SJiri Slaby .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
871606bd0a8SJiri Slaby { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500),
872606bd0a8SJiri Slaby .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
873606bd0a8SJiri Slaby
874606bd0a8SJiri Slaby { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
875606bd0a8SJiri Slaby .driver_data = LG_NOGET },
87656d0c8b7SVitaly Katraew { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DUAL_ACTION),
87756d0c8b7SVitaly Katraew .driver_data = LG_NOGET },
878606bd0a8SJiri Slaby { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
8797362cd22SMichal Malý .driver_data = LG_NOGET | LG_FF4 },
880606bd0a8SJiri Slaby
8812c6118e4SHendrik Iben { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD),
8822c6118e4SHendrik Iben .driver_data = LG_FF2 },
883606bd0a8SJiri Slaby { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD),
884606bd0a8SJiri Slaby .driver_data = LG_FF },
885606bd0a8SJiri Slaby { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2),
886606bd0a8SJiri Slaby .driver_data = LG_FF },
887c873d9abSSimon Wood { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G29_WHEEL),
888c873d9abSSimon Wood .driver_data = LG_FF4 },
889606bd0a8SJiri Slaby { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D),
890606bd0a8SJiri Slaby .driver_data = LG_FF },
891606bd0a8SJiri Slaby { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
892606bd0a8SJiri Slaby .driver_data = LG_FF },
893606bd0a8SJiri Slaby { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL),
894270baef1SSimon Wood .driver_data = LG_NOGET | LG_FF4 },
895606bd0a8SJiri Slaby { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
8967362cd22SMichal Malý .driver_data = LG_FF4 },
897bd04363dSElias Vanderstuyft { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL),
898bd04363dSElias Vanderstuyft .driver_data = LG_FF2 },
899243b706dSChristophe Borivant { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
9007362cd22SMichal Malý .driver_data = LG_FF4 },
9017362cd22SMichal Malý { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL),
9027362cd22SMichal Malý .driver_data = LG_FF4 },
903fdc6807fSPeter Gundermann { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL),
9047362cd22SMichal Malý .driver_data = LG_FF4 },
9055623a24aSJiri Kosina { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL),
9067362cd22SMichal Malý .driver_data = LG_NOGET | LG_FF4 },
90732c88cbcSSimon Wood { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
90832c88cbcSSimon Wood .driver_data = LG_FF4 },
909e41b3cdaSJarrad Whitaker { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FG),
910e41b3cdaSJarrad Whitaker .driver_data = LG_NOGET },
911fd30ea8cSJiri Kosina { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
912560bea30SSimon Wood .driver_data = LG_NOGET | LG_FF4 },
913606bd0a8SJiri Slaby { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
914877a021eSArdinartsev Nikita .driver_data = LG_NOGET | LG_FF2 },
91574f292caSGary Stein { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940),
91674f292caSGary Stein .driver_data = LG_FF3 },
91724985cf6SJiri Kosina { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR),
91824985cf6SJiri Kosina .driver_data = LG_RDESC_REL_ABS },
91924985cf6SJiri Kosina { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER),
92024985cf6SJiri Kosina .driver_data = LG_RDESC_REL_ABS },
921606bd0a8SJiri Slaby { }
922606bd0a8SJiri Slaby };
92324985cf6SJiri Kosina
924606bd0a8SJiri Slaby MODULE_DEVICE_TABLE(hid, lg_devices);
925606bd0a8SJiri Slaby
926606bd0a8SJiri Slaby static struct hid_driver lg_driver = {
927606bd0a8SJiri Slaby .name = "logitech",
928606bd0a8SJiri Slaby .id_table = lg_devices,
929606bd0a8SJiri Slaby .report_fixup = lg_report_fixup,
930606bd0a8SJiri Slaby .input_mapping = lg_input_mapping,
931606bd0a8SJiri Slaby .input_mapped = lg_input_mapped,
932606bd0a8SJiri Slaby .event = lg_event,
933c832f86eSSimon Wood .raw_event = lg_raw_event,
934606bd0a8SJiri Slaby .probe = lg_probe,
93530bb75d7SMichal Malý .remove = lg_remove,
936606bd0a8SJiri Slaby };
937f425458eSH Hartley Sweeten module_hid_driver(lg_driver);
938606bd0a8SJiri Slaby
939a54dc779SMichal Malý #ifdef CONFIG_LOGIWHEELS_FF
940a54dc779SMichal Malý int lg4ff_no_autoswitch = 0;
941a54dc779SMichal Malý module_param_named(lg4ff_no_autoswitch, lg4ff_no_autoswitch, int, S_IRUGO);
942a54dc779SMichal Malý MODULE_PARM_DESC(lg4ff_no_autoswitch, "Do not switch multimode wheels to their native mode automatically");
943a54dc779SMichal Malý #endif
944a54dc779SMichal Malý
945606bd0a8SJiri Slaby MODULE_LICENSE("GPL");
946