Skip to content

Commit be8c39d

Browse files
committed
hid: tmff2: add Thrustmaster T500RS wheel base driver
Add initial support for the Thrustmaster T500RS wheel base in the hid-tmff2 driver. This introduces a new device backend that formats and sends device-specific force-feedback commands over the wheel's USB interrupt endpoint and plugs into the tmff2 core machinery. Highlights: - New t500rs USB backend wired into Kbuild/Makefile and core registration. - Device hooks implement effect lifecycle (play/stop/update), global gain, and autocenter; FF_GAIN/FF_AUTOCENTER updates are routed through the same process-context path as effect updates. - Input device is initialized with the expected capabilities for this base, and packets follow the T500RS protocol. This is the foundational driver support required for the T500RS wheel base.
1 parent 5095d47 commit be8c39d

File tree

7 files changed

+1593
-7
lines changed

7 files changed

+1593
-7
lines changed

Kbuild

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,8 @@ hid-tmff-new-y := \
44
src/tmt300rs/hid-tmt300rs.o \
55
src/tmt248/hid-tmt248.o \
66
src/tmtx/hid-tmtx.o \
7-
src/tmtsxw/hid-tmtsxw.o
7+
src/tmtsxw/hid-tmtsxw.o \
8+
src/tmt500rs/hid-tmt500rs-usb.o
9+
10+
# Pass through the T500RS version define from Makefile (original branch style)
11+
ccflags-y += $(T500RS_VERSION_DEF)

Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
KDIR ?= /lib/modules/$(shell uname -r)/build
22

3+
# Auto-generated build-time version for T500RS
4+
T500RS_BASE_VERSION ?= 0.1
5+
GIT_HASH := $(shell git rev-parse --short=7 HEAD 2>/dev/null || echo "local")
6+
BUILD_HASH := $(shell date +%s | sha1sum | cut -c1-7)
7+
8+
T500RS_VERSION ?= $(T500RS_BASE_VERSION)-$(GIT_HASH)+b$(BUILD_HASH)
9+
export T500RS_VERSION_DEF := -DT500RS_DRIVER_VERSION=\"$(T500RS_VERSION)\"
10+
11+
312
all: deps/hid-tminit
13+
@echo "T500RS build version: $(T500RS_VERSION)"
14+
@echo " - base: $(T500RS_BASE_VERSION), commit: $(GIT_HASH), build: $(BUILD_HASH)"
415
$(MAKE) -C $(KDIR) M=$(shell pwd) modules
516

617
install: deps/hid-tminit

src/hid-tmff2.c

Lines changed: 105 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,22 @@
55
#include <linux/version.h>
66
#include "hid-tmff2.h"
77

8+
/* Share t500rs_log_level across compilation units (level 3 = unknown-only) */
9+
extern int t500rs_log_level;
10+
11+
/* Known vendor/opcode IDs managed by the driver (TX side) */
12+
static inline int tmff2_is_known_vendor_id(unsigned char id)
13+
{
14+
switch (id) {
15+
case 0x01: case 0x02: case 0x03: case 0x04: case 0x05:
16+
case 0x40: case 0x41: case 0x42: case 0x43:
17+
case 0x0a:
18+
return 1;
19+
default:
20+
return 0;
21+
}
22+
}
23+
824

925
int open_mode = 1;
1026
module_param(open_mode, int, 0660);
@@ -242,8 +258,15 @@ static ssize_t gain_store(struct device *dev,
242258
}
243259

244260
gain = value;
245-
if (tmff2->set_gain) /* if we can, update gain immediately */
246-
tmff2->set_gain(tmff2->data, (GAIN_MAX * gain) / GAIN_MAX);
261+
if (tmff2->set_gain) {
262+
unsigned long flags;
263+
spin_lock_irqsave(&tmff2->lock, flags);
264+
tmff2->pending_gain_value = GAIN_MAX;
265+
tmff2->gain_pending = 1;
266+
spin_unlock_irqrestore(&tmff2->lock, flags);
267+
if (!delayed_work_pending(&tmff2->work) && tmff2->allow_scheduling)
268+
schedule_delayed_work(&tmff2->work, 0);
269+
}
247270

248271
return count;
249272
}
@@ -258,6 +281,7 @@ static DEVICE_ATTR_RW(gain);
258281
static void tmff2_set_gain(struct input_dev *dev, uint16_t value)
259282
{
260283
struct tmff2_device_entry *tmff2 = tmff2_from_input(dev);
284+
unsigned long flags;
261285

262286
if (!tmff2)
263287
return;
@@ -267,13 +291,20 @@ static void tmff2_set_gain(struct input_dev *dev, uint16_t value)
267291
return;
268292
}
269293

