xref: /linux/drivers/hid/hid-lg.c (revision 8bc7c5e525584903ea83332e18a2118ed3b1985e)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *  HID driver for some logitech "special" devices
4  *
5  *  Copyright (c) 1999 Andreas Gal
6  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
7  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
8  *  Copyright (c) 2006-2007 Jiri Kosina
9  *  Copyright (c) 2008 Jiri Slaby
10  *  Copyright (c) 2010 Hendrik Iben
11  */
12 
13 /*
14  */
15 
16 #include <linux/device.h>
17 #include <linux/hid.h>
18 #include <linux/module.h>
19 #include <linux/random.h>
20 #include <linux/sched.h>
21 #include <linux/usb.h>
22 #include <linux/wait.h>
23 
24 #include "usbhid/usbhid.h"
25 #include "hid-ids.h"
26 #include "hid-lg.h"
27 #include "hid-lg4ff.h"
28 
29 #define LG_RDESC		0x001
30 #define LG_BAD_RELATIVE_KEYS	0x002
31 #define LG_DUPLICATE_USAGES	0x004
32 #define LG_EXPANDED_KEYMAP	0x010
33 #define LG_IGNORE_DOUBLED_WHEEL	0x020
34 #define LG_WIRELESS		0x040
35 #define LG_INVERT_HWHEEL	0x080
36 #define LG_NOGET		0x100
37 #define LG_FF			0x200
38 #define LG_FF2			0x400
39 #define LG_RDESC_REL_ABS	0x800
40 #define LG_FF3			0x1000
41 #define LG_FF4			0x2000
42 
43 /* Size of the original descriptors of the Driving Force (and Pro) wheels */
44 #define DF_RDESC_ORIG_SIZE	130
45 #define DFP_RDESC_ORIG_SIZE	97
46 #define FV_RDESC_ORIG_SIZE	130
47 #define MOMO_RDESC_ORIG_SIZE	87
48 #define MOMO2_RDESC_ORIG_SIZE	87
49 #define FFG_RDESC_ORIG_SIZE	85
50 #define FG_RDESC_ORIG_SIZE	82
51 
52 /* Fixed report descriptors for Logitech Driving Force (and Pro)
53  * wheel controllers
54  *
55  * The original descriptors hide the separate throttle and brake axes in
56  * a custom vendor usage page, providing only a combined value as
57  * GenericDesktop.Y.
58  * These descriptors remove the combined Y axis and instead report
59  * separate throttle (Y) and brake (RZ).
60  */
61 static __u8 df_rdesc_fixed[] = {
62 0x05, 0x01,         /*  Usage Page (Desktop),                   */
63 0x09, 0x04,         /*  Usage (Joystick),                       */
64 0xA1, 0x01,         /*  Collection (Application),               */
65 0xA1, 0x02,         /*      Collection (Logical),               */
66 0x95, 0x01,         /*          Report Count (1),               */
67 0x75, 0x0A,         /*          Report Size (10),               */
68 0x14,               /*          Logical Minimum (0),            */
69 0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),         */
70 0x34,               /*          Physical Minimum (0),           */
71 0x46, 0xFF, 0x03,   /*          Physical Maximum (1023),        */
72 0x09, 0x30,         /*          Usage (X),                      */
73 0x81, 0x02,         /*          Input (Variable),               */
74 0x95, 0x0C,         /*          Report Count (12),              */
75 0x75, 0x01,         /*          Report Size (1),                */
76 0x25, 0x01,         /*          Logical Maximum (1),            */
77 0x45, 0x01,         /*          Physical Maximum (1),           */
78 0x05, 0x09,         /*          Usage (Buttons),                */
79 0x19, 0x01,         /*          Usage Minimum (1),              */
80 0x29, 0x0c,         /*          Usage Maximum (12),             */
81 0x81, 0x02,         /*          Input (Variable),               */
82 0x95, 0x02,         /*          Report Count (2),               */
83 0x06, 0x00, 0xFF,   /*          Usage Page (Vendor: 65280),     */
84 0x09, 0x01,         /*          Usage (?: 1),                   */
85 0x81, 0x02,         /*          Input (Variable),               */
86 0x05, 0x01,         /*          Usage Page (Desktop),           */
87 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
88 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
89 0x95, 0x01,         /*          Report Count (1),               */
90 0x75, 0x08,         /*          Report Size (8),                */
91 0x81, 0x02,         /*          Input (Variable),               */
92 0x25, 0x07,         /*          Logical Maximum (7),            */
93 0x46, 0x3B, 0x01,   /*          Physical Maximum (315),         */
94 0x75, 0x04,         /*          Report Size (4),                */
95 0x65, 0x14,         /*          Unit (Degrees),                 */
96 0x09, 0x39,         /*          Usage (Hat Switch),             */
97 0x81, 0x42,         /*          Input (Variable, Null State),   */
98 0x75, 0x01,         /*          Report Size (1),                */
99 0x95, 0x04,         /*          Report Count (4),               */
100 0x65, 0x00,         /*          Unit (none),                    */
101 0x06, 0x00, 0xFF,   /*          Usage Page (Vendor: 65280),     */
102 0x09, 0x01,         /*          Usage (?: 1),                   */
103 0x25, 0x01,         /*          Logical Maximum (1),            */
104 0x45, 0x01,         /*          Physical Maximum (1),           */
105 0x81, 0x02,         /*          Input (Variable),               */
106 0x05, 0x01,         /*          Usage Page (Desktop),           */
107 0x95, 0x01,         /*          Report Count (1),               */
108 0x75, 0x08,         /*          Report Size (8),                */
109 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
110 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
111 0x09, 0x31,         /*          Usage (Y),                      */
112 0x81, 0x02,         /*          Input (Variable),               */
113 0x09, 0x35,         /*          Usage (Rz),                     */
114 0x81, 0x02,         /*          Input (Variable),               */
115 0xC0,               /*      End Collection,                     */
116 0xA1, 0x02,         /*      Collection (Logical),               */
117 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
118 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
119 0x95, 0x07,         /*          Report Count (7),               */
120 0x75, 0x08,         /*          Report Size (8),                */
121 0x09, 0x03,         /*          Usage (?: 3),                   */
122 0x91, 0x02,         /*          Output (Variable),              */
123 0xC0,               /*      End Collection,                     */
124 0xC0                /*  End Collection                          */
125 };
126 
127 static __u8 dfp_rdesc_fixed[] = {
128 0x05, 0x01,         /*  Usage Page (Desktop),                   */
129 0x09, 0x04,         /*  Usage (Joystick),                       */
130 0xA1, 0x01,         /*  Collection (Application),               */
131 0xA1, 0x02,         /*      Collection (Logical),               */
132 0x95, 0x01,         /*          Report Count (1),               */
133 0x75, 0x0E,         /*          Report Size (14),               */
134 0x14,               /*          Logical Minimum (0),            */
135 0x26, 0xFF, 0x3F,   /*          Logical Maximum (16383),        */
136 0x34,               /*          Physical Minimum (0),           */
137 0x46, 0xFF, 0x3F,   /*          Physical Maximum (16383),       */
138 0x09, 0x30,         /*          Usage (X),                      */
139 0x81, 0x02,         /*          Input (Variable),               */
140 0x95, 0x0E,         /*          Report Count (14),              */
141 0x75, 0x01,         /*          Report Size (1),                */
142 0x25, 0x01,         /*          Logical Maximum (1),            */
143 0x45, 0x01,         /*          Physical Maximum (1),           */
144 0x05, 0x09,         /*          Usage Page (Button),            */
145 0x19, 0x01,         /*          Usage Minimum (01h),            */
146 0x29, 0x0E,         /*          Usage Maximum (0Eh),            */
147 0x81, 0x02,         /*          Input (Variable),               */
148 0x05, 0x01,         /*          Usage Page (Desktop),           */
149 0x95, 0x01,         /*          Report Count (1),               */
150 0x75, 0x04,         /*          Report Size (4),                */
151 0x25, 0x07,         /*          Logical Maximum (7),            */
152 0x46, 0x3B, 0x01,   /*          Physical Maximum (315),         */
153 0x65, 0x14,         /*          Unit (Degrees),                 */
154 0x09, 0x39,         /*          Usage (Hat Switch),             */
155 0x81, 0x42,         /*          Input (Variable, Nullstate),    */
156 0x65, 0x00,         /*          Unit,                           */
157 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
158 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
159 0x75, 0x08,         /*          Report Size (8),                */
160 0x81, 0x01,         /*          Input (Constant),               */
161 0x09, 0x31,         /*          Usage (Y),                      */
162 0x81, 0x02,         /*          Input (Variable),               */
163 0x09, 0x35,         /*          Usage (Rz),                     */
164 0x81, 0x02,         /*          Input (Variable),               */
165 0x81, 0x01,         /*          Input (Constant),               */
166 0xC0,               /*      End Collection,                     */
167 0xA1, 0x02,         /*      Collection (Logical),               */
168 0x09, 0x02,         /*          Usage (02h),                    */
169 0x95, 0x07,         /*          Report Count (7),               */
170 0x91, 0x02,         /*          Output (Variable),              */
171 0xC0,               /*      End Collection,                     */
172 0xC0                /*  End Collection                          */
173 };
174 
175 static __u8 fv_rdesc_fixed[] = {
176 0x05, 0x01,         /*  Usage Page (Desktop),                   */
177 0x09, 0x04,         /*  Usage (Joystick),                       */
178 0xA1, 0x01,         /*  Collection (Application),               */
179 0xA1, 0x02,         /*      Collection (Logical),               */
180 0x95, 0x01,         /*          Report Count (1),               */
181 0x75, 0x0A,         /*          Report Size (10),               */
182 0x15, 0x00,         /*          Logical Minimum (0),            */
183 0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),         */
184 0x35, 0x00,         /*          Physical Minimum (0),           */
185 0x46, 0xFF, 0x03,   /*          Physical Maximum (1023),        */
186 0x09, 0x30,         /*          Usage (X),                      */
187 0x81, 0x02,         /*          Input (Variable),               */
188 0x95, 0x0C,         /*          Report Count (12),              */
189 0x75, 0x01,         /*          Report Size (1),                */
190 0x25, 0x01,         /*          Logical Maximum (1),            */
191 0x45, 0x01,         /*          Physical Maximum (1),           */
192 0x05, 0x09,         /*          Usage Page (Button),            */
193 0x19, 0x01,         /*          Usage Minimum (01h),            */
194 0x29, 0x0C,         /*          Usage Maximum (0Ch),            */
195 0x81, 0x02,         /*          Input (Variable),               */
196 0x95, 0x02,         /*          Report Count (2),               */
197 0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),             */
198 0x09, 0x01,         /*          Usage (01h),                    */
199 0x81, 0x02,         /*          Input (Variable),               */
200 0x09, 0x02,         /*          Usage (02h),                    */
201 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
202 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
203 0x95, 0x01,         /*          Report Count (1),               */
204 0x75, 0x08,         /*          Report Size (8),                */
205 0x81, 0x02,         /*          Input (Variable),               */
206 0x05, 0x01,         /*          Usage Page (Desktop),           */
207 0x25, 0x07,         /*          Logical Maximum (7),            */
208 0x46, 0x3B, 0x01,   /*          Physical Maximum (315),         */
209 0x75, 0x04,         /*          Report Size (4),                */
210 0x65, 0x14,         /*          Unit (Degrees),                 */
211 0x09, 0x39,         /*          Usage (Hat Switch),             */
212 0x81, 0x42,         /*          Input (Variable, Null State),   */
213 0x75, 0x01,         /*          Report Size (1),                */
214 0x95, 0x04,         /*          Report Count (4),               */
215 0x65, 0x00,         /*          Unit,                           */
216 0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),             */
217 0x09, 0x01,         /*          Usage (01h),                    */
218 0x25, 0x01,         /*          Logical Maximum (1),            */
219 0x45, 0x01,         /*          Physical Maximum (1),           */
220 0x81, 0x02,         /*          Input (Variable),               */
221 0x05, 0x01,         /*          Usage Page (Desktop),           */
222 0x95, 0x01,         /*          Report Count (1),               */
223 0x75, 0x08,         /*          Report Size (8),                */
224 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
225 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
226 0x09, 0x31,         /*          Usage (Y),                      */
227 0x81, 0x02,         /*          Input (Variable),               */
228 0x09, 0x32,         /*          Usage (Z),                      */
229 0x81, 0x02,         /*          Input (Variable),               */
230 0xC0,               /*      End Collection,                     */
231 0xA1, 0x02,         /*      Collection (Logical),               */
232 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
233 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
234 0x95, 0x07,         /*          Report Count (7),               */
235 0x75, 0x08,         /*          Report Size (8),                */
236 0x09, 0x03,         /*          Usage (03h),                    */
237 0x91, 0x02,         /*          Output (Variable),              */
238 0xC0,               /*      End Collection,                     */
239 0xC0                /*  End Collection                          */
240 };
241 
242 static __u8 momo_rdesc_fixed[] = {
243 0x05, 0x01,         /*  Usage Page (Desktop),               */
244 0x09, 0x04,         /*  Usage (Joystick),                   */
245 0xA1, 0x01,         /*  Collection (Application),           */
246 0xA1, 0x02,         /*      Collection (Logical),           */
247 0x95, 0x01,         /*          Report Count (1),           */
248 0x75, 0x0A,         /*          Report Size (10),           */
249 0x15, 0x00,         /*          Logical Minimum (0),        */
250 0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
251 0x35, 0x00,         /*          Physical Minimum (0),       */
252 0x46, 0xFF, 0x03,   /*          Physical Maximum (1023),    */
253 0x09, 0x30,         /*          Usage (X),                  */
254 0x81, 0x02,         /*          Input (Variable),           */
255 0x95, 0x08,         /*          Report Count (8),           */
256 0x75, 0x01,         /*          Report Size (1),            */
257 0x25, 0x01,         /*          Logical Maximum (1),        */
258 0x45, 0x01,         /*          Physical Maximum (1),       */
259 0x05, 0x09,         /*          Usage Page (Button),        */
260 0x19, 0x01,         /*          Usage Minimum (01h),        */
261 0x29, 0x08,         /*          Usage Maximum (08h),        */
262 0x81, 0x02,         /*          Input (Variable),           */
263 0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
264 0x75, 0x0E,         /*          Report Size (14),           */
265 0x95, 0x01,         /*          Report Count (1),           */
266 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
267 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),     */
268 0x09, 0x00,         /*          Usage (00h),                */
269 0x81, 0x02,         /*          Input (Variable),           */
270 0x05, 0x01,         /*          Usage Page (Desktop),       */
271 0x75, 0x08,         /*          Report Size (8),            */
272 0x09, 0x31,         /*          Usage (Y),                  */
273 0x81, 0x02,         /*          Input (Variable),           */
274 0x09, 0x32,         /*          Usage (Z),                  */
275 0x81, 0x02,         /*          Input (Variable),           */
276 0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
277 0x09, 0x01,         /*          Usage (01h),                */
278 0x81, 0x02,         /*          Input (Variable),           */
279 0xC0,               /*      End Collection,                 */
280 0xA1, 0x02,         /*      Collection (Logical),           */
281 0x09, 0x02,         /*          Usage (02h),                */
282 0x95, 0x07,         /*          Report Count (7),           */
283 0x91, 0x02,         /*          Output (Variable),          */
284 0xC0,               /*      End Collection,                 */
285 0xC0                /*  End Collection                      */
286 };
287 
288 static __u8 momo2_rdesc_fixed[] = {
289 0x05, 0x01,         /*  Usage Page (Desktop),               */
290 0x09, 0x04,         /*  Usage (Joystick),                   */
291 0xA1, 0x01,         /*  Collection (Application),           */
292 0xA1, 0x02,         /*      Collection (Logical),           */
293 0x95, 0x01,         /*          Report Count (1),           */
294 0x75, 0x0A,         /*          Report Size (10),           */
295 0x15, 0x00,         /*          Logical Minimum (0),        */
296 0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
297 0x35, 0x00,         /*          Physical Minimum (0),       */
298 0x46, 0xFF, 0x03,   /*          Physical Maximum (1023),    */
299 0x09, 0x30,         /*          Usage (X),                  */
300 0x81, 0x02,         /*          Input (Variable),           */
301 0x95, 0x0A,         /*          Report Count (10),          */
302 0x75, 0x01,         /*          Report Size (1),            */
303 0x25, 0x01,         /*          Logical Maximum (1),        */
304 0x45, 0x01,         /*          Physical Maximum (1),       */
305 0x05, 0x09,         /*          Usage Page (Button),        */
306 0x19, 0x01,         /*          Usage Minimum (01h),        */
307 0x29, 0x0A,         /*          Usage Maximum (0Ah),        */
308 0x81, 0x02,         /*          Input (Variable),           */
309 0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
310 0x09, 0x00,         /*          Usage (00h),                */
311 0x95, 0x04,         /*          Report Count (4),           */
312 0x81, 0x02,         /*          Input (Variable),           */
313 0x95, 0x01,         /*          Report Count (1),           */
314 0x75, 0x08,         /*          Report Size (8),            */
315 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
316 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),     */
317 0x09, 0x01,         /*          Usage (01h),                */
318 0x81, 0x02,         /*          Input (Variable),           */
319 0x05, 0x01,         /*          Usage Page (Desktop),       */
320 0x09, 0x31,         /*          Usage (Y),                  */
321 0x81, 0x02,         /*          Input (Variable),           */
322 0x09, 0x32,         /*          Usage (Z),                  */
323 0x81, 0x02,         /*          Input (Variable),           */
324 0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
325 0x09, 0x00,         /*          Usage (00h),                */
326 0x81, 0x02,         /*          Input (Variable),           */
327 0xC0,               /*      End Collection,                 */
328 0xA1, 0x02,         /*      Collection (Logical),           */
329 0x09, 0x02,         /*          Usage (02h),                */
330 0x95, 0x07,         /*          Report Count (7),           */
331 0x91, 0x02,         /*          Output (Variable),          */
332 0xC0,               /*      End Collection,                 */
333 0xC0                /*  End Collection                      */
334 };
335 
336 static __u8 ffg_rdesc_fixed[] = {
337 0x05, 0x01,         /*  Usage Page (Desktop),               */
338 0x09, 0x04,         /*  Usage (Joystik),                    */
339 0xA1, 0x01,         /*  Collection (Application),           */
340 0xA1, 0x02,         /*      Collection (Logical),           */
341 0x95, 0x01,         /*          Report Count (1),           */
342 0x75, 0x0A,         /*          Report Size (10),           */
343 0x15, 0x00,         /*          Logical Minimum (0),        */
344 0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
345 0x35, 0x00,         /*          Physical Minimum (0),       */
346 0x46, 0xFF, 0x03,   /*          Physical Maximum (1023),    */
347 0x09, 0x30,         /*          Usage (X),                  */
348 0x81, 0x02,         /*          Input (Variable),           */
349 0x95, 0x06,         /*          Report Count (6),           */
350 0x75, 0x01,         /*          Report Size (1),            */
351 0x25, 0x01,         /*          Logical Maximum (1),        */
352 0x45, 0x01,         /*          Physical Maximum (1),       */
353 0x05, 0x09,         /*          Usage Page (Button),        */
354 0x19, 0x01,         /*          Usage Minimum (01h),        */
355 0x29, 0x06,         /*          Usage Maximum (06h),        */
356 0x81, 0x02,         /*          Input (Variable),           */
357 0x95, 0x01,         /*          Report Count (1),           */
358 0x75, 0x08,         /*          Report Size (8),            */
359 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
360 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),     */
361 0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
362 0x09, 0x01,         /*          Usage (01h),                */
363 0x81, 0x02,         /*          Input (Variable),           */
364 0x05, 0x01,         /*          Usage Page (Desktop),       */
365 0x81, 0x01,         /*          Input (Constant),           */
366 0x09, 0x31,         /*          Usage (Y),                  */
367 0x81, 0x02,         /*          Input (Variable),           */
368 0x09, 0x32,         /*          Usage (Z),                  */
369 0x81, 0x02,         /*          Input (Variable),           */
370 0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
371 0x09, 0x01,         /*          Usage (01h),                */
372 0x81, 0x02,         /*          Input (Variable),           */
373 0xC0,               /*      End Collection,                 */
374 0xA1, 0x02,         /*      Collection (Logical),           */
375 0x09, 0x02,         /*          Usage (02h),                */
376 0x95, 0x07,         /*          Report Count (7),           */
377 0x91, 0x02,         /*          Output (Variable),          */
378 0xC0,               /*      End Collection,                 */
379 0xC0                /*  End Collection                      */
380 };
381 
382 static __u8 fg_rdesc_fixed[] = {
383 0x05, 0x01,         /*  Usage Page (Desktop),               */
384 0x09, 0x04,         /*  Usage (Joystik),                    */
385 0xA1, 0x01,         /*  Collection (Application),           */
386 0xA1, 0x02,         /*      Collection (Logical),           */
387 0x15, 0x00,         /*          Logical Minimum (0),        */
388 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
389 0x35, 0x00,         /*          Physical Minimum (0),       */
390 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),     */
391 0x75, 0x08,         /*          Report Size (8),            */
392 0x95, 0x01,         /*          Report Count (1),           */
393 0x09, 0x30,         /*          Usage (X),                  */
394 0x81, 0x02,         /*          Input (Variable),           */
395 0xA4,               /*  Push,                               */
396 0x25, 0x01,         /*          Logical Maximum (1),        */
397 0x45, 0x01,         /*          Physical Maximum (1),       */
398 0x75, 0x01,         /*          Report Size (1),            */
399 0x95, 0x02,         /*          Report Count (2),           */
400 0x81, 0x01,         /*          Input (Constant),           */
401 0x95, 0x06,         /*          Report Count (6),           */
402 0x05, 0x09,         /*          Usage Page (Button),        */
403 0x19, 0x01,         /*          Usage Minimum (01h),        */
404 0x29, 0x06,         /*          Usage Maximum (06h),        */
405 0x81, 0x02,         /*          Input (Variable),           */
406 0x05, 0x01,         /*          Usage Page (Desktop),       */
407 0xB4,               /*  Pop,                                */
408 0x81, 0x02,         /*          Input (Constant),           */
409 0x09, 0x31,         /*          Usage (Y),                  */
410 0x81, 0x02,         /*          Input (Variable),           */
411 0x09, 0x32,         /*          Usage (Z),                  */
412 0x81, 0x02,         /*          Input (Variable),           */
413 0xC0,               /*      End Collection,                 */
414 0xA1, 0x02,         /*      Collection (Logical),           */
415 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
416 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),     */
417 0x75, 0x08,         /*          Report Size (8),            */
418 0x95, 0x04,         /*          Report Count (4),           */
419 0x09, 0x02,         /*          Usage (02h),                */
420 0xB1, 0x02,         /*          Feature (Variable),         */
421 0xC0,               /*      End Collection,                 */
422 0xC0                /*  End Collection,                     */
423 };
424 
425 /*
426  * Certain Logitech keyboards send in report #3 keys which are far
427  * above the logical maximum described in descriptor. This extends
428  * the original value of 0x28c of logical maximum to 0x104d
429  */
430 static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
431 		unsigned int *rsize)
432 {
433 	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
434 
435 	if ((drv_data->quirks & LG_RDESC) && *rsize >= 91 && rdesc[83] == 0x26 &&
436 			rdesc[84] == 0x8c && rdesc[85] == 0x02) {
437 		hid_info(hdev,
438 			 "fixing up Logitech keyboard report descriptor\n");
439 		rdesc[84] = rdesc[89] = 0x4d;
440 		rdesc[85] = rdesc[90] = 0x10;
441 	}
442 	if ((drv_data->quirks & LG_RDESC_REL_ABS) && *rsize >= 51 &&
443 			rdesc[32] == 0x81 && rdesc[33] == 0x06 &&
444 			rdesc[49] == 0x81 && rdesc[50] == 0x06) {
445 		hid_info(hdev,
446 			 "fixing up rel/abs in Logitech report descriptor\n");
447 		rdesc[33] = rdesc[50] = 0x02;
448 	}
449 
450 	switch (hdev->product) {
451 
452 	case USB_DEVICE_ID_LOGITECH_WINGMAN_FG:
453 		if (*rsize == FG_RDESC_ORIG_SIZE) {
454 			hid_info(hdev,
455 				"fixing up Logitech Wingman Formula GP report descriptor\n");
456 			rdesc = fg_rdesc_fixed;
457 			*rsize = sizeof(fg_rdesc_fixed);
458 		} else {
459 			hid_info(hdev,
460 				"rdesc size test failed for formula gp\n");
461 		}
462 		break;
463 
464 
465 	case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
466 		if (*rsize == FFG_RDESC_ORIG_SIZE) {
467 			hid_info(hdev,
468 				"fixing up Logitech Wingman Formula Force GP report descriptor\n");
469 			rdesc = ffg_rdesc_fixed;
470 			*rsize = sizeof(ffg_rdesc_fixed);
471 		}
472 		break;
473 
474 	/* Several wheels report as this id when operating in emulation mode. */
475 	case USB_DEVICE_ID_LOGITECH_WHEEL:
476 		if (*rsize == DF_RDESC_ORIG_SIZE) {
477 			hid_info(hdev,
478 				"fixing up Logitech Driving Force report descriptor\n");
479 			rdesc = df_rdesc_fixed;
480 			*rsize = sizeof(df_rdesc_fixed);
481 		}
482 		break;
483 
484 	case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
485 		if (*rsize == MOMO_RDESC_ORIG_SIZE) {
486 			hid_info(hdev,
487 				"fixing up Logitech Momo Force (Red) report descriptor\n");
488 			rdesc = momo_rdesc_fixed;
489 			*rsize = sizeof(momo_rdesc_fixed);
490 		}
491 		break;
492 
493 	case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
494 		if (*rsize == MOMO2_RDESC_ORIG_SIZE) {
495 			hid_info(hdev,
496 				"fixing up Logitech Momo Racing Force (Black) report descriptor\n");
497 			rdesc = momo2_rdesc_fixed;
498 			*rsize = sizeof(momo2_rdesc_fixed);
499 		}
500 		break;
501 
502 	case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL:
503 		if (*rsize == FV_RDESC_ORIG_SIZE) {
504 			hid_info(hdev,
505 				"fixing up Logitech Formula Vibration report descriptor\n");
506 			rdesc = fv_rdesc_fixed;
507 			*rsize = sizeof(fv_rdesc_fixed);
508 		}
509 		break;
510 
511 	case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
512 		if (*rsize == DFP_RDESC_ORIG_SIZE) {
513 			hid_info(hdev,
514 				"fixing up Logitech Driving Force Pro report descriptor\n");
515 			rdesc = dfp_rdesc_fixed;
516 			*rsize = sizeof(dfp_rdesc_fixed);
517 		}
518 		break;
519 
520 	case USB_DEVICE_ID_LOGITECH_WII_WHEEL:
521 		if (*rsize >= 101 && rdesc[41] == 0x95 && rdesc[42] == 0x0B &&
522 				rdesc[47] == 0x05 && rdesc[48] == 0x09) {
523 			hid_info(hdev, "fixing up Logitech Speed Force Wireless report descriptor\n");
524 			rdesc[41] = 0x05;
525 			rdesc[42] = 0x09;
526 			rdesc[47] = 0x95;
527 			rdesc[48] = 0x0B;
528 		}
529 		break;
530 	}
531 
532 	return rdesc;
533 }
534 
535 #define lg_map_key_clear(c)	hid_map_usage_clear(hi, usage, bit, max, \
536 		EV_KEY, (c))
537 
538 static int lg_ultrax_remote_mapping(struct hid_input *hi,
539 		struct hid_usage *usage, unsigned long **bit, int *max)
540 {
541 	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
542 		return 0;
543 
544 	set_bit(EV_REP, hi->input->evbit);
545 	switch (usage->hid & HID_USAGE) {
546 	/* Reported on Logitech Ultra X Media Remote */
547 	case 0x004: lg_map_key_clear(KEY_AGAIN);	break;
548 	case 0x00d: lg_map_key_clear(KEY_HOME);		break;
549 	case 0x024: lg_map_key_clear(KEY_SHUFFLE);	break;
550 	case 0x025: lg_map_key_clear(KEY_TV);		break;
551 	case 0x026: lg_map_key_clear(KEY_MENU);		break;
552 	case 0x031: lg_map_key_clear(KEY_AUDIO);	break;
553 	case 0x032: lg_map_key_clear(KEY_TEXT);		break;
554 	case 0x033: lg_map_key_clear(KEY_LAST);		break;
555 	case 0x047: lg_map_key_clear(KEY_MP3);		break;
556 	case 0x048: lg_map_key_clear(KEY_DVD);		break;
557 	case 0x049: lg_map_key_clear(KEY_MEDIA);	break;
558 	case 0x04a: lg_map_key_clear(KEY_VIDEO);	break;
559 	case 0x04b: lg_map_key_clear(KEY_ANGLE);	break;
560 	case 0x04c: lg_map_key_clear(KEY_LANGUAGE);	break;
561 	case 0x04d: lg_map_key_clear(KEY_SUBTITLE);	break;
562 	case 0x051: lg_map_key_clear(KEY_RED);		break;
563 	case 0x052: lg_map_key_clear(KEY_CLOSE);	break;
564 
565 	default:
566 		return 0;
567 	}
568 	return 1;
569 }
570 
571 static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
572 		unsigned long **bit, int *max)
573 {
574 	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
575 		return 0;
576 
577 	switch (usage->hid & HID_USAGE) {
578 	case 0x1001: lg_map_key_clear(KEY_MESSENGER);		break;
579 	case 0x1003: lg_map_key_clear(KEY_SOUND);		break;
580 	case 0x1004: lg_map_key_clear(KEY_VIDEO);		break;
581 	case 0x1005: lg_map_key_clear(KEY_AUDIO);		break;
582 	case 0x100a: lg_map_key_clear(KEY_DOCUMENTS);		break;
583 	/* The following two entries are Playlist 1 and 2 on the MX3200 */
584 	case 0x100f: lg_map_key_clear(KEY_FN_1);		break;
585 	case 0x1010: lg_map_key_clear(KEY_FN_2);		break;
586 	case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG);	break;
587 	case 0x1012: lg_map_key_clear(KEY_NEXTSONG);		break;
588 	case 0x1013: lg_map_key_clear(KEY_CAMERA);		break;
589 	case 0x1014: lg_map_key_clear(KEY_MESSENGER);		break;
590 	case 0x1015: lg_map_key_clear(KEY_RECORD);		break;
591 	case 0x1016: lg_map_key_clear(KEY_PLAYER);		break;
592 	case 0x1017: lg_map_key_clear(KEY_EJECTCD);		break;
593 	case 0x1018: lg_map_key_clear(KEY_MEDIA);		break;
594 	case 0x1019: lg_map_key_clear(KEY_PROG1);		break;
595 	case 0x101a: lg_map_key_clear(KEY_PROG2);		break;
596 	case 0x101b: lg_map_key_clear(KEY_PROG3);		break;
597 	case 0x101c: lg_map_key_clear(KEY_CYCLEWINDOWS);	break;
598 	case 0x101f: lg_map_key_clear(KEY_ZOOMIN);		break;
599 	case 0x1020: lg_map_key_clear(KEY_ZOOMOUT);		break;
600 	case 0x1021: lg_map_key_clear(KEY_ZOOMRESET);		break;
601 	case 0x1023: lg_map_key_clear(KEY_CLOSE);		break;
602 	case 0x1027: lg_map_key_clear(KEY_MENU);		break;
603 	/* this one is marked as 'Rotate' */
604 	case 0x1028: lg_map_key_clear(KEY_ANGLE);		break;
605 	case 0x1029: lg_map_key_clear(KEY_SHUFFLE);		break;
606 	case 0x102a: lg_map_key_clear(KEY_BACK);		break;
607 	case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS);	break;
608 	case 0x102d: lg_map_key_clear(KEY_WWW);			break;
609 	/* The following two are 'Start/answer call' and 'End/reject call'
610 	   on the MX3200 */
611 	case 0x1031: lg_map_key_clear(KEY_OK);			break;
612 	case 0x1032: lg_map_key_clear(KEY_CANCEL);		break;
613 	case 0x1041: lg_map_key_clear(KEY_BATTERY);		break;
614 	case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR);	break;
615 	case 0x1043: lg_map_key_clear(KEY_SPREADSHEET);		break;
616 	case 0x1044: lg_map_key_clear(KEY_PRESENTATION);	break;
617 	case 0x1045: lg_map_key_clear(KEY_UNDO);		break;
618 	case 0x1046: lg_map_key_clear(KEY_REDO);		break;
619 	case 0x1047: lg_map_key_clear(KEY_PRINT);		break;
620 	case 0x1048: lg_map_key_clear(KEY_SAVE);		break;
621 	case 0x1049: lg_map_key_clear(KEY_PROG1);		break;
622 	case 0x104a: lg_map_key_clear(KEY_PROG2);		break;
623 	case 0x104b: lg_map_key_clear(KEY_PROG3);		break;
624 	case 0x104c: lg_map_key_clear(KEY_PROG4);		break;
625 
626 	default:
627 		return 0;
628 	}
629 	return 1;
630 }
631 
632 static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
633 		struct hid_field *field, struct hid_usage *usage,
634 		unsigned long **bit, int *max)
635 {
636 	/* extended mapping for certain Logitech hardware (Logitech cordless
637 	   desktop LX500) */
638 	static const u8 e_keymap[] = {
639 		  0,216,  0,213,175,156,  0,  0,  0,  0,
640 		144,  0,  0,  0,  0,  0,  0,  0,  0,212,
641 		174,167,152,161,112,  0,  0,  0,154,  0,
642 		  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
643 		  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
644 		  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
645 		  0,  0,  0,  0,  0,183,184,185,186,187,
646 		188,189,190,191,192,193,194,  0,  0,  0
647 	};
648 	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
649 	unsigned int hid = usage->hid;
650 
651 	if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER &&
652 			lg_ultrax_remote_mapping(hi, usage, bit, max))
653 		return 1;
654 
655 	if ((drv_data->quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
656 		return 1;
657 
658 	if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
659 		return 0;
660 
661 	hid &= HID_USAGE;
662 
663 	/* Special handling for Logitech Cordless Desktop */
664 	if (field->application == HID_GD_MOUSE) {
665 		if ((drv_data->quirks & LG_IGNORE_DOUBLED_WHEEL) &&
666 				(hid == 7 || hid == 8))
667 			return -1;
668 	} else {
669 		if ((drv_data->quirks & LG_EXPANDED_KEYMAP) &&
670 				hid < ARRAY_SIZE(e_keymap) &&
671 				e_keymap[hid] != 0) {
672 			hid_map_usage(hi, usage, bit, max, EV_KEY,
673 					e_keymap[hid]);
674 			return 1;
675 		}
676 	}
677 
678 	return 0;
679 }
680 
681 static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
682 		struct hid_field *field, struct hid_usage *usage,
683 		unsigned long **bit, int *max)
684 {
685 	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
686 
687 	if ((drv_data->quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
688 			(field->flags & HID_MAIN_ITEM_RELATIVE))
689 		field->flags &= ~HID_MAIN_ITEM_RELATIVE;
690 
691 	if ((drv_data->quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY ||
692 			 usage->type == EV_REL || usage->type == EV_ABS))
693 		clear_bit(usage->code, *bit);
694 
695 	/* Ensure that Logitech wheels are not given a default fuzz/flat value */
696 	if (usage->type == EV_ABS && (usage->code == ABS_X ||
697 			usage->code == ABS_Y || usage->code == ABS_Z ||
698 			usage->code == ABS_RZ)) {
699 		switch (hdev->product) {
700 		case USB_DEVICE_ID_LOGITECH_G29_WHEEL:
701 		case USB_DEVICE_ID_LOGITECH_WINGMAN_FG:
702 		case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
703 		case USB_DEVICE_ID_LOGITECH_WHEEL:
704 		case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
705 		case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
706 		case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
707 		case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
708 		case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
709 		case USB_DEVICE_ID_LOGITECH_WII_WHEEL:
710 		case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
711 		case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL:
712 			field->application = HID_GD_MULTIAXIS;
713 			break;
714 		default:
715 			break;
716 		}
717 	}
718 
719 	return 0;
720 }
721 
722 static int lg_event(struct hid_device *hdev, struct hid_field *field,
723 		struct hid_usage *usage, __s32 value)
724 {
725 	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
726 
727 	if ((drv_data->quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
728 		input_event(field->hidinput->input, usage->type, usage->code,
729 				-value);
730 		return 1;
731 	}
732 	if (drv_data->quirks & LG_FF4) {
733 		return lg4ff_adjust_input_event(hdev, field, usage, value, drv_data);
734 	}
735 
736 	return 0;
737 }
738 
739 static int lg_raw_event(struct hid_device *hdev, struct hid_report *report,
740 		u8 *rd, int size)
741 {
742 	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
743 
744 	if (drv_data->quirks & LG_FF4)
745 		return lg4ff_raw_event(hdev, report, rd, size, drv_data);
746 
747 	return 0;
748 }
749 
750 static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
751 {
752 	struct usb_interface *iface;
753 	__u8 iface_num;
754 	unsigned int connect_mask = HID_CONNECT_DEFAULT;
755 	struct lg_drv_data *drv_data;
756 	int ret;
757 
758 	if (!hid_is_usb(hdev))
759 		return -EINVAL;
760 
761 	iface = to_usb_interface(hdev->dev.parent);
762 	iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
763 
764 	/* G29 only work with the 1st interface */
765 	if ((hdev->product == USB_DEVICE_ID_LOGITECH_G29_WHEEL) &&
766 	    (iface_num != 0)) {
767 		dbg_hid("%s: ignoring ifnum %d\n", __func__, iface_num);
768 		return -ENODEV;
769 	}
770 
771 	drv_data = kzalloc(sizeof(struct lg_drv_data), GFP_KERNEL);
772 	if (!drv_data) {
773 		hid_err(hdev, "Insufficient memory, cannot allocate driver data\n");
774 		return -ENOMEM;
775 	}
776 	drv_data->quirks = id->driver_data;
777 
778 	hid_set_drvdata(hdev, (void *)drv_data);
779 
780 	if (drv_data->quirks & LG_NOGET)
781 		hdev->quirks |= HID_QUIRK_NOGET;
782 
783 	ret = hid_parse(hdev);
784 	if (ret) {
785 		hid_err(hdev, "parse failed\n");
786 		goto err_free;
787 	}
788 
789 	if (drv_data->quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4))
790 		connect_mask &= ~HID_CONNECT_FF;
791 
792 	ret = hid_hw_start(hdev, connect_mask);
793 	if (ret) {
794 		hid_err(hdev, "hw start failed\n");
795 		goto err_free;
796 	}
797 
798 	/* Setup wireless link with Logitech Wii wheel */
799 	if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
800 		static const unsigned char cbuf[] = {
801 			0x00, 0xAF,  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
802 		};
803 		u8 *buf = kmemdup(cbuf, sizeof(cbuf), GFP_KERNEL);
804 
805 		if (!buf) {
806 			ret = -ENOMEM;
807 			goto err_stop;
808 		}
809 
810 		ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(cbuf),
811 					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
812 		if (ret >= 0) {
813 			/* insert a little delay of 10 jiffies ~ 40ms */
814 			wait_queue_head_t wait;
815 			init_waitqueue_head (&wait);
816 			wait_event_interruptible_timeout(wait, 0,
817 							 msecs_to_jiffies(40));
818 
819 			/* Select random Address */
820 			buf[1] = 0xB2;
821 			get_random_bytes(&buf[2], 2);
822 
823 			ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(cbuf),
824 					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
825 		}
826 		kfree(buf);
827 	}
828 
829 	if (drv_data->quirks & LG_FF)
830 		ret = lgff_init(hdev);
831 	else if (drv_data->quirks & LG_FF2)
832 		ret = lg2ff_init(hdev);
833 	else if (drv_data->quirks & LG_FF3)
834 		ret = lg3ff_init(hdev);
835 	else if (drv_data->quirks & LG_FF4)
836 		ret = lg4ff_init(hdev);
837 
838 	if (ret)
839 		goto err_stop;
840 
841 	return 0;
842 
843 err_stop:
844 	hid_hw_stop(hdev);
845 err_free:
846 	kfree(drv_data);
847 	return ret;
848 }
849 
850 static void lg_remove(struct hid_device *hdev)
851 {
852 	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
853 	if (drv_data->quirks & LG_FF4)
854 		lg4ff_deinit(hdev);
855 	hid_hw_stop(hdev);
856 	kfree(drv_data);
857 }
858 
859 static const struct hid_device_id lg_devices[] = {
860 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER),
861 		.driver_data = LG_RDESC | LG_WIRELESS },
862 
863 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER),
864 		.driver_data = LG_BAD_RELATIVE_KEYS },
865 
866 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP),
867 		.driver_data = LG_DUPLICATE_USAGES },
868 
869 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD),
870 		.driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
871 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500),
872 		.driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
873 
874 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
875 		.driver_data = LG_NOGET },
876 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DUAL_ACTION),
877 		.driver_data = LG_NOGET },
878 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
879 		.driver_data = LG_NOGET | LG_FF4 },
880 
881 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD),
882 		.driver_data = LG_FF2 },
883 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD),
884 		.driver_data = LG_FF },
885 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2),
886 		.driver_data = LG_FF },
887 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G29_WHEEL),
888 		.driver_data = LG_FF4 },
889 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D),
890 		.driver_data = LG_FF },
891 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
892 		.driver_data = LG_FF },
893 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL),
894 		.driver_data = LG_NOGET | LG_FF4 },
895 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
896 		.driver_data = LG_FF4 },
897 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL),
898 		.driver_data = LG_FF2 },
899 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
900 		.driver_data = LG_FF4 },
901 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL),
902 		.driver_data = LG_FF4 },
903 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL),
904 		.driver_data = LG_FF4 },
905 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL),
906 		.driver_data = LG_NOGET | LG_FF4 },
907 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
908 		.driver_data = LG_FF4 },
909 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FG),
910 		.driver_data = LG_NOGET },
911 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
912 		.driver_data = LG_NOGET | LG_FF4 },
913 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
914 		.driver_data = LG_NOGET | LG_FF2 },
915 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940),
916 		.driver_data = LG_FF3 },
917 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR),
918 		.driver_data = LG_RDESC_REL_ABS },
919 	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER),
920 		.driver_data = LG_RDESC_REL_ABS },
921 	{ }
922 };
923 
924 MODULE_DEVICE_TABLE(hid, lg_devices);
925 
926 static struct hid_driver lg_driver = {
927 	.name = "logitech",
928 	.id_table = lg_devices,
929 	.report_fixup = lg_report_fixup,
930 	.input_mapping = lg_input_mapping,
931 	.input_mapped = lg_input_mapped,
932 	.event = lg_event,
933 	.raw_event = lg_raw_event,
934 	.probe = lg_probe,
935 	.remove = lg_remove,
936 };
937 module_hid_driver(lg_driver);
938 
939 #ifdef CONFIG_LOGIWHEELS_FF
940 int lg4ff_no_autoswitch = 0;
941 module_param_named(lg4ff_no_autoswitch, lg4ff_no_autoswitch, int, S_IRUGO);
942 MODULE_PARM_DESC(lg4ff_no_autoswitch, "Do not switch multimode wheels to their native mode automatically");
943 #endif
944 
945 MODULE_DESCRIPTION("HID driver for some logitech \"special\" devices");
946 MODULE_LICENSE("GPL");
947