LCOV - code coverage report
Current view: top level - drivers/hid - hid-lgff.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 0 74 0.0 %
Date: 2022-03-28 16:04:14 Functions: 0 3 0.0 %
Branches: 0 26 0.0 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0-or-later
       2                 :            : /*
       3                 :            :  * Force feedback support for hid-compliant for some of the devices from
       4                 :            :  * Logitech, namely:
       5                 :            :  * - WingMan Cordless RumblePad
       6                 :            :  * - WingMan Force 3D
       7                 :            :  *
       8                 :            :  *  Copyright (c) 2002-2004 Johann Deneux
       9                 :            :  *  Copyright (c) 2006 Anssi Hannula <anssi.hannula@gmail.com>
      10                 :            :  */
      11                 :            : 
      12                 :            : /*
      13                 :            :  *
      14                 :            :  * Should you need to contact me, the author, you can do so by
      15                 :            :  * e-mail - mail your message to <johann.deneux@it.uu.se>
      16                 :            :  */
      17                 :            : 
      18                 :            : #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
      19                 :            : 
      20                 :            : #include <linux/input.h>
      21                 :            : #include <linux/hid.h>
      22                 :            : 
      23                 :            : #include "hid-lg.h"
      24                 :            : 
      25                 :            : struct dev_type {
      26                 :            :         u16 idVendor;
      27                 :            :         u16 idProduct;
      28                 :            :         const signed short *ff;
      29                 :            : };
      30                 :            : 
      31                 :            : static const signed short ff_rumble[] = {
      32                 :            :         FF_RUMBLE,
      33                 :            :         -1
      34                 :            : };
      35                 :            : 
      36                 :            : static const signed short ff_joystick[] = {
      37                 :            :         FF_CONSTANT,
      38                 :            :         -1
      39                 :            : };
      40                 :            : 
      41                 :            : static const signed short ff_joystick_ac[] = {
      42                 :            :         FF_CONSTANT,
      43                 :            :         FF_AUTOCENTER,
      44                 :            :         -1
      45                 :            : };
      46                 :            : 
      47                 :            : static const struct dev_type devices[] = {
      48                 :            :         { 0x046d, 0xc211, ff_rumble },
      49                 :            :         { 0x046d, 0xc219, ff_rumble },
      50                 :            :         { 0x046d, 0xc283, ff_joystick },
      51                 :            :         { 0x046d, 0xc286, ff_joystick_ac },
      52                 :            :         { 0x046d, 0xc287, ff_joystick_ac },
      53                 :            :         { 0x046d, 0xc293, ff_joystick },
      54                 :            :         { 0x046d, 0xc295, ff_joystick },
      55                 :            : };
      56                 :            : 
      57                 :          0 : static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
      58                 :            : {
      59      [ #  #  # ]:          0 :         struct hid_device *hid = input_get_drvdata(dev);
      60                 :          0 :         struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
      61                 :          0 :         struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
      62                 :          0 :         int x, y;
      63                 :          0 :         unsigned int left, right;
      64                 :            : 
      65                 :            : #define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
      66                 :            : 
      67      [ #  #  # ]:          0 :         switch (effect->type) {
      68                 :          0 :         case FF_CONSTANT:
      69                 :          0 :                 x = effect->u.ramp.start_level + 0x7f;       /* 0x7f is center */
      70                 :          0 :                 y = effect->u.ramp.end_level + 0x7f;
      71                 :          0 :                 CLAMP(x);
      72                 :          0 :                 CLAMP(y);
      73                 :          0 :                 report->field[0]->value[0] = 0x51;
      74                 :          0 :                 report->field[0]->value[1] = 0x08;
      75                 :          0 :                 report->field[0]->value[2] = x;
      76                 :          0 :                 report->field[0]->value[3] = y;
      77         [ #  # ]:          0 :                 dbg_hid("(x, y)=(%04x, %04x)\n", x, y);
      78                 :          0 :                 hid_hw_request(hid, report, HID_REQ_SET_REPORT);
      79                 :          0 :                 break;
      80                 :            : 
      81                 :          0 :         case FF_RUMBLE:
      82                 :          0 :                 right = effect->u.rumble.strong_magnitude;
      83                 :          0 :                 left = effect->u.rumble.weak_magnitude;
      84                 :          0 :                 right = right * 0xff / 0xffff;
      85                 :          0 :                 left = left * 0xff / 0xffff;
      86                 :          0 :                 CLAMP(left);
      87                 :          0 :                 CLAMP(right);
      88                 :          0 :                 report->field[0]->value[0] = 0x42;
      89                 :          0 :                 report->field[0]->value[1] = 0x00;
      90                 :          0 :                 report->field[0]->value[2] = left;
      91                 :          0 :                 report->field[0]->value[3] = right;
      92         [ #  # ]:          0 :                 dbg_hid("(left, right)=(%04x, %04x)\n", left, right);
      93                 :          0 :                 hid_hw_request(hid, report, HID_REQ_SET_REPORT);
      94                 :          0 :                 break;
      95                 :            :         }
      96                 :          0 :         return 0;
      97                 :            : }
      98                 :            : 
      99                 :          0 : static void hid_lgff_set_autocenter(struct input_dev *dev, u16 magnitude)
     100                 :            : {
     101                 :          0 :         struct hid_device *hid = input_get_drvdata(dev);
     102                 :          0 :         struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
     103                 :          0 :         struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
     104                 :          0 :         __s32 *value = report->field[0]->value;
     105                 :          0 :         magnitude = (magnitude >> 12) & 0xf;
     106                 :          0 :         *value++ = 0xfe;
     107                 :          0 :         *value++ = 0x0d;
     108                 :          0 :         *value++ = magnitude;   /* clockwise strength */
     109                 :          0 :         *value++ = magnitude;   /* counter-clockwise strength */
     110                 :          0 :         *value++ = 0x80;
     111                 :          0 :         *value++ = 0x00;
     112                 :          0 :         *value = 0x00;
     113                 :          0 :         hid_hw_request(hid, report, HID_REQ_SET_REPORT);
     114                 :          0 : }
     115                 :            : 
     116                 :          0 : int lgff_init(struct hid_device* hid)
     117                 :            : {
     118                 :          0 :         struct hid_input *hidinput;
     119                 :          0 :         struct input_dev *dev;
     120                 :          0 :         const signed short *ff_bits = ff_joystick;
     121                 :          0 :         int error;
     122                 :          0 :         int i;
     123                 :            : 
     124         [ #  # ]:          0 :         if (list_empty(&hid->inputs)) {
     125                 :          0 :                 hid_err(hid, "no inputs found\n");
     126                 :          0 :                 return -ENODEV;
     127                 :            :         }
     128                 :          0 :         hidinput = list_entry(hid->inputs.next, struct hid_input, list);
     129                 :          0 :         dev = hidinput->input;
     130                 :            : 
     131                 :            :         /* Check that the report looks ok */
     132         [ #  # ]:          0 :         if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))
     133                 :            :                 return -ENODEV;
     134                 :            : 
     135         [ #  # ]:          0 :         for (i = 0; i < ARRAY_SIZE(devices); i++) {
     136         [ #  # ]:          0 :                 if (dev->id.vendor == devices[i].idVendor &&
     137         [ #  # ]:          0 :                     dev->id.product == devices[i].idProduct) {
     138                 :          0 :                         ff_bits = devices[i].ff;
     139                 :          0 :                         break;
     140                 :            :                 }
     141                 :            :         }
     142                 :            : 
     143         [ #  # ]:          0 :         for (i = 0; ff_bits[i] >= 0; i++)
     144                 :          0 :                 set_bit(ff_bits[i], dev->ffbit);
     145                 :            : 
     146                 :          0 :         error = input_ff_create_memless(dev, NULL, hid_lgff_play);
     147         [ #  # ]:          0 :         if (error)
     148                 :            :                 return error;
     149                 :            : 
     150         [ #  # ]:          0 :         if ( test_bit(FF_AUTOCENTER, dev->ffbit) )
     151                 :          0 :                 dev->ff->set_autocenter = hid_lgff_set_autocenter;
     152                 :            : 
     153                 :          0 :         pr_info("Force feedback for Logitech force feedback devices by Johann Deneux <johann.deneux@it.uu.se>\n");
     154                 :            : 
     155                 :          0 :         return 0;
     156                 :            : }

Generated by: LCOV version 1.14