From f014217288927246f92ee876afbbf9de8ba707be Mon Sep 17 00:00:00 2001 From: Kai Krakow Date: Sat, 8 Aug 2026 22:36:04 +0200 Subject: use an unsigned 16-bit type for gain + Gain is documented and sent to the hardware as a value in the range 0 to 65535, but the module parameter and sysfs attribute accept wider integer values. Store gain as a u16 and parse sysfs writes with kstrtou16 so out-of-range values are rejected instead of being silently changed. + This constrains only the configured value. Calculations combining gain values still require a wider intermediate to cover the product of two full-range 16-bit values. --- src/hid-tmff2.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'src/hid-tmff2.c') diff --git a/src/hid-tmff2.c b/src/hid-tmff2.c index 162a30a..9cf07b9 100644 --- a/src/hid-tmff2.c +++ b/src/hid-tmff2.c @@ -43,8 +43,8 @@ MODULE_PARM_DESC(alt_mode, "Alternate mode, eg. F1 mode"); #define GAIN_MAX 65535 -int gain = 40000; -module_param(gain, int, 0); +u16 gain = 40000; +module_param(gain, ushort, 0); MODULE_PARM_DESC(gain, "Level of gain (0-65535)"); @@ -230,14 +230,15 @@ static ssize_t gain_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct tmff2_device_entry *tmff2 = tmff2_from_hdev(to_hid_device(dev)); - unsigned int value; + u16 value; int ret; if (!tmff2) return -ENODEV; - if ((ret = kstrtouint(buf, 0, &value))) { - dev_err(dev, "kstrtouint failed at gain_store: %i", ret); + ret = kstrtou16(buf, 0, &value); + if (ret) { + dev_err(dev, "failed to parse gain: %d\n", ret); return ret; } @@ -251,7 +252,7 @@ static ssize_t gain_store(struct device *dev, static ssize_t gain_show(struct device *dev, struct device_attribute *attr, char *buf) { - return scnprintf(buf, PAGE_SIZE, "%i\n", gain); + return scnprintf(buf, PAGE_SIZE, "%u\n", gain); } static DEVICE_ATTR_RW(gain); -- cgit v1.3 From 465ce84d2997e5af6630b88d88b63509d5a88031 Mon Sep 17 00:00:00 2001 From: Kai Krakow Date: Sat, 8 Aug 2026 12:27:06 +0200 Subject: avoid signed overflow when scaling gain + FF_GAIN and the driver gain both use the full 16-bit range. Their product can exceed INT_MAX. Signed overflow is undefined and happens for common settings such as 75 percent times 75 percent. Use a u32 intermediate for the scaling operation. + Pass the configured gain directly when initializing or updating the hardware. This also removes two redundant multiply-divide expressions which could overflow before cancelling out. --- src/hid-tmff2.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src/hid-tmff2.c') diff --git a/src/hid-tmff2.c b/src/hid-tmff2.c index 9cf07b9..02e70f1 100644 --- a/src/hid-tmff2.c +++ b/src/hid-tmff2.c @@ -48,6 +48,11 @@ module_param(gain, ushort, 0); MODULE_PARM_DESC(gain, "Level of gain (0-65535)"); +static u16 tmff2_scale_gain(u16 value) +{ + return (u32)value * gain / GAIN_MAX; +} + static spinlock_t lock; static struct tmff2_device_entry *tmff2_from_hdev(struct hid_device *hdev) @@ -244,7 +249,7 @@ static ssize_t gain_store(struct device *dev, gain = value; if (tmff2->set_gain) /* if we can, update gain immediately */ - tmff2->set_gain(tmff2->data, (GAIN_MAX * gain) / GAIN_MAX); + tmff2->set_gain(tmff2->data, gain); return count; } @@ -268,7 +273,7 @@ static void tmff2_set_gain(struct input_dev *dev, uint16_t value) return; } - if (tmff2->set_gain(tmff2->data, (value * gain) / GAIN_MAX)) + if (tmff2->set_gain(tmff2->data, tmff2_scale_gain(value))) hid_warn(tmff2->hdev, "unable to set gain\n"); } @@ -644,7 +649,7 @@ static int tmff2_wheel_init(struct tmff2_device_entry *tmff2) /* set defaults wherever possible */ if (tmff2->set_gain) { ff->set_gain = tmff2_set_gain; - tmff2->set_gain(tmff2->data, (GAIN_MAX * gain) / GAIN_MAX); + tmff2->set_gain(tmff2->data, gain); } if (tmff2->set_autocenter) -- cgit v1.3 From 4c142e19d14d02d0149198274b0f3f11091c71c6 Mon Sep 17 00:00:00 2001 From: Kai Krakow Date: Sat, 8 Aug 2026 13:04:36 +0200 Subject: remove generic input filtering from wheel and pedal axes + HID derives fuzz and flat from the logical axis range. On the tested T300 this gives steering fuzz=255 and flat=4095. It filters small corrections and adds a large centered deadzone before applications apply their own steering model. + Flat is also centered for pedal axes, while their rest position is an endpoint. Fuzz without an endpoint deadzone can retain a small nonzero value after the hardware has returned to rest. Both behaviours are inappropriate for wheel and pedal input, and applications already provide the required steering and endpoint deadzones. + Override fuzz and flat for every available wheel and pedal axis in input_configured on devices handled by hid-tmff2, following the existing hid-universal-pidff precedent for high-resolution racing devices. Skip absent axes through the input device's absbit mask. --- src/hid-tmff2.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'src/hid-tmff2.c') diff --git a/src/hid-tmff2.c b/src/hid-tmff2.c index 02e70f1..55e2173 100644 --- a/src/hid-tmff2.c +++ b/src/hid-tmff2.c @@ -807,6 +807,26 @@ static void tmff2_remove(struct hid_device *hdev) kfree(tmff2); } +static int tmff2_input_configured(struct hid_device *hdev, + struct hid_input *hidinput) +{ + struct input_dev *input = hidinput->input; + int axis; + + if (!input->absinfo) + return 0; + + for (axis = ABS_X; axis <= ABS_BRAKE; axis++) { + if (!test_bit(axis, input->absbit)) + continue; + + input_abs_set_fuzz(input, axis, 0); + input_abs_set_flat(input, axis, 0); + } + + return 0; +} + static const struct hid_device_id tmff2_devices[] = { /* t300rs and variations */ {HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMT300RS_PS3_NORM_ID)}, @@ -831,6 +851,7 @@ static struct hid_driver tmff2_driver = { .probe = tmff2_probe, .remove = tmff2_remove, .report_fixup = tmff2_report_fixup, + .input_configured = tmff2_input_configured, }; module_hid_driver(tmff2_driver); -- cgit v1.3