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