18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Force feedback support for various HID compliant devices by ThrustMaster: 48c2ecf20Sopenharmony_ci * ThrustMaster FireStorm Dual Power 2 58c2ecf20Sopenharmony_ci * and possibly others whose device ids haven't been added. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Modified to support ThrustMaster devices by Zinx Verituse 88c2ecf20Sopenharmony_ci * on 2003-01-25 from the Logitech force feedback driver, 98c2ecf20Sopenharmony_ci * which is by Johann Deneux. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Copyright (c) 2003 Zinx Verituse <zinx@epicsol.org> 128c2ecf20Sopenharmony_ci * Copyright (c) 2002 Johann Deneux 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/* 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/hid.h> 198c2ecf20Sopenharmony_ci#include <linux/input.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/module.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "hid-ids.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define THRUSTMASTER_DEVICE_ID_2_IN_1_DT 0xb320 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic const signed short ff_rumble[] = { 288c2ecf20Sopenharmony_ci FF_RUMBLE, 298c2ecf20Sopenharmony_ci -1 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic const signed short ff_joystick[] = { 338c2ecf20Sopenharmony_ci FF_CONSTANT, 348c2ecf20Sopenharmony_ci -1 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#ifdef CONFIG_THRUSTMASTER_FF 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* Usages for thrustmaster devices I know about */ 408c2ecf20Sopenharmony_ci#define THRUSTMASTER_USAGE_FF (HID_UP_GENDESK | 0xbb) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistruct tmff_device { 438c2ecf20Sopenharmony_ci struct hid_report *report; 448c2ecf20Sopenharmony_ci struct hid_field *ff_field; 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* Changes values from 0 to 0xffff into values from minimum to maximum */ 488c2ecf20Sopenharmony_cistatic inline int tmff_scale_u16(unsigned int in, int minimum, int maximum) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci int ret; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci ret = (in * (maximum - minimum) / 0xffff) + minimum; 538c2ecf20Sopenharmony_ci if (ret < minimum) 548c2ecf20Sopenharmony_ci return minimum; 558c2ecf20Sopenharmony_ci if (ret > maximum) 568c2ecf20Sopenharmony_ci return maximum; 578c2ecf20Sopenharmony_ci return ret; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* Changes values from -0x80 to 0x7f into values from minimum to maximum */ 618c2ecf20Sopenharmony_cistatic inline int tmff_scale_s8(int in, int minimum, int maximum) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci int ret; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci ret = (((in + 0x80) * (maximum - minimum)) / 0xff) + minimum; 668c2ecf20Sopenharmony_ci if (ret < minimum) 678c2ecf20Sopenharmony_ci return minimum; 688c2ecf20Sopenharmony_ci if (ret > maximum) 698c2ecf20Sopenharmony_ci return maximum; 708c2ecf20Sopenharmony_ci return ret; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int tmff_play(struct input_dev *dev, void *data, 748c2ecf20Sopenharmony_ci struct ff_effect *effect) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct hid_device *hid = input_get_drvdata(dev); 778c2ecf20Sopenharmony_ci struct tmff_device *tmff = data; 788c2ecf20Sopenharmony_ci struct hid_field *ff_field = tmff->ff_field; 798c2ecf20Sopenharmony_ci int x, y; 808c2ecf20Sopenharmony_ci int left, right; /* Rumbling */ 818c2ecf20Sopenharmony_ci int motor_swap; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci switch (effect->type) { 848c2ecf20Sopenharmony_ci case FF_CONSTANT: 858c2ecf20Sopenharmony_ci x = tmff_scale_s8(effect->u.ramp.start_level, 868c2ecf20Sopenharmony_ci ff_field->logical_minimum, 878c2ecf20Sopenharmony_ci ff_field->logical_maximum); 888c2ecf20Sopenharmony_ci y = tmff_scale_s8(effect->u.ramp.end_level, 898c2ecf20Sopenharmony_ci ff_field->logical_minimum, 908c2ecf20Sopenharmony_ci ff_field->logical_maximum); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci dbg_hid("(x, y)=(%04x, %04x)\n", x, y); 938c2ecf20Sopenharmony_ci ff_field->value[0] = x; 948c2ecf20Sopenharmony_ci ff_field->value[1] = y; 958c2ecf20Sopenharmony_ci hid_hw_request(hid, tmff->report, HID_REQ_SET_REPORT); 968c2ecf20Sopenharmony_ci break; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci case FF_RUMBLE: 998c2ecf20Sopenharmony_ci left = tmff_scale_u16(effect->u.rumble.weak_magnitude, 1008c2ecf20Sopenharmony_ci ff_field->logical_minimum, 1018c2ecf20Sopenharmony_ci ff_field->logical_maximum); 1028c2ecf20Sopenharmony_ci right = tmff_scale_u16(effect->u.rumble.strong_magnitude, 1038c2ecf20Sopenharmony_ci ff_field->logical_minimum, 1048c2ecf20Sopenharmony_ci ff_field->logical_maximum); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* 2-in-1 strong motor is left */ 1078c2ecf20Sopenharmony_ci if (hid->product == THRUSTMASTER_DEVICE_ID_2_IN_1_DT) { 1088c2ecf20Sopenharmony_ci motor_swap = left; 1098c2ecf20Sopenharmony_ci left = right; 1108c2ecf20Sopenharmony_ci right = motor_swap; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci dbg_hid("(left,right)=(%08x, %08x)\n", left, right); 1148c2ecf20Sopenharmony_ci ff_field->value[0] = left; 1158c2ecf20Sopenharmony_ci ff_field->value[1] = right; 1168c2ecf20Sopenharmony_ci hid_hw_request(hid, tmff->report, HID_REQ_SET_REPORT); 1178c2ecf20Sopenharmony_ci break; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic int tmff_init(struct hid_device *hid, const signed short *ff_bits) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct tmff_device *tmff; 1258c2ecf20Sopenharmony_ci struct hid_report *report; 1268c2ecf20Sopenharmony_ci struct list_head *report_list; 1278c2ecf20Sopenharmony_ci struct hid_input *hidinput; 1288c2ecf20Sopenharmony_ci struct input_dev *input_dev; 1298c2ecf20Sopenharmony_ci int error; 1308c2ecf20Sopenharmony_ci int i; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (list_empty(&hid->inputs)) { 1338c2ecf20Sopenharmony_ci hid_err(hid, "no inputs found\n"); 1348c2ecf20Sopenharmony_ci return -ENODEV; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci hidinput = list_entry(hid->inputs.next, struct hid_input, list); 1378c2ecf20Sopenharmony_ci input_dev = hidinput->input; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL); 1408c2ecf20Sopenharmony_ci if (!tmff) 1418c2ecf20Sopenharmony_ci return -ENOMEM; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* Find the report to use */ 1448c2ecf20Sopenharmony_ci report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list; 1458c2ecf20Sopenharmony_ci list_for_each_entry(report, report_list, list) { 1468c2ecf20Sopenharmony_ci int fieldnum; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci for (fieldnum = 0; fieldnum < report->maxfield; ++fieldnum) { 1498c2ecf20Sopenharmony_ci struct hid_field *field = report->field[fieldnum]; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci if (field->maxusage <= 0) 1528c2ecf20Sopenharmony_ci continue; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci switch (field->usage[0].hid) { 1558c2ecf20Sopenharmony_ci case THRUSTMASTER_USAGE_FF: 1568c2ecf20Sopenharmony_ci if (field->report_count < 2) { 1578c2ecf20Sopenharmony_ci hid_warn(hid, "ignoring FF field with report_count < 2\n"); 1588c2ecf20Sopenharmony_ci continue; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (field->logical_maximum == 1628c2ecf20Sopenharmony_ci field->logical_minimum) { 1638c2ecf20Sopenharmony_ci hid_warn(hid, "ignoring FF field with logical_maximum == logical_minimum\n"); 1648c2ecf20Sopenharmony_ci continue; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (tmff->report && tmff->report != report) { 1688c2ecf20Sopenharmony_ci hid_warn(hid, "ignoring FF field in other report\n"); 1698c2ecf20Sopenharmony_ci continue; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (tmff->ff_field && tmff->ff_field != field) { 1738c2ecf20Sopenharmony_ci hid_warn(hid, "ignoring duplicate FF field\n"); 1748c2ecf20Sopenharmony_ci continue; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci tmff->report = report; 1788c2ecf20Sopenharmony_ci tmff->ff_field = field; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci for (i = 0; ff_bits[i] >= 0; i++) 1818c2ecf20Sopenharmony_ci set_bit(ff_bits[i], input_dev->ffbit); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci break; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci default: 1868c2ecf20Sopenharmony_ci hid_warn(hid, "ignoring unknown output usage %08x\n", 1878c2ecf20Sopenharmony_ci field->usage[0].hid); 1888c2ecf20Sopenharmony_ci continue; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (!tmff->report) { 1948c2ecf20Sopenharmony_ci hid_err(hid, "can't find FF field in output reports\n"); 1958c2ecf20Sopenharmony_ci error = -ENODEV; 1968c2ecf20Sopenharmony_ci goto fail; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci error = input_ff_create_memless(input_dev, tmff, tmff_play); 2008c2ecf20Sopenharmony_ci if (error) 2018c2ecf20Sopenharmony_ci goto fail; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci hid_info(hid, "force feedback for ThrustMaster devices by Zinx Verituse <zinx@epicsol.org>\n"); 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cifail: 2078c2ecf20Sopenharmony_ci kfree(tmff); 2088c2ecf20Sopenharmony_ci return error; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci#else 2118c2ecf20Sopenharmony_cistatic inline int tmff_init(struct hid_device *hid, const signed short *ff_bits) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci return 0; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci#endif 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic int tm_probe(struct hid_device *hdev, const struct hid_device_id *id) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci int ret; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci ret = hid_parse(hdev); 2228c2ecf20Sopenharmony_ci if (ret) { 2238c2ecf20Sopenharmony_ci hid_err(hdev, "parse failed\n"); 2248c2ecf20Sopenharmony_ci goto err; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); 2288c2ecf20Sopenharmony_ci if (ret) { 2298c2ecf20Sopenharmony_ci hid_err(hdev, "hw start failed\n"); 2308c2ecf20Sopenharmony_ci goto err; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci tmff_init(hdev, (void *)id->driver_data); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return 0; 2368c2ecf20Sopenharmony_cierr: 2378c2ecf20Sopenharmony_ci return ret; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic const struct hid_device_id tm_devices[] = { 2418c2ecf20Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300), 2428c2ecf20Sopenharmony_ci .driver_data = (unsigned long)ff_rumble }, 2438c2ecf20Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304), /* FireStorm Dual Power 2 (and 3) */ 2448c2ecf20Sopenharmony_ci .driver_data = (unsigned long)ff_rumble }, 2458c2ecf20Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, THRUSTMASTER_DEVICE_ID_2_IN_1_DT), /* Dual Trigger 2-in-1 */ 2468c2ecf20Sopenharmony_ci .driver_data = (unsigned long)ff_rumble }, 2478c2ecf20Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb323), /* Dual Trigger 3-in-1 (PC Mode) */ 2488c2ecf20Sopenharmony_ci .driver_data = (unsigned long)ff_rumble }, 2498c2ecf20Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb324), /* Dual Trigger 3-in-1 (PS3 Mode) */ 2508c2ecf20Sopenharmony_ci .driver_data = (unsigned long)ff_rumble }, 2518c2ecf20Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb605), /* NASCAR PRO FF2 Wheel */ 2528c2ecf20Sopenharmony_ci .driver_data = (unsigned long)ff_joystick }, 2538c2ecf20Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651), /* FGT Rumble Force Wheel */ 2548c2ecf20Sopenharmony_ci .driver_data = (unsigned long)ff_rumble }, 2558c2ecf20Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb653), /* RGT Force Feedback CLUTCH Raging Wheel */ 2568c2ecf20Sopenharmony_ci .driver_data = (unsigned long)ff_joystick }, 2578c2ecf20Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654), /* FGT Force Feedback Wheel */ 2588c2ecf20Sopenharmony_ci .driver_data = (unsigned long)ff_joystick }, 2598c2ecf20Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb65a), /* F430 Force Feedback Wheel */ 2608c2ecf20Sopenharmony_ci .driver_data = (unsigned long)ff_joystick }, 2618c2ecf20Sopenharmony_ci { } 2628c2ecf20Sopenharmony_ci}; 2638c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(hid, tm_devices); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic struct hid_driver tm_driver = { 2668c2ecf20Sopenharmony_ci .name = "thrustmaster", 2678c2ecf20Sopenharmony_ci .id_table = tm_devices, 2688c2ecf20Sopenharmony_ci .probe = tm_probe, 2698c2ecf20Sopenharmony_ci}; 2708c2ecf20Sopenharmony_cimodule_hid_driver(tm_driver); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 273