diff options
| author | Kimplul <kimi.h.kuparinen@gmail.com> | 2024-09-29 17:14:10 +0300 |
|---|---|---|
| committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2025-11-23 14:37:44 +0200 |
| commit | f1b2b85a4920d8c78b5b2b51b8cf5ddb1e8e03fd (patch) | |
| tree | ce23c4bf065422bb3a239f79519c1671b720420f /src | |
| parent | 2a7b3568792d50e94479298b5d0e5602d4e230f8 (diff) | |
| download | hid-tmff2-f1b2b85a4920d8c78b5b2b51b8cf5ddb1e8e03fd.tar.gz hid-tmff2-f1b2b85a4920d8c78b5b2b51b8cf5ddb1e8e03fd.zip | |
apply initial tspc patches
+ Courtesy of @BDave95
Diffstat (limited to 'src')
| -rw-r--r-- | src/hid-tmff2.c | 6 | ||||
| -rw-r--r-- | src/hid-tmff2.h | 3 | ||||
| -rw-r--r-- | src/tmtspc/hid-tmtspc.c | 324 |
3 files changed, 333 insertions, 0 deletions
diff --git a/src/hid-tmff2.c b/src/hid-tmff2.c index ee3188a..162a30a 100644 --- a/src/hid-tmff2.c +++ b/src/hid-tmff2.c @@ -707,6 +707,10 @@ static int tmff2_probe(struct hid_device *hdev, const struct hid_device_id *id) if ((ret = tsxw_populate_api(tmff2))) goto wheel_err; break; + case TMTS_PC_RACER_ID: + if ((ret = tspc_populate_api(tmff2))) + goto wheel_err; + break; default: ret = -ENODEV; goto wheel_err; @@ -806,6 +810,8 @@ static const struct hid_device_id tmff2_devices[] = { {HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMT248_PC_ID)}, /* tx */ {HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TX_ACTIVE)}, + /* TS-PC RACER */ + {HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMTS_PC_RACER_ID)}, /* tsxw */ {HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TSXW_ACTIVE)}, diff --git a/src/hid-tmff2.h b/src/hid-tmff2.h index f747f44..04bc368 100644 --- a/src/hid-tmff2.h +++ b/src/hid-tmff2.h @@ -103,6 +103,7 @@ int t300rs_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); +int tspc_populate_api(struct tmff2_device_entry *tmff2); #define TMT300RS_PS3_NORM_ID 0xb66e #define TMT300RS_PS3_ADV_ID 0xb66f @@ -114,6 +115,8 @@ int tsxw_populate_api(struct tmff2_device_entry *tmff2); #define TSXW_ACTIVE 0xb692 +#define TMTS_PC_RACER_ID 0xb689 + /* APIs to different wheel families */ /* 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 */ diff --git a/src/tmtspc/hid-tmtspc.c b/src/tmtspc/hid-tmtspc.c new file mode 100644 index 0000000..3f85ba7 --- /dev/null +++ b/src/tmtspc/hid-tmtspc.c @@ -0,0 +1,324 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include <linux/usb.h> +#include <linux/hid.h> +#include "../hid-tmff2.h" + +#define TMTSPC_MAX_EFFECTS 16 +#define TMTSPC_BUFFER_LENGTH 63 + +static const u8 setup_0[64] = { 0x42, 0x01 }; +static const u8 setup_1[64] = { 0x0a, 0x04, 0x90, 0x03 }; +static const u8 setup_2[64] = { 0x0a, 0x04, 0x00, 0x0c }; +static const u8 setup_3[64] = { 0x0a, 0x04, 0x12, 0x10 }; +static const u8 setup_4[64] = { 0x0a, 0x04, 0x00, 0x06 }; +static const u8 setup_5[64] = { 0x0a, 0x04, 0x00, 0x0e }; +static const u8 setup_6[64] = { 0x0a, 0x04, 0x00, 0x0e, 0x01 }; +static const u8 *const setup_arr[] = { setup_0, setup_1, setup_2, setup_3, setup_4, setup_5, setup_6 }; +static const unsigned int setup_arr_sizes[] = { + ARRAY_SIZE(setup_0), + ARRAY_SIZE(setup_1), + ARRAY_SIZE(setup_2), + ARRAY_SIZE(setup_3), + ARRAY_SIZE(setup_4), + ARRAY_SIZE(setup_5), + ARRAY_SIZE(setup_6) +}; + +static const unsigned long tspc_params = + PARAM_SPRING_LEVEL + | PARAM_DAMPER_LEVEL + | PARAM_FRICTION_LEVEL + | PARAM_RANGE + | PARAM_GAIN + ; + +static const signed short tspc_effects[] = { + FF_CONSTANT, + FF_RAMP, + FF_SPRING, + FF_DAMPER, + FF_FRICTION, + FF_INERTIA, + FF_PERIODIC, + FF_SINE, + FF_TRIANGLE, + FF_SQUARE, + FF_SAW_UP, + FF_SAW_DOWN, + FF_AUTOCENTER, + FF_GAIN, + -1 +}; + +/* TODO: sort through this stuff */ +static u8 tspc_pc_rdesc_fixed[] = { + 0x05, 0x01, /* Usage page (Generic Desktop) */ + 0x09, 0x04, /* Usage (Joystick) */ + 0xa1, 0x01, /* Collection (Application) */ + 0x09, 0x01, /* Usage (Pointer) */ + 0xa1, 0x00, /* Collection (Physical) */ + 0x85, 0x07, /* Report ID (7) */ + 0x09, 0x30, /* Usage (X) */ + 0x15, 0x00, /* Logical minimum (0) */ + 0x27, 0xff, 0xff, 0x00, 0x00, /* Logical maximum (65535) */ + 0x35, 0x00, /* Physical minimum (0) */ + 0x47, 0xff, 0xff, 0x00, 0x00, /* Physical maximum (65535) */ + 0x75, 0x10, /* Report size (16) */ + 0x95, 0x01, /* Report count (1) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x09, 0x35, /* Usage (Rz) (Brake) */ + 0x26, 0xff, 0x03, /* Logical maximum (1023) */ + 0x46, 0xff, 0x03, /* Physical maximum (1023) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x09, 0x32, /* Usage (Z) (Gas) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x09, 0x31, /* Usage (Y) (Clutch) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x81, 0x03, /* Input (Variable, Absolute, Constant) */ + 0x05, 0x09, /* Usage page (Button) */ + 0x19, 0x01, /* Usage minimum (1) */ + 0x29, 0x0d, /* Usage maximum (13) */ + 0x25, 0x01, /* Logical maximum (1) */ + 0x45, 0x01, /* Physical maximum (1) */ + 0x75, 0x01, /* Report size (1) */ + 0x95, 0x0d, /* Report count (13) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x75, 0x0b, /* Report size (13) */ + 0x95, 0x01, /* Report count (1) */ + 0x81, 0x03, /* Usage (Variable, Absolute, Constant) */ + 0x05, 0x01, /* Usage page (Generic Desktop) */ + 0x09, 0x39, /* Usage (Hat Switch) */ + 0x25, 0x07, /* Logical maximum (7) */ + 0x46, 0x3b, 0x01, /* Physical maximum (315) */ + 0x55, 0x00, /* Unit exponent (0) */ + 0x65, 0x14, /* Unit (Eng Rot, Angular Pos) */ + 0x75, 0x04, /* Report size (4) */ + 0x81, 0x42, /* Input (Variable, Absolute, NullState) */ + 0x65, 0x00, /* Unit (None) */ + 0x81, 0x03, /* Input (Variable, Absolute, Constant) */ + 0x85, 0x60, /* Report ID (96), prev 10 */ + 0x06, 0x00, 0xff, /* Usage page (Vendor 1) */ + 0x09, 0x60, /* Usage (96), prev 10 */ + 0x75, 0x08, /* Report size (8) */ + 0x95, 0x3f, /* Report count (63) */ + 0x26, 0xff, 0x7f, /* Logical maximum (32767) */ + 0x15, 0x00, /* Logical minimum (0) */ + 0x46, 0xff, 0x7f, /* Physical maximum (32767) */ + 0x36, 0x00, 0x80, /* Physical minimum (-32768) */ + 0x91, 0x02, /* Output (Variable, Absolute) */ + 0x85, 0x02, /* Report ID (2) */ + 0x09, 0x02, /* Usage (2) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x09, 0x14, /* Usage (20) */ + 0x85, 0x14, /* Report ID (20) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0xc0, /* End collection */ + 0xc0, /* End collection */ +}; + +static int tspc_interrupts(struct t300rs_device_entry *tspc) +{ + u8 *send_buf = kmalloc(256, GFP_KERNEL); + struct usb_interface *usbif = to_usb_interface(tspc->hdev->dev.parent); + struct usb_host_endpoint *ep; + int ret, trans, b_ep, i; + + if (!send_buf) { + hid_err(tspc->hdev, "failed allocating send buffer\n"); + return -ENOMEM; + } + + ep = &usbif->cur_altsetting->endpoint[1]; + b_ep = ep->desc.bEndpointAddress; + + for (i = 0; i < ARRAY_SIZE(setup_arr); ++i) { + memcpy(send_buf, setup_arr[i], setup_arr_sizes[i]); + + ret = usb_interrupt_msg(tspc->usbdev, + usb_sndintpipe(tspc->usbdev, b_ep), + send_buf, setup_arr_sizes[i], + &trans, + USB_CTRL_SET_TIMEOUT); + + if (ret) { + hid_err(tspc->hdev, "setup data couldn't be sent\n"); + goto err; + } + } + +err: + kfree(send_buf); + return ret; +} + +static int tspc_wheel_destroy(void *data) +{ + struct t300rs_device_entry *t300rs = data; + + if (!t300rs) + return -ENODEV; + + kfree(t300rs->send_buffer); + kfree(t300rs); + return 0; +} + +static int tspc_set_range(void *data, uint16_t value) +{ + struct t300rs_device_entry *tspc = data; + + if (value < 140) { + hid_info(tspc->hdev, "value %i too small, clamping to 140\n", value); + value = 140; + } + + if (value > 900) { + hid_info(tspc->hdev, "value %i too large, clamping to 1080\n", value); + value = 900; + } + + return t300rs_set_range(data, value); +} + +static int tspc_send_open(struct t300rs_device_entry *tspc) +{ + int r1, r2; + tspc->send_buffer[0] = 0x01; + tspc->send_buffer[1] = 0x04; + if ((r1 = t300rs_send_int(tspc))) + return r1; + + tspc->send_buffer[0] = 0x01; + tspc->send_buffer[1] = 0x05; + if ((r2 = t300rs_send_int(tspc))) + return r2; + + return 0; +} + +static int tspc_open(void *data, int open_mode) +{ + struct t300rs_device_entry *tspc = data; + + if (!tspc) + return -ENODEV; + + if (open_mode) + tspc_send_open(tspc); + + return tspc->open(tspc->input_dev); +} + +static int tspc_send_close(struct t300rs_device_entry *tspc) +{ + int r1, r2; + tspc->send_buffer[0] = 0x01; + tspc->send_buffer[1] = 0x05; + if ((r1 = t300rs_send_int(tspc))) + return r1; + + tspc->send_buffer[0] = 0x01; + tspc->send_buffer[1] = 0x00; + if ((r2 = t300rs_send_int(tspc))) + return r2; + + return 0; +} + +static int tspc_close(void *data, int open_mode) +{ + struct t300rs_device_entry *tspc = data; + + if (!tspc) + return -ENODEV; + + if (open_mode) + tspc_send_close(tspc); + + tspc->close(tspc->input_dev); + return 0; +} + +static int tspc_wheel_init(struct tmff2_device_entry *tmff2, int open_mode) +{ + struct t300rs_device_entry *tspc = kzalloc(sizeof(struct t300rs_device_entry), + GFP_KERNEL); + struct list_head *report_list; + int ret; + + + if (!tspc) { + ret = -ENOMEM; + goto tspc_err; + } + + tspc->hdev = tmff2->hdev; + tspc->input_dev = tmff2->input_dev; + tspc->usbdev = to_usb_device(tmff2->hdev->dev.parent->parent); + tspc->buffer_length = TMTSPC_BUFFER_LENGTH; + + tspc->send_buffer = kzalloc(tspc->buffer_length, GFP_KERNEL); + if (!tspc->send_buffer) { + ret = -ENOMEM; + goto send_err; + } + + report_list = &tspc->hdev->report_enum[HID_OUTPUT_REPORT].report_list; + tspc->report = list_entry(report_list->next, struct hid_report, list); + tspc->ff_field = tspc->report->field[0]; + + tspc->open = tspc->input_dev->open; + tspc->close = tspc->input_dev->close; + + if ((ret = tspc_interrupts(tspc))) + goto interrupt_err; + + /* everything went OK */ + tmff2->data = tspc; + tmff2->params = tspc_params; + tmff2->max_effects = TMTSPC_MAX_EFFECTS; + memcpy(tmff2->supported_effects, tspc_effects, sizeof(tspc_effects)); + + if (!open_mode) + tspc_send_open(tspc); + + hid_info(tspc->hdev, "force feedback for TS-XW\n"); + return 0; + +interrupt_err: +send_err: + kfree(tspc); +tspc_err: + hid_err(tmff2->hdev, "failed initializing TS-XW\n"); + return ret; +} + +static __u8 *tspc_wheel_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + rdesc = tspc_pc_rdesc_fixed; + *rsize = sizeof(tspc_pc_rdesc_fixed); + return rdesc; +} + +int tspc_populate_api(struct tmff2_device_entry *tmff2) +{ + tmff2->play_effect = t300rs_play_effect; + tmff2->upload_effect = t300rs_upload_effect; + tmff2->update_effect = t300rs_update_effect; + tmff2->stop_effect = t300rs_stop_effect; + + tmff2->set_gain = t300rs_set_gain; + tmff2->set_autocenter = t300rs_set_autocenter; + /* TS-PC has 900 degree range, unlike T300RS 1080 */ + tmff2->set_range = tspc_set_range; + tmff2->wheel_fixup = tspc_wheel_fixup; + + tmff2->open = tspc_open; + tmff2->close = tspc_close; + + tmff2->wheel_init = tspc_wheel_init; + tmff2->wheel_destroy = tspc_wheel_destroy; + + return 0; +} |
