Linux Audio

Check our new training course

Loading...
v6.8
  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_wireless_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_CONSUMER)
575		return 0;
576
577	switch (usage->hid & HID_USAGE) {
578	case 0x1001: lg_map_key_clear(KEY_MESSENGER);		break;
579	case 0x1003: lg_map_key_clear(KEY_SOUND);		break;
580	case 0x1004: lg_map_key_clear(KEY_VIDEO);		break;
581	case 0x1005: lg_map_key_clear(KEY_AUDIO);		break;
582	case 0x100a: lg_map_key_clear(KEY_DOCUMENTS);		break;
583	/* The following two entries are Playlist 1 and 2 on the MX3200 */
584	case 0x100f: lg_map_key_clear(KEY_FN_1);		break;
585	case 0x1010: lg_map_key_clear(KEY_FN_2);		break;
586	case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG);	break;
587	case 0x1012: lg_map_key_clear(KEY_NEXTSONG);		break;
588	case 0x1013: lg_map_key_clear(KEY_CAMERA);		break;
589	case 0x1014: lg_map_key_clear(KEY_MESSENGER);		break;
590	case 0x1015: lg_map_key_clear(KEY_RECORD);		break;
591	case 0x1016: lg_map_key_clear(KEY_PLAYER);		break;
592	case 0x1017: lg_map_key_clear(KEY_EJECTCD);		break;
593	case 0x1018: lg_map_key_clear(KEY_MEDIA);		break;
594	case 0x1019: lg_map_key_clear(KEY_PROG1);		break;
595	case 0x101a: lg_map_key_clear(KEY_PROG2);		break;
596	case 0x101b: lg_map_key_clear(KEY_PROG3);		break;
597	case 0x101c: lg_map_key_clear(KEY_CYCLEWINDOWS);	break;
598	case 0x101f: lg_map_key_clear(KEY_ZOOMIN);		break;
599	case 0x1020: lg_map_key_clear(KEY_ZOOMOUT);		break;
600	case 0x1021: lg_map_key_clear(KEY_ZOOMRESET);		break;
601	case 0x1023: lg_map_key_clear(KEY_CLOSE);		break;
602	case 0x1027: lg_map_key_clear(KEY_MENU);		break;
603	/* this one is marked as 'Rotate' */
604	case 0x1028: lg_map_key_clear(KEY_ANGLE);		break;
605	case 0x1029: lg_map_key_clear(KEY_SHUFFLE);		break;
606	case 0x102a: lg_map_key_clear(KEY_BACK);		break;
607	case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS);	break;
608	case 0x102d: lg_map_key_clear(KEY_WWW);			break;
609	/* The following two are 'Start/answer call' and 'End/reject call'
610	   on the MX3200 */
611	case 0x1031: lg_map_key_clear(KEY_OK);			break;
612	case 0x1032: lg_map_key_clear(KEY_CANCEL);		break;
613	case 0x1041: lg_map_key_clear(KEY_BATTERY);		break;
614	case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR);	break;
615	case 0x1043: lg_map_key_clear(KEY_SPREADSHEET);		break;
616	case 0x1044: lg_map_key_clear(KEY_PRESENTATION);	break;
617	case 0x1045: lg_map_key_clear(KEY_UNDO);		break;
618	case 0x1046: lg_map_key_clear(KEY_REDO);		break;
619	case 0x1047: lg_map_key_clear(KEY_PRINT);		break;
620	case 0x1048: lg_map_key_clear(KEY_SAVE);		break;
621	case 0x1049: lg_map_key_clear(KEY_PROG1);		break;
622	case 0x104a: lg_map_key_clear(KEY_PROG2);		break;
623	case 0x104b: lg_map_key_clear(KEY_PROG3);		break;
624	case 0x104c: lg_map_key_clear(KEY_PROG4);		break;
625
626	default:
627		return 0;
628	}
629	return 1;
630}
631
632static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
633		struct hid_field *field, struct hid_usage *usage,
634		unsigned long **bit, int *max)
635{
636	/* extended mapping for certain Logitech hardware (Logitech cordless
637	   desktop LX500) */
638	static const u8 e_keymap[] = {
639		  0,216,  0,213,175,156,  0,  0,  0,  0,
640		144,  0,  0,  0,  0,  0,  0,  0,  0,212,
641		174,167,152,161,112,  0,  0,  0,154,  0,
642		  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
643		  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
644		  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
645		  0,  0,  0,  0,  0,183,184,185,186,187,
646		188,189,190,191,192,193,194,  0,  0,  0
647	};
648	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
649	unsigned int hid = usage->hid;
650
651	if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER &&
652			lg_ultrax_remote_mapping(hi, usage, bit, max))
653		return 1;
654
 
 
 
 
655	if ((drv_data->quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
656		return 1;
657
658	if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
659		return 0;
660
661	hid &= HID_USAGE;
662
663	/* Special handling for Logitech Cordless Desktop */
664	if (field->application == HID_GD_MOUSE) {
665		if ((drv_data->quirks & LG_IGNORE_DOUBLED_WHEEL) &&
666				(hid == 7 || hid == 8))
667			return -1;
668	} else {
669		if ((drv_data->quirks & LG_EXPANDED_KEYMAP) &&
670				hid < ARRAY_SIZE(e_keymap) &&
671				e_keymap[hid] != 0) {
672			hid_map_usage(hi, usage, bit, max, EV_KEY,
673					e_keymap[hid]);
674			return 1;
675		}
676	}
677
678	return 0;
679}
680
681static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
682		struct hid_field *field, struct hid_usage *usage,
683		unsigned long **bit, int *max)
684{
685	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
686
687	if ((drv_data->quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
688			(field->flags & HID_MAIN_ITEM_RELATIVE))
689		field->flags &= ~HID_MAIN_ITEM_RELATIVE;
690
691	if ((drv_data->quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY ||
692			 usage->type == EV_REL || usage->type == EV_ABS))
693		clear_bit(usage->code, *bit);
694
695	/* Ensure that Logitech wheels are not given a default fuzz/flat value */
696	if (usage->type == EV_ABS && (usage->code == ABS_X ||
697			usage->code == ABS_Y || usage->code == ABS_Z ||
698			usage->code == ABS_RZ)) {
699		switch (hdev->product) {
700		case USB_DEVICE_ID_LOGITECH_G29_WHEEL:
701		case USB_DEVICE_ID_LOGITECH_WINGMAN_FG:
702		case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
703		case USB_DEVICE_ID_LOGITECH_WHEEL:
704		case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
705		case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
706		case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
707		case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
708		case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
709		case USB_DEVICE_ID_LOGITECH_WII_WHEEL:
710		case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
711		case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL:
712			field->application = HID_GD_MULTIAXIS;
713			break;
714		default:
715			break;
716		}
717	}
718
719	return 0;
720}
721
722static int lg_event(struct hid_device *hdev, struct hid_field *field,
723		struct hid_usage *usage, __s32 value)
724{
725	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
726
727	if ((drv_data->quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
728		input_event(field->hidinput->input, usage->type, usage->code,
729				-value);
730		return 1;
731	}
732	if (drv_data->quirks & LG_FF4) {
733		return lg4ff_adjust_input_event(hdev, field, usage, value, drv_data);
734	}
735
736	return 0;
737}
738
739static int lg_raw_event(struct hid_device *hdev, struct hid_report *report,
740		u8 *rd, int size)
741{
742	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
743
744	if (drv_data->quirks & LG_FF4)
745		return lg4ff_raw_event(hdev, report, rd, size, drv_data);
746
747	return 0;
748}
749
750static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
751{
752	struct usb_interface *iface;
753	__u8 iface_num;
754	unsigned int connect_mask = HID_CONNECT_DEFAULT;
755	struct lg_drv_data *drv_data;
756	int ret;
757
758	if (!hid_is_usb(hdev))
759		return -EINVAL;
760
761	iface = to_usb_interface(hdev->dev.parent);
762	iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
763
764	/* G29 only work with the 1st interface */
765	if ((hdev->product == USB_DEVICE_ID_LOGITECH_G29_WHEEL) &&
766	    (iface_num != 0)) {
767		dbg_hid("%s: ignoring ifnum %d\n", __func__, iface_num);
768		return -ENODEV;
769	}
770
771	drv_data = kzalloc(sizeof(struct lg_drv_data), GFP_KERNEL);
772	if (!drv_data) {
773		hid_err(hdev, "Insufficient memory, cannot allocate driver data\n");
774		return -ENOMEM;
775	}
776	drv_data->quirks = id->driver_data;
777
778	hid_set_drvdata(hdev, (void *)drv_data);
779
780	if (drv_data->quirks & LG_NOGET)
781		hdev->quirks |= HID_QUIRK_NOGET;
782
783	ret = hid_parse(hdev);
784	if (ret) {
785		hid_err(hdev, "parse failed\n");
786		goto err_free;
787	}
788
789	if (drv_data->quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4))
790		connect_mask &= ~HID_CONNECT_FF;
791
792	ret = hid_hw_start(hdev, connect_mask);
793	if (ret) {
794		hid_err(hdev, "hw start failed\n");
795		goto err_free;
796	}
797
798	/* Setup wireless link with Logitech Wii wheel */
799	if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
800		static const unsigned char cbuf[] = {
801			0x00, 0xAF,  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
802		};
803		u8 *buf = kmemdup(cbuf, sizeof(cbuf), GFP_KERNEL);
804
805		if (!buf) {
806			ret = -ENOMEM;
807			goto err_stop;
808		}
809
810		ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(cbuf),
811					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
812		if (ret >= 0) {
813			/* insert a little delay of 10 jiffies ~ 40ms */
814			wait_queue_head_t wait;
815			init_waitqueue_head (&wait);
816			wait_event_interruptible_timeout(wait, 0,
817							 msecs_to_jiffies(40));
818
819			/* Select random Address */
820			buf[1] = 0xB2;
821			get_random_bytes(&buf[2], 2);
822
823			ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(cbuf),
824					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
825		}
826		kfree(buf);
827	}
828
829	if (drv_data->quirks & LG_FF)
830		ret = lgff_init(hdev);
831	else if (drv_data->quirks & LG_FF2)
832		ret = lg2ff_init(hdev);
833	else if (drv_data->quirks & LG_FF3)
834		ret = lg3ff_init(hdev);
835	else if (drv_data->quirks & LG_FF4)
836		ret = lg4ff_init(hdev);
837
838	if (ret)
839		goto err_stop;
840
841	return 0;
842
843err_stop:
844	hid_hw_stop(hdev);
845err_free:
846	kfree(drv_data);
847	return ret;
848}
849
850static void lg_remove(struct hid_device *hdev)
851{
852	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
853	if (drv_data->quirks & LG_FF4)
854		lg4ff_deinit(hdev);
855	hid_hw_stop(hdev);
 
856	kfree(drv_data);
857}
858
859static const struct hid_device_id lg_devices[] = {
 
 
860	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER),
861		.driver_data = LG_RDESC | LG_WIRELESS },
 
 
862
863	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER),
864		.driver_data = LG_BAD_RELATIVE_KEYS },
865
866	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP),
867		.driver_data = LG_DUPLICATE_USAGES },
 
 
 
 
868
869	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD),
870		.driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
871	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500),
872		.driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
873
874	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
875		.driver_data = LG_NOGET },
876	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DUAL_ACTION),
877		.driver_data = LG_NOGET },
878	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
879		.driver_data = LG_NOGET | LG_FF4 },
880
881	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD),
882		.driver_data = LG_FF2 },
883	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD),
884		.driver_data = LG_FF },
885	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2),
886		.driver_data = LG_FF },
887	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G29_WHEEL),
888		.driver_data = LG_FF4 },
889	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D),
890		.driver_data = LG_FF },
891	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
892		.driver_data = LG_FF },
893	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL),
894		.driver_data = LG_NOGET | LG_FF4 },
895	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
896		.driver_data = LG_FF4 },
897	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL),
898		.driver_data = LG_FF2 },
899	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
900		.driver_data = LG_FF4 },
901	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL),
902		.driver_data = LG_FF4 },
903	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL),
904		.driver_data = LG_FF4 },
905	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL),
906		.driver_data = LG_NOGET | LG_FF4 },
907	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
908		.driver_data = LG_FF4 },
909	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FG),
910		.driver_data = LG_NOGET },
911	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
912		.driver_data = LG_NOGET | LG_FF4 },
913	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
914		.driver_data = LG_NOGET | LG_FF2 },
915	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940),
916		.driver_data = LG_FF3 },
917	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR),
918		.driver_data = LG_RDESC_REL_ABS },
919	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER),
920		.driver_data = LG_RDESC_REL_ABS },
921	{ }
922};
923
924MODULE_DEVICE_TABLE(hid, lg_devices);
925
926static struct hid_driver lg_driver = {
927	.name = "logitech",
928	.id_table = lg_devices,
929	.report_fixup = lg_report_fixup,
930	.input_mapping = lg_input_mapping,
931	.input_mapped = lg_input_mapped,
932	.event = lg_event,
933	.raw_event = lg_raw_event,
934	.probe = lg_probe,
935	.remove = lg_remove,
936};
937module_hid_driver(lg_driver);
938
939#ifdef CONFIG_LOGIWHEELS_FF
940int lg4ff_no_autoswitch = 0;
941module_param_named(lg4ff_no_autoswitch, lg4ff_no_autoswitch, int, S_IRUGO);
942MODULE_PARM_DESC(lg4ff_no_autoswitch, "Do not switch multimode wheels to their native mode automatically");
943#endif
944
945MODULE_LICENSE("GPL");
v4.10.11
 
  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
  8 *  Copyright (c) 2008 Jiri Slaby
  9 *  Copyright (c) 2010 Hendrik Iben
 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>
 22#include <linux/random.h>
 23#include <linux/sched.h>
 24#include <linux/usb.h>
 25#include <linux/wait.h>
 26
 27#include "usbhid/usbhid.h"
 28#include "hid-ids.h"
 29#include "hid-lg.h"
 30#include "hid-lg4ff.h"
 31
 32#define LG_RDESC		0x001
 33#define LG_BAD_RELATIVE_KEYS	0x002
 34#define LG_DUPLICATE_USAGES	0x004
 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
 40#define LG_FF			0x200
 41#define LG_FF2			0x400
 42#define LG_RDESC_REL_ABS	0x800
 43#define LG_FF3			0x1000
 44#define LG_FF4			0x2000
 45
 46/* Size of the original descriptors of the Driving Force (and Pro) wheels */
 47#define DF_RDESC_ORIG_SIZE	130
 48#define DFP_RDESC_ORIG_SIZE	97
 49#define FV_RDESC_ORIG_SIZE	130
 50#define MOMO_RDESC_ORIG_SIZE	87
 51#define MOMO2_RDESC_ORIG_SIZE	87
 52#define FFG_RDESC_ORIG_SIZE	85
 
 53
 54/* Fixed report descriptors for Logitech Driving Force (and Pro)
 55 * wheel controllers
 56 *
 57 * The original descriptors hide the separate throttle and brake axes in
 58 * a custom vendor usage page, providing only a combined value as
 59 * GenericDesktop.Y.
 60 * These descriptors remove the combined Y axis and instead report
 61 * separate throttle (Y) and brake (RZ).
 62 */
 63static __u8 df_rdesc_fixed[] = {
 640x05, 0x01,         /*  Usage Page (Desktop),                   */
 650x09, 0x04,         /*  Usage (Joystick),                       */
 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),           */
 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),               */
