Linux Audio

Check our new training course

Loading...
v5.9
  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 = to_usb_interface(hdev->dev.parent);
773	__u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
774	unsigned int connect_mask = HID_CONNECT_DEFAULT;
775	struct lg_drv_data *drv_data;
776	int ret;
777
778	/* G29 only work with the 1st interface */
779	if ((hdev->product == USB_DEVICE_ID_LOGITECH_G29_WHEEL) &&
780	    (iface_num != 0)) {
781		dbg_hid("%s: ignoring ifnum %d\n", __func__, iface_num);
782		return -ENODEV;
783	}
784
785	drv_data = kzalloc(sizeof(struct lg_drv_data), GFP_KERNEL);
786	if (!drv_data) {
787		hid_err(hdev, "Insufficient memory, cannot allocate driver data\n");
788		return -ENOMEM;
789	}
790	drv_data->quirks = id->driver_data;
791
792	hid_set_drvdata(hdev, (void *)drv_data);
793
794	if (drv_data->quirks & LG_NOGET)
795		hdev->quirks |= HID_QUIRK_NOGET;
796
797	ret = hid_parse(hdev);
798	if (ret) {
799		hid_err(hdev, "parse failed\n");
800		goto err_free;
801	}
802
803	if (drv_data->quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4))
804		connect_mask &= ~HID_CONNECT_FF;
805
806	ret = hid_hw_start(hdev, connect_mask);
807	if (ret) {
808		hid_err(hdev, "hw start failed\n");
809		goto err_free;
810	}
811
812	/* Setup wireless link with Logitech Wii wheel */
813	if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
814		static const unsigned char cbuf[] = {
815			0x00, 0xAF,  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
816		};
817		u8 *buf = kmemdup(cbuf, sizeof(cbuf), GFP_KERNEL);
818
819		if (!buf) {
820			ret = -ENOMEM;
821			goto err_stop;
822		}
823
824		ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(cbuf),
825					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
826		if (ret >= 0) {
827			/* insert a little delay of 10 jiffies ~ 40ms */
828			wait_queue_head_t wait;
829			init_waitqueue_head (&wait);
830			wait_event_interruptible_timeout(wait, 0,
831							 msecs_to_jiffies(40));
832
833			/* Select random Address */
834			buf[1] = 0xB2;
835			get_random_bytes(&buf[2], 2);
836
837			ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(cbuf),
838					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
839		}
840		kfree(buf);
841	}
842
843	if (drv_data->quirks & LG_FF)
844		ret = lgff_init(hdev);
845	else if (drv_data->quirks & LG_FF2)
846		ret = lg2ff_init(hdev);
847	else if (drv_data->quirks & LG_FF3)
848		ret = lg3ff_init(hdev);
849	else if (drv_data->quirks & LG_FF4)
850		ret = lg4ff_init(hdev);
851
852	if (ret)
853		goto err_stop;
854
855	return 0;
856
857err_stop:
858	hid_hw_stop(hdev);
859err_free:
860	kfree(drv_data);
861	return ret;
862}
863
864static void lg_remove(struct hid_device *hdev)
865{
866	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
867	if (drv_data->quirks & LG_FF4)
868		lg4ff_deinit(hdev);
869	hid_hw_stop(hdev);
870	kfree(drv_data);
871}
872
873static const struct hid_device_id lg_devices[] = {
874	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER),
875		.driver_data = LG_RDESC | LG_WIRELESS },
876
877	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER),
878		.driver_data = LG_BAD_RELATIVE_KEYS },
879
880	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP),
881		.driver_data = LG_DUPLICATE_USAGES },
882	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE),
883		.driver_data = LG_DUPLICATE_USAGES },
884	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI),
885		.driver_data = LG_DUPLICATE_USAGES },
886
887	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD),
888		.driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
889	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500),
890		.driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
891
892	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
893		.driver_data = LG_NOGET },
894	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DUAL_ACTION),
895		.driver_data = LG_NOGET },
896	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
897		.driver_data = LG_NOGET | LG_FF4 },
898
899	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD),
900		.driver_data = LG_FF2 },
901	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD),
902		.driver_data = LG_FF },
903	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2),
904		.driver_data = LG_FF },
905	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G29_WHEEL),
906		.driver_data = LG_FF4 },
907	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D),
908		.driver_data = LG_FF },
909	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
910		.driver_data = LG_FF },
911	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL),
912		.driver_data = LG_NOGET | LG_FF4 },
913	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
914		.driver_data = LG_FF4 },
915	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL),
916		.driver_data = LG_FF2 },
917	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
918		.driver_data = LG_FF4 },
919	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL),
920		.driver_data = LG_FF4 },
921	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL),
922		.driver_data = LG_FF4 },
923	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL),
924		.driver_data = LG_NOGET | LG_FF4 },
925	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
926		.driver_data = LG_FF4 },
927	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FG),
928		.driver_data = LG_NOGET },
929	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
930		.driver_data = LG_NOGET | LG_FF4 },
931	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
932		.driver_data = LG_NOGET | LG_FF2 },
933	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940),
934		.driver_data = LG_FF3 },
935	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR),
936		.driver_data = LG_RDESC_REL_ABS },
937	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER),
938		.driver_data = LG_RDESC_REL_ABS },
939	{ }
940};
941
942MODULE_DEVICE_TABLE(hid, lg_devices);
943
944static struct hid_driver lg_driver = {
945	.name = "logitech",
946	.id_table = lg_devices,
947	.report_fixup = lg_report_fixup,
948	.input_mapping = lg_input_mapping,
949	.input_mapped = lg_input_mapped,
950	.event = lg_event,
951	.raw_event = lg_raw_event,
952	.probe = lg_probe,
953	.remove = lg_remove,
954};
955module_hid_driver(lg_driver);
956
957#ifdef CONFIG_LOGIWHEELS_FF
958int lg4ff_no_autoswitch = 0;
959module_param_named(lg4ff_no_autoswitch, lg4ff_no_autoswitch, int, S_IRUGO);
960MODULE_PARM_DESC(lg4ff_no_autoswitch, "Do not switch multimode wheels to their native mode automatically");
961#endif
962
963MODULE_LICENSE("GPL");
v5.4
  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 = to_usb_interface(hdev->dev.parent);
