2 * HID driver for some logitech "special" devices
4 * Copyright (c) 1999 Andreas Gal
5 * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
6 * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
7 * Copyright (c) 2006-2007 Jiri Kosina
8 * Copyright (c) 2008 Jiri Slaby
9 * Copyright (c) 2010 Hendrik Iben
13 * This program is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU General Public License as published by the Free
15 * Software Foundation; either version 2 of the License, or (at your option)
19 #include <linux/device.h>
20 #include <linux/hid.h>
21 #include <linux/module.h>
22 #include <linux/random.h>
23 #include <linux/sched.h>
24 #include <linux/usb.h>
25 #include <linux/wait.h>
27 #include "usbhid/usbhid.h"
31 #define LG_RDESC 0x001
32 #define LG_BAD_RELATIVE_KEYS 0x002
33 #define LG_DUPLICATE_USAGES 0x004
34 #define LG_EXPANDED_KEYMAP 0x010
35 #define LG_IGNORE_DOUBLED_WHEEL 0x020
36 #define LG_WIRELESS 0x040
37 #define LG_INVERT_HWHEEL 0x080
38 #define LG_NOGET 0x100
41 #define LG_RDESC_REL_ABS 0x800
45 /* Size of the original descriptors of the Driving Force (and Pro) wheels */
46 #define DF_RDESC_ORIG_SIZE 130
47 #define DFP_RDESC_ORIG_SIZE 97
48 #define MOMO_RDESC_ORIG_SIZE 87
50 /* Fixed report descriptors for Logitech Driving Force (and Pro)
53 * The original descriptors hide the separate throttle and brake axes in
54 * a custom vendor usage page, providing only a combined value as
56 * These descriptors remove the combined Y axis and instead report
57 * separate throttle (Y) and brake (RZ).
59 static __u8 df_rdesc_fixed
[] = {
60 0x05, 0x01, /* Usage Page (Desktop), */
61 0x09, 0x04, /* Usage (Joystik), */
62 0xA1, 0x01, /* Collection (Application), */
63 0xA1, 0x02, /* Collection (Logical), */
64 0x95, 0x01, /* Report Count (1), */
65 0x75, 0x0A, /* Report Size (10), */
66 0x14, /* Logical Minimum (0), */
67 0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
68 0x34, /* Physical Minimum (0), */
69 0x46, 0xFF, 0x03, /* Physical Maximum (1023), */
70 0x09, 0x30, /* Usage (X), */
71 0x81, 0x02, /* Input (Variable), */
72 0x95, 0x0C, /* Report Count (12), */
73 0x75, 0x01, /* Report Size (1), */
74 0x25, 0x01, /* Logical Maximum (1), */
75 0x45, 0x01, /* Physical Maximum (1), */
76 0x05, 0x09, /* Usage (Buttons), */
77 0x19, 0x01, /* Usage Minimum (1), */
78 0x29, 0x0c, /* Usage Maximum (12), */
79 0x81, 0x02, /* Input (Variable), */
80 0x95, 0x02, /* Report Count (2), */
81 0x06, 0x00, 0xFF, /* Usage Page (Vendor: 65280), */
82 0x09, 0x01, /* Usage (?: 1), */
83 0x81, 0x02, /* Input (Variable), */
84 0x05, 0x01, /* Usage Page (Desktop), */
85 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
86 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
87 0x95, 0x01, /* Report Count (1), */
88 0x75, 0x08, /* Report Size (8), */
89 0x81, 0x02, /* Input (Variable), */
90 0x25, 0x07, /* Logical Maximum (7), */
91 0x46, 0x3B, 0x01, /* Physical Maximum (315), */
92 0x75, 0x04, /* Report Size (4), */
93 0x65, 0x14, /* Unit (Degrees), */
94 0x09, 0x39, /* Usage (Hat Switch), */
95 0x81, 0x42, /* Input (Variable, Null State), */
96 0x75, 0x01, /* Report Size (1), */
97 0x95, 0x04, /* Report Count (4), */
98 0x65, 0x00, /* Unit (none), */
99 0x06, 0x00, 0xFF, /* Usage Page (Vendor: 65280), */
100 0x09, 0x01, /* Usage (?: 1), */
101 0x25, 0x01, /* Logical Maximum (1), */
102 0x45, 0x01, /* Physical Maximum (1), */
103 0x81, 0x02, /* Input (Variable), */
104 0x05, 0x01, /* Usage Page (Desktop), */
105 0x95, 0x01, /* Report Count (1), */
106 0x75, 0x08, /* Report Size (8), */
107 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
108 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
109 0x09, 0x31, /* Usage (Y), */
110 0x81, 0x02, /* Input (Variable), */
111 0x09, 0x35, /* Usage (Rz), */
112 0x81, 0x02, /* Input (Variable), */
113 0xC0, /* End Collection, */
114 0xA1, 0x02, /* Collection (Logical), */
115 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
116 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
117 0x95, 0x07, /* Report Count (7), */
118 0x75, 0x08, /* Report Size (8), */
119 0x09, 0x03, /* Usage (?: 3), */
120 0x91, 0x02, /* Output (Variable), */
121 0xC0, /* End Collection, */
122 0xC0 /* End Collection */
125 static __u8 dfp_rdesc_fixed
[] = {
126 0x05, 0x01, /* Usage Page (Desktop), */
127 0x09, 0x04, /* Usage (Joystik), */
128 0xA1, 0x01, /* Collection (Application), */
129 0xA1, 0x02, /* Collection (Logical), */
130 0x95, 0x01, /* Report Count (1), */
131 0x75, 0x0E, /* Report Size (14), */
132 0x14, /* Logical Minimum (0), */
133 0x26, 0xFF, 0x3F, /* Logical Maximum (16383), */
134 0x34, /* Physical Minimum (0), */
135 0x46, 0xFF, 0x3F, /* Physical Maximum (16383), */
136 0x09, 0x30, /* Usage (X), */
137 0x81, 0x02, /* Input (Variable), */
138 0x95, 0x0E, /* Report Count (14), */
139 0x75, 0x01, /* Report Size (1), */
140 0x25, 0x01, /* Logical Maximum (1), */
141 0x45, 0x01, /* Physical Maximum (1), */
142 0x05, 0x09, /* Usage Page (Button), */
143 0x19, 0x01, /* Usage Minimum (01h), */
144 0x29, 0x0E, /* Usage Maximum (0Eh), */
145 0x81, 0x02, /* Input (Variable), */
146 0x05, 0x01, /* Usage Page (Desktop), */
147 0x95, 0x01, /* Report Count (1), */
148 0x75, 0x04, /* Report Size (4), */
149 0x25, 0x07, /* Logical Maximum (7), */
150 0x46, 0x3B, 0x01, /* Physical Maximum (315), */
151 0x65, 0x14, /* Unit (Degrees), */
152 0x09, 0x39, /* Usage (Hat Switch), */
153 0x81, 0x42, /* Input (Variable, Nullstate), */
154 0x65, 0x00, /* Unit, */
155 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
156 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
157 0x75, 0x08, /* Report Size (8), */
158 0x81, 0x01, /* Input (Constant), */
159 0x09, 0x31, /* Usage (Y), */
160 0x81, 0x02, /* Input (Variable), */
161 0x09, 0x35, /* Usage (Rz), */
162 0x81, 0x02, /* Input (Variable), */
163 0x81, 0x01, /* Input (Constant), */
164 0xC0, /* End Collection, */
165 0xA1, 0x02, /* Collection (Logical), */
166 0x09, 0x02, /* Usage (02h), */
167 0x95, 0x07, /* Report Count (7), */
168 0x91, 0x02, /* Output (Variable), */
169 0xC0, /* End Collection, */
170 0xC0 /* End Collection */
173 static __u8 momo_rdesc_fixed
[] = {
174 0x05, 0x01, /* Usage Page (Desktop), */
175 0x09, 0x04, /* Usage (Joystik), */
176 0xA1, 0x01, /* Collection (Application), */
177 0xA1, 0x02, /* Collection (Logical), */
178 0x95, 0x01, /* Report Count (1), */
179 0x75, 0x0A, /* Report Size (10), */
180 0x15, 0x00, /* Logical Minimum (0), */
181 0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
182 0x35, 0x00, /* Physical Minimum (0), */
183 0x46, 0xFF, 0x03, /* Physical Maximum (1023), */
184 0x09, 0x30, /* Usage (X), */
185 0x81, 0x02, /* Input (Variable), */
186 0x95, 0x08, /* Report Count (8), */
187 0x75, 0x01, /* Report Size (1), */
188 0x25, 0x01, /* Logical Maximum (1), */
189 0x45, 0x01, /* Physical Maximum (1), */
190 0x05, 0x09, /* Usage Page (Button), */
191 0x19, 0x01, /* Usage Minimum (01h), */
192 0x29, 0x08, /* Usage Maximum (08h), */
193 0x81, 0x02, /* Input (Variable), */
194 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
195 0x75, 0x0E, /* Report Size (14), */
196 0x95, 0x01, /* Report Count (1), */
197 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
198 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
199 0x09, 0x00, /* Usage (00h), */
200 0x81, 0x02, /* Input (Variable), */
201 0x05, 0x01, /* Usage Page (Desktop), */
202 0x75, 0x08, /* Report Size (8), */
203 0x09, 0x31, /* Usage (Y), */
204 0x81, 0x02, /* Input (Variable), */
205 0x09, 0x32, /* Usage (Z), */
206 0x81, 0x02, /* Input (Variable), */
207 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
208 0x09, 0x01, /* Usage (01h), */
209 0x81, 0x02, /* Input (Variable), */
210 0xC0, /* End Collection, */
211 0xA1, 0x02, /* Collection (Logical), */
212 0x09, 0x02, /* Usage (02h), */
213 0x95, 0x07, /* Report Count (7), */
214 0x91, 0x02, /* Output (Variable), */
215 0xC0, /* End Collection, */
216 0xC0 /* End Collection */
220 * Certain Logitech keyboards send in report #3 keys which are far
221 * above the logical maximum described in descriptor. This extends
222 * the original value of 0x28c of logical maximum to 0x104d
224 static __u8
*lg_report_fixup(struct hid_device
*hdev
, __u8
*rdesc
,
227 struct lg_drv_data
*drv_data
= hid_get_drvdata(hdev
);
228 struct usb_device_descriptor
*udesc
;
229 __u16 bcdDevice
, rev_maj
, rev_min
;
231 if ((drv_data
->quirks
& LG_RDESC
) && *rsize
>= 90 && rdesc
[83] == 0x26 &&
232 rdesc
[84] == 0x8c && rdesc
[85] == 0x02) {
234 "fixing up Logitech keyboard report descriptor\n");
235 rdesc
[84] = rdesc
[89] = 0x4d;
236 rdesc
[85] = rdesc
[90] = 0x10;
238 if ((drv_data
->quirks
& LG_RDESC_REL_ABS
) && *rsize
>= 50 &&
239 rdesc
[32] == 0x81 && rdesc
[33] == 0x06 &&
240 rdesc
[49] == 0x81 && rdesc
[50] == 0x06) {
242 "fixing up rel/abs in Logitech report descriptor\n");
243 rdesc
[33] = rdesc
[50] = 0x02;
246 switch (hdev
->product
) {
248 /* Several wheels report as this id when operating in emulation mode. */
249 case USB_DEVICE_ID_LOGITECH_WHEEL
:
250 udesc
= &(hid_to_usb_dev(hdev
)->descriptor
);
252 hid_err(hdev
, "NULL USB device descriptor\n");
255 bcdDevice
= le16_to_cpu(udesc
->bcdDevice
);
256 rev_maj
= bcdDevice
>> 8;
257 rev_min
= bcdDevice
& 0xff;
259 /* Update the report descriptor for only the Driving Force wheel */
260 if (rev_maj
== 1 && rev_min
== 2 &&
261 *rsize
== DF_RDESC_ORIG_SIZE
) {
263 "fixing up Logitech Driving Force report descriptor\n");
264 rdesc
= df_rdesc_fixed
;
265 *rsize
= sizeof(df_rdesc_fixed
);
269 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL
:
270 if (*rsize
== MOMO_RDESC_ORIG_SIZE
) {
272 "fixing up Logitech Momo Force (Red) report descriptor\n");
273 rdesc
= momo_rdesc_fixed
;
274 *rsize
= sizeof(momo_rdesc_fixed
);
278 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL
:
279 if (*rsize
== DFP_RDESC_ORIG_SIZE
) {
281 "fixing up Logitech Driving Force Pro report descriptor\n");
282 rdesc
= dfp_rdesc_fixed
;
283 *rsize
= sizeof(dfp_rdesc_fixed
);
287 case USB_DEVICE_ID_LOGITECH_WII_WHEEL
:
288 if (*rsize
>= 101 && rdesc
[41] == 0x95 && rdesc
[42] == 0x0B &&
289 rdesc
[47] == 0x05 && rdesc
[48] == 0x09) {
290 hid_info(hdev
, "fixing up Logitech Speed Force Wireless report descriptor\n");
302 #define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
305 static int lg_ultrax_remote_mapping(struct hid_input
*hi
,
306 struct hid_usage
*usage
, unsigned long **bit
, int *max
)
308 if ((usage
->hid
& HID_USAGE_PAGE
) != HID_UP_LOGIVENDOR
)
311 set_bit(EV_REP
, hi
->input
->evbit
);
312 switch (usage
->hid
& HID_USAGE
) {
313 /* Reported on Logitech Ultra X Media Remote */
314 case 0x004: lg_map_key_clear(KEY_AGAIN
); break;
315 case 0x00d: lg_map_key_clear(KEY_HOME
); break;
316 case 0x024: lg_map_key_clear(KEY_SHUFFLE
); break;
317 case 0x025: lg_map_key_clear(KEY_TV
); break;
318 case 0x026: lg_map_key_clear(KEY_MENU
); break;
319 case 0x031: lg_map_key_clear(KEY_AUDIO
); break;
320 case 0x032: lg_map_key_clear(KEY_TEXT
); break;
321 case 0x033: lg_map_key_clear(KEY_LAST
); break;
322 case 0x047: lg_map_key_clear(KEY_MP3
); break;
323 case 0x048: lg_map_key_clear(KEY_DVD
); break;
324 case 0x049: lg_map_key_clear(KEY_MEDIA
); break;
325 case 0x04a: lg_map_key_clear(KEY_VIDEO
); break;
326 case 0x04b: lg_map_key_clear(KEY_ANGLE
); break;
327 case 0x04c: lg_map_key_clear(KEY_LANGUAGE
); break;
328 case 0x04d: lg_map_key_clear(KEY_SUBTITLE
); break;
329 case 0x051: lg_map_key_clear(KEY_RED
); break;
330 case 0x052: lg_map_key_clear(KEY_CLOSE
); break;
338 static int lg_dinovo_mapping(struct hid_input
*hi
, struct hid_usage
*usage
,
339 unsigned long **bit
, int *max
)
341 if ((usage
->hid
& HID_USAGE_PAGE
) != HID_UP_LOGIVENDOR
)
344 switch (usage
->hid
& HID_USAGE
) {
346 case 0x00d: lg_map_key_clear(KEY_MEDIA
); break;
354 static int lg_wireless_mapping(struct hid_input
*hi
, struct hid_usage
*usage
,
355 unsigned long **bit
, int *max
)
357 if ((usage
->hid
& HID_USAGE_PAGE
) != HID_UP_CONSUMER
)
360 switch (usage
->hid
& HID_USAGE
) {
361 case 0x1001: lg_map_key_clear(KEY_MESSENGER
); break;
362 case 0x1003: lg_map_key_clear(KEY_SOUND
); break;
363 case 0x1004: lg_map_key_clear(KEY_VIDEO
); break;
364 case 0x1005: lg_map_key_clear(KEY_AUDIO
); break;
365 case 0x100a: lg_map_key_clear(KEY_DOCUMENTS
); break;
366 /* The following two entries are Playlist 1 and 2 on the MX3200 */
367 case 0x100f: lg_map_key_clear(KEY_FN_1
); break;
368 case 0x1010: lg_map_key_clear(KEY_FN_2
); break;
369 case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG
); break;
370 case 0x1012: lg_map_key_clear(KEY_NEXTSONG
); break;
371 case 0x1013: lg_map_key_clear(KEY_CAMERA
); break;
372 case 0x1014: lg_map_key_clear(KEY_MESSENGER
); break;
373 case 0x1015: lg_map_key_clear(KEY_RECORD
); break;
374 case 0x1016: lg_map_key_clear(KEY_PLAYER
); break;
375 case 0x1017: lg_map_key_clear(KEY_EJECTCD
); break;
376 case 0x1018: lg_map_key_clear(KEY_MEDIA
); break;
377 case 0x1019: lg_map_key_clear(KEY_PROG1
); break;
378 case 0x101a: lg_map_key_clear(KEY_PROG2
); break;
379 case 0x101b: lg_map_key_clear(KEY_PROG3
); break;
380 case 0x101c: lg_map_key_clear(KEY_CYCLEWINDOWS
); break;
381 case 0x101f: lg_map_key_clear(KEY_ZOOMIN
); break;
382 case 0x1020: lg_map_key_clear(KEY_ZOOMOUT
); break;
383 case 0x1021: lg_map_key_clear(KEY_ZOOMRESET
); break;
384 case 0x1023: lg_map_key_clear(KEY_CLOSE
); break;
385 case 0x1027: lg_map_key_clear(KEY_MENU
); break;
386 /* this one is marked as 'Rotate' */
387 case 0x1028: lg_map_key_clear(KEY_ANGLE
); break;
388 case 0x1029: lg_map_key_clear(KEY_SHUFFLE
); break;
389 case 0x102a: lg_map_key_clear(KEY_BACK
); break;
390 case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS
); break;
391 case 0x102d: lg_map_key_clear(KEY_WWW
); break;
392 /* The following two are 'Start/answer call' and 'End/reject call'
394 case 0x1031: lg_map_key_clear(KEY_OK
); break;
395 case 0x1032: lg_map_key_clear(KEY_CANCEL
); break;
396 case 0x1041: lg_map_key_clear(KEY_BATTERY
); break;
397 case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR
); break;
398 case 0x1043: lg_map_key_clear(KEY_SPREADSHEET
); break;
399 case 0x1044: lg_map_key_clear(KEY_PRESENTATION
); break;
400 case 0x1045: lg_map_key_clear(KEY_UNDO
); break;
401 case 0x1046: lg_map_key_clear(KEY_REDO
); break;
402 case 0x1047: lg_map_key_clear(KEY_PRINT
); break;
403 case 0x1048: lg_map_key_clear(KEY_SAVE
); break;
404 case 0x1049: lg_map_key_clear(KEY_PROG1
); break;
405 case 0x104a: lg_map_key_clear(KEY_PROG2
); break;
406 case 0x104b: lg_map_key_clear(KEY_PROG3
); break;
407 case 0x104c: lg_map_key_clear(KEY_PROG4
); break;
415 static int lg_input_mapping(struct hid_device
*hdev
, struct hid_input
*hi
,
416 struct hid_field
*field
, struct hid_usage
*usage
,
417 unsigned long **bit
, int *max
)
419 /* extended mapping for certain Logitech hardware (Logitech cordless
421 static const u8 e_keymap
[] = {
422 0,216, 0,213,175,156, 0, 0, 0, 0,
423 144, 0, 0, 0, 0, 0, 0, 0, 0,212,
424 174,167,152,161,112, 0, 0, 0,154, 0,
425 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
426 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
427 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
428 0, 0, 0, 0, 0,183,184,185,186,187,
429 188,189,190,191,192,193,194, 0, 0, 0
431 struct lg_drv_data
*drv_data
= hid_get_drvdata(hdev
);
432 unsigned int hid
= usage
->hid
;
434 if (hdev
->product
== USB_DEVICE_ID_LOGITECH_RECEIVER
&&
435 lg_ultrax_remote_mapping(hi
, usage
, bit
, max
))
438 if (hdev
->product
== USB_DEVICE_ID_DINOVO_MINI
&&
439 lg_dinovo_mapping(hi
, usage
, bit
, max
))
442 if ((drv_data
->quirks
& LG_WIRELESS
) && lg_wireless_mapping(hi
, usage
, bit
, max
))
445 if ((hid
& HID_USAGE_PAGE
) != HID_UP_BUTTON
)
450 /* Special handling for Logitech Cordless Desktop */
451 if (field
->application
== HID_GD_MOUSE
) {
452 if ((drv_data
->quirks
& LG_IGNORE_DOUBLED_WHEEL
) &&
453 (hid
== 7 || hid
== 8))
456 if ((drv_data
->quirks
& LG_EXPANDED_KEYMAP
) &&
457 hid
< ARRAY_SIZE(e_keymap
) &&
458 e_keymap
[hid
] != 0) {
459 hid_map_usage(hi
, usage
, bit
, max
, EV_KEY
,
468 static int lg_input_mapped(struct hid_device
*hdev
, struct hid_input
*hi
,
469 struct hid_field
*field
, struct hid_usage
*usage
,
470 unsigned long **bit
, int *max
)
472 struct lg_drv_data
*drv_data
= hid_get_drvdata(hdev
);
474 if ((drv_data
->quirks
& LG_BAD_RELATIVE_KEYS
) && usage
->type
== EV_KEY
&&
475 (field
->flags
& HID_MAIN_ITEM_RELATIVE
))
476 field
->flags
&= ~HID_MAIN_ITEM_RELATIVE
;
478 if ((drv_data
->quirks
& LG_DUPLICATE_USAGES
) && (usage
->type
== EV_KEY
||
479 usage
->type
== EV_REL
|| usage
->type
== EV_ABS
))
480 clear_bit(usage
->code
, *bit
);
482 /* Ensure that Logitech wheels are not given a default fuzz/flat value */
483 if (usage
->type
== EV_ABS
&& (usage
->code
== ABS_X
||
484 usage
->code
== ABS_Y
|| usage
->code
== ABS_Z
||
485 usage
->code
== ABS_RZ
)) {
486 switch (hdev
->product
) {
487 case USB_DEVICE_ID_LOGITECH_WHEEL
:
488 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL
:
489 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL
:
490 case USB_DEVICE_ID_LOGITECH_G25_WHEEL
:
491 case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL
:
492 case USB_DEVICE_ID_LOGITECH_G27_WHEEL
:
493 case USB_DEVICE_ID_LOGITECH_WII_WHEEL
:
494 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2
:
495 field
->application
= HID_GD_MULTIAXIS
;
505 static int lg_event(struct hid_device
*hdev
, struct hid_field
*field
,
506 struct hid_usage
*usage
, __s32 value
)
508 struct lg_drv_data
*drv_data
= hid_get_drvdata(hdev
);
510 if ((drv_data
->quirks
& LG_INVERT_HWHEEL
) && usage
->code
== REL_HWHEEL
) {
511 input_event(field
->hidinput
->input
, usage
->type
, usage
->code
,
515 if (drv_data
->quirks
& LG_FF4
) {
516 return lg4ff_adjust_input_event(hdev
, field
, usage
, value
, drv_data
);
522 static int lg_probe(struct hid_device
*hdev
, const struct hid_device_id
*id
)
524 unsigned int connect_mask
= HID_CONNECT_DEFAULT
;
525 struct lg_drv_data
*drv_data
;
528 drv_data
= kzalloc(sizeof(struct lg_drv_data
), GFP_KERNEL
);
530 hid_err(hdev
, "Insufficient memory, cannot allocate driver data\n");
533 drv_data
->quirks
= id
->driver_data
;
535 hid_set_drvdata(hdev
, (void *)drv_data
);
537 if (drv_data
->quirks
& LG_NOGET
)
538 hdev
->quirks
|= HID_QUIRK_NOGET
;
540 ret
= hid_parse(hdev
);
542 hid_err(hdev
, "parse failed\n");
546 if (drv_data
->quirks
& (LG_FF
| LG_FF2
| LG_FF3
| LG_FF4
))
547 connect_mask
&= ~HID_CONNECT_FF
;
549 ret
= hid_hw_start(hdev
, connect_mask
);
551 hid_err(hdev
, "hw start failed\n");
555 /* Setup wireless link with Logitech Wii wheel */
556 if (hdev
->product
== USB_DEVICE_ID_LOGITECH_WII_WHEEL
) {
557 unsigned char buf
[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
559 ret
= hdev
->hid_output_raw_report(hdev
, buf
, sizeof(buf
), HID_FEATURE_REPORT
);
562 /* insert a little delay of 10 jiffies ~ 40ms */
563 wait_queue_head_t wait
;
564 init_waitqueue_head (&wait
);
565 wait_event_interruptible_timeout(wait
, 0, 10);
567 /* Select random Address */
569 get_random_bytes(&buf
[2], 2);
571 ret
= hdev
->hid_output_raw_report(hdev
, buf
, sizeof(buf
), HID_FEATURE_REPORT
);
575 if (drv_data
->quirks
& LG_FF
)
577 if (drv_data
->quirks
& LG_FF2
)
579 if (drv_data
->quirks
& LG_FF3
)
581 if (drv_data
->quirks
& LG_FF4
)
590 static void lg_remove(struct hid_device
*hdev
)
592 struct lg_drv_data
*drv_data
= hid_get_drvdata(hdev
);
593 if (drv_data
->quirks
& LG_FF4
)
600 static const struct hid_device_id lg_devices
[] = {
601 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_MX3000_RECEIVER
),
602 .driver_data
= LG_RDESC
| LG_WIRELESS
},
603 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_S510_RECEIVER
),
604 .driver_data
= LG_RDESC
| LG_WIRELESS
},
605 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_S510_RECEIVER_2
),
606 .driver_data
= LG_RDESC
| LG_WIRELESS
},
608 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_RECEIVER
),
609 .driver_data
= LG_BAD_RELATIVE_KEYS
},
611 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_DINOVO_DESKTOP
),
612 .driver_data
= LG_DUPLICATE_USAGES
},
613 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_DINOVO_EDGE
),
614 .driver_data
= LG_DUPLICATE_USAGES
},
615 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_DINOVO_MINI
),
616 .driver_data
= LG_DUPLICATE_USAGES
},
618 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_ELITE_KBD
),
619 .driver_data
= LG_IGNORE_DOUBLED_WHEEL
| LG_EXPANDED_KEYMAP
},
620 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500
),
621 .driver_data
= LG_IGNORE_DOUBLED_WHEEL
| LG_EXPANDED_KEYMAP
},
623 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_EXTREME_3D
),
624 .driver_data
= LG_NOGET
},
625 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_WHEEL
),
626 .driver_data
= LG_NOGET
| LG_FF4
},
628 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD
),
629 .driver_data
= LG_FF2
},
630 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_RUMBLEPAD
),
631 .driver_data
= LG_FF
},
632 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2
),
633 .driver_data
= LG_FF
},
634 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D
),
635 .driver_data
= LG_FF
},
636 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO
),
637 .driver_data
= LG_FF
},
638 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL
),
639 .driver_data
= LG_NOGET
| LG_FF4
},
640 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2
),
641 .driver_data
= LG_FF4
},
642 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_G25_WHEEL
),
643 .driver_data
= LG_FF4
},
644 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL
),
645 .driver_data
= LG_FF4
},
646 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_G27_WHEEL
),
647 .driver_data
= LG_FF4
},
648 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_DFP_WHEEL
),
649 .driver_data
= LG_NOGET
| LG_FF4
},
650 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_WII_WHEEL
),
651 .driver_data
= LG_FF4
},
652 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG
),
653 .driver_data
= LG_FF
},
654 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2
),
655 .driver_data
= LG_FF2
},
656 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940
),
657 .driver_data
= LG_FF3
},
658 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_SPACENAVIGATOR
),
659 .driver_data
= LG_RDESC_REL_ABS
},
660 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_SPACETRAVELLER
),
661 .driver_data
= LG_RDESC_REL_ABS
},
665 MODULE_DEVICE_TABLE(hid
, lg_devices
);
667 static struct hid_driver lg_driver
= {
669 .id_table
= lg_devices
,
670 .report_fixup
= lg_report_fixup
,
671 .input_mapping
= lg_input_mapping
,
672 .input_mapped
= lg_input_mapped
,
677 module_hid_driver(lg_driver
);
679 MODULE_LICENSE("GPL");