LCOV - code coverage report
Current view: top level - drivers/hid - hid-lg4ff.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 0 725 0.0 %
Date: 2022-04-01 14:17:54 Functions: 0 25 0.0 %
Branches: 0 368 0.0 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0-or-later
       2                 :            : /*
       3                 :            :  *  Force feedback support for Logitech Gaming Wheels
       4                 :            :  *
       5                 :            :  *  Including G27, G25, DFP, DFGT, FFEX, Momo, Momo2 &
       6                 :            :  *  Speed Force Wireless (WiiWheel)
       7                 :            :  *
       8                 :            :  *  Copyright (c) 2010 Simon Wood <simon@mungewell.org>
       9                 :            :  */
      10                 :            : 
      11                 :            : /*
      12                 :            :  */
      13                 :            : 
      14                 :            : 
      15                 :            : #include <linux/input.h>
      16                 :            : #include <linux/usb.h>
      17                 :            : #include <linux/hid.h>
      18                 :            : 
      19                 :            : #include "usbhid/usbhid.h"
      20                 :            : #include "hid-lg.h"
      21                 :            : #include "hid-lg4ff.h"
      22                 :            : #include "hid-ids.h"
      23                 :            : 
      24                 :            : #define LG4FF_MMODE_IS_MULTIMODE 0
      25                 :            : #define LG4FF_MMODE_SWITCHED 1
      26                 :            : #define LG4FF_MMODE_NOT_MULTIMODE 2
      27                 :            : 
      28                 :            : #define LG4FF_MODE_NATIVE_IDX 0
      29                 :            : #define LG4FF_MODE_DFEX_IDX 1
      30                 :            : #define LG4FF_MODE_DFP_IDX 2
      31                 :            : #define LG4FF_MODE_G25_IDX 3
      32                 :            : #define LG4FF_MODE_DFGT_IDX 4
      33                 :            : #define LG4FF_MODE_G27_IDX 5
      34                 :            : #define LG4FF_MODE_G29_IDX 6
      35                 :            : #define LG4FF_MODE_MAX_IDX 7
      36                 :            : 
      37                 :            : #define LG4FF_MODE_NATIVE BIT(LG4FF_MODE_NATIVE_IDX)
      38                 :            : #define LG4FF_MODE_DFEX BIT(LG4FF_MODE_DFEX_IDX)
      39                 :            : #define LG4FF_MODE_DFP BIT(LG4FF_MODE_DFP_IDX)
      40                 :            : #define LG4FF_MODE_G25 BIT(LG4FF_MODE_G25_IDX)
      41                 :            : #define LG4FF_MODE_DFGT BIT(LG4FF_MODE_DFGT_IDX)
      42                 :            : #define LG4FF_MODE_G27 BIT(LG4FF_MODE_G27_IDX)
      43                 :            : #define LG4FF_MODE_G29 BIT(LG4FF_MODE_G29_IDX)
      44                 :            : 
      45                 :            : #define LG4FF_DFEX_TAG "DF-EX"
      46                 :            : #define LG4FF_DFEX_NAME "Driving Force / Formula EX"
      47                 :            : #define LG4FF_DFP_TAG "DFP"
      48                 :            : #define LG4FF_DFP_NAME "Driving Force Pro"
      49                 :            : #define LG4FF_G25_TAG "G25"
      50                 :            : #define LG4FF_G25_NAME "G25 Racing Wheel"
      51                 :            : #define LG4FF_G27_TAG "G27"
      52                 :            : #define LG4FF_G27_NAME "G27 Racing Wheel"
      53                 :            : #define LG4FF_G29_TAG "G29"
      54                 :            : #define LG4FF_G29_NAME "G29 Racing Wheel"
      55                 :            : #define LG4FF_DFGT_TAG "DFGT"
      56                 :            : #define LG4FF_DFGT_NAME "Driving Force GT"
      57                 :            : 
      58                 :            : #define LG4FF_FFEX_REV_MAJ 0x21
      59                 :            : #define LG4FF_FFEX_REV_MIN 0x00
      60                 :            : 
      61                 :            : static void lg4ff_set_range_dfp(struct hid_device *hid, u16 range);
      62                 :            : static void lg4ff_set_range_g25(struct hid_device *hid, u16 range);
      63                 :            : 
      64                 :            : struct lg4ff_wheel_data {
      65                 :            :         const u32 product_id;
      66                 :            :         u16 combine;
      67                 :            :         u16 range;
      68                 :            :         const u16 min_range;
      69                 :            :         const u16 max_range;
      70                 :            : #ifdef CONFIG_LEDS_CLASS
      71                 :            :         u8  led_state;
      72                 :            :         struct led_classdev *led[5];
      73                 :            : #endif
      74                 :            :         const u32 alternate_modes;
      75                 :            :         const char * const real_tag;
      76                 :            :         const char * const real_name;
      77                 :            :         const u16 real_product_id;
      78                 :            : 
      79                 :            :         void (*set_range)(struct hid_device *hid, u16 range);
      80                 :            : };
      81                 :            : 
      82                 :            : struct lg4ff_device_entry {
      83                 :            :         spinlock_t report_lock; /* Protect output HID report */
      84                 :            :         struct hid_report *report;
      85                 :            :         struct lg4ff_wheel_data wdata;
      86                 :            : };
      87                 :            : 
      88                 :            : static const signed short lg4ff_wheel_effects[] = {
      89                 :            :         FF_CONSTANT,
      90                 :            :         FF_AUTOCENTER,
      91                 :            :         -1
      92                 :            : };
      93                 :            : 
      94                 :            : static const signed short no_wheel_effects[] = {
      95                 :            :         -1
      96                 :            : };
      97                 :            : 
      98                 :            : struct lg4ff_wheel {
      99                 :            :         const u32 product_id;
     100                 :            :         const signed short *ff_effects;
     101                 :            :         const u16 min_range;
     102                 :            :         const u16 max_range;
     103                 :            :         void (*set_range)(struct hid_device *hid, u16 range);
     104                 :            : };
     105                 :            : 
     106                 :            : struct lg4ff_compat_mode_switch {
     107                 :            :         const u8 cmd_count;     /* Number of commands to send */
     108                 :            :         const u8 cmd[];
     109                 :            : };
     110                 :            : 
     111                 :            : struct lg4ff_wheel_ident_info {
     112                 :            :         const u32 modes;
     113                 :            :         const u16 mask;
     114                 :            :         const u16 result;
     115                 :            :         const u16 real_product_id;
     116                 :            : };
     117                 :            : 
     118                 :            : struct lg4ff_multimode_wheel {
     119                 :            :         const u16 product_id;
     120                 :            :         const u32 alternate_modes;
     121                 :            :         const char *real_tag;
     122                 :            :         const char *real_name;
     123                 :            : };
     124                 :            : 
     125                 :            : struct lg4ff_alternate_mode {
     126                 :            :         const u16 product_id;
     127                 :            :         const char *tag;
     128                 :            :         const char *name;
     129                 :            : };
     130                 :            : 
     131                 :            : static const struct lg4ff_wheel lg4ff_devices[] = {
     132                 :            :         {USB_DEVICE_ID_LOGITECH_WINGMAN_FG,  no_wheel_effects,    40, 180, NULL},
     133                 :            :         {USB_DEVICE_ID_LOGITECH_WINGMAN_FFG, lg4ff_wheel_effects, 40, 180, NULL},
     134                 :            :         {USB_DEVICE_ID_LOGITECH_WHEEL,       lg4ff_wheel_effects, 40, 270, NULL},
     135                 :            :         {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL,  lg4ff_wheel_effects, 40, 270, NULL},
     136                 :            :         {USB_DEVICE_ID_LOGITECH_DFP_WHEEL,   lg4ff_wheel_effects, 40, 900, lg4ff_set_range_dfp},
     137                 :            :         {USB_DEVICE_ID_LOGITECH_G25_WHEEL,   lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25},
     138                 :            :         {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL,  lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25},
     139                 :            :         {USB_DEVICE_ID_LOGITECH_G27_WHEEL,   lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25},
     140                 :            :         {USB_DEVICE_ID_LOGITECH_G29_WHEEL,   lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25},
     141                 :            :         {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2, lg4ff_wheel_effects, 40, 270, NULL},
     142                 :            :         {USB_DEVICE_ID_LOGITECH_WII_WHEEL,   lg4ff_wheel_effects, 40, 270, NULL}
     143                 :            : };
     144                 :            : 
     145                 :            : static const struct lg4ff_multimode_wheel lg4ff_multimode_wheels[] = {
     146                 :            :         {USB_DEVICE_ID_LOGITECH_DFP_WHEEL,
     147                 :            :          LG4FF_MODE_NATIVE | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
     148                 :            :          LG4FF_DFP_TAG, LG4FF_DFP_NAME},
     149                 :            :         {USB_DEVICE_ID_LOGITECH_G25_WHEEL,
     150                 :            :          LG4FF_MODE_NATIVE | LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
     151                 :            :          LG4FF_G25_TAG, LG4FF_G25_NAME},
     152                 :            :         {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL,
     153                 :            :          LG4FF_MODE_NATIVE | LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
     154                 :            :          LG4FF_DFGT_TAG, LG4FF_DFGT_NAME},
     155                 :            :         {USB_DEVICE_ID_LOGITECH_G27_WHEEL,
     156                 :            :          LG4FF_MODE_NATIVE | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
     157                 :            :          LG4FF_G27_TAG, LG4FF_G27_NAME},
     158                 :            :         {USB_DEVICE_ID_LOGITECH_G29_WHEEL,
     159                 :            :          LG4FF_MODE_NATIVE | LG4FF_MODE_G29 | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
     160                 :            :          LG4FF_G29_TAG, LG4FF_G29_NAME},
     161                 :            : };
     162                 :            : 
     163                 :            : static const struct lg4ff_alternate_mode lg4ff_alternate_modes[] = {
     164                 :            :         [LG4FF_MODE_NATIVE_IDX] = {0, "native", ""},
     165                 :            :         [LG4FF_MODE_DFEX_IDX] = {USB_DEVICE_ID_LOGITECH_WHEEL, LG4FF_DFEX_TAG, LG4FF_DFEX_NAME},
     166                 :            :         [LG4FF_MODE_DFP_IDX] = {USB_DEVICE_ID_LOGITECH_DFP_WHEEL, LG4FF_DFP_TAG, LG4FF_DFP_NAME},
     167                 :            :         [LG4FF_MODE_G25_IDX] = {USB_DEVICE_ID_LOGITECH_G25_WHEEL, LG4FF_G25_TAG, LG4FF_G25_NAME},
     168                 :            :         [LG4FF_MODE_DFGT_IDX] = {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, LG4FF_DFGT_TAG, LG4FF_DFGT_NAME},
     169                 :            :         [LG4FF_MODE_G27_IDX] = {USB_DEVICE_ID_LOGITECH_G27_WHEEL, LG4FF_G27_TAG, LG4FF_G27_NAME},
     170                 :            :         [LG4FF_MODE_G29_IDX] = {USB_DEVICE_ID_LOGITECH_G29_WHEEL, LG4FF_G29_TAG, LG4FF_G29_NAME},
     171                 :            : };
     172                 :            : 
     173                 :            : /* Multimode wheel identificators */
     174                 :            : static const struct lg4ff_wheel_ident_info lg4ff_dfp_ident_info = {
     175                 :            :         LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
     176                 :            :         0xf000,
     177                 :            :         0x1000,
     178                 :            :         USB_DEVICE_ID_LOGITECH_DFP_WHEEL
     179                 :            : };
     180                 :            : 
     181                 :            : static const struct lg4ff_wheel_ident_info lg4ff_g25_ident_info = {
     182                 :            :         LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
     183                 :            :         0xff00,
     184                 :            :         0x1200,
     185                 :            :         USB_DEVICE_ID_LOGITECH_G25_WHEEL
     186                 :            : };
     187                 :            : 
     188                 :            : static const struct lg4ff_wheel_ident_info lg4ff_g27_ident_info = {
     189                 :            :         LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
     190                 :            :         0xfff0,
     191                 :            :         0x1230,
     192                 :            :         USB_DEVICE_ID_LOGITECH_G27_WHEEL
     193                 :            : };
     194                 :            : 
     195                 :            : static const struct lg4ff_wheel_ident_info lg4ff_dfgt_ident_info = {
     196                 :            :         LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
     197                 :            :         0xff00,
     198                 :            :         0x1300,
     199                 :            :         USB_DEVICE_ID_LOGITECH_DFGT_WHEEL
     200                 :            : };
     201                 :            : 
     202                 :            : static const struct lg4ff_wheel_ident_info lg4ff_g29_ident_info = {
     203                 :            :         LG4FF_MODE_G29 | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
     204                 :            :         0xfff8,
     205                 :            :         0x1350,
     206                 :            :         USB_DEVICE_ID_LOGITECH_G29_WHEEL
     207                 :            : };
     208                 :            : 
     209                 :            : static const struct lg4ff_wheel_ident_info lg4ff_g29_ident_info2 = {
     210                 :            :         LG4FF_MODE_G29 | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,
     211                 :            :         0xff00,
     212                 :            :         0x8900,
     213                 :            :         USB_DEVICE_ID_LOGITECH_G29_WHEEL
     214                 :            : };
     215                 :            : 
     216                 :            : /* Multimode wheel identification checklists */
     217                 :            : static const struct lg4ff_wheel_ident_info *lg4ff_main_checklist[] = {
     218                 :            :         &lg4ff_g29_ident_info,
     219                 :            :         &lg4ff_g29_ident_info2,
     220                 :            :         &lg4ff_dfgt_ident_info,
     221                 :            :         &lg4ff_g27_ident_info,
     222                 :            :         &lg4ff_g25_ident_info,
     223                 :            :         &lg4ff_dfp_ident_info
     224                 :            : };
     225                 :            : 
     226                 :            : /* Compatibility mode switching commands */
     227                 :            : /* EXT_CMD9 - Understood by G27 and DFGT */
     228                 :            : static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_dfex = {
     229                 :            :         2,
     230                 :            :         {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,      /* Revert mode upon USB reset */
     231                 :            :          0xf8, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00}      /* Switch mode to DF-EX with detach */
     232                 :            : };
     233                 :            : 
     234                 :            : static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_dfp = {
     235                 :            :         2,
     236                 :            :         {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,      /* Revert mode upon USB reset */
     237                 :            :          0xf8, 0x09, 0x01, 0x01, 0x00, 0x00, 0x00}      /* Switch mode to DFP with detach */
     238                 :            : };
     239                 :            : 
     240                 :            : static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_g25 = {
     241                 :            :         2,
     242                 :            :         {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,      /* Revert mode upon USB reset */
     243                 :            :          0xf8, 0x09, 0x02, 0x01, 0x00, 0x00, 0x00}      /* Switch mode to G25 with detach */
     244                 :            : };
     245                 :            : 
     246                 :            : static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_dfgt = {
     247                 :            :         2,
     248                 :            :         {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,      /* Revert mode upon USB reset */
     249                 :            :          0xf8, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00}      /* Switch mode to DFGT with detach */
     250                 :            : };
     251                 :            : 
     252                 :            : static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_g27 = {
     253                 :            :         2,
     254                 :            :         {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,      /* Revert mode upon USB reset */
     255                 :            :          0xf8, 0x09, 0x04, 0x01, 0x00, 0x00, 0x00}      /* Switch mode to G27 with detach */
     256                 :            : };
     257                 :            : 
     258                 :            : static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_g29 = {
     259                 :            :         2,
     260                 :            :         {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,      /* Revert mode upon USB reset */
     261                 :            :          0xf8, 0x09, 0x05, 0x01, 0x01, 0x00, 0x00}      /* Switch mode to G29 with detach */
     262                 :            : };
     263                 :            : 
     264                 :            : /* EXT_CMD1 - Understood by DFP, G25, G27 and DFGT */
     265                 :            : static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext01_dfp = {
     266                 :            :         1,
     267                 :            :         {0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}
     268                 :            : };
     269                 :            : 
     270                 :            : /* EXT_CMD16 - Understood by G25 and G27 */
     271                 :            : static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext16_g25 = {
     272                 :            :         1,
     273                 :            :         {0xf8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00}
     274                 :            : };
     275                 :            : 
     276                 :            : /* Recalculates X axis value accordingly to currently selected range */
     277                 :          0 : static s32 lg4ff_adjust_dfp_x_axis(s32 value, u16 range)
     278                 :            : {
     279                 :          0 :         u16 max_range;
     280                 :          0 :         s32 new_value;
     281                 :            : 
     282                 :          0 :         if (range == 900)
     283                 :            :                 return value;
     284         [ #  # ]:          0 :         else if (range == 200)
     285                 :            :                 return value;
     286         [ #  # ]:          0 :         else if (range < 200)
     287                 :            :                 max_range = 200;
     288                 :            :         else
     289                 :          0 :                 max_range = 900;
     290                 :            : 
     291                 :          0 :         new_value = 8192 + mult_frac(value - 8192, max_range, range);
     292                 :          0 :         if (new_value < 0)
     293                 :            :                 return 0;
     294                 :            :         else if (new_value > 16383)
     295                 :            :                 return 16383;
     296                 :            :         else
     297                 :            :                 return new_value;
     298                 :            : }
     299                 :            : 
     300                 :          0 : int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
     301                 :            :                              struct hid_usage *usage, s32 value, struct lg_drv_data *drv_data)
     302                 :            : {
     303                 :          0 :         struct lg4ff_device_entry *entry = drv_data->device_props;
     304                 :          0 :         s32 new_value = 0;
     305                 :            : 
     306         [ #  # ]:          0 :         if (!entry) {
     307                 :          0 :                 hid_err(hid, "Device properties not found");
     308                 :          0 :                 return 0;
     309                 :            :         }
     310                 :            : 
     311         [ #  # ]:          0 :         switch (entry->wdata.product_id) {
     312                 :          0 :         case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
     313         [ #  # ]:          0 :                 switch (usage->code) {
     314                 :          0 :                 case ABS_X:
     315         [ #  # ]:          0 :                         new_value = lg4ff_adjust_dfp_x_axis(value, entry->wdata.range);
     316                 :          0 :                         input_event(field->hidinput->input, usage->type, usage->code, new_value);
     317                 :          0 :                         return 1;
     318                 :            :                 default:
     319                 :            :                         return 0;
     320                 :            :                 }
     321                 :            :         default:
     322                 :            :                 return 0;
     323                 :            :         }
     324                 :            : }
     325                 :            : 
     326                 :          0 : int lg4ff_raw_event(struct hid_device *hdev, struct hid_report *report,
     327                 :            :                 u8 *rd, int size, struct lg_drv_data *drv_data)
     328                 :            : {
     329                 :          0 :         int offset;
     330                 :          0 :         struct lg4ff_device_entry *entry = drv_data->device_props;
     331                 :            : 
     332         [ #  # ]:          0 :         if (!entry)
     333                 :            :                 return 0;
     334                 :            : 
     335                 :            :         /* adjust HID report present combined pedals data */
     336         [ #  # ]:          0 :         if (entry->wdata.combine) {
     337   [ #  #  #  #  :          0 :                 switch (entry->wdata.product_id) {
                #  #  # ]
     338                 :          0 :                 case USB_DEVICE_ID_LOGITECH_WHEEL:
     339                 :          0 :                         rd[5] = rd[3];
     340                 :          0 :                         rd[6] = 0x7F;
     341                 :          0 :                         return 1;
     342                 :          0 :                 case USB_DEVICE_ID_LOGITECH_WINGMAN_FG:
     343                 :            :                 case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:
     344                 :            :                 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
     345                 :            :                 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
     346                 :          0 :                         rd[4] = rd[3];
     347                 :          0 :                         rd[5] = 0x7F;
     348                 :          0 :                         return 1;
     349                 :          0 :                 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
     350                 :          0 :                         rd[5] = rd[4];
     351                 :          0 :                         rd[6] = 0x7F;
     352                 :          0 :                         return 1;
     353                 :            :                 case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
     354                 :            :                 case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
     355                 :            :                         offset = 5;
     356                 :            :                         break;
     357                 :          0 :                 case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
     358                 :            :                 case USB_DEVICE_ID_LOGITECH_G29_WHEEL:
     359                 :          0 :                         offset = 6;
     360                 :          0 :                         break;
     361                 :          0 :                 case USB_DEVICE_ID_LOGITECH_WII_WHEEL:
     362                 :          0 :                         offset = 3;
     363                 :          0 :                         break;
     364                 :            :                 default:
     365                 :            :                         return 0;
     366                 :            :                 }
     367                 :            : 
     368                 :            :                 /* Compute a combined axis when wheel does not supply it */
     369                 :          0 :                 rd[offset] = (0xFF + rd[offset] - rd[offset+1]) >> 1;
     370                 :          0 :                 rd[offset+1] = 0x7F;
     371                 :          0 :                 return 1;
     372                 :            :         }
     373                 :            : 
     374                 :            :         return 0;
     375                 :            : }
     376                 :            : 
     377                 :          0 : static void lg4ff_init_wheel_data(struct lg4ff_wheel_data * const wdata, const struct lg4ff_wheel *wheel,
     378                 :            :                                   const struct lg4ff_multimode_wheel *mmode_wheel,
     379                 :            :                                   const u16 real_product_id)
     380                 :            : {
     381                 :          0 :         u32 alternate_modes = 0;
     382                 :          0 :         const char *real_tag = NULL;
     383                 :          0 :         const char *real_name = NULL;
     384                 :            : 
     385         [ #  # ]:          0 :         if (mmode_wheel) {
     386                 :          0 :                 alternate_modes = mmode_wheel->alternate_modes;
     387                 :          0 :                 real_tag = mmode_wheel->real_tag;
     388                 :          0 :                 real_name = mmode_wheel->real_name;
     389                 :            :         }
     390                 :            : 
     391                 :            :         {
     392                 :          0 :                 struct lg4ff_wheel_data t_wdata =  { .product_id = wheel->product_id,
     393                 :            :                                                      .real_product_id = real_product_id,
     394                 :            :                                                      .combine = 0,
     395                 :          0 :                                                      .min_range = wheel->min_range,
     396                 :          0 :                                                      .max_range = wheel->max_range,
     397                 :          0 :                                                      .set_range = wheel->set_range,
     398                 :            :                                                      .alternate_modes = alternate_modes,
     399                 :            :                                                      .real_tag = real_tag,
     400                 :            :                                                      .real_name = real_name };
     401                 :            : 
     402                 :          0 :                 memcpy(wdata, &t_wdata, sizeof(t_wdata));
     403                 :            :         }
     404                 :          0 : }
     405                 :            : 
     406                 :          0 : static int lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
     407                 :            : {
     408         [ #  # ]:          0 :         struct hid_device *hid = input_get_drvdata(dev);
     409                 :          0 :         struct lg4ff_device_entry *entry;
     410                 :          0 :         struct lg_drv_data *drv_data;
     411                 :          0 :         unsigned long flags;
     412                 :          0 :         s32 *value;
     413                 :          0 :         int x;
     414                 :            : 
     415         [ #  # ]:          0 :         drv_data = hid_get_drvdata(hid);
     416         [ #  # ]:          0 :         if (!drv_data) {
     417                 :          0 :                 hid_err(hid, "Private driver data not found!\n");
     418                 :          0 :                 return -EINVAL;
     419                 :            :         }
     420                 :            : 
     421                 :          0 :         entry = drv_data->device_props;
     422         [ #  # ]:          0 :         if (!entry) {
     423                 :          0 :                 hid_err(hid, "Device properties not found!\n");
     424                 :          0 :                 return -EINVAL;
     425                 :            :         }
     426                 :          0 :         value = entry->report->field[0]->value;
     427                 :            : 
     428                 :            : #define CLAMP(x) do { if (x < 0) x = 0; else if (x > 0xff) x = 0xff; } while (0)
     429                 :            : 
     430         [ #  # ]:          0 :         switch (effect->type) {
     431                 :          0 :         case FF_CONSTANT:
     432                 :          0 :                 x = effect->u.ramp.start_level + 0x80;       /* 0x80 is no force */
     433                 :          0 :                 CLAMP(x);
     434                 :            : 
     435                 :          0 :                 spin_lock_irqsave(&entry->report_lock, flags);
     436         [ #  # ]:          0 :                 if (x == 0x80) {
     437                 :            :                         /* De-activate force in slot-1*/
     438                 :          0 :                         value[0] = 0x13;
     439                 :          0 :                         value[1] = 0x00;
     440                 :          0 :                         value[2] = 0x00;
     441                 :          0 :                         value[3] = 0x00;
     442                 :          0 :                         value[4] = 0x00;
     443                 :          0 :                         value[5] = 0x00;
     444                 :          0 :                         value[6] = 0x00;
     445                 :            : 
     446                 :          0 :                         hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
     447                 :          0 :                         spin_unlock_irqrestore(&entry->report_lock, flags);
     448                 :          0 :                         return 0;
     449                 :            :                 }
     450                 :            : 
     451                 :          0 :                 value[0] = 0x11;        /* Slot 1 */
     452                 :          0 :                 value[1] = 0x08;
     453                 :          0 :                 value[2] = x;
     454                 :          0 :                 value[3] = 0x80;
     455                 :          0 :                 value[4] = 0x00;
     456                 :          0 :                 value[5] = 0x00;
     457                 :          0 :                 value[6] = 0x00;
     458                 :            : 
     459                 :          0 :                 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
     460                 :          0 :                 spin_unlock_irqrestore(&entry->report_lock, flags);
     461                 :            :                 break;
     462                 :            :         }
     463                 :            :         return 0;
     464                 :            : }
     465                 :            : 
     466                 :            : /* Sends default autocentering command compatible with
     467                 :            :  * all wheels except Formula Force EX */
     468                 :          0 : static void lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitude)
     469                 :            : {
     470         [ #  # ]:          0 :         struct hid_device *hid = input_get_drvdata(dev);
     471                 :          0 :         s32 *value;
     472                 :          0 :         u32 expand_a, expand_b;
     473                 :          0 :         struct lg4ff_device_entry *entry;
     474                 :          0 :         struct lg_drv_data *drv_data;
     475                 :          0 :         unsigned long flags;
     476                 :            : 
     477         [ #  # ]:          0 :         drv_data = hid_get_drvdata(hid);
     478         [ #  # ]:          0 :         if (!drv_data) {
     479                 :          0 :                 hid_err(hid, "Private driver data not found!\n");
     480                 :          0 :                 return;
     481                 :            :         }
     482                 :            : 
     483                 :          0 :         entry = drv_data->device_props;
     484         [ #  # ]:          0 :         if (!entry) {
     485                 :          0 :                 hid_err(hid, "Device properties not found!\n");
     486                 :          0 :                 return;
     487                 :            :         }
     488                 :          0 :         value = entry->report->field[0]->value;
     489                 :            : 
     490                 :            :         /* De-activate Auto-Center */
     491                 :          0 :         spin_lock_irqsave(&entry->report_lock, flags);
     492         [ #  # ]:          0 :         if (magnitude == 0) {
     493                 :          0 :                 value[0] = 0xf5;
     494                 :          0 :                 value[1] = 0x00;
     495                 :          0 :                 value[2] = 0x00;
     496                 :          0 :                 value[3] = 0x00;
     497                 :          0 :                 value[4] = 0x00;
     498                 :          0 :                 value[5] = 0x00;
     499                 :          0 :                 value[6] = 0x00;
     500                 :            : 
     501                 :          0 :                 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
     502                 :          0 :                 spin_unlock_irqrestore(&entry->report_lock, flags);
     503                 :          0 :                 return;
     504                 :            :         }
     505                 :            : 
     506         [ #  # ]:          0 :         if (magnitude <= 0xaaaa) {
     507                 :          0 :                 expand_a = 0x0c * magnitude;
     508                 :          0 :                 expand_b = 0x80 * magnitude;
     509                 :            :         } else {
     510                 :          0 :                 expand_a = (0x0c * 0xaaaa) + 0x06 * (magnitude - 0xaaaa);
     511                 :          0 :                 expand_b = (0x80 * 0xaaaa) + 0xff * (magnitude - 0xaaaa);
     512                 :            :         }
     513                 :            : 
     514                 :            :         /* Adjust for non-MOMO wheels */
     515         [ #  # ]:          0 :         switch (entry->wdata.product_id) {
     516                 :            :         case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:
     517                 :            :         case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:
     518                 :            :                 break;
     519                 :          0 :         default:
     520                 :          0 :                 expand_a = expand_a >> 1;
     521                 :          0 :                 break;
     522                 :            :         }
     523                 :            : 
     524                 :          0 :         value[0] = 0xfe;
     525                 :          0 :         value[1] = 0x0d;
     526                 :          0 :         value[2] = expand_a / 0xaaaa;
     527                 :          0 :         value[3] = expand_a / 0xaaaa;
     528                 :          0 :         value[4] = expand_b / 0xaaaa;
     529                 :          0 :         value[5] = 0x00;
     530                 :          0 :         value[6] = 0x00;
     531                 :            : 
     532                 :          0 :         hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
     533                 :            : 
     534                 :            :         /* Activate Auto-Center */
     535                 :          0 :         value[0] = 0x14;
     536                 :          0 :         value[1] = 0x00;
     537                 :          0 :         value[2] = 0x00;
     538                 :          0 :         value[3] = 0x00;
     539                 :          0 :         value[4] = 0x00;
     540                 :          0 :         value[5] = 0x00;
     541                 :          0 :         value[6] = 0x00;
     542                 :            : 
     543                 :          0 :         hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
     544                 :          0 :         spin_unlock_irqrestore(&entry->report_lock, flags);
     545                 :            : }
     546                 :            : 
     547                 :            : /* Sends autocentering command compatible with Formula Force EX */
     548                 :          0 : static void lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)
     549                 :            : {
     550         [ #  # ]:          0 :         struct hid_device *hid = input_get_drvdata(dev);
     551                 :          0 :         struct lg4ff_device_entry *entry;
     552                 :          0 :         struct lg_drv_data *drv_data;
     553                 :          0 :         unsigned long flags;
     554                 :          0 :         s32 *value;
     555                 :          0 :         magnitude = magnitude * 90 / 65535;
     556                 :            : 
     557         [ #  # ]:          0 :         drv_data = hid_get_drvdata(hid);
     558         [ #  # ]:          0 :         if (!drv_data) {
     559                 :          0 :                 hid_err(hid, "Private driver data not found!\n");
     560                 :          0 :                 return;
     561                 :            :         }
     562                 :            : 
     563                 :          0 :         entry = drv_data->device_props;
     564         [ #  # ]:          0 :         if (!entry) {
     565                 :          0 :                 hid_err(hid, "Device properties not found!\n");
     566                 :          0 :                 return;
     567                 :            :         }
     568                 :          0 :         value = entry->report->field[0]->value;
     569                 :            : 
     570                 :          0 :         spin_lock_irqsave(&entry->report_lock, flags);
     571                 :          0 :         value[0] = 0xfe;
     572                 :          0 :         value[1] = 0x03;
     573                 :          0 :         value[2] = magnitude >> 14;
     574                 :          0 :         value[3] = magnitude >> 14;
     575                 :          0 :         value[4] = magnitude;
     576                 :          0 :         value[5] = 0x00;
     577                 :          0 :         value[6] = 0x00;
     578                 :            : 
     579                 :          0 :         hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
     580                 :          0 :         spin_unlock_irqrestore(&entry->report_lock, flags);
     581                 :            : }
     582                 :            : 
     583                 :            : /* Sends command to set range compatible with G25/G27/Driving Force GT */
     584                 :          0 : static void lg4ff_set_range_g25(struct hid_device *hid, u16 range)
     585                 :            : {
     586                 :          0 :         struct lg4ff_device_entry *entry;
     587                 :          0 :         struct lg_drv_data *drv_data;
     588                 :          0 :         unsigned long flags;
     589                 :          0 :         s32 *value;
     590                 :            : 
     591         [ #  # ]:          0 :         drv_data = hid_get_drvdata(hid);
     592         [ #  # ]:          0 :         if (!drv_data) {
     593                 :          0 :                 hid_err(hid, "Private driver data not found!\n");
     594                 :          0 :                 return;
     595                 :            :         }
     596                 :            : 
     597                 :          0 :         entry = drv_data->device_props;
     598         [ #  # ]:          0 :         if (!entry) {
     599                 :          0 :                 hid_err(hid, "Device properties not found!\n");
     600                 :          0 :                 return;
     601                 :            :         }
     602                 :          0 :         value = entry->report->field[0]->value;
     603         [ #  # ]:          0 :         dbg_hid("G25/G27/DFGT: setting range to %u\n", range);
     604                 :            : 
     605                 :          0 :         spin_lock_irqsave(&entry->report_lock, flags);
     606                 :          0 :         value[0] = 0xf8;
     607                 :          0 :         value[1] = 0x81;
     608                 :          0 :         value[2] = range & 0x00ff;
     609                 :          0 :         value[3] = (range & 0xff00) >> 8;
     610                 :          0 :         value[4] = 0x00;
     611                 :          0 :         value[5] = 0x00;
     612                 :          0 :         value[6] = 0x00;
     613                 :            : 
     614                 :          0 :         hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
     615                 :          0 :         spin_unlock_irqrestore(&entry->report_lock, flags);
     616                 :            : }
     617                 :            : 
     618                 :            : /* Sends commands to set range compatible with Driving Force Pro wheel */
     619                 :          0 : static void lg4ff_set_range_dfp(struct hid_device *hid, u16 range)
     620                 :            : {
     621                 :          0 :         struct lg4ff_device_entry *entry;
     622                 :          0 :         struct lg_drv_data *drv_data;
     623                 :          0 :         unsigned long flags;
     624                 :          0 :         int start_left, start_right, full_range;
     625                 :          0 :         s32 *value;
     626                 :            : 
     627         [ #  # ]:          0 :         drv_data = hid_get_drvdata(hid);
     628         [ #  # ]:          0 :         if (!drv_data) {
     629                 :          0 :                 hid_err(hid, "Private driver data not found!\n");
     630                 :          0 :                 return;
     631                 :            :         }
     632                 :            : 
     633                 :          0 :         entry = drv_data->device_props;
     634         [ #  # ]:          0 :         if (!entry) {
     635                 :          0 :                 hid_err(hid, "Device properties not found!\n");
     636                 :          0 :                 return;
     637                 :            :         }
     638                 :          0 :         value = entry->report->field[0]->value;
     639         [ #  # ]:          0 :         dbg_hid("Driving Force Pro: setting range to %u\n", range);
     640                 :            : 
     641                 :            :         /* Prepare "coarse" limit command */
     642                 :          0 :         spin_lock_irqsave(&entry->report_lock, flags);
     643                 :          0 :         value[0] = 0xf8;
     644                 :          0 :         value[1] = 0x00;        /* Set later */
     645                 :          0 :         value[2] = 0x00;
     646                 :          0 :         value[3] = 0x00;
     647                 :          0 :         value[4] = 0x00;
     648                 :          0 :         value[5] = 0x00;
     649                 :          0 :         value[6] = 0x00;
     650                 :            : 
     651         [ #  # ]:          0 :         if (range > 200) {
     652                 :          0 :                 value[1] = 0x03;
     653                 :          0 :                 full_range = 900;
     654                 :            :         } else {
     655                 :          0 :                 value[1] = 0x02;
     656                 :          0 :                 full_range = 200;
     657                 :            :         }
     658                 :          0 :         hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
     659                 :            : 
     660                 :            :         /* Prepare "fine" limit command */
     661                 :          0 :         value[0] = 0x81;
     662                 :          0 :         value[1] = 0x0b;
     663                 :          0 :         value[2] = 0x00;
     664                 :          0 :         value[3] = 0x00;
     665                 :          0 :         value[4] = 0x00;
     666                 :          0 :         value[5] = 0x00;
     667                 :          0 :         value[6] = 0x00;
     668                 :            : 
     669         [ #  # ]:          0 :         if (range == 200 || range == 900) {     /* Do not apply any fine limit */
     670                 :          0 :                 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
     671                 :          0 :                 spin_unlock_irqrestore(&entry->report_lock, flags);
     672                 :          0 :                 return;
     673                 :            :         }
     674                 :            : 
     675                 :            :         /* Construct fine limit command */
     676                 :          0 :         start_left = (((full_range - range + 1) * 2047) / full_range);
     677                 :          0 :         start_right = 0xfff - start_left;
     678                 :            : 
     679                 :          0 :         value[2] = start_left >> 4;
     680                 :          0 :         value[3] = start_right >> 4;
     681                 :          0 :         value[4] = 0xff;
     682                 :          0 :         value[5] = (start_right & 0xe) << 4 | (start_left & 0xe);
     683                 :          0 :         value[6] = 0xff;
     684                 :            : 
     685                 :          0 :         hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
     686                 :          0 :         spin_unlock_irqrestore(&entry->report_lock, flags);
     687                 :            : }
     688                 :            : 
     689                 :          0 : static const struct lg4ff_compat_mode_switch *lg4ff_get_mode_switch_command(const u16 real_product_id, const u16 target_product_id)
     690                 :            : {
     691   [ #  #  #  #  :          0 :         switch (real_product_id) {
                   #  # ]
     692                 :          0 :         case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
     693         [ #  # ]:          0 :                 switch (target_product_id) {
     694                 :            :                 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
     695                 :            :                         return &lg4ff_mode_switch_ext01_dfp;
     696                 :            :                 /* DFP can only be switched to its native mode */
     697                 :          0 :                 default:
     698                 :          0 :                         return NULL;
     699                 :            :                 }
     700                 :          0 :                 break;
     701                 :          0 :         case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
     702      [ #  #  # ]:          0 :                 switch (target_product_id) {
     703                 :            :                 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
     704                 :            :                         return &lg4ff_mode_switch_ext01_dfp;
     705                 :          0 :                 case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
     706                 :          0 :                         return &lg4ff_mode_switch_ext16_g25;
     707                 :            :                 /* G25 can only be switched to DFP mode or its native mode */
     708                 :          0 :                 default:
     709                 :          0 :                         return NULL;
     710                 :            :                 }
     711                 :          0 :                 break;
     712                 :          0 :         case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
     713         [ #  # ]:          0 :                 switch (target_product_id) {
     714                 :            :                 case USB_DEVICE_ID_LOGITECH_WHEEL:
     715                 :            :                         return &lg4ff_mode_switch_ext09_dfex;
     716                 :            :                 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
     717                 :            :                         return &lg4ff_mode_switch_ext09_dfp;
     718                 :            :                 case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
     719                 :            :                         return &lg4ff_mode_switch_ext09_g25;
     720                 :            :                 case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
     721                 :            :                         return &lg4ff_mode_switch_ext09_g27;
     722                 :            :                 /* G27 can only be switched to DF-EX, DFP, G25 or its native mode */
     723                 :            :                 default:
     724                 :            :                         return NULL;
     725                 :            :                 }
     726                 :          0 :                 break;
     727                 :          0 :         case USB_DEVICE_ID_LOGITECH_G29_WHEEL:
     728   [ #  #  #  #  :          0 :                 switch (target_product_id) {
                   #  # ]
     729                 :            :                 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
     730                 :            :                         return &lg4ff_mode_switch_ext09_dfp;
     731                 :          0 :                 case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
     732                 :          0 :                         return &lg4ff_mode_switch_ext09_dfgt;
     733                 :          0 :                 case USB_DEVICE_ID_LOGITECH_G25_WHEEL:
     734                 :          0 :                         return &lg4ff_mode_switch_ext09_g25;
     735                 :          0 :                 case USB_DEVICE_ID_LOGITECH_G27_WHEEL:
     736                 :          0 :                         return &lg4ff_mode_switch_ext09_g27;
     737                 :          0 :                 case USB_DEVICE_ID_LOGITECH_G29_WHEEL:
     738                 :          0 :                         return &lg4ff_mode_switch_ext09_g29;
     739                 :            :                 /* G29 can only be switched to DF-EX, DFP, DFGT, G25, G27 or its native mode */
     740                 :          0 :                 default:
     741                 :          0 :                         return NULL;
     742                 :            :                 }
     743                 :          0 :                 break;
     744                 :          0 :         case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
     745         [ #  # ]:          0 :                 switch (target_product_id) {
     746                 :            :                 case USB_DEVICE_ID_LOGITECH_WHEEL:
     747                 :            :                         return &lg4ff_mode_switch_ext09_dfex;
     748                 :            :                 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
     749                 :            :                         return &lg4ff_mode_switch_ext09_dfp;
     750                 :            :                 case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:
     751                 :            :                         return &lg4ff_mode_switch_ext09_dfgt;
     752                 :            :                 /* DFGT can only be switched to DF-EX, DFP or its native mode */
     753                 :            :                 default:
     754                 :            :                         return NULL;
     755                 :            :                 }
     756                 :            :                 break;
     757                 :            :         /* No other wheels have multiple modes */
     758                 :            :         default:
     759                 :            :                 return NULL;
     760                 :            :         }
     761                 :            : }
     762                 :            : 
     763                 :          0 : static int lg4ff_switch_compatibility_mode(struct hid_device *hid, const struct lg4ff_compat_mode_switch *s)
     764                 :            : {
     765                 :          0 :         struct lg4ff_device_entry *entry;
     766                 :          0 :         struct lg_drv_data *drv_data;
     767                 :          0 :         unsigned long flags;
     768                 :          0 :         s32 *value;
     769                 :          0 :         u8 i;
     770                 :            : 
     771         [ #  # ]:          0 :         drv_data = hid_get_drvdata(hid);
     772         [ #  # ]:          0 :         if (!drv_data) {
     773                 :          0 :                 hid_err(hid, "Private driver data not found!\n");
     774                 :          0 :                 return -EINVAL;
     775                 :            :         }
     776                 :            : 
     777                 :          0 :         entry = drv_data->device_props;
     778         [ #  # ]:          0 :         if (!entry) {
     779                 :          0 :                 hid_err(hid, "Device properties not found!\n");
     780                 :          0 :                 return -EINVAL;
     781                 :            :         }
     782                 :          0 :         value = entry->report->field[0]->value;
     783                 :            : 
     784                 :          0 :         spin_lock_irqsave(&entry->report_lock, flags);
     785         [ #  # ]:          0 :         for (i = 0; i < s->cmd_count; i++) {
     786                 :            :                 u8 j;
     787                 :            : 
     788         [ #  # ]:          0 :                 for (j = 0; j < 7; j++)
     789                 :          0 :                         value[j] = s->cmd[j + (7*i)];
     790                 :            : 
     791                 :          0 :                 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
     792                 :            :         }
     793                 :          0 :         spin_unlock_irqrestore(&entry->report_lock, flags);
     794         [ #  # ]:          0 :         hid_hw_wait(hid);
     795                 :            :         return 0;
     796                 :            : }
     797                 :            : 
     798                 :          0 : static ssize_t lg4ff_alternate_modes_show(struct device *dev, struct device_attribute *attr, char *buf)
     799                 :            : {
     800                 :          0 :         struct hid_device *hid = to_hid_device(dev);
     801                 :          0 :         struct lg4ff_device_entry *entry;
     802                 :          0 :         struct lg_drv_data *drv_data;
     803                 :          0 :         ssize_t count = 0;
     804                 :          0 :         int i;
     805                 :            : 
     806         [ #  # ]:          0 :         drv_data = hid_get_drvdata(hid);
     807         [ #  # ]:          0 :         if (!drv_data) {
     808                 :          0 :                 hid_err(hid, "Private driver data not found!\n");
     809                 :          0 :                 return 0;
     810                 :            :         }
     811                 :            : 
     812                 :          0 :         entry = drv_data->device_props;
     813         [ #  # ]:          0 :         if (!entry) {
     814                 :          0 :                 hid_err(hid, "Device properties not found!\n");
     815                 :          0 :                 return 0;
     816                 :            :         }
     817                 :            : 
     818         [ #  # ]:          0 :         if (!entry->wdata.real_name) {
     819                 :          0 :                 hid_err(hid, "NULL pointer to string\n");
     820                 :          0 :                 return 0;
     821                 :            :         }
     822                 :            : 
     823         [ #  # ]:          0 :         for (i = 0; i < LG4FF_MODE_MAX_IDX; i++) {
     824         [ #  # ]:          0 :                 if (entry->wdata.alternate_modes & BIT(i)) {
     825                 :            :                         /* Print tag and full name */
     826                 :          0 :                         count += scnprintf(buf + count, PAGE_SIZE - count, "%s: %s",
     827                 :            :                                            lg4ff_alternate_modes[i].tag,
     828         [ #  # ]:          0 :                                            !lg4ff_alternate_modes[i].product_id ? entry->wdata.real_name : lg4ff_alternate_modes[i].name);
     829         [ #  # ]:          0 :                         if (count >= PAGE_SIZE - 1)
     830                 :          0 :                                 return count;
     831                 :            : 
     832                 :            :                         /* Mark the currently active mode with an asterisk */
     833   [ #  #  #  # ]:          0 :                         if (lg4ff_alternate_modes[i].product_id == entry->wdata.product_id ||
     834         [ #  # ]:          0 :                             (lg4ff_alternate_modes[i].product_id == 0 && entry->wdata.product_id == entry->wdata.real_product_id))
     835                 :          0 :                                 count += scnprintf(buf + count, PAGE_SIZE - count, " *\n");
     836                 :            :                         else
     837                 :          0 :                                 count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
     838                 :            : 
     839         [ #  # ]:          0 :                         if (count >= PAGE_SIZE - 1)
     840                 :          0 :                                 return count;
     841                 :            :                 }
     842                 :            :         }
     843                 :            : 
     844                 :            :         return count;
     845                 :            : }
     846                 :            : 
     847                 :          0 : static ssize_t lg4ff_alternate_modes_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
     848                 :            : {
     849                 :          0 :         struct hid_device *hid = to_hid_device(dev);
     850                 :          0 :         struct lg4ff_device_entry *entry;
     851                 :          0 :         struct lg_drv_data *drv_data;
     852                 :          0 :         const struct lg4ff_compat_mode_switch *s;
     853                 :          0 :         u16 target_product_id = 0;
     854                 :          0 :         int i, ret;
     855                 :          0 :         char *lbuf;
     856                 :            : 
     857         [ #  # ]:          0 :         drv_data = hid_get_drvdata(hid);
     858         [ #  # ]:          0 :         if (!drv_data) {
     859                 :          0 :                 hid_err(hid, "Private driver data not found!\n");
     860                 :          0 :                 return -EINVAL;
     861                 :            :         }
     862                 :            : 
     863                 :          0 :         entry = drv_data->device_props;
     864         [ #  # ]:          0 :         if (!entry) {
     865                 :          0 :                 hid_err(hid, "Device properties not found!\n");
     866                 :          0 :                 return -EINVAL;
     867                 :            :         }
     868                 :            : 
     869                 :            :         /* Allow \n at the end of the input parameter */
     870                 :          0 :         lbuf = kasprintf(GFP_KERNEL, "%s", buf);
     871         [ #  # ]:          0 :         if (!lbuf)
     872                 :            :                 return -ENOMEM;
     873                 :            : 
     874                 :          0 :         i = strlen(lbuf);
     875         [ #  # ]:          0 :         if (lbuf[i-1] == '\n') {
     876         [ #  # ]:          0 :                 if (i == 1) {
     877                 :          0 :                         kfree(lbuf);
     878                 :          0 :                         return -EINVAL;
     879                 :            :                 }
     880                 :          0 :                 lbuf[i-1] = '\0';
     881                 :            :         }
     882                 :            : 
     883         [ #  # ]:          0 :         for (i = 0; i < LG4FF_MODE_MAX_IDX; i++) {
     884                 :          0 :                 const u16 mode_product_id = lg4ff_alternate_modes[i].product_id;
     885                 :          0 :                 const char *tag = lg4ff_alternate_modes[i].tag;
     886                 :            : 
     887         [ #  # ]:          0 :                 if (entry->wdata.alternate_modes & BIT(i)) {
     888         [ #  # ]:          0 :                         if (!strcmp(tag, lbuf)) {
     889         [ #  # ]:          0 :                                 if (!mode_product_id)
     890                 :          0 :                                         target_product_id = entry->wdata.real_product_id;
     891                 :            :                                 else
     892                 :            :                                         target_product_id = mode_product_id;
     893                 :            :                                 break;
     894                 :            :                         }
     895                 :            :                 }
     896                 :            :         }
     897                 :            : 
     898         [ #  # ]:          0 :         if (i == LG4FF_MODE_MAX_IDX) {
     899                 :          0 :                 hid_info(hid, "Requested mode \"%s\" is not supported by the device\n", lbuf);
     900                 :          0 :                 kfree(lbuf);
     901                 :          0 :                 return -EINVAL;
     902                 :            :         }
     903                 :          0 :         kfree(lbuf); /* Not needed anymore */
     904                 :            : 
     905         [ #  # ]:          0 :         if (target_product_id == entry->wdata.product_id) /* Nothing to do */
     906                 :          0 :                 return count;
     907                 :            : 
     908                 :            :         /* Automatic switching has to be disabled for the switch to DF-EX mode to work correctly */
     909   [ #  #  #  # ]:          0 :         if (target_product_id == USB_DEVICE_ID_LOGITECH_WHEEL && !lg4ff_no_autoswitch) {
     910                 :          0 :                 hid_info(hid, "\"%s\" cannot be switched to \"DF-EX\" mode. Load the \"hid_logitech\" module with \"lg4ff_no_autoswitch=1\" parameter set and try again\n",
     911                 :            :                          entry->wdata.real_name);
     912                 :          0 :                 return -EINVAL;
     913                 :            :         }
     914                 :            : 
     915                 :            :         /* Take care of hardware limitations */
     916   [ #  #  #  # ]:          0 :         if ((entry->wdata.real_product_id == USB_DEVICE_ID_LOGITECH_DFP_WHEEL || entry->wdata.real_product_id == USB_DEVICE_ID_LOGITECH_G25_WHEEL) &&
     917                 :            :             entry->wdata.product_id > target_product_id) {
     918                 :          0 :                 hid_info(hid, "\"%s\" cannot be switched back into \"%s\" mode\n", entry->wdata.real_name, lg4ff_alternate_modes[i].name);
     919                 :          0 :                 return -EINVAL;
     920                 :            :         }
     921                 :            : 
     922                 :          0 :         s = lg4ff_get_mode_switch_command(entry->wdata.real_product_id, target_product_id);
     923         [ #  # ]:          0 :         if (!s) {
     924                 :          0 :                 hid_err(hid, "Invalid target product ID %X\n", target_product_id);
     925                 :          0 :                 return -EINVAL;
     926                 :            :         }
     927                 :            : 
     928                 :          0 :         ret = lg4ff_switch_compatibility_mode(hid, s);
     929         [ #  # ]:          0 :         return (ret == 0 ? count : ret);
     930                 :            : }
     931                 :            : static DEVICE_ATTR(alternate_modes, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_alternate_modes_show, lg4ff_alternate_modes_store);
     932                 :            : 
     933                 :          0 : static ssize_t lg4ff_combine_show(struct device *dev, struct device_attribute *attr,
     934                 :            :                                 char *buf)
     935                 :            : {
     936                 :          0 :         struct hid_device *hid = to_hid_device(dev);
     937                 :          0 :         struct lg4ff_device_entry *entry;
     938                 :          0 :         struct lg_drv_data *drv_data;
     939                 :          0 :         size_t count;
     940                 :            : 
     941         [ #  # ]:          0 :         drv_data = hid_get_drvdata(hid);
     942         [ #  # ]:          0 :         if (!drv_data) {
     943                 :          0 :                 hid_err(hid, "Private driver data not found!\n");
     944                 :          0 :                 return 0;
     945                 :            :         }
     946                 :            : 
     947                 :          0 :         entry = drv_data->device_props;
     948         [ #  # ]:          0 :         if (!entry) {
     949                 :          0 :                 hid_err(hid, "Device properties not found!\n");
     950                 :          0 :                 return 0;
     951                 :            :         }
     952                 :            : 
     953                 :          0 :         count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->wdata.combine);
     954                 :          0 :         return count;
     955                 :            : }
     956                 :            : 
     957                 :          0 : static ssize_t lg4ff_combine_store(struct device *dev, struct device_attribute *attr,
     958                 :            :                                  const char *buf, size_t count)
     959                 :            : {
     960                 :          0 :         struct hid_device *hid = to_hid_device(dev);
     961                 :          0 :         struct lg4ff_device_entry *entry;
     962                 :          0 :         struct lg_drv_data *drv_data;
     963                 :          0 :         u16 combine = simple_strtoul(buf, NULL, 10);
     964                 :            : 
     965         [ #  # ]:          0 :         drv_data = hid_get_drvdata(hid);
     966         [ #  # ]:          0 :         if (!drv_data) {
     967                 :          0 :                 hid_err(hid, "Private driver data not found!\n");
     968                 :          0 :                 return -EINVAL;
     969                 :            :         }
     970                 :            : 
     971                 :          0 :         entry = drv_data->device_props;
     972         [ #  # ]:          0 :         if (!entry) {
     973                 :          0 :                 hid_err(hid, "Device properties not found!\n");
     974                 :          0 :                 return -EINVAL;
     975                 :            :         }
     976                 :            : 
     977                 :          0 :         if (combine > 1)
     978                 :            :                 combine = 1;
     979                 :            : 
     980                 :          0 :         entry->wdata.combine = combine;
     981                 :          0 :         return count;
     982                 :            : }
     983                 :            : static DEVICE_ATTR(combine_pedals, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_combine_show, lg4ff_combine_store);
     984                 :            : 
     985                 :            : /* Export the currently set range of the wheel */
     986                 :          0 : static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr,
     987                 :            :                                 char *buf)
     988                 :            : {
     989                 :          0 :         struct hid_device *hid = to_hid_device(dev);
     990                 :          0 :         struct lg4ff_device_entry *entry;
     991                 :          0 :         struct lg_drv_data *drv_data;
     992                 :          0 :         size_t count;
     993                 :            : 
     994         [ #  # ]:          0 :         drv_data = hid_get_drvdata(hid);
     995         [ #  # ]:          0 :         if (!drv_data) {
     996                 :          0 :                 hid_err(hid, "Private driver data not found!\n");
     997                 :          0 :                 return 0;
     998                 :            :         }
     999                 :            : 
    1000                 :          0 :         entry = drv_data->device_props;
    1001         [ #  # ]:          0 :         if (!entry) {
    1002                 :          0 :                 hid_err(hid, "Device properties not found!\n");
    1003                 :          0 :                 return 0;
    1004                 :            :         }
    1005                 :            : 
    1006                 :          0 :         count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->wdata.range);
    1007                 :          0 :         return count;
    1008                 :            : }
    1009                 :            : 
    1010                 :            : /* Set range to user specified value, call appropriate function
    1011                 :            :  * according to the type of the wheel */
    1012                 :          0 : static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *attr,
    1013                 :            :                                  const char *buf, size_t count)
    1014                 :            : {
    1015                 :          0 :         struct hid_device *hid = to_hid_device(dev);
    1016                 :          0 :         struct lg4ff_device_entry *entry;
    1017                 :          0 :         struct lg_drv_data *drv_data;
    1018                 :          0 :         u16 range = simple_strtoul(buf, NULL, 10);
    1019                 :            : 
    1020         [ #  # ]:          0 :         drv_data = hid_get_drvdata(hid);
    1021         [ #  # ]:          0 :         if (!drv_data) {
    1022                 :          0 :                 hid_err(hid, "Private driver data not found!\n");
    1023                 :          0 :                 return -EINVAL;
    1024                 :            :         }
    1025                 :            : 
    1026                 :          0 :         entry = drv_data->device_props;
    1027         [ #  # ]:          0 :         if (!entry) {
    1028                 :          0 :                 hid_err(hid, "Device properties not found!\n");
    1029                 :          0 :                 return -EINVAL;
    1030                 :            :         }
    1031                 :            : 
    1032         [ #  # ]:          0 :         if (range == 0)
    1033                 :          0 :                 range = entry->wdata.max_range;
    1034                 :            : 
    1035                 :            :         /* Check if the wheel supports range setting
    1036                 :            :          * and that the range is within limits for the wheel */
    1037   [ #  #  #  #  :          0 :         if (entry->wdata.set_range && range >= entry->wdata.min_range && range <= entry->wdata.max_range) {
                   #  # ]
    1038                 :          0 :                 entry->wdata.set_range(hid, range);
    1039                 :          0 :                 entry->wdata.range = range;
    1040                 :            :         }
    1041                 :            : 
    1042                 :          0 :         return count;
    1043                 :            : }
    1044                 :            : static DEVICE_ATTR(range, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_range_show, lg4ff_range_store);
    1045                 :            : 
    1046                 :          0 : static ssize_t lg4ff_real_id_show(struct device *dev, struct device_attribute *attr, char *buf)
    1047                 :            : {
    1048                 :          0 :         struct hid_device *hid = to_hid_device(dev);
    1049                 :          0 :         struct lg4ff_device_entry *entry;
    1050                 :          0 :         struct lg_drv_data *drv_data;
    1051                 :          0 :         size_t count;
    1052                 :            : 
    1053         [ #  # ]:          0 :         drv_data = hid_get_drvdata(hid);
    1054         [ #  # ]:          0 :         if (!drv_data) {
    1055                 :          0 :                 hid_err(hid, "Private driver data not found!\n");
    1056                 :          0 :                 return 0;
    1057                 :            :         }
    1058                 :            : 
    1059                 :          0 :         entry = drv_data->device_props;
    1060         [ #  # ]:          0 :         if (!entry) {
    1061                 :          0 :                 hid_err(hid, "Device properties not found!\n");
    1062                 :          0 :                 return 0;
    1063                 :            :         }
    1064                 :            : 
    1065   [ #  #  #  # ]:          0 :         if (!entry->wdata.real_tag || !entry->wdata.real_name) {
    1066                 :          0 :                 hid_err(hid, "NULL pointer to string\n");
    1067                 :          0 :                 return 0;
    1068                 :            :         }
    1069                 :            : 
    1070                 :          0 :         count = scnprintf(buf, PAGE_SIZE, "%s: %s\n", entry->wdata.real_tag, entry->wdata.real_name);
    1071                 :          0 :         return count;
    1072                 :            : }
    1073                 :            : 
    1074                 :          0 : static ssize_t lg4ff_real_id_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
    1075                 :            : {
    1076                 :            :         /* Real ID is a read-only value */
    1077                 :          0 :         return -EPERM;
    1078                 :            : }
    1079                 :            : static DEVICE_ATTR(real_id, S_IRUGO, lg4ff_real_id_show, lg4ff_real_id_store);
    1080                 :            : 
    1081                 :            : #ifdef CONFIG_LEDS_CLASS
    1082                 :          0 : static void lg4ff_set_leds(struct hid_device *hid, u8 leds)
    1083                 :            : {
    1084                 :          0 :         struct lg_drv_data *drv_data;
    1085                 :          0 :         struct lg4ff_device_entry *entry;
    1086                 :          0 :         unsigned long flags;
    1087                 :          0 :         s32 *value;
    1088                 :            : 
    1089         [ #  # ]:          0 :         drv_data = hid_get_drvdata(hid);
    1090         [ #  # ]:          0 :         if (!drv_data) {
    1091                 :          0 :                 hid_err(hid, "Private driver data not found!\n");
    1092                 :          0 :                 return;
    1093                 :            :         }
    1094                 :            : 
    1095                 :          0 :         entry = drv_data->device_props;
    1096         [ #  # ]:          0 :         if (!entry) {
    1097                 :          0 :                 hid_err(hid, "Device properties not found!\n");
    1098                 :          0 :                 return;
    1099                 :            :         }
    1100                 :          0 :         value = entry->report->field[0]->value;
    1101                 :            : 
    1102                 :          0 :         spin_lock_irqsave(&entry->report_lock, flags);
    1103                 :          0 :         value[0] = 0xf8;
    1104                 :          0 :         value[1] = 0x12;
    1105                 :          0 :         value[2] = leds;
    1106                 :          0 :         value[3] = 0x00;
    1107                 :          0 :         value[4] = 0x00;
    1108                 :          0 :         value[5] = 0x00;
    1109                 :          0 :         value[6] = 0x00;
    1110                 :          0 :         hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);
    1111                 :          0 :         spin_unlock_irqrestore(&entry->report_lock, flags);
    1112                 :            : }
    1113                 :            : 
    1114                 :          0 : static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,
    1115                 :            :                         enum led_brightness value)
    1116                 :            : {
    1117                 :          0 :         struct device *dev = led_cdev->dev->parent;
    1118                 :          0 :         struct hid_device *hid = to_hid_device(dev);
    1119         [ #  # ]:          0 :         struct lg_drv_data *drv_data = hid_get_drvdata(hid);
    1120                 :          0 :         struct lg4ff_device_entry *entry;
    1121                 :          0 :         int i, state = 0;
    1122                 :            : 
    1123         [ #  # ]:          0 :         if (!drv_data) {
    1124                 :          0 :                 hid_err(hid, "Device data not found.");
    1125                 :          0 :                 return;
    1126                 :            :         }
    1127                 :            : 
    1128                 :          0 :         entry = drv_data->device_props;
    1129                 :            : 
    1130         [ #  # ]:          0 :         if (!entry) {
    1131                 :          0 :                 hid_err(hid, "Device properties not found.");
    1132                 :          0 :                 return;
    1133                 :            :         }
    1134                 :            : 
    1135         [ #  # ]:          0 :         for (i = 0; i < 5; i++) {
    1136         [ #  # ]:          0 :                 if (led_cdev != entry->wdata.led[i])
    1137                 :          0 :                         continue;
    1138                 :          0 :                 state = (entry->wdata.led_state >> i) & 1;
    1139         [ #  # ]:          0 :                 if (value == LED_OFF && state) {
    1140                 :          0 :                         entry->wdata.led_state &= ~(1 << i);
    1141                 :          0 :                         lg4ff_set_leds(hid, entry->wdata.led_state);
    1142         [ #  # ]:          0 :                 } else if (value != LED_OFF && !state) {
    1143                 :          0 :                         entry->wdata.led_state |= 1 << i;
    1144                 :          0 :                         lg4ff_set_leds(hid, entry->wdata.led_state);
    1145                 :            :                 }
    1146                 :            :                 break;
    1147                 :            :         }
    1148                 :            : }
    1149                 :            : 
    1150                 :          0 : static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cdev)
    1151                 :            : {
    1152                 :          0 :         struct device *dev = led_cdev->dev->parent;
    1153                 :          0 :         struct hid_device *hid = to_hid_device(dev);
    1154         [ #  # ]:          0 :         struct lg_drv_data *drv_data = hid_get_drvdata(hid);
    1155                 :          0 :         struct lg4ff_device_entry *entry;
    1156                 :          0 :         int i, value = 0;
    1157                 :            : 
    1158         [ #  # ]:          0 :         if (!drv_data) {
    1159                 :          0 :                 hid_err(hid, "Device data not found.");
    1160                 :          0 :                 return LED_OFF;
    1161                 :            :         }
    1162                 :            : 
    1163                 :          0 :         entry = drv_data->device_props;
    1164                 :            : 
    1165         [ #  # ]:          0 :         if (!entry) {
    1166                 :          0 :                 hid_err(hid, "Device properties not found.");
    1167                 :          0 :                 return LED_OFF;
    1168                 :            :         }
    1169                 :            : 
    1170         [ #  # ]:          0 :         for (i = 0; i < 5; i++)
    1171         [ #  # ]:          0 :                 if (led_cdev == entry->wdata.led[i]) {
    1172                 :          0 :                         value = (entry->wdata.led_state >> i) & 1;
    1173                 :          0 :                         break;
    1174                 :            :                 }
    1175                 :            : 
    1176         [ #  # ]:          0 :         return value ? LED_FULL : LED_OFF;
    1177                 :            : }
    1178                 :            : #endif
    1179                 :            : 
    1180                 :          0 : static u16 lg4ff_identify_multimode_wheel(struct hid_device *hid, const u16 reported_product_id, const u16 bcdDevice)
    1181                 :            : {
    1182                 :          0 :         u32 current_mode;
    1183                 :          0 :         int i;
    1184                 :            : 
    1185                 :            :         /* identify current mode from USB PID */
    1186         [ #  # ]:          0 :         for (i = 1; i < ARRAY_SIZE(lg4ff_alternate_modes); i++) {
    1187         [ #  # ]:          0 :                 dbg_hid("Testing whether PID is %X\n", lg4ff_alternate_modes[i].product_id);
    1188         [ #  # ]:          0 :                 if (reported_product_id == lg4ff_alternate_modes[i].product_id)
    1189                 :            :                         break;
    1190                 :            :         }
    1191                 :            : 
    1192         [ #  # ]:          0 :         if (i == ARRAY_SIZE(lg4ff_alternate_modes))
    1193                 :            :                 return 0;
    1194                 :            : 
    1195                 :          0 :         current_mode = BIT(i);
    1196                 :            : 
    1197         [ #  # ]:          0 :         for (i = 0; i < ARRAY_SIZE(lg4ff_main_checklist); i++) {
    1198                 :          0 :                 const u16 mask = lg4ff_main_checklist[i]->mask;
    1199                 :          0 :                 const u16 result = lg4ff_main_checklist[i]->result;
    1200                 :          0 :                 const u16 real_product_id = lg4ff_main_checklist[i]->real_product_id;
    1201                 :            : 
    1202         [ #  # ]:          0 :                 if ((current_mode & lg4ff_main_checklist[i]->modes) && \
    1203         [ #  # ]:          0 :                                 (bcdDevice & mask) == result) {
    1204         [ #  # ]:          0 :                         dbg_hid("Found wheel with real PID %X whose reported PID is %X\n", real_product_id, reported_product_id);
    1205                 :          0 :                         return real_product_id;
    1206                 :            :                 }
    1207                 :            :         }
    1208                 :            : 
    1209                 :            :         /* No match found. This is either Driving Force or an unknown
    1210                 :            :          * wheel model, do not touch it */
    1211         [ #  # ]:          0 :         dbg_hid("Wheel with bcdDevice %X was not recognized as multimode wheel, leaving in its current mode\n", bcdDevice);
    1212                 :            :         return 0;
    1213                 :            : }
    1214                 :            : 
    1215                 :          0 : static int lg4ff_handle_multimode_wheel(struct hid_device *hid, u16 *real_product_id, const u16 bcdDevice)
    1216                 :            : {
    1217                 :          0 :         const u16 reported_product_id = hid->product;
    1218                 :          0 :         int ret;
    1219                 :            : 
    1220                 :          0 :         *real_product_id = lg4ff_identify_multimode_wheel(hid, reported_product_id, bcdDevice);
    1221                 :            :         /* Probed wheel is not a multimode wheel */
    1222         [ #  # ]:          0 :         if (!*real_product_id) {
    1223                 :          0 :                 *real_product_id = reported_product_id;
    1224         [ #  # ]:          0 :                 dbg_hid("Wheel is not a multimode wheel\n");
    1225                 :          0 :                 return LG4FF_MMODE_NOT_MULTIMODE;
    1226                 :            :         }
    1227                 :            : 
    1228                 :            :         /* Switch from "Driving Force" mode to native mode automatically.
    1229                 :            :          * Otherwise keep the wheel in its current mode */
    1230   [ #  #  #  # ]:          0 :         if (reported_product_id == USB_DEVICE_ID_LOGITECH_WHEEL &&
    1231                 :          0 :             reported_product_id != *real_product_id &&
    1232         [ #  # ]:          0 :             !lg4ff_no_autoswitch) {
    1233                 :          0 :                 const struct lg4ff_compat_mode_switch *s = lg4ff_get_mode_switch_command(*real_product_id, *real_product_id);
    1234                 :            : 
    1235         [ #  # ]:          0 :                 if (!s) {
    1236                 :          0 :                         hid_err(hid, "Invalid product id %X\n", *real_product_id);
    1237                 :          0 :                         return LG4FF_MMODE_NOT_MULTIMODE;
    1238                 :            :                 }
    1239                 :            : 
    1240                 :          0 :                 ret = lg4ff_switch_compatibility_mode(hid, s);
    1241         [ #  # ]:          0 :                 if (ret) {
    1242                 :            :                         /* Wheel could not have been switched to native mode,
    1243                 :            :                          * leave it in "Driving Force" mode and continue */
    1244                 :          0 :                         hid_err(hid, "Unable to switch wheel mode, errno %d\n", ret);
    1245                 :          0 :                         return LG4FF_MMODE_IS_MULTIMODE;
    1246                 :            :                 }
    1247                 :            :                 return LG4FF_MMODE_SWITCHED;
    1248                 :            :         }
    1249                 :            : 
    1250                 :            :         return LG4FF_MMODE_IS_MULTIMODE;
    1251                 :            : }
    1252                 :            : 
    1253                 :            : 
    1254                 :          0 : int lg4ff_init(struct hid_device *hid)
    1255                 :            : {
    1256                 :          0 :         struct hid_input *hidinput;
    1257                 :          0 :         struct input_dev *dev;
    1258                 :          0 :         struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
    1259                 :          0 :         struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
    1260                 :          0 :         const struct usb_device_descriptor *udesc = &(hid_to_usb_dev(hid)->descriptor);
    1261                 :          0 :         const u16 bcdDevice = le16_to_cpu(udesc->bcdDevice);
    1262                 :          0 :         const struct lg4ff_multimode_wheel *mmode_wheel = NULL;
    1263                 :          0 :         struct lg4ff_device_entry *entry;
    1264                 :          0 :         struct lg_drv_data *drv_data;
    1265                 :          0 :         int error, i, j;
    1266                 :          0 :         int mmode_ret, mmode_idx = -1;
    1267                 :          0 :         u16 real_product_id;
    1268                 :            : 
    1269         [ #  # ]:          0 :         if (list_empty(&hid->inputs)) {
    1270                 :          0 :                 hid_err(hid, "no inputs found\n");
    1271                 :          0 :                 return -ENODEV;
    1272                 :            :         }
    1273                 :          0 :         hidinput = list_entry(hid->inputs.next, struct hid_input, list);
    1274                 :          0 :         dev = hidinput->input;
    1275                 :            : 
    1276                 :            :         /* Check that the report looks ok */
    1277         [ #  # ]:          0 :         if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))
    1278                 :            :                 return -1;
    1279                 :            : 
    1280         [ #  # ]:          0 :         drv_data = hid_get_drvdata(hid);
    1281         [ #  # ]:          0 :         if (!drv_data) {
    1282                 :          0 :                 hid_err(hid, "Cannot add device, private driver data not allocated\n");
    1283                 :          0 :                 return -1;
    1284                 :            :         }
    1285                 :          0 :         entry = kzalloc(sizeof(*entry), GFP_KERNEL);
    1286         [ #  # ]:          0 :         if (!entry)
    1287                 :            :                 return -ENOMEM;
    1288                 :          0 :         spin_lock_init(&entry->report_lock);
    1289                 :          0 :         entry->report = report;
    1290                 :          0 :         drv_data->device_props = entry;
    1291                 :            : 
    1292                 :            :         /* Check if a multimode wheel has been connected and
    1293                 :            :          * handle it appropriately */
    1294                 :          0 :         mmode_ret = lg4ff_handle_multimode_wheel(hid, &real_product_id, bcdDevice);
    1295                 :            : 
    1296                 :            :         /* Wheel has been told to switch to native mode. There is no point in going on
    1297                 :            :          * with the initialization as the wheel will do a USB reset when it switches mode
    1298                 :            :          */
    1299         [ #  # ]:          0 :         if (mmode_ret == LG4FF_MMODE_SWITCHED)
    1300                 :            :                 return 0;
    1301         [ #  # ]:          0 :         else if (mmode_ret < 0) {
    1302                 :          0 :                 hid_err(hid, "Unable to switch device mode during initialization, errno %d\n", mmode_ret);
    1303                 :          0 :                 error = mmode_ret;
    1304                 :          0 :                 goto err_init;
    1305                 :            :         }
    1306                 :            : 
    1307                 :            :         /* Check what wheel has been connected */
    1308         [ #  # ]:          0 :         for (i = 0; i < ARRAY_SIZE(lg4ff_devices); i++) {
    1309         [ #  # ]:          0 :                 if (hid->product == lg4ff_devices[i].product_id) {
    1310         [ #  # ]:          0 :                         dbg_hid("Found compatible device, product ID %04X\n", lg4ff_devices[i].product_id);
    1311                 :            :                         break;
    1312                 :            :                 }
    1313                 :            :         }
    1314                 :            : 
    1315         [ #  # ]:          0 :         if (i == ARRAY_SIZE(lg4ff_devices)) {
    1316                 :          0 :                 hid_err(hid, "This device is flagged to be handled by the lg4ff module but this module does not know how to handle it. "
    1317                 :            :                              "Please report this as a bug to LKML, Simon Wood <simon@mungewell.org> or "
    1318                 :            :                              "Michal Maly <madcatxster@devoid-pointer.net>\n");
    1319                 :          0 :                 error = -1;
    1320                 :          0 :                 goto err_init;
    1321                 :            :         }
    1322                 :            : 
    1323         [ #  # ]:          0 :         if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) {
    1324         [ #  # ]:          0 :                 for (mmode_idx = 0; mmode_idx < ARRAY_SIZE(lg4ff_multimode_wheels); mmode_idx++) {
    1325         [ #  # ]:          0 :                         if (real_product_id == lg4ff_multimode_wheels[mmode_idx].product_id)
    1326                 :            :                                 break;
    1327                 :            :                 }
    1328                 :            : 
    1329         [ #  # ]:          0 :                 if (mmode_idx == ARRAY_SIZE(lg4ff_multimode_wheels)) {
    1330                 :          0 :                         hid_err(hid, "Device product ID %X is not listed as a multimode wheel", real_product_id);
    1331                 :          0 :                         error = -1;
    1332                 :          0 :                         goto err_init;
    1333                 :            :                 }
    1334                 :            :         }
    1335                 :            : 
    1336                 :            :         /* Set supported force feedback capabilities */
    1337         [ #  # ]:          0 :         for (j = 0; lg4ff_devices[i].ff_effects[j] >= 0; j++)
    1338                 :          0 :                 set_bit(lg4ff_devices[i].ff_effects[j], dev->ffbit);
    1339                 :            : 
    1340                 :          0 :         error = input_ff_create_memless(dev, NULL, lg4ff_play);
    1341                 :            : 
    1342         [ #  # ]:          0 :         if (error)
    1343                 :          0 :                 goto err_init;
    1344                 :            : 
    1345                 :            :         /* Initialize device properties */
    1346         [ #  # ]:          0 :         if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) {
    1347         [ #  # ]:          0 :                 BUG_ON(mmode_idx == -1);
    1348                 :          0 :                 mmode_wheel = &lg4ff_multimode_wheels[mmode_idx];
    1349                 :            :         }
    1350                 :          0 :         lg4ff_init_wheel_data(&entry->wdata, &lg4ff_devices[i], mmode_wheel, real_product_id);
    1351                 :            : 
    1352                 :            :         /* Check if autocentering is available and
    1353                 :            :          * set the centering force to zero by default */
    1354         [ #  # ]:          0 :         if (test_bit(FF_AUTOCENTER, dev->ffbit)) {
    1355                 :            :                 /* Formula Force EX expects different autocentering command */
    1356   [ #  #  #  # ]:          0 :                 if ((bcdDevice >> 8) == LG4FF_FFEX_REV_MAJ &&
    1357                 :            :                     (bcdDevice & 0xff) == LG4FF_FFEX_REV_MIN)
    1358                 :          0 :                         dev->ff->set_autocenter = lg4ff_set_autocenter_ffex;
    1359                 :            :                 else
    1360                 :          0 :                         dev->ff->set_autocenter = lg4ff_set_autocenter_default;
    1361                 :            : 
    1362                 :          0 :                 dev->ff->set_autocenter(dev, 0);
    1363                 :            :         }
    1364                 :            : 
    1365                 :            :         /* Create sysfs interface */
    1366                 :          0 :         error = device_create_file(&hid->dev, &dev_attr_combine_pedals);
    1367         [ #  # ]:          0 :         if (error)
    1368                 :          0 :                 hid_warn(hid, "Unable to create sysfs interface for \"combine\", errno %d\n", error);
    1369                 :          0 :         error = device_create_file(&hid->dev, &dev_attr_range);
    1370         [ #  # ]:          0 :         if (error)
    1371                 :          0 :                 hid_warn(hid, "Unable to create sysfs interface for \"range\", errno %d\n", error);
    1372         [ #  # ]:          0 :         if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) {
    1373                 :          0 :                 error = device_create_file(&hid->dev, &dev_attr_real_id);
    1374         [ #  # ]:          0 :                 if (error)
    1375                 :          0 :                         hid_warn(hid, "Unable to create sysfs interface for \"real_id\", errno %d\n", error);
    1376                 :          0 :                 error = device_create_file(&hid->dev, &dev_attr_alternate_modes);
    1377         [ #  # ]:          0 :                 if (error)
    1378                 :          0 :                         hid_warn(hid, "Unable to create sysfs interface for \"alternate_modes\", errno %d\n", error);
    1379                 :            :         }
    1380         [ #  # ]:          0 :         dbg_hid("sysfs interface created\n");
    1381                 :            : 
    1382                 :            :         /* Set the maximum range to start with */
    1383                 :          0 :         entry->wdata.range = entry->wdata.max_range;
    1384         [ #  # ]:          0 :         if (entry->wdata.set_range)
    1385                 :          0 :                 entry->wdata.set_range(hid, entry->wdata.range);
    1386                 :            : 
    1387                 :            : #ifdef CONFIG_LEDS_CLASS
    1388                 :            :         /* register led subsystem - G27/G29 only */
    1389                 :          0 :         entry->wdata.led_state = 0;
    1390         [ #  # ]:          0 :         for (j = 0; j < 5; j++)
    1391                 :          0 :                 entry->wdata.led[j] = NULL;
    1392                 :            : 
    1393         [ #  # ]:          0 :         if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_G27_WHEEL ||
    1394                 :            :                         lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_G29_WHEEL) {
    1395                 :          0 :                 struct led_classdev *led;
    1396                 :          0 :                 size_t name_sz;
    1397                 :          0 :                 char *name;
    1398                 :            : 
    1399                 :          0 :                 lg4ff_set_leds(hid, 0);
    1400                 :            : 
    1401         [ #  # ]:          0 :                 name_sz = strlen(dev_name(&hid->dev)) + 8;
    1402                 :            : 
    1403         [ #  # ]:          0 :                 for (j = 0; j < 5; j++) {
    1404                 :          0 :                         led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL);
    1405         [ #  # ]:          0 :                         if (!led) {
    1406                 :          0 :                                 hid_err(hid, "can't allocate memory for LED %d\n", j);
    1407                 :          0 :                                 goto err_leds;
    1408                 :            :                         }
    1409                 :            : 
    1410                 :          0 :                         name = (void *)(&led[1]);
    1411         [ #  # ]:          0 :                         snprintf(name, name_sz, "%s::RPM%d", dev_name(&hid->dev), j+1);
    1412                 :          0 :                         led->name = name;
    1413                 :          0 :                         led->brightness = 0;
    1414                 :          0 :                         led->max_brightness = 1;
    1415                 :          0 :                         led->brightness_get = lg4ff_led_get_brightness;
    1416                 :          0 :                         led->brightness_set = lg4ff_led_set_brightness;
    1417                 :            : 
    1418                 :          0 :                         entry->wdata.led[j] = led;
    1419                 :          0 :                         error = led_classdev_register(&hid->dev, led);
    1420                 :            : 
    1421         [ #  # ]:          0 :                         if (error) {
    1422                 :          0 :                                 hid_err(hid, "failed to register LED %d. Aborting.\n", j);
    1423                 :            : err_leds:
    1424                 :            :                                 /* Deregister LEDs (if any) */
    1425         [ #  # ]:          0 :                                 for (j = 0; j < 5; j++) {
    1426                 :          0 :                                         led = entry->wdata.led[j];
    1427                 :          0 :                                         entry->wdata.led[j] = NULL;
    1428         [ #  # ]:          0 :                                         if (!led)
    1429                 :          0 :                                                 continue;
    1430                 :          0 :                                         led_classdev_unregister(led);
    1431                 :          0 :                                         kfree(led);
    1432                 :            :                                 }
    1433                 :          0 :                                 goto out;       /* Let the driver continue without LEDs */
    1434                 :            :                         }
    1435                 :            :                 }
    1436                 :            :         }
    1437                 :          0 : out:
    1438                 :            : #endif
    1439                 :          0 :         hid_info(hid, "Force feedback support for Logitech Gaming Wheels\n");
    1440                 :          0 :         return 0;
    1441                 :            : 
    1442                 :          0 : err_init:
    1443                 :          0 :         drv_data->device_props = NULL;
    1444                 :          0 :         kfree(entry);
    1445                 :          0 :         return error;
    1446                 :            : }
    1447                 :            : 
    1448                 :          0 : int lg4ff_deinit(struct hid_device *hid)
    1449                 :            : {
    1450                 :          0 :         struct lg4ff_device_entry *entry;
    1451                 :          0 :         struct lg_drv_data *drv_data;
    1452                 :            : 
    1453         [ #  # ]:          0 :         drv_data = hid_get_drvdata(hid);
    1454         [ #  # ]:          0 :         if (!drv_data) {
    1455                 :          0 :                 hid_err(hid, "Error while deinitializing device, no private driver data.\n");
    1456                 :          0 :                 return -1;
    1457                 :            :         }
    1458                 :          0 :         entry = drv_data->device_props;
    1459         [ #  # ]:          0 :         if (!entry)
    1460                 :          0 :                 goto out; /* Nothing more to do */
    1461                 :            : 
    1462                 :            :         /* Multimode devices will have at least the "MODE_NATIVE" bit set */
    1463         [ #  # ]:          0 :         if (entry->wdata.alternate_modes) {
    1464                 :          0 :                 device_remove_file(&hid->dev, &dev_attr_real_id);
    1465                 :          0 :                 device_remove_file(&hid->dev, &dev_attr_alternate_modes);
    1466                 :            :         }
    1467                 :            : 
    1468                 :          0 :         device_remove_file(&hid->dev, &dev_attr_combine_pedals);
    1469                 :          0 :         device_remove_file(&hid->dev, &dev_attr_range);
    1470                 :            : #ifdef CONFIG_LEDS_CLASS
    1471                 :            :         {
    1472                 :          0 :                 int j;
    1473                 :          0 :                 struct led_classdev *led;
    1474                 :            : 
    1475                 :            :                 /* Deregister LEDs (if any) */
    1476         [ #  # ]:          0 :                 for (j = 0; j < 5; j++) {
    1477                 :            : 
    1478                 :          0 :                         led = entry->wdata.led[j];
    1479                 :          0 :                         entry->wdata.led[j] = NULL;
    1480         [ #  # ]:          0 :                         if (!led)
    1481                 :          0 :                                 continue;
    1482                 :          0 :                         led_classdev_unregister(led);
    1483                 :          0 :                         kfree(led);
    1484                 :            :                 }
    1485                 :            :         }
    1486                 :            : #endif
    1487                 :          0 :         drv_data->device_props = NULL;
    1488                 :            : 
    1489                 :          0 :         kfree(entry);
    1490                 :          0 : out:
    1491         [ #  # ]:          0 :         dbg_hid("Device successfully unregistered\n");
    1492                 :            :         return 0;
    1493                 :            : }

Generated by: LCOV version 1.14