blob: 6daa1927bf696824a58e1d58da497a2ddcd1941b [file] [log] [blame]
Jiri Slaby5f22a792008-05-16 11:49:19 +02001/*
2 * HID driver for some logitech "special" devices
3 *
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
Jiri Slaby5f22a792008-05-16 11:49:19 +02008 * Copyright (c) 2008 Jiri Slaby
Hendrik Iben2c6118e2010-10-04 15:39:49 +02009 * Copyright (c) 2010 Hendrik Iben
Jiri Slaby5f22a792008-05-16 11:49:19 +020010 */
11
12/*
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)
16 * any later version.
17 */
18
19#include <linux/device.h>
20#include <linux/hid.h>
21#include <linux/module.h>
Simon Wood32c88cb2010-09-22 13:19:42 +020022#include <linux/random.h>
23#include <linux/sched.h>
Paul Sbarra54bfe3f2013-02-17 11:53:13 -060024#include <linux/usb.h>
Simon Wood32c88cb2010-09-22 13:19:42 +020025#include <linux/wait.h>
Jiri Slaby5f22a792008-05-16 11:49:19 +020026
Paul Sbarra54bfe3f2013-02-17 11:53:13 -060027#include "usbhid/usbhid.h"
Jiri Slaby5f22a792008-05-16 11:49:19 +020028#include "hid-ids.h"
Jiri Slaby606bd0a2008-07-04 23:06:45 +020029#include "hid-lg.h"
Jiri Slaby5f22a792008-05-16 11:49:19 +020030
31#define LG_RDESC 0x001
32#define LG_BAD_RELATIVE_KEYS 0x002
33#define LG_DUPLICATE_USAGES 0x004
Jiri Slaby5f22a792008-05-16 11:49:19 +020034#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
Jiri Slaby606bd0a2008-07-04 23:06:45 +020039#define LG_FF 0x200
40#define LG_FF2 0x400
Jiri Kosina24985cf2009-11-13 10:45:53 +010041#define LG_RDESC_REL_ABS 0x800
Gary Stein74f292c2010-01-13 00:25:58 +010042#define LG_FF3 0x1000
Simon Wood32c88cb2010-09-22 13:19:42 +020043#define LG_FF4 0x2000
Jiri Slaby5f22a792008-05-16 11:49:19 +020044
Paul Sbarra54bfe3f2013-02-17 11:53:13 -060045/* Size of the original descriptors of the Driving Force (and Pro) wheels */
46#define DF_RDESC_ORIG_SIZE 130
Michael Bauerdc0a4f02011-06-02 15:40:14 -070047#define DFP_RDESC_ORIG_SIZE 97
48
Paul Sbarra54bfe3f2013-02-17 11:53:13 -060049/* Fixed report descriptors for Logitech Driving Force (and Pro)
50 * wheel controllers
Michael Bauerdc0a4f02011-06-02 15:40:14 -070051 *
Paul Sbarra54bfe3f2013-02-17 11:53:13 -060052 * The original descriptors hide the separate throttle and brake axes in
Michael Bauerdc0a4f02011-06-02 15:40:14 -070053 * a custom vendor usage page, providing only a combined value as
54 * GenericDesktop.Y.
Paul Sbarra54bfe3f2013-02-17 11:53:13 -060055 * These descriptors remove the combined Y axis and instead report
Michael Bauerdc0a4f02011-06-02 15:40:14 -070056 * separate throttle (Y) and brake (RZ).
57 */
Paul Sbarra54bfe3f2013-02-17 11:53:13 -060058static __u8 df_rdesc_fixed[] = {
590x05, 0x01, /* Usage Page (Desktop), */
600x09, 0x04, /* Usage (Joystik), */
610xA1, 0x01, /* Collection (Application), */
620xA1, 0x02, /* Collection (Logical), */
630x95, 0x01, /* Report Count (1), */
640x75, 0x0A, /* Report Size (10), */
650x14, /* Logical Minimum (0), */
660x26, 0xFF, 0x03, /* Logical Maximum (1023), */
670x34, /* Physical Minimum (0), */
680x46, 0xFF, 0x03, /* Physical Maximum (1023), */
690x09, 0x30, /* Usage (X), */
700x81, 0x02, /* Input (Variable), */
710x95, 0x0C, /* Report Count (12), */
720x75, 0x01, /* Report Size (1), */
730x25, 0x01, /* Logical Maximum (1), */
740x45, 0x01, /* Physical Maximum (1), */
750x05, 0x09, /* Usage (Buttons), */
760x19, 0x01, /* Usage Minimum (1), */
770x29, 0x0c, /* Usage Maximum (12), */
780x81, 0x02, /* Input (Variable), */
790x95, 0x02, /* Report Count (2), */
800x06, 0x00, 0xFF, /* Usage Page (Vendor: 65280), */
810x09, 0x01, /* Usage (?: 1), */
820x81, 0x02, /* Input (Variable), */
830x05, 0x01, /* Usage Page (Desktop), */
840x09, 0x31, /* Usage (Y), */
850x26, 0xFF, 0x00, /* Logical Maximum (255), */
860x46, 0xFF, 0x00, /* Physical Maximum (255), */
870x95, 0x01, /* Report Count (1), */
880x75, 0x08, /* Report Size (8), */
890x81, 0x02, /* Input (Variable), */
900x25, 0x07, /* Logical Maximum (7), */
910x46, 0x3B, 0x01, /* Physical Maximum (315), */
920x75, 0x04, /* Report Size (4), */
930x65, 0x14, /* Unit (Degrees), */
940x09, 0x39, /* Usage (Hat Switch), */
950x81, 0x42, /* Input (Variable, Null State), */
960x75, 0x01, /* Report Size (1), */
970x95, 0x04, /* Report Count (4), */
980x65, 0x00, /* Unit (none), */
990x06, 0x00, 0xFF, /* Usage Page (Vendor: 65280), */
1000x09, 0x01, /* Usage (?: 1), */
1010x25, 0x01, /* Logical Maximum (1), */
1020x45, 0x01, /* Physical Maximum (1), */
1030x81, 0x02, /* Input (Variable), */
1040x95, 0x02, /* Report Count (2), */
1050x75, 0x08, /* Report Size (8), */
1060x26, 0xFF, 0x00, /* Logical Maximum (255), */
1070x46, 0xFF, 0x00, /* Physical Maximum (255), */
1080x09, 0x02, /* Usage (?: 2), */
1090x81, 0x02, /* Input (Variable), */
1100xC0, /* End Collection, */
1110xA1, 0x02, /* Collection (Logical), */
1120x26, 0xFF, 0x00, /* Logical Maximum (255), */
1130x46, 0xFF, 0x00, /* Physical Maximum (255), */
1140x95, 0x07, /* Report Count (7), */
1150x75, 0x08, /* Report Size (8), */
1160x09, 0x03, /* Usage (?: 3), */
1170x91, 0x02, /* Output (Variable), */
1180xC0, /* End Collection, */
1190xC0 /* End Collection */
120};
121
Michael Bauerdc0a4f02011-06-02 15:40:14 -0700122static __u8 dfp_rdesc_fixed[] = {
1230x05, 0x01, /* Usage Page (Desktop), */
1240x09, 0x04, /* Usage (Joystik), */
1250xA1, 0x01, /* Collection (Application), */
1260xA1, 0x02, /* Collection (Logical), */
1270x95, 0x01, /* Report Count (1), */
1280x75, 0x0E, /* Report Size (14), */
1290x14, /* Logical Minimum (0), */
1300x26, 0xFF, 0x3F, /* Logical Maximum (16383), */
1310x34, /* Physical Minimum (0), */
1320x46, 0xFF, 0x3F, /* Physical Maximum (16383), */
1330x09, 0x30, /* Usage (X), */
1340x81, 0x02, /* Input (Variable), */
1350x95, 0x0E, /* Report Count (14), */
1360x75, 0x01, /* Report Size (1), */
1370x25, 0x01, /* Logical Maximum (1), */
1380x45, 0x01, /* Physical Maximum (1), */
1390x05, 0x09, /* Usage Page (Button), */
1400x19, 0x01, /* Usage Minimum (01h), */
1410x29, 0x0E, /* Usage Maximum (0Eh), */
1420x81, 0x02, /* Input (Variable), */
1430x05, 0x01, /* Usage Page (Desktop), */
1440x95, 0x01, /* Report Count (1), */
1450x75, 0x04, /* Report Size (4), */
1460x25, 0x07, /* Logical Maximum (7), */
1470x46, 0x3B, 0x01, /* Physical Maximum (315), */
1480x65, 0x14, /* Unit (Degrees), */
1490x09, 0x39, /* Usage (Hat Switch), */
1500x81, 0x42, /* Input (Variable, Nullstate), */
1510x65, 0x00, /* Unit, */
1520x26, 0xFF, 0x00, /* Logical Maximum (255), */
1530x46, 0xFF, 0x00, /* Physical Maximum (255), */
1540x75, 0x08, /* Report Size (8), */
1550x81, 0x01, /* Input (Constant), */
1560x09, 0x31, /* Usage (Y), */
1570x81, 0x02, /* Input (Variable), */
1580x09, 0x35, /* Usage (Rz), */
1590x81, 0x02, /* Input (Variable), */
1600x81, 0x01, /* Input (Constant), */
1610xC0, /* End Collection, */
1620xA1, 0x02, /* Collection (Logical), */
1630x09, 0x02, /* Usage (02h), */
1640x95, 0x07, /* Report Count (7), */
1650x91, 0x02, /* Output (Variable), */
1660xC0, /* End Collection, */
1670xC0 /* End Collection */
168};
169
Jiri Slaby5f22a792008-05-16 11:49:19 +0200170/*
171 * Certain Logitech keyboards send in report #3 keys which are far
172 * above the logical maximum described in descriptor. This extends
173 * the original value of 0x28c of logical maximum to 0x104d
174 */
Nikolai Kondrashov73e40082010-08-06 23:03:06 +0400175static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
176 unsigned int *rsize)
Jiri Slaby5f22a792008-05-16 11:49:19 +0200177{
Axel Lin25751552012-09-13 14:08:32 +0800178 struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
Paul Sbarra54bfe3f2013-02-17 11:53:13 -0600179 struct usb_device_descriptor *udesc;
180 __u16 bcdDevice, rev_maj, rev_min;
Jiri Slaby5f22a792008-05-16 11:49:19 +0200181
Michal Malý8577dbf2012-03-31 11:17:25 +0200182 if ((drv_data->quirks & LG_RDESC) && *rsize >= 90 && rdesc[83] == 0x26 &&
Jiri Slaby5f22a792008-05-16 11:49:19 +0200183 rdesc[84] == 0x8c && rdesc[85] == 0x02) {
Joe Perches4291ee32010-12-09 19:29:03 -0800184 hid_info(hdev,
185 "fixing up Logitech keyboard report descriptor\n");
Jiri Slaby5f22a792008-05-16 11:49:19 +0200186 rdesc[84] = rdesc[89] = 0x4d;
187 rdesc[85] = rdesc[90] = 0x10;
188 }
Michal Malý8577dbf2012-03-31 11:17:25 +0200189 if ((drv_data->quirks & LG_RDESC_REL_ABS) && *rsize >= 50 &&
Jiri Kosina24985cf2009-11-13 10:45:53 +0100190 rdesc[32] == 0x81 && rdesc[33] == 0x06 &&
191 rdesc[49] == 0x81 && rdesc[50] == 0x06) {
Joe Perches4291ee32010-12-09 19:29:03 -0800192 hid_info(hdev,
193 "fixing up rel/abs in Logitech report descriptor\n");
Jiri Kosina24985cf2009-11-13 10:45:53 +0100194 rdesc[33] = rdesc[50] = 0x02;
195 }
Michal Malý8577dbf2012-03-31 11:17:25 +0200196 if ((drv_data->quirks & LG_FF4) && *rsize >= 101 &&
Simon Wood32c88cb2010-09-22 13:19:42 +0200197 rdesc[41] == 0x95 && rdesc[42] == 0x0B &&
198 rdesc[47] == 0x05 && rdesc[48] == 0x09) {
Joe Perches4291ee32010-12-09 19:29:03 -0800199 hid_info(hdev, "fixing up Logitech Speed Force Wireless button descriptor\n");
Simon Wood32c88cb2010-09-22 13:19:42 +0200200 rdesc[41] = 0x05;
201 rdesc[42] = 0x09;
202 rdesc[47] = 0x95;
203 rdesc[48] = 0x0B;
204 }
Michael Bauerdc0a4f02011-06-02 15:40:14 -0700205
206 switch (hdev->product) {
Paul Sbarra54bfe3f2013-02-17 11:53:13 -0600207
208 /* Several wheels report as this id when operating in emulation mode. */
209 case USB_DEVICE_ID_LOGITECH_WHEEL:
210 udesc = &(hid_to_usb_dev(hdev)->descriptor);
211 if (!udesc) {
212 hid_err(hdev, "NULL USB device descriptor\n");
213 break;
214 }
215 bcdDevice = le16_to_cpu(udesc->bcdDevice);
216 rev_maj = bcdDevice >> 8;
217 rev_min = bcdDevice & 0xff;
218
219 /* Update the report descriptor for only the Driving Force wheel */
220 if (rev_maj == 1 && rev_min == 2 &&
221 *rsize == DF_RDESC_ORIG_SIZE) {
222 hid_info(hdev,
223 "fixing up Logitech Driving Force report descriptor\n");
224 rdesc = df_rdesc_fixed;
225 *rsize = sizeof(df_rdesc_fixed);
226 }
227 break;
228
Michael Bauerdc0a4f02011-06-02 15:40:14 -0700229 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
230 if (*rsize == DFP_RDESC_ORIG_SIZE) {
231 hid_info(hdev,
232 "fixing up Logitech Driving Force Pro report descriptor\n");
233 rdesc = dfp_rdesc_fixed;
234 *rsize = sizeof(dfp_rdesc_fixed);
235 }
236 break;
237 }
238
Nikolai Kondrashov73e40082010-08-06 23:03:06 +0400239 return rdesc;
Jiri Slaby5f22a792008-05-16 11:49:19 +0200240}
241
242#define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
243 EV_KEY, (c))
244
245static int lg_ultrax_remote_mapping(struct hid_input *hi,
246 struct hid_usage *usage, unsigned long **bit, int *max)
247{
248 if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
249 return 0;
250
251 set_bit(EV_REP, hi->input->evbit);
252 switch (usage->hid & HID_USAGE) {
253 /* Reported on Logitech Ultra X Media Remote */
254 case 0x004: lg_map_key_clear(KEY_AGAIN); break;
255 case 0x00d: lg_map_key_clear(KEY_HOME); break;
256 case 0x024: lg_map_key_clear(KEY_SHUFFLE); break;
257 case 0x025: lg_map_key_clear(KEY_TV); break;
258 case 0x026: lg_map_key_clear(KEY_MENU); break;
259 case 0x031: lg_map_key_clear(KEY_AUDIO); break;
260 case 0x032: lg_map_key_clear(KEY_TEXT); break;
261 case 0x033: lg_map_key_clear(KEY_LAST); break;
262 case 0x047: lg_map_key_clear(KEY_MP3); break;
263 case 0x048: lg_map_key_clear(KEY_DVD); break;
264 case 0x049: lg_map_key_clear(KEY_MEDIA); break;
265 case 0x04a: lg_map_key_clear(KEY_VIDEO); break;
266 case 0x04b: lg_map_key_clear(KEY_ANGLE); break;
267 case 0x04c: lg_map_key_clear(KEY_LANGUAGE); break;
268 case 0x04d: lg_map_key_clear(KEY_SUBTITLE); break;
269 case 0x051: lg_map_key_clear(KEY_RED); break;
270 case 0x052: lg_map_key_clear(KEY_CLOSE); break;
271
272 default:
273 return 0;
274 }
275 return 1;
276}
277
Jiri Kosina66d61be2009-11-24 18:22:20 +0100278static int lg_dinovo_mapping(struct hid_input *hi, struct hid_usage *usage,
279 unsigned long **bit, int *max)
280{
281 if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
282 return 0;
283
284 switch (usage->hid & HID_USAGE) {
285
286 case 0x00d: lg_map_key_clear(KEY_MEDIA); break;
287 default:
288 return 0;
289
290 }
291 return 1;
292}
293
Jiri Slaby5f22a792008-05-16 11:49:19 +0200294static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
295 unsigned long **bit, int *max)
296{
297 if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
298 return 0;
299
300 switch (usage->hid & HID_USAGE) {
301 case 0x1001: lg_map_key_clear(KEY_MESSENGER); break;
302 case 0x1003: lg_map_key_clear(KEY_SOUND); break;
303 case 0x1004: lg_map_key_clear(KEY_VIDEO); break;
304 case 0x1005: lg_map_key_clear(KEY_AUDIO); break;
305 case 0x100a: lg_map_key_clear(KEY_DOCUMENTS); break;
Lorenzo Castelli183922122010-04-16 19:00:31 +0200306 /* The following two entries are Playlist 1 and 2 on the MX3200 */
307 case 0x100f: lg_map_key_clear(KEY_FN_1); break;
308 case 0x1010: lg_map_key_clear(KEY_FN_2); break;
Jiri Slaby5f22a792008-05-16 11:49:19 +0200309 case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG); break;
310 case 0x1012: lg_map_key_clear(KEY_NEXTSONG); break;
311 case 0x1013: lg_map_key_clear(KEY_CAMERA); break;
312 case 0x1014: lg_map_key_clear(KEY_MESSENGER); break;
313 case 0x1015: lg_map_key_clear(KEY_RECORD); break;
314 case 0x1016: lg_map_key_clear(KEY_PLAYER); break;
315 case 0x1017: lg_map_key_clear(KEY_EJECTCD); break;
316 case 0x1018: lg_map_key_clear(KEY_MEDIA); break;
317 case 0x1019: lg_map_key_clear(KEY_PROG1); break;
318 case 0x101a: lg_map_key_clear(KEY_PROG2); break;
319 case 0x101b: lg_map_key_clear(KEY_PROG3); break;
Lorenzo Castelli183922122010-04-16 19:00:31 +0200320 case 0x101c: lg_map_key_clear(KEY_CYCLEWINDOWS); break;
Jiri Slaby5f22a792008-05-16 11:49:19 +0200321 case 0x101f: lg_map_key_clear(KEY_ZOOMIN); break;
322 case 0x1020: lg_map_key_clear(KEY_ZOOMOUT); break;
323 case 0x1021: lg_map_key_clear(KEY_ZOOMRESET); break;
324 case 0x1023: lg_map_key_clear(KEY_CLOSE); break;
325 case 0x1027: lg_map_key_clear(KEY_MENU); break;
326 /* this one is marked as 'Rotate' */
327 case 0x1028: lg_map_key_clear(KEY_ANGLE); break;
328 case 0x1029: lg_map_key_clear(KEY_SHUFFLE); break;
329 case 0x102a: lg_map_key_clear(KEY_BACK); break;
330 case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS); break;
Lorenzo Castelli183922122010-04-16 19:00:31 +0200331 case 0x102d: lg_map_key_clear(KEY_WWW); break;
332 /* The following two are 'Start/answer call' and 'End/reject call'
333 on the MX3200 */
334 case 0x1031: lg_map_key_clear(KEY_OK); break;
335 case 0x1032: lg_map_key_clear(KEY_CANCEL); break;
Jiri Slaby5f22a792008-05-16 11:49:19 +0200336 case 0x1041: lg_map_key_clear(KEY_BATTERY); break;
337 case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR); break;
338 case 0x1043: lg_map_key_clear(KEY_SPREADSHEET); break;
339 case 0x1044: lg_map_key_clear(KEY_PRESENTATION); break;
340 case 0x1045: lg_map_key_clear(KEY_UNDO); break;
341 case 0x1046: lg_map_key_clear(KEY_REDO); break;
342 case 0x1047: lg_map_key_clear(KEY_PRINT); break;
343 case 0x1048: lg_map_key_clear(KEY_SAVE); break;
344 case 0x1049: lg_map_key_clear(KEY_PROG1); break;
345 case 0x104a: lg_map_key_clear(KEY_PROG2); break;
346 case 0x104b: lg_map_key_clear(KEY_PROG3); break;
347 case 0x104c: lg_map_key_clear(KEY_PROG4); break;
348
349 default:
350 return 0;
351 }
352 return 1;
353}
354
355static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
356 struct hid_field *field, struct hid_usage *usage,
357 unsigned long **bit, int *max)
358{
359 /* extended mapping for certain Logitech hardware (Logitech cordless
360 desktop LX500) */
361 static const u8 e_keymap[] = {
362 0,216, 0,213,175,156, 0, 0, 0, 0,
363 144, 0, 0, 0, 0, 0, 0, 0, 0,212,
364 174,167,152,161,112, 0, 0, 0,154, 0,
365 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
366 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
367 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
368 0, 0, 0, 0, 0,183,184,185,186,187,
369 188,189,190,191,192,193,194, 0, 0, 0
370 };
Axel Lin25751552012-09-13 14:08:32 +0800371 struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
Jiri Slaby5f22a792008-05-16 11:49:19 +0200372 unsigned int hid = usage->hid;
373
374 if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER &&
375 lg_ultrax_remote_mapping(hi, usage, bit, max))
376 return 1;
377
Jiri Kosina66d61be2009-11-24 18:22:20 +0100378 if (hdev->product == USB_DEVICE_ID_DINOVO_MINI &&
379 lg_dinovo_mapping(hi, usage, bit, max))
380 return 1;
381
Michal Malý8577dbf2012-03-31 11:17:25 +0200382 if ((drv_data->quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
Jiri Slaby5f22a792008-05-16 11:49:19 +0200383 return 1;
384
385 if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
386 return 0;
387
388 hid &= HID_USAGE;
389
390 /* Special handling for Logitech Cordless Desktop */
391 if (field->application == HID_GD_MOUSE) {
Michal Malý8577dbf2012-03-31 11:17:25 +0200392 if ((drv_data->quirks & LG_IGNORE_DOUBLED_WHEEL) &&
Jiri Slaby5f22a792008-05-16 11:49:19 +0200393 (hid == 7 || hid == 8))
394 return -1;
395 } else {
Michal Malý8577dbf2012-03-31 11:17:25 +0200396 if ((drv_data->quirks & LG_EXPANDED_KEYMAP) &&
Jiri Slaby5f22a792008-05-16 11:49:19 +0200397 hid < ARRAY_SIZE(e_keymap) &&
398 e_keymap[hid] != 0) {
399 hid_map_usage(hi, usage, bit, max, EV_KEY,
400 e_keymap[hid]);
401 return 1;
402 }
403 }
404
405 return 0;
406}
407
408static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
409 struct hid_field *field, struct hid_usage *usage,
410 unsigned long **bit, int *max)
411{
Axel Lin25751552012-09-13 14:08:32 +0800412 struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
Jiri Slaby5f22a792008-05-16 11:49:19 +0200413
Michal Malý8577dbf2012-03-31 11:17:25 +0200414 if ((drv_data->quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
Jiri Slaby5f22a792008-05-16 11:49:19 +0200415 (field->flags & HID_MAIN_ITEM_RELATIVE))
416 field->flags &= ~HID_MAIN_ITEM_RELATIVE;
417
Michal Malý8577dbf2012-03-31 11:17:25 +0200418 if ((drv_data->quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY ||
Jiri Slaby5f22a792008-05-16 11:49:19 +0200419 usage->type == EV_REL || usage->type == EV_ABS))
420 clear_bit(usage->code, *bit);
421
422 return 0;
423}
424
425static int lg_event(struct hid_device *hdev, struct hid_field *field,
426 struct hid_usage *usage, __s32 value)
427{
Axel Lin25751552012-09-13 14:08:32 +0800428 struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
Jiri Slaby5f22a792008-05-16 11:49:19 +0200429
Michal Malý8577dbf2012-03-31 11:17:25 +0200430 if ((drv_data->quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
Jiri Slaby5f22a792008-05-16 11:49:19 +0200431 input_event(field->hidinput->input, usage->type, usage->code,
432 -value);
433 return 1;
434 }
Michal Malý2b24a962012-09-23 22:41:08 +0200435 if (drv_data->quirks & LG_FF4) {
436 return lg4ff_adjust_input_event(hdev, field, usage, value, drv_data);
437 }
Jiri Slaby5f22a792008-05-16 11:49:19 +0200438
439 return 0;
440}
441
442static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
443{
Jiri Slaby606bd0a2008-07-04 23:06:45 +0200444 unsigned int connect_mask = HID_CONNECT_DEFAULT;
Michal Malý8577dbf2012-03-31 11:17:25 +0200445 struct lg_drv_data *drv_data;
Jiri Slaby5f22a792008-05-16 11:49:19 +0200446 int ret;
447
Michal Malý8577dbf2012-03-31 11:17:25 +0200448 drv_data = kzalloc(sizeof(struct lg_drv_data), GFP_KERNEL);
449 if (!drv_data) {
450 hid_err(hdev, "Insufficient memory, cannot allocate driver data\n");
451 return -ENOMEM;
452 }
453 drv_data->quirks = id->driver_data;
Michal Malýa80fe5d2012-09-24 01:13:17 +0200454
Michal Malý8577dbf2012-03-31 11:17:25 +0200455 hid_set_drvdata(hdev, (void *)drv_data);
Jiri Slaby5f22a792008-05-16 11:49:19 +0200456
Michal Malý8577dbf2012-03-31 11:17:25 +0200457 if (drv_data->quirks & LG_NOGET)
Jiri Slaby5f22a792008-05-16 11:49:19 +0200458 hdev->quirks |= HID_QUIRK_NOGET;
459
460 ret = hid_parse(hdev);
461 if (ret) {
Joe Perches4291ee32010-12-09 19:29:03 -0800462 hid_err(hdev, "parse failed\n");
Jiri Slaby5f22a792008-05-16 11:49:19 +0200463 goto err_free;
464 }
465
Michal Malý8577dbf2012-03-31 11:17:25 +0200466 if (drv_data->quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4))
Jiri Slaby606bd0a2008-07-04 23:06:45 +0200467 connect_mask &= ~HID_CONNECT_FF;
468
469 ret = hid_hw_start(hdev, connect_mask);
Jiri Slaby5f22a792008-05-16 11:49:19 +0200470 if (ret) {
Joe Perches4291ee32010-12-09 19:29:03 -0800471 hid_err(hdev, "hw start failed\n");
Jiri Slaby5f22a792008-05-16 11:49:19 +0200472 goto err_free;
473 }
474
Michal Malý7362cd22011-08-04 16:16:09 +0200475 /* Setup wireless link with Logitech Wii wheel */
Michal Malýa80fe5d2012-09-24 01:13:17 +0200476 if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
Simon Wood32c88cb2010-09-22 13:19:42 +0200477 unsigned char buf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
478
479 ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
480
481 if (ret >= 0) {
482 /* insert a little delay of 10 jiffies ~ 40ms */
483 wait_queue_head_t wait;
484 init_waitqueue_head (&wait);
485 wait_event_interruptible_timeout(wait, 0, 10);
486
487 /* Select random Address */
488 buf[1] = 0xB2;
489 get_random_bytes(&buf[2], 2);
490
491 ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
492 }
493 }
494
Michal Malý8577dbf2012-03-31 11:17:25 +0200495 if (drv_data->quirks & LG_FF)
Jiri Slaby606bd0a2008-07-04 23:06:45 +0200496 lgff_init(hdev);
Michal Malý8577dbf2012-03-31 11:17:25 +0200497 if (drv_data->quirks & LG_FF2)
Jiri Slaby606bd0a2008-07-04 23:06:45 +0200498 lg2ff_init(hdev);
Michal Malý8577dbf2012-03-31 11:17:25 +0200499 if (drv_data->quirks & LG_FF3)
Gary Stein74f292c2010-01-13 00:25:58 +0100500 lg3ff_init(hdev);
Michal Malý8577dbf2012-03-31 11:17:25 +0200501 if (drv_data->quirks & LG_FF4)
Simon Wood32c88cb2010-09-22 13:19:42 +0200502 lg4ff_init(hdev);
Jiri Slaby606bd0a2008-07-04 23:06:45 +0200503
Jiri Slaby5f22a792008-05-16 11:49:19 +0200504 return 0;
505err_free:
Michal Malý8577dbf2012-03-31 11:17:25 +0200506 kfree(drv_data);
Jiri Slaby5f22a792008-05-16 11:49:19 +0200507 return ret;
508}
509
Michal Malý30bb75d2011-08-04 16:20:40 +0200510static void lg_remove(struct hid_device *hdev)
511{
Axel Lin25751552012-09-13 14:08:32 +0800512 struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
Michal Malý8577dbf2012-03-31 11:17:25 +0200513 if (drv_data->quirks & LG_FF4)
Michal Malý30bb75d2011-08-04 16:20:40 +0200514 lg4ff_deinit(hdev);
515
516 hid_hw_stop(hdev);
Michal Malý8577dbf2012-03-31 11:17:25 +0200517 kfree(drv_data);
Michal Malý30bb75d2011-08-04 16:20:40 +0200518}
519
Jiri Slaby5f22a792008-05-16 11:49:19 +0200520static const struct hid_device_id lg_devices[] = {
521 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER),
522 .driver_data = LG_RDESC | LG_WIRELESS },
523 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER),
524 .driver_data = LG_RDESC | LG_WIRELESS },
525 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2),
526 .driver_data = LG_RDESC | LG_WIRELESS },
527
528 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER),
529 .driver_data = LG_BAD_RELATIVE_KEYS },
530
531 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP),
532 .driver_data = LG_DUPLICATE_USAGES },
533 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE),
534 .driver_data = LG_DUPLICATE_USAGES },
535 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI),
536 .driver_data = LG_DUPLICATE_USAGES },
537
Jiri Slaby5f22a792008-05-16 11:49:19 +0200538 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD),
539 .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
540 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500),
541 .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
542
Jiri Slaby5f22a792008-05-16 11:49:19 +0200543 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
544 .driver_data = LG_NOGET },
545 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
Michal Malý7362cd22011-08-04 16:16:09 +0200546 .driver_data = LG_NOGET | LG_FF4 },
Jiri Slaby606bd0a2008-07-04 23:06:45 +0200547
Hendrik Iben2c6118e2010-10-04 15:39:49 +0200548 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD),
549 .driver_data = LG_FF2 },
Jiri Slaby606bd0a2008-07-04 23:06:45 +0200550 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD),
551 .driver_data = LG_FF },
552 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2),
553 .driver_data = LG_FF },
554 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D),
555 .driver_data = LG_FF },
556 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
557 .driver_data = LG_FF },
558 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL),
Michal Malý7362cd22011-08-04 16:16:09 +0200559 .driver_data = LG_FF4 },
Jiri Slaby606bd0a2008-07-04 23:06:45 +0200560 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
Michal Malý7362cd22011-08-04 16:16:09 +0200561 .driver_data = LG_FF4 },
Christophe Borivant243b7062009-04-17 11:39:39 +0200562 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
Michal Malý7362cd22011-08-04 16:16:09 +0200563 .driver_data = LG_FF4 },
564 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL),
565 .driver_data = LG_FF4 },
Peter Gundermannfdc68072011-05-03 10:15:03 +0200566 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL),
Michal Malý7362cd22011-08-04 16:16:09 +0200567 .driver_data = LG_FF4 },
Jiri Kosina5623a242011-03-17 00:43:23 +0100568 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL),
Michal Malý7362cd22011-08-04 16:16:09 +0200569 .driver_data = LG_NOGET | LG_FF4 },
Simon Wood32c88cb2010-09-22 13:19:42 +0200570 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
571 .driver_data = LG_FF4 },
Michal Malýa80fe5d2012-09-24 01:13:17 +0200572 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
Jiri Kosinafd30ea82009-06-23 12:11:31 +0200573 .driver_data = LG_FF },
Jiri Slaby606bd0a2008-07-04 23:06:45 +0200574 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
575 .driver_data = LG_FF2 },
Gary Stein74f292c2010-01-13 00:25:58 +0100576 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940),
577 .driver_data = LG_FF3 },
Jiri Kosina24985cf2009-11-13 10:45:53 +0100578 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR),
579 .driver_data = LG_RDESC_REL_ABS },
580 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER),
581 .driver_data = LG_RDESC_REL_ABS },
Jiri Slaby5f22a792008-05-16 11:49:19 +0200582 { }
583};
Jiri Kosina24985cf2009-11-13 10:45:53 +0100584
Jiri Slaby5f22a792008-05-16 11:49:19 +0200585MODULE_DEVICE_TABLE(hid, lg_devices);
586
587static struct hid_driver lg_driver = {
588 .name = "logitech",
589 .id_table = lg_devices,
590 .report_fixup = lg_report_fixup,
591 .input_mapping = lg_input_mapping,
592 .input_mapped = lg_input_mapped,
593 .event = lg_event,
594 .probe = lg_probe,
Michal Malý30bb75d2011-08-04 16:20:40 +0200595 .remove = lg_remove,
Jiri Slaby5f22a792008-05-16 11:49:19 +0200596};
597
Peter Huewea24f4232009-07-02 19:08:38 +0200598static int __init lg_init(void)
Jiri Slaby5f22a792008-05-16 11:49:19 +0200599{
600 return hid_register_driver(&lg_driver);
601}
602
Peter Huewea24f4232009-07-02 19:08:38 +0200603static void __exit lg_exit(void)
Jiri Slaby5f22a792008-05-16 11:49:19 +0200604{
605 hid_unregister_driver(&lg_driver);
606}
607
608module_init(lg_init);
609module_exit(lg_exit);
610MODULE_LICENSE("GPL");