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-tmff.c (7086B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Force feedback support for various HID compliant devices by ThrustMaster:
      4 *    ThrustMaster FireStorm Dual Power 2
      5 * and possibly others whose device ids haven't been added.
      6 *
      7 *  Modified to support ThrustMaster devices by Zinx Verituse
      8 *  on 2003-01-25 from the Logitech force feedback driver,
      9 *  which is by Johann Deneux.
     10 *
     11 *  Copyright (c) 2003 Zinx Verituse <zinx@epicsol.org>
     12 *  Copyright (c) 2002 Johann Deneux
     13 */
     14
     15/*
     16 */
     17
     18#include <linux/hid.h>
     19#include <linux/input.h>
     20#include <linux/slab.h>
     21#include <linux/module.h>
     22
     23#include "hid-ids.h"
     24
     25#define THRUSTMASTER_DEVICE_ID_2_IN_1_DT	0xb320
     26
     27static const signed short ff_rumble[] = {
     28	FF_RUMBLE,
     29	-1
     30};
     31
     32static const signed short ff_joystick[] = {
     33	FF_CONSTANT,
     34	-1
     35};
     36
     37#ifdef CONFIG_THRUSTMASTER_FF
     38
     39/* Usages for thrustmaster devices I know about */
     40#define THRUSTMASTER_USAGE_FF	(HID_UP_GENDESK | 0xbb)
     41
     42struct tmff_device {
     43	struct hid_report *report;
     44	struct hid_field *ff_field;
     45};
     46
     47/* Changes values from 0 to 0xffff into values from minimum to maximum */
     48static inline int tmff_scale_u16(unsigned int in, int minimum, int maximum)
     49{
     50	int ret;
     51
     52	ret = (in * (maximum - minimum) / 0xffff) + minimum;
     53	if (ret < minimum)
     54		return minimum;
     55	if (ret > maximum)
     56		return maximum;
     57	return ret;
     58}
     59
     60/* Changes values from -0x80 to 0x7f into values from minimum to maximum */
     61static inline int tmff_scale_s8(int in, int minimum, int maximum)
     62{
     63	int ret;
     64
     65	ret = (((in + 0x80) * (maximum - minimum)) / 0xff) + minimum;
     66	if (ret < minimum)
     67		return minimum;
     68	if (ret > maximum)
     69		return maximum;
     70	return ret;
     71}
     72
     73static int tmff_play(struct input_dev *dev, void *data,
     74		struct ff_effect *effect)
     75{
     76	struct hid_device *hid = input_get_drvdata(dev);
     77	struct tmff_device *tmff = data;
     78	struct hid_field *ff_field = tmff->ff_field;
     79	int x, y;
     80	int left, right;	/* Rumbling */
     81
     82	switch (effect->type) {
     83	case FF_CONSTANT:
     84		x = tmff_scale_s8(effect->u.ramp.start_level,
     85					ff_field->logical_minimum,
     86					ff_field->logical_maximum);
     87		y = tmff_scale_s8(effect->u.ramp.end_level,
     88					ff_field->logical_minimum,
     89					ff_field->logical_maximum);
     90
     91		dbg_hid("(x, y)=(%04x, %04x)\n", x, y);
     92		ff_field->value[0] = x;
     93		ff_field->value[1] = y;
     94		hid_hw_request(hid, tmff->report, HID_REQ_SET_REPORT);
     95		break;
     96
     97	case FF_RUMBLE:
     98		left = tmff_scale_u16(effect->u.rumble.weak_magnitude,
     99					ff_field->logical_minimum,
    100					ff_field->logical_maximum);
    101		right = tmff_scale_u16(effect->u.rumble.strong_magnitude,
    102					ff_field->logical_minimum,
    103					ff_field->logical_maximum);
    104
    105		/* 2-in-1 strong motor is left */
    106		if (hid->product == THRUSTMASTER_DEVICE_ID_2_IN_1_DT)
    107			swap(left, right);
    108
    109		dbg_hid("(left,right)=(%08x, %08x)\n", left, right);
    110		ff_field->value[0] = left;
    111		ff_field->value[1] = right;
    112		hid_hw_request(hid, tmff->report, HID_REQ_SET_REPORT);
    113		break;
    114	}
    115	return 0;
    116}
    117
    118static int tmff_init(struct hid_device *hid, const signed short *ff_bits)
    119{
    120	struct tmff_device *tmff;
    121	struct hid_report *report;
    122	struct list_head *report_list;
    123	struct hid_input *hidinput;
    124	struct input_dev *input_dev;
    125	int error;
    126	int i;
    127
    128	if (list_empty(&hid->inputs)) {
    129		hid_err(hid, "no inputs found\n");
    130		return -ENODEV;
    131	}
    132	hidinput = list_entry(hid->inputs.next, struct hid_input, list);
    133	input_dev = hidinput->input;
    134
    135	tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL);
    136	if (!tmff)
    137		return -ENOMEM;
    138
    139	/* Find the report to use */
    140	report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
    141	list_for_each_entry(report, report_list, list) {
    142		int fieldnum;
    143
    144		for (fieldnum = 0; fieldnum < report->maxfield; ++fieldnum) {
    145			struct hid_field *field = report->field[fieldnum];
    146
    147			if (field->maxusage <= 0)
    148				continue;
    149
    150			switch (field->usage[0].hid) {
    151			case THRUSTMASTER_USAGE_FF:
    152				if (field->report_count < 2) {
    153					hid_warn(hid, "ignoring FF field with report_count < 2\n");
    154					continue;
    155				}
    156
    157				if (field->logical_maximum ==
    158						field->logical_minimum) {
    159					hid_warn(hid, "ignoring FF field with logical_maximum == logical_minimum\n");
    160					continue;
    161				}
    162
    163				if (tmff->report && tmff->report != report) {
    164					hid_warn(hid, "ignoring FF field in other report\n");
    165					continue;
    166				}
    167
    168				if (tmff->ff_field && tmff->ff_field != field) {
    169					hid_warn(hid, "ignoring duplicate FF field\n");
    170					continue;
    171				}
    172
    173				tmff->report = report;
    174				tmff->ff_field = field;
    175
    176				for (i = 0; ff_bits[i] >= 0; i++)
    177					set_bit(ff_bits[i], input_dev->ffbit);
    178
    179				break;
    180
    181			default:
    182				hid_warn(hid, "ignoring unknown output usage %08x\n",
    183					 field->usage[0].hid);
    184				continue;
    185			}
    186		}
    187	}
    188
    189	if (!tmff->report) {
    190		hid_err(hid, "can't find FF field in output reports\n");
    191		error = -ENODEV;
    192		goto fail;
    193	}
    194
    195	error = input_ff_create_memless(input_dev, tmff, tmff_play);
    196	if (error)
    197		goto fail;
    198
    199	hid_info(hid, "force feedback for ThrustMaster devices by Zinx Verituse <zinx@epicsol.org>\n");
    200	return 0;
    201
    202fail:
    203	kfree(tmff);
    204	return error;
    205}
    206#else
    207static inline int tmff_init(struct hid_device *hid, const signed short *ff_bits)
    208{
    209	return 0;
    210}
    211#endif
    212
    213static int tm_probe(struct hid_device *hdev, const struct hid_device_id *id)
    214{
    215	int ret;
    216
    217	ret = hid_parse(hdev);
    218	if (ret) {
    219		hid_err(hdev, "parse failed\n");
    220		goto err;
    221	}
    222
    223	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
    224	if (ret) {
    225		hid_err(hdev, "hw start failed\n");
    226		goto err;
    227	}
    228
    229	tmff_init(hdev, (void *)id->driver_data);
    230
    231	return 0;
    232err:
    233	return ret;
    234}
    235
    236static const struct hid_device_id tm_devices[] = {
    237	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300),
    238		.driver_data = (unsigned long)ff_rumble },
    239	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304),   /* FireStorm Dual Power 2 (and 3) */
    240		.driver_data = (unsigned long)ff_rumble },
    241	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, THRUSTMASTER_DEVICE_ID_2_IN_1_DT),   /* Dual Trigger 2-in-1 */
    242		.driver_data = (unsigned long)ff_rumble },
    243	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb323),   /* Dual Trigger 3-in-1 (PC Mode) */
    244		.driver_data = (unsigned long)ff_rumble },
    245	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb324),   /* Dual Trigger 3-in-1 (PS3 Mode) */
    246		.driver_data = (unsigned long)ff_rumble },
    247	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb605),   /* NASCAR PRO FF2 Wheel */
    248		.driver_data = (unsigned long)ff_joystick },
    249	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651),	/* FGT Rumble Force Wheel */
    250		.driver_data = (unsigned long)ff_rumble },
    251	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb653),	/* RGT Force Feedback CLUTCH Raging Wheel */
    252		.driver_data = (unsigned long)ff_joystick },
    253	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654),	/* FGT Force Feedback Wheel */
    254		.driver_data = (unsigned long)ff_joystick },
    255	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb65a),	/* F430 Force Feedback Wheel */
    256		.driver_data = (unsigned long)ff_joystick },
    257	{ }
    258};
    259MODULE_DEVICE_TABLE(hid, tm_devices);
    260
    261static struct hid_driver tm_driver = {
    262	.name = "thrustmaster",
    263	.id_table = tm_devices,
    264	.probe = tm_probe,
    265};
    266module_hid_driver(tm_driver);
    267
    268MODULE_LICENSE("GPL");