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 FV_RDESC_ORIG_SIZE 130
49 #define MOMO_RDESC_ORIG_SIZE 87
50 #define MOMO2_RDESC_ORIG_SIZE 87
52 /* Fixed report descriptors for Logitech Driving Force (and Pro)
55 * The original descriptors hide the separate throttle and brake axes in
56 * a custom vendor usage page, providing only a combined value as
58 * These descriptors remove the combined Y axis and instead report
59 * separate throttle (Y) and brake (RZ).
61 static __u8 df_rdesc_fixed
[] = {
62 0x05, 0x01, /* Usage Page (Desktop), */
63 0x09, 0x04, /* Usage (Joystik), */
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 */
127 static __u8 dfp_rdesc_fixed
[] = {
128 0x05, 0x01, /* Usage Page (Desktop), */
129 0x09, 0x04, /* Usage (Joystik), */
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 */
175 static __u8 fv_rdesc_fixed
[] = {
176 0x05, 0x01, /* Usage Page (Desktop), */
177 0x09, 0x04, /* Usage (Joystik), */
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 */
242 static __u8 momo_rdesc_fixed
[] = {
243 0x05, 0x01, /* Usage Page (Desktop), */
244 0x09, 0x04, /* Usage (Joystik), */
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 */
288 static __u8 momo2_rdesc_fixed
[] = {
289 0x05, 0x01, /* Usage Page (Desktop), */
290 0x09, 0x04, /* Usage (Joystik), */
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 */
337 * Certain Logitech keyboards send in report #3 keys which are far
338 * above the logical maximum described in descriptor. This extends
339 * the original value of 0x28c of logical maximum to 0x104d
341 static __u8
*lg_report_fixup(struct hid_device
*hdev
, __u8
*rdesc
,
344 struct lg_drv_data
*drv_data
= hid_get_drvdata(hdev
);
345 struct usb_device_descriptor
*udesc
;
346 __u16 bcdDevice
, rev_maj
, rev_min
;
348 if ((drv_data
->quirks
& LG_RDESC
) && *rsize
>= 91 && rdesc
[83] == 0x26 &&
349 rdesc
[84] == 0x8c && rdesc
[85] == 0x02) {
351 "fixing up Logitech keyboard report descriptor\n");
352 rdesc
[84] = rdesc
[89] = 0x4d;
353 rdesc
[85] = rdesc
[90] = 0x10;
355 if ((drv_data
->quirks
& LG_RDESC_REL_ABS
) && *rsize
>= 51 &&
356 rdesc
[32] == 0x81 && rdesc
[33] == 0x06 &&
357 rdesc
[49] == 0x81 && rdesc
[50] == 0x06) {
359 "fixing up rel/abs in Logitech report descriptor\n");
360 rdesc
[33] = rdesc
[50] = 0x02;
363 switch (hdev
->product
) {
365 /* Several wheels report as this id when operating in emulation mode. */
366 case USB_DEVICE_ID_LOGITECH_WHEEL
:
367 udesc
= &(hid_to_usb_dev(hdev
)->descriptor
);
369 hid_err(hdev
, "NULL USB device descriptor\n");
372 bcdDevice
= le16_to_cpu(udesc
->bcdDevice
);
373 rev_maj
= bcdDevice
>> 8;
374 rev_min
= bcdDevice
& 0xff;
376 /* Update the report descriptor for only the Driving Force wheel */
377 if (rev_maj
== 1 && rev_min
== 2 &&
378 *rsize
== DF_RDESC_ORIG_SIZE
) {
380 "fixing up Logitech Driving Force report descriptor\n");
381 rdesc
= df_rdesc_fixed
;
382 *rsize
= sizeof(df_rdesc_fixed
);
386 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL
:
387 if (*rsize
== MOMO_RDESC_ORIG_SIZE
) {
389 "fixing up Logitech Momo Force (Red) report descriptor\n");
390 rdesc
= momo_rdesc_fixed
;
391 *rsize
= sizeof(momo_rdesc_fixed
);
395 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2
:
396 if (*rsize
== MOMO2_RDESC_ORIG_SIZE
) {
398 "fixing up Logitech Momo Racing Force (Black) report descriptor\n");
399 rdesc
= momo2_rdesc_fixed
;
400 *rsize
= sizeof(momo2_rdesc_fixed
);
404 case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL
:
405 if (*rsize
== FV_RDESC_ORIG_SIZE
) {
407 "fixing up Logitech Formula Vibration report descriptor\n");
408 rdesc
= fv_rdesc_fixed
;
409 *rsize
= sizeof(fv_rdesc_fixed
);
413 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL
:
414 if (*rsize
== DFP_RDESC_ORIG_SIZE
) {
416 "fixing up Logitech Driving Force Pro report descriptor\n");
417 rdesc
= dfp_rdesc_fixed
;
418 *rsize
= sizeof(dfp_rdesc_fixed
);
422 case USB_DEVICE_ID_LOGITECH_WII_WHEEL
:
423 if (*rsize
>= 101 && rdesc
[41] == 0x95 && rdesc
[42] == 0x0B &&
424 rdesc
[47] == 0x05 && rdesc
[48] == 0x09) {
425 hid_info(hdev
, "fixing up Logitech Speed Force Wireless report descriptor\n");
437 #define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
440 static int lg_ultrax_remote_mapping(struct hid_input
*hi
,
441 struct hid_usage
*usage
, unsigned long **bit
, int *max
)
443 if ((usage
->hid
& HID_USAGE_PAGE
) != HID_UP_LOGIVENDOR
)
446 set_bit(EV_REP
, hi
->input
->evbit
);
447 switch (usage
->hid
& HID_USAGE
) {
448 /* Reported on Logitech Ultra X Media Remote */
449 case 0x004: lg_map_key_clear(KEY_AGAIN
); break;
450 case 0x00d: lg_map_key_clear(KEY_HOME
); break;
451 case 0x024: lg_map_key_clear(KEY_SHUFFLE
); break;
452 case 0x025: lg_map_key_clear(KEY_TV
); break;
453 case 0x026: lg_map_key_clear(KEY_MENU
); break;
454 case 0x031: lg_map_key_clear(KEY_AUDIO
); break;
455 case 0x032: lg_map_key_clear(KEY_TEXT
); break;
456 case 0x033: lg_map_key_clear(KEY_LAST
); break;
457 case 0x047: lg_map_key_clear(KEY_MP3
); break;
458 case 0x048: lg_map_key_clear(KEY_DVD
); break;
459 case 0x049: lg_map_key_clear(KEY_MEDIA
); break;
460 case 0x04a: lg_map_key_clear(KEY_VIDEO
); break;
461 case 0x04b: lg_map_key_clear(KEY_ANGLE
); break;
462 case 0x04c: lg_map_key_clear(KEY_LANGUAGE
); break;
463 case 0x04d: lg_map_key_clear(KEY_SUBTITLE
); break;
464 case 0x051: lg_map_key_clear(KEY_RED
); break;
465 case 0x052: lg_map_key_clear(KEY_CLOSE
); break;
473 static int lg_dinovo_mapping(struct hid_input
*hi
, struct hid_usage
*usage
,
474 unsigned long **bit
, int *max
)
476 if ((usage
->hid
& HID_USAGE_PAGE
) != HID_UP_LOGIVENDOR
)
479 switch (usage
->hid
& HID_USAGE
) {
481 case 0x00d: lg_map_key_clear(KEY_MEDIA
); break;
489 static int lg_wireless_mapping(struct hid_input
*hi
, struct hid_usage
*usage
,
490 unsigned long **bit
, int *max
)
492 if ((usage
->hid
& HID_USAGE_PAGE
) != HID_UP_CONSUMER
)
495 switch (usage
->hid
& HID_USAGE
) {
496 case 0x1001: lg_map_key_clear(KEY_MESSENGER
); break;
497 case 0x1003: lg_map_key_clear(KEY_SOUND
); break;
498 case 0x1004: lg_map_key_clear(KEY_VIDEO
); break;
499 case 0x1005: lg_map_key_clear(KEY_AUDIO
); break;
500 case 0x100a: lg_map_key_clear(KEY_DOCUMENTS
); break;
501 /* The following two entries are Playlist 1 and 2 on the MX3200 */
502 case 0x100f: lg_map_key_clear(KEY_FN_1
); break;
503 case 0x1010: lg_map_key_clear(KEY_FN_2
); break;
504 case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG
); break;
505 case 0x1012: lg_map_key_clear(KEY_NEXTSONG
); break;
506 case 0x1013: lg_map_key_clear(KEY_CAMERA
); break;
507 case 0x1014: lg_map_key_clear(KEY_MESSENGER
); break;
508 case 0x1015: lg_map_key_clear(KEY_RECORD
); break;
509 case 0x1016: lg_map_key_clear(KEY_PLAYER
); break;
510 case 0x1017: lg_map_key_clear(KEY_EJECTCD
); break;
511 case 0x1018: lg_map_key_clear(KEY_MEDIA
); break;
512 case 0x1019: lg_map_key_clear(KEY_PROG1
); break;
513 case 0x101a: lg_map_key_clear(KEY_PROG2
); break;
514 case 0x101b: lg_map_key_clear(KEY_PROG3
); break;
515 case 0x101c: lg_map_key_clear(KEY_CYCLEWINDOWS
); break;
516 case 0x101f: lg_map_key_clear(KEY_ZOOMIN
); break;
517 case 0x1020: lg_map_key_clear(KEY_ZOOMOUT
); break;
518 case 0x1021: lg_map_key_clear(KEY_ZOOMRESET
); break;
519 case 0x1023: lg_map_key_clear(KEY_CLOSE
); break;
520 case 0x1027: lg_map_key_clear(KEY_MENU
); break;
521 /* this one is marked as 'Rotate' */
522 case 0x1028: lg_map_key_clear(KEY_ANGLE
); break;
523 case 0x1029: lg_map_key_clear(KEY_SHUFFLE
); break;
524 case 0x102a: lg_map_key_clear(KEY_BACK
); break;
525 case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS
); break;
526 case 0x102d: lg_map_key_clear(KEY_WWW
); break;
527 /* The following two are 'Start/answer call' and 'End/reject call'
529 case 0x1031: lg_map_key_clear(KEY_OK
); break;
530 case 0x1032: lg_map_key_clear(KEY_CANCEL
); break;
531 case 0x1041: lg_map_key_clear(KEY_BATTERY
); break;
532 case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR
); break;
533 case 0x1043: lg_map_key_clear(KEY_SPREADSHEET
); break;
534 case 0x1044: lg_map_key_clear(KEY_PRESENTATION
); break;
535 case 0x1045: lg_map_key_clear(KEY_UNDO
); break;
536 case 0x1046: lg_map_key_clear(KEY_REDO
); break;
537 case 0x1047: lg_map_key_clear(KEY_PRINT
); break;
538 case 0x1048: lg_map_key_clear(KEY_SAVE
); break;
539 case 0x1049: lg_map_key_clear(KEY_PROG1
); break;
540 case 0x104a: lg_map_key_clear(KEY_PROG2
); break;
541 case 0x104b: lg_map_key_clear(KEY_PROG3
); break;
542 case 0x104c: lg_map_key_clear(KEY_PROG4
); break;
550 static int lg_input_mapping(struct hid_device
*hdev
, struct hid_input
*hi
,
551 struct hid_field
*field
, struct hid_usage
*usage
,
552 unsigned long **bit
, int *max
)
554 /* extended mapping for certain Logitech hardware (Logitech cordless
556 static const u8 e_keymap
[] = {
557 0,216, 0,213,175,156, 0, 0, 0, 0,
558 144, 0, 0, 0, 0, 0, 0, 0, 0,212,
559 174,167,152,161,112, 0, 0, 0,154, 0,
560 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
561 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
562 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
563 0, 0, 0, 0, 0,183,184,185,186,187,
564 188,189,190,191,192,193,194, 0, 0, 0
566 struct lg_drv_data
*drv_data
= hid_get_drvdata(hdev
);
567 unsigned int hid
= usage
->hid
;
569 if (hdev
->product
== USB_DEVICE_ID_LOGITECH_RECEIVER
&&
570 lg_ultrax_remote_mapping(hi
, usage
, bit
, max
))
573 if (hdev
->product
== USB_DEVICE_ID_DINOVO_MINI
&&
574 lg_dinovo_mapping(hi
, usage
, bit
, max
))
577 if ((drv_data
->quirks
& LG_WIRELESS
) && lg_wireless_mapping(hi
, usage
, bit
, max
))
580 if ((hid
& HID_USAGE_PAGE
) != HID_UP_BUTTON
)
585 /* Special handling for Logitech Cordless Desktop */
586 if (field
->application
== HID_GD_MOUSE
) {
587 if ((drv_data
->quirks
& LG_IGNORE_DOUBLED_WHEEL
) &&
588 (hid
== 7 || hid
== 8))
591 if ((drv_data
->quirks
& LG_EXPANDED_KEYMAP
) &&
592 hid
< ARRAY_SIZE(e_keymap
) &&
593 e_keymap
[hid
] != 0) {
594 hid_map_usage(hi
, usage
, bit
, max
, EV_KEY
,
603 static int lg_input_mapped(struct hid_device
*hdev
, struct hid_input
*hi
,
604 struct hid_field
*field
, struct hid_usage
*usage
,
605 unsigned long **bit
, int *max
)
607 struct lg_drv_data
*drv_data
= hid_get_drvdata(hdev
);
609 if ((drv_data
->quirks
& LG_BAD_RELATIVE_KEYS
) && usage
->type
== EV_KEY
&&
610 (field
->flags
& HID_MAIN_ITEM_RELATIVE
))
611 field
->flags
&= ~HID_MAIN_ITEM_RELATIVE
;
613 if ((drv_data
->quirks
& LG_DUPLICATE_USAGES
) && (usage
->type
== EV_KEY
||
614 usage
->type
== EV_REL
|| usage
->type
== EV_ABS
))
615 clear_bit(usage
->code
, *bit
);
617 /* Ensure that Logitech wheels are not given a default fuzz/flat value */
618 if (usage
->type
== EV_ABS
&& (usage
->code
== ABS_X
||
619 usage
->code
== ABS_Y
|| usage
->code
== ABS_Z
||
620 usage
->code
== ABS_RZ
)) {
621 switch (hdev
->product
) {
622 case USB_DEVICE_ID_LOGITECH_WHEEL
:
623 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL
:
624 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL
:
625 case USB_DEVICE_ID_LOGITECH_G25_WHEEL
:
626 case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL
:
627 case USB_DEVICE_ID_LOGITECH_G27_WHEEL
:
628 case USB_DEVICE_ID_LOGITECH_WII_WHEEL
:
629 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2
:
630 case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL
:
631 field
->application
= HID_GD_MULTIAXIS
;
641 static int lg_event(struct hid_device
*hdev
, struct hid_field
*field
,
642 struct hid_usage
*usage
, __s32 value
)
644 struct lg_drv_data
*drv_data
= hid_get_drvdata(hdev
);
646 if ((drv_data
->quirks
& LG_INVERT_HWHEEL
) && usage
->code
== REL_HWHEEL
) {
647 input_event(field
->hidinput
->input
, usage
->type
, usage
->code
,
651 if (drv_data
->quirks
& LG_FF4
) {
652 return lg4ff_adjust_input_event(hdev
, field
, usage
, value
, drv_data
);
658 static int lg_probe(struct hid_device
*hdev
, const struct hid_device_id
*id
)
660 unsigned int connect_mask
= HID_CONNECT_DEFAULT
;
661 struct lg_drv_data
*drv_data
;
664 drv_data
= kzalloc(sizeof(struct lg_drv_data
), GFP_KERNEL
);
666 hid_err(hdev
, "Insufficient memory, cannot allocate driver data\n");
669 drv_data
->quirks
= id
->driver_data
;
671 hid_set_drvdata(hdev
, (void *)drv_data
);
673 if (drv_data
->quirks
& LG_NOGET
)
674 hdev
->quirks
|= HID_QUIRK_NOGET
;
676 ret
= hid_parse(hdev
);
678 hid_err(hdev
, "parse failed\n");
682 if (drv_data
->quirks
& (LG_FF
| LG_FF2
| LG_FF3
| LG_FF4
))
683 connect_mask
&= ~HID_CONNECT_FF
;
685 ret
= hid_hw_start(hdev
, connect_mask
);
687 hid_err(hdev
, "hw start failed\n");
691 /* Setup wireless link with Logitech Wii wheel */
692 if (hdev
->product
== USB_DEVICE_ID_LOGITECH_WII_WHEEL
) {
693 unsigned char buf
[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
695 ret
= hdev
->hid_output_raw_report(hdev
, buf
, sizeof(buf
), HID_FEATURE_REPORT
);
698 /* insert a little delay of 10 jiffies ~ 40ms */
699 wait_queue_head_t wait
;
700 init_waitqueue_head (&wait
);
701 wait_event_interruptible_timeout(wait
, 0, 10);
703 /* Select random Address */
705 get_random_bytes(&buf
[2], 2);
707 ret
= hdev
->hid_output_raw_report(hdev
, buf
, sizeof(buf
), HID_FEATURE_REPORT
);
711 if (drv_data
->quirks
& LG_FF
)
713 if (drv_data
->quirks
& LG_FF2
)
715 if (drv_data
->quirks
& LG_FF3
)
717 if (drv_data
->quirks
& LG_FF4
)
726 static void lg_remove(struct hid_device
*hdev
)
728 struct lg_drv_data
*drv_data
= hid_get_drvdata(hdev
);
729 if (drv_data
->quirks
& LG_FF4
)
736 static const struct hid_device_id lg_devices
[] = {
737 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_MX3000_RECEIVER
),
738 .driver_data
= LG_RDESC
| LG_WIRELESS
},
739 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_S510_RECEIVER
),
740 .driver_data
= LG_RDESC
| LG_WIRELESS
},
741 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_S510_RECEIVER_2
),
742 .driver_data
= LG_RDESC
| LG_WIRELESS
},
744 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_RECEIVER
),
745 .driver_data
= LG_BAD_RELATIVE_KEYS
},
747 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_DINOVO_DESKTOP
),
748 .driver_data
= LG_DUPLICATE_USAGES
},
749 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_DINOVO_EDGE
),
750 .driver_data
= LG_DUPLICATE_USAGES
},
751 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_DINOVO_MINI
),
752 .driver_data
= LG_DUPLICATE_USAGES
},
754 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_ELITE_KBD
),
755 .driver_data
= LG_IGNORE_DOUBLED_WHEEL
| LG_EXPANDED_KEYMAP
},
756 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500
),
757 .driver_data
= LG_IGNORE_DOUBLED_WHEEL
| LG_EXPANDED_KEYMAP
},
759 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_EXTREME_3D
),
760 .driver_data
= LG_NOGET
},
761 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_WHEEL
),
762 .driver_data
= LG_NOGET
| LG_FF4
},
764 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD
),
765 .driver_data
= LG_FF2
},
766 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_RUMBLEPAD
),
767 .driver_data
= LG_FF
},
768 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2
),
769 .driver_data
= LG_FF
},
770 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D
),
771 .driver_data
= LG_FF
},
772 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO
),
773 .driver_data
= LG_FF
},
774 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL
),
775 .driver_data
= LG_NOGET
| LG_FF4
},
776 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2
),
777 .driver_data
= LG_FF4
},
778 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL
),
779 .driver_data
= LG_FF2
},
780 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_G25_WHEEL
),
781 .driver_data
= LG_FF4
},
782 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL
),
783 .driver_data
= LG_FF4
},
784 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_G27_WHEEL
),
785 .driver_data
= LG_FF4
},
786 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_DFP_WHEEL
),
787 .driver_data
= LG_NOGET
| LG_FF4
},
788 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_WII_WHEEL
),
789 .driver_data
= LG_FF4
},
790 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG
),
791 .driver_data
= LG_FF
},
792 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2
),
793 .driver_data
= LG_FF2
},
794 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940
),
795 .driver_data
= LG_FF3
},
796 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_SPACENAVIGATOR
),
797 .driver_data
= LG_RDESC_REL_ABS
},
798 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH
, USB_DEVICE_ID_SPACETRAVELLER
),
799 .driver_data
= LG_RDESC_REL_ABS
},
803 MODULE_DEVICE_TABLE(hid
, lg_devices
);
805 static struct hid_driver lg_driver
= {
807 .id_table
= lg_devices
,
808 .report_fixup
= lg_report_fixup
,
809 .input_mapping
= lg_input_mapping
,
810 .input_mapped
= lg_input_mapped
,
815 module_hid_driver(lg_driver
);
817 MODULE_LICENSE("GPL");