1080x05, 0x01,         /*          Usage Page (Desktop),           */
1090x95, 0x01,         /*          Report Count (1),               */
1100x75, 0x08,         /*          Report Size (8),                */
1110x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
1120x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
1130x09, 0x31,         /*          Usage (Y),                      */
1140x81, 0x02,         /*          Input (Variable),               */
1150x09, 0x35,         /*          Usage (Rz),                     */
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
129static __u8 dfp_rdesc_fixed[] = {
1300x05, 0x01,         /*  Usage Page (Desktop),                   */
1310x09, 0x04,         /*  Usage (Joystick),                       */
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
177static __u8 fv_rdesc_fixed[] = {
1780x05, 0x01,         /*  Usage Page (Desktop),                   */
1790x09, 0x04,         /*  Usage (Joystick),                       */
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
244static __u8 momo_rdesc_fixed[] = {
2450x05, 0x01,         /*  Usage Page (Desktop),               */
2460x09, 0x04,         /*  Usage (Joystick),                   */
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};
289
290static __u8 momo2_rdesc_fixed[] = {
2910x05, 0x01,         /*  Usage Page (Desktop),               */
2920x09, 0x04,         /*  Usage (Joystick),                   */
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
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
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 */
389static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
390		unsigned int *rsize)
391{
392	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
393
394	if ((drv_data->quirks & LG_RDESC) && *rsize >= 91 && rdesc[83] == 0x26 &&
395			rdesc[84] == 0x8c && rdesc[85] == 0x02) {
396		hid_info(hdev,
397			 "fixing up Logitech keyboard report descriptor\n");
398		rdesc[84] = rdesc[89] = 0x4d;
399		rdesc[85] = rdesc[90] = 0x10;
400	}
401	if ((drv_data->quirks & LG_RDESC_REL_ABS) && *rsize >= 51 &&
402			rdesc[32] == 0x81 && rdesc[33] == 0x06 &&
403			rdesc[49] == 0x81 && rdesc[50] == 0x06) {
404		hid_info(hdev,
405			 "fixing up rel/abs in Logitech report descriptor\n");
406		rdesc[33] = rdesc[50] = 0x02;
407	}
408
409	switch (hdev->product) {
410
 
 
 
 
 
 
 
 
 
 
 
 
 
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
420	/* Several wheels report as this id when operating in emulation mode. */
421	case USB_DEVICE_ID_LOGITECH_WHEEL:
422		if (*rsize == DF_RDESC_ORIG_SIZE) {
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
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
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
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
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;
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;
476	}
477
478	return rdesc;
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
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
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;
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;
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;
559	case 0x101c: lg_map_key_clear(KEY_CYCLEWINDOWS);	break;
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;
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;
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	};
610	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
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
617	if (hdev->product == USB_DEVICE_ID_DINOVO_MINI &&
618			lg_dinovo_mapping(hi, usage, bit, max))
619		return 1;
620
621	if ((drv_data->quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
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) {
631		if ((drv_data->quirks & LG_IGNORE_DOUBLED_WHEEL) &&
632				(hid == 7 || hid == 8))
633			return -1;
634	} else {
635		if ((drv_data->quirks & LG_EXPANDED_KEYMAP) &&
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{
651	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
652
653	if ((drv_data->quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
654			(field->flags & HID_MAIN_ITEM_RELATIVE))
655		field->flags &= ~HID_MAIN_ITEM_RELATIVE;
656
657	if ((drv_data->quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY ||
658			 usage->type == EV_REL || usage->type == EV_ABS))
659		clear_bit(usage->code, *bit);
660
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) {
666		case USB_DEVICE_ID_LOGITECH_G29_WHEEL:
 
667		case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
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:
676		case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL:
677			field->application = HID_GD_MULTIAXIS;
678			break;
679		default:
680			break;
681		}
682	}
683
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{
690	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
691
692	if ((drv_data->quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
693		input_event(field->hidinput->input, usage->type, usage->code,
694				-value);
695		return 1;
696	}
697	if (drv_data->quirks & LG_FF4) {
698		return lg4ff_adjust_input_event(hdev, field, usage, value, drv_data);
699	}
700
701	return 0;
702}
703
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
715static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
716{
717	struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
718	__u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
719	unsigned int connect_mask = HID_CONNECT_DEFAULT;
720	struct lg_drv_data *drv_data;
721	int ret;
722
 
 
 
 
 
 
723	/* G29 only work with the 1st interface */
724	if ((hdev->product == USB_DEVICE_ID_LOGITECH_G29_WHEEL) &&
725	    (iface_num != 0)) {
726		dbg_hid("%s: ignoring ifnum %d\n", __func__, iface_num);
727		return -ENODEV;
728	}
729
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;
736
737	hid_set_drvdata(hdev, (void *)drv_data);
738
739	if (drv_data->quirks & LG_NOGET)
740		hdev->quirks |= HID_QUIRK_NOGET;
741
742	ret = hid_parse(hdev);
743	if (ret) {
744		hid_err(hdev, "parse failed\n");
745		goto err_free;
746	}
747
748	if (drv_data->quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4))
749		connect_mask &= ~HID_CONNECT_FF;
750
751	ret = hid_hw_start(hdev, connect_mask);
752	if (ret) {
753		hid_err(hdev, "hw start failed\n");
754		goto err_free;
755	}
756
757	/* Setup wireless link with Logitech Wii wheel */
758	if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
759		const unsigned char cbuf[] = { 0x00, 0xAF,  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
 
 
760		u8 *buf = kmemdup(cbuf, sizeof(cbuf), GFP_KERNEL);
761
762		if (!buf) {
763			ret = -ENOMEM;
764			goto err_free;
765		}
766
767		ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(cbuf),
768					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
769		if (ret >= 0) {
770			/* insert a little delay of 10 jiffies ~ 40ms */
771			wait_queue_head_t wait;
772			init_waitqueue_head (&wait);
773			wait_event_interruptible_timeout(wait, 0,
774							 msecs_to_jiffies(40));
775
776			/* Select random Address */
777			buf[1] = 0xB2;
778			get_random_bytes(&buf[2], 2);
779
780			ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(cbuf),
781					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
782		}
783		kfree(buf);
784	}
785
786	if (drv_data->quirks & LG_FF)
787		ret = lgff_init(hdev);
788	else if (drv_data->quirks & LG_FF2)
789		ret = lg2ff_init(hdev);
790	else if (drv_data->quirks & LG_FF3)
791		ret = lg3ff_init(hdev);
792	else if (drv_data->quirks & LG_FF4)
793		ret = lg4ff_init(hdev);
794
795	if (ret)
796		goto err_free;
797
798	return 0;
 
 
 
799err_free:
800	kfree(drv_data);
801	return ret;
802}
803
804static void lg_remove(struct hid_device *hdev)
805{
806	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
807	if (drv_data->quirks & LG_FF4)
808		lg4ff_deinit(hdev);
809	else
810		hid_hw_stop(hdev);
811	kfree(drv_data);
812}
813
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
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
837	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
838		.driver_data = LG_NOGET },
839	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DUAL_ACTION),
840		.driver_data = LG_NOGET },
841	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
842		.driver_data = LG_NOGET | LG_FF4 },
843
844	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD),
845		.driver_data = LG_FF2 },
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 },
850	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G29_WHEEL),
851		.driver_data = LG_FF4 },
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),
857		.driver_data = LG_NOGET | LG_FF4 },
858	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
859		.driver_data = LG_FF4 },
860	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL),
861		.driver_data = LG_FF2 },
862	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
863		.driver_data = LG_FF4 },
864	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL),
865		.driver_data = LG_FF4 },
866	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL),
867		.driver_data = LG_FF4 },
868	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL),
869		.driver_data = LG_NOGET | LG_FF4 },
870	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
871		.driver_data = LG_FF4 },
 
 
872	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
873		.driver_data = LG_NOGET | LG_FF4 },
874	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
875		.driver_data = LG_NOGET | LG_FF2 },
876	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940),
877		.driver_data = LG_FF3 },
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 },
882	{ }
883};
884
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,
894	.raw_event = lg_raw_event,
895	.probe = lg_probe,
896	.remove = lg_remove,
897};
898module_hid_driver(lg_driver);
899
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
906MODULE_LICENSE("GPL");