blob: 0dc7cdfc56f77470b8a8c853ebd0077b4955853c [file] [log] [blame]
Thomas Gleixner2874c5f2019-05-27 08:55:01 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Jiri Slaby5f22a792008-05-16 11:49:19 +02002/*
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
Jiri Slaby5f22a792008-05-16 11:49:19 +02009 * Copyright (c) 2008 Jiri Slaby
Hendrik Iben2c6118e2010-10-04 15:39:49 +020010 * Copyright (c) 2010 Hendrik Iben
Jiri Slaby5f22a792008-05-16 11:49:19 +020011 */
12
13/*
Jiri Slaby5f22a792008-05-16 11:49:19 +020014 */
15
16#include <linux/device.h>
17#include <linux/hid.h>
18#include <linux/module.h>
Simon Wood32c88cb2010-09-22 13:19:42 +020019#include <linux/random.h>
20#include <linux/sched.h>
Paul Sbarra54bfe3f2013-02-17 11:53:13 -060021#include <linux/usb.h>
Simon Wood32c88cb2010-09-22 13:19:42 +020022#include <linux/wait.h>
Jiri Slaby5f22a792008-05-16 11:49:19 +020023
Paul Sbarra54bfe3f2013-02-17 11:53:13 -060024#include "usbhid/usbhid.h"
Jiri Slaby5f22a792008-05-16 11:49:19 +020025#include "hid-ids.h"
Jiri Slaby606bd0a2008-07-04 23:06:45 +020026#include "hid-lg.h"
Michal Malýa54dc7792015-02-18 17:59:22 +010027#include "hid-lg4ff.h"
Jiri Slaby5f22a792008-05-16 11:49:19 +020028
29#define LG_RDESC 0x001
30#define LG_BAD_RELATIVE_KEYS 0x002
31#define LG_DUPLICATE_USAGES 0x004
Jiri Slaby5f22a792008-05-16 11:49:19 +020032#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
Jiri Slaby606bd0a2008-07-04 23:06:45 +020037#define LG_FF 0x200
38#define LG_FF2 0x400
Jiri Kosina24985cf2009-11-13 10:45:53 +010039#define LG_RDESC_REL_ABS 0x800
Gary Stein74f292c2010-01-13 00:25:58 +010040#define LG_FF3 0x1000
Simon Wood32c88cb2010-09-22 13:19:42 +020041#define LG_FF4 0x2000
Jiri Slaby5f22a792008-05-16 11:49:19 +020042
Paul Sbarra54bfe3f2013-02-17 11:53:13 -060043/* Size of the original descriptors of the Driving Force (and Pro) wheels */
44#define DF_RDESC_ORIG_SIZE 130
Michael Bauerdc0a4f02011-06-02 15:40:14 -070045#define DFP_RDESC_ORIG_SIZE 97
Simon Wood7f505472013-10-10 08:20:12 -060046#define FV_RDESC_ORIG_SIZE 130
Simon Wood270baef2013-02-19 20:25:10 -070047#define MOMO_RDESC_ORIG_SIZE 87
Simon Wood348cbaa2013-10-10 08:20:13 -060048#define MOMO2_RDESC_ORIG_SIZE 87
Simon Wood560bea32016-09-18 10:55:41 -060049#define FFG_RDESC_ORIG_SIZE 85
Jarrad Whitakere41b3cd2019-01-24 22:40:50 +110050#define FG_RDESC_ORIG_SIZE 82
Michael Bauerdc0a4f02011-06-02 15:40:14 -070051
Paul Sbarra54bfe3f2013-02-17 11:53:13 -060052/* Fixed report descriptors for Logitech Driving Force (and Pro)
53 * wheel controllers
Michael Bauerdc0a4f02011-06-02 15:40:14 -070054 *
Paul Sbarra54bfe3f2013-02-17 11:53:13 -060055 * The original descriptors hide the separate throttle and brake axes in
Michael Bauerdc0a4f02011-06-02 15:40:14 -070056 * a custom vendor usage page, providing only a combined value as
57 * GenericDesktop.Y.
Paul Sbarra54bfe3f2013-02-17 11:53:13 -060058 * These descriptors remove the combined Y axis and instead report
Michael Bauerdc0a4f02011-06-02 15:40:14 -070059 * separate throttle (Y) and brake (RZ).
60 */
Paul Sbarra54bfe3f2013-02-17 11:53:13 -060061static __u8 df_rdesc_fixed[] = {
620x05, 0x01, /* Usage Page (Desktop), */
Antonio Ospitebfb61ab2016-02-09 13:55:04 +0100630x09, 0x04, /* Usage (Joystick), */
Paul Sbarra54bfe3f2013-02-17 11:53:13 -0600640xA1, 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), */
Paul Sbarra54bfe3f2013-02-17 11:53:13 -0600870x26, 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), */
Paul Sbarra5a9b571b2013-02-17 11:53:14 -06001060x05, 0x01, /* Usage Page (Desktop), */
1070x95, 0x01, /* Report Count (1), */
Paul Sbarra54bfe3f2013-02-17 11:53:13 -06001080x75, 0x08, /* Report Size (8), */
1090x26, 0xFF, 0x00, /* Logical Maximum (255), */
1100x46, 0xFF, 0x00, /* Physical Maximum (255), */
Paul Sbarra5a9b571b2013-02-17 11:53:14 -06001110x09, 0x31, /* Usage (Y), */
1120x81, 0x02, /* Input (Variable), */
1130x09, 0x35, /* Usage (Rz), */
Paul Sbarra54bfe3f2013-02-17 11:53:13 -06001140x81, 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
Michael Bauerdc0a4f02011-06-02 15:40:14 -0700127static __u8 dfp_rdesc_fixed[] = {
1280x05, 0x01, /* Usage Page (Desktop), */
Antonio Ospitebfb61ab2016-02-09 13:55:04 +01001290x09, 0x04, /* Usage (Joystick), */
Michael Bauerdc0a4f02011-06-02 15:40:14 -07001300xA1, 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
Simon Wood7f505472013-10-10 08:20:12 -0600175static __u8 fv_rdesc_fixed[] = {
1760x05, 0x01, /* Usage Page (Desktop), */
Antonio Ospitebfb61ab2016-02-09 13:55:04 +01001770x09, 0x04, /* Usage (Joystick), */
Simon Wood7f505472013-10-10 08:20:12 -06001780xA1, 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
Simon Wood270baef2013-02-19 20:25:10 -0700242static __u8 momo_rdesc_fixed[] = {
2430x05, 0x01, /* Usage Page (Desktop), */
Antonio Ospitebfb61ab2016-02-09 13:55:04 +01002440x09, 0x04, /* Usage (Joystick), */
Simon Wood270baef2013-02-19 20:25:10 -07002450xA1, 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};
Michael Bauerdc0a4f02011-06-02 15:40:14 -0700287
Simon Wood348cbaa2013-10-10 08:20:13 -0600288static __u8 momo2_rdesc_fixed[] = {
2890x05, 0x01, /* Usage Page (Desktop), */
Antonio Ospitebfb61ab2016-02-09 13:55:04 +01002900x09, 0x04, /* Usage (Joystick), */
Simon Wood348cbaa2013-10-10 08:20:13 -06002910xA1, 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
Simon Wood560bea32016-09-18 10:55:41 -0600336static __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
Jarrad Whitakere41b3cd2019-01-24 22:40:50 +1100382static __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
Jiri Slaby5f22a792008-05-16 11:49:19 +0200425/*
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 */
Nikolai Kondrashov73e40082010-08-06 23:03:06 +0400430static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
431 unsigned int *rsize)
Jiri Slaby5f22a792008-05-16 11:49:19 +0200432{
Axel Lin25751552012-09-13 14:08:32 +0800433 struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
Jiri Slaby5f22a792008-05-16 11:49:19 +0200434
Jiri Kosina4ab25782014-08-21 09:57:48 -0500435 if ((drv_data->quirks & LG_RDESC) && *rsize >= 91 && rdesc[83] == 0x26 &&
Jiri Slaby5f22a792008-05-16 11:49:19 +0200436 rdesc[84] == 0x8c && rdesc[85] == 0x02) {
Joe Perches4291ee32010-12-09 19:29:03 -0800437 hid_info(hdev,
438 "fixing up Logitech keyboard report descriptor\n");
Jiri Slaby5f22a792008-05-16 11:49:19 +0200439 rdesc[84] = rdesc[89] = 0x4d;
440 rdesc[85] = rdesc[90] = 0x10;
441 }
Jiri Kosina4ab25782014-08-21 09:57:48 -0500442 if ((drv_data->quirks & LG_RDESC_REL_ABS) && *rsize >= 51 &&
Jiri Kosina24985cf2009-11-13 10:45:53 +0100443 rdesc[32] == 0x81 && rdesc[33] == 0x06 &&
444 rdesc[49] == 0x81 && rdesc[50] == 0x06) {
Joe Perches4291ee32010-12-09 19:29:03 -0800445 hid_info(hdev,
446 "fixing up rel/abs in Logitech report descriptor\n");
Jiri Kosina24985cf2009-11-13 10:45:53 +0100447 rdesc[33] = rdesc[50] = 0x02;
448 }
Michael Bauerdc0a4f02011-06-02 15:40:14 -0700449
450 switch (hdev->product) {
Paul Sbarra54bfe3f2013-02-17 11:53:13 -0600451
Jarrad Whitakere41b3cd2019-01-24 22:40:50 +1100452 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
Simon Wood560bea32016-09-18 10:55:41 -0600465 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
Paul Sbarra54bfe3f2013-02-17 11:53:13 -0600474 /* Several wheels report as this id when operating in emulation mode. */
475 case USB_DEVICE_ID_LOGITECH_WHEEL:
Simon Wood7363b252016-09-18 10:55:40 -0600476 if (*rsize == DF_RDESC_ORIG_SIZE) {
Paul Sbarra54bfe3f2013-02-17 11:53:13 -0600477 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
Simon Wood270baef2013-02-19 20:25:10 -0700484 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
Simon Wood348cbaa2013-10-10 08:20:13 -0600493 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
Simon Wood7f505472013-10-10 08:20:12 -0600502 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
Michael Bauerdc0a4f02011-06-02 15:40:14 -0700511 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;
Simon Woodb58362462013-02-19 20:25:11 -0700519
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;
Michael Bauerdc0a4f02011-06-02 15:40:14 -0700530 }
531
Nikolai Kondrashov73e40082010-08-06 23:03:06 +0400532 return rdesc;
Jiri Slaby5f22a792008-05-16 11:49:19 +0200533}
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
Jiri Kosina66d61be2009-11-24 18:22:20 +0100571static 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
Jiri Slaby5f22a792008-05-16 11:49:19 +0200587static 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;
Lorenzo Castelli183922122010-04-16 19:00:31 +0200599 /* 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;
Jiri Slaby5f22a792008-05-16 11:49:19 +0200602 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;
Lorenzo Castelli183922122010-04-16 19:00:31 +0200613 case 0x101c: lg_map_key_clear(KEY_CYCLEWINDOWS); break;
Jiri Slaby5f22a792008-05-16 11:49:19 +0200614 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;
Lorenzo Castelli183922122010-04-16 19:00:31 +0200624 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;
Jiri Slaby5f22a792008-05-16 11:49:19 +0200629 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 };
Axel Lin25751552012-09-13 14:08:32 +0800664 struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
Jiri Slaby5f22a792008-05-16 11:49:19 +0200665 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
Jiri Kosina66d61be2009-11-24 18:22:20 +0100671 if (hdev->product == USB_DEVICE_ID_DINOVO_MINI &&
672 lg_dinovo_mapping(hi, usage, bit, max))
673 return 1;
674
Michal Malý8577dbf2012-03-31 11:17:25 +0200675 if ((drv_data->quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
Jiri Slaby5f22a792008-05-16 11:49:19 +0200676 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) {
Michal Malý8577dbf2012-03-31 11:17:25 +0200685 if ((drv_data->quirks & LG_IGNORE_DOUBLED_WHEEL) &&
Jiri Slaby5f22a792008-05-16 11:49:19 +0200686 (hid == 7 || hid == 8))
687 return -1;
688 } else {
Michal Malý8577dbf2012-03-31 11:17:25 +0200689 if ((drv_data->quirks & LG_EXPANDED_KEYMAP) &&
Jiri Slaby5f22a792008-05-16 11:49:19 +0200690 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{
Axel Lin25751552012-09-13 14:08:32 +0800705 struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
Jiri Slaby5f22a792008-05-16 11:49:19 +0200706
Michal Malý8577dbf2012-03-31 11:17:25 +0200707 if ((drv_data->quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
Jiri Slaby5f22a792008-05-16 11:49:19 +0200708 (field->flags & HID_MAIN_ITEM_RELATIVE))
709 field->flags &= ~HID_MAIN_ITEM_RELATIVE;
710
Michal Malý8577dbf2012-03-31 11:17:25 +0200711 if ((drv_data->quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY ||
Jiri Slaby5f22a792008-05-16 11:49:19 +0200712 usage->type == EV_REL || usage->type == EV_ABS))
713 clear_bit(usage->code, *bit);
714
Simon Wood94b3f712013-02-19 20:25:12 -0700715 /* 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) {
Simon Wood29fae1c2015-11-02 07:56:52 -0700720 case USB_DEVICE_ID_LOGITECH_G29_WHEEL:
Jarrad Whitakere41b3cd2019-01-24 22:40:50 +1100721 case USB_DEVICE_ID_LOGITECH_WINGMAN_FG:
Simon Wood560bea32016-09-18 10:55:41 -0600722 case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
Simon Wood94b3f712013-02-19 20:25:12 -0700723 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:
Elias Vanderstuyftbd043632013-10-07 19:48:12 +0300731 case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL:
Simon Wood94b3f712013-02-19 20:25:12 -0700732 field->application = HID_GD_MULTIAXIS;
733 break;
734 default:
735 break;
736 }
737 }
738
Jiri Slaby5f22a792008-05-16 11:49:19 +0200739 return 0;
740}
741
742static int lg_event(struct hid_device *hdev, struct hid_field *field,
743 struct hid_usage *usage, __s32 value)
744{
Axel Lin25751552012-09-13 14:08:32 +0800745 struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
Jiri Slaby5f22a792008-05-16 11:49:19 +0200746
Michal Malý8577dbf2012-03-31 11:17:25 +0200747 if ((drv_data->quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
Jiri Slaby5f22a792008-05-16 11:49:19 +0200748 input_event(field->hidinput->input, usage->type, usage->code,
749 -value);
750 return 1;
751 }
Michal Malý2b24a962012-09-23 22:41:08 +0200752 if (drv_data->quirks & LG_FF4) {
753 return lg4ff_adjust_input_event(hdev, field, usage, value, drv_data);
754 }
Jiri Slaby5f22a792008-05-16 11:49:19 +0200755
756 return 0;
757}
758
Simon Woodc832f862016-09-18 10:55:38 -0600759static 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
Jiri Slaby5f22a792008-05-16 11:49:19 +0200770static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
771{
Simon Wood29fae1c2015-11-02 07:56:52 -0700772 struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
773 __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
Jiri Slaby606bd0a2008-07-04 23:06:45 +0200774 unsigned int connect_mask = HID_CONNECT_DEFAULT;
Michal Malý8577dbf2012-03-31 11:17:25 +0200775 struct lg_drv_data *drv_data;
Jiri Slaby5f22a792008-05-16 11:49:19 +0200776 int ret;
777
Benjamin Tissoires20786652015-12-01 10:26:24 +0100778 /* G29 only work with the 1st interface */
779 if ((hdev->product == USB_DEVICE_ID_LOGITECH_G29_WHEEL) &&
780 (iface_num != 0)) {
Simon Wood29fae1c2015-11-02 07:56:52 -0700781 dbg_hid("%s: ignoring ifnum %d\n", __func__, iface_num);
782 return -ENODEV;
783 }
784
Michal Malý8577dbf2012-03-31 11:17:25 +0200785 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;
Michal Malýa80fe5d2012-09-24 01:13:17 +0200791
Michal Malý8577dbf2012-03-31 11:17:25 +0200792 hid_set_drvdata(hdev, (void *)drv_data);
Jiri Slaby5f22a792008-05-16 11:49:19 +0200793
Michal Malý8577dbf2012-03-31 11:17:25 +0200794 if (drv_data->quirks & LG_NOGET)
Jiri Slaby5f22a792008-05-16 11:49:19 +0200795 hdev->quirks |= HID_QUIRK_NOGET;
796
797 ret = hid_parse(hdev);
798 if (ret) {
Joe Perches4291ee32010-12-09 19:29:03 -0800799 hid_err(hdev, "parse failed\n");
Jiri Slaby5f22a792008-05-16 11:49:19 +0200800 goto err_free;
801 }
802
Michal Malý8577dbf2012-03-31 11:17:25 +0200803 if (drv_data->quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4))
Jiri Slaby606bd0a2008-07-04 23:06:45 +0200804 connect_mask &= ~HID_CONNECT_FF;
805
806 ret = hid_hw_start(hdev, connect_mask);
Jiri Slaby5f22a792008-05-16 11:49:19 +0200807 if (ret) {
Joe Perches4291ee32010-12-09 19:29:03 -0800808 hid_err(hdev, "hw start failed\n");
Jiri Slaby5f22a792008-05-16 11:49:19 +0200809 goto err_free;
810 }
811
Michal Malý7362cd22011-08-04 16:16:09 +0200812 /* Setup wireless link with Logitech Wii wheel */
Michal Malýa80fe5d2012-09-24 01:13:17 +0200813 if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
Colin Ian King47af1cd2017-08-25 18:12:50 +0100814 static const unsigned char cbuf[] = {
815 0x00, 0xAF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
816 };
Benjamin Tissoires061232f2016-11-21 11:48:40 +0100817 u8 *buf = kmemdup(cbuf, sizeof(cbuf), GFP_KERNEL);
Simon Wood32c88cb2010-09-22 13:19:42 +0200818
Benjamin Tissoires061232f2016-11-21 11:48:40 +0100819 if (!buf) {
820 ret = -ENOMEM;
Alan Stern5f924272019-08-20 16:00:21 -0400821 goto err_stop;
Benjamin Tissoires061232f2016-11-21 11:48:40 +0100822 }
823
824 ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(cbuf),
Benjamin Tissoiresb0dd72a2014-02-10 12:58:54 -0500825 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
Simon Wood32c88cb2010-09-22 13:19:42 +0200826 if (ret >= 0) {
827 /* insert a little delay of 10 jiffies ~ 40ms */
828 wait_queue_head_t wait;
829 init_waitqueue_head (&wait);
Nicholas Mc Guiree0393062015-05-02 17:55:15 +0200830 wait_event_interruptible_timeout(wait, 0,
831 msecs_to_jiffies(40));
Simon Wood32c88cb2010-09-22 13:19:42 +0200832
833 /* Select random Address */
834 buf[1] = 0xB2;
835 get_random_bytes(&buf[2], 2);
836
Benjamin Tissoires061232f2016-11-21 11:48:40 +0100837 ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(cbuf),
Benjamin Tissoiresb0dd72a2014-02-10 12:58:54 -0500838 HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
Simon Wood32c88cb2010-09-22 13:19:42 +0200839 }
Benjamin Tissoires061232f2016-11-21 11:48:40 +0100840 kfree(buf);
Simon Wood32c88cb2010-09-22 13:19:42 +0200841 }
842
Michal Malý8577dbf2012-03-31 11:17:25 +0200843 if (drv_data->quirks & LG_FF)
Michal Malý2dbf6352015-04-08 22:56:45 +0200844 ret = lgff_init(hdev);
Michal Malýf4e1eb92015-04-08 22:56:53 +0200845 else if (drv_data->quirks & LG_FF2)
Michal Malý2dbf6352015-04-08 22:56:45 +0200846 ret = lg2ff_init(hdev);
Michal Malýf4e1eb92015-04-08 22:56:53 +0200847 else if (drv_data->quirks & LG_FF3)
Michal Malý2dbf6352015-04-08 22:56:45 +0200848 ret = lg3ff_init(hdev);
Michal Malýf4e1eb92015-04-08 22:56:53 +0200849 else if (drv_data->quirks & LG_FF4)
Michal Malý2dbf6352015-04-08 22:56:45 +0200850 ret = lg4ff_init(hdev);
851
852 if (ret)
Alan Stern5f924272019-08-20 16:00:21 -0400853 goto err_stop;
Jiri Slaby606bd0a2008-07-04 23:06:45 +0200854
Jiri Slaby5f22a792008-05-16 11:49:19 +0200855 return 0;
Alan Stern5f924272019-08-20 16:00:21 -0400856
857err_stop:
858 hid_hw_stop(hdev);
Jiri Slaby5f22a792008-05-16 11:49:19 +0200859err_free:
Michal Malý8577dbf2012-03-31 11:17:25 +0200860 kfree(drv_data);
Jiri Slaby5f22a792008-05-16 11:49:19 +0200861 return ret;
862}
863
Michal Malý30bb75d2011-08-04 16:20:40 +0200864static void lg_remove(struct hid_device *hdev)
865{
Axel Lin25751552012-09-13 14:08:32 +0800866 struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
Michal Malý8577dbf2012-03-31 11:17:25 +0200867 if (drv_data->quirks & LG_FF4)
Michal Malý30bb75d2011-08-04 16:20:40 +0200868 lg4ff_deinit(hdev);
Alan Stern5f924272019-08-20 16:00:21 -0400869 hid_hw_stop(hdev);
Michal Malý8577dbf2012-03-31 11:17:25 +0200870 kfree(drv_data);
Michal Malý30bb75d2011-08-04 16:20:40 +0200871}
872
Jiri Slaby5f22a792008-05-16 11:49:19 +0200873static const struct hid_device_id lg_devices[] = {
Jiri Slaby5f22a792008-05-16 11:49:19 +0200874 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER),
875 .driver_data = LG_RDESC | LG_WIRELESS },
Jiri Slaby5f22a792008-05-16 11:49:19 +0200876
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
Jiri Slaby5f22a792008-05-16 11:49:19 +0200887 { 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
Jiri Slaby5f22a792008-05-16 11:49:19 +0200892 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
893 .driver_data = LG_NOGET },
Vitaly Katraew56d0c8b2013-11-30 13:51:28 +0400894 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DUAL_ACTION),
895 .driver_data = LG_NOGET },
Jiri Slaby5f22a792008-05-16 11:49:19 +0200896 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
Michal Malý7362cd22011-08-04 16:16:09 +0200897 .driver_data = LG_NOGET | LG_FF4 },
Jiri Slaby606bd0a2008-07-04 23:06:45 +0200898
Hendrik Iben2c6118e2010-10-04 15:39:49 +0200899 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD),
900 .driver_data = LG_FF2 },
Jiri Slaby606bd0a2008-07-04 23:06:45 +0200901 { 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 },
Simon Woodc873d9a2015-07-23 19:10:03 -0600905 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G29_WHEEL),
906 .driver_data = LG_FF4 },
Jiri Slaby606bd0a2008-07-04 23:06:45 +0200907 { 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),
Simon Wood270baef2013-02-19 20:25:10 -0700912 .driver_data = LG_NOGET | LG_FF4 },
Jiri Slaby606bd0a2008-07-04 23:06:45 +0200913 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
Michal Malý7362cd22011-08-04 16:16:09 +0200914 .driver_data = LG_FF4 },
Elias Vanderstuyftbd043632013-10-07 19:48:12 +0300915 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL),
916 .driver_data = LG_FF2 },
Christophe Borivant243b7062009-04-17 11:39:39 +0200917 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
Michal Malý7362cd22011-08-04 16:16:09 +0200918 .driver_data = LG_FF4 },
919 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL),
920 .driver_data = LG_FF4 },
Peter Gundermannfdc68072011-05-03 10:15:03 +0200921 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL),
Michal Malý7362cd22011-08-04 16:16:09 +0200922 .driver_data = LG_FF4 },
Jiri Kosina5623a242011-03-17 00:43:23 +0100923 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL),
Michal Malý7362cd22011-08-04 16:16:09 +0200924 .driver_data = LG_NOGET | LG_FF4 },
Simon Wood32c88cb2010-09-22 13:19:42 +0200925 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
926 .driver_data = LG_FF4 },
Jarrad Whitakere41b3cd2019-01-24 22:40:50 +1100927 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FG),
928 .driver_data = LG_NOGET },
Michal Malýa80fe5d2012-09-24 01:13:17 +0200929 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
Simon Wood560bea32016-09-18 10:55:41 -0600930 .driver_data = LG_NOGET | LG_FF4 },
Jiri Slaby606bd0a2008-07-04 23:06:45 +0200931 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
Ardinartsev Nikita877a0212017-01-26 16:54:42 +0300932 .driver_data = LG_NOGET | LG_FF2 },
Gary Stein74f292c2010-01-13 00:25:58 +0100933 { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940),
934 .driver_data = LG_FF3 },
Jiri Kosina24985cf2009-11-13 10:45:53 +0100935 { 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 },
Jiri Slaby5f22a792008-05-16 11:49:19 +0200939 { }
940};
Jiri Kosina24985cf2009-11-13 10:45:53 +0100941
Jiri Slaby5f22a792008-05-16 11:49:19 +0200942MODULE_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,
Simon Woodc832f862016-09-18 10:55:38 -0600951 .raw_event = lg_raw_event,
Jiri Slaby5f22a792008-05-16 11:49:19 +0200952 .probe = lg_probe,
Michal Malý30bb75d2011-08-04 16:20:40 +0200953 .remove = lg_remove,
Jiri Slaby5f22a792008-05-16 11:49:19 +0200954};
H Hartley Sweetenf4254582012-12-17 15:28:26 -0700955module_hid_driver(lg_driver);
Jiri Slaby5f22a792008-05-16 11:49:19 +0200956
Michal Malýa54dc7792015-02-18 17:59:22 +0100957#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
Jiri Slaby5f22a792008-05-16 11:49:19 +0200963MODULE_LICENSE("GPL");