270-
if (tmff2->set_gain(tmff2->data, (value * gain) / GAIN_MAX))
271-
hid_warn(tmff2->hdev, "unable to set gain\n");
294+
/* Defer to workqueue: store pending gain and schedule */
295+
spin_lock_irqsave(&tmff2->lock, flags);
296+
tmff2->pending_gain_value = value;
297+
tmff2->gain_pending = 1;
298+
spin_unlock_irqrestore(&tmff2->lock, flags);
299+
300+
if (!delayed_work_pending(&tmff2->work) && tmff2->allow_scheduling)
301+
schedule_delayed_work(&tmff2->work, 0);
272302
}
273303

274304
static void tmff2_set_autocenter(struct input_dev *dev, uint16_t value)
275305
{
276306
struct tmff2_device_entry *tmff2 = tmff2_from_input(dev);
307+
unsigned long flags;
277308

278309
if (!tmff2)
279310
return;
@@ -283,8 +314,14 @@ static void tmff2_set_autocenter(struct input_dev *dev, uint16_t value)
283314
return;
284315
}
285316

286-
if (tmff2->set_autocenter(tmff2->data, value))
287-
hid_warn(tmff2->hdev, "unable to set autocenter\n");
317+
/* Defer to workqueue: store pending autocenter and schedule */
318+
spin_lock_irqsave(&tmff2->lock, flags);
319+
tmff2->pending_autocenter_value = value;
320+
tmff2->autocenter_pending = 1;
321+
spin_unlock_irqrestore(&tmff2->lock, flags);
322+
323+
if (!delayed_work_pending(&tmff2->work) && tmff2->allow_scheduling)
324+
schedule_delayed_work(&tmff2->work, 0);
288325
}
289326

290327
static void tmff2_work_handler(struct work_struct *w)
@@ -301,6 +338,30 @@ static void tmff2_work_handler(struct work_struct *w)
301338
if (!tmff2)
302339
return;
303340

341+
/* Apply pending control changes (gain/autocenter) in process context */
342+
{
343+
unsigned long f2;
344+
uint16_t pg = 0, pac = 0;
345+
int do_gain = 0, do_ac = 0;
346+
spin_lock_irqsave(&tmff2->lock, f2);
347+
if (tmff2->gain_pending) {
348+
pg = tmff2->pending_gain_value;
349+
tmff2->gain_pending = 0;
350+
do_gain = 1;
351+
}
352+
if (tmff2->autocenter_pending) {
353+
pac = tmff2->pending_autocenter_value;
354+
tmff2->autocenter_pending = 0;
355+
do_ac = 1;
356+
}
357+
spin_unlock_irqrestore(&tmff2->lock, f2);
358+
359+
if (do_gain && tmff2->set_gain)
360+
tmff2->set_gain(tmff2->data, (pg * gain) / GAIN_MAX);
361+
if (do_ac && tmff2->set_autocenter)
362+
tmff2->set_autocenter(tmff2->data, pac);
363+
}
364+
304365
for (effect_id = 0; effect_id < tmff2->max_effects; ++effect_id) {
305366
unsigned long actions = 0;
306367
struct tmff2_effect_state effect;
@@ -694,6 +755,11 @@ static int tmff2_probe(struct hid_device *hdev, const struct hid_device_id *id)
694755
goto wheel_err;
695756
break;
696757

758+
case TMT500RS_PC_ID:
759+
if ((ret = t500rs_populate_api(tmff2)))
760+
goto wheel_err;
761+
break;
762+
697763
case TMT248_PC_ID:
698764
if ((ret = t248_populate_api(tmff2)))
699765
goto wheel_err;
@@ -743,6 +809,7 @@ static int tmff2_probe(struct hid_device *hdev, const struct hid_device_id *id)
743809

744810
#if LINUX_VERSION_CODE < KERNEL_VERSION(6,12,0)
745811
static __u8 *tmff2_report_fixup(struct hid_device *hdev, __u8 *rdesc,
812+
746813
unsigned int *rsize)
747814
#else
748815
static const __u8 *tmff2_report_fixup(struct hid_device *hdev, __u8 *rdesc,
@@ -778,6 +845,7 @@ static void tmff2_remove(struct hid_device *hdev)
778845
if (tmff2->params & PARAM_DAMPER_LEVEL)
779846
device_remove_file(dev, &dev_attr_damper_level);
780847

848+
781849
if (tmff2->params & PARAM_SPRING_LEVEL)
782850
device_remove_file(dev, &dev_attr_spring_level);
783851

@@ -802,6 +870,8 @@ static const struct hid_device_id tmff2_devices[] = {
802870
{HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMT300RS_PS3_NORM_ID)},
803871
{HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMT300RS_PS3_ADV_ID)},
804872
{HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMT300RS_PS4_NORM_ID)},
873+
/* t500rs */
874+
{HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMT500RS_PC_ID)},
805875
/* t248 PC*/
806876
{HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMT248_PC_ID)},
807877
/* tx */
@@ -812,13 +882,42 @@ static const struct hid_device_id tmff2_devices[] = {
812882
{}
813883
};
814884
MODULE_DEVICE_TABLE(hid, tmff2_devices);
885+
static int tmff2_raw_event(struct hid_device *hdev, struct hid_report *report,
886+
__u8 *data, int size)
887+
{
888+
/* At level 3: ignore normal input reports (axes/buttons) and idless reports;
889+
* only dump vendor/feature-like unknowns. */
890+
if (t500rs_log_level >= 3 && report && data && size > 0) {
891+
/* Skip input traffic entirely (these are the steering updates you saw). */
892+
if (report->type == HID_INPUT_REPORT)
893+
return 0;
894+
895+
/* Use the real Report ID, not data[0] (id==0 means no Report ID). */
896+
if (report->id == 0)
897+
return 0;
898+
899+
if (!tmff2_is_known_vendor_id((unsigned char)report->id)) {
900+
char hex[3 * 64 + 4];
901+
int i, off = 0, max = size > 64 ? 64 : size;
902+
for (i = 0; i < max && off + 3 < sizeof(hex); i++)
903+
off += scnprintf(hex + off, sizeof(hex) - off, "%02x ", data[i]);
904+
if (size > max)
905+
scnprintf(hex + off, sizeof(hex) - off, "...");
906+
hid_info(hdev, "HID RX UNKNOWN [type=%d id=0x%02x len=%d]: %s\n",
907+
report->type, report->id, size, hex);
908+
}
909+
}
910+
return 0; /* always pass through */
911+
}
912+
815913

