18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  HID driver for some logitech "special" devices
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (c) 1999 Andreas Gal
68c2ecf20Sopenharmony_ci *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
78c2ecf20Sopenharmony_ci *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
88c2ecf20Sopenharmony_ci *  Copyright (c) 2006-2007 Jiri Kosina
98c2ecf20Sopenharmony_ci *  Copyright (c) 2008 Jiri Slaby
108c2ecf20Sopenharmony_ci *  Copyright (c) 2010 Hendrik Iben
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/*
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/device.h>
178c2ecf20Sopenharmony_ci#include <linux/hid.h>
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <linux/random.h>
208c2ecf20Sopenharmony_ci#include <linux/sched.h>
218c2ecf20Sopenharmony_ci#include <linux/usb.h>
228c2ecf20Sopenharmony_ci#include <linux/wait.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "usbhid/usbhid.h"
258c2ecf20Sopenharmony_ci#include "hid-ids.h"
268c2ecf20Sopenharmony_ci#include "hid-lg.h"
278c2ecf20Sopenharmony_ci#include "hid-lg4ff.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define LG_RDESC		0x001
308c2ecf20Sopenharmony_ci#define LG_BAD_RELATIVE_KEYS	0x002
318c2ecf20Sopenharmony_ci#define LG_DUPLICATE_USAGES	0x004
328c2ecf20Sopenharmony_ci#define LG_EXPANDED_KEYMAP	0x010
338c2ecf20Sopenharmony_ci#define LG_IGNORE_DOUBLED_WHEEL	0x020
348c2ecf20Sopenharmony_ci#define LG_WIRELESS		0x040
358c2ecf20Sopenharmony_ci#define LG_INVERT_HWHEEL	0x080
368c2ecf20Sopenharmony_ci#define LG_NOGET		0x100
378c2ecf20Sopenharmony_ci#define LG_FF			0x200
388c2ecf20Sopenharmony_ci#define LG_FF2			0x400
398c2ecf20Sopenharmony_ci#define LG_RDESC_REL_ABS	0x800
408c2ecf20Sopenharmony_ci#define LG_FF3			0x1000
418c2ecf20Sopenharmony_ci#define LG_FF4			0x2000
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/* Size of the original descriptors of the Driving Force (and Pro) wheels */
448c2ecf20Sopenharmony_ci#define DF_RDESC_ORIG_SIZE	130
458c2ecf20Sopenharmony_ci#define DFP_RDESC_ORIG_SIZE	97
468c2ecf20Sopenharmony_ci#define FV_RDESC_ORIG_SIZE	130
478c2ecf20Sopenharmony_ci#define MOMO_RDESC_ORIG_SIZE	87
488c2ecf20Sopenharmony_ci#define MOMO2_RDESC_ORIG_SIZE	87
498c2ecf20Sopenharmony_ci#define FFG_RDESC_ORIG_SIZE	85
508c2ecf20Sopenharmony_ci#define FG_RDESC_ORIG_SIZE	82
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/* Fixed report descriptors for Logitech Driving Force (and Pro)
538c2ecf20Sopenharmony_ci * wheel controllers
548c2ecf20Sopenharmony_ci *
558c2ecf20Sopenharmony_ci * The original descriptors hide the separate throttle and brake axes in
568c2ecf20Sopenharmony_ci * a custom vendor usage page, providing only a combined value as
578c2ecf20Sopenharmony_ci * GenericDesktop.Y.
588c2ecf20Sopenharmony_ci * These descriptors remove the combined Y axis and instead report
598c2ecf20Sopenharmony_ci * separate throttle (Y) and brake (RZ).
608c2ecf20Sopenharmony_ci */
618c2ecf20Sopenharmony_cistatic __u8 df_rdesc_fixed[] = {
628c2ecf20Sopenharmony_ci0x05, 0x01,         /*  Usage Page (Desktop),                   */
638c2ecf20Sopenharmony_ci0x09, 0x04,         /*  Usage (Joystick),                       */
648c2ecf20Sopenharmony_ci0xA1, 0x01,         /*  Collection (Application),               */
658c2ecf20Sopenharmony_ci0xA1, 0x02,         /*      Collection (Logical),               */
668c2ecf20Sopenharmony_ci0x95, 0x01,         /*          Report Count (1),               */
678c2ecf20Sopenharmony_ci0x75, 0x0A,         /*          Report Size (10),               */
688c2ecf20Sopenharmony_ci0x14,               /*          Logical Minimum (0),            */
698c2ecf20Sopenharmony_ci0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),         */
708c2ecf20Sopenharmony_ci0x34,               /*          Physical Minimum (0),           */
718c2ecf20Sopenharmony_ci0x46, 0xFF, 0x03,   /*          Physical Maximum (1023),        */
728c2ecf20Sopenharmony_ci0x09, 0x30,         /*          Usage (X),                      */
738c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),               */
748c2ecf20Sopenharmony_ci0x95, 0x0C,         /*          Report Count (12),              */
758c2ecf20Sopenharmony_ci0x75, 0x01,         /*          Report Size (1),                */
768c2ecf20Sopenharmony_ci0x25, 0x01,         /*          Logical Maximum (1),            */
778c2ecf20Sopenharmony_ci0x45, 0x01,         /*          Physical Maximum (1),           */
788c2ecf20Sopenharmony_ci0x05, 0x09,         /*          Usage (Buttons),                */
798c2ecf20Sopenharmony_ci0x19, 0x01,         /*          Usage Minimum (1),              */
808c2ecf20Sopenharmony_ci0x29, 0x0c,         /*          Usage Maximum (12),             */
818c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),               */
828c2ecf20Sopenharmony_ci0x95, 0x02,         /*          Report Count (2),               */
838c2ecf20Sopenharmony_ci0x06, 0x00, 0xFF,   /*          Usage Page (Vendor: 65280),     */
848c2ecf20Sopenharmony_ci0x09, 0x01,         /*          Usage (?: 1),                   */
858c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),               */
868c2ecf20Sopenharmony_ci0x05, 0x01,         /*          Usage Page (Desktop),           */
878c2ecf20Sopenharmony_ci0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
888c2ecf20Sopenharmony_ci0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
898c2ecf20Sopenharmony_ci0x95, 0x01,         /*          Report Count (1),               */
908c2ecf20Sopenharmony_ci0x75, 0x08,         /*          Report Size (8),                */
918c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),               */
928c2ecf20Sopenharmony_ci0x25, 0x07,         /*          Logical Maximum (7),            */
938c2ecf20Sopenharmony_ci0x46, 0x3B, 0x01,   /*          Physical Maximum (315),         */
948c2ecf20Sopenharmony_ci0x75, 0x04,         /*          Report Size (4),                */
958c2ecf20Sopenharmony_ci0x65, 0x14,         /*          Unit (Degrees),                 */
968c2ecf20Sopenharmony_ci0x09, 0x39,         /*          Usage (Hat Switch),             */
978c2ecf20Sopenharmony_ci0x81, 0x42,         /*          Input (Variable, Null State),   */
988c2ecf20Sopenharmony_ci0x75, 0x01,         /*          Report Size (1),                */
998c2ecf20Sopenharmony_ci0x95, 0x04,         /*          Report Count (4),               */
1008c2ecf20Sopenharmony_ci0x65, 0x00,         /*          Unit (none),                    */
1018c2ecf20Sopenharmony_ci0x06, 0x00, 0xFF,   /*          Usage Page (Vendor: 65280),     */
1028c2ecf20Sopenharmony_ci0x09, 0x01,         /*          Usage (?: 1),                   */
1038c2ecf20Sopenharmony_ci0x25, 0x01,         /*          Logical Maximum (1),            */
1048c2ecf20Sopenharmony_ci0x45, 0x01,         /*          Physical Maximum (1),           */
1058c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),               */
1068c2ecf20Sopenharmony_ci0x05, 0x01,         /*          Usage Page (Desktop),           */
1078c2ecf20Sopenharmony_ci0x95, 0x01,         /*          Report Count (1),               */
1088c2ecf20Sopenharmony_ci0x75, 0x08,         /*          Report Size (8),                */
1098c2ecf20Sopenharmony_ci0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
1108c2ecf20Sopenharmony_ci0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
1118c2ecf20Sopenharmony_ci0x09, 0x31,         /*          Usage (Y),                      */
1128c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),               */
1138c2ecf20Sopenharmony_ci0x09, 0x35,         /*          Usage (Rz),                     */
1148c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),               */
1158c2ecf20Sopenharmony_ci0xC0,               /*      End Collection,                     */
1168c2ecf20Sopenharmony_ci0xA1, 0x02,         /*      Collection (Logical),               */
1178c2ecf20Sopenharmony_ci0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
1188c2ecf20Sopenharmony_ci0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
1198c2ecf20Sopenharmony_ci0x95, 0x07,         /*          Report Count (7),               */
1208c2ecf20Sopenharmony_ci0x75, 0x08,         /*          Report Size (8),                */
1218c2ecf20Sopenharmony_ci0x09, 0x03,         /*          Usage (?: 3),                   */
1228c2ecf20Sopenharmony_ci0x91, 0x02,         /*          Output (Variable),              */
1238c2ecf20Sopenharmony_ci0xC0,               /*      End Collection,                     */
1248c2ecf20Sopenharmony_ci0xC0                /*  End Collection                          */
1258c2ecf20Sopenharmony_ci};
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic __u8 dfp_rdesc_fixed[] = {
1288c2ecf20Sopenharmony_ci0x05, 0x01,         /*  Usage Page (Desktop),                   */
1298c2ecf20Sopenharmony_ci0x09, 0x04,         /*  Usage (Joystick),                       */
1308c2ecf20Sopenharmony_ci0xA1, 0x01,         /*  Collection (Application),               */
1318c2ecf20Sopenharmony_ci0xA1, 0x02,         /*      Collection (Logical),               */
1328c2ecf20Sopenharmony_ci0x95, 0x01,         /*          Report Count (1),               */
1338c2ecf20Sopenharmony_ci0x75, 0x0E,         /*          Report Size (14),               */
1348c2ecf20Sopenharmony_ci0x14,               /*          Logical Minimum (0),            */
1358c2ecf20Sopenharmony_ci0x26, 0xFF, 0x3F,   /*          Logical Maximum (16383),        */
1368c2ecf20Sopenharmony_ci0x34,               /*          Physical Minimum (0),           */
1378c2ecf20Sopenharmony_ci0x46, 0xFF, 0x3F,   /*          Physical Maximum (16383),       */
1388c2ecf20Sopenharmony_ci0x09, 0x30,         /*          Usage (X),                      */
1398c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),               */
1408c2ecf20Sopenharmony_ci0x95, 0x0E,         /*          Report Count (14),              */
1418c2ecf20Sopenharmony_ci0x75, 0x01,         /*          Report Size (1),                */
1428c2ecf20Sopenharmony_ci0x25, 0x01,         /*          Logical Maximum (1),            */
1438c2ecf20Sopenharmony_ci0x45, 0x01,         /*          Physical Maximum (1),           */
1448c2ecf20Sopenharmony_ci0x05, 0x09,         /*          Usage Page (Button),            */
1458c2ecf20Sopenharmony_ci0x19, 0x01,         /*          Usage Minimum (01h),            */
1468c2ecf20Sopenharmony_ci0x29, 0x0E,         /*          Usage Maximum (0Eh),            */
1478c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),               */
1488c2ecf20Sopenharmony_ci0x05, 0x01,         /*          Usage Page (Desktop),           */
1498c2ecf20Sopenharmony_ci0x95, 0x01,         /*          Report Count (1),               */
1508c2ecf20Sopenharmony_ci0x75, 0x04,         /*          Report Size (4),                */
1518c2ecf20Sopenharmony_ci0x25, 0x07,         /*          Logical Maximum (7),            */
1528c2ecf20Sopenharmony_ci0x46, 0x3B, 0x01,   /*          Physical Maximum (315),         */
1538c2ecf20Sopenharmony_ci0x65, 0x14,         /*          Unit (Degrees),                 */
1548c2ecf20Sopenharmony_ci0x09, 0x39,         /*          Usage (Hat Switch),             */
1558c2ecf20Sopenharmony_ci0x81, 0x42,         /*          Input (Variable, Nullstate),    */
1568c2ecf20Sopenharmony_ci0x65, 0x00,         /*          Unit,                           */
1578c2ecf20Sopenharmony_ci0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
1588c2ecf20Sopenharmony_ci0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
1598c2ecf20Sopenharmony_ci0x75, 0x08,         /*          Report Size (8),                */
1608c2ecf20Sopenharmony_ci0x81, 0x01,         /*          Input (Constant),               */
1618c2ecf20Sopenharmony_ci0x09, 0x31,         /*          Usage (Y),                      */
1628c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),               */
1638c2ecf20Sopenharmony_ci0x09, 0x35,         /*          Usage (Rz),                     */
1648c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),               */
1658c2ecf20Sopenharmony_ci0x81, 0x01,         /*          Input (Constant),               */
1668c2ecf20Sopenharmony_ci0xC0,               /*      End Collection,                     */
1678c2ecf20Sopenharmony_ci0xA1, 0x02,         /*      Collection (Logical),               */
1688c2ecf20Sopenharmony_ci0x09, 0x02,         /*          Usage (02h),                    */
1698c2ecf20Sopenharmony_ci0x95, 0x07,         /*          Report Count (7),               */
1708c2ecf20Sopenharmony_ci0x91, 0x02,         /*          Output (Variable),              */
1718c2ecf20Sopenharmony_ci0xC0,               /*      End Collection,                     */
1728c2ecf20Sopenharmony_ci0xC0                /*  End Collection                          */
1738c2ecf20Sopenharmony_ci};
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic __u8 fv_rdesc_fixed[] = {
1768c2ecf20Sopenharmony_ci0x05, 0x01,         /*  Usage Page (Desktop),                   */
1778c2ecf20Sopenharmony_ci0x09, 0x04,         /*  Usage (Joystick),                       */
1788c2ecf20Sopenharmony_ci0xA1, 0x01,         /*  Collection (Application),               */
1798c2ecf20Sopenharmony_ci0xA1, 0x02,         /*      Collection (Logical),               */
1808c2ecf20Sopenharmony_ci0x95, 0x01,         /*          Report Count (1),               */
1818c2ecf20Sopenharmony_ci0x75, 0x0A,         /*          Report Size (10),               */
1828c2ecf20Sopenharmony_ci0x15, 0x00,         /*          Logical Minimum (0),            */
1838c2ecf20Sopenharmony_ci0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),         */
1848c2ecf20Sopenharmony_ci0x35, 0x00,         /*          Physical Minimum (0),           */
1858c2ecf20Sopenharmony_ci0x46, 0xFF, 0x03,   /*          Physical Maximum (1023),        */
1868c2ecf20Sopenharmony_ci0x09, 0x30,         /*          Usage (X),                      */
1878c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),               */
1888c2ecf20Sopenharmony_ci0x95, 0x0C,         /*          Report Count (12),              */
1898c2ecf20Sopenharmony_ci0x75, 0x01,         /*          Report Size (1),                */
1908c2ecf20Sopenharmony_ci0x25, 0x01,         /*          Logical Maximum (1),            */
1918c2ecf20Sopenharmony_ci0x45, 0x01,         /*          Physical Maximum (1),           */
1928c2ecf20Sopenharmony_ci0x05, 0x09,         /*          Usage Page (Button),            */
1938c2ecf20Sopenharmony_ci0x19, 0x01,         /*          Usage Minimum (01h),            */
1948c2ecf20Sopenharmony_ci0x29, 0x0C,         /*          Usage Maximum (0Ch),            */
1958c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),               */
1968c2ecf20Sopenharmony_ci0x95, 0x02,         /*          Report Count (2),               */
1978c2ecf20Sopenharmony_ci0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),             */
1988c2ecf20Sopenharmony_ci0x09, 0x01,         /*          Usage (01h),                    */
1998c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),               */
2008c2ecf20Sopenharmony_ci0x09, 0x02,         /*          Usage (02h),                    */
2018c2ecf20Sopenharmony_ci0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
2028c2ecf20Sopenharmony_ci0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
2038c2ecf20Sopenharmony_ci0x95, 0x01,         /*          Report Count (1),               */
2048c2ecf20Sopenharmony_ci0x75, 0x08,         /*          Report Size (8),                */
2058c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),               */
2068c2ecf20Sopenharmony_ci0x05, 0x01,         /*          Usage Page (Desktop),           */
2078c2ecf20Sopenharmony_ci0x25, 0x07,         /*          Logical Maximum (7),            */
2088c2ecf20Sopenharmony_ci0x46, 0x3B, 0x01,   /*          Physical Maximum (315),         */
2098c2ecf20Sopenharmony_ci0x75, 0x04,         /*          Report Size (4),                */
2108c2ecf20Sopenharmony_ci0x65, 0x14,         /*          Unit (Degrees),                 */
2118c2ecf20Sopenharmony_ci0x09, 0x39,         /*          Usage (Hat Switch),             */
2128c2ecf20Sopenharmony_ci0x81, 0x42,         /*          Input (Variable, Null State),   */
2138c2ecf20Sopenharmony_ci0x75, 0x01,         /*          Report Size (1),                */
2148c2ecf20Sopenharmony_ci0x95, 0x04,         /*          Report Count (4),               */
2158c2ecf20Sopenharmony_ci0x65, 0x00,         /*          Unit,                           */
2168c2ecf20Sopenharmony_ci0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),             */
2178c2ecf20Sopenharmony_ci0x09, 0x01,         /*          Usage (01h),                    */
2188c2ecf20Sopenharmony_ci0x25, 0x01,         /*          Logical Maximum (1),            */
2198c2ecf20Sopenharmony_ci0x45, 0x01,         /*          Physical Maximum (1),           */
2208c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),               */
2218c2ecf20Sopenharmony_ci0x05, 0x01,         /*          Usage Page (Desktop),           */
2228c2ecf20Sopenharmony_ci0x95, 0x01,         /*          Report Count (1),               */
2238c2ecf20Sopenharmony_ci0x75, 0x08,         /*          Report Size (8),                */
2248c2ecf20Sopenharmony_ci0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
2258c2ecf20Sopenharmony_ci0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
2268c2ecf20Sopenharmony_ci0x09, 0x31,         /*          Usage (Y),                      */
2278c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),               */
2288c2ecf20Sopenharmony_ci0x09, 0x32,         /*          Usage (Z),                      */
2298c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),               */
2308c2ecf20Sopenharmony_ci0xC0,               /*      End Collection,                     */
2318c2ecf20Sopenharmony_ci0xA1, 0x02,         /*      Collection (Logical),               */
2328c2ecf20Sopenharmony_ci0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
2338c2ecf20Sopenharmony_ci0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
2348c2ecf20Sopenharmony_ci0x95, 0x07,         /*          Report Count (7),               */
2358c2ecf20Sopenharmony_ci0x75, 0x08,         /*          Report Size (8),                */
2368c2ecf20Sopenharmony_ci0x09, 0x03,         /*          Usage (03h),                    */
2378c2ecf20Sopenharmony_ci0x91, 0x02,         /*          Output (Variable),              */
2388c2ecf20Sopenharmony_ci0xC0,               /*      End Collection,                     */
2398c2ecf20Sopenharmony_ci0xC0                /*  End Collection                          */
2408c2ecf20Sopenharmony_ci};
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic __u8 momo_rdesc_fixed[] = {
2438c2ecf20Sopenharmony_ci0x05, 0x01,         /*  Usage Page (Desktop),               */
2448c2ecf20Sopenharmony_ci0x09, 0x04,         /*  Usage (Joystick),                   */
2458c2ecf20Sopenharmony_ci0xA1, 0x01,         /*  Collection (Application),           */
2468c2ecf20Sopenharmony_ci0xA1, 0x02,         /*      Collection (Logical),           */
2478c2ecf20Sopenharmony_ci0x95, 0x01,         /*          Report Count (1),           */
2488c2ecf20Sopenharmony_ci0x75, 0x0A,         /*          Report Size (10),           */
2498c2ecf20Sopenharmony_ci0x15, 0x00,         /*          Logical Minimum (0),        */
2508c2ecf20Sopenharmony_ci0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
2518c2ecf20Sopenharmony_ci0x35, 0x00,         /*          Physical Minimum (0),       */
2528c2ecf20Sopenharmony_ci0x46, 0xFF, 0x03,   /*          Physical Maximum (1023),    */
2538c2ecf20Sopenharmony_ci0x09, 0x30,         /*          Usage (X),                  */
2548c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),           */
2558c2ecf20Sopenharmony_ci0x95, 0x08,         /*          Report Count (8),           */
2568c2ecf20Sopenharmony_ci0x75, 0x01,         /*          Report Size (1),            */
2578c2ecf20Sopenharmony_ci0x25, 0x01,         /*          Logical Maximum (1),        */
2588c2ecf20Sopenharmony_ci0x45, 0x01,         /*          Physical Maximum (1),       */
2598c2ecf20Sopenharmony_ci0x05, 0x09,         /*          Usage Page (Button),        */
2608c2ecf20Sopenharmony_ci0x19, 0x01,         /*          Usage Minimum (01h),        */
2618c2ecf20Sopenharmony_ci0x29, 0x08,         /*          Usage Maximum (08h),        */
2628c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),           */
2638c2ecf20Sopenharmony_ci0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
2648c2ecf20Sopenharmony_ci0x75, 0x0E,         /*          Report Size (14),           */
2658c2ecf20Sopenharmony_ci0x95, 0x01,         /*          Report Count (1),           */
2668c2ecf20Sopenharmony_ci0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
2678c2ecf20Sopenharmony_ci0x46, 0xFF, 0x00,   /*          Physical Maximum (255),     */
2688c2ecf20Sopenharmony_ci0x09, 0x00,         /*          Usage (00h),                */
2698c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),           */
2708c2ecf20Sopenharmony_ci0x05, 0x01,         /*          Usage Page (Desktop),       */
2718c2ecf20Sopenharmony_ci0x75, 0x08,         /*          Report Size (8),            */
2728c2ecf20Sopenharmony_ci0x09, 0x31,         /*          Usage (Y),                  */
2738c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),           */
2748c2ecf20Sopenharmony_ci0x09, 0x32,         /*          Usage (Z),                  */
2758c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),           */
2768c2ecf20Sopenharmony_ci0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
2778c2ecf20Sopenharmony_ci0x09, 0x01,         /*          Usage (01h),                */
2788c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),           */
2798c2ecf20Sopenharmony_ci0xC0,               /*      End Collection,                 */
2808c2ecf20Sopenharmony_ci0xA1, 0x02,         /*      Collection (Logical),           */
2818c2ecf20Sopenharmony_ci0x09, 0x02,         /*          Usage (02h),                */
2828c2ecf20Sopenharmony_ci0x95, 0x07,         /*          Report Count (7),           */
2838c2ecf20Sopenharmony_ci0x91, 0x02,         /*          Output (Variable),          */
2848c2ecf20Sopenharmony_ci0xC0,               /*      End Collection,                 */
2858c2ecf20Sopenharmony_ci0xC0                /*  End Collection                      */
2868c2ecf20Sopenharmony_ci};
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic __u8 momo2_rdesc_fixed[] = {
2898c2ecf20Sopenharmony_ci0x05, 0x01,         /*  Usage Page (Desktop),               */
2908c2ecf20Sopenharmony_ci0x09, 0x04,         /*  Usage (Joystick),                   */
2918c2ecf20Sopenharmony_ci0xA1, 0x01,         /*  Collection (Application),           */
2928c2ecf20Sopenharmony_ci0xA1, 0x02,         /*      Collection (Logical),           */
2938c2ecf20Sopenharmony_ci0x95, 0x01,         /*          Report Count (1),           */
2948c2ecf20Sopenharmony_ci0x75, 0x0A,         /*          Report Size (10),           */
2958c2ecf20Sopenharmony_ci0x15, 0x00,         /*          Logical Minimum (0),        */
2968c2ecf20Sopenharmony_ci0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
2978c2ecf20Sopenharmony_ci0x35, 0x00,         /*          Physical Minimum (0),       */
2988c2ecf20Sopenharmony_ci0x46, 0xFF, 0x03,   /*          Physical Maximum (1023),    */
2998c2ecf20Sopenharmony_ci0x09, 0x30,         /*          Usage (X),                  */
3008c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),           */
3018c2ecf20Sopenharmony_ci0x95, 0x0A,         /*          Report Count (10),          */
3028c2ecf20Sopenharmony_ci0x75, 0x01,         /*          Report Size (1),            */
3038c2ecf20Sopenharmony_ci0x25, 0x01,         /*          Logical Maximum (1),        */
3048c2ecf20Sopenharmony_ci0x45, 0x01,         /*          Physical Maximum (1),       */
3058c2ecf20Sopenharmony_ci0x05, 0x09,         /*          Usage Page (Button),        */
3068c2ecf20Sopenharmony_ci0x19, 0x01,         /*          Usage Minimum (01h),        */
3078c2ecf20Sopenharmony_ci0x29, 0x0A,         /*          Usage Maximum (0Ah),        */
3088c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),           */
3098c2ecf20Sopenharmony_ci0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
3108c2ecf20Sopenharmony_ci0x09, 0x00,         /*          Usage (00h),                */
3118c2ecf20Sopenharmony_ci0x95, 0x04,         /*          Report Count (4),           */
3128c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),           */
3138c2ecf20Sopenharmony_ci0x95, 0x01,         /*          Report Count (1),           */
3148c2ecf20Sopenharmony_ci0x75, 0x08,         /*          Report Size (8),            */
3158c2ecf20Sopenharmony_ci0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
3168c2ecf20Sopenharmony_ci0x46, 0xFF, 0x00,   /*          Physical Maximum (255),     */
3178c2ecf20Sopenharmony_ci0x09, 0x01,         /*          Usage (01h),                */
3188c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),           */
3198c2ecf20Sopenharmony_ci0x05, 0x01,         /*          Usage Page (Desktop),       */
3208c2ecf20Sopenharmony_ci0x09, 0x31,         /*          Usage (Y),                  */
3218c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),           */
3228c2ecf20Sopenharmony_ci0x09, 0x32,         /*          Usage (Z),                  */
3238c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),           */
3248c2ecf20Sopenharmony_ci0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
3258c2ecf20Sopenharmony_ci0x09, 0x00,         /*          Usage (00h),                */
3268c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),           */
3278c2ecf20Sopenharmony_ci0xC0,               /*      End Collection,                 */
3288c2ecf20Sopenharmony_ci0xA1, 0x02,         /*      Collection (Logical),           */
3298c2ecf20Sopenharmony_ci0x09, 0x02,         /*          Usage (02h),                */
3308c2ecf20Sopenharmony_ci0x95, 0x07,         /*          Report Count (7),           */
3318c2ecf20Sopenharmony_ci0x91, 0x02,         /*          Output (Variable),          */
3328c2ecf20Sopenharmony_ci0xC0,               /*      End Collection,                 */
3338c2ecf20Sopenharmony_ci0xC0                /*  End Collection                      */
3348c2ecf20Sopenharmony_ci};
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic __u8 ffg_rdesc_fixed[] = {
3378c2ecf20Sopenharmony_ci0x05, 0x01,         /*  Usage Page (Desktop),               */
3388c2ecf20Sopenharmony_ci0x09, 0x04,         /*  Usage (Joystik),                    */
3398c2ecf20Sopenharmony_ci0xA1, 0x01,         /*  Collection (Application),           */
3408c2ecf20Sopenharmony_ci0xA1, 0x02,         /*      Collection (Logical),           */
3418c2ecf20Sopenharmony_ci0x95, 0x01,         /*          Report Count (1),           */
3428c2ecf20Sopenharmony_ci0x75, 0x0A,         /*          Report Size (10),           */
3438c2ecf20Sopenharmony_ci0x15, 0x00,         /*          Logical Minimum (0),        */
3448c2ecf20Sopenharmony_ci0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
3458c2ecf20Sopenharmony_ci0x35, 0x00,         /*          Physical Minimum (0),       */
3468c2ecf20Sopenharmony_ci0x46, 0xFF, 0x03,   /*          Physical Maximum (1023),    */
3478c2ecf20Sopenharmony_ci0x09, 0x30,         /*          Usage (X),                  */
3488c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),           */
3498c2ecf20Sopenharmony_ci0x95, 0x06,         /*          Report Count (6),           */
3508c2ecf20Sopenharmony_ci0x75, 0x01,         /*          Report Size (1),            */
3518c2ecf20Sopenharmony_ci0x25, 0x01,         /*          Logical Maximum (1),        */
3528c2ecf20Sopenharmony_ci0x45, 0x01,         /*          Physical Maximum (1),       */
3538c2ecf20Sopenharmony_ci0x05, 0x09,         /*          Usage Page (Button),        */
3548c2ecf20Sopenharmony_ci0x19, 0x01,         /*          Usage Minimum (01h),        */
3558c2ecf20Sopenharmony_ci0x29, 0x06,         /*          Usage Maximum (06h),        */
3568c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),           */
3578c2ecf20Sopenharmony_ci0x95, 0x01,         /*          Report Count (1),           */
3588c2ecf20Sopenharmony_ci0x75, 0x08,         /*          Report Size (8),            */
3598c2ecf20Sopenharmony_ci0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
3608c2ecf20Sopenharmony_ci0x46, 0xFF, 0x00,   /*          Physical Maximum (255),     */
3618c2ecf20Sopenharmony_ci0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
3628c2ecf20Sopenharmony_ci0x09, 0x01,         /*          Usage (01h),                */
3638c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),           */
3648c2ecf20Sopenharmony_ci0x05, 0x01,         /*          Usage Page (Desktop),       */
3658c2ecf20Sopenharmony_ci0x81, 0x01,         /*          Input (Constant),           */
3668c2ecf20Sopenharmony_ci0x09, 0x31,         /*          Usage (Y),                  */
3678c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),           */
3688c2ecf20Sopenharmony_ci0x09, 0x32,         /*          Usage (Z),                  */
3698c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),           */
3708c2ecf20Sopenharmony_ci0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
3718c2ecf20Sopenharmony_ci0x09, 0x01,         /*          Usage (01h),                */
3728c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),           */
3738c2ecf20Sopenharmony_ci0xC0,               /*      End Collection,                 */
3748c2ecf20Sopenharmony_ci0xA1, 0x02,         /*      Collection (Logical),           */
3758c2ecf20Sopenharmony_ci0x09, 0x02,         /*          Usage (02h),                */
3768c2ecf20Sopenharmony_ci0x95, 0x07,         /*          Report Count (7),           */
3778c2ecf20Sopenharmony_ci0x91, 0x02,         /*          Output (Variable),          */
3788c2ecf20Sopenharmony_ci0xC0,               /*      End Collection,                 */
3798c2ecf20Sopenharmony_ci0xC0                /*  End Collection                      */
3808c2ecf20Sopenharmony_ci};
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_cistatic __u8 fg_rdesc_fixed[] = {
3838c2ecf20Sopenharmony_ci0x05, 0x01,         /*  Usage Page (Desktop),               */
3848c2ecf20Sopenharmony_ci0x09, 0x04,         /*  Usage (Joystik),                    */
3858c2ecf20Sopenharmony_ci0xA1, 0x01,         /*  Collection (Application),           */
3868c2ecf20Sopenharmony_ci0xA1, 0x02,         /*      Collection (Logical),           */
3878c2ecf20Sopenharmony_ci0x15, 0x00,         /*          Logical Minimum (0),        */
3888c2ecf20Sopenharmony_ci0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
3898c2ecf20Sopenharmony_ci0x35, 0x00,         /*          Physical Minimum (0),       */
3908c2ecf20Sopenharmony_ci0x46, 0xFF, 0x00,   /*          Physical Maximum (255),     */
3918c2ecf20Sopenharmony_ci0x75, 0x08,         /*          Report Size (8),            */
3928c2ecf20Sopenharmony_ci0x95, 0x01,         /*          Report Count (1),           */
3938c2ecf20Sopenharmony_ci0x09, 0x30,         /*          Usage (X),                  */
3948c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),           */
3958c2ecf20Sopenharmony_ci0xA4,               /*  Push,                               */
3968c2ecf20Sopenharmony_ci0x25, 0x01,         /*          Logical Maximum (1),        */
3978c2ecf20Sopenharmony_ci0x45, 0x01,         /*          Physical Maximum (1),       */
3988c2ecf20Sopenharmony_ci0x75, 0x01,         /*          Report Size (1),            */
3998c2ecf20Sopenharmony_ci0x95, 0x02,         /*          Report Count (2),           */
4008c2ecf20Sopenharmony_ci0x81, 0x01,         /*          Input (Constant),           */
4018c2ecf20Sopenharmony_ci0x95, 0x06,         /*          Report Count (6),           */
4028c2ecf20Sopenharmony_ci0x05, 0x09,         /*          Usage Page (Button),        */
4038c2ecf20Sopenharmony_ci0x19, 0x01,         /*          Usage Minimum (01h),        */
4048c2ecf20Sopenharmony_ci0x29, 0x06,         /*          Usage Maximum (06h),        */
4058c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),           */
4068c2ecf20Sopenharmony_ci0x05, 0x01,         /*          Usage Page (Desktop),       */
4078c2ecf20Sopenharmony_ci0xB4,               /*  Pop,                                */
4088c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Constant),           */
4098c2ecf20Sopenharmony_ci0x09, 0x31,         /*          Usage (Y),                  */
4108c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),           */
4118c2ecf20Sopenharmony_ci0x09, 0x32,         /*          Usage (Z),                  */
4128c2ecf20Sopenharmony_ci0x81, 0x02,         /*          Input (Variable),           */
4138c2ecf20Sopenharmony_ci0xC0,               /*      End Collection,                 */
4148c2ecf20Sopenharmony_ci0xA1, 0x02,         /*      Collection (Logical),           */
4158c2ecf20Sopenharmony_ci0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
4168c2ecf20Sopenharmony_ci0x46, 0xFF, 0x00,   /*          Physical Maximum (255),     */
4178c2ecf20Sopenharmony_ci0x75, 0x08,         /*          Report Size (8),            */
4188c2ecf20Sopenharmony_ci0x95, 0x04,         /*          Report Count (4),           */
4198c2ecf20Sopenharmony_ci0x09, 0x02,         /*          Usage (02h),                */
4208c2ecf20Sopenharmony_ci0xB1, 0x02,         /*          Feature (Variable),         */
4218c2ecf20Sopenharmony_ci0xC0,               /*      End Collection,                 */
4228c2ecf20Sopenharmony_ci0xC0                /*  End Collection,                     */
4238c2ecf20Sopenharmony_ci};
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci/*
4268c2ecf20Sopenharmony_ci * Certain Logitech keyboards send in report #3 keys which are far
4278c2ecf20Sopenharmony_ci * above the logical maximum described in descriptor. This extends
4288c2ecf20Sopenharmony_ci * the original value of 0x28c of logical maximum to 0x104d
4298c2ecf20Sopenharmony_ci */
4308c2ecf20Sopenharmony_cistatic __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
4318c2ecf20Sopenharmony_ci		unsigned int *rsize)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	if ((drv_data->quirks & LG_RDESC) && *rsize >= 91 && rdesc[83] == 0x26 &&
4368c2ecf20Sopenharmony_ci			rdesc[84] == 0x8c && rdesc[85] == 0x02) {
4378c2ecf20Sopenharmony_ci		hid_info(hdev,
4388c2ecf20Sopenharmony_ci			 "fixing up Logitech keyboard report descriptor\n");
4398c2ecf20Sopenharmony_ci		rdesc[84] = rdesc[89] = 0x4d;
4408c2ecf20Sopenharmony_ci		rdesc[85] = rdesc[90] = 0x10;
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci	if ((drv_data->quirks & LG_RDESC_REL_ABS) && *rsize >= 51 &&
4438c2ecf20Sopenharmony_ci			rdesc[32] == 0x81 && rdesc[33] == 0x06 &&
4448c2ecf20Sopenharmony_ci			rdesc[49] == 0x81 && rdesc[50] == 0x06) {
4458c2ecf20Sopenharmony_ci		hid_info(hdev,
4468c2ecf20Sopenharmony_ci			 "fixing up rel/abs in Logitech report descriptor\n");
4478c2ecf20Sopenharmony_ci		rdesc[33] = rdesc[50] = 0x02;
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	switch (hdev->product) {
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	case USB_DEVICE_ID_LOGITECH_WINGMAN_FG:
4538c2ecf20Sopenharmony_ci		if (*rsize == FG_RDESC_ORIG_SIZE) {
4548c2ecf20Sopenharmony_ci			hid_info(hdev,
4558c2ecf20Sopenharmony_ci				"fixing up Logitech Wingman Formula GP report descriptor\n");
4568c2ecf20Sopenharmony_ci			rdesc = fg_rdesc_fixed;
4578c2ecf20Sopenharmony_ci			*rsize = sizeof(fg_rdesc_fixed);
4588c2ecf20Sopenharmony_ci		} else {
4598c2ecf20Sopenharmony_ci			hid_info(hdev,
4608c2ecf20Sopenharmony_ci				"rdesc size test failed for formula gp\n");
4618c2ecf20Sopenharmony_ci		}
4628c2ecf20Sopenharmony_ci		break;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
4668c2ecf20Sopenharmony_ci		if (*rsize == FFG_RDESC_ORIG_SIZE) {
4678c2ecf20Sopenharmony_ci			hid_info(hdev,
4688c2ecf20Sopenharmony_ci				"fixing up Logitech Wingman Formula Force GP report descriptor\n");
4698c2ecf20Sopenharmony_ci			rdesc = ffg_rdesc_fixed;
4708c2ecf20Sopenharmony_ci			*rsize = sizeof(ffg_rdesc_fixed);
4718c2ecf20Sopenharmony_ci		}
4728c2ecf20Sopenharmony_ci		break;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	/* Several wheels report as this id when operating in emulation mode. */
4758c2ecf20Sopenharmony_ci	case USB_DEVICE_ID_LOGITECH_WHEEL:
4768c2ecf20Sopenharmony_ci		if (*rsize == DF_RDESC_ORIG_SIZE) {
4778c2ecf20Sopenharmony_ci			hid_info(hdev,
4788c2ecf20Sopenharmony_ci				"fixing up Logitech Driving Force report descriptor\n");
4798c2ecf20Sopenharmony_ci			rdesc = df_rdesc_fixed;
4808c2ecf20Sopenharmony_ci			*rsize = sizeof(df_rdesc_fixed);
4818c2ecf20Sopenharmony_ci		}
4828c2ecf20Sopenharmony_ci		break;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
4858c2ecf20Sopenharmony_ci		if (*rsize == MOMO_RDESC_ORIG_SIZE) {
4868c2ecf20Sopenharmony_ci			hid_info(hdev,
4878c2ecf20Sopenharmony_ci				"fixing up Logitech Momo Force (Red) report descriptor\n");
4888c2ecf20Sopenharmony_ci			rdesc = momo_rdesc_fixed;
4898c2ecf20Sopenharmony_ci			*rsize = sizeof(momo_rdesc_fixed);
4908c2ecf20Sopenharmony_ci		}
4918c2ecf20Sopenharmony_ci		break;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
4948c2ecf20Sopenharmony_ci		if (*rsize == MOMO2_RDESC_ORIG_SIZE) {
4958c2ecf20Sopenharmony_ci			hid_info(hdev,
4968c2ecf20Sopenharmony_ci				"fixing up Logitech Momo Racing Force (Black) report descriptor\n");
4978c2ecf20Sopenharmony_ci			rdesc = momo2_rdesc_fixed;
4988c2ecf20Sopenharmony_ci			*rsize = sizeof(momo2_rdesc_fixed);
4998c2ecf20Sopenharmony_ci		}
5008c2ecf20Sopenharmony_ci		break;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL:
5038c2ecf20Sopenharmony_ci		if (*rsize == FV_RDESC_ORIG_SIZE) {
5048c2ecf20Sopenharmony_ci			hid_info(hdev,
5058c2ecf20Sopenharmony_ci				"fixing up Logitech Formula Vibration report descriptor\n");
5068c2ecf20Sopenharmony_ci			rdesc = fv_rdesc_fixed;
5078c2ecf20Sopenharmony_ci			*rsize = sizeof(fv_rdesc_fixed);
5088c2ecf20Sopenharmony_ci		}
5098c2ecf20Sopenharmony_ci		break;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
5128c2ecf20Sopenharmony_ci		if (*rsize == DFP_RDESC_ORIG_SIZE) {
5138c2ecf20Sopenharmony_ci			hid_info(hdev,
5148c2ecf20Sopenharmony_ci				"fixing up Logitech Driving Force Pro report descriptor\n");
5158c2ecf20Sopenharmony_ci			rdesc = dfp_rdesc_fixed;
5168c2ecf20Sopenharmony_ci			*rsize = sizeof(dfp_rdesc_fixed);
5178c2ecf20Sopenharmony_ci		}
5188c2ecf20Sopenharmony_ci		break;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	case USB_DEVICE_ID_LOGITECH_WII_WHEEL:
5218c2ecf20Sopenharmony_ci		if (*rsize >= 101 && rdesc[41] == 0x95 && rdesc[42] == 0x0B &&
5228c2ecf20Sopenharmony_ci				rdesc[47] == 0x05 && rdesc[48] == 0x09) {
5238c2ecf20Sopenharmony_ci			hid_info(hdev, "fixing up Logitech Speed Force Wireless report descriptor\n");
5248c2ecf20Sopenharmony_ci			rdesc[41] = 0x05;
5258c2ecf20Sopenharmony_ci			rdesc[42] = 0x09;
5268c2ecf20Sopenharmony_ci			rdesc[47] = 0x95;
5278c2ecf20Sopenharmony_ci			rdesc[48] = 0x0B;
5288c2ecf20Sopenharmony_ci		}
5298c2ecf20Sopenharmony_ci		break;
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	return rdesc;
5338c2ecf20Sopenharmony_ci}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci#define lg_map_key_clear(c)	hid_map_usage_clear(hi, usage, bit, max, \
5368c2ecf20Sopenharmony_ci		EV_KEY, (c))
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_cistatic int lg_ultrax_remote_mapping(struct hid_input *hi,
5398c2ecf20Sopenharmony_ci		struct hid_usage *usage, unsigned long **bit, int *max)
5408c2ecf20Sopenharmony_ci{
5418c2ecf20Sopenharmony_ci	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
5428c2ecf20Sopenharmony_ci		return 0;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	set_bit(EV_REP, hi->input->evbit);
5458c2ecf20Sopenharmony_ci	switch (usage->hid & HID_USAGE) {
5468c2ecf20Sopenharmony_ci	/* Reported on Logitech Ultra X Media Remote */
5478c2ecf20Sopenharmony_ci	case 0x004: lg_map_key_clear(KEY_AGAIN);	break;
5488c2ecf20Sopenharmony_ci	case 0x00d: lg_map_key_clear(KEY_HOME);		break;
5498c2ecf20Sopenharmony_ci	case 0x024: lg_map_key_clear(KEY_SHUFFLE);	break;
5508c2ecf20Sopenharmony_ci	case 0x025: lg_map_key_clear(KEY_TV);		break;
5518c2ecf20Sopenharmony_ci	case 0x026: lg_map_key_clear(KEY_MENU);		break;
5528c2ecf20Sopenharmony_ci	case 0x031: lg_map_key_clear(KEY_AUDIO);	break;
5538c2ecf20Sopenharmony_ci	case 0x032: lg_map_key_clear(KEY_TEXT);		break;
5548c2ecf20Sopenharmony_ci	case 0x033: lg_map_key_clear(KEY_LAST);		break;
5558c2ecf20Sopenharmony_ci	case 0x047: lg_map_key_clear(KEY_MP3);		break;
5568c2ecf20Sopenharmony_ci	case 0x048: lg_map_key_clear(KEY_DVD);		break;
5578c2ecf20Sopenharmony_ci	case 0x049: lg_map_key_clear(KEY_MEDIA);	break;
5588c2ecf20Sopenharmony_ci	case 0x04a: lg_map_key_clear(KEY_VIDEO);	break;
5598c2ecf20Sopenharmony_ci	case 0x04b: lg_map_key_clear(KEY_ANGLE);	break;
5608c2ecf20Sopenharmony_ci	case 0x04c: lg_map_key_clear(KEY_LANGUAGE);	break;
5618c2ecf20Sopenharmony_ci	case 0x04d: lg_map_key_clear(KEY_SUBTITLE);	break;
5628c2ecf20Sopenharmony_ci	case 0x051: lg_map_key_clear(KEY_RED);		break;
5638c2ecf20Sopenharmony_ci	case 0x052: lg_map_key_clear(KEY_CLOSE);	break;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	default:
5668c2ecf20Sopenharmony_ci		return 0;
5678c2ecf20Sopenharmony_ci	}
5688c2ecf20Sopenharmony_ci	return 1;
5698c2ecf20Sopenharmony_ci}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_cistatic int lg_dinovo_mapping(struct hid_input *hi, struct hid_usage *usage,
5728c2ecf20Sopenharmony_ci		unsigned long **bit, int *max)
5738c2ecf20Sopenharmony_ci{
5748c2ecf20Sopenharmony_ci	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
5758c2ecf20Sopenharmony_ci		return 0;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	switch (usage->hid & HID_USAGE) {
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	case 0x00d: lg_map_key_clear(KEY_MEDIA);	break;
5808c2ecf20Sopenharmony_ci	default:
5818c2ecf20Sopenharmony_ci		return 0;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	}
5848c2ecf20Sopenharmony_ci	return 1;
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_cistatic int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
5888c2ecf20Sopenharmony_ci		unsigned long **bit, int *max)
5898c2ecf20Sopenharmony_ci{
5908c2ecf20Sopenharmony_ci	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
5918c2ecf20Sopenharmony_ci		return 0;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	switch (usage->hid & HID_USAGE) {
5948c2ecf20Sopenharmony_ci	case 0x1001: lg_map_key_clear(KEY_MESSENGER);		break;
5958c2ecf20Sopenharmony_ci	case 0x1003: lg_map_key_clear(KEY_SOUND);		break;
5968c2ecf20Sopenharmony_ci	case 0x1004: lg_map_key_clear(KEY_VIDEO);		break;
5978c2ecf20Sopenharmony_ci	case 0x1005: lg_map_key_clear(KEY_AUDIO);		break;
5988c2ecf20Sopenharmony_ci	case 0x100a: lg_map_key_clear(KEY_DOCUMENTS);		break;
5998c2ecf20Sopenharmony_ci	/* The following two entries are Playlist 1 and 2 on the MX3200 */
6008c2ecf20Sopenharmony_ci	case 0x100f: lg_map_key_clear(KEY_FN_1);		break;
6018c2ecf20Sopenharmony_ci	case 0x1010: lg_map_key_clear(KEY_FN_2);		break;
6028c2ecf20Sopenharmony_ci	case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG);	break;
6038c2ecf20Sopenharmony_ci	case 0x1012: lg_map_key_clear(KEY_NEXTSONG);		break;
6048c2ecf20Sopenharmony_ci	case 0x1013: lg_map_key_clear(KEY_CAMERA);		break;
6058c2ecf20Sopenharmony_ci	case 0x1014: lg_map_key_clear(KEY_MESSENGER);		break;
6068c2ecf20Sopenharmony_ci	case 0x1015: lg_map_key_clear(KEY_RECORD);		break;
6078c2ecf20Sopenharmony_ci	case 0x1016: lg_map_key_clear(KEY_PLAYER);		break;
6088c2ecf20Sopenharmony_ci	case 0x1017: lg_map_key_clear(KEY_EJECTCD);		break;
6098c2ecf20Sopenharmony_ci	case 0x1018: lg_map_key_clear(KEY_MEDIA);		break;
6108c2ecf20Sopenharmony_ci	case 0x1019: lg_map_key_clear(KEY_PROG1);		break;
6118c2ecf20Sopenharmony_ci	case 0x101a: lg_map_key_clear(KEY_PROG2);		break;
6128c2ecf20Sopenharmony_ci	case 0x101b: lg_map_key_clear(KEY_PROG3);		break;
6138c2ecf20Sopenharmony_ci	case 0x101c: lg_map_key_clear(KEY_CYCLEWINDOWS);	break;
6148c2ecf20Sopenharmony_ci	case 0x101f: lg_map_key_clear(KEY_ZOOMIN);		break;
6158c2ecf20Sopenharmony_ci	case 0x1020: lg_map_key_clear(KEY_ZOOMOUT);		break;
6168c2ecf20Sopenharmony_ci	case 0x1021: lg_map_key_clear(KEY_ZOOMRESET);		break;
6178c2ecf20Sopenharmony_ci	case 0x1023: lg_map_key_clear(KEY_CLOSE);		break;
6188c2ecf20Sopenharmony_ci	case 0x1027: lg_map_key_clear(KEY_MENU);		break;
6198c2ecf20Sopenharmony_ci	/* this one is marked as 'Rotate' */
6208c2ecf20Sopenharmony_ci	case 0x1028: lg_map_key_clear(KEY_ANGLE);		break;
6218c2ecf20Sopenharmony_ci	case 0x1029: lg_map_key_clear(KEY_SHUFFLE);		break;
6228c2ecf20Sopenharmony_ci	case 0x102a: lg_map_key_clear(KEY_BACK);		break;
6238c2ecf20Sopenharmony_ci	case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS);	break;
6248c2ecf20Sopenharmony_ci	case 0x102d: lg_map_key_clear(KEY_WWW);			break;
6258c2ecf20Sopenharmony_ci	/* The following two are 'Start/answer call' and 'End/reject call'
6268c2ecf20Sopenharmony_ci	   on the MX3200 */
6278c2ecf20Sopenharmony_ci	case 0x1031: lg_map_key_clear(KEY_OK);			break;
6288c2ecf20Sopenharmony_ci	case 0x1032: lg_map_key_clear(KEY_CANCEL);		break;
6298c2ecf20Sopenharmony_ci	case 0x1041: lg_map_key_clear(KEY_BATTERY);		break;
6308c2ecf20Sopenharmony_ci	case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR);	break;
6318c2ecf20Sopenharmony_ci	case 0x1043: lg_map_key_clear(KEY_SPREADSHEET);		break;
6328c2ecf20Sopenharmony_ci	case 0x1044: lg_map_key_clear(KEY_PRESENTATION);	break;
6338c2ecf20Sopenharmony_ci	case 0x1045: lg_map_key_clear(KEY_UNDO);		break;
6348c2ecf20Sopenharmony_ci	case 0x1046: lg_map_key_clear(KEY_REDO);		break;
6358c2ecf20Sopenharmony_ci	case 0x1047: lg_map_key_clear(KEY_PRINT);		break;
6368c2ecf20Sopenharmony_ci	case 0x1048: lg_map_key_clear(KEY_SAVE);		break;
6378c2ecf20Sopenharmony_ci	case 0x1049: lg_map_key_clear(KEY_PROG1);		break;
6388c2ecf20Sopenharmony_ci	case 0x104a: lg_map_key_clear(KEY_PROG2);		break;
6398c2ecf20Sopenharmony_ci	case 0x104b: lg_map_key_clear(KEY_PROG3);		break;
6408c2ecf20Sopenharmony_ci	case 0x104c: lg_map_key_clear(KEY_PROG4);		break;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	default:
6438c2ecf20Sopenharmony_ci		return 0;
6448c2ecf20Sopenharmony_ci	}
6458c2ecf20Sopenharmony_ci	return 1;
6468c2ecf20Sopenharmony_ci}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_cistatic int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
6498c2ecf20Sopenharmony_ci		struct hid_field *field, struct hid_usage *usage,
6508c2ecf20Sopenharmony_ci		unsigned long **bit, int *max)
6518c2ecf20Sopenharmony_ci{
6528c2ecf20Sopenharmony_ci	/* extended mapping for certain Logitech hardware (Logitech cordless
6538c2ecf20Sopenharmony_ci	   desktop LX500) */
6548c2ecf20Sopenharmony_ci	static const u8 e_keymap[] = {
6558c2ecf20Sopenharmony_ci		  0,216,  0,213,175,156,  0,  0,  0,  0,
6568c2ecf20Sopenharmony_ci		144,  0,  0,  0,  0,  0,  0,  0,  0,212,
6578c2ecf20Sopenharmony_ci		174,167,152,161,112,  0,  0,  0,154,  0,
6588c2ecf20Sopenharmony_ci		  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
6598c2ecf20Sopenharmony_ci		  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
6608c2ecf20Sopenharmony_ci		  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
6618c2ecf20Sopenharmony_ci		  0,  0,  0,  0,  0,183,184,185,186,187,
6628c2ecf20Sopenharmony_ci		188,189,190,191,192,193,194,  0,  0,  0
6638c2ecf20Sopenharmony_ci	};
6648c2ecf20Sopenharmony_ci	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
6658c2ecf20Sopenharmony_ci	unsigned int hid = usage->hid;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER &&
6688c2ecf20Sopenharmony_ci			lg_ultrax_remote_mapping(hi, usage, bit, max))
6698c2ecf20Sopenharmony_ci		return 1;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	if (hdev->product == USB_DEVICE_ID_DINOVO_MINI &&
6728c2ecf20Sopenharmony_ci			lg_dinovo_mapping(hi, usage, bit, max))
6738c2ecf20Sopenharmony_ci		return 1;
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	if ((drv_data->quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
6768c2ecf20Sopenharmony_ci		return 1;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
6798c2ecf20Sopenharmony_ci		return 0;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	hid &= HID_USAGE;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	/* Special handling for Logitech Cordless Desktop */
6848c2ecf20Sopenharmony_ci	if (field->application == HID_GD_MOUSE) {
6858c2ecf20Sopenharmony_ci		if ((drv_data->quirks & LG_IGNORE_DOUBLED_WHEEL) &&
6868c2ecf20Sopenharmony_ci				(hid == 7 || hid == 8))
6878c2ecf20Sopenharmony_ci			return -1;
6888c2ecf20Sopenharmony_ci	} else {
6898c2ecf20Sopenharmony_ci		if ((drv_data->quirks & LG_EXPANDED_KEYMAP) &&
6908c2ecf20Sopenharmony_ci				hid < ARRAY_SIZE(e_keymap) &&
6918c2ecf20Sopenharmony_ci				e_keymap[hid] != 0) {
6928c2ecf20Sopenharmony_ci			hid_map_usage(hi, usage, bit, max, EV_KEY,
6938c2ecf20Sopenharmony_ci					e_keymap[hid]);
6948c2ecf20Sopenharmony_ci			return 1;
6958c2ecf20Sopenharmony_ci		}
6968c2ecf20Sopenharmony_ci	}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	return 0;
6998c2ecf20Sopenharmony_ci}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_cistatic int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
7028c2ecf20Sopenharmony_ci		struct hid_field *field, struct hid_usage *usage,
7038c2ecf20Sopenharmony_ci		unsigned long **bit, int *max)
7048c2ecf20Sopenharmony_ci{
7058c2ecf20Sopenharmony_ci	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	if ((drv_data->quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
7088c2ecf20Sopenharmony_ci			(field->flags & HID_MAIN_ITEM_RELATIVE))
7098c2ecf20Sopenharmony_ci		field->flags &= ~HID_MAIN_ITEM_RELATIVE;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	if ((drv_data->quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY ||
7128c2ecf20Sopenharmony_ci			 usage->type == EV_REL || usage->type == EV_ABS))
7138c2ecf20Sopenharmony_ci		clear_bit(usage->code, *bit);
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	/* Ensure that Logitech wheels are not given a default fuzz/flat value */
7168c2ecf20Sopenharmony_ci	if (usage->type == EV_ABS && (usage->code == ABS_X ||
7178c2ecf20Sopenharmony_ci			usage->code == ABS_Y || usage->code == ABS_Z ||
7188c2ecf20Sopenharmony_ci			usage->code == ABS_RZ)) {
7198c2ecf20Sopenharmony_ci		switch (hdev->product) {
7208c2ecf20Sopenharmony_ci		case USB_DEVICE_ID_LOGITECH_G29_WHEEL:
7218c2ecf20Sopenharmony_ci		case USB_DEVICE_ID_LOGITECH_WINGMAN_FG:
7228c2ecf20Sopenharmony_ci		case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
7238c2ecf20Sopenharmony_ci		case USB_DEVICE_ID_LOGITECH_WHEEL:
7248c2ecf20Sopenharmony_ci		case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
7258c2ecf20Sopenharmony_ci		case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
7268c2ecf20Sopenharmony_ci		case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
7278c2ecf20Sopenharmony_ci		case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
7288c2ecf20Sopenharmony_ci		case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
7298c2ecf20Sopenharmony_ci		case USB_DEVICE_ID_LOGITECH_WII_WHEEL:
7308c2ecf20Sopenharmony_ci		case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
7318c2ecf20Sopenharmony_ci		case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL:
7328c2ecf20Sopenharmony_ci			field->application = HID_GD_MULTIAXIS;
7338c2ecf20Sopenharmony_ci			break;
7348c2ecf20Sopenharmony_ci		default:
7358c2ecf20Sopenharmony_ci			break;
7368c2ecf20Sopenharmony_ci		}
7378c2ecf20Sopenharmony_ci	}
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	return 0;
7408c2ecf20Sopenharmony_ci}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_cistatic int lg_event(struct hid_device *hdev, struct hid_field *field,
7438c2ecf20Sopenharmony_ci		struct hid_usage *usage, __s32 value)
7448c2ecf20Sopenharmony_ci{
7458c2ecf20Sopenharmony_ci	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	if ((drv_data->quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
7488c2ecf20Sopenharmony_ci		input_event(field->hidinput->input, usage->type, usage->code,
7498c2ecf20Sopenharmony_ci				-value);
7508c2ecf20Sopenharmony_ci		return 1;
7518c2ecf20Sopenharmony_ci	}
7528c2ecf20Sopenharmony_ci	if (drv_data->quirks & LG_FF4) {
7538c2ecf20Sopenharmony_ci		return lg4ff_adjust_input_event(hdev, field, usage, value, drv_data);
7548c2ecf20Sopenharmony_ci	}
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	return 0;
7578c2ecf20Sopenharmony_ci}
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_cistatic int lg_raw_event(struct hid_device *hdev, struct hid_report *report,
7608c2ecf20Sopenharmony_ci		u8 *rd, int size)
7618c2ecf20Sopenharmony_ci{
7628c2ecf20Sopenharmony_ci	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	if (drv_data->quirks & LG_FF4)
7658c2ecf20Sopenharmony_ci		return lg4ff_raw_event(hdev, report, rd, size, drv_data);
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	return 0;
7688c2ecf20Sopenharmony_ci}
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_cistatic int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
7718c2ecf20Sopenharmony_ci{
7728c2ecf20Sopenharmony_ci	struct usb_interface *iface;
7738c2ecf20Sopenharmony_ci	__u8 iface_num;
7748c2ecf20Sopenharmony_ci	unsigned int connect_mask = HID_CONNECT_DEFAULT;
7758c2ecf20Sopenharmony_ci	struct lg_drv_data *drv_data;
7768c2ecf20Sopenharmony_ci	int ret;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	if (!hid_is_usb(hdev))
7798c2ecf20Sopenharmony_ci		return -EINVAL;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	iface = to_usb_interface(hdev->dev.parent);
7828c2ecf20Sopenharmony_ci	iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci	/* G29 only work with the 1st interface */
7858c2ecf20Sopenharmony_ci	if ((hdev->product == USB_DEVICE_ID_LOGITECH_G29_WHEEL) &&
7868c2ecf20Sopenharmony_ci	    (iface_num != 0)) {
7878c2ecf20Sopenharmony_ci		dbg_hid("%s: ignoring ifnum %d\n", __func__, iface_num);
7888c2ecf20Sopenharmony_ci		return -ENODEV;
7898c2ecf20Sopenharmony_ci	}
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	drv_data = kzalloc(sizeof(struct lg_drv_data), GFP_KERNEL);
7928c2ecf20Sopenharmony_ci	if (!drv_data) {
7938c2ecf20Sopenharmony_ci		hid_err(hdev, "Insufficient memory, cannot allocate driver data\n");
7948c2ecf20Sopenharmony_ci		return -ENOMEM;
7958c2ecf20Sopenharmony_ci	}
7968c2ecf20Sopenharmony_ci	drv_data->quirks = id->driver_data;
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	hid_set_drvdata(hdev, (void *)drv_data);
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	if (drv_data->quirks & LG_NOGET)
8018c2ecf20Sopenharmony_ci		hdev->quirks |= HID_QUIRK_NOGET;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	ret = hid_parse(hdev);
8048c2ecf20Sopenharmony_ci	if (ret) {
8058c2ecf20Sopenharmony_ci		hid_err(hdev, "parse failed\n");
8068c2ecf20Sopenharmony_ci		goto err_free;
8078c2ecf20Sopenharmony_ci	}
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	if (drv_data->quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4))
8108c2ecf20Sopenharmony_ci		connect_mask &= ~HID_CONNECT_FF;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	ret = hid_hw_start(hdev, connect_mask);
8138c2ecf20Sopenharmony_ci	if (ret) {
8148c2ecf20Sopenharmony_ci		hid_err(hdev, "hw start failed\n");
8158c2ecf20Sopenharmony_ci		goto err_free;
8168c2ecf20Sopenharmony_ci	}
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	/* Setup wireless link with Logitech Wii wheel */
8198c2ecf20Sopenharmony_ci	if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
8208c2ecf20Sopenharmony_ci		static const unsigned char cbuf[] = {
8218c2ecf20Sopenharmony_ci			0x00, 0xAF,  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
8228c2ecf20Sopenharmony_ci		};
8238c2ecf20Sopenharmony_ci		u8 *buf = kmemdup(cbuf, sizeof(cbuf), GFP_KERNEL);
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci		if (!buf) {
8268c2ecf20Sopenharmony_ci			ret = -ENOMEM;
8278c2ecf20Sopenharmony_ci			goto err_stop;
8288c2ecf20Sopenharmony_ci		}
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci		ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(cbuf),
8318c2ecf20Sopenharmony_ci					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
8328c2ecf20Sopenharmony_ci		if (ret >= 0) {
8338c2ecf20Sopenharmony_ci			/* insert a little delay of 10 jiffies ~ 40ms */
8348c2ecf20Sopenharmony_ci			wait_queue_head_t wait;
8358c2ecf20Sopenharmony_ci			init_waitqueue_head (&wait);
8368c2ecf20Sopenharmony_ci			wait_event_interruptible_timeout(wait, 0,
8378c2ecf20Sopenharmony_ci							 msecs_to_jiffies(40));
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci			/* Select random Address */
8408c2ecf20Sopenharmony_ci			buf[1] = 0xB2;
8418c2ecf20Sopenharmony_ci			get_random_bytes(&buf[2], 2);
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci			ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(cbuf),
8448c2ecf20Sopenharmony_ci					HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
8458c2ecf20Sopenharmony_ci		}
8468c2ecf20Sopenharmony_ci		kfree(buf);
8478c2ecf20Sopenharmony_ci	}
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	if (drv_data->quirks & LG_FF)
8508c2ecf20Sopenharmony_ci		ret = lgff_init(hdev);
8518c2ecf20Sopenharmony_ci	else if (drv_data->quirks & LG_FF2)
8528c2ecf20Sopenharmony_ci		ret = lg2ff_init(hdev);
8538c2ecf20Sopenharmony_ci	else if (drv_data->quirks & LG_FF3)
8548c2ecf20Sopenharmony_ci		ret = lg3ff_init(hdev);
8558c2ecf20Sopenharmony_ci	else if (drv_data->quirks & LG_FF4)
8568c2ecf20Sopenharmony_ci		ret = lg4ff_init(hdev);
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	if (ret)
8598c2ecf20Sopenharmony_ci		goto err_stop;
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	return 0;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_cierr_stop:
8648c2ecf20Sopenharmony_ci	hid_hw_stop(hdev);
8658c2ecf20Sopenharmony_cierr_free:
8668c2ecf20Sopenharmony_ci	kfree(drv_data);
8678c2ecf20Sopenharmony_ci	return ret;
8688c2ecf20Sopenharmony_ci}
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_cistatic void lg_remove(struct hid_device *hdev)
8718c2ecf20Sopenharmony_ci{
8728c2ecf20Sopenharmony_ci	struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
8738c2ecf20Sopenharmony_ci	if (drv_data->quirks & LG_FF4)
8748c2ecf20Sopenharmony_ci		lg4ff_deinit(hdev);
8758c2ecf20Sopenharmony_ci	hid_hw_stop(hdev);
8768c2ecf20Sopenharmony_ci	kfree(drv_data);
8778c2ecf20Sopenharmony_ci}
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_cistatic const struct hid_device_id lg_devices[] = {
8808c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER),
8818c2ecf20Sopenharmony_ci		.driver_data = LG_RDESC | LG_WIRELESS },
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER),
8848c2ecf20Sopenharmony_ci		.driver_data = LG_BAD_RELATIVE_KEYS },
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP),
8878c2ecf20Sopenharmony_ci		.driver_data = LG_DUPLICATE_USAGES },
8888c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE),
8898c2ecf20Sopenharmony_ci		.driver_data = LG_DUPLICATE_USAGES },
8908c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI),
8918c2ecf20Sopenharmony_ci		.driver_data = LG_DUPLICATE_USAGES },
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD),
8948c2ecf20Sopenharmony_ci		.driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
8958c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500),
8968c2ecf20Sopenharmony_ci		.driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
8998c2ecf20Sopenharmony_ci		.driver_data = LG_NOGET },
9008c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DUAL_ACTION),
9018c2ecf20Sopenharmony_ci		.driver_data = LG_NOGET },
9028c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
9038c2ecf20Sopenharmony_ci		.driver_data = LG_NOGET | LG_FF4 },
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD),
9068c2ecf20Sopenharmony_ci		.driver_data = LG_FF2 },
9078c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD),
9088c2ecf20Sopenharmony_ci		.driver_data = LG_FF },
9098c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2),
9108c2ecf20Sopenharmony_ci		.driver_data = LG_FF },
9118c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G29_WHEEL),
9128c2ecf20Sopenharmony_ci		.driver_data = LG_FF4 },
9138c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D),
9148c2ecf20Sopenharmony_ci		.driver_data = LG_FF },
9158c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
9168c2ecf20Sopenharmony_ci		.driver_data = LG_FF },
9178c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL),
9188c2ecf20Sopenharmony_ci		.driver_data = LG_NOGET | LG_FF4 },
9198c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
9208c2ecf20Sopenharmony_ci		.driver_data = LG_FF4 },
9218c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL),
9228c2ecf20Sopenharmony_ci		.driver_data = LG_FF2 },
9238c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
9248c2ecf20Sopenharmony_ci		.driver_data = LG_FF4 },
9258c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL),
9268c2ecf20Sopenharmony_ci		.driver_data = LG_FF4 },
9278c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL),
9288c2ecf20Sopenharmony_ci		.driver_data = LG_FF4 },
9298c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL),
9308c2ecf20Sopenharmony_ci		.driver_data = LG_NOGET | LG_FF4 },
9318c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
9328c2ecf20Sopenharmony_ci		.driver_data = LG_FF4 },
9338c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FG),
9348c2ecf20Sopenharmony_ci		.driver_data = LG_NOGET },
9358c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
9368c2ecf20Sopenharmony_ci		.driver_data = LG_NOGET | LG_FF4 },
9378c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
9388c2ecf20Sopenharmony_ci		.driver_data = LG_NOGET | LG_FF2 },
9398c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940),
9408c2ecf20Sopenharmony_ci		.driver_data = LG_FF3 },
9418c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR),
9428c2ecf20Sopenharmony_ci		.driver_data = LG_RDESC_REL_ABS },
9438c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER),
9448c2ecf20Sopenharmony_ci		.driver_data = LG_RDESC_REL_ABS },
9458c2ecf20Sopenharmony_ci	{ }
9468c2ecf20Sopenharmony_ci};
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(hid, lg_devices);
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_cistatic struct hid_driver lg_driver = {
9518c2ecf20Sopenharmony_ci	.name = "logitech",
9528c2ecf20Sopenharmony_ci	.id_table = lg_devices,
9538c2ecf20Sopenharmony_ci	.report_fixup = lg_report_fixup,
9548c2ecf20Sopenharmony_ci	.input_mapping = lg_input_mapping,
9558c2ecf20Sopenharmony_ci	.input_mapped = lg_input_mapped,
9568c2ecf20Sopenharmony_ci	.event = lg_event,
9578c2ecf20Sopenharmony_ci	.raw_event = lg_raw_event,
9588c2ecf20Sopenharmony_ci	.probe = lg_probe,
9598c2ecf20Sopenharmony_ci	.remove = lg_remove,
9608c2ecf20Sopenharmony_ci};
9618c2ecf20Sopenharmony_cimodule_hid_driver(lg_driver);
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci#ifdef CONFIG_LOGIWHEELS_FF
9648c2ecf20Sopenharmony_ciint lg4ff_no_autoswitch = 0;
9658c2ecf20Sopenharmony_cimodule_param_named(lg4ff_no_autoswitch, lg4ff_no_autoswitch, int, S_IRUGO);
9668c2ecf20Sopenharmony_ciMODULE_PARM_DESC(lg4ff_no_autoswitch, "Do not switch multimode wheels to their native mode automatically");
9678c2ecf20Sopenharmony_ci#endif
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
970