]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/hid/hid-lg.c
UBUNTU: Ubuntu-4.13.0-45.50
[mirror_ubuntu-artful-kernel.git] / drivers / hid / hid-lg.c
CommitLineData
5f22a799
JS
1/*
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
5f22a799 8 * Copyright (c) 2008 Jiri Slaby
2c6118e4 9 * Copyright (c) 2010 Hendrik Iben
5f22a799
JS
10 */
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>
32c88cbc
SW
22#include <linux/random.h>
23#include <linux/sched.h>
54bfe3f0 24#include <linux/usb.h>
32c88cbc 25#include <linux/wait.h>
5f22a799 26
54bfe3f0 27#include "usbhid/usbhid.h"
5f22a799 28#include "hid-ids.h"
606bd0a8 29#include "hid-lg.h"
a54dc779 30#include "hid-lg4ff.h"
5f22a799
JS
31
32#define LG_RDESC 0x001
33#define LG_BAD_RELATIVE_KEYS 0x002
34#define LG_DUPLICATE_USAGES 0x004
5f22a799
JS
35#define LG_EXPANDED_KEYMAP 0x010
36#define LG_IGNORE_DOUBLED_WHEEL 0x020
37#define LG_WIRELESS 0x040
38#define LG_INVERT_HWHEEL 0x080
39#define LG_NOGET 0x100
606bd0a8
JS
40#define LG_FF 0x200
41#define LG_FF2 0x400
24985cf6 42#define LG_RDESC_REL_ABS 0x800
74f292ca 43#define LG_FF3 0x1000
32c88cbc 44#define LG_FF4 0x2000
5f22a799 45
54bfe3f0
PS
46/* Size of the original descriptors of the Driving Force (and Pro) wheels */
47#define DF_RDESC_ORIG_SIZE 130
dc0a4f0c 48#define DFP_RDESC_ORIG_SIZE 97
7f505470 49#define FV_RDESC_ORIG_SIZE 130
270baef1 50#define MOMO_RDESC_ORIG_SIZE 87
348cbaa8 51#define MOMO2_RDESC_ORIG_SIZE 87
560bea30 52#define FFG_RDESC_ORIG_SIZE 85
dc0a4f0c 53
54bfe3f0
PS
54/* Fixed report descriptors for Logitech Driving Force (and Pro)
55 * wheel controllers
dc0a4f0c 56 *
54bfe3f0 57 * The original descriptors hide the separate throttle and brake axes in
dc0a4f0c
MB
58 * a custom vendor usage page, providing only a combined value as
59 * GenericDesktop.Y.
54bfe3f0 60 * These descriptors remove the combined Y axis and instead report
dc0a4f0c
MB
61 * separate throttle (Y) and brake (RZ).
62 */
54bfe3f0
PS
63static __u8 df_rdesc_fixed[] = {
640x05, 0x01, /* Usage Page (Desktop), */
bfb61abf 650x09, 0x04, /* Usage (Joystick), */
54bfe3f0
PS
660xA1, 0x01, /* Collection (Application), */
670xA1, 0x02, /* Collection (Logical), */
680x95, 0x01, /* Report Count (1), */
690x75, 0x0A, /* Report Size (10), */
700x14, /* Logical Minimum (0), */
710x26, 0xFF, 0x03, /* Logical Maximum (1023), */
720x34, /* Physical Minimum (0), */
730x46, 0xFF, 0x03, /* Physical Maximum (1023), */
740x09, 0x30, /* Usage (X), */
750x81, 0x02, /* Input (Variable), */
760x95, 0x0C, /* Report Count (12), */
770x75, 0x01, /* Report Size (1), */
780x25, 0x01, /* Logical Maximum (1), */
790x45, 0x01, /* Physical Maximum (1), */
800x05, 0x09, /* Usage (Buttons), */
810x19, 0x01, /* Usage Minimum (1), */
820x29, 0x0c, /* Usage Maximum (12), */
830x81, 0x02, /* Input (Variable), */
840x95, 0x02, /* Report Count (2), */
850x06, 0x00, 0xFF, /* Usage Page (Vendor: 65280), */
860x09, 0x01, /* Usage (?: 1), */
870x81, 0x02, /* Input (Variable), */
880x05, 0x01, /* Usage Page (Desktop), */
54bfe3f0
PS
890x26, 0xFF, 0x00, /* Logical Maximum (255), */
900x46, 0xFF, 0x00, /* Physical Maximum (255), */
910x95, 0x01, /* Report Count (1), */
920x75, 0x08, /* Report Size (8), */
930x81, 0x02, /* Input (Variable), */
940x25, 0x07, /* Logical Maximum (7), */
950x46, 0x3B, 0x01, /* Physical Maximum (315), */
960x75, 0x04, /* Report Size (4), */
970x65, 0x14, /* Unit (Degrees), */
980x09, 0x39, /* Usage (Hat Switch), */
990x81, 0x42, /* Input (Variable, Null State), */
1000x75, 0x01, /* Report Size (1), */
1010x95, 0x04, /* Report Count (4), */
1020x65, 0x00, /* Unit (none), */
1030x06, 0x00, 0xFF, /* Usage Page (Vendor: 65280), */
1040x09, 0x01, /* Usage (?: 1), */
1050x25, 0x01, /* Logical Maximum (1), */
1060x45, 0x01, /* Physical Maximum (1), */
1070x81, 0x02, /* Input (Variable), */
5a9b571b
PS
1080x05, 0x01, /* Usage Page (Desktop), */
1090x95, 0x01, /* Report Count (1), */
54bfe3f0
PS
1100x75, 0x08, /* Report Size (8), */
1110x26, 0xFF, 0x00, /* Logical Maximum (255), */
1120x46, 0xFF, 0x00, /* Physical Maximum (255), */
5a9b571b
PS
1130x09, 0x31, /* Usage (Y), */
1140x81, 0x02, /* Input (Variable), */
1150x09, 0x35, /* Usage (Rz), */
54bfe3f0
PS
1160x81, 0x02, /* Input (Variable), */
1170xC0, /* End Collection, */
1180xA1, 0x02, /* Collection (Logical), */
1190x26, 0xFF, 0x00, /* Logical Maximum (255), */
1200x46, 0xFF, 0x00, /* Physical Maximum (255), */
1210x95, 0x07, /* Report Count (7), */
1220x75, 0x08, /* Report Size (8), */
1230x09, 0x03, /* Usage (?: 3), */
1240x91, 0x02, /* Output (Variable), */
1250xC0, /* End Collection, */
1260xC0 /* End Collection */
127};
128
dc0a4f0c
MB
129static __u8 dfp_rdesc_fixed[] = {
1300x05, 0x01, /* Usage Page (Desktop), */
bfb61abf 1310x09, 0x04, /* Usage (Joystick), */
dc0a4f0c
MB
1320xA1, 0x01, /* Collection (Application), */
1330xA1, 0x02, /* Collection (Logical), */
1340x95, 0x01, /* Report Count (1), */
1350x75, 0x0E, /* Report Size (14), */
1360x14, /* Logical Minimum (0), */
1370x26, 0xFF, 0x3F, /* Logical Maximum (16383), */
1380x34, /* Physical Minimum (0), */
1390x46, 0xFF, 0x3F, /* Physical Maximum (16383), */
1400x09, 0x30, /* Usage (X), */
1410x81, 0x02, /* Input (Variable), */
1420x95, 0x0E, /* Report Count (14), */
1430x75, 0x01, /* Report Size (1), */
1440x25, 0x01, /* Logical Maximum (1), */
1450x45, 0x01, /* Physical Maximum (1), */
1460x05, 0x09, /* Usage Page (Button), */
1470x19, 0x01, /* Usage Minimum (01h), */
1480x29, 0x0E, /* Usage Maximum (0Eh), */
1490x81, 0x02, /* Input (Variable), */
1500x05, 0x01, /* Usage Page (Desktop), */
1510x95, 0x01, /* Report Count (1), */
1520x75, 0x04, /* Report Size (4), */
1530x25, 0x07, /* Logical Maximum (7), */
1540x46, 0x3B, 0x01, /* Physical Maximum (315), */
1550x65, 0x14, /* Unit (Degrees), */
1560x09, 0x39, /* Usage (Hat Switch), */
1570x81, 0x42, /* Input (Variable, Nullstate), */
1580x65, 0x00, /* Unit, */
1590x26, 0xFF, 0x00, /* Logical Maximum (255), */
1600x46, 0xFF, 0x00, /* Physical Maximum (255), */
1610x75, 0x08, /* Report Size (8), */
1620x81, 0x01, /* Input (Constant), */
1630x09, 0x31, /* Usage (Y), */
1640x81, 0x02, /* Input (Variable), */
1650x09, 0x35, /* Usage (Rz), */
1660x81, 0x02, /* Input (Variable), */
1670x81, 0x01, /* Input (Constant), */
1680xC0, /* End Collection, */
1690xA1, 0x02, /* Collection (Logical), */
1700x09, 0x02, /* Usage (02h), */
1710x95, 0x07, /* Report Count (7), */
1720x91, 0x02, /* Output (Variable), */
1730xC0, /* End Collection, */
1740xC0 /* End Collection */
175};
176
7f505470
SW
177static __u8 fv_rdesc_fixed[] = {
1780x05, 0x01, /* Usage Page (Desktop), */
bfb61abf 1790x09, 0x04, /* Usage (Joystick), */
7f505470
SW
1800xA1, 0x01, /* Collection (Application), */
1810xA1, 0x02, /* Collection (Logical), */
1820x95, 0x01, /* Report Count (1), */
1830x75, 0x0A, /* Report Size (10), */
1840x15, 0x00, /* Logical Minimum (0), */
1850x26, 0xFF, 0x03, /* Logical Maximum (1023), */
1860x35, 0x00, /* Physical Minimum (0), */
1870x46, 0xFF, 0x03, /* Physical Maximum (1023), */
1880x09, 0x30, /* Usage (X), */
1890x81, 0x02, /* Input (Variable), */
1900x95, 0x0C, /* Report Count (12), */
1910x75, 0x01, /* Report Size (1), */
1920x25, 0x01, /* Logical Maximum (1), */
1930x45, 0x01, /* Physical Maximum (1), */
1940x05, 0x09, /* Usage Page (Button), */
1950x19, 0x01, /* Usage Minimum (01h), */
1960x29, 0x0C, /* Usage Maximum (0Ch), */
1970x81, 0x02, /* Input (Variable), */
1980x95, 0x02, /* Report Count (2), */
1990x06, 0x00, 0xFF, /* Usage Page (FF00h), */
2000x09, 0x01, /* Usage (01h), */
2010x81, 0x02, /* Input (Variable), */
2020x09, 0x02, /* Usage (02h), */
2030x26, 0xFF, 0x00, /* Logical Maximum (255), */
2040x46, 0xFF, 0x00, /* Physical Maximum (255), */
2050x95, 0x01, /* Report Count (1), */
2060x75, 0x08, /* Report Size (8), */
2070x81, 0x02, /* Input (Variable), */
2080x05, 0x01, /* Usage Page (Desktop), */
2090x25, 0x07, /* Logical Maximum (7), */
2100x46, 0x3B, 0x01, /* Physical Maximum (315), */
2110x75, 0x04, /* Report Size (4), */
2120x65, 0x14, /* Unit (Degrees), */
2130x09, 0x39, /* Usage (Hat Switch), */
2140x81, 0x42, /* Input (Variable, Null State), */
2150x75, 0x01, /* Report Size (1), */
2160x95, 0x04, /* Report Count (4), */
2170x65, 0x00, /* Unit, */
2180x06, 0x00, 0xFF, /* Usage Page (FF00h), */
2190x09, 0x01, /* Usage (01h), */
2200x25, 0x01, /* Logical Maximum (1), */
2210x45, 0x01, /* Physical Maximum (1), */
2220x81, 0x02, /* Input (Variable), */
2230x05, 0x01, /* Usage Page (Desktop), */
2240x95, 0x01, /* Report Count (1), */
2250x75, 0x08, /* Report Size (8), */
2260x26, 0xFF, 0x00, /* Logical Maximum (255), */
2270x46, 0xFF, 0x00, /* Physical Maximum (255), */
2280x09, 0x31, /* Usage (Y), */
2290x81, 0x02, /* Input (Variable), */
2300x09, 0x32, /* Usage (Z), */
2310x81, 0x02, /* Input (Variable), */
2320xC0, /* End Collection, */
2330xA1, 0x02, /* Collection (Logical), */
2340x26, 0xFF, 0x00, /* Logical Maximum (255), */
2350x46, 0xFF, 0x00, /* Physical Maximum (255), */
2360x95, 0x07, /* Report Count (7), */
2370x75, 0x08, /* Report Size (8), */
2380x09, 0x03, /* Usage (03h), */
2390x91, 0x02, /* Output (Variable), */
2400xC0, /* End Collection, */
2410xC0 /* End Collection */
242};
243
270baef1
SW
244static __u8 momo_rdesc_fixed[] = {
2450x05, 0x01, /* Usage Page (Desktop), */
bfb61abf 2460x09, 0x04, /* Usage (Joystick), */
270baef1
SW
2470xA1, 0x01, /* Collection (Application), */
2480xA1, 0x02, /* Collection (Logical), */
2490x95, 0x01, /* Report Count (1), */
2500x75, 0x0A, /* Report Size (10), */
2510x15, 0x00, /* Logical Minimum (0), */
2520x26, 0xFF, 0x03, /* Logical Maximum (1023), */
2530x35, 0x00, /* Physical Minimum (0), */
2540x46, 0xFF, 0x03, /* Physical Maximum (1023), */
2550x09, 0x30, /* Usage (X), */
2560x81, 0x02, /* Input (Variable), */
2570x95, 0x08, /* Report Count (8), */
2580x75, 0x01, /* Report Size (1), */
2590x25, 0x01, /* Logical Maximum (1), */
2600x45, 0x01, /* Physical Maximum (1), */
2610x05, 0x09, /* Usage Page (Button), */
2620x19, 0x01, /* Usage Minimum (01h), */
2630x29, 0x08, /* Usage Maximum (08h), */
2640x81, 0x02, /* Input (Variable), */
2650x06, 0x00, 0xFF, /* Usage Page (FF00h), */
2660x75, 0x0E, /* Report Size (14), */
2670x95, 0x01, /* Report Count (1), */
2680x26, 0xFF, 0x00, /* Logical Maximum (255), */
2690x46, 0xFF, 0x00, /* Physical Maximum (255), */
2700x09, 0x00, /* Usage (00h), */
2710x81, 0x02, /* Input (Variable), */
2720x05, 0x01, /* Usage Page (Desktop), */
2730x75, 0x08, /* Report Size (8), */
2740x09, 0x31, /* Usage (Y), */
2750x81, 0x02, /* Input (Variable), */
2760x09, 0x32, /* Usage (Z), */
2770x81, 0x02, /* Input (Variable), */
2780x06, 0x00, 0xFF, /* Usage Page (FF00h), */
2790x09, 0x01, /* Usage (01h), */
2800x81, 0x02, /* Input (Variable), */
2810xC0, /* End Collection, */
2820xA1, 0x02, /* Collection (Logical), */
2830x09, 0x02, /* Usage (02h), */
2840x95, 0x07, /* Report Count (7), */
2850x91, 0x02, /* Output (Variable), */
2860xC0, /* End Collection, */
2870xC0 /* End Collection */
288};
dc0a4f0c 289
348cbaa8
SW
290static __u8 momo2_rdesc_fixed[] = {
2910x05, 0x01, /* Usage Page (Desktop), */
bfb61abf 2920x09, 0x04, /* Usage (Joystick), */
348cbaa8
SW
2930xA1, 0x01, /* Collection (Application), */
2940xA1, 0x02, /* Collection (Logical), */
2950x95, 0x01, /* Report Count (1), */
2960x75, 0x0A, /* Report Size (10), */
2970x15, 0x00, /* Logical Minimum (0), */
2980x26, 0xFF, 0x03, /* Logical Maximum (1023), */
2990x35, 0x00, /* Physical Minimum (0), */
3000x46, 0xFF, 0x03, /* Physical Maximum (1023), */
3010x09, 0x30, /* Usage (X), */
3020x81, 0x02, /* Input (Variable), */
3030x95, 0x0A, /* Report Count (10), */
3040x75, 0x01, /* Report Size (1), */
3050x25, 0x01, /* Logical Maximum (1), */
3060x45, 0x01, /* Physical Maximum (1), */
3070x05, 0x09, /* Usage Page (Button), */
3080x19, 0x01, /* Usage Minimum (01h), */
3090x29, 0x0A, /* Usage Maximum (0Ah), */
3100x81, 0x02, /* Input (Variable), */
3110x06, 0x00, 0xFF, /* Usage Page (FF00h), */
3120x09, 0x00, /* Usage (00h), */
3130x95, 0x04, /* Report Count (4), */
3140x81, 0x02, /* Input (Variable), */
3150x95, 0x01, /* Report Count (1), */
3160x75, 0x08, /* Report Size (8), */
3170x26, 0xFF, 0x00, /* Logical Maximum (255), */
3180x46, 0xFF, 0x00, /* Physical Maximum (255), */
3190x09, 0x01, /* Usage (01h), */
3200x81, 0x02, /* Input (Variable), */
3210x05, 0x01, /* Usage Page (Desktop), */
3220x09, 0x31, /* Usage (Y), */
3230x81, 0x02, /* Input (Variable), */
3240x09, 0x32, /* Usage (Z), */
3250x81, 0x02, /* Input (Variable), */
3260x06, 0x00, 0xFF, /* Usage Page (FF00h), */
3270x09, 0x00, /* Usage (00h), */
3280x81, 0x02, /* Input (Variable), */
3290xC0, /* End Collection, */
3300xA1, 0x02, /* Collection (Logical), */
3310x09, 0x02, /* Usage (02h), */
3320x95, 0x07, /* Report Count (7), */
3330x91, 0x02, /* Output (Variable), */
3340xC0, /* End Collection, */
3350xC0 /* End Collection */
336};
337
560bea30
SW
338static __u8 ffg_rdesc_fixed[] = {
3390x05, 0x01, /* Usage Page (Desktop), */
3400x09, 0x04, /* Usage (Joystik), */
3410xA1, 0x01, /* Collection (Application), */
3420xA1, 0x02, /* Collection (Logical), */
3430x95, 0x01, /* Report Count (1), */
3440x75, 0x0A, /* Report Size (10), */
3450x15, 0x00, /* Logical Minimum (0), */
3460x26, 0xFF, 0x03, /* Logical Maximum (1023), */
3470x35, 0x00, /* Physical Minimum (0), */
3480x46, 0xFF, 0x03, /* Physical Maximum (1023), */
3490x09, 0x30, /* Usage (X), */
3500x81, 0x02, /* Input (Variable), */
3510x95, 0x06, /* Report Count (6), */
3520x75, 0x01, /* Report Size (1), */
3530x25, 0x01, /* Logical Maximum (1), */
3540x45, 0x01, /* Physical Maximum (1), */
3550x05, 0x09, /* Usage Page (Button), */
3560x19, 0x01, /* Usage Minimum (01h), */
3570x29, 0x06, /* Usage Maximum (06h), */
3580x81, 0x02, /* Input (Variable), */
3590x95, 0x01, /* Report Count (1), */
3600x75, 0x08, /* Report Size (8), */
3610x26, 0xFF, 0x00, /* Logical Maximum (255), */
3620x46, 0xFF, 0x00, /* Physical Maximum (255), */
3630x06, 0x00, 0xFF, /* Usage Page (FF00h), */
3640x09, 0x01, /* Usage (01h), */
3650x81, 0x02, /* Input (Variable), */
3660x05, 0x01, /* Usage Page (Desktop), */
3670x81, 0x01, /* Input (Constant), */
3680x09, 0x31, /* Usage (Y), */
3690x81, 0x02, /* Input (Variable), */
3700x09, 0x32, /* Usage (Z), */
3710x81, 0x02, /* Input (Variable), */
3720x06, 0x00, 0xFF, /* Usage Page (FF00h), */
3730x09, 0x01, /* Usage (01h), */
3740x81, 0x02, /* Input (Variable), */
3750xC0, /* End Collection, */
3760xA1, 0x02, /* Collection (Logical), */
3770x09, 0x02, /* Usage (02h), */
3780x95, 0x07, /* Report Count (7), */
3790x91, 0x02, /* Output (Variable), */
3800xC0, /* End Collection, */
3810xC0 /* End Collection */
382};
383
5f22a799
JS
384/*
385 * Certain Logitech keyboards send in report #3 keys which are far
386 * above the logical maximum described in descriptor. This extends
387 * the original value of 0x28c of logical maximum to 0x104d
388 */
73e4008d
NK
389static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
390 unsigned int *rsize)
5f22a799 391{
25751553 392 struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
5f22a799 393
4ab25786 394 if ((drv_data->quirks & LG_RDESC) && *rsize >= 91 && rdesc[83] == 0x26 &&
5f22a799 395 rdesc[84] == 0x8c && rdesc[85] == 0x02) {
4291ee30
JP
396 hid_info(hdev,
397 "fixing up Logitech keyboard report descriptor\n");
5f22a799
JS
398 rdesc[84] = rdesc[89] = 0x4d;
399 rdesc[85] = rdesc[90] = 0x10;
400 }
4ab25786 401 if ((drv_data->quirks & LG_RDESC_REL_ABS) && *rsize >= 51 &&
24985cf6
JK
402 rdesc[32] == 0x81 && rdesc[33] == 0x06 &&
403 rdesc[49] == 0x81 && rdesc[50] == 0x06) {
4291ee30
JP
404 hid_info(hdev,
405 "fixing up rel/abs in Logitech report descriptor\n");
24985cf6
JK
406 rdesc[33] = rdesc[50] = 0x02;
407 }
dc0a4f0c
MB
408
409 switch (hdev->product) {
54bfe3f0 410
560bea30
SW
411 case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
412 if (*rsize == FFG_RDESC_ORIG_SIZE) {
413 hid_info(hdev,
414 "fixing up Logitech Wingman Formula Force GP report descriptor\n");
415 rdesc = ffg_rdesc_fixed;
416 *rsize = sizeof(ffg_rdesc_fixed);
417 }
418 break;
419
54bfe3f0
PS
420 /* Several wheels report as this id when operating in emulation mode. */
421 case USB_DEVICE_ID_LOGITECH_WHEEL:
7363b25a 422 if (*rsize == DF_RDESC_ORIG_SIZE) {
54bfe3f0
PS
423 hid_info(hdev,
424 "fixing up Logitech Driving Force report descriptor\n");
425 rdesc = df_rdesc_fixed;
426 *rsize = sizeof(df_rdesc_fixed);
427 }
428 break;
429
270baef1
SW
430 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
431 if (*rsize == MOMO_RDESC_ORIG_SIZE) {
432 hid_info(hdev,
433 "fixing up Logitech Momo Force (Red) report descriptor\n");
434 rdesc = momo_rdesc_fixed;
435 *rsize = sizeof(momo_rdesc_fixed);
436 }
437 break;
438
348cbaa8
SW
439 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
440 if (*rsize == MOMO2_RDESC_ORIG_SIZE) {
441 hid_info(hdev,
442 "fixing up Logitech Momo Racing Force (Black) report descriptor\n");
443 rdesc = momo2_rdesc_fixed;
444 *rsize = sizeof(momo2_rdesc_fixed);
445 }
446 break;
447
7f505470
SW
448 case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL:
449 if (*rsize == FV_RDESC_ORIG_SIZE) {
450 hid_info(hdev,
451 "fixing up Logitech Formula Vibration report descriptor\n");
452 rdesc = fv_rdesc_fixed;
453 *rsize = sizeof(fv_rdesc_fixed);
454 }
455 break;
456
dc0a4f0c
MB
457 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
458 if (*rsize == DFP_RDESC_ORIG_SIZE) {
459 hid_info(hdev,
460 "fixing up Logitech Driving Force Pro report descriptor\n");
461 rdesc = dfp_rdesc_fixed;
462 *rsize = sizeof(dfp_rdesc_fixed);
463 }
464 break;
b5836246
SW
465
466 case USB_DEVICE_ID_LOGITECH_WII_WHEEL:
467 if (*rsize >= 101 && rdesc[41] == 0x95 && rdesc[42] == 0x0B &&
468 rdesc[47] == 0x05 && rdesc[48] == 0x09) {
469 hid_info(hdev, "fixing up Logitech Speed Force Wireless report descriptor\n");
470 rdesc[41] = 0x05;
471 rdesc[42] = 0x09;
472 rdesc[47] = 0x95;
473 rdesc[48] = 0x0B;
474 }
475 break;
dc0a4f0c
MB
476 }
477
73e4008d 478 return rdesc;
5f22a799
JS
479}
480
481#define lg_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
482 EV_KEY, (c))
483
484static int lg_ultrax_remote_mapping(struct hid_input *hi,
485 struct hid_usage *usage, unsigned long **bit, int *max)
486{
487 if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
488 return 0;
489
490 set_bit(EV_REP, hi->input->evbit);
491 switch (usage->hid & HID_USAGE) {
492 /* Reported on Logitech Ultra X Media Remote */
493 case 0x004: lg_map_key_clear(KEY_AGAIN); break;
494 case 0x00d: lg_map_key_clear(KEY_HOME); break;
495 case 0x024: lg_map_key_clear(KEY_SHUFFLE); break;
496 case 0x025: lg_map_key_clear(KEY_TV); break;
497 case 0x026: lg_map_key_clear(KEY_MENU); break;
498 case 0x031: lg_map_key_clear(KEY_AUDIO); break;
499 case 0x032: lg_map_key_clear(KEY_TEXT); break;
500 case 0x033: lg_map_key_clear(KEY_LAST); break;
501 case 0x047: lg_map_key_clear(KEY_MP3); break;
502 case 0x048: lg_map_key_clear(KEY_DVD); break;
503 case 0x049: lg_map_key_clear(KEY_MEDIA); break;
504 case 0x04a: lg_map_key_clear(KEY_VIDEO); break;
505 case 0x04b: lg_map_key_clear(KEY_ANGLE); break;
506 case 0x04c: lg_map_key_clear(KEY_LANGUAGE); break;
507 case 0x04d: lg_map_key_clear(KEY_SUBTITLE); break;
508 case 0x051: lg_map_key_clear(KEY_RED); break;
509 case 0x052: lg_map_key_clear(KEY_CLOSE); break;
510
511 default:
512 return 0;
513 }
514 return 1;
515}
516
66d61bec
JK
517static int lg_dinovo_mapping(struct hid_input *hi, struct hid_usage *usage,
518 unsigned long **bit, int *max)
519{
520 if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
521 return 0;
522
523 switch (usage->hid & HID_USAGE) {
524
525 case 0x00d: lg_map_key_clear(KEY_MEDIA); break;
526 default:
527 return 0;
528
529 }
530 return 1;
531}
532
5f22a799
JS
533static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
534 unsigned long **bit, int *max)
535{
536 if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
537 return 0;
538
539 switch (usage->hid & HID_USAGE) {
540 case 0x1001: lg_map_key_clear(KEY_MESSENGER); break;
541 case 0x1003: lg_map_key_clear(KEY_SOUND); break;
542 case 0x1004: lg_map_key_clear(KEY_VIDEO); break;
543 case 0x1005: lg_map_key_clear(KEY_AUDIO); break;
544 case 0x100a: lg_map_key_clear(KEY_DOCUMENTS); break;
18392212
LC
545 /* The following two entries are Playlist 1 and 2 on the MX3200 */
546 case 0x100f: lg_map_key_clear(KEY_FN_1); break;
547 case 0x1010: lg_map_key_clear(KEY_FN_2); break;
5f22a799
JS
548 case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG); break;
549 case 0x1012: lg_map_key_clear(KEY_NEXTSONG); break;
550 case 0x1013: lg_map_key_clear(KEY_CAMERA); break;
551 case 0x1014: lg_map_key_clear(KEY_MESSENGER); break;
552 case 0x1015: lg_map_key_clear(KEY_RECORD); break;
553 case 0x1016: lg_map_key_clear(KEY_PLAYER); break;
554 case 0x1017: lg_map_key_clear(KEY_EJECTCD); break;
555 case 0x1018: lg_map_key_clear(KEY_MEDIA); break;
556 case 0x1019: lg_map_key_clear(KEY_PROG1); break;
557 case 0x101a: lg_map_key_clear(KEY_PROG2); break;
558 case 0x101b: lg_map_key_clear(KEY_PROG3); break;
18392212 559 case 0x101c: lg_map_key_clear(KEY_CYCLEWINDOWS); break;
5f22a799
JS
560 case 0x101f: lg_map_key_clear(KEY_ZOOMIN); break;
561 case 0x1020: lg_map_key_clear(KEY_ZOOMOUT); break;
562 case 0x1021: lg_map_key_clear(KEY_ZOOMRESET); break;
563 case 0x1023: lg_map_key_clear(KEY_CLOSE); break;
564 case 0x1027: lg_map_key_clear(KEY_MENU); break;
565 /* this one is marked as 'Rotate' */
566 case 0x1028: lg_map_key_clear(KEY_ANGLE); break;
567 case 0x1029: lg_map_key_clear(KEY_SHUFFLE); break;
568 case 0x102a: lg_map_key_clear(KEY_BACK); break;
569 case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS); break;
18392212
LC
570 case 0x102d: lg_map_key_clear(KEY_WWW); break;
571 /* The following two are 'Start/answer call' and 'End/reject call'
572 on the MX3200 */
573 case 0x1031: lg_map_key_clear(KEY_OK); break;
574 case 0x1032: lg_map_key_clear(KEY_CANCEL); break;
5f22a799
JS
575 case 0x1041: lg_map_key_clear(KEY_BATTERY); break;
576 case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR); break;
577 case 0x1043: lg_map_key_clear(KEY_SPREADSHEET); break;
578 case 0x1044: lg_map_key_clear(KEY_PRESENTATION); break;
579 case 0x1045: lg_map_key_clear(KEY_UNDO); break;
580 case 0x1046: lg_map_key_clear(KEY_REDO); break;
581 case 0x1047: lg_map_key_clear(KEY_PRINT); break;
582 case 0x1048: lg_map_key_clear(KEY_SAVE); break;
583 case 0x1049: lg_map_key_clear(KEY_PROG1); break;
584 case 0x104a: lg_map_key_clear(KEY_PROG2); break;
585 case 0x104b: lg_map_key_clear(KEY_PROG3); break;
586 case 0x104c: lg_map_key_clear(KEY_PROG4); break;
587
588 default:
589 return 0;
590 }
591 return 1;
592}
593
594static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
595 struct hid_field *field, struct hid_usage *usage,
596 unsigned long **bit, int *max)
597{
598 /* extended mapping for certain Logitech hardware (Logitech cordless
599 desktop LX500) */
600 static const u8 e_keymap[] = {
601 0,216, 0,213,175,156, 0, 0, 0, 0,
602 144, 0, 0, 0, 0, 0, 0, 0, 0,212,
603 174,167,152,161,112, 0, 0, 0,154, 0,
604 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
605 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
606 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
607 0, 0, 0, 0, 0,183,184,185,186,187,
608 188,189,190,191,192,193,194, 0, 0, 0
609 };
25751553 610 struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
5f22a799
JS
611 unsigned int hid = usage->hid;
612
613 if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER &&
614 lg_ultrax_remote_mapping(hi, usage, bit, max))
615 return 1;
616
66d61bec
JK
617 if (hdev->product == USB_DEVICE_ID_DINOVO_MINI &&
618 lg_dinovo_mapping(hi, usage, bit, max))
619 return 1;
620
8577dbf9 621 if ((drv_data->quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
5f22a799
JS
622 return 1;
623
624 if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
625 return 0;
626
627 hid &= HID_USAGE;
628
629 /* Special handling for Logitech Cordless Desktop */
630 if (field->application == HID_GD_MOUSE) {
8577dbf9 631 if ((drv_data->quirks & LG_IGNORE_DOUBLED_WHEEL) &&
5f22a799
JS
632 (hid == 7 || hid == 8))
633 return -1;
634 } else {
8577dbf9 635 if ((drv_data->quirks & LG_EXPANDED_KEYMAP) &&
5f22a799
JS
636 hid < ARRAY_SIZE(e_keymap) &&
637 e_keymap[hid] != 0) {
638 hid_map_usage(hi, usage, bit, max, EV_KEY,
639 e_keymap[hid]);
640 return 1;
641 }
642 }
643
644 return 0;
645}
646
647static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
648 struct hid_field *field, struct hid_usage *usage,
649 unsigned long **bit, int *max)
650{
25751553 651 struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
5f22a799 652
8577dbf9 653 if ((drv_data->quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
5f22a799
JS
654 (field->flags & HID_MAIN_ITEM_RELATIVE))
655 field->flags &= ~HID_MAIN_ITEM_RELATIVE;
656
8577dbf9 657 if ((drv_data->quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY ||
5f22a799
JS
658 usage->type == EV_REL || usage->type == EV_ABS))
659 clear_bit(usage->code, *bit);
660
94b3f712
SW
661 /* Ensure that Logitech wheels are not given a default fuzz/flat value */
662 if (usage->type == EV_ABS && (usage->code == ABS_X ||
663 usage->code == ABS_Y || usage->code == ABS_Z ||
664 usage->code == ABS_RZ)) {
665 switch (hdev->product) {
29fae1c8 666 case USB_DEVICE_ID_LOGITECH_G29_WHEEL:
560bea30 667 case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
94b3f712
SW
668 case USB_DEVICE_ID_LOGITECH_WHEEL:
669 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
670 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
671 case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
672 case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
673 case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
674 case USB_DEVICE_ID_LOGITECH_WII_WHEEL:
675 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
bd04363d 676 case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL:
94b3f712
SW
677 field->application = HID_GD_MULTIAXIS;
678 break;
679 default:
680 break;
681 }
682 }
683
5f22a799
JS
684 return 0;
685}
686
687static int lg_event(struct hid_device *hdev, struct hid_field *field,
688 struct hid_usage *usage, __s32 value)
689{
25751553 690 struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
5f22a799 691
8577dbf9 692 if ((drv_data->quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
5f22a799
JS
693 input_event(field->hidinput->input, usage->type, usage->code,
694 -value);
695 return 1;
696 }
2b24a960
MM
697 if (drv_data->quirks & LG_FF4) {
698 return lg4ff_adjust_input_event(hdev, field, usage, value, drv_data);
699 }
5f22a799
JS
700
701 return 0;
702}
703
c832f86e
SW
704static int lg_raw_event(struct hid_device *hdev, struct hid_report *report,
705 u8 *rd, int size)
706{
707 struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
708
709 if (drv_data->quirks & LG_FF4)
710 return lg4ff_raw_event(hdev, report, rd, size, drv_data);
711
712 return 0;
713}
714
5f22a799
JS
715static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
716{
29fae1c8
SW
717 struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
718 __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
606bd0a8 719 unsigned int connect_mask = HID_CONNECT_DEFAULT;
8577dbf9 720 struct lg_drv_data *drv_data;
5f22a799
JS
721 int ret;
722
2078665a
BT
723 /* G29 only work with the 1st interface */
724 if ((hdev->product == USB_DEVICE_ID_LOGITECH_G29_WHEEL) &&
725 (iface_num != 0)) {
29fae1c8
SW
726 dbg_hid("%s: ignoring ifnum %d\n", __func__, iface_num);
727 return -ENODEV;
728 }
729
8577dbf9
MM
730 drv_data = kzalloc(sizeof(struct lg_drv_data), GFP_KERNEL);
731 if (!drv_data) {
732 hid_err(hdev, "Insufficient memory, cannot allocate driver data\n");
733 return -ENOMEM;
734 }
735 drv_data->quirks = id->driver_data;
a80fe5d6 736
8577dbf9 737 hid_set_drvdata(hdev, (void *)drv_data);
5f22a799 738
8577dbf9 739 if (drv_data->quirks & LG_NOGET)
5f22a799
JS
740 hdev->quirks |= HID_QUIRK_NOGET;
741
742 ret = hid_parse(hdev);
743 if (ret) {
4291ee30 744 hid_err(hdev, "parse failed\n");
5f22a799
JS
745 goto err_free;
746 }
747
8577dbf9 748 if (drv_data->quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4))
606bd0a8
JS
749 connect_mask &= ~HID_CONNECT_FF;
750
751 ret = hid_hw_start(hdev, connect_mask);
5f22a799 752 if (ret) {
4291ee30 753 hid_err(hdev, "hw start failed\n");
5f22a799
JS
754 goto err_free;
755 }
756
7362cd22 757 /* Setup wireless link with Logitech Wii wheel */
a80fe5d6 758 if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
061232f0
BT
759 const unsigned char cbuf[] = { 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
760 u8 *buf = kmemdup(cbuf, sizeof(cbuf), GFP_KERNEL);
32c88cbc 761
061232f0
BT
762 if (!buf) {
763 ret = -ENOMEM;
764 goto err_free;
765 }
32c88cbc 766
061232f0
BT
767 ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(cbuf),
768 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
32c88cbc
SW
769 if (ret >= 0) {
770 /* insert a little delay of 10 jiffies ~ 40ms */
771 wait_queue_head_t wait;
772 init_waitqueue_head (&wait);
e0393062
NMG
773 wait_event_interruptible_timeout(wait, 0,
774 msecs_to_jiffies(40));
32c88cbc
SW
775
776 /* Select random Address */
777 buf[1] = 0xB2;
778 get_random_bytes(&buf[2], 2);
779
061232f0 780 ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(cbuf),
b0dd72aa 781 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
32c88cbc 782 }
061232f0 783 kfree(buf);
32c88cbc
SW
784 }
785
8577dbf9 786 if (drv_data->quirks & LG_FF)
2dbf635e 787 ret = lgff_init(hdev);
f4e1eb96 788 else if (drv_data->quirks & LG_FF2)
2dbf635e 789 ret = lg2ff_init(hdev);
f4e1eb96 790 else if (drv_data->quirks & LG_FF3)
2dbf635e 791 ret = lg3ff_init(hdev);
f4e1eb96 792 else if (drv_data->quirks & LG_FF4)
2dbf635e
MM
793 ret = lg4ff_init(hdev);
794
795 if (ret)
796 goto err_free;
606bd0a8 797
5f22a799
JS
798 return 0;
799err_free:
8577dbf9 800 kfree(drv_data);
5f22a799
JS
801 return ret;
802}
803
30bb75d7
MM
804static void lg_remove(struct hid_device *hdev)
805{
25751553 806 struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
8577dbf9 807 if (drv_data->quirks & LG_FF4)
30bb75d7 808 lg4ff_deinit(hdev);
b211a638
MM
809 else
810 hid_hw_stop(hdev);
8577dbf9 811 kfree(drv_data);
30bb75d7
MM
812}
813
5f22a799
JS
814static const struct hid_device_id lg_devices[] = {
815 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER),
816 .driver_data = LG_RDESC | LG_WIRELESS },
817 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER),
818 .driver_data = LG_RDESC | LG_WIRELESS },
819 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2),
820 .driver_data = LG_RDESC | LG_WIRELESS },
821
822 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER),
823 .driver_data = LG_BAD_RELATIVE_KEYS },
824
825 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP),
826 .driver_data = LG_DUPLICATE_USAGES },
827 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE),
828 .driver_data = LG_DUPLICATE_USAGES },
829 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI),
830 .driver_data = LG_DUPLICATE_USAGES },
831
5f22a799
JS
832 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD),
833 .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
834 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500),
835 .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
836
5f22a799
JS
837 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
838 .driver_data = LG_NOGET },
56d0c8b7
VK
839 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DUAL_ACTION),
840 .driver_data = LG_NOGET },
5f22a799 841 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
7362cd22 842 .driver_data = LG_NOGET | LG_FF4 },
606bd0a8 843
2c6118e4
HI
844 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD),
845 .driver_data = LG_FF2 },
606bd0a8
JS
846 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD),
847 .driver_data = LG_FF },
848 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2),
849 .driver_data = LG_FF },
c873d9ab
SW
850 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G29_WHEEL),
851 .driver_data = LG_FF4 },
606bd0a8
JS
852 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D),
853 .driver_data = LG_FF },
854 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
855 .driver_data = LG_FF },
856 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL),
270baef1 857 .driver_data = LG_NOGET | LG_FF4 },
606bd0a8 858 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
7362cd22 859 .driver_data = LG_FF4 },
bd04363d
EV
860 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL),
861 .driver_data = LG_FF2 },
243b706d 862 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
7362cd22
MM
863 .driver_data = LG_FF4 },
864 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL),
865 .driver_data = LG_FF4 },
fdc6807f 866 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL),
7362cd22 867 .driver_data = LG_FF4 },
5623a24a 868 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL),
7362cd22 869 .driver_data = LG_NOGET | LG_FF4 },
32c88cbc
SW
870 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
871 .driver_data = LG_FF4 },
a80fe5d6 872 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
560bea30 873 .driver_data = LG_NOGET | LG_FF4 },
606bd0a8 874 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
877a021e 875 .driver_data = LG_NOGET | LG_FF2 },
74f292ca
GS
876 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940),
877 .driver_data = LG_FF3 },
24985cf6
JK
878 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR),
879 .driver_data = LG_RDESC_REL_ABS },
880 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER),
881 .driver_data = LG_RDESC_REL_ABS },
5f22a799
JS
882 { }
883};
24985cf6 884
5f22a799
JS
885MODULE_DEVICE_TABLE(hid, lg_devices);
886
887static struct hid_driver lg_driver = {
888 .name = "logitech",
889 .id_table = lg_devices,
890 .report_fixup = lg_report_fixup,
891 .input_mapping = lg_input_mapping,
892 .input_mapped = lg_input_mapped,
893 .event = lg_event,
c832f86e 894 .raw_event = lg_raw_event,
5f22a799 895 .probe = lg_probe,
30bb75d7 896 .remove = lg_remove,
5f22a799 897};
f425458e 898module_hid_driver(lg_driver);
5f22a799 899
a54dc779
MM
900#ifdef CONFIG_LOGIWHEELS_FF
901int lg4ff_no_autoswitch = 0;
902module_param_named(lg4ff_no_autoswitch, lg4ff_no_autoswitch, int, S_IRUGO);
903MODULE_PARM_DESC(lg4ff_no_autoswitch, "Do not switch multimode wheels to their native mode automatically");
904#endif
905
5f22a799 906MODULE_LICENSE("GPL");