Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Kbuild
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ hid-tmff-new-y := \
src/tmt248/hid-tmt248.o \
src/tmtx/hid-tmtx.o \
src/tmtsxw/hid-tmtsxw.o \
src/tmtspc/hid-tmtspc.o
src/tmtspc/hid-tmtspc.o \
src/tmt500rs/hid-tmt500rs.o
File renamed without changes.
564 changes: 564 additions & 0 deletions docs/T500RS_FFBEFFECTS.md

Large diffs are not rendered by default.

94 changes: 87 additions & 7 deletions src/hid-tmff2.c
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,38 @@ 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);

if (!tmff2->set_gain)
return count;

/* Rationale: two-level gain model
* - The input API's set_gain (pending_gain) is the in-game gain (0..GAIN_MAX).
* - This driver also exposes a device/system gain via sysfs param `gain`.
* - The device callback receives the product: (pg * gain) / GAIN_MAX.
* See worker at tmff2->set_gain(... (pg * gain) / GAIN_MAX ).
* When the sysfs `gain` changes, we trigger a recompute by pushing
* pending_gain = GAIN_MAX here so the effective device gain becomes
* exactly the sysfs value (GAIN_MAX * gain / GAIN_MAX == gain) and future
* in-game set_gain calls continue to multiply in.
*
* References:
* - docs/T300RS_FFBEFFECTS.md: section "FF_GAIN" shows a dedicated
* device gain path.
*
* - docs/T500RS_FFBEFFECTS.md: Report glossary mentions 0x43 (gain),
* i.e. device-side gain separate from per-effect magnitudes; drivers should
* expose both levels.
*/
unsigned long flags;
spin_lock_irqsave(&tmff2->lock, flags);

tmff2->pending_gain = GAIN_MAX;
__set_bit(FF_EFFECT_QUEUE_GAIN, &tmff2->pending_flags);

spin_unlock_irqrestore(&tmff2->lock, flags);

if (!delayed_work_pending(&tmff2->work) && tmff2->allow_scheduling)
schedule_delayed_work(&tmff2->work, 0);
return count;
}

Expand All @@ -258,6 +287,7 @@ static DEVICE_ATTR_RW(gain);
static void tmff2_set_gain(struct input_dev *dev, uint16_t value)
{
struct tmff2_device_entry *tmff2 = tmff2_from_input(dev);
unsigned long flags;

if (!tmff2)
return;
Expand All @@ -267,13 +297,20 @@ static void tmff2_set_gain(struct input_dev *dev, uint16_t value)
return;
}

if (tmff2->set_gain(tmff2->data, (value * gain) / GAIN_MAX))
hid_warn(tmff2->hdev, "unable to set gain\n");
/* Defer to workqueue: store pending gain and schedule */
spin_lock_irqsave(&tmff2->lock, flags);
tmff2->pending_gain = value;
__set_bit(FF_EFFECT_QUEUE_GAIN, &tmff2->pending_flags);
spin_unlock_irqrestore(&tmff2->lock, flags);

if (!delayed_work_pending(&tmff2->work) && tmff2->allow_scheduling)
schedule_delayed_work(&tmff2->work, 0);
}

static void tmff2_set_autocenter(struct input_dev *dev, uint16_t value)
{
struct tmff2_device_entry *tmff2 = tmff2_from_input(dev);
unsigned long flags;

if (!tmff2)
return;
Expand All @@ -283,8 +320,14 @@ static void tmff2_set_autocenter(struct input_dev *dev, uint16_t value)
return;
}

if (tmff2->set_autocenter(tmff2->data, value))
hid_warn(tmff2->hdev, "unable to set autocenter\n");
/* Defer to workqueue: store pending autocenter and schedule */
spin_lock_irqsave(&tmff2->lock, flags);
tmff2->pending_autocenter = value;
__set_bit(FF_EFFECT_QUEUE_AUTOCENTER, &tmff2->pending_flags);
spin_unlock_irqrestore(&tmff2->lock, flags);

if (!delayed_work_pending(&tmff2->work) && tmff2->allow_scheduling)
schedule_delayed_work(&tmff2->work, 0);
}

static void tmff2_work_handler(struct work_struct *w)
Expand All @@ -297,10 +340,35 @@ static void tmff2_work_handler(struct work_struct *w)
unsigned long time_now;
__u16 effect_delay, effect_length;

uint16_t pending_gain = 0, pending_autocenter = 0;
bool set_gain = 0, set_autocenter = 0;

if (!tmff2)
return;

/* Apply pending control changes (gain/autocenter) in process context */
spin_lock_irqsave(&tmff2->lock, lock_flags);

