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