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