773	__u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
774	unsigned int connect_mask = HID_CONNECT_DEFAULT;
775	struct lg_drv_data *drv_data;
776	int ret;
777
778	/* G29 only work with the 1st interface */
779	if ((hdev->product == USB_DEVICE_ID_LOGITECH_G29_WHEEL) &&
780	    (iface_num != 0)) {
781		dbg_hid("%s: ignoring ifnum %d\n", __func__, iface_num);
782		return -ENODEV;
783	}
784
785	drv_data = kzalloc(sizeof(struct lg_drv_data), GFP_KERNEL);
786	if (!drv_data) {
787		hid_err(hdev, "Insufficient memory, cannot allocate driver data\n");
788		return -ENOMEM;
789	}
790	drv_data->quirks = id->driver_data;
791
792	hid_set_drvdata(hdev, (void *)drv_data);
793
794	if (drv_data->quirks & LG_NOGET)
795		hdev->quirks |= HID_QUIRK_NOGET;
796
797	ret = hid_parse(hdev);
798	if (ret) {
799		hid_err(hdev, "parse failed\n");
800		goto err_free;
801	}
802
803	if (drv_data->quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4))
804		connect_mask &= ~HID_CONNECT_FF;
805
806	ret = hid_hw_start(hdev, connect_mask);
807	if (ret) {
808		hid_err(hdev, "hw start failed\n");
809		goto err_free;
810	}
811
812	/* Setup wireless link with Logitech Wii wheel */
813	if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
814		static const unsigned char cbuf[] = {
815			0x00, 0xAF,  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
816		};
817		u8 *buf = kmemdup(cbuf, sizeof(cbuf), GFP_KERNEL);
818
819		if (!buf) {
820			ret = -ENOMEM;
821			goto err_stop;
822		}
823
824		ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(cbuf),
825					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
826		if (ret >= 0) {
827			/* insert a little delay of 10 jiffies ~ 40ms */
828			wait_queue_head_t wait;
829			init_waitqueue_head (&wait);
830			wait_event_interruptible_timeout(wait, 0,
831							 msecs_to_jiffies(40));
832
833			/* Select random Address */
834			buf[1] = 0xB2;
835			get_random_bytes(&buf[2], 2);
836
837			ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(cbuf),
838					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
839		}
840		kfree(buf);
841	}
842
843	if (drv_data->quirks & LG_FF)
844		ret = lgff_init(hdev);
845	else if (drv_data->quirks & LG_FF2)
846		ret = lg2ff_init(hdev);
847	else if (drv_data->quirks & LG_FF3)
848		ret = lg3ff_init(hdev);
849	else if (drv_data->quirks & LG_FF4)
850		ret = lg4ff_init(hdev);
851
852	if (ret)
853		goto err_stop;
854
855	return 0;
856
857err_stop:
858	hid_hw_stop(hdev);
859err_free:
860	kfree(drv_data);
861	return ret;
862}
863
864static void lg_remove(struct hid_device *hdev)
865{
866	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
867	if (drv_data->quirks & LG_FF4)
868		lg4ff_deinit(hdev);
869	hid_hw_stop(hdev);
870	kfree(drv_data);
871}
872
873static const struct hid_device_id lg_devices[] = {
874	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER),
875		.driver_data = LG_RDESC | LG_WIRELESS },
876
877	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER),
878		.driver_data = LG_BAD_RELATIVE_KEYS },
879
880	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP),
881		.driver_data = LG_DUPLICATE_USAGES },
882	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE),
883		.driver_data = LG_DUPLICATE_USAGES },
884	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI),
885		.driver_data = LG_DUPLICATE_USAGES },
886
887	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD),
888		.driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
889	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500),
890		.driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
891
892	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
893		.driver_data = LG_NOGET },
894	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DUAL_ACTION),
895		.driver_data = LG_NOGET },
896	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
897		.driver_data = LG_NOGET | LG_FF4 },
898
899	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD),
900		.driver_data = LG_FF2 },
901	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD),
902		.driver_data = LG_FF },
903	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2),
904		.driver_data = LG_FF },
905	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G29_WHEEL),
906		.driver_data = LG_FF4 },
907	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D),
908		.driver_data = LG_FF },
909	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
910		.driver_data = LG_FF },
911	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL),
912		.driver_data = LG_NOGET | LG_FF4 },
913	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
914		.driver_data = LG_FF4 },
915	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL),
916		.driver_data = LG_FF2 },
917	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
918		.driver_data = LG_FF4 },
919	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL),
920		.driver_data = LG_FF4 },
921	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL),
922		.driver_data = LG_FF4 },
923	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL),
924		.driver_data = LG_NOGET | LG_FF4 },
925	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
926		.driver_data = LG_FF4 },
927	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FG),
928		.driver_data = LG_NOGET },
929	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
930		.driver_data = LG_NOGET | LG_FF4 },
931	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
932		.driver_data = LG_NOGET | LG_FF2 },
933	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940),
934		.driver_data = LG_FF3 },
935	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR),
936		.driver_data = LG_RDESC_REL_ABS },
937	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER),
938		.driver_data = LG_RDESC_REL_ABS },
939	{ }
940};
941
942MODULE_DEVICE_TABLE(hid, lg_devices);
943
944static struct hid_driver lg_driver = {
945	.name = "logitech",
946	.id_table = lg_devices,
947	.report_fixup = lg_report_fixup,
948	.input_mapping = lg_input_mapping,
949	.input_mapped = lg_input_mapped,
950	.event = lg_event,
951	.raw_event = lg_raw_event,
952	.probe = lg_probe,
953	.remove = lg_remove,
954};
955module_hid_driver(lg_driver);
956
957#ifdef CONFIG_LOGIWHEELS_FF
958int lg4ff_no_autoswitch = 0;
959module_param_named(lg4ff_no_autoswitch, lg4ff_no_autoswitch, int, S_IRUGO);
960MODULE_PARM_DESC(lg4ff_no_autoswitch, "Do not switch multimode wheels to their native mode automatically");
961#endif
962
963MODULE_LICENSE("GPL");