aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2022-03-20 19:41:25 +0200
committerKimplul <kimi.h.kuparinen@gmail.com>2022-03-20 19:41:25 +0200
commit725e8ea20b616b8cd29865a8819112ae2c61c54f (patch)
treead3873b3755e235fe95d3385271b4f883b6248b4
parent28d0a3f76748a2555fc1417216ed51112dba945c (diff)
downloadhid-tmff2-725e8ea20b616b8cd29865a8819112ae2c61c54f.tar.gz
hid-tmff2-725e8ea20b616b8cd29865a8819112ae2c61c54f.zip
add initial t248 support
-rw-r--r--Kbuild2
-rw-r--r--TODO3
-rw-r--r--hid-tmff2.c7
-rw-r--r--hid-tmff2.h38
-rw-r--r--hid-tmt248.c201
-rw-r--r--hid-tmt300rs.c34
6 files changed, 256 insertions, 29 deletions
diff --git a/Kbuild b/Kbuild
index 697f3e1..590c69d 100644
--- a/Kbuild
+++ b/Kbuild
@@ -1,2 +1,2 @@
obj-m := hid-tmff-new.o
-hid-tmff-new-y := hid-tmff2.o hid-tmt300rs.o
+hid-tmff-new-y := hid-tmff2.o hid-tmt300rs.o hid-tmt248.o
diff --git a/TODO b/TODO
index 87db3d1..70d3652 100644
--- a/TODO
+++ b/TODO
@@ -2,3 +2,6 @@
easily included.
+ Use workqueues instead of doing everything in t300rs_timer
+
++ T248 open/close seems to crash the wheel and the kernel along with it, check how Windows handles it
++ T248 fix rdesc, 0x0a somewhere needs to be set to 0x60
diff --git a/hid-tmff2.c b/hid-tmff2.c
index 2e7221f..4aa6141 100644
--- a/hid-tmff2.c
+++ b/hid-tmff2.c
@@ -552,6 +552,11 @@ static int tmff2_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto wheel_err;
break;
+ case TMT248_PC_ID:
+ if ((ret = t248_populate_api(tmff2)))
+ goto wheel_err;
+ break;
+
default:
ret = -ENODEV;
goto wheel_err;
@@ -634,6 +639,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)},
+ /* t248 PC*/
+ {HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMT248_PC_ID)},
{}
};
MODULE_DEVICE_TABLE(hid, tmff2_devices);
diff --git a/hid-tmff2.h b/hid-tmff2.h
index d59104a..f474ca7 100644
--- a/hid-tmff2.h
+++ b/hid-tmff2.h
@@ -91,9 +91,41 @@ struct tmff2_device_entry {
/* external */
int t300rs_populate_api(struct tmff2_device_entry *tmff2);
+int t248_populate_api(struct tmff2_device_entry *tmff2);
-#define TMT300RS_PS3_NORM_ID 0xb66e
-#define TMT300RS_PS3_ADV_ID 0xb66f
-#define TMT300RS_PS4_NORM_ID 0xb66d
+#define TMT300RS_PS3_NORM_ID 0xb66e
+#define TMT300RS_PS3_ADV_ID 0xb66f
+#define TMT300RS_PS4_NORM_ID 0xb66d
+
+#define TMT248_PC_ID 0xb696
+
+/* 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 */
+
+struct t300rs_device_entry {
+ struct hid_device *hdev;
+ struct input_dev *input_dev;
+ struct hid_report *report;
+ struct hid_field *ff_field;
+ struct usb_device *usbdev;
+
+ int (*open)(struct input_dev *dev);
+ void (*close)(struct input_dev *dev);
+
+ u8 buffer_length;
+ u8 *send_buffer;
+};
+
+int t300rs_play_effect(void *, struct tmff2_effect_state *);
+int t300rs_upload_effect(void *, struct tmff2_effect_state *);
+int t300rs_update_effect(void *, struct tmff2_effect_state *);
+int t300rs_stop_effect(void *, struct tmff2_effect_state *);
+
+int t300rs_open(void *);
+int t300rs_close(void *);
+int t300rs_set_gain(void *, uint16_t);
+int t300rs_set_range(void *, uint16_t);
+int t300rs_set_autocenter(void *, uint16_t);
#endif /* __HID_TMFF2_H */
diff --git a/hid-tmt248.c b/hid-tmt248.c
new file mode 100644
index 0000000..5a19bb0
--- /dev/null
+++ b/hid-tmt248.c
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/usb.h>
+#include <linux/hid.h>
+#include "hid-tmff2.h"
+
+#define T248_MAX_EFFECTS 16
+#define T248_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 t248_params =
+ PARAM_SPRING_LEVEL
+ | PARAM_DAMPER_LEVEL
+ | PARAM_FRICTION_LEVEL
+ | PARAM_RANGE
+ ;
+
+static const signed short t248_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
+};
+
+static int t248_interrupts(struct t300rs_device_entry *t248)
+{
+ u8 *send_buf = kmalloc(256, GFP_KERNEL);
+ struct usb_interface *usbif = to_usb_interface(t248->hdev->dev.parent);
+ struct usb_host_endpoint *ep;
+ int ret, trans, b_ep, i;
+ if (!send_buf) {
+ hid_err(t248->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(t248->usbdev,
+ usb_sndintpipe(t248->usbdev, b_ep),
+ send_buf, setup_arr_sizes[i],
+ &trans,
+ USB_CTRL_SET_TIMEOUT);
+
+ if (ret) {
+ hid_err(t248->hdev, "setup data couldn't be sent\n");
+ goto err;
+ }
+ }
+
+err:
+ kfree(send_buf);
+ return ret;
+}
+
+int t248_wheel_init(struct tmff2_device_entry *tmff2)
+{
+ struct t300rs_device_entry *t248 = kzalloc(sizeof(struct t300rs_device_entry), GFP_KERNEL);
+ struct list_head *report_list;
+ int ret;
+
+ if (!t248) {
+ ret = -ENOMEM;
+ goto t248_err;
+ }
+
+ t248->hdev = tmff2->hdev;
+ t248->input_dev = tmff2->input_dev;
+ t248->usbdev = to_usb_device(tmff2->hdev->dev.parent->parent);
+ t248->buffer_length = T248_BUFFER_LENGTH;
+
+ t248->send_buffer = kzalloc(t248->buffer_length, GFP_KERNEL);
+ if (!t248->send_buffer) {
+ ret = -ENOMEM;
+ goto send_err;
+ }
+
+ report_list = &t248->hdev->report_enum[HID_OUTPUT_REPORT].report_list;
+ t248->report = list_entry(report_list->next, struct hid_report, list);
+ t248->ff_field = t248->report->field[0];
+
+ t248->open = t248->input_dev->open;
+ t248->close = t248->input_dev->close;
+
+ if ((ret = t248_interrupts(t248)))
+ goto interrupt_err;
+
+ /* everything went OK */
+ tmff2->data = t248;
+ tmff2->params = t248_params;
+ tmff2->max_effects = T248_MAX_EFFECTS;
+ memcpy(tmff2->supported_effects, t248_effects, sizeof(t248_effects));
+
+ hid_info(t248->hdev, "force feedback for T248\n");
+ return 0;
+
+interrupt_err:
+send_err:
+ kfree(t248);
+t248_err:
+ hid_err(tmff2->hdev, "failed initializing T248\n");
+ return ret;
+}
+
+int t248_wheel_destroy(void *data)
+{
+ struct t300rs_device_entry *t300rs = data;
+ if (!t300rs)
+ return -ENODEV;
+
+ kfree(t300rs->send_buffer);
+ kfree(t300rs);
+ return 0;
+}
+
+int t248_set_range(void *data, uint16_t value)
+{
+ struct t300rs_device_entry *t248 = data;
+ if (value < 140) {
+ hid_info(t248->hdev, "value %i too small, clamping to 140\n", value);
+ value = 140;
+ }
+
+ if (value > 900) {
+ hid_info(t248->hdev, "value %i too large, clamping to 900\n", value);
+ value = 900;
+ }
+
+ return t300rs_set_range(data, value);
+}
+
+static int t248_open(void *data)
+{
+ struct t300rs_device_entry *t248 = data;
+ if (!t248)
+ return -ENODEV;
+
+ /* TODO: send usb commands to actually open device */
+ return t248->open(t248->input_dev);
+}
+
+static int t248_close(void *data)
+{
+ struct t300rs_device_entry *t248 = data;
+ if (!t248)
+ return -ENODEV;
+
+ /* TODO: send usb commands to actually close device */
+ t248->close(t248->input_dev);
+ return 0;
+}
+
+int t248_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->wheel_init = t248_wheel_init;
+ tmff2->wheel_destroy = t248_wheel_destroy;
+
+ tmff2->open = t248_open;
+ tmff2->close = t248_close;
+ tmff2->set_gain = t300rs_set_gain;
+ /* T248 only has 900 degree range, instead of T300RS 1080 */
+ tmff2->set_range = t248_set_range;
+ tmff2->set_autocenter = t300rs_set_autocenter;
+
+ return 0;
+}
diff --git a/hid-tmt300rs.c b/hid-tmt300rs.c
index 8c4655b..92e9acd 100644
--- a/hid-tmt300rs.c
+++ b/hid-tmt300rs.c
@@ -75,22 +75,6 @@ struct usb_ctrlrequest t300rs_fw_request = {
.wLength = 8
};
-
-struct t300rs_device_entry {
- struct hid_device *hdev;
- struct input_dev *input_dev;
- struct hid_report *report;
- struct hid_field *ff_field;
- struct usb_device *usbdev;
-
- int (*open)(struct input_dev *dev);
- void (*close)(struct input_dev *dev);
-
- u8 buffer_length;
- u8 *send_buffer;
-};
-
-
struct t300rs_data {
unsigned long quirks;
void *device_props;
@@ -390,7 +374,7 @@ static void t300rs_fill_header(struct t300rs_packet_header *packet_header,
packet_header->code = code;
}
-static int t300rs_play_effect(void *data, struct tmff2_effect_state *state)
+int t300rs_play_effect(void *data, struct tmff2_effect_state *state)
{
struct t300rs_device_entry *t300rs = data;
struct __packed t300rs_packet_play {
@@ -411,7 +395,7 @@ static int t300rs_play_effect(void *data, struct tmff2_effect_state *state)
return ret;
}
-static int t300rs_stop_effect(void *data, struct tmff2_effect_state *state)
+int t300rs_stop_effect(void *data, struct tmff2_effect_state *state)
{
struct t300rs_device_entry *t300rs = data;
struct __packed t300rs_packet_stop {
@@ -1132,7 +1116,7 @@ static int t300rs_upload_periodic(struct t300rs_device_entry *t300rs,
return ret;
}
-static int t300rs_update_effect(void *data, struct tmff2_effect_state *state)
+int t300rs_update_effect(void *data, struct tmff2_effect_state *state)
{
struct t300rs_device_entry *t300rs = data;
switch (state->effect.type) {
@@ -1154,7 +1138,7 @@ static int t300rs_update_effect(void *data, struct tmff2_effect_state *state)
}
}
-static int t300rs_upload_effect(void *data, struct tmff2_effect_state *state)
+int t300rs_upload_effect(void *data, struct tmff2_effect_state *state)
{
struct t300rs_device_entry *t300rs = data;
switch (state->effect.type) {
@@ -1201,7 +1185,7 @@ static int t300rs_switch_mode(void *data, uint16_t mode)
return 0;
}
-static int t300rs_set_autocenter(void *data, uint16_t value)
+int t300rs_set_autocenter(void *data, uint16_t value)
{
struct t300rs_device_entry *t300rs = data;
struct __packed t300rs_packet_autocenter {
@@ -1236,7 +1220,7 @@ static int t300rs_set_autocenter(void *data, uint16_t value)
return ret;
}
-static int t300rs_set_gain(void *data, uint16_t gain)
+int t300rs_set_gain(void *data, uint16_t gain)
{
struct t300rs_device_entry *t300rs = data;
struct __packed t300rs_packet_gain {
@@ -1257,7 +1241,7 @@ static int t300rs_set_gain(void *data, uint16_t gain)
return ret;
}
-static int t300rs_set_range(void *data, uint16_t value)
+int t300rs_set_range(void *data, uint16_t value)
{
struct t300rs_device_entry *t300rs = data;
/* it's important that we don't use t300rs->send_buffer, as range can be
@@ -1299,7 +1283,7 @@ err:
return ret;
}
-static int t300rs_open(void *data)
+int t300rs_open(void *data)
{
struct t300rs_device_entry *t300rs = data;
struct __packed t300rs_packet_open {
@@ -1319,7 +1303,7 @@ static int t300rs_open(void *data)
return t300rs->open(t300rs->input_dev);
}
-static int t300rs_close(void *data)
+int t300rs_close(void *data)
{
struct t300rs_device_entry *t300rs = data;
struct t300rs_packet_close {