LCOV - code coverage report
Current view: top level - drivers/hid - hid-lg.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 1 173 0.6 %
Date: 2022-04-01 14:58:12 Functions: 1 9 11.1 %
Branches: 0 159 0.0 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0-or-later
       2                 :            : /*
       3                 :            :  *  HID driver for some logitech "special" devices
       4                 :            :  *
       5                 :            :  *  Copyright (c) 1999 Andreas Gal
       6                 :            :  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
       7                 :            :  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
       8                 :            :  *  Copyright (c) 2006-2007 Jiri Kosina
       9                 :            :  *  Copyright (c) 2008 Jiri Slaby
      10                 :            :  *  Copyright (c) 2010 Hendrik Iben
      11                 :            :  */
      12                 :            : 
      13                 :            : /*
      14                 :            :  */
      15                 :            : 
      16                 :            : #include <linux/device.h>
      17                 :            : #include <linux/hid.h>
      18                 :            : #include <linux/module.h>
      19                 :            : #include <linux/random.h>
      20                 :            : #include <linux/sched.h>
      21                 :            : #include <linux/usb.h>
      22                 :            : #include <linux/wait.h>
      23                 :            : 
      24                 :            : #include "usbhid/usbhid.h"
      25                 :            : #include "hid-ids.h"
      26                 :            : #include "hid-lg.h"
      27                 :            : #include "hid-lg4ff.h"
      28                 :            : 
      29                 :            : #define LG_RDESC                0x001
      30                 :            : #define LG_BAD_RELATIVE_KEYS    0x002
      31                 :            : #define LG_DUPLICATE_USAGES     0x004
      32                 :            : #define LG_EXPANDED_KEYMAP      0x010
      33                 :            : #define LG_IGNORE_DOUBLED_WHEEL 0x020
      34                 :            : #define LG_WIRELESS             0x040
      35                 :            : #define LG_INVERT_HWHEEL        0x080
      36                 :            : #define LG_NOGET                0x100
      37                 :            : #define LG_FF                   0x200
      38                 :            : #define LG_FF2                  0x400
      39                 :            : #define LG_RDESC_REL_ABS        0x800
      40                 :            : #define LG_FF3                  0x1000
      41                 :            : #define LG_FF4                  0x2000
      42                 :            : 
      43                 :            : /* Size of the original descriptors of the Driving Force (and Pro) wheels */
      44                 :            : #define DF_RDESC_ORIG_SIZE      130
      45                 :            : #define DFP_RDESC_ORIG_SIZE     97
      46                 :            : #define FV_RDESC_ORIG_SIZE      130
      47                 :            : #define MOMO_RDESC_ORIG_SIZE    87
      48                 :            : #define MOMO2_RDESC_ORIG_SIZE   87
      49                 :            : #define FFG_RDESC_ORIG_SIZE     85
      50                 :            : #define FG_RDESC_ORIG_SIZE      82
      51                 :            : 
      52                 :            : /* Fixed report descriptors for Logitech Driving Force (and Pro)
      53                 :            :  * wheel controllers
      54                 :            :  *
      55                 :            :  * The original descriptors hide the separate throttle and brake axes in
      56                 :            :  * a custom vendor usage page, providing only a combined value as
      57                 :            :  * GenericDesktop.Y.
      58                 :            :  * These descriptors remove the combined Y axis and instead report
      59                 :            :  * separate throttle (Y) and brake (RZ).
      60                 :            :  */
      61                 :            : static __u8 df_rdesc_fixed[] = {
      62                 :            : 0x05, 0x01,         /*  Usage Page (Desktop),                   */
      63                 :            : 0x09, 0x04,         /*  Usage (Joystick),                       */
      64                 :            : 0xA1, 0x01,         /*  Collection (Application),               */
      65                 :            : 0xA1, 0x02,         /*      Collection (Logical),               */
      66                 :            : 0x95, 0x01,         /*          Report Count (1),               */
      67                 :            : 0x75, 0x0A,         /*          Report Size (10),               */
      68                 :            : 0x14,               /*          Logical Minimum (0),            */
      69                 :            : 0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),         */
      70                 :            : 0x34,               /*          Physical Minimum (0),           */
      71                 :            : 0x46, 0xFF, 0x03,   /*          Physical Maximum (1023),        */
      72                 :            : 0x09, 0x30,         /*          Usage (X),                      */
      73                 :            : 0x81, 0x02,         /*          Input (Variable),               */
      74                 :            : 0x95, 0x0C,         /*          Report Count (12),              */
      75                 :            : 0x75, 0x01,         /*          Report Size (1),                */
      76                 :            : 0x25, 0x01,         /*          Logical Maximum (1),            */
      77                 :            : 0x45, 0x01,         /*          Physical Maximum (1),           */
      78                 :            : 0x05, 0x09,         /*          Usage (Buttons),                */
      79                 :            : 0x19, 0x01,         /*          Usage Minimum (1),              */
      80                 :            : 0x29, 0x0c,         /*          Usage Maximum (12),             */
      81                 :            : 0x81, 0x02,         /*          Input (Variable),               */
      82                 :            : 0x95, 0x02,         /*          Report Count (2),               */
      83                 :            : 0x06, 0x00, 0xFF,   /*          Usage Page (Vendor: 65280),     */
      84                 :            : 0x09, 0x01,         /*          Usage (?: 1),                   */
      85                 :            : 0x81, 0x02,         /*          Input (Variable),               */
      86                 :            : 0x05, 0x01,         /*          Usage Page (Desktop),           */
      87                 :            : 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
      88                 :            : 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
      89                 :            : 0x95, 0x01,         /*          Report Count (1),               */
      90                 :            : 0x75, 0x08,         /*          Report Size (8),                */
      91                 :            : 0x81, 0x02,         /*          Input (Variable),               */
      92                 :            : 0x25, 0x07,         /*          Logical Maximum (7),            */
      93                 :            : 0x46, 0x3B, 0x01,   /*          Physical Maximum (315),         */
      94                 :            : 0x75, 0x04,         /*          Report Size (4),                */
      95                 :            : 0x65, 0x14,         /*          Unit (Degrees),                 */
      96                 :            : 0x09, 0x39,         /*          Usage (Hat Switch),             */
      97                 :            : 0x81, 0x42,         /*          Input (Variable, Null State),   */
      98                 :            : 0x75, 0x01,         /*          Report Size (1),                */
      99                 :            : 0x95, 0x04,         /*          Report Count (4),               */
     100                 :            : 0x65, 0x00,         /*          Unit (none),                    */
     101                 :            : 0x06, 0x00, 0xFF,   /*          Usage Page (Vendor: 65280),     */
     102                 :            : 0x09, 0x01,         /*          Usage (?: 1),                   */
     103                 :            : 0x25, 0x01,         /*          Logical Maximum (1),            */
     104                 :            : 0x45, 0x01,         /*          Physical Maximum (1),           */
     105                 :            : 0x81, 0x02,         /*          Input (Variable),               */
     106                 :            : 0x05, 0x01,         /*          Usage Page (Desktop),           */
     107                 :            : 0x95, 0x01,         /*          Report Count (1),               */
     108                 :            : 0x75, 0x08,         /*          Report Size (8),                */
     109                 :            : 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
     110                 :            : 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
     111                 :            : 0x09, 0x31,         /*          Usage (Y),                      */
     112                 :            : 0x81, 0x02,         /*          Input (Variable),               */
     113                 :            : 0x09, 0x35,         /*          Usage (Rz),                     */
     114                 :            : 0x81, 0x02,         /*          Input (Variable),               */
     115                 :            : 0xC0,               /*      End Collection,                     */
     116                 :            : 0xA1, 0x02,         /*      Collection (Logical),               */
     117                 :            : 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
     118                 :            : 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
     119                 :            : 0x95, 0x07,         /*          Report Count (7),               */
     120                 :            : 0x75, 0x08,         /*          Report Size (8),                */
     121                 :            : 0x09, 0x03,         /*          Usage (?: 3),                   */
     122                 :            : 0x91, 0x02,         /*          Output (Variable),              */
     123                 :            : 0xC0,               /*      End Collection,                     */
     124                 :            : 0xC0                /*  End Collection                          */
     125                 :            : };
     126                 :            : 
     127                 :            : static __u8 dfp_rdesc_fixed[] = {
     128                 :            : 0x05, 0x01,         /*  Usage Page (Desktop),                   */
     129                 :            : 0x09, 0x04,         /*  Usage (Joystick),                       */
     130                 :            : 0xA1, 0x01,         /*  Collection (Application),               */
     131                 :            : 0xA1, 0x02,         /*      Collection (Logical),               */
     132                 :            : 0x95, 0x01,         /*          Report Count (1),               */
     133                 :            : 0x75, 0x0E,         /*          Report Size (14),               */
     134                 :            : 0x14,               /*          Logical Minimum (0),            */
     135                 :            : 0x26, 0xFF, 0x3F,   /*          Logical Maximum (16383),        */
     136                 :            : 0x34,               /*          Physical Minimum (0),           */
     137                 :            : 0x46, 0xFF, 0x3F,   /*          Physical Maximum (16383),       */
     138                 :            : 0x09, 0x30,         /*          Usage (X),                      */
     139                 :            : 0x81, 0x02,         /*          Input (Variable),               */
     140                 :            : 0x95, 0x0E,         /*          Report Count (14),              */
     141                 :            : 0x75, 0x01,         /*          Report Size (1),                */
     142                 :            : 0x25, 0x01,         /*          Logical Maximum (1),            */
     143                 :            : 0x45, 0x01,         /*          Physical Maximum (1),           */
     144                 :            : 0x05, 0x09,         /*          Usage Page (Button),            */
     145                 :            : 0x19, 0x01,         /*          Usage Minimum (01h),            */
     146                 :            : 0x29, 0x0E,         /*          Usage Maximum (0Eh),            */
     147                 :            : 0x81, 0x02,         /*          Input (Variable),               */
     148                 :            : 0x05, 0x01,         /*          Usage Page (Desktop),           */
     149                 :            : 0x95, 0x01,         /*          Report Count (1),               */
     150                 :            : 0x75, 0x04,         /*          Report Size (4),                */
     151                 :            : 0x25, 0x07,         /*          Logical Maximum (7),            */
     152                 :            : 0x46, 0x3B, 0x01,   /*          Physical Maximum (315),         */
     153                 :            : 0x65, 0x14,         /*          Unit (Degrees),                 */
     154                 :            : 0x09, 0x39,         /*          Usage (Hat Switch),             */
     155                 :            : 0x81, 0x42,         /*          Input (Variable, Nullstate),    */
     156                 :            : 0x65, 0x00,         /*          Unit,                           */
     157                 :            : 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
     158                 :            : 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
     159                 :            : 0x75, 0x08,         /*          Report Size (8),                */
     160                 :            : 0x81, 0x01,         /*          Input (Constant),               */
     161                 :            : 0x09, 0x31,         /*          Usage (Y),                      */
     162                 :            : 0x81, 0x02,         /*          Input (Variable),               */
     163                 :            : 0x09, 0x35,         /*          Usage (Rz),                     */
     164                 :            : 0x81, 0x02,         /*          Input (Variable),               */
     165                 :            : 0x81, 0x01,         /*          Input (Constant),               */
     166                 :            : 0xC0,               /*      End Collection,                     */
     167                 :            : 0xA1, 0x02,         /*      Collection (Logical),               */
     168                 :            : 0x09, 0x02,         /*          Usage (02h),                    */
     169                 :            : 0x95, 0x07,         /*          Report Count (7),               */
     170                 :            : 0x91, 0x02,         /*          Output (Variable),              */
     171                 :            : 0xC0,               /*      End Collection,                     */
     172                 :            : 0xC0                /*  End Collection                          */
     173                 :            : };
     174                 :            : 
     175                 :            : static __u8 fv_rdesc_fixed[] = {
     176                 :            : 0x05, 0x01,         /*  Usage Page (Desktop),                   */
     177                 :            : 0x09, 0x04,         /*  Usage (Joystick),                       */
     178                 :            : 0xA1, 0x01,         /*  Collection (Application),               */
     179                 :            : 0xA1, 0x02,         /*      Collection (Logical),               */
     180                 :            : 0x95, 0x01,         /*          Report Count (1),               */
     181                 :            : 0x75, 0x0A,         /*          Report Size (10),               */
     182                 :            : 0x15, 0x00,         /*          Logical Minimum (0),            */
     183                 :            : 0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),         */
     184                 :            : 0x35, 0x00,         /*          Physical Minimum (0),           */
     185                 :            : 0x46, 0xFF, 0x03,   /*          Physical Maximum (1023),        */
     186                 :            : 0x09, 0x30,         /*          Usage (X),                      */
     187                 :            : 0x81, 0x02,         /*          Input (Variable),               */
     188                 :            : 0x95, 0x0C,         /*          Report Count (12),              */
     189                 :            : 0x75, 0x01,         /*          Report Size (1),                */
     190                 :            : 0x25, 0x01,         /*          Logical Maximum (1),            */
     191                 :            : 0x45, 0x01,         /*          Physical Maximum (1),           */
     192                 :            : 0x05, 0x09,         /*          Usage Page (Button),            */
     193                 :            : 0x19, 0x01,         /*          Usage Minimum (01h),            */
     194                 :            : 0x29, 0x0C,         /*          Usage Maximum (0Ch),            */
     195                 :            : 0x81, 0x02,         /*          Input (Variable),               */
     196                 :            : 0x95, 0x02,         /*          Report Count (2),               */
     197                 :            : 0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),             */
     198                 :            : 0x09, 0x01,         /*          Usage (01h),                    */
     199                 :            : 0x81, 0x02,         /*          Input (Variable),               */
     200                 :            : 0x09, 0x02,         /*          Usage (02h),                    */
     201                 :            : 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
     202                 :            : 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
     203                 :            : 0x95, 0x01,         /*          Report Count (1),               */
     204                 :            : 0x75, 0x08,         /*          Report Size (8),                */
     205                 :            : 0x81, 0x02,         /*          Input (Variable),               */
     206                 :            : 0x05, 0x01,         /*          Usage Page (Desktop),           */
     207                 :            : 0x25, 0x07,         /*          Logical Maximum (7),            */
     208                 :            : 0x46, 0x3B, 0x01,   /*          Physical Maximum (315),         */
     209                 :            : 0x75, 0x04,         /*          Report Size (4),                */
     210                 :            : 0x65, 0x14,         /*          Unit (Degrees),                 */
     211                 :            : 0x09, 0x39,         /*          Usage (Hat Switch),             */
     212                 :            : 0x81, 0x42,         /*          Input (Variable, Null State),   */
     213                 :            : 0x75, 0x01,         /*          Report Size (1),                */
     214                 :            : 0x95, 0x04,         /*          Report Count (4),               */
     215                 :            : 0x65, 0x00,         /*          Unit,                           */
     216                 :            : 0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),             */
     217                 :            : 0x09, 0x01,         /*          Usage (01h),                    */
     218                 :            : 0x25, 0x01,         /*          Logical Maximum (1),            */
     219                 :            : 0x45, 0x01,         /*          Physical Maximum (1),           */
     220                 :            : 0x81, 0x02,         /*          Input (Variable),               */
     221                 :            : 0x05, 0x01,         /*          Usage Page (Desktop),           */
     222                 :            : 0x95, 0x01,         /*          Report Count (1),               */
     223                 :            : 0x75, 0x08,         /*          Report Size (8),                */
     224                 :            : 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
     225                 :            : 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
     226                 :            : 0x09, 0x31,         /*          Usage (Y),                      */
     227                 :            : 0x81, 0x02,         /*          Input (Variable),               */
     228                 :            : 0x09, 0x32,         /*          Usage (Z),                      */
     229                 :            : 0x81, 0x02,         /*          Input (Variable),               */
     230                 :            : 0xC0,               /*      End Collection,                     */
     231                 :            : 0xA1, 0x02,         /*      Collection (Logical),               */
     232                 :            : 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),          */
     233                 :            : 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),         */
     234                 :            : 0x95, 0x07,         /*          Report Count (7),               */
     235                 :            : 0x75, 0x08,         /*          Report Size (8),                */
     236                 :            : 0x09, 0x03,         /*          Usage (03h),                    */
     237                 :            : 0x91, 0x02,         /*          Output (Variable),              */
     238                 :            : 0xC0,               /*      End Collection,                     */
     239                 :            : 0xC0                /*  End Collection                          */
     240                 :            : };
     241                 :            : 
     242                 :            : static __u8 momo_rdesc_fixed[] = {
     243                 :            : 0x05, 0x01,         /*  Usage Page (Desktop),               */
     244                 :            : 0x09, 0x04,         /*  Usage (Joystick),                   */
     245                 :            : 0xA1, 0x01,         /*  Collection (Application),           */
     246                 :            : 0xA1, 0x02,         /*      Collection (Logical),           */
     247                 :            : 0x95, 0x01,         /*          Report Count (1),           */
     248                 :            : 0x75, 0x0A,         /*          Report Size (10),           */
     249                 :            : 0x15, 0x00,         /*          Logical Minimum (0),        */
     250                 :            : 0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
     251                 :            : 0x35, 0x00,         /*          Physical Minimum (0),       */
     252                 :            : 0x46, 0xFF, 0x03,   /*          Physical Maximum (1023),    */
     253                 :            : 0x09, 0x30,         /*          Usage (X),                  */
     254                 :            : 0x81, 0x02,         /*          Input (Variable),           */
     255                 :            : 0x95, 0x08,         /*          Report Count (8),           */
     256                 :            : 0x75, 0x01,         /*          Report Size (1),            */
     257                 :            : 0x25, 0x01,         /*          Logical Maximum (1),        */
     258                 :            : 0x45, 0x01,         /*          Physical Maximum (1),       */
     259                 :            : 0x05, 0x09,         /*          Usage Page (Button),        */
     260                 :            : 0x19, 0x01,         /*          Usage Minimum (01h),        */
     261                 :            : 0x29, 0x08,         /*          Usage Maximum (08h),        */
     262                 :            : 0x81, 0x02,         /*          Input (Variable),           */
     263                 :            : 0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
     264                 :            : 0x75, 0x0E,         /*          Report Size (14),           */
     265                 :            : 0x95, 0x01,         /*          Report Count (1),           */
     266                 :            : 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
     267                 :            : 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),     */
     268                 :            : 0x09, 0x00,         /*          Usage (00h),                */
     269                 :            : 0x81, 0x02,         /*          Input (Variable),           */
     270                 :            : 0x05, 0x01,         /*          Usage Page (Desktop),       */
     271                 :            : 0x75, 0x08,         /*          Report Size (8),            */
     272                 :            : 0x09, 0x31,         /*          Usage (Y),                  */
     273                 :            : 0x81, 0x02,         /*          Input (Variable),           */
     274                 :            : 0x09, 0x32,         /*          Usage (Z),                  */
     275                 :            : 0x81, 0x02,         /*          Input (Variable),           */
     276                 :            : 0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
     277                 :            : 0x09, 0x01,         /*          Usage (01h),                */
     278                 :            : 0x81, 0x02,         /*          Input (Variable),           */
     279                 :            : 0xC0,               /*      End Collection,                 */
     280                 :            : 0xA1, 0x02,         /*      Collection (Logical),           */
     281                 :            : 0x09, 0x02,         /*          Usage (02h),                */
     282                 :            : 0x95, 0x07,         /*          Report Count (7),           */
     283                 :            : 0x91, 0x02,         /*          Output (Variable),          */
     284                 :            : 0xC0,               /*      End Collection,                 */
     285                 :            : 0xC0                /*  End Collection                      */
     286                 :            : };
     287                 :            : 
     288                 :            : static __u8 momo2_rdesc_fixed[] = {
     289                 :            : 0x05, 0x01,         /*  Usage Page (Desktop),               */
     290                 :            : 0x09, 0x04,         /*  Usage (Joystick),                   */
     291                 :            : 0xA1, 0x01,         /*  Collection (Application),           */
     292                 :            : 0xA1, 0x02,         /*      Collection (Logical),           */
     293                 :            : 0x95, 0x01,         /*          Report Count (1),           */
     294                 :            : 0x75, 0x0A,         /*          Report Size (10),           */
     295                 :            : 0x15, 0x00,         /*          Logical Minimum (0),        */
     296                 :            : 0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
     297                 :            : 0x35, 0x00,         /*          Physical Minimum (0),       */
     298                 :            : 0x46, 0xFF, 0x03,   /*          Physical Maximum (1023),    */
     299                 :            : 0x09, 0x30,         /*          Usage (X),                  */
     300                 :            : 0x81, 0x02,         /*          Input (Variable),           */
     301                 :            : 0x95, 0x0A,         /*          Report Count (10),          */
     302                 :            : 0x75, 0x01,         /*          Report Size (1),            */
     303                 :            : 0x25, 0x01,         /*          Logical Maximum (1),        */
     304                 :            : 0x45, 0x01,         /*          Physical Maximum (1),       */
     305                 :            : 0x05, 0x09,         /*          Usage Page (Button),        */
     306                 :            : 0x19, 0x01,         /*          Usage Minimum (01h),        */
     307                 :            : 0x29, 0x0A,         /*          Usage Maximum (0Ah),        */
     308                 :            : 0x81, 0x02,         /*          Input (Variable),           */
     309                 :            : 0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
     310                 :            : 0x09, 0x00,         /*          Usage (00h),                */
     311                 :            : 0x95, 0x04,         /*          Report Count (4),           */
     312                 :            : 0x81, 0x02,         /*          Input (Variable),           */
     313                 :            : 0x95, 0x01,         /*          Report Count (1),           */
     314                 :            : 0x75, 0x08,         /*          Report Size (8),            */
     315                 :            : 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
     316                 :            : 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),     */
     317                 :            : 0x09, 0x01,         /*          Usage (01h),                */
     318                 :            : 0x81, 0x02,         /*          Input (Variable),           */
     319                 :            : 0x05, 0x01,         /*          Usage Page (Desktop),       */
     320                 :            : 0x09, 0x31,         /*          Usage (Y),                  */
     321                 :            : 0x81, 0x02,         /*          Input (Variable),           */
     322                 :            : 0x09, 0x32,         /*          Usage (Z),                  */
     323                 :            : 0x81, 0x02,         /*          Input (Variable),           */
     324                 :            : 0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
     325                 :            : 0x09, 0x00,         /*          Usage (00h),                */
     326                 :            : 0x81, 0x02,         /*          Input (Variable),           */
     327                 :            : 0xC0,               /*      End Collection,                 */
     328                 :            : 0xA1, 0x02,         /*      Collection (Logical),           */
     329                 :            : 0x09, 0x02,         /*          Usage (02h),                */
     330                 :            : 0x95, 0x07,         /*          Report Count (7),           */
     331                 :            : 0x91, 0x02,         /*          Output (Variable),          */
     332                 :            : 0xC0,               /*      End Collection,                 */
     333                 :            : 0xC0                /*  End Collection                      */
     334                 :            : };
     335                 :            : 
     336                 :            : static __u8 ffg_rdesc_fixed[] = {
     337                 :            : 0x05, 0x01,         /*  Usage Page (Desktop),               */
     338                 :            : 0x09, 0x04,         /*  Usage (Joystik),                    */
     339                 :            : 0xA1, 0x01,         /*  Collection (Application),           */
     340                 :            : 0xA1, 0x02,         /*      Collection (Logical),           */
     341                 :            : 0x95, 0x01,         /*          Report Count (1),           */
     342                 :            : 0x75, 0x0A,         /*          Report Size (10),           */
     343                 :            : 0x15, 0x00,         /*          Logical Minimum (0),        */
     344                 :            : 0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
     345                 :            : 0x35, 0x00,         /*          Physical Minimum (0),       */
     346                 :            : 0x46, 0xFF, 0x03,   /*          Physical Maximum (1023),    */
     347                 :            : 0x09, 0x30,         /*          Usage (X),                  */
     348                 :            : 0x81, 0x02,         /*          Input (Variable),           */
     349                 :            : 0x95, 0x06,         /*          Report Count (6),           */
     350                 :            : 0x75, 0x01,         /*          Report Size (1),            */
     351                 :            : 0x25, 0x01,         /*          Logical Maximum (1),        */
     352                 :            : 0x45, 0x01,         /*          Physical Maximum (1),       */
     353                 :            : 0x05, 0x09,         /*          Usage Page (Button),        */
     354                 :            : 0x19, 0x01,         /*          Usage Minimum (01h),        */
     355                 :            : 0x29, 0x06,         /*          Usage Maximum (06h),        */
     356                 :            : 0x81, 0x02,         /*          Input (Variable),           */
     357                 :            : 0x95, 0x01,         /*          Report Count (1),           */
     358                 :            : 0x75, 0x08,         /*          Report Size (8),            */
     359                 :            : 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
     360                 :            : 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),     */
     361                 :            : 0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
     362                 :            : 0x09, 0x01,         /*          Usage (01h),                */
     363                 :            : 0x81, 0x02,         /*          Input (Variable),           */
     364                 :            : 0x05, 0x01,         /*          Usage Page (Desktop),       */
     365                 :            : 0x81, 0x01,         /*          Input (Constant),           */
     366                 :            : 0x09, 0x31,         /*          Usage (Y),                  */
     367                 :            : 0x81, 0x02,         /*          Input (Variable),           */
     368                 :            : 0x09, 0x32,         /*          Usage (Z),                  */
     369                 :            : 0x81, 0x02,         /*          Input (Variable),           */
     370                 :            : 0x06, 0x00, 0xFF,   /*          Usage Page (FF00h),         */
     371                 :            : 0x09, 0x01,         /*          Usage (01h),                */
     372                 :            : 0x81, 0x02,         /*          Input (Variable),           */
     373                 :            : 0xC0,               /*      End Collection,                 */
     374                 :            : 0xA1, 0x02,         /*      Collection (Logical),           */
     375                 :            : 0x09, 0x02,         /*          Usage (02h),                */
     376                 :            : 0x95, 0x07,         /*          Report Count (7),           */
     377                 :            : 0x91, 0x02,         /*          Output (Variable),          */
     378                 :            : 0xC0,               /*      End Collection,                 */
     379                 :            : 0xC0                /*  End Collection                      */
     380                 :            : };
     381                 :            : 
     382                 :            : static __u8 fg_rdesc_fixed[] = {
     383                 :            : 0x05, 0x01,         /*  Usage Page (Desktop),               */
     384                 :            : 0x09, 0x04,         /*  Usage (Joystik),                    */
     385                 :            : 0xA1, 0x01,         /*  Collection (Application),           */
     386                 :            : 0xA1, 0x02,         /*      Collection (Logical),           */
     387                 :            : 0x15, 0x00,         /*          Logical Minimum (0),        */
     388                 :            : 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
     389                 :            : 0x35, 0x00,         /*          Physical Minimum (0),       */
     390                 :            : 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),     */
     391                 :            : 0x75, 0x08,         /*          Report Size (8),            */
     392                 :            : 0x95, 0x01,         /*          Report Count (1),           */
     393                 :            : 0x09, 0x30,         /*          Usage (X),                  */
     394                 :            : 0x81, 0x02,         /*          Input (Variable),           */
     395                 :            : 0xA4,               /*  Push,                               */
     396                 :            : 0x25, 0x01,         /*          Logical Maximum (1),        */
     397                 :            : 0x45, 0x01,         /*          Physical Maximum (1),       */
     398                 :            : 0x75, 0x01,         /*          Report Size (1),            */
     399                 :            : 0x95, 0x02,         /*          Report Count (2),           */
     400                 :            : 0x81, 0x01,         /*          Input (Constant),           */
     401                 :            : 0x95, 0x06,         /*          Report Count (6),           */
     402                 :            : 0x05, 0x09,         /*          Usage Page (Button),        */
     403                 :            : 0x19, 0x01,         /*          Usage Minimum (01h),        */
     404                 :            : 0x29, 0x06,         /*          Usage Maximum (06h),        */
     405                 :            : 0x81, 0x02,         /*          Input (Variable),           */
     406                 :            : 0x05, 0x01,         /*          Usage Page (Desktop),       */
     407                 :            : 0xB4,               /*  Pop,                                */
     408                 :            : 0x81, 0x02,         /*          Input (Constant),           */
     409                 :            : 0x09, 0x31,         /*          Usage (Y),                  */
     410                 :            : 0x81, 0x02,         /*          Input (Variable),           */
     411                 :            : 0x09, 0x32,         /*          Usage (Z),                  */
     412                 :            : 0x81, 0x02,         /*          Input (Variable),           */
     413                 :            : 0xC0,               /*      End Collection,                 */
     414                 :            : 0xA1, 0x02,         /*      Collection (Logical),           */
     415                 :            : 0x26, 0xFF, 0x00,   /*          Logical Maximum (255),      */
     416                 :            : 0x46, 0xFF, 0x00,   /*          Physical Maximum (255),     */
     417                 :            : 0x75, 0x08,         /*          Report Size (8),            */
     418                 :            : 0x95, 0x04,         /*          Report Count (4),           */
     419                 :            : 0x09, 0x02,         /*          Usage (02h),                */
     420                 :            : 0xB1, 0x02,         /*          Feature (Variable),         */
     421                 :            : 0xC0,               /*      End Collection,                 */
     422                 :            : 0xC0                /*  End Collection,                     */
     423                 :            : };
     424                 :            : 
     425                 :            : /*
     426                 :            :  * Certain Logitech keyboards send in report #3 keys which are far
     427                 :            :  * above the logical maximum described in descriptor. This extends
     428                 :            :  * the original value of 0x28c of logical maximum to 0x104d
     429                 :            :  */
     430                 :          0 : static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
     431                 :            :                 unsigned int *rsize)
     432                 :            : {
     433         [ #  # ]:          0 :         struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
     434                 :            : 
     435   [ #  #  #  #  :          0 :         if ((drv_data->quirks & LG_RDESC) && *rsize >= 91 && rdesc[83] == 0x26 &&
                   #  # ]
     436   [ #  #  #  # ]:          0 :                         rdesc[84] == 0x8c && rdesc[85] == 0x02) {
     437                 :          0 :                 hid_info(hdev,
     438                 :            :                          "fixing up Logitech keyboard report descriptor\n");
     439                 :          0 :                 rdesc[84] = rdesc[89] = 0x4d;
     440                 :          0 :                 rdesc[85] = rdesc[90] = 0x10;
     441                 :            :         }
     442   [ #  #  #  # ]:          0 :         if ((drv_data->quirks & LG_RDESC_REL_ABS) && *rsize >= 51 &&
     443   [ #  #  #  # ]:          0 :                         rdesc[32] == 0x81 && rdesc[33] == 0x06 &&
     444   [ #  #  #  # ]:          0 :                         rdesc[49] == 0x81 && rdesc[50] == 0x06) {
     445                 :          0 :                 hid_info(hdev,
     446                 :            :                          "fixing up rel/abs in Logitech report descriptor\n");
     447                 :          0 :                 rdesc[33] = rdesc[50] = 0x02;
     448                 :            :         }
     449                 :            : 
     450   [ #  #  #  #  :          0 :         switch (hdev->product) {
             #  #  #  #  
                      # ]
     451                 :            : 
     452                 :          0 :         case USB_DEVICE_ID_LOGITECH_WINGMAN_FG:
     453         [ #  # ]:          0 :                 if (*rsize == FG_RDESC_ORIG_SIZE) {
     454                 :          0 :                         hid_info(hdev,
     455                 :            :                                 "fixing up Logitech Wingman Formula GP report descriptor\n");
     456                 :          0 :                         rdesc = fg_rdesc_fixed;
     457                 :          0 :                         *rsize = sizeof(fg_rdesc_fixed);
     458                 :            :                 } else {
     459                 :          0 :                         hid_info(hdev,
     460                 :            :                                 "rdesc size test failed for formula gp\n");
     461                 :            :                 }
     462                 :            :                 break;
     463                 :            : 
     464                 :            : 
     465                 :          0 :         case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
     466         [ #  # ]:          0 :                 if (*rsize == FFG_RDESC_ORIG_SIZE) {
     467                 :          0 :                         hid_info(hdev,
     468                 :            :                                 "fixing up Logitech Wingman Formula Force GP report descriptor\n");
     469                 :          0 :                         rdesc = ffg_rdesc_fixed;
     470                 :          0 :                         *rsize = sizeof(ffg_rdesc_fixed);
     471                 :            :                 }
     472                 :            :                 break;
     473                 :            : 
     474                 :            :         /* Several wheels report as this id when operating in emulation mode. */
     475                 :          0 :         case USB_DEVICE_ID_LOGITECH_WHEEL:
     476         [ #  # ]:          0 :                 if (*rsize == DF_RDESC_ORIG_SIZE) {
     477                 :          0 :                         hid_info(hdev,
     478                 :            :                                 "fixing up Logitech Driving Force report descriptor\n");
     479                 :          0 :                         rdesc = df_rdesc_fixed;
     480                 :          0 :                         *rsize = sizeof(df_rdesc_fixed);
     481                 :            :                 }
     482                 :            :                 break;
     483                 :            : 
     484                 :          0 :         case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
     485         [ #  # ]:          0 :                 if (*rsize == MOMO_RDESC_ORIG_SIZE) {
     486                 :          0 :                         hid_info(hdev,
     487                 :            :                                 "fixing up Logitech Momo Force (Red) report descriptor\n");
     488                 :          0 :                         rdesc = momo_rdesc_fixed;
     489                 :          0 :                         *rsize = sizeof(momo_rdesc_fixed);
     490                 :            :                 }
     491                 :            :                 break;
     492                 :            : 
     493                 :          0 :         case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
     494         [ #  # ]:          0 :                 if (*rsize == MOMO2_RDESC_ORIG_SIZE) {
     495                 :          0 :                         hid_info(hdev,
     496                 :            :                                 "fixing up Logitech Momo Racing Force (Black) report descriptor\n");
     497                 :          0 :                         rdesc = momo2_rdesc_fixed;
     498                 :          0 :                         *rsize = sizeof(momo2_rdesc_fixed);
     499                 :            :                 }
     500                 :            :                 break;
     501                 :            : 
     502                 :          0 :         case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL:
     503         [ #  # ]:          0 :                 if (*rsize == FV_RDESC_ORIG_SIZE) {
     504                 :          0 :                         hid_info(hdev,
     505                 :            :                                 "fixing up Logitech Formula Vibration report descriptor\n");
     506                 :          0 :                         rdesc = fv_rdesc_fixed;
     507                 :          0 :                         *rsize = sizeof(fv_rdesc_fixed);
     508                 :            :                 }
     509                 :            :                 break;
     510                 :            : 
     511                 :          0 :         case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
     512         [ #  # ]:          0 :                 if (*rsize == DFP_RDESC_ORIG_SIZE) {
     513                 :          0 :                         hid_info(hdev,
     514                 :            :                                 "fixing up Logitech Driving Force Pro report descriptor\n");
     515                 :          0 :                         rdesc = dfp_rdesc_fixed;
     516                 :          0 :                         *rsize = sizeof(dfp_rdesc_fixed);
     517                 :            :                 }
     518                 :            :                 break;
     519                 :            : 
     520                 :          0 :         case USB_DEVICE_ID_LOGITECH_WII_WHEEL:
     521   [ #  #  #  #  :          0 :                 if (*rsize >= 101 && rdesc[41] == 0x95 && rdesc[42] == 0x0B &&
                   #  # ]
     522   [ #  #  #  # ]:          0 :                                 rdesc[47] == 0x05 && rdesc[48] == 0x09) {
     523                 :          0 :                         hid_info(hdev, "fixing up Logitech Speed Force Wireless report descriptor\n");
     524                 :          0 :                         rdesc[41] = 0x05;
     525                 :          0 :                         rdesc[42] = 0x09;
     526                 :          0 :                         rdesc[47] = 0x95;
     527                 :          0 :                         rdesc[48] = 0x0B;
     528                 :            :                 }
     529                 :            :                 break;
     530                 :            :         }
     531                 :            : 
     532                 :          0 :         return rdesc;
     533                 :            : }
     534                 :            : 
     535                 :            : #define lg_map_key_clear(c)     hid_map_usage_clear(hi, usage, bit, max, \
     536                 :            :                 EV_KEY, (c))
     537                 :            : 
     538                 :            : static int lg_ultrax_remote_mapping(struct hid_input *hi,
     539                 :            :                 struct hid_usage *usage, unsigned long **bit, int *max)
     540                 :            : {
     541                 :            :         if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
     542                 :            :                 return 0;
     543                 :            : 
     544                 :            :         set_bit(EV_REP, hi->input->evbit);
     545                 :            :         switch (usage->hid & HID_USAGE) {
     546                 :            :         /* Reported on Logitech Ultra X Media Remote */
     547                 :            :         case 0x004: lg_map_key_clear(KEY_AGAIN);        break;
     548                 :            :         case 0x00d: lg_map_key_clear(KEY_HOME);         break;
     549                 :            :         case 0x024: lg_map_key_clear(KEY_SHUFFLE);      break;
     550                 :            :         case 0x025: lg_map_key_clear(KEY_TV);           break;
     551                 :            :         case 0x026: lg_map_key_clear(KEY_MENU);         break;
     552                 :            :         case 0x031: lg_map_key_clear(KEY_AUDIO);        break;
     553                 :            :         case 0x032: lg_map_key_clear(KEY_TEXT);         break;
     554                 :            :         case 0x033: lg_map_key_clear(KEY_LAST);         break;
     555                 :            :         case 0x047: lg_map_key_clear(KEY_MP3);          break;
     556                 :            :         case 0x048: lg_map_key_clear(KEY_DVD);          break;
     557                 :            :         case 0x049: lg_map_key_clear(KEY_MEDIA);        break;
     558                 :            :         case 0x04a: lg_map_key_clear(KEY_VIDEO);        break;
     559                 :            :         case 0x04b: lg_map_key_clear(KEY_ANGLE);        break;
     560                 :            :         case 0x04c: lg_map_key_clear(KEY_LANGUAGE);     break;
     561                 :            :         case 0x04d: lg_map_key_clear(KEY_SUBTITLE);     break;
     562                 :            :         case 0x051: lg_map_key_clear(KEY_RED);          break;
     563                 :            :         case 0x052: lg_map_key_clear(KEY_CLOSE);        break;
     564                 :            : 
     565                 :            :         default:
     566                 :            :                 return 0;
     567                 :            :         }
     568                 :            :         return 1;
     569                 :            : }
     570                 :            : 
     571                 :          0 : static int lg_dinovo_mapping(struct hid_input *hi, struct hid_usage *usage,
     572                 :            :                 unsigned long **bit, int *max)
     573                 :            : {
     574         [ #  # ]:          0 :         if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
     575                 :            :                 return 0;
     576                 :            : 
     577         [ #  # ]:          0 :         switch (usage->hid & HID_USAGE) {
     578                 :            : 
     579                 :          0 :         case 0x00d: lg_map_key_clear(KEY_MEDIA);        break;
     580                 :            :         default:
     581                 :            :                 return 0;
     582                 :            : 
     583                 :            :         }
     584                 :          0 :         return 1;
     585                 :            : }
     586                 :            : 
     587                 :            : static int lg_wireless_mapping(struct hid_input *hi, struct hid_usage *usage,
     588                 :            :                 unsigned long **bit, int *max)
     589                 :            : {
     590                 :            :         if ((usage->hid & HID_USAGE_PAGE) != HID_UP_CONSUMER)
     591                 :            :                 return 0;
     592                 :            : 
     593                 :            :         switch (usage->hid & HID_USAGE) {
     594                 :            :         case 0x1001: lg_map_key_clear(KEY_MESSENGER);           break;
     595                 :            :         case 0x1003: lg_map_key_clear(KEY_SOUND);               break;
     596                 :            :         case 0x1004: lg_map_key_clear(KEY_VIDEO);               break;
     597                 :            :         case 0x1005: lg_map_key_clear(KEY_AUDIO);               break;
     598                 :            :         case 0x100a: lg_map_key_clear(KEY_DOCUMENTS);           break;
     599                 :            :         /* The following two entries are Playlist 1 and 2 on the MX3200 */
     600                 :            :         case 0x100f: lg_map_key_clear(KEY_FN_1);                break;
     601                 :            :         case 0x1010: lg_map_key_clear(KEY_FN_2);                break;
     602                 :            :         case 0x1011: lg_map_key_clear(KEY_PREVIOUSSONG);        break;
     603                 :            :         case 0x1012: lg_map_key_clear(KEY_NEXTSONG);            break;
     604                 :            :         case 0x1013: lg_map_key_clear(KEY_CAMERA);              break;
     605                 :            :         case 0x1014: lg_map_key_clear(KEY_MESSENGER);           break;
     606                 :            :         case 0x1015: lg_map_key_clear(KEY_RECORD);              break;
     607                 :            :         case 0x1016: lg_map_key_clear(KEY_PLAYER);              break;
     608                 :            :         case 0x1017: lg_map_key_clear(KEY_EJECTCD);             break;
     609                 :            :         case 0x1018: lg_map_key_clear(KEY_MEDIA);               break;
     610                 :            :         case 0x1019: lg_map_key_clear(KEY_PROG1);               break;
     611                 :            :         case 0x101a: lg_map_key_clear(KEY_PROG2);               break;
     612                 :            :         case 0x101b: lg_map_key_clear(KEY_PROG3);               break;
     613                 :            :         case 0x101c: lg_map_key_clear(KEY_CYCLEWINDOWS);        break;
     614                 :            :         case 0x101f: lg_map_key_clear(KEY_ZOOMIN);              break;
     615                 :            :         case 0x1020: lg_map_key_clear(KEY_ZOOMOUT);             break;
     616                 :            :         case 0x1021: lg_map_key_clear(KEY_ZOOMRESET);           break;
     617                 :            :         case 0x1023: lg_map_key_clear(KEY_CLOSE);               break;
     618                 :            :         case 0x1027: lg_map_key_clear(KEY_MENU);                break;
     619                 :            :         /* this one is marked as 'Rotate' */
     620                 :            :         case 0x1028: lg_map_key_clear(KEY_ANGLE);               break;
     621                 :            :         case 0x1029: lg_map_key_clear(KEY_SHUFFLE);             break;
     622                 :            :         case 0x102a: lg_map_key_clear(KEY_BACK);                break;
     623                 :            :         case 0x102b: lg_map_key_clear(KEY_CYCLEWINDOWS);        break;
     624                 :            :         case 0x102d: lg_map_key_clear(KEY_WWW);                 break;
     625                 :            :         /* The following two are 'Start/answer call' and 'End/reject call'
     626                 :            :            on the MX3200 */
     627                 :            :         case 0x1031: lg_map_key_clear(KEY_OK);                  break;
     628                 :            :         case 0x1032: lg_map_key_clear(KEY_CANCEL);              break;
     629                 :            :         case 0x1041: lg_map_key_clear(KEY_BATTERY);             break;
     630                 :            :         case 0x1042: lg_map_key_clear(KEY_WORDPROCESSOR);       break;
     631                 :            :         case 0x1043: lg_map_key_clear(KEY_SPREADSHEET);         break;
     632                 :            :         case 0x1044: lg_map_key_clear(KEY_PRESENTATION);        break;
     633                 :            :         case 0x1045: lg_map_key_clear(KEY_UNDO);                break;
     634                 :            :         case 0x1046: lg_map_key_clear(KEY_REDO);                break;
     635                 :            :         case 0x1047: lg_map_key_clear(KEY_PRINT);               break;
     636                 :            :         case 0x1048: lg_map_key_clear(KEY_SAVE);                break;
     637                 :            :         case 0x1049: lg_map_key_clear(KEY_PROG1);               break;
     638                 :            :         case 0x104a: lg_map_key_clear(KEY_PROG2);               break;
     639                 :            :         case 0x104b: lg_map_key_clear(KEY_PROG3);               break;
     640                 :            :         case 0x104c: lg_map_key_clear(KEY_PROG4);               break;
     641                 :            : 
     642                 :            :         default:
     643                 :            :                 return 0;
     644                 :            :         }
     645                 :            :         return 1;
     646                 :            : }
     647                 :            : 
     648                 :          0 : static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
     649                 :            :                 struct hid_field *field, struct hid_usage *usage,
     650                 :            :                 unsigned long **bit, int *max)
     651                 :            : {
     652                 :            :         /* extended mapping for certain Logitech hardware (Logitech cordless
     653                 :            :            desktop LX500) */
     654                 :          0 :         static const u8 e_keymap[] = {
     655                 :            :                   0,216,  0,213,175,156,  0,  0,  0,  0,
     656                 :            :                 144,  0,  0,  0,  0,  0,  0,  0,  0,212,
     657                 :            :                 174,167,152,161,112,  0,  0,  0,154,  0,
     658                 :            :                   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
     659                 :            :                   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
     660                 :            :                   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
     661                 :            :                   0,  0,  0,  0,  0,183,184,185,186,187,
     662                 :            :                 188,189,190,191,192,193,194,  0,  0,  0
     663                 :            :         };
     664         [ #  # ]:          0 :         struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
     665                 :          0 :         unsigned int hid = usage->hid;
     666                 :            : 
     667   [ #  #  #  # ]:          0 :         if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER &&
     668                 :          0 :                         lg_ultrax_remote_mapping(hi, usage, bit, max))
     669                 :            :                 return 1;
     670                 :            : 
     671         [ #  # ]:          0 :         if (hdev->product == USB_DEVICE_ID_DINOVO_MINI &&
     672                 :            :                         lg_dinovo_mapping(hi, usage, bit, max))
     673                 :          0 :                 return 1;
     674                 :            : 
     675   [ #  #  #  # ]:          0 :         if ((drv_data->quirks & LG_WIRELESS) && lg_wireless_mapping(hi, usage, bit, max))
     676                 :            :                 return 1;
     677                 :            : 
     678         [ #  # ]:          0 :         if ((hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
     679                 :            :                 return 0;
     680                 :            : 
     681                 :          0 :         hid &= HID_USAGE;
     682                 :            : 
     683                 :            :         /* Special handling for Logitech Cordless Desktop */
     684         [ #  # ]:          0 :         if (field->application == HID_GD_MOUSE) {
     685         [ #  # ]:          0 :                 if ((drv_data->quirks & LG_IGNORE_DOUBLED_WHEEL) &&
     686         [ #  # ]:          0 :                                 (hid == 7 || hid == 8))
     687                 :          0 :                         return -1;
     688                 :            :         } else {
     689   [ #  #  #  # ]:          0 :                 if ((drv_data->quirks & LG_EXPANDED_KEYMAP) &&
     690                 :          0 :                                 hid < ARRAY_SIZE(e_keymap) &&
     691         [ #  # ]:          0 :                                 e_keymap[hid] != 0) {
     692                 :          0 :                         hid_map_usage(hi, usage, bit, max, EV_KEY,
     693                 :            :                                         e_keymap[hid]);
     694                 :          0 :                         return 1;
     695                 :            :                 }
     696                 :            :         }
     697                 :            : 
     698                 :            :         return 0;
     699                 :            : }
     700                 :            : 
     701                 :          0 : static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
     702                 :            :                 struct hid_field *field, struct hid_usage *usage,
     703                 :            :                 unsigned long **bit, int *max)
     704                 :            : {
     705         [ #  # ]:          0 :         struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
     706                 :            : 
     707   [ #  #  #  # ]:          0 :         if ((drv_data->quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
     708         [ #  # ]:          0 :                         (field->flags & HID_MAIN_ITEM_RELATIVE))
     709                 :          0 :                 field->flags &= ~HID_MAIN_ITEM_RELATIVE;
     710                 :            : 
     711         [ #  # ]:          0 :         if ((drv_data->quirks & LG_DUPLICATE_USAGES) && (usage->type == EV_KEY ||
     712         [ #  # ]:          0 :                          usage->type == EV_REL || usage->type == EV_ABS))
     713                 :          0 :                 clear_bit(usage->code, *bit);
     714                 :            : 
     715                 :            :         /* Ensure that Logitech wheels are not given a default fuzz/flat value */
     716         [ #  # ]:          0 :         if (usage->type == EV_ABS && (usage->code == ABS_X ||
     717         [ #  # ]:          0 :                         usage->code == ABS_Y || usage->code == ABS_Z ||
     718                 :            :                         usage->code == ABS_RZ)) {
     719         [ #  # ]:          0 :                 switch (hdev->product) {
     720                 :          0 :                 case USB_DEVICE_ID_LOGITECH_G29_WHEEL:
     721                 :            :                 case USB_DEVICE_ID_LOGITECH_WINGMAN_FG:
     722                 :            :                 case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
     723                 :            :                 case USB_DEVICE_ID_LOGITECH_WHEEL:
     724                 :            :                 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
     725                 :            :                 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
     726                 :            :                 case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
     727                 :            :                 case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
     728                 :            :                 case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
     729                 :            :                 case USB_DEVICE_ID_LOGITECH_WII_WHEEL:
     730                 :            :                 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
     731                 :            :                 case USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL:
     732                 :          0 :                         field->application = HID_GD_MULTIAXIS;
     733                 :          0 :                         break;
     734                 :            :                 default:
     735                 :            :                         break;
     736                 :            :                 }
     737                 :          0 :         }
     738                 :            : 
     739                 :          0 :         return 0;
     740                 :            : }
     741                 :            : 
     742                 :          0 : static int lg_event(struct hid_device *hdev, struct hid_field *field,
     743                 :            :                 struct hid_usage *usage, __s32 value)
     744                 :            : {
     745         [ #  # ]:          0 :         struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
     746                 :            : 
     747   [ #  #  #  # ]:          0 :         if ((drv_data->quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
     748                 :          0 :                 input_event(field->hidinput->input, usage->type, usage->code,
     749                 :            :                                 -value);
     750                 :          0 :                 return 1;
     751                 :            :         }
     752         [ #  # ]:          0 :         if (drv_data->quirks & LG_FF4) {
     753                 :          0 :                 return lg4ff_adjust_input_event(hdev, field, usage, value, drv_data);
     754                 :            :         }
     755                 :            : 
     756                 :            :         return 0;
     757                 :            : }
     758                 :            : 
     759                 :          0 : static int lg_raw_event(struct hid_device *hdev, struct hid_report *report,
     760                 :            :                 u8 *rd, int size)
     761                 :            : {
     762         [ #  # ]:          0 :         struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
     763                 :            : 
     764         [ #  # ]:          0 :         if (drv_data->quirks & LG_FF4)
     765                 :          0 :                 return lg4ff_raw_event(hdev, report, rd, size, drv_data);
     766                 :            : 
     767                 :            :         return 0;
     768                 :            : }
     769                 :            : 
     770                 :          0 : static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
     771                 :            : {
     772                 :          0 :         struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
     773                 :          0 :         __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
     774                 :          0 :         unsigned int connect_mask = HID_CONNECT_DEFAULT;
     775                 :          0 :         struct lg_drv_data *drv_data;
     776                 :          0 :         int ret;
     777                 :            : 
     778                 :            :         /* G29 only work with the 1st interface */
     779   [ #  #  #  # ]:          0 :         if ((hdev->product == USB_DEVICE_ID_LOGITECH_G29_WHEEL) &&
     780                 :            :             (iface_num != 0)) {
     781         [ #  # ]:          0 :                 dbg_hid("%s: ignoring ifnum %d\n", __func__, iface_num);
     782                 :          0 :                 return -ENODEV;
     783                 :            :         }
     784                 :            : 
     785                 :          0 :         drv_data = kzalloc(sizeof(struct lg_drv_data), GFP_KERNEL);
     786         [ #  # ]:          0 :         if (!drv_data) {
     787                 :          0 :                 hid_err(hdev, "Insufficient memory, cannot allocate driver data\n");
     788                 :          0 :                 return -ENOMEM;
     789                 :            :         }
     790                 :          0 :         drv_data->quirks = id->driver_data;
     791                 :            : 
     792         [ #  # ]:          0 :         hid_set_drvdata(hdev, (void *)drv_data);
     793                 :            : 
     794         [ #  # ]:          0 :         if (drv_data->quirks & LG_NOGET)
     795                 :          0 :                 hdev->quirks |= HID_QUIRK_NOGET;
     796                 :            : 
     797                 :          0 :         ret = hid_parse(hdev);
     798         [ #  # ]:          0 :         if (ret) {
     799                 :          0 :                 hid_err(hdev, "parse failed\n");
     800                 :          0 :                 goto err_free;
     801                 :            :         }
     802                 :            : 
     803         [ #  # ]:          0 :         if (drv_data->quirks & (LG_FF | LG_FF2 | LG_FF3 | LG_FF4))
     804                 :          0 :                 connect_mask &= ~HID_CONNECT_FF;
     805                 :            : 
     806                 :          0 :         ret = hid_hw_start(hdev, connect_mask);
     807         [ #  # ]:          0 :         if (ret) {
     808                 :          0 :                 hid_err(hdev, "hw start failed\n");
     809                 :          0 :                 goto err_free;
     810                 :            :         }
     811                 :            : 
     812                 :            :         /* Setup wireless link with Logitech Wii wheel */
     813         [ #  # ]:          0 :         if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
     814                 :          0 :                 static const unsigned char cbuf[] = {
     815                 :            :                         0x00, 0xAF,  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
     816                 :            :                 };
     817                 :          0 :                 u8 *buf = kmemdup(cbuf, sizeof(cbuf), GFP_KERNEL);
     818                 :            : 
     819         [ #  # ]:          0 :                 if (!buf) {
     820                 :          0 :                         ret = -ENOMEM;
     821                 :          0 :                         goto err_stop;
     822                 :            :                 }
     823                 :            : 
     824                 :          0 :                 ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(cbuf),
     825                 :            :                                         HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
     826         [ #  # ]:          0 :                 if (ret >= 0) {
     827                 :            :                         /* insert a little delay of 10 jiffies ~ 40ms */
     828                 :          0 :                         wait_queue_head_t wait;
     829                 :          0 :                         init_waitqueue_head (&wait);
     830   [ #  #  #  # ]:          0 :                         wait_event_interruptible_timeout(wait, 0,
     831                 :            :                                                          msecs_to_jiffies(40));
     832                 :            : 
     833                 :            :                         /* Select random Address */
     834                 :          0 :                         buf[1] = 0xB2;
     835                 :          0 :                         get_random_bytes(&buf[2], 2);
     836                 :            : 
     837                 :          0 :                         ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(cbuf),
     838                 :            :                                         HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
     839                 :            :                 }
     840                 :          0 :                 kfree(buf);
     841                 :            :         }
     842                 :            : 
     843         [ #  # ]:          0 :         if (drv_data->quirks & LG_FF)
     844                 :          0 :                 ret = lgff_init(hdev);
     845         [ #  # ]:          0 :         else if (drv_data->quirks & LG_FF2)
     846                 :            :                 ret = lg2ff_init(hdev);
     847         [ #  # ]:          0 :         else if (drv_data->quirks & LG_FF3)
     848                 :            :                 ret = lg3ff_init(hdev);
     849         [ #  # ]:          0 :         else if (drv_data->quirks & LG_FF4)
     850                 :          0 :                 ret = lg4ff_init(hdev);
     851                 :            : 
     852         [ #  # ]:          0 :         if (ret)
     853                 :          0 :                 goto err_stop;
     854                 :            : 
     855                 :            :         return 0;
     856                 :            : 
     857                 :          0 : err_stop:
     858                 :          0 :         hid_hw_stop(hdev);
     859                 :          0 : err_free:
     860                 :          0 :         kfree(drv_data);
     861                 :          0 :         return ret;
     862                 :            : }
     863                 :            : 
     864                 :          0 : static void lg_remove(struct hid_device *hdev)
     865                 :            : {
     866         [ #  # ]:          0 :         struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
     867         [ #  # ]:          0 :         if (drv_data->quirks & LG_FF4)
     868                 :          0 :                 lg4ff_deinit(hdev);
     869                 :          0 :         hid_hw_stop(hdev);
     870                 :          0 :         kfree(drv_data);
     871                 :          0 : }
     872                 :            : 
     873                 :            : static const struct hid_device_id lg_devices[] = {
     874                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER),
     875                 :            :                 .driver_data = LG_RDESC | LG_WIRELESS },
     876                 :            : 
     877                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER),
     878                 :            :                 .driver_data = LG_BAD_RELATIVE_KEYS },
     879                 :            : 
     880                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP),
     881                 :            :                 .driver_data = LG_DUPLICATE_USAGES },
     882                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE),
     883                 :            :                 .driver_data = LG_DUPLICATE_USAGES },
     884                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI),
     885                 :            :                 .driver_data = LG_DUPLICATE_USAGES },
     886                 :            : 
     887                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_ELITE_KBD),
     888                 :            :                 .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
     889                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_CORDLESS_DESKTOP_LX500),
     890                 :            :                 .driver_data = LG_IGNORE_DOUBLED_WHEEL | LG_EXPANDED_KEYMAP },
     891                 :            : 
     892                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_EXTREME_3D),
     893                 :            :                 .driver_data = LG_NOGET },
     894                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DUAL_ACTION),
     895                 :            :                 .driver_data = LG_NOGET },
     896                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WHEEL),
     897                 :            :                 .driver_data = LG_NOGET | LG_FF4 },
     898                 :            : 
     899                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD),
     900                 :            :                 .driver_data = LG_FF2 },
     901                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD),
     902                 :            :                 .driver_data = LG_FF },
     903                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2),
     904                 :            :                 .driver_data = LG_FF },
     905                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G29_WHEEL),
     906                 :            :                 .driver_data = LG_FF4 },
     907                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D),
     908                 :            :                 .driver_data = LG_FF },
     909                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO),
     910                 :            :                 .driver_data = LG_FF },
     911                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL),
     912                 :            :                 .driver_data = LG_NOGET | LG_FF4 },
     913                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2),
     914                 :            :                 .driver_data = LG_FF4 },
     915                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_VIBRATION_WHEEL),
     916                 :            :                 .driver_data = LG_FF2 },
     917                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
     918                 :            :                 .driver_data = LG_FF4 },
     919                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFGT_WHEEL),
     920                 :            :                 .driver_data = LG_FF4 },
     921                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G27_WHEEL),
     922                 :            :                 .driver_data = LG_FF4 },
     923                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_DFP_WHEEL),
     924                 :            :                 .driver_data = LG_NOGET | LG_FF4 },
     925                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
     926                 :            :                 .driver_data = LG_FF4 },
     927                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FG),
     928                 :            :                 .driver_data = LG_NOGET },
     929                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
     930                 :            :                 .driver_data = LG_NOGET | LG_FF4 },
     931                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
     932                 :            :                 .driver_data = LG_NOGET | LG_FF2 },
     933                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FLIGHT_SYSTEM_G940),
     934                 :            :                 .driver_data = LG_FF3 },
     935                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACENAVIGATOR),
     936                 :            :                 .driver_data = LG_RDESC_REL_ABS },
     937                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_SPACETRAVELLER),
     938                 :            :                 .driver_data = LG_RDESC_REL_ABS },
     939                 :            :         { }
     940                 :            : };
     941                 :            : 
     942                 :            : MODULE_DEVICE_TABLE(hid, lg_devices);
     943                 :            : 
     944                 :            : static struct hid_driver lg_driver = {
     945                 :            :         .name = "logitech",
     946                 :            :         .id_table = lg_devices,
     947                 :            :         .report_fixup = lg_report_fixup,
     948                 :            :         .input_mapping = lg_input_mapping,
     949                 :            :         .input_mapped = lg_input_mapped,
     950                 :            :         .event = lg_event,
     951                 :            :         .raw_event = lg_raw_event,
     952                 :            :         .probe = lg_probe,
     953                 :            :         .remove = lg_remove,
     954                 :            : };
     955                 :          3 : module_hid_driver(lg_driver);
     956                 :            : 
     957                 :            : #ifdef CONFIG_LOGIWHEELS_FF
     958                 :            : int lg4ff_no_autoswitch = 0;
     959                 :            : module_param_named(lg4ff_no_autoswitch, lg4ff_no_autoswitch, int, S_IRUGO);
     960                 :            : MODULE_PARM_DESC(lg4ff_no_autoswitch, "Do not switch multimode wheels to their native mode automatically");
     961                 :            : #endif
     962                 :            : 
     963                 :            : MODULE_LICENSE("GPL");

Generated by: LCOV version 1.14