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
51 /* Fixed report descriptors for Logitech Driving Force (and Pro)
54 * The original descriptors hide the separate throttle and brake axes in
55 * a custom vendor usage page, providing only a combined value as
57 * These descriptors remove the combined Y axis and instead report
58 * separate throttle (Y) and brake (RZ).
60 static __u8 df_rdesc_fixed[] = {
61 0x05, 0x01, /* Usage Page (Desktop), */
62 0x09, 0x04, /* Usage (Joystik), */
63 0xA1, 0x01, /* Collection (Application), */
64 0xA1, 0x02, /* Collection (Logical), */
65 0x95, 0x01, /* Report Count (1), */
66 0x75, 0x0A, /* Report Size (10), */
67 0x14, /* Logical Minimum (0), */
68 0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
69 0x34, /* Physical Minimum (0), */
70 0x46, 0xFF, 0x03, /* Physical Maximum (1023), */
71 0x09, 0x30, /* Usage (X), */
72 0x81, 0x02, /* Input (Variable), */
73 0x95, 0x0C, /* Report Count (12), */
74 0x75, 0x01, /* Report Size (1), */
75 0x25, 0x01, /* Logical Maximum (1), */
76 0x45, 0x01, /* Physical Maximum (1), */
77 0x05, 0x09, /* Usage (Buttons), */
78 0x19, 0x01, /* Usage Minimum (1), */
79 0x29, 0x0c, /* Usage Maximum (12), */
80 0x81, 0x02, /* Input (Variable), */
81 0x95, 0x02, /* Report Count (2), */
82 0x06, 0x00, 0xFF, /* Usage Page (Vendor: 65280), */
83 0x09, 0x01, /* Usage (?: 1), */
84 0x81, 0x02, /* Input (Variable), */
85 0x05, 0x01, /* Usage Page (Desktop), */
86 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
87 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
88 0x95, 0x01, /* Report Count (1), */
89 0x75, 0x08, /* Report Size (8), */
90 0x81, 0x02, /* Input (Variable), */
91 0x25, 0x07, /* Logical Maximum (7), */
92 0x46, 0x3B, 0x01, /* Physical Maximum (315), */
93 0x75, 0x04, /* Report Size (4), */
94 0x65, 0x14, /* Unit (Degrees), */
95 0x09, 0x39, /* Usage (Hat Switch), */
96 0x81, 0x42, /* Input (Variable, Null State), */
97 0x75, 0x01, /* Report Size (1), */
98 0x95, 0x04, /* Report Count (4), */
99 0x65, 0x00, /* Unit (none), */
100 0x06, 0x00, 0xFF, /* Usage Page (Vendor: 65280), */
101 0x09, 0x01, /* Usage (?: 1), */
102 0x25, 0x01, /* Logical Maximum (1), */
103 0x45, 0x01, /* Physical Maximum (1), */
104 0x81, 0x02, /* Input (Variable), */
105 0x05, 0x01, /* Usage Page (Desktop), */
106 0x95, 0x01, /* Report Count (1), */
107 0x75, 0x08, /* Report Size (8), */
108 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
109 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
110 0x09, 0x31, /* Usage (Y), */
111 0x81, 0x02, /* Input (Variable), */
112 0x09, 0x35, /* Usage (Rz), */
113 0x81, 0x02, /* Input (Variable), */
114 0xC0, /* End Collection, */
115 0xA1, 0x02, /* Collection (Logical), */
116 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
117 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
118 0x95, 0x07, /* Report Count (7), */
119 0x75, 0x08, /* Report Size (8), */
120 0x09, 0x03, /* Usage (?: 3), */
121 0x91, 0x02, /* Output (Variable), */
122 0xC0, /* End Collection, */
123 0xC0 /* End Collection */
126 static __u8 dfp_rdesc_fixed[] = {
127 0x05, 0x01, /* Usage Page (Desktop), */
128 0x09, 0x04, /* Usage (Joystik), */
129 0xA1, 0x01, /* Collection (Application), */
130 0xA1, 0x02, /* Collection (Logical), */
131 0x95, 0x01, /* Report Count (1), */
132 0x75, 0x0E, /* Report Size (14), */
133 0x14, /* Logical Minimum (0), */
134 0x26, 0xFF, 0x3F, /* Logical Maximum (16383), */
135 0x34, /* Physical Minimum (0), */
136 0x46, 0xFF, 0x3F, /* Physical Maximum (16383), */
137 0x09, 0x30, /* Usage (X), */
138 0x81, 0x02, /* Input (Variable), */
139 0x95, 0x0E, /* Report Count (14), */
140 0x75, 0x01, /* Report Size (1), */
141 0x25, 0x01, /* Logical Maximum (1), */
142 0x45, 0x01, /* Physical Maximum (1), */
143 0x05, 0x09, /* Usage Page (Button), */
144 0x19, 0x01, /* Usage Minimum (01h), */
145 0x29, 0x0E, /* Usage Maximum (0Eh), */
146 0x81, 0x02, /* Input (Variable), */
147 0x05, 0x01, /* Usage Page (Desktop), */
148 0x95, 0x01, /* Report Count (1), */
149 0x75, 0x04, /* Report Size (4), */
150 0x25, 0x07, /* Logical Maximum (7), */
151 0x46, 0x3B, 0x01, /* Physical Maximum (315), */
152 0x65, 0x14, /* Unit (Degrees), */
153 0x09, 0x39, /* Usage (Hat Switch), */
154 0x81, 0x42, /* Input (Variable, Nullstate), */
155 0x65, 0x00, /* Unit, */
156 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
157 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
158 0x75, 0x08, /* Report Size (8), */
159 0x81, 0x01, /* Input (Constant), */
160 0x09, 0x31, /* Usage (Y), */
161 0x81, 0x02, /* Input (Variable), */
162 0x09, 0x35, /* Usage (Rz), */
163 0x81, 0x02, /* Input (Variable), */
164 0x81, 0x01, /* Input (Constant), */
165 0xC0, /* End Collection, */
166 0xA1, 0x02, /* Collection (Logical), */
167 0x09, 0x02, /* Usage (02h), */
168 0x95, 0x07, /* Report Count (7), */
169 0x91, 0x02, /* Output (Variable), */
170 0xC0, /* End Collection, */
171 0xC0 /* End Collection */
174 static __u8 fv_rdesc_fixed[] = {
175 0x05, 0x01, /* Usage Page (Desktop), */
176 0x09, 0x04, /* Usage (Joystik), */
177 0xA1, 0x01, /* Collection (Application), */
178 0xA1, 0x02, /* Collection (Logical), */
179 0x95, 0x01, /* Report Count (1), */
180 0x75, 0x0A, /* Report Size (10), */
181 0x15, 0x00, /* Logical Minimum (0), */
182 0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
183 0x35, 0x00, /* Physical Minimum (0), */
184 0x46, 0xFF, 0x03, /* Physical Maximum (1023), */
185 0x09, 0x30, /* Usage (X), */
186 0x81, 0x02, /* Input (Variable), */
187 0x95, 0x0C, /* Report Count (12), */
188 0x75, 0x01, /* Report Size (1), */
189 0x25, 0x01, /* Logical Maximum (1), */
190 0x45, 0x01, /* Physical Maximum (1), */
191 0x05, 0x09, /* Usage Page (Button), */
192 0x19, 0x01, /* Usage Minimum (01h), */
193 0x29, 0x0C, /* Usage Maximum (0Ch), */
194 0x81, 0x02, /* Input (Variable), */
195 0x95, 0x02, /* Report Count (2), */
196 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
197 0x09, 0x01, /* Usage (01h), */
198 0x81, 0x02, /* Input (Variable), */
199 0x09, 0x02, /* Usage (02h), */
200 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
201 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
202 0x95, 0x01, /* Report Count (1), */
203 0x75, 0x08, /* Report Size (8), */
204 0x81, 0x02, /* Input (Variable), */
205 0x05, 0x01, /* Usage Page (Desktop), */
206 0x25, 0x07, /* Logical Maximum (7), */
207 0x46, 0x3B, 0x01, /* Physical Maximum (315), */
208 0x75, 0x04, /* Report Size (4), */
209 0x65, 0x14, /* Unit (Degrees), */
210 0x09, 0x39, /* Usage (Hat Switch), */
211 0x81, 0x42, /* Input (Variable, Null State), */
212 0x75, 0x01, /* Report Size (1), */
213 0x95, 0x04, /* Report Count (4), */
214 0x65, 0x00, /* Unit, */
215 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
216 0x09, 0x01, /* Usage (01h), */
217 0x25, 0x01, /* Logical Maximum (1), */
218 0x45, 0x01, /* Physical Maximum (1), */
219 0x81, 0x02, /* Input (Variable), */
220 0x05, 0x01, /* Usage Page (Desktop), */
221 0x95, 0x01, /* Report Count (1), */
222 0x75, 0x08, /* Report Size (8), */
223 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
224 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
225 0x09, 0x31, /* Usage (Y), */
226 0x81, 0x02, /* Input (Variable), */
227 0x09, 0x32, /* Usage (Z), */
228 0x81, 0x02, /* Input (Variable), */
229 0xC0, /* End Collection, */
230 0xA1, 0x02, /* Collection (Logical), */
231 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
232 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
233 0x95, 0x07, /* Report Count (7), */
234 0x75, 0x08, /* Report Size (8), */
235 0x09, 0x03, /* Usage (03h), */
236 0x91, 0x02, /* Output (Variable), */
237 0xC0, /* End Collection, */
238 0xC0 /* End Collection */
241 static __u8 momo_rdesc_fixed[] = {
242 0x05, 0x01, /* Usage Page (Desktop), */
243 0x09, 0x04, /* Usage (Joystik), */
244 0xA1, 0x01, /* Collection (Application), */
245 0xA1, 0x02, /* Collection (Logical), */
246 0x95, 0x01, /* Report Count (1), */
247 0x75, 0x0A, /* Report Size (10), */
248 0x15, 0x00, /* Logical Minimum (0), */
249 0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
250 0x35, 0x00, /* Physical Minimum (0), */
251 0x46, 0xFF, 0x03, /* Physical Maximum (1023), */
252 0x09, 0x30, /* Usage (X), */
253 0x81, 0x02, /* Input (Variable), */
254 0x95, 0x08, /* Report Count (8), */
255 0x75, 0x01, /* Report Size (1), */
256 0x25, 0x01, /* Logical Maximum (1), */
257 0x45, 0x01, /* Physical Maximum (1), */
258 0x05, 0x09, /* Usage Page (Button), */
259 0x19, 0x01, /* Usage Minimum (01h), */
260 0x29, 0x08, /* Usage Maximum (08h), */
261 0x81, 0x02, /* Input (Variable), */
262 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
263 0x75, 0x0E, /* Report Size (14), */
264 0x95, 0x01, /* Report Count (1), */
265 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
266 0x46, 0xFF, 0x00, /* Physical Maximum (255), */
267 0x09, 0x00, /* Usage (00h), */
268 0x81, 0x02, /* Input (Variable), */
269 0x05, 0x01, /* Usage Page (Desktop), */
270 0x75, 0x08, /* Report Size (8), */
271 0x09, 0x31, /* Usage (Y), */
272 0x81, 0x02, /* Input (Variable), */
273 0x09, 0x32, /* Usage (Z), */
274 0x81, 0x02, /* Input (Variable), */
275 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
276 0x09, 0x01, /* Usage (01h), */
277 0x81, 0x02, /* Input (Variable), */
278 0xC0, /* End Collection, */
279 0xA1, 0x02, /* Collection (Logical), */
280 0x09, 0x02, /* Usage (02h), */
281 0x95, 0x07, /* Report Count (7), */
282 0x91, 0x02, /* Output (Variable), */
283 0xC0, /* End Collection, */
284 0xC0 /* End Collection */
288 * Certain Logitech keyboards send in report #3 keys which are far
289 * above the logical maximum described in descriptor. This extends
290 * the original value of 0x28c of logical maximum to 0x104d
292 static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
295 struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
296 struct usb_device_descriptor *udesc;
297 __u16 bcdDevice, rev_maj, rev_min;
299 if ((drv_data->quirks & LG_RDESC) && *rsize >= 90 && rdesc[83] == 0x26 &&
300 rdesc[84] == 0x8c && rdesc[85] == 0x02) {
302 "fixing up Logitech keyboard report descriptor\n");
303 rdesc[84] = rdesc[89] = 0x4d;
304 rdesc[85] = rdesc[90] = 0x10;
306 if ((drv_data->quirks & LG_RDESC_REL_ABS) && *rsize >= 50 &&
307 rdesc[32] == 0x81 && rdesc[33] == 0x06 &&
308 rdesc[49] == 0x81 && rdesc[50] == 0x06) {
310 "fixing up rel/abs in Logitech report descriptor\n");
311 rdesc[33] = rdesc[50] = 0x02;
314 switch (hdev->product) {
316 /* Several wheels report as this id when operating in emulation mode. */
317 case USB_DEVICE_ID_LOGITECH_WHEEL:
318 udesc = &(hid_to_usb_dev(hdev)->descriptor);
320 hid_err(hdev, "NULL USB device descriptor\n");
323 bcdDevice = le16_to_cpu(udesc->bcdDevice);
324 rev_maj = bcdDevice >> 8;
325 rev_min = bcdDevice & 0xff;
327 /* Update the report descriptor for only the Driving Force wheel */
328 if (rev_maj == 1 && rev_min == 2 &&
329 *rsize == DF_RDESC_ORIG_SIZE) {
331 "fixing up Logitech Driving Force report descriptor\n");
332 rdesc = df_rdesc_fixed;
333 *rsize = sizeof(df_rdesc_fixed);
337 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
338 if (*rsize == MOMO_RDESC_ORIG_SIZE) {
340 "fixing up Logitech Momo Force (Red) report descriptor\n");
341 rdesc = momo_rdesc_fixed;
342 *rsize = sizeof(momo_rdesc_fixed);
346 case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL:
347 if (*rsize == FV_RDESC_ORIG_SIZE) {
349 "fixing up Logitech Formula Vibration report descriptor\n");
350 rdesc = fv_rdesc_fixed;
351 *rsize = sizeof(fv_rdesc_fixed);
355 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
356 if (*rsize == DFP_RDESC_ORIG_SIZE) {
358 "fixing up Logitech Driving Force Pro report descriptor\n");
359 rdesc = dfp_rdesc_fixed;
360 *rsize = sizeof(dfp_rdesc_fixed);
364 case USB_DEVICE_ID_LOGITECH_WII_WHEEL:
365 if (*rsize >= 101 && rdesc[41] == 0x95 && rdesc[42] == 0x0B &&
366 rdesc[47] == 0x05 && rdesc[48] == 0x09) {
367 hid_info(hdev, "fixing up Logitech Speed Force Wireless report descriptor\n");
379 #define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
382 static int lg_ultrax_remote_mapping(struct hid_input *hi,
383 struct hid_usage *usage, unsigned long **bit, int *max)
385 if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
388 set_bit(EV_REP, hi->input->evbit);
389 switch (usage->hid & HID_USAGE) {
390 /* Reported on Logitech Ultra X Media Remote */
391 case 0x004: lg_map_key_clear(KEY_AGAIN); break;
392 case 0x00d: lg_map_key_clear(KEY_HOME); break;
393 case 0x024: lg_map_key_clear(KEY_SHUFFLE); break;
394 case 0x025: lg_map_key_clear(KEY_TV); break;
395 case 0x026: lg_map_key_clear(KEY_MENU); break;
396 case 0x031: lg_map_key_clear(KEY_AUDIO); break;
397 case 0x032: lg_map_key_clear(KEY_TEXT); break;
398 case 0x033: lg_map_key_clear(KEY_LAST); break;
399 case 0x047: lg_map_key_clear(KEY_MP3); break;
400 case 0x048: lg_map_key_clear(KEY_DVD); break;
401 case 0x049: lg_map_key_clear(KEY_MEDIA); break;
402 case 0x04a: lg_map_key_clear(KEY_VIDEO); break;
403 case 0x04b: lg_map_key_clear(KEY_ANGLE); break;
404 case 0x04c: lg_map_key_clear(KEY_LANGUAGE); break;
405 case 0x04d: lg_map_key_clear(KEY_SUBTITLE); break;
406 case 0x051: lg_map_key_clear(KEY_RED); break;
407 case 0x052: lg_map_key_clear(KEY_CLOSE); break;
415 static int lg_dinovo_mapping(struct hid_input *hi, struct hid_usage *usage,
416 unsigned long **bit, int *max)
418 if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
421 switch (usage->hid & HID_USAGE) {
423 case 0x00d: lg_map_key_clear(KEY_MEDIA); break;
431 static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
432 unsigned long **bit, int *max)
434 if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
437 switch (usage->hid & HID_USAGE) {
438 case 0x1001: lg_map_key_clear(KEY_MESSENGER); break;
439 case 0x1003: lg_map_key_clear(KEY_SOUND); break;
440 case 0x1004: lg_map_key_clear(KEY_VIDEO); break;
441 case 0x1005: lg_map_key_clear(KEY_AUDIO); break;
442 case 0x100a: lg_map_key_clear(KEY_DOCUMENTS); break;
443 /* The following two entries are Playlist 1 and 2 on the MX3200 */
444 case 0x100f: lg_map_key_clear(KEY_FN_1); break;
445 case 0x1010: lg_map_key_clear(KEY_FN_2); break;
446 case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG); break;
447 case 0x1012: lg_map_key_clear(KEY_NEXTSONG); break;
448 case 0x1013: lg_map_key_clear(KEY_CAMERA); break;
449 case 0x1014: lg_map_key_clear(KEY_MESSENGER); break;
450 case 0x1015: lg_map_key_clear(KEY_RECORD); break;
451 case 0x1016: lg_map_key_clear(KEY_PLAYER); break;
452 case 0x1017: lg_map_key_clear(KEY_EJECTCD); break;
453 case 0x1018: lg_map_key_clear(KEY_MEDIA); break;
454 case 0x1019: lg_map_key_clear(KEY_PROG1); break;
455 case 0x101a: lg_map_key_clear(KEY_PROG2); break;
456 case 0x101b: lg_map_key_clear(KEY_PROG3); break;
457 case 0x101c: lg_map_key_clear(KEY_CYCLEWINDOWS); break;
458 case 0x101f: lg_map_key_clear(KEY_ZOOMIN); break;
459 case 0x1020: lg_map_key_clear(KEY_ZOOMOUT); break;
460 case 0x1021: lg_map_key_clear(KEY_ZOOMRESET); break;
461 case 0x1023: lg_map_key_clear(KEY_CLOSE); break;
462 case 0x1027: lg_map_key_clear(KEY_MENU); break;
463 /* this one is marked as 'Rotate' */
464 case 0x1028: lg_map_key_clear(KEY_ANGLE); break;
465 case 0x1029: lg_map_key_clear(KEY_SHUFFLE); break;
466 case 0x102a: lg_map_key_clear(KEY_BACK); break;
467 case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS); break;
468 case 0x102d: lg_map_key_clear(KEY_WWW); break;
469 /* The following two are 'Start/answer call' and 'End/reject call'
471 case 0x1031: lg_map_key_clear(KEY_OK); break;
472 case 0x1032: lg_map_key_clear(KEY_CANCEL); break;
473 case 0x1041: lg_map_key_clear(KEY_BATTERY); break;
474 case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR); break;
475 case 0x1043: lg_map_key_clear(KEY_SPREADSHEET); break;
476 case 0x1044: lg_map_key_clear(KEY_PRESENTATION); break;
477 case 0x1045: lg_map_key_clear(KEY_UNDO); break;
478 case 0x1046: lg_map_key_clear(KEY_REDO); break;
479 case 0x1047: lg_map_key_clear(KEY_PRINT); break;
480 case 0x1048: lg_map_key_clear(KEY_SAVE); break;
481 case 0x1049: lg_map_key_clear(KEY_PROG1); break;
482 case 0x104a: lg_map_key_clear(KEY_PROG2); break;
483 case 0x104b: lg_map_key_clear(KEY_PROG3); break;
484 case 0x104c: lg_map_key_clear(KEY_PROG4); break;
492 static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
493 struct hid_field *field, struct hid_usage *usage,
494 unsigned long **bit, int *max)
496 /* extended mapping for certain Logitech hardware (Logitech cordless
498 static const u8 e_keymap[] = {
499 0,216, 0,213,175,156, 0, 0, 0, 0,
500 144, 0, 0, 0, 0, 0, 0, 0, 0,212,
501 174,167,152,161,112, 0, 0, 0,154, 0,
502 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
503 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
504 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
505 0, 0, 0, 0, 0,183,184,185,186,187,
506 188,189,190,191,192,193,194, 0, 0, 0
508 struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
509 unsigned int hid = usage->hid;
511 if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER &&
512 lg_ultrax_remote_mapping(hi, usage, bit, max))
515 if (hdev->product == USB_DEVICE_ID_DINOVO_MINI &&
516 lg_dinovo_mapping(hi, usage, bit, max))
519 if ((drv_data->quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
522 if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
527 /* Special handling for Logitech Cordless Desktop */
528 if (field->application == HID_GD_MOUSE) {
529 if ((drv_data->quirks & LG_IGNORE_DOUBLED_WHEEL) &&
530 (hid == 7 || hid == 8))
533 if ((drv_data->quirks & LG_EXPANDED_KEYMAP) &&
534 hid < ARRAY_SIZE(e_keymap) &&
535 e_keymap[hid] != 0) {
536 hid_map_usage(hi, usage, bit, max, EV_KEY,
545 static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
546 struct hid_field *field, struct hid_usage *usage,
547 unsigned long **bit, int *max)
549 struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
551 if ((drv_data->quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
552 (field->flags & HID_MAIN_ITEM_RELATIVE))
553 field->flags &= ~HID_MAIN_ITEM_RELATIVE;
555 if ((drv_data->quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY ||
556 usage->type == EV_REL || usage->type == EV_ABS))
557 clear_bit(usage->code, *bit);
559 /* Ensure that Logitech wheels are not given a default fuzz/flat value */
560 if (usage->type == EV_ABS && (usage->code == ABS_X ||
561 usage->code == ABS_Y || usage->code == ABS_Z ||
562 usage->code == ABS_RZ)) {
563 switch (hdev->product) {
564 case USB_DEVICE_ID_LOGITECH_WHEEL:
565 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
566 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
567 case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
568 case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
569 case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
570 case USB_DEVICE_ID_LOGITECH_WII_WHEEL:
571 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
572 case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL:
573 field->application = HID_GD_MULTIAXIS;
583 static int lg_event(struct hid_device *hdev, struct hid_field *field,
584 struct hid_usage *usage, __s32 value)
586 struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
588 if ((drv_data->quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
589 input_event(field->hidinput->input, usage->type, usage->code,
593 if (drv_data->quirks & LG_FF4) {
594 return lg4ff_adjust_input_event(hdev, field, usage, value, drv_data);
600 static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
602 unsigned int connect_mask = HID_CONNECT_DEFAULT;
603 struct lg_drv_data *drv_data;
606 drv_data = kzalloc(sizeof(struct lg_drv_data), GFP_KERNEL);
608 hid_err(hdev, "Insufficient memory, cannot allocate driver data\n");
611 drv_data->quirks = id->driver_data;
613 hid_set_drvdata(hdev, (void *)drv_data);
615 if (drv_data->quirks & LG_NOGET)
616 hdev->quirks |= HID_QUIRK_NOGET;
618 ret = hid_parse(hdev);
620 hid_err(hdev, "parse failed\n");
624 if (drv_data->quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4))
625 connect_mask &= ~HID_CONNECT_FF;
627 ret = hid_hw_start(hdev, connect_mask);
629 hid_err(hdev, "hw start failed\n");
633 /* Setup wireless link with Logitech Wii wheel */
634 if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
635 unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
637 ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
640 /* insert a little delay of 10 jiffies ~ 40ms */
641 wait_queue_head_t wait;
642 init_waitqueue_head (&wait);
643 wait_event_interruptible_timeout(wait, 0, 10);
645 /* Select random Address */
647 get_random_bytes(&buf[2], 2);
649 ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
653 if (drv_data->quirks & LG_FF)
655 if (drv_data->quirks & LG_FF2)
657 if (drv_data->quirks & LG_FF3)
659 if (drv_data->quirks & LG_FF4)
668 static void lg_remove(struct hid_device *hdev)
670 struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
671 if (drv_data->quirks & LG_FF4)
678 static const struct hid_device_id lg_devices[] = {
679 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER),
680 .driver_data = LG_RDESC | LG_WIRELESS },
681 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER),
682 .driver_data = LG_RDESC | LG_WIRELESS },
683 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2),
684 .driver_data = LG_RDESC | LG_WIRELESS },
686 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER),
687 .driver_data = LG_BAD_RELATIVE_KEYS },
689 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP),
690 .driver_data = LG_DUPLICATE_USAGES },
691 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE),
692 .driver_data = LG_DUPLICATE_USAGES },
693 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI),
694 .driver_data = LG_DUPLICATE_USAGES },
696 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD),
697 .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
698 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500),
699 .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
701 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
702 .driver_data = LG_NOGET },
703 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
704 .driver_data = LG_NOGET | LG_FF4 },
706 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD),
707 .driver_data = LG_FF2 },
708 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD),
709 .driver_data = LG_FF },
710 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2),
711 .driver_data = LG_FF },
712 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D),
713 .driver_data = LG_FF },
714 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
715 .driver_data = LG_FF },
716 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL),
717 .driver_data = LG_NOGET | LG_FF4 },
718 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
719 .driver_data = LG_FF4 },
720 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL),
721 .driver_data = LG_FF2 },
722 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
723 .driver_data = LG_FF4 },
724 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL),
725 .driver_data = LG_FF4 },
726 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL),
727 .driver_data = LG_FF4 },
728 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL),
729 .driver_data = LG_NOGET | LG_FF4 },
730 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
731 .driver_data = LG_FF4 },
732 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
733 .driver_data = LG_FF },
734 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
735 .driver_data = LG_FF2 },
736 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940),
737 .driver_data = LG_FF3 },
738 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR),
739 .driver_data = LG_RDESC_REL_ABS },
740 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER),
741 .driver_data = LG_RDESC_REL_ABS },
745 MODULE_DEVICE_TABLE(hid, lg_devices);
747 static struct hid_driver lg_driver = {
749 .id_table = lg_devices,
750 .report_fixup = lg_report_fixup,
751 .input_mapping = lg_input_mapping,
752 .input_mapped = lg_input_mapped,
757 module_hid_driver(lg_driver);
759 MODULE_LICENSE("GPL");