if (test_bit(FF_EFFECT_QUEUE_GAIN, &tmff2->pending_flags)) {
pending_gain = tmff2->pending_gain;
__clear_bit(FF_EFFECT_QUEUE_GAIN, &tmff2->pending_flags);
set_gain = 1;
}

if (test_bit(FF_EFFECT_QUEUE_AUTOCENTER, &tmff2->pending_flags)) {
pending_autocenter = tmff2->pending_autocenter;
__clear_bit(FF_EFFECT_QUEUE_AUTOCENTER, &tmff2->pending_flags);
set_autocenter = 1;
}

spin_unlock_irqrestore(&tmff2->lock, lock_flags);

if (set_gain && tmff2->set_gain)
tmff2->set_gain(tmff2->data, (pending_gain * gain) / GAIN_MAX);

if (set_gain && tmff2->set_autocenter)
tmff2->set_autocenter(tmff2->data, pending_autocenter);

for (effect_id = 0; effect_id < tmff2->max_effects; ++effect_id) {
unsigned long actions = 0;
struct tmff2_effect_state effect;
Expand All @@ -320,7 +388,6 @@ static void tmff2_work_handler(struct work_struct *w)
(effect_delay + effect_length) * state->count) {
__clear_bit(FF_EFFECT_PLAYING, &state->flags);
__clear_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags);

state->count = 0;
}
}
Expand Down Expand Up @@ -694,6 +761,11 @@ static int tmff2_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto wheel_err;
break;

case TMT500RS_PC_ID:
if ((ret = t500rs_populate_api(tmff2)))
goto wheel_err;
break;

case TMT248_PC_ID:
if ((ret = t248_populate_api(tmff2)))
goto wheel_err;
Expand Down Expand Up @@ -806,6 +878,8 @@ static const struct hid_device_id tmff2_devices[] = {
{HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMT300RS_PS3_NORM_ID)},
{HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMT300RS_PS3_ADV_ID)},
{HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMT300RS_PS4_NORM_ID)},
/* t500rs */
{HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMT500RS_PC_ID)},
/* t248 PC*/
{HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMT248_PC_ID)},
/* tx */
Expand All @@ -828,4 +902,10 @@ static struct hid_driver tmff2_driver = {
};
module_hid_driver(tmff2_driver);


#ifndef TMFF2_DRIVER_VERSION
#define TMFF2_DRIVER_VERSION "dev"
#endif
MODULE_VERSION(TMFF2_DRIVER_VERSION);

MODULE_LICENSE("GPL");
11 changes: 11 additions & 0 deletions src/hid-tmff2.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ extern int alt_mode;
#define FF_EFFECT_QUEUE_STOP 2
#define FF_EFFECT_QUEUE_UPDATE 3
#define FF_EFFECT_PLAYING 4
#define FF_EFFECT_QUEUE_GAIN 5
#define FF_EFFECT_QUEUE_AUTOCENTER 6

#define PARAM_SPRING_LEVEL (1 << 0)
#define PARAM_DAMPER_LEVEL (1 << 1)
Expand Down Expand Up @@ -64,6 +66,11 @@ struct tmff2_device_entry {

spinlock_t lock;

/* Pending control changes to be applied from workqueue context */
uint16_t pending_gain;
uint16_t pending_autocenter;
unsigned long pending_flags;

int allow_scheduling;

/* fields relevant to each actual device (T300, T248...) */
Expand Down Expand Up @@ -92,6 +99,7 @@ struct tmff2_device_entry {
ssize_t (*alt_mode_show)(void *data, char *buf);
ssize_t (*alt_mode_store)(void *data, const char *buf, size_t count);
int (*set_autocenter)(void *data, uint16_t autocenter);

__u8 *(*wheel_fixup)(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize);

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

/* external */
int t300rs_populate_api(struct tmff2_device_entry *tmff2);
int t500rs_populate_api(struct tmff2_device_entry *tmff2);
int t248_populate_api(struct tmff2_device_entry *tmff2);
int tx_populate_api(struct tmff2_device_entry *tmff2);
int tsxw_populate_api(struct tmff2_device_entry *tmff2);
Expand All @@ -109,6 +118,8 @@ int tspc_populate_api(struct tmff2_device_entry *tmff2);
#define TMT300RS_PS3_ADV_ID 0xb66f
#define TMT300RS_PS4_NORM_ID 0xb66d

#define TMT500RS_PC_ID 0xb65e

#define TMT248_PC_ID 0xb696

#define TX_ACTIVE 0xb669
Expand Down
Loading