cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hid-pl.c (5593B)


      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
     40struct plff_device {
     41	struct hid_report *report;
     42	s32 maxval;
     43	s32 *strong;
     44	s32 *weak;
     45};
     46
     47static int hid_plff_play(struct input_dev *dev, void *data,
     48			 struct ff_effect *effect)
     49{
     50	struct hid_device *hid = input_get_drvdata(dev);
     51	struct plff_device *plff = data;
     52	int left, right;
     53
     54	left = effect->u.rumble.strong_magnitude;
     55	right = effect->u.rumble.weak_magnitude;
     56	debug("called with 0x%04x 0x%04x", left, right);
     57
     58	left = left * plff->maxval / 0xffff;
     59	right = right * plff->maxval / 0xffff;
     60
     61	*plff->strong = left;
     62	*plff->weak = right;
     63	debug("running with 0x%02x 0x%02x", left, right);
     64	hid_hw_request(hid, plff->report, HID_REQ_SET_REPORT);
     65
     66	return 0;
     67}
     68
     69static int plff_init(struct hid_device *hid)
     70{
     71	struct plff_device *plff;
     72	struct hid_report *report;
     73	struct hid_input *hidinput;
     74	struct list_head *report_list =
     75			&hid->report_enum[HID_OUTPUT_REPORT].report_list;
     76	struct list_head *report_ptr = report_list;
     77	struct input_dev *dev;
     78	int error;
     79	s32 maxval;
     80	s32 *strong;
     81	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	if (list_empty(report_list)) {
     97		hid_err(hid, "no output reports found\n");
     98		return -ENODEV;
     99	}
    100
    101	list_for_each_entry(hidinput, &hid->inputs, list) {
    102
    103		report_ptr = report_ptr->next;
    104
    105		if (report_ptr == report_list) {
    106			hid_err(hid, "required output report is missing\n");
    107			return -ENODEV;
    108		}
    109
    110		report = list_entry(report_ptr, struct hid_report, list);
    111		if (report->maxfield < 1) {
    112			hid_err(hid, "no fields in the report\n");
    113			return -ENODEV;
    114		}
    115
    116		maxval = 0x7f;
    117		if (report->field[0]->report_count >= 4) {
    118			report->field[0]->value[0] = 0x00;
    119			report->field[0]->value[1] = 0x00;
    120			strong = &report->field[0]->value[2];
    121			weak = &report->field[0]->value[3];
    122			debug("detected single-field device");
    123		} else if (report->field[0]->maxusage == 1 &&
    124			   report->field[0]->usage[0].hid ==
    125				(HID_UP_LED | 0x43) &&
    126			   report->maxfield >= 4 &&
    127			   report->field[0]->report_count >= 1 &&
    128			   report->field[1]->report_count >= 1 &&
    129			   report->field[2]->report_count >= 1 &&
    130			   report->field[3]->report_count >= 1) {
    131			report->field[0]->value[0] = 0x00;
    132			report->field[1]->value[0] = 0x00;
    133			strong = &report->field[2]->value[0];
    134			weak = &report->field[3]->value[0];
    135			if (hid->vendor == USB_VENDOR_ID_JESS2)
    136				maxval = 0xff;
    137			debug("detected 4-field device");
    138		} else {
    139			hid_err(hid, "not enough fields or values\n");
    140			return -ENODEV;
    141		}
    142
    143		plff = kzalloc(sizeof(struct plff_device), GFP_KERNEL);
    144		if (!plff)
    145			return -ENOMEM;
    146
    147		dev = hidinput->input;
    148
    149		set_bit(FF_RUMBLE, dev->ffbit);
    150
    151		error = input_ff_create_memless(dev, plff, hid_plff_play);
    152		if (error) {
    153			kfree(plff);
    154			return error;
    155		}
    156
    157		plff->report = report;
    158		plff->strong = strong;
    159		plff->weak = weak;
    160		plff->maxval = maxval;
    161
    162		*strong = 0x00;
    163		*weak = 0x00;
    164		hid_hw_request(hid, plff->report, HID_REQ_SET_REPORT);
    165	}
    166
    167	hid_info(hid, "Force feedback for PantherLord/GreenAsia devices by Anssi Hannula <anssi.hannula@gmail.com>\n");
    168
    169	return 0;
    170}
    171#else
    172static inline int plff_init(struct hid_device *hid)
    173{
    174	return 0;
    175}
    176#endif
    177
    178static int pl_probe(struct hid_device *hdev, const struct hid_device_id *id)
    179{
    180	int ret;
    181
    182	if (id->driver_data)
    183		hdev->quirks |= HID_QUIRK_MULTI_INPUT;
    184
    185	ret = hid_parse(hdev);
    186	if (ret) {
    187		hid_err(hdev, "parse failed\n");
    188		goto err;
    189	}
    190
    191	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
    192	if (ret) {
    193		hid_err(hdev, "hw start failed\n");
    194		goto err;
    195	}
    196
    197	plff_init(hdev);
    198
    199	return 0;
    200err:
    201	return ret;
    202}
    203
    204static 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};
    213MODULE_DEVICE_TABLE(hid, pl_devices);
    214
    215static struct hid_driver pl_driver = {
    216	.name = "pantherlord",
    217	.id_table = pl_devices,
    218	.probe = pl_probe,
    219};
    220module_hid_driver(pl_driver);
    221
    222MODULE_LICENSE("GPL");