816914
static struct hid_driver tmff2_driver = {
817915
.name = "tmff2",
818916
.id_table = tmff2_devices,
819917
.probe = tmff2_probe,
820918
.remove = tmff2_remove,
821919
.report_fixup = tmff2_report_fixup,
920+
.raw_event = tmff2_raw_event,
822921
};
823922
module_hid_driver(tmff2_driver);
824923

src/hid-tmff2.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ struct tmff2_device_entry {
6464

6565
spinlock_t lock;
6666

67+
/* Pending control changes to be applied from workqueue context */
68+
uint16_t pending_gain_value;
69+
uint16_t pending_autocenter_value;
70+
int gain_pending;
71+
int autocenter_pending;
72+
6773
int allow_scheduling;
6874

6975
/* fields relevant to each actual device (T300, T248...) */
@@ -92,6 +98,10 @@ struct tmff2_device_entry {
9298
ssize_t (*alt_mode_show)(void *data, char *buf);
9399
ssize_t (*alt_mode_store)(void *data, const char *buf, size_t count);
94100
int (*set_autocenter)(void *data, uint16_t autocenter);
101+
int (*set_spring_level)(void *data, u8 level);
102+
int (*set_damper_level)(void *data, u8 level);
103+
int (*set_friction_level)(void *data, u8 level);
104+
95105
__u8 *(*wheel_fixup)(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize);
96106

97107
/* void pointers are dangerous, I know, but in this case likely the
@@ -100,6 +110,7 @@ struct tmff2_device_entry {
100110

101111
/* external */
102112
int t300rs_populate_api(struct tmff2_device_entry *tmff2);
113+
int t500rs_populate_api(struct tmff2_device_entry *tmff2);
103114
int t248_populate_api(struct tmff2_device_entry *tmff2);
104115
int tx_populate_api(struct tmff2_device_entry *tmff2);
105116
int tsxw_populate_api(struct tmff2_device_entry *tmff2);
@@ -108,6 +119,9 @@ int tsxw_populate_api(struct tmff2_device_entry *tmff2);
108119
#define TMT300RS_PS3_ADV_ID 0xb66f
109120
#define TMT300RS_PS4_NORM_ID 0xb66d
110121

122+
#define TMT500RS_INIT_ID 0xb65d
123+
#define TMT500RS_PC_ID 0xb65e
124+
111125
#define TMT248_PC_ID 0xb696
112126

113127
#define TX_ACTIVE 0xb669

0 commit comments

Comments
 (0)