aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Pagani <dario.pagani.146@gmail.com>2022-05-21 21:26:28 +0200
committerGitHub <noreply@github.com>2022-05-21 21:26:28 +0200
commit778cfe9e98cbcf972cb42d7dbf6f47be2e0f5cee (patch)
tree9ffbe2e3e45e5e9cf787b5b486a97e4e3ff9fe24
parent53fde059e88a4314d987ab75518edb8ce86cf8a3 (diff)
downloadhid-tminit-778cfe9e98cbcf972cb42d7dbf6f47be2e0f5cee.tar.gz
hid-tminit-778cfe9e98cbcf972cb42d7dbf6f47be2e0f5cee.zip
Sync with Linux + better attachment handling (#14)
* ported stuff from mainline Linux to here. preparing from the next patch * logic to find the attachment (if exists)
-rw-r--r--hid-tminit.c224
-rw-r--r--hid-tminit.h124
2 files changed, 188 insertions, 160 deletions
diff --git a/hid-tminit.c b/hid-tminit.c
index b64d1f0..266086e 100644
--- a/hid-tminit.c
+++ b/hid-tminit.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* When connected to the machine, the Thrustmaster wheels appear as
* a «generic» hid gamepad called "Thrustmaster FFB Wheel".
*
@@ -18,14 +18,139 @@
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/module.h>
-#include "hid-tminit.h"
-/**
+/*
+ * These interrupts are used to prevent a nasty crash when initializing the
+ * T300RS. Used in thrustmaster_interrupts().
+ */
+static const u8 setup_0[] = { 0x42, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static const u8 setup_1[] = { 0x0a, 0x04, 0x90, 0x03, 0x00, 0x00, 0x00, 0x00 };
+static const u8 setup_2[] = { 0x0a, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00 };
+static const u8 setup_3[] = { 0x0a, 0x04, 0x12, 0x10, 0x00, 0x00, 0x00, 0x00 };
+static const u8 setup_4[] = { 0x0a, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00 };
+static const u8 *const setup_arr[] = { setup_0, setup_1, setup_2, setup_3, setup_4 };
+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)
+};
+/*
+ * This struct contains for each type of
+ * Thrustmaster wheel
+ *
+ * Note: The values are stored in the CPU
+ * endianness, the USB protocols always use
+ * little endian; the macro cpu_to_le[BIT]()
+ * must be used when preparing USB packets
+ * and vice-versa
+ */
+struct tm_wheel_info {
+ uint8_t model;
+ uint8_t attachment;
+
+ /**
+ * See when the USB control out packet is prepared...
+ * @TODO The TMX seems to require multiple control codes to switch.
+ */
+ uint16_t switch_value;
+
+ char const *const wheel_name;
+};
+
+/*
+ * Known wheels.
+ * Note: TMX does not work as it requires 2 control packets
+ */
+static const struct tm_wheel_info tm_wheels_infos[] = {
+ {0x00, 0x02, 0x0002, "Thrustmaster T500RS"},
+ {0x02, 0x00, 0x0005, "Thrustmaster T300RS (Missing Attachment)"},
+ {0x02, 0x03, 0x0005, "Thrustmaster T300RS (F1 attachment)"},
+ {0x02, 0x04, 0x0005, "Thrustmaster T300 Ferrari Alcantara Edition"},
+ {0x02, 0x06, 0x0005, "Thrustmaster T300RS"},
+ {0x02, 0x09, 0x0005, "Thrustmaster T300RS (Open Wheel Attachment)"},
+ {0x03, 0x06, 0x0006, "Thrustmaster T150RS"}
+ //{0x04, 0x07, 0x0001, "Thrustmaster TMX"}
+};
+
+static const uint8_t tm_wheels_infos_length = 7;
+
+/*
+ * This structs contains (in little endian) the response data
+ * of the wheel to the request 73
+ *
+ * A sufficient research to understand what each field does is not
+ * beign conducted yet. The position and meaning of fields are a
+ * just a very optimistic guess based on instinct....
+ */
+struct __packed tm_wheel_response
+{
+ /*
+ * Seems to be the type of packet
+ * - 0x0049 if is data.a (15 bytes)
+ * - 0x0047 if is data.b (7 bytes)
+ */
+ uint16_t type;
+
+ union {
+ struct __packed {
+ uint16_t field0;
+ uint16_t field1;
+ /*
+ * Seems to be the model code of the wheel
+ * Read table thrustmaster_wheels to values
+ */
+ uint8_t attachment;
+ uint8_t model;
+
+ uint16_t field2;
+ uint16_t field3;
+ uint16_t field4;
+ uint16_t field5;
+ } a;
+ struct __packed {
+ uint16_t field0;
+ uint16_t field1;
+ uint8_t attachment;
+ uint8_t model;
+ } b;
+ } data;
+};
+
+struct tm_wheel {
+ struct usb_device *usb_dev;
+ struct urb *urb;
+
+ struct usb_ctrlrequest *model_request;
+ struct tm_wheel_response *response;
+
+ struct usb_ctrlrequest *change_request;
+};
+
+/* The control packet to send to wheel */
+static const struct usb_ctrlrequest model_request = {
+ .bRequestType = 0xc1,
+ .bRequest = 73,
+ .wValue = 0,
+ .wIndex = 0,
+ .wLength = cpu_to_le16(0x0010)
+};
+
+static const struct usb_ctrlrequest change_request = {
+ .bRequestType = 0x41,
+ .bRequest = 83,
+ .wValue = 0, // Will be filled by the driver
+ .wIndex = 0,
+ .wLength = 0
+};
+
+/*
* On some setups initializing the T300RS crashes the kernel,
* these interrupts fix that particular issue. So far they haven't caused any
* adverse effects in other wheels.
*/
-static void tminit_interrupts(struct hid_device *hdev)
+static void thrustmaster_interrupts(struct hid_device *hdev)
{
int ret, trans, i, b_ep;
u8 *send_buf = kmalloc(256, GFP_KERNEL);
@@ -39,6 +164,12 @@ static void tminit_interrupts(struct hid_device *hdev)
return;
}
+ if (usbif->cur_altsetting->desc.bNumEndpoints < 2) {
+ kfree(send_buf);
+ hid_err(hdev, "Wrong number of endpoints?\n");
+ return;
+ }
+
ep = &usbif->cur_altsetting->endpoint[1];
b_ep = ep->desc.bEndpointAddress;
@@ -54,6 +185,7 @@ static void tminit_interrupts(struct hid_device *hdev)
if (ret) {
hid_err(hdev, "setup data couldn't be sent\n");
+ kfree(send_buf);
return;
}
}
@@ -61,7 +193,7 @@ static void tminit_interrupts(struct hid_device *hdev)
kfree(send_buf);
}
-static void tminit_change_handler(struct urb *urb)
+static void thrustmaster_change_handler(struct urb *urb)
{
struct hid_device *hdev = urb->context;
@@ -72,31 +204,35 @@ static void tminit_change_handler(struct urb *urb)
hid_warn(hdev, "URB to change wheel mode seems to have failed with error %d\n", urb->status);
}
-/**
+/*
* Called by the USB subsystem when the wheel responses to our request
* to get [what it seems to be] the wheel's model.
*
* If the model id is recognized then we send an opportune USB CONTROL REQUEST
* to switch the wheel to its full capabilities
*/
-static void tminit_model_handler(struct urb *urb)
+static void thrustmaster_model_handler(struct urb *urb)
{
struct hid_device *hdev = urb->context;
struct tm_wheel *tm_wheel = hid_get_drvdata(hdev);
uint8_t model = 0;
+ uint8_t attachment = 0;
+ uint8_t attachment_found;
int i, ret;
- const struct tm_wheel_info *twi = 0;
+ const struct tm_wheel_info *twi = NULL;
if (urb->status) {
hid_err(hdev, "URB to get model id failed with error %d\n", urb->status);
return;
}
- if (tm_wheel->response->type == cpu_to_le16(0x49))
+ if (tm_wheel->response->type == cpu_to_le16(0x49)) {
model = tm_wheel->response->data.a.model;
- else if (tm_wheel->response->type == cpu_to_le16(0x47))
+ attachment = tm_wheel->response->data.a.attachment;
+ } else if (tm_wheel->response->type == cpu_to_le16(0x47)) {
model = tm_wheel->response->data.b.model;
- else {
+ attachment = tm_wheel->response->data.b.attachment;
+ } else {
hid_err(hdev, "Unknown packet type 0x%x, unable to proceed further with wheel init\n", tm_wheel->response->type);
return;
}
@@ -105,9 +241,16 @@ static void tminit_model_handler(struct urb *urb)
if (tm_wheels_infos[i].model == model)
twi = tm_wheels_infos + i;
- if (twi)
- hid_info(hdev, "Wheel with model 0x%x is a %s\n", model, twi->wheel_name);
- else {
+ if (twi) {
+ // Trying to find the best attachment
+ for (attachment_found = twi->attachment == attachment; !attachment_found && i < tm_wheels_infos_length && tm_wheels_infos[i].model == model; i++)
+ if (tm_wheels_infos[i].attachment == attachment) {
+ twi = tm_wheels_infos + i;
+ attachment_found = 1;
+ }
+
+ hid_info(hdev, "Wheel with (model, attachment) = (0x%x, 0x%x) is a %s. attachment_found=%u\n", model, attachment, twi->wheel_name, attachment_found);
+ } else {
hid_err(hdev, "Unknown wheel's model id 0x%x, unable to proceed further with wheel init\n", model);
return;
}
@@ -118,8 +261,8 @@ static void tminit_model_handler(struct urb *urb)
tm_wheel->usb_dev,
usb_sndctrlpipe(tm_wheel->usb_dev, 0),
(char *)tm_wheel->change_request,
- 0, 0, // We do not expect any response from the wheel
- tminit_change_handler,
+ NULL, 0, // We do not expect any response from the wheel
+ thrustmaster_change_handler,
hdev
);
@@ -128,12 +271,13 @@ static void tminit_model_handler(struct urb *urb)
hid_err(hdev, "Error %d while submitting the change URB. I am unable to initialize this wheel...\n", ret);
}
-static void tminit_remove(struct hid_device *hdev)
+static void thrustmaster_remove(struct hid_device *hdev)
{
struct tm_wheel *tm_wheel = hid_get_drvdata(hdev);
usb_kill_urb(tm_wheel->urb);
+ kfree(tm_wheel->change_request);
kfree(tm_wheel->response);
kfree(tm_wheel->model_request);
usb_free_urb(tm_wheel->urb);
@@ -142,16 +286,19 @@ static void tminit_remove(struct hid_device *hdev)
hid_hw_stop(hdev);
}
-/**
+/*
* Function called by HID when a hid Thrustmaster FFB wheel is connected to the host.
* This function starts the hid dev, tries to allocate the tm_wheel data structure and
* finally send an USB CONTROL REQUEST to the wheel to get [what it seems to be] its
* model type.
*/
-static int tminit_probe(struct hid_device *hdev, const struct hid_device_id *id)
+static int thrustmaster_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret = 0;
- struct tm_wheel *tm_wheel = 0;
+ struct tm_wheel *tm_wheel = NULL;
+
+ if (!hid_is_usb(hdev))
+ return -EINVAL;
ret = hid_parse(hdev);
if (ret) {
@@ -178,12 +325,13 @@ static int tminit_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto error2;
}
- tm_wheel->model_request = kzalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
+ tm_wheel->model_request = kmemdup(&model_request,
+ sizeof(struct usb_ctrlrequest),
+ GFP_KERNEL);
if (!tm_wheel->model_request) {
ret = -ENOMEM;
goto error3;
}
- memcpy(tm_wheel->model_request, &model_request, sizeof(struct usb_ctrlrequest));
tm_wheel->response = kzalloc(sizeof(struct tm_wheel_response), GFP_KERNEL);
if (!tm_wheel->response) {
@@ -191,17 +339,18 @@ static int tminit_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto error4;
}
- tm_wheel->change_request = kzalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
- if (!tm_wheel->model_request) {
+ tm_wheel->change_request = kmemdup(&change_request,
+ sizeof(struct usb_ctrlrequest),
+ GFP_KERNEL);
+ if (!tm_wheel->change_request) {
ret = -ENOMEM;
goto error5;
}
- memcpy(tm_wheel->change_request, &change_request, sizeof(struct usb_ctrlrequest));
tm_wheel->usb_dev = interface_to_usbdev(to_usb_interface(hdev->dev.parent));
hid_set_drvdata(hdev, tm_wheel);
- tminit_interrupts(hdev);
+ thrustmaster_interrupts(hdev);
usb_fill_control_urb(
tm_wheel->urb,
@@ -210,16 +359,19 @@ static int tminit_probe(struct hid_device *hdev, const struct hid_device_id *id)
(char *)tm_wheel->model_request,
tm_wheel->response,
sizeof(struct tm_wheel_response),
- tminit_model_handler,
+ thrustmaster_model_handler,
hdev
);
ret = usb_submit_urb(tm_wheel->urb, GFP_ATOMIC);
- if (ret)
+ if (ret) {
hid_err(hdev, "Error %d while submitting the URB. I am unable to initialize this wheel...\n", ret);
+ goto error6;
+ }
return ret;
+error6: kfree(tm_wheel->change_request);
error5: kfree(tm_wheel->response);
error4: kfree(tm_wheel->model_request);
error3: usb_free_urb(tm_wheel->urb);
@@ -229,21 +381,21 @@ error0:
return ret;
}
-static const struct hid_device_id tminit_devices[] = {
+static const struct hid_device_id thrustmaster_devices[] = {
{ HID_USB_DEVICE(0x044f, 0xb65d)},
{}
};
-MODULE_DEVICE_TABLE(hid, tminit_devices);
+MODULE_DEVICE_TABLE(hid, thrustmaster_devices);
-static struct hid_driver tminit_driver = {
- .name = "hid-tminit",
- .id_table = tminit_devices,
- .probe = tminit_probe,
- .remove = tminit_remove,
+static struct hid_driver thrustmaster_driver = {
+ .name = "hid-thrustmaster",
+ .id_table = thrustmaster_devices,
+ .probe = thrustmaster_probe,
+ .remove = thrustmaster_remove,
};
-module_hid_driver(tminit_driver);
+module_hid_driver(thrustmaster_driver);
MODULE_AUTHOR("Dario Pagani <dario.pagani.146+linuxk@gmail.com>");
MODULE_LICENSE("GPL");
diff --git a/hid-tminit.h b/hid-tminit.h
deleted file mode 100644
index 286fe3d..0000000
--- a/hid-tminit.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0*/
-/**
- * These interrupts are used to prevent a nasty crash when initializing the
- * T300RS. Used in tminit_interrupts().
- */
-static const u8 setup_0[] = { 0x42, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-static const u8 setup_1[] = { 0x0a, 0x04, 0x90, 0x03, 0x00, 0x00, 0x00, 0x00 };
-static const u8 setup_2[] = { 0x0a, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00 };
-static const u8 setup_3[] = { 0x0a, 0x04, 0x12, 0x10, 0x00, 0x00, 0x00, 0x00 };
-static const u8 setup_4[] = { 0x0a, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00 };
-static const u8 *const setup_arr[] = { setup_0, setup_1, setup_2, setup_3, setup_4 };
-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)
-};
-/**
- * This struct contains for each type of
- * Thrustmaster wheel
- *
- * Note: The values are stored in the CPU
- * endianness, the USB protocols always use
- * little endian; the macro cpu_to_le[BIT]()
- * must be used when preparing USB packets
- * and vice-versa
- */
-struct tm_wheel_info {
- uint8_t model;
- uint8_t attachment;
-
- /**
- * See when the USB control out packet is prepared...
- * @TODO The TMX seems to require multiple control codes to switch.
- */
- uint16_t switch_value;
-
- char const *const wheel_name;
-};
-
-/**
- * Known wheels.
- * Note: TMX does not work as it requires 2 control packets
- */
-static const struct tm_wheel_info tm_wheels_infos[] = {
- {0x03, 0x06, 0x0006, "Thrustmaster T150RS"},
- {0x02, 0x06, 0x0005, "Thrustmaster T300RS"},
- {0x02, 0x03, 0x0005, "Thrustmaster T300RS (F1 attachment)"},
- {0x02, 0x04, 0x0005, "Thrustmaster T300 Ferrari Alcantara Edition"},
- {0x00, 0x02, 0x0002, "Thrustmaster T500RS"},
- {0x04, 0x07, 0x0001, "Thrustmaster TMX"}
-};
-
-static const uint8_t tm_wheels_infos_length = 6;
-
-/**
- * This structs contains (in little endian) the response data
- * of the wheel to the request 73
- *
- * A sufficient research to understand what each field does is not
- * beign conducted yet. The position and meaning of fields are a
- * just a very optimistic guess based on instinct....
- */
-struct __packed tm_wheel_response
-{
- /**
- * Seems to be the type of packet
- * - 0x0049 if is data.a (15 bytes)
- * - 0x0047 if is data.b (7 bytes)
- */
- uint16_t type;
-
- union {
- struct __packed {
- uint16_t field0;
- uint16_t field1;
- /**
- * Seems to be the model code of the wheel
- * Read table thrustmaster_wheels to values
- */
- uint8_t attachment;
- uint8_t model;
-
- uint16_t field2;
- uint16_t field3;
- uint16_t field4;
- uint16_t field5;
- } a;
- struct __packed {
- uint16_t field0;
- uint16_t field1;
- uint8_t attachment;
- uint8_t model;
- } b;
- } data;
-};
-
-struct tm_wheel {
- struct usb_device *usb_dev;
- struct urb *urb;
-
- struct usb_ctrlrequest *model_request;
- struct tm_wheel_response *response;
-
- struct usb_ctrlrequest *change_request;
-};
-
-/** The control packet to send to wheel */
-static const struct usb_ctrlrequest model_request = {
- .bRequestType = 0xc1,
- .bRequest = 73,
- .wValue = 0,
- .wIndex = 0,
- .wLength = cpu_to_le16(0x0010)
-};
-
-static const struct usb_ctrlrequest change_request = {
- .bRequestType = 0x41,
- .bRequest = 83,
- .wValue = 0, // Will be filled by the driver
- .wIndex = 0,
- .wLength = 0
-};