LCOV - code coverage report
Current view: top level - drivers/hid - hid-pl.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 1 92 1.1 %
Date: 2022-04-01 14:17:54 Functions: 1 5 20.0 %
Branches: 0 36 0.0 %

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: GPL-2.0-or-later
       2                 :            : /*
       3                 :            :  *  Force feedback support for PantherLord/GreenAsia based devices
       4                 :            :  *
       5                 :            :  *  The devices are distributed under various names and the same USB device ID
       6                 :            :  *  can be used in both adapters and actual game controllers.
       7                 :            :  *
       8                 :            :  *  0810:0001 "Twin USB Joystick"
       9                 :            :  *   - tested with PantherLord USB/PS2 2in1 Adapter
      10                 :            :  *   - contains two reports, one for each port (HID_QUIRK_MULTI_INPUT)
      11                 :            :  *
      12                 :            :  *  0e8f:0003 "GreenAsia Inc.    USB Joystick     "
      13                 :            :  *   - tested with König Gaming gamepad
      14                 :            :  *
      15                 :            :  *  0e8f:0003 "GASIA USB Gamepad"
      16                 :            :  *   - another version of the König gamepad
      17                 :            :  *
      18                 :            :  *  0f30:0111 "Saitek Color Rumble Pad"
      19                 :            :  *
      20                 :            :  *  Copyright (c) 2007, 2009 Anssi Hannula <anssi.hannula@gmail.com>
      21                 :            :  */
      22                 :            : 
      23                 :            : /*
      24                 :            :  */
      25                 :            : 
      26                 :            : 
      27                 :            : /* #define DEBUG */
      28                 :            : 
      29                 :            : #define debug(format, arg...) pr_debug("hid-plff: " format "\n" , ## arg)
      30                 :            : 
      31                 :            : #include <linux/input.h>
      32                 :            : #include <linux/slab.h>
      33                 :            : #include <linux/module.h>
      34                 :            : #include <linux/hid.h>
      35                 :            : 
      36                 :            : #include "hid-ids.h"
      37                 :            : 
      38                 :            : #ifdef CONFIG_PANTHERLORD_FF
      39                 :            : 
      40                 :            : struct plff_device {
      41                 :            :         struct hid_report *report;
      42                 :            :         s32 maxval;
      43                 :            :         s32 *strong;
      44                 :            :         s32 *weak;
      45                 :            : };
      46                 :            : 
      47                 :          0 : static int hid_plff_play(struct input_dev *dev, void *data,
      48                 :            :                          struct ff_effect *effect)
      49                 :            : {
      50                 :          0 :         struct hid_device *hid = input_get_drvdata(dev);
      51                 :          0 :         struct plff_device *plff = data;
      52                 :          0 :         int left, right;
      53                 :            : 
      54                 :          0 :         left = effect->u.rumble.strong_magnitude;
      55                 :          0 :         right = effect->u.rumble.weak_magnitude;
      56                 :          0 :         debug("called with 0x%04x 0x%04x", left, right);
      57                 :            : 
      58                 :          0 :         left = left * plff->maxval / 0xffff;
      59                 :          0 :         right = right * plff->maxval / 0xffff;
      60                 :            : 
      61                 :          0 :         *plff->strong = left;
      62                 :          0 :         *plff->weak = right;
      63                 :          0 :         debug("running with 0x%02x 0x%02x", left, right);
      64                 :          0 :         hid_hw_request(hid, plff->report, HID_REQ_SET_REPORT);
      65                 :            : 
      66                 :          0 :         return 0;
      67                 :            : }
      68                 :            : 
      69                 :          0 : static int plff_init(struct hid_device *hid)
      70                 :            : {
      71                 :          0 :         struct plff_device *plff;
      72                 :          0 :         struct hid_report *report;
      73                 :          0 :         struct hid_input *hidinput;
      74                 :          0 :         struct list_head *report_list =
      75                 :            :                         &hid->report_enum[HID_OUTPUT_REPORT].report_list;
      76                 :          0 :         struct list_head *report_ptr = report_list;
      77                 :          0 :         struct input_dev *dev;
      78                 :          0 :         int error;
      79                 :          0 :         s32 maxval;
      80                 :          0 :         s32 *strong;
      81                 :          0 :         s32 *weak;
      82                 :            : 
      83                 :            :         /* The device contains one output report per physical device, all
      84                 :            :            containing 1 field, which contains 4 ff00.0002 usages and 4 16bit
      85                 :            :            absolute values.
      86                 :            : 
      87                 :            :            The input reports also contain a field which contains
      88                 :            :            8 ff00.0001 usages and 8 boolean values. Their meaning is
      89                 :            :            currently unknown.
      90                 :            :            
      91                 :            :            A version of the 0e8f:0003 exists that has all the values in
      92                 :            :            separate fields and misses the extra input field, thus resembling
      93                 :            :            Zeroplus (hid-zpff) devices.
      94                 :            :         */
      95                 :            : 
      96         [ #  # ]:          0 :         if (list_empty(report_list)) {
      97                 :          0 :                 hid_err(hid, "no output reports found\n");
      98                 :          0 :                 return -ENODEV;
      99                 :            :         }
     100                 :            : 
     101         [ #  # ]:          0 :         list_for_each_entry(hidinput, &hid->inputs, list) {
     102                 :            : 
     103                 :          0 :                 report_ptr = report_ptr->next;
     104                 :            : 
     105         [ #  # ]:          0 :                 if (report_ptr == report_list) {
     106                 :          0 :                         hid_err(hid, "required output report is missing\n");
     107                 :          0 :                         return -ENODEV;
     108                 :            :                 }
     109                 :            : 
     110                 :          0 :                 report = list_entry(report_ptr, struct hid_report, list);
     111         [ #  # ]:          0 :                 if (report->maxfield < 1) {
     112                 :          0 :                         hid_err(hid, "no fields in the report\n");
     113                 :          0 :                         return -ENODEV;
     114                 :            :                 }
     115                 :            : 
     116                 :          0 :                 maxval = 0x7f;
     117         [ #  # ]:          0 :                 if (report->field[0]->report_count >= 4) {
     118                 :          0 :                         report->field[0]->value[0] = 0x00;
     119                 :          0 :                         report->field[0]->value[1] = 0x00;
     120                 :          0 :                         strong = &report->field[0]->value[2];
     121                 :          0 :                         weak = &report->field[0]->value[3];
     122                 :          0 :                         debug("detected single-field device");
     123         [ #  # ]:          0 :                 } else if (report->field[0]->maxusage == 1 &&
     124         [ #  # ]:          0 :                            report->field[0]->usage[0].hid ==
     125         [ #  # ]:          0 :                                 (HID_UP_LED | 0x43) &&
     126         [ #  # ]:          0 :                            report->maxfield >= 4 &&
     127                 :          0 :                            report->field[0]->report_count >= 1 &&
     128         [ #  # ]:          0 :                            report->field[1]->report_count >= 1 &&
     129         [ #  # ]:          0 :                            report->field[2]->report_count >= 1 &&
     130         [ #  # ]:          0 :                            report->field[3]->report_count >= 1) {
     131                 :          0 :                         report->field[0]->value[0] = 0x00;
     132                 :          0 :                         report->field[1]->value[0] = 0x00;
     133                 :          0 :                         strong = &report->field[2]->value[0];
     134                 :          0 :                         weak = &report->field[3]->value[0];
     135         [ #  # ]:          0 :                         if (hid->vendor == USB_VENDOR_ID_JESS2)
     136                 :          0 :                                 maxval = 0xff;
     137                 :            :                         debug("detected 4-field device");
     138                 :            :                 } else {
     139                 :          0 :                         hid_err(hid, "not enough fields or values\n");
     140                 :          0 :                         return -ENODEV;
     141                 :            :                 }
     142                 :            : 
     143                 :          0 :                 plff = kzalloc(sizeof(struct plff_device), GFP_KERNEL);
     144         [ #  # ]:          0 :                 if (!plff)
     145                 :            :                         return -ENOMEM;
     146                 :            : 
     147                 :          0 :                 dev = hidinput->input;
     148                 :            : 
     149                 :          0 :                 set_bit(FF_RUMBLE, dev->ffbit);
     150                 :            : 
     151                 :          0 :                 error = input_ff_create_memless(dev, plff, hid_plff_play);
     152         [ #  # ]:          0 :                 if (error) {
     153                 :          0 :                         kfree(plff);
     154                 :          0 :                         return error;
     155                 :            :                 }
     156                 :            : 
     157                 :          0 :                 plff->report = report;
     158                 :          0 :                 plff->strong = strong;
     159                 :          0 :                 plff->weak = weak;
     160                 :          0 :                 plff->maxval = maxval;
     161                 :            : 
     162                 :          0 :                 *strong = 0x00;
     163                 :          0 :                 *weak = 0x00;
     164                 :          0 :                 hid_hw_request(hid, plff->report, HID_REQ_SET_REPORT);
     165                 :            :         }
     166                 :            : 
     167                 :          0 :         hid_info(hid, "Force feedback for PantherLord/GreenAsia devices by Anssi Hannula <anssi.hannula@gmail.com>\n");
     168                 :            : 
     169                 :          0 :         return 0;
     170                 :            : }
     171                 :            : #else
     172                 :            : static inline int plff_init(struct hid_device *hid)
     173                 :            : {
     174                 :            :         return 0;
     175                 :            : }
     176                 :            : #endif
     177                 :            : 
     178                 :          0 : static int pl_probe(struct hid_device *hdev, const struct hid_device_id *id)
     179                 :            : {
     180                 :          0 :         int ret;
     181                 :            : 
     182         [ #  # ]:          0 :         if (id->driver_data)
     183                 :          0 :                 hdev->quirks |= HID_QUIRK_MULTI_INPUT;
     184                 :            : 
     185                 :          0 :         ret = hid_parse(hdev);
     186         [ #  # ]:          0 :         if (ret) {
     187                 :          0 :                 hid_err(hdev, "parse failed\n");
     188                 :          0 :                 goto err;
     189                 :            :         }
     190                 :            : 
     191                 :          0 :         ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
     192         [ #  # ]:          0 :         if (ret) {
     193                 :          0 :                 hid_err(hdev, "hw start failed\n");
     194                 :          0 :                 goto err;
     195                 :            :         }
     196                 :            : 
     197                 :          0 :         plff_init(hdev);
     198                 :            : 
     199                 :          0 :         return 0;
     200                 :            : err:
     201                 :            :         return ret;
     202                 :            : }
     203                 :            : 
     204                 :            : static const struct hid_device_id pl_devices[] = {
     205                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR),
     206                 :            :                 .driver_data = 1 }, /* Twin USB Joystick */
     207                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR),
     208                 :            :                 .driver_data = 1 }, /* Twin USB Joystick */
     209                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003), },
     210                 :            :         { HID_USB_DEVICE(USB_VENDOR_ID_JESS2, USB_DEVICE_ID_JESS2_COLOR_RUMBLE_PAD), },
     211                 :            :         { }
     212                 :            : };
     213                 :            : MODULE_DEVICE_TABLE(hid, pl_devices);
     214                 :            : 
     215                 :            : static struct hid_driver pl_driver = {
     216                 :            :         .name = "pantherlord",
     217                 :            :         .id_table = pl_devices,
     218                 :            :         .probe = pl_probe,
     219                 :            : };
     220                 :         11 : module_hid_driver(pl_driver);
     221                 :            : 
     222                 :            : MODULE_LICENSE("GPL");

Generated by: LCOV version 1.14