From 75006183d0a379a2b0fa844fc32b3e27d229f368 Mon Sep 17 00:00:00 2001 From: RaySlash Date: Mon, 2 Oct 2023 13:08:52 +1000 Subject: src: initial refactoring --- src/hid-tmff2.c | 749 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 749 insertions(+) create mode 100644 src/hid-tmff2.c (limited to 'src/hid-tmff2.c') diff --git a/src/hid-tmff2.c b/src/hid-tmff2.c new file mode 100644 index 0000000..44df1e3 --- /dev/null +++ b/src/hid-tmff2.c @@ -0,0 +1,749 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include +#include "hid-tmff2.h" + + +int open_mode = 1; +module_param(open_mode, int, 0660); +MODULE_PARM_DESC(open_mode, + "Whether to send mode change commands on open/close"); + +int timer_msecs = DEFAULT_TIMER_PERIOD; +module_param(timer_msecs, int, 0660); +MODULE_PARM_DESC(timer_msecs, + "Timer resolution in msecs"); + +/* should these be removed and just rely on /sys? */ +int spring_level = 30; +module_param(spring_level, int, 0); +MODULE_PARM_DESC(spring_level, + "Level of spring force (0-100), as per Oversteer standards"); + +int damper_level = 30; +module_param(damper_level, int, 0); +MODULE_PARM_DESC(damper_level, + "Level of damper force (0-100), as per Oversteer standards"); + +int friction_level = 30; +module_param(friction_level, int, 0); +MODULE_PARM_DESC(friction_level, + "Level of friction force (0-100), as per Oversteer standards"); + +int range = 900; +module_param(range, int, 0); +MODULE_PARM_DESC(range, + "Range of wheel, depends on the wheel. Invalid values are ignored"); + +int alt_mode = 0; +module_param(alt_mode, int, 0); +MODULE_PARM_DESC(alt_mode, + "Alternate mode, eg. F1 mode"); + +#define GAIN_MAX 65535 +int gain = 40000; +module_param(gain, int, 0); +MODULE_PARM_DESC(gain, + "Level of gain (0-65535)"); + +static spinlock_t lock; +static unsigned long lock_flags; + +static struct tmff2_device_entry *tmff2_from_hdev(struct hid_device *hdev) +{ + struct tmff2_device_entry *tmff2; + spin_lock_irqsave(&lock, lock_flags); + + if (!(tmff2 = hid_get_drvdata(hdev))) + dev_err(&hdev->dev, "hdev private data not found\n"); + + spin_unlock_irqrestore(&lock, lock_flags); + + return tmff2; +} + +static struct tmff2_device_entry *tmff2_from_input(struct input_dev *input_dev) +{ + struct hid_device *hdev; + spin_lock_irqsave(&lock, lock_flags); + + if (!(hdev = input_get_drvdata(input_dev))) + dev_err(&input_dev->dev, "input_dev private data not found\n"); + + spin_unlock_irqrestore(&lock, lock_flags); + + return tmff2_from_hdev(hdev); +} + +static ssize_t spring_level_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int value; + int ret; + + ret = kstrtouint(buf, 0, &value); + if (ret) { + dev_err(dev, "kstrtouint failed at spring_level_store: %i", ret); + return ret; + } + + if (value > 100) { + dev_info(dev, "value %i larger than max 100, clamping to 100.\n", value); + value = 100; + } + + spring_level = value; + + return count; +} + +static ssize_t spring_level_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + return scnprintf(buf, PAGE_SIZE, "%u\n", spring_level); +} +static DEVICE_ATTR_RW(spring_level); + +static ssize_t damper_level_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int value; + int ret; + + + ret = kstrtouint(buf, 0, &value); + if (ret) { + dev_err(dev, "kstrtouint failed at damper_level_store: %i", ret); + return ret; + } + + if (value > 100) { + dev_info(dev, "value %i larger than max 100, clamping to 100.\n", value); + value = 100; + } + + damper_level = value; + + return count; +} + +static ssize_t damper_level_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + return scnprintf(buf, PAGE_SIZE, "%u\n", damper_level); +} +static DEVICE_ATTR_RW(damper_level); + +static ssize_t friction_level_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int value; + int ret; + + + ret = kstrtouint(buf, 0, &value); + if (ret) { + dev_err(dev, "kstrtouint failed at friction_level_store: %i", ret); + return ret; + } + + if (value > 100) { + dev_info(dev, "value %i larger than max 100, clamping to 100.\n", value); + value = 100; + } + + friction_level = value; + + return count; +} + +static ssize_t friction_level_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + size_t count; + + + count = scnprintf(buf, PAGE_SIZE, "%u\n", friction_level); + + return count; +} +static DEVICE_ATTR_RW(friction_level); + +static ssize_t range_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; + int ret; + + + if (!tmff2) + return -ENODEV; + + if ((ret = kstrtouint(buf, 0, &value))) { + hid_err(tmff2->hdev, "kstrtouint failed at range_store: %i", ret); + return ret; + } + + if (tmff2->set_range) { + if ((ret = tmff2->set_range(tmff2->data, value))) + return ret; + } + + return count; +} + +static ssize_t range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + return scnprintf(buf, PAGE_SIZE, "%u\n", range); +} +static DEVICE_ATTR_RW(range); + +static ssize_t alternate_modes_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)); + + if (!tmff2) + return -ENODEV; + + if (tmff2->alt_mode_store) + return tmff2->alt_mode_store(tmff2->data, buf, count); + + return 0; +} + +static ssize_t alternate_modes_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tmff2_device_entry *tmff2 = tmff2_from_hdev(to_hid_device(dev)); + + if (!tmff2) + return -ENODEV; + + if (tmff2->alt_mode_show) + return tmff2->alt_mode_show(tmff2->data, buf); + + return 0; +} +static DEVICE_ATTR_RW(alternate_modes); + +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; + int ret; + + if (!tmff2) + return -ENODEV; + + if ((ret = kstrtouint(buf, 0, &value))) { + dev_err(dev, "kstrtouint failed at gain_store: %i", ret); + return ret; + } + + gain = value; + if (tmff2->set_gain) /* if we can, update gain immediately */ + tmff2->set_gain(tmff2->data, (GAIN_MAX * gain) / GAIN_MAX); + + return count; +} + +static ssize_t gain_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%i\n", gain); +} +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); + + if (!tmff2) + return; + + if (!tmff2->set_gain) { + hid_err(tmff2->hdev, "missing set_gain\n"); + return; + } + + if (tmff2->set_gain(tmff2->data, (value * gain) / GAIN_MAX)) + hid_warn(tmff2->hdev, "unable to set gain\n"); +} + +static void tmff2_set_autocenter(struct input_dev *dev, uint16_t value) +{ + struct tmff2_device_entry *tmff2 = tmff2_from_input(dev); + + if (!tmff2) + return; + + if (!tmff2->set_autocenter) { + hid_err(tmff2->hdev, "missing set_autocenter\n"); + return; + } + + if (tmff2->set_autocenter(tmff2->data, value)) + hid_warn(tmff2->hdev, "unable to set autocenter\n"); +} + +static void tmff2_work_handler(struct work_struct *w) +{ + struct delayed_work *dw = container_of(w, struct delayed_work, work); + struct tmff2_device_entry *tmff2 = container_of(dw, struct tmff2_device_entry, work); + struct tmff2_effect_state *state; + int max_count = 0, effect_id; + unsigned long time_now; + __u16 effect_length; + + + if (!tmff2) + return; + + for (effect_id = 0; effect_id < tmff2->max_effects; ++effect_id) { + spin_lock(&tmff2->lock); + + time_now = JIFFIES2MS(jiffies); + state = &tmff2->states[effect_id]; + + effect_length = state->effect.replay.length; + if (test_bit(FF_EFFECT_PLAYING, &state->flags) && effect_length) { + if ((time_now - state->start_time) >= effect_length) { + __clear_bit(FF_EFFECT_PLAYING, &state->flags); + __clear_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags); + + if (state->count) + state->count--; + + if (state->count) + __set_bit(FF_EFFECT_QUEUE_START, &state->flags); + } + } + + if (test_bit(FF_EFFECT_QUEUE_UPLOAD, &state->flags)) { + if (tmff2->upload_effect(tmff2->data, state)) { + hid_warn(tmff2->hdev, "failed uploading effect\n"); + } else { + __clear_bit(FF_EFFECT_QUEUE_UPLOAD, &state->flags); + /* if we're uploading an effect, it's bound to be the up + * to date available */ + __clear_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags); + } + } + + if (test_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags)) { + if (tmff2->update_effect(tmff2->data, state)) + hid_warn(tmff2->hdev, "failed updating effect\n"); + else + __clear_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags); + } + + if (test_bit(FF_EFFECT_QUEUE_START, &state->flags)) { + if (tmff2->play_effect(tmff2->data, state)) { + hid_warn(tmff2->hdev, "failed starting effect\n"); + } else { + __clear_bit(FF_EFFECT_QUEUE_START, &state->flags); + __set_bit(FF_EFFECT_PLAYING, &state->flags); + } + + } + + if (test_bit(FF_EFFECT_QUEUE_STOP, &state->flags)) { + if (tmff2->stop_effect(tmff2->data, state)) { + hid_warn(tmff2->hdev, "failed stopping effect\n"); + } else { + __clear_bit(FF_EFFECT_PLAYING, &state->flags); + __clear_bit(FF_EFFECT_QUEUE_STOP, &state->flags); + } + } + + if (state->count > max_count) + max_count = state->count; + + spin_unlock(&tmff2->lock); + } + + if (max_count && tmff2->allow_scheduling) + schedule_delayed_work(&tmff2->work, msecs_to_jiffies(timer_msecs)); +} + +static int tmff2_upload(struct input_dev *dev, + struct ff_effect *effect, struct ff_effect *old) +{ + struct tmff2_effect_state *state; + struct tmff2_device_entry *tmff2 = tmff2_from_input(dev); + + if (!tmff2) + return -ENODEV; + + if (effect->type == FF_PERIODIC && effect->u.periodic.period == 0) + return -EINVAL; + + state = &tmff2->states[effect->id]; + + spin_lock(&tmff2->lock); + + state->effect = *effect; + + if (old) { + if (!test_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags)) + state->old = *old; + + __set_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags); + } else { + __set_bit(FF_EFFECT_QUEUE_UPLOAD, &state->flags); + } + + spin_unlock(&tmff2->lock); + return 0; +} + +static int tmff2_play(struct input_dev *dev, int effect_id, int value) +{ + struct tmff2_effect_state *state; + struct tmff2_device_entry *tmff2 = tmff2_from_input(dev); + + if (!tmff2) + return -ENODEV; + + state = &tmff2->states[effect_id]; + if (!state) + return 0; + + spin_lock(&tmff2->lock); + if (value > 0) { + state->count = value; + state->start_time = JIFFIES2MS(jiffies); + __set_bit(FF_EFFECT_QUEUE_START, &state->flags); + __clear_bit(FF_EFFECT_QUEUE_STOP, &state->flags); + } else { + __set_bit(FF_EFFECT_QUEUE_STOP, &state->flags); + __clear_bit(FF_EFFECT_QUEUE_START, &state->flags); + } + + spin_unlock(&tmff2->lock); + + if (!delayed_work_pending(&tmff2->work) && tmff2->allow_scheduling) + schedule_delayed_work(&tmff2->work, 0); + + return 0; +} + +static int tmff2_open(struct input_dev *dev) +{ + struct tmff2_device_entry *tmff2 = tmff2_from_input(dev); + + if (!tmff2) + return -ENODEV; + + if (tmff2->open) + return tmff2->open(tmff2->data, open_mode); + + hid_err(tmff2->hdev, "no open callback set\n"); + return -EINVAL; +} + +static void tmff2_close(struct input_dev *dev) +{ + struct tmff2_device_entry *tmff2 = tmff2_from_input(dev); + + if (!tmff2) + return; + + /* since we're closing the device, no need to continue feeding it new data */ + cancel_delayed_work_sync(&tmff2->work); + + if (tmff2->close) { + tmff2->close(tmff2->data, open_mode); + return; + } + + hid_err(tmff2->hdev, "no close callback set\n"); +} + +static int tmff2_create_files(struct tmff2_device_entry *tmff2) +{ + struct device *dev = &tmff2->hdev->dev; + int ret; + + + /* could use short circuiting but this is more explicit */ + if (tmff2->params & PARAM_GAIN) { + if ((ret = device_create_file(dev, &dev_attr_gain))) { + hid_err(tmff2->hdev, "unable to create sysfs for gain\n"); + goto gain_err; + } + } + + if (tmff2->params & PARAM_ALT_MODE) { + if ((ret = device_create_file(dev, &dev_attr_alternate_modes))) { + hid_err(tmff2->hdev, "unable to create sysfs for alternate_modes\n"); + goto alt_err; + } + } + + if (tmff2->params & PARAM_RANGE) { + if ((ret = device_create_file(dev, &dev_attr_range))) { + hid_warn(tmff2->hdev, "unable to create sysfs for range\n"); + goto range_err; + } + } + + if (tmff2->params & PARAM_SPRING_LEVEL) { + if ((ret = device_create_file(dev, &dev_attr_spring_level))) { + hid_warn(tmff2->hdev, "unable to create sysfs for spring_level\n"); + goto spring_err; + } + } + + if (tmff2->params & PARAM_DAMPER_LEVEL) { + if ((ret = device_create_file(dev, &dev_attr_damper_level))) { + hid_warn(tmff2->hdev, "unable to create sysfs for damper_level\n"); + goto damper_err; + } + } + + if (tmff2->params & PARAM_FRICTION_LEVEL) { + if ((ret = device_create_file(dev, &dev_attr_friction_level))) { + hid_warn(tmff2->hdev, "unable to create sysfs for friction_level\n"); + goto friction_err; + } + } + + return 0; + +friction_err: + device_remove_file(dev, &dev_attr_damper_level); +damper_err: + device_remove_file(dev, &dev_attr_spring_level); +spring_err: + device_remove_file(dev, &dev_attr_range); +range_err: + device_remove_file(dev, &dev_attr_alternate_modes); +alt_err: + device_remove_file(dev, &dev_attr_gain); +gain_err: + return ret; +} + +static int tmff2_wheel_init(struct tmff2_device_entry *tmff2) +{ + int ret, i; + struct ff_device *ff; + + spin_lock_init(&lock); + spin_lock_init(&tmff2->lock); + INIT_DELAYED_WORK(&tmff2->work, tmff2_work_handler); + + /* get parameters etc from backend */ + if ((ret = tmff2->wheel_init(tmff2, open_mode))) + goto err; + + + tmff2->states = kzalloc(sizeof(struct tmff2_effect_state) * tmff2->max_effects, + GFP_KERNEL); + + if (!tmff2->states) { + ret = -ENOMEM; + goto err; + } + + /* set supported effects into input_dev->ffbit */ + for (i = 0; tmff2->supported_effects[i] >= 0; ++i) + __set_bit(tmff2->supported_effects[i], tmff2->input_dev->ffbit); + + /* create actual ff device*/ + if ((ret = input_ff_create(tmff2->input_dev, tmff2->max_effects))) { + hid_err(tmff2->hdev, "could not create input_ff\n"); + goto err; + } + + /* set ff callbacks */ + ff = tmff2->input_dev->ff; + ff->upload = tmff2_upload; + ff->playback = tmff2_play; + + if (tmff2->open) + tmff2->input_dev->open = tmff2_open; + + if (tmff2->close) + tmff2->input_dev->close = tmff2_close; + + /* set defaults wherever possible */ + if (tmff2->set_gain) { + ff->set_gain = tmff2_set_gain; + tmff2->set_gain(tmff2->data, (GAIN_MAX * gain) / GAIN_MAX); + } + + if (tmff2->set_autocenter) + ff->set_autocenter = tmff2_set_autocenter; + + if (tmff2->set_range) + tmff2->set_range(tmff2->data, range); + + if (tmff2->switch_mode) + tmff2->switch_mode(tmff2->data, alt_mode); + + /* create files */ + if ((ret = tmff2_create_files(tmff2))) + goto err; + + tmff2->allow_scheduling = 1; + return 0; + + input_ff_destroy(tmff2->input_dev); +err: + return ret; +} + +static int tmff2_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + struct tmff2_device_entry *tmff2 = + kzalloc(sizeof(struct tmff2_device_entry), GFP_KERNEL); + + int ret; + + + if (!tmff2) { + ret = -ENOMEM; + goto oom_err; + } + + tmff2->hdev = hdev; + hid_set_drvdata(tmff2->hdev, tmff2); + + switch (tmff2->hdev->product) { + /* t300rs */ + case TMT300RS_PS3_NORM_ID: + case TMT300RS_PS3_ADV_ID: + case TMT300RS_PS4_NORM_ID: + if ((ret = t300rs_populate_api(tmff2))) + goto wheel_err; + break; + + case TMT248_PC_ID: + if ((ret = t248_populate_api(tmff2))) + goto wheel_err; + break; + + case TX_ACTIVE: + if ((ret = tx_populate_api(tmff2))) + goto wheel_err; + break; + + default: + ret = -ENODEV; + goto wheel_err; + } + + if ((ret = hid_parse(tmff2->hdev))) { + hid_err(hdev, "parse failed\n"); + goto hid_err; + } + + if ((ret = hid_hw_start(tmff2->hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF))) { + hid_err(hdev, "hw start failed\n"); + goto hid_err; + } + + tmff2->input_dev = list_entry(hdev->inputs.next, struct hid_input, list)->input; + + if ((ret = tmff2_wheel_init(tmff2))) { + hid_err(hdev, "init failed\n"); + goto init_err; + } + + return 0; + +init_err: + hid_hw_stop(hdev); +hid_err: + tmff2->wheel_destroy(tmff2->data); +wheel_err: + kfree(tmff2); +oom_err: + return ret; +} + +static __u8 *tmff2_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + struct tmff2_device_entry *tmff2 = tmff2_from_hdev(hdev); + + if (!tmff2) /* not entirely sure what the best course of action would be here */ + return rdesc; + + if (tmff2->wheel_fixup) + return tmff2->wheel_fixup(hdev, rdesc, rsize); + + return rdesc; +} + +static void tmff2_remove(struct hid_device *hdev) +{ + struct tmff2_device_entry *tmff2 = tmff2_from_hdev(hdev); + struct device *dev; + + if (!tmff2) + return; + + tmff2->allow_scheduling = 0; + cancel_delayed_work_sync(&tmff2->work); + + dev = &tmff2->hdev->dev; + if (tmff2->params & PARAM_FRICTION_LEVEL) + device_remove_file(dev, &dev_attr_friction_level); + + if (tmff2->params & PARAM_DAMPER_LEVEL) + device_remove_file(dev, &dev_attr_damper_level); + + if (tmff2->params & PARAM_SPRING_LEVEL) + device_remove_file(dev, &dev_attr_spring_level); + + if (tmff2->params & PARAM_RANGE) + device_remove_file(dev, &dev_attr_range); + + if (tmff2->params & PARAM_ALT_MODE) + device_remove_file(dev, &dev_attr_alternate_modes); + + if (tmff2->params & PARAM_GAIN) + device_remove_file(dev, &dev_attr_gain); + + hid_hw_stop(hdev); + tmff2->wheel_destroy(tmff2->data); + + kfree(tmff2->states); + kfree(tmff2); +} + +static const struct hid_device_id tmff2_devices[] = { + /* t300rs and variations */ + {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)}, + /* t248 PC*/ + {HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMT248_PC_ID)}, + /* tx */ + {HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TX_ACTIVE)}, + + {} +}; +MODULE_DEVICE_TABLE(hid, tmff2_devices); + +static struct hid_driver tmff2_driver = { + .name = "tmff2", + .id_table = tmff2_devices, + .probe = tmff2_probe, + .remove = tmff2_remove, + .report_fixup = tmff2_report_fixup, +}; +module_hid_driver(tmff2_driver); + +MODULE_LICENSE("GPL"); -- cgit v1.3 From 29400ff75b47ef4320c3fccef5860e44fd8f3a62 Mon Sep 17 00:00:00 2001 From: Kimplul Date: Sun, 8 Oct 2023 21:46:49 +0300 Subject: docs: general tidbits --- README.md | 9 +++++---- docs/CONTRIBUTING.md | 9 ++++++--- docs/TODO.md | 12 ++---------- src/hid-tmff2.c | 16 ++++------------ src/hid-tmff2.h | 17 +++++++++-------- src/tmt300rs/hid-tmt300rs.c | 17 +++++++++++------ 6 files changed, 37 insertions(+), 43 deletions(-) (limited to 'src/hid-tmff2.c') diff --git a/README.md b/README.md index 683fb79..56520b9 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ for wheels: [upstreamed](https://github.com/scarburato/hid-tminit), you might want to blacklist the kernel module `hid-thrustmaster`. Do this with ```shell - echo 'blacklist hid_thrustmaster' > /etc/modprobe.d/hid_thrustmaster.con + echo 'blacklist hid_thrustmaster' > /etc/modprobe.d/hid_thrustmaster.conf ``` + If you've bought a new wheel, you will most likely have to update the firmware @@ -141,9 +141,9 @@ for wheels: + If a wheel has a deadzone in games, you can try setting up a udev rule: `/etc/udev/rules.d/99-joydev.rules` - ``` SUBSYSTEM=="input", ATTRS{idVendor}=="044f", - ATTRS{idProduct}=="WHEEL_ID", RUN+="/usr/bin/evdev-joystick --evdev - %E{DEVNAME} --deadzone 0" ``` + ``` + SUBSYSTEM=="input", ATTRS{idVendor}=="044f", ATTRS{idProduct}=="WHEEL_ID", RUN+="/usr/bin/evdev-joystick --evdev %E{DEVNAME} --deadzone 0" + ``` where `WHEEL_ID` is @@ -153,6 +153,7 @@ for wheels: | T300 RS, PS3 advanced mode | b66f | | T300 RS, PS4 normal mode | b66d | | T248 | b696 | + | TX | b669 | This should make sure that the wheel behaves like you'd want from a wheel. diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 46b8b26..7e4b0dd 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -15,13 +15,16 @@ Currently open requests for wheels: Other documents available are linked here: + [FFBEEFFECTS](./FFBEFFECTS.md): - Force feedback effects example for T300RS and compatible wheels + Force feedback effects example for T300RS and compatible wheels + [STRUCTURE](./STRUCTURE.md): - Structure of Thrustmaster device stack + Structure of Thrustmaster device stack + ++ [DRIVER.md](./DRIVER.md): + Info on installing Thrustmaster drivers under Wine + [TODO](./TODO.md): - TO-DO list for maintainers + TODO list for maintainers ## How to capture what effects a game sends to the driver? diff --git a/docs/TODO.md b/docs/TODO.md index 14b8d2e..632a8a8 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -1,11 +1,3 @@ -# TO-DO list +# TODO list -- Make code more general to allow for stuff like T150/T248/T500 to be relatively easily included. - -- Use workqueues instead of doing everything in t300rs_timer - -- Document changes to code in an organised manner. - -### T248 -- open/close seems to crash the wheel and the kernel along with it, check how Windows handles it -- fix rdesc, 0x0a somewhere needs to be set to 0x60 ++ Try to figure out how/if RRRE (#39) determines FFB from wheel input diff --git a/src/hid-tmff2.c b/src/hid-tmff2.c index 44df1e3..013039f 100644 --- a/src/hid-tmff2.c +++ b/src/hid-tmff2.c @@ -101,7 +101,6 @@ static ssize_t spring_level_store(struct device *dev, static ssize_t spring_level_show(struct device *dev, struct device_attribute *attr, char *buf) { - return scnprintf(buf, PAGE_SIZE, "%u\n", spring_level); } static DEVICE_ATTR_RW(spring_level); @@ -132,7 +131,6 @@ static ssize_t damper_level_store(struct device *dev, static ssize_t damper_level_show(struct device *dev, struct device_attribute *attr, char *buf) { - return scnprintf(buf, PAGE_SIZE, "%u\n", damper_level); } static DEVICE_ATTR_RW(damper_level); @@ -163,12 +161,7 @@ static ssize_t friction_level_store(struct device *dev, static ssize_t friction_level_show(struct device *dev, struct device_attribute *attr, char *buf) { - size_t count; - - - count = scnprintf(buf, PAGE_SIZE, "%u\n", friction_level); - - return count; + return scnprintf(buf, PAGE_SIZE, "%u\n", friction_level); } static DEVICE_ATTR_RW(friction_level); @@ -179,7 +172,6 @@ static ssize_t range_store(struct device *dev, unsigned int value; int ret; - if (!tmff2) return -ENODEV; @@ -199,7 +191,6 @@ static ssize_t range_store(struct device *dev, static ssize_t range_show(struct device *dev, struct device_attribute *attr, char *buf) { - return scnprintf(buf, PAGE_SIZE, "%u\n", range); } static DEVICE_ATTR_RW(range); @@ -333,7 +324,7 @@ static void tmff2_work_handler(struct work_struct *w) } else { __clear_bit(FF_EFFECT_QUEUE_UPLOAD, &state->flags); /* if we're uploading an effect, it's bound to be the up - * to date available */ + * to date */ __clear_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags); } } @@ -458,6 +449,8 @@ static void tmff2_close(struct input_dev *dev) return; /* since we're closing the device, no need to continue feeding it new data */ + /* TODO: check somewhere that multiple users can't open us at the same + * time */ cancel_delayed_work_sync(&tmff2->work); if (tmff2->close) { @@ -620,7 +613,6 @@ static int tmff2_probe(struct hid_device *hdev, const struct hid_device_id *id) hid_set_drvdata(tmff2->hdev, tmff2); switch (tmff2->hdev->product) { - /* t300rs */ case TMT300RS_PS3_NORM_ID: case TMT300RS_PS3_ADV_ID: case TMT300RS_PS4_NORM_ID: diff --git a/src/hid-tmff2.h b/src/hid-tmff2.h index c8019dd..1eb3546 100644 --- a/src/hid-tmff2.h +++ b/src/hid-tmff2.h @@ -17,7 +17,7 @@ extern int alt_mode; #define USB_VENDOR_ID_THRUSTMASTER 0x044f /* the wheel seems to only be capable of processing a certain number of - * interrupts per second, and if this value is too low the kernel urb buffer(or + * interrupts per second, and if this value is too low the kernel urb buffer (or * some buffer at least) fills up. Optimally I would figure out some way to * space out the interrupts so that they all leave at regular intervals, but * for now this is good enough, go slow enough that everything works. @@ -66,7 +66,7 @@ struct tmff2_device_entry { int allow_scheduling; - /* fields relevant to each actual device (T300, T150...) */ + /* fields relevant to each actual device (T300, T248...) */ void *data; unsigned long params; unsigned long max_effects; @@ -86,15 +86,16 @@ struct tmff2_device_entry { int (*close)(void *data, int); int (*set_gain)(void *data, uint16_t gain); int (*set_range)(void *data, uint16_t range); - /* switch_mode has to not do anything if we're alredy in the specified - * mode */ + /* switch_mode is required to not do anything if we're alredy in the + * specified mode */ int (*switch_mode)(void *data, uint16_t mode); 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 best option... */ + /* void pointers are dangerous, I know, but in this case likely the + * best option... */ }; /* external */ @@ -108,11 +109,11 @@ int tx_populate_api(struct tmff2_device_entry *tmff2); #define TMT248_PC_ID 0xb696 -#define TX_ACTIVE 0xb669 +#define TX_ACTIVE 0xb669 /* APIs to different wheel families */ -/* T248 at least uses the T300RS api, not sure if there are other wheels but that's - * why these functions are given global linkage */ +/* T248 and TX at least uses the T300RS api, not sure if there are other wheels + * but that's why these functions are given global linkage */ struct t300rs_device_entry { struct hid_device *hdev; diff --git a/src/tmt300rs/hid-tmt300rs.c b/src/tmt300rs/hid-tmt300rs.c index 20bb239..0191620 100644 --- a/src/tmt300rs/hid-tmt300rs.c +++ b/src/tmt300rs/hid-tmt300rs.c @@ -1187,7 +1187,8 @@ int t300rs_update_effect(void *data, struct tmff2_effect_state *state) case FF_PERIODIC: return t300rs_update_periodic(t300rs, state); default: - hid_err(t300rs->hdev, "invalid effect type: %x", state->effect.type); + hid_err(t300rs->hdev, "invalid effect type: %x", + state->effect.type); return -1; } } @@ -1209,7 +1210,8 @@ int t300rs_upload_effect(void *data, struct tmff2_effect_state *state) case FF_PERIODIC: return t300rs_upload_periodic(t300rs, state); default: - hid_err(t300rs->hdev, "invalid effect type: %x", state->effect.type); + hid_err(t300rs->hdev, "invalid effect type: %x", + state->effect.type); return -1; } } @@ -1328,7 +1330,9 @@ int t300rs_set_autocenter(void *data, uint16_t value) if (!t300rs) return -ENODEV; - /* TODO: this should probably also use a separately allocated buffer? */ + /* TODO: this should probably also use a separately allocated buffer? + * someone might change autocentering while we're updating the buffer + * which would cause corruption */ autocenter_packet = (struct t300rs_packet_autocenter *)t300rs->send_buffer; autocenter_packet->header.cmd = 0x08; @@ -1477,7 +1481,7 @@ static int t300rs_check_firmware(struct t300rs_device_entry *t300rs) return -ENOMEM; } - /* Fetch firmware version */ + /* fetch firmware version */ ret = usb_control_msg(t300rs->usbdev, usb_rcvctrlpipe(t300rs->usbdev, 0), t300rs_fw_request.bRequest, @@ -1494,7 +1498,7 @@ static int t300rs_check_firmware(struct t300rs_device_entry *t300rs) goto out; } - /* Educated guess */ + /* educated guess */ if (fw_response->fw_version < 31 && ret >= 0) { hid_err(t300rs->hdev, "firmware version %i is too old, please update.\n", @@ -1574,7 +1578,8 @@ static int t300rs_get_attachment(struct t300rs_device_entry *t300rs) } else if (response->type == cpu_to_le16(0x47)) { attachment = response->b.attachment; } else { - hid_err(t300rs->hdev, "unknown packet type %hx\n, please contact a maintainer", + hid_err(t300rs->hdev, + "unknown packet type %hx\n, please contact a maintainer", response->type); ret = -EINVAL; goto out; -- cgit v1.3