linux/drivers/hid/hid-thrustmaster.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * When connected to the machine, the Thrustmaster wheels appear as
   4 * a «generic» hid gamepad called "Thrustmaster FFB Wheel".
   5 *
   6 * When in this mode not every functionality of the wheel, like the force feedback,
   7 * are available. To enable all functionalities of a Thrustmaster wheel we have to send
   8 * to it a specific USB CONTROL request with a code different for each wheel.
   9 *
  10 * This driver tries to understand which model of Thrustmaster wheel the generic
  11 * "Thrustmaster FFB Wheel" really is and then sends the appropriate control code.
  12 *
  13 * Copyright (c) 2020-2021 Dario Pagani <dario.pagani.146+linuxk@gmail.com>
  14 * Copyright (c) 2020-2021 Kim Kuparinen <kimi.h.kuparinen@gmail.com>
  15 */
  16#include <linux/hid.h>
  17#include <linux/usb.h>
  18#include <linux/input.h>
  19#include <linux/slab.h>
  20#include <linux/module.h>
  21
  22/*
  23 * These interrupts are used to prevent a nasty crash when initializing the
  24 * T300RS. Used in thrustmaster_interrupts().
  25 */
  26static const u8 setup_0[] = { 0x42, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
  27static const u8 setup_1[] = { 0x0a, 0x04, 0x90, 0x03, 0x00, 0x00, 0x00, 0x00 };
  28static const u8 setup_2[] = { 0x0a, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00 };
  29static const u8 setup_3[] = { 0x0a, 0x04, 0x12, 0x10, 0x00, 0x00, 0x00, 0x00 };
  30static const u8 setup_4[] = { 0x0a, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00 };
  31static const u8 *const setup_arr[] = { setup_0, setup_1, setup_2, setup_3, setup_4 };
  32static const unsigned int setup_arr_sizes[] = {
  33        ARRAY_SIZE(setup_0),
  34        ARRAY_SIZE(setup_1),
  35        ARRAY_SIZE(setup_2),
  36        ARRAY_SIZE(setup_3),
  37        ARRAY_SIZE(setup_4)
  38};
  39/*
  40 * This struct contains for each type of
  41 * Thrustmaster wheel
  42 *
  43 * Note: The values are stored in the CPU
  44 * endianness, the USB protocols always use
  45 * little endian; the macro cpu_to_le[BIT]()
  46 * must be used when preparing USB packets
  47 * and vice-versa
  48 */
  49struct tm_wheel_info {
  50        uint16_t wheel_type;
  51
  52        /*
  53         * See when the USB control out packet is prepared...
  54         * @TODO The TMX seems to require multiple control codes to switch.
  55         */
  56        uint16_t switch_value;
  57
  58        char const *const wheel_name;
  59};
  60
  61/*
  62 * Known wheels.
  63 * Note: TMX does not work as it requires 2 control packets
  64 */
  65static const struct tm_wheel_info tm_wheels_infos[] = {
  66        {0x0306, 0x0006, "Thrustmaster T150RS"},
  67        {0x0206, 0x0005, "Thrustmaster T300RS"},
  68        {0x0204, 0x0005, "Thrustmaster T300 Ferrari Alcantara Edition"},
  69        {0x0002, 0x0002, "Thrustmaster T500RS"}
  70        //{0x0407, 0x0001, "Thrustmaster TMX"}
  71};
  72
  73static const uint8_t tm_wheels_infos_length = 4;
  74
  75/*
  76 * This structs contains (in little endian) the response data
  77 * of the wheel to the request 73
  78 *
  79 * A sufficient research to understand what each field does is not
  80 * beign conducted yet. The position and meaning of fields are a
  81 * just a very optimistic guess based on instinct....
  82 */
  83struct __packed tm_wheel_response
  84{
  85        /*
  86         * Seems to be the type of packet
  87         * - 0x0049 if is data.a (15 bytes)
  88         * - 0x0047 if is data.b (7 bytes)
  89         */
  90        uint16_t type;
  91
  92        union {
  93                struct __packed {
  94                        uint16_t field0;
  95                        uint16_t field1;
  96                        /*
  97                         * Seems to be the model code of the wheel
  98                         * Read table thrustmaster_wheels to values
  99                         */
 100                        uint16_t model;
 101
 102                        uint16_t field2;
 103                        uint16_t field3;
 104                        uint16_t field4;
 105                        uint16_t field5;
 106                } a;
 107                struct __packed {
 108                        uint16_t field0;
 109                        uint16_t field1;
 110                        uint16_t model;
 111                } b;
 112        } data;
 113};
 114
 115struct tm_wheel {
 116        struct usb_device *usb_dev;
 117        struct urb *urb;
 118
 119        struct usb_ctrlrequest *model_request;
 120        struct tm_wheel_response *response;
 121
 122        struct usb_ctrlrequest *change_request;
 123};
 124
 125/* The control packet to send to wheel */
 126static const struct usb_ctrlrequest model_request = {
 127        .bRequestType = 0xc1,
 128        .bRequest = 73,
 129        .wValue = 0,
 130        .wIndex = 0,
 131        .wLength = cpu_to_le16(0x0010)
 132};
 133
 134static const struct usb_ctrlrequest change_request = {
 135        .bRequestType = 0x41,
 136        .bRequest = 83,
 137        .wValue = 0, // Will be filled by the driver
 138        .wIndex = 0,
 139        .wLength = 0
 140};
 141
 142/*
 143 * On some setups initializing the T300RS crashes the kernel,
 144 * these interrupts fix that particular issue. So far they haven't caused any
 145 * adverse effects in other wheels.
 146 */
 147static void thrustmaster_interrupts(struct hid_device *hdev)
 148{
 149        int ret, trans, i, b_ep;
 150        u8 *send_buf = kmalloc(256, GFP_KERNEL);
 151        struct usb_host_endpoint *ep;
 152        struct device *dev = &hdev->dev;
 153        struct usb_interface *usbif = to_usb_interface(dev->parent);
 154        struct usb_device *usbdev = interface_to_usbdev(usbif);
 155
 156        if (!send_buf) {
 157                hid_err(hdev, "failed allocating send buffer\n");
 158                return;
 159        }
 160
 161        ep = &usbif->cur_altsetting->endpoint[1];
 162        b_ep = ep->desc.bEndpointAddress;
 163
 164        for (i = 0; i < ARRAY_SIZE(setup_arr); ++i) {
 165                memcpy(send_buf, setup_arr[i], setup_arr_sizes[i]);
 166
 167                ret = usb_interrupt_msg(usbdev,
 168                        usb_sndintpipe(usbdev, b_ep),
 169                        send_buf,
 170                        setup_arr_sizes[i],
 171                        &trans,
 172                        USB_CTRL_SET_TIMEOUT);
 173
 174                if (ret) {
 175                        hid_err(hdev, "setup data couldn't be sent\n");
 176                        kfree(send_buf);
 177                        return;
 178                }
 179        }
 180
 181        kfree(send_buf);
 182}
 183
 184static void thrustmaster_change_handler(struct urb *urb)
 185{
 186        struct hid_device *hdev = urb->context;
 187
 188        // The wheel seems to kill himself before answering the host and therefore is violating the USB protocol...
 189        if (urb->status == 0 || urb->status == -EPROTO || urb->status == -EPIPE)
 190                hid_info(hdev, "Success?! The wheel should have been initialized!\n");
 191        else
 192                hid_warn(hdev, "URB to change wheel mode seems to have failed with error %d\n", urb->status);
 193}
 194
 195/*
 196 * Called by the USB subsystem when the wheel responses to our request
 197 * to get [what it seems to be] the wheel's model.
 198 *
 199 * If the model id is recognized then we send an opportune USB CONTROL REQUEST
 200 * to switch the wheel to its full capabilities
 201 */
 202static void thrustmaster_model_handler(struct urb *urb)
 203{
 204        struct hid_device *hdev = urb->context;
 205        struct tm_wheel *tm_wheel = hid_get_drvdata(hdev);
 206        uint16_t model = 0;
 207        int i, ret;
 208        const struct tm_wheel_info *twi = 0;
 209
 210        if (urb->status) {
 211                hid_err(hdev, "URB to get model id failed with error %d\n", urb->status);
 212                return;
 213        }
 214
 215        if (tm_wheel->response->type == cpu_to_le16(0x49))
 216                model = le16_to_cpu(tm_wheel->response->data.a.model);
 217        else if (tm_wheel->response->type == cpu_to_le16(0x47))
 218                model = le16_to_cpu(tm_wheel->response->data.b.model);
 219        else {
 220                hid_err(hdev, "Unknown packet type 0x%x, unable to proceed further with wheel init\n", tm_wheel->response->type);
 221                return;
 222        }
 223
 224        for (i = 0; i < tm_wheels_infos_length && !twi; i++)
 225                if (tm_wheels_infos[i].wheel_type == model)
 226                        twi = tm_wheels_infos + i;
 227
 228        if (twi)
 229                hid_info(hdev, "Wheel with model id 0x%x is a %s\n", model, twi->wheel_name);
 230        else {
 231                hid_err(hdev, "Unknown wheel's model id 0x%x, unable to proceed further with wheel init\n", model);
 232                return;
 233        }
 234
 235        tm_wheel->change_request->wValue = cpu_to_le16(twi->switch_value);
 236        usb_fill_control_urb(
 237                tm_wheel->urb,
 238                tm_wheel->usb_dev,
 239                usb_sndctrlpipe(tm_wheel->usb_dev, 0),
 240                (char *)tm_wheel->change_request,
 241                0, 0, // We do not expect any response from the wheel
 242                thrustmaster_change_handler,
 243                hdev
 244        );
 245
 246        ret = usb_submit_urb(tm_wheel->urb, GFP_ATOMIC);
 247        if (ret)
 248                hid_err(hdev, "Error %d while submitting the change URB. I am unable to initialize this wheel...\n", ret);
 249}
 250
 251static void thrustmaster_remove(struct hid_device *hdev)
 252{
 253        struct tm_wheel *tm_wheel = hid_get_drvdata(hdev);
 254
 255        usb_kill_urb(tm_wheel->urb);
 256
 257        kfree(tm_wheel->change_request);
 258        kfree(tm_wheel->response);
 259        kfree(tm_wheel->model_request);
 260        usb_free_urb(tm_wheel->urb);
 261        kfree(tm_wheel);
 262
 263        hid_hw_stop(hdev);
 264}
 265
 266/*
 267 * Function called by HID when a hid Thrustmaster FFB wheel is connected to the host.
 268 * This function starts the hid dev, tries to allocate the tm_wheel data structure and
 269 * finally send an USB CONTROL REQUEST to the wheel to get [what it seems to be] its
 270 * model type.
 271 */
 272static int thrustmaster_probe(struct hid_device *hdev, const struct hid_device_id *id)
 273{
 274        int ret = 0;
 275        struct tm_wheel *tm_wheel = 0;
 276
 277        ret = hid_parse(hdev);
 278        if (ret) {
 279                hid_err(hdev, "parse failed with error %d\n", ret);
 280                goto error0;
 281        }
 282
 283        ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
 284        if (ret) {
 285                hid_err(hdev, "hw start failed with error %d\n", ret);
 286                goto error0;
 287        }
 288
 289        // Now we allocate the tm_wheel
 290        tm_wheel = kzalloc(sizeof(struct tm_wheel), GFP_KERNEL);
 291        if (!tm_wheel) {
 292                ret = -ENOMEM;
 293                goto error1;
 294        }
 295
 296        tm_wheel->urb = usb_alloc_urb(0, GFP_ATOMIC);
 297        if (!tm_wheel->urb) {
 298                ret = -ENOMEM;
 299                goto error2;
 300        }
 301
 302        tm_wheel->model_request = kmemdup(&model_request,
 303                                          sizeof(struct usb_ctrlrequest),
 304                                          GFP_KERNEL);
 305        if (!tm_wheel->model_request) {
 306                ret = -ENOMEM;
 307                goto error3;
 308        }
 309
 310        tm_wheel->response = kzalloc(sizeof(struct tm_wheel_response), GFP_KERNEL);
 311        if (!tm_wheel->response) {
 312                ret = -ENOMEM;
 313                goto error4;
 314        }
 315
 316        tm_wheel->change_request = kmemdup(&change_request,
 317                                           sizeof(struct usb_ctrlrequest),
 318                                           GFP_KERNEL);
 319        if (!tm_wheel->change_request) {
 320                ret = -ENOMEM;
 321                goto error5;
 322        }
 323
 324        tm_wheel->usb_dev = interface_to_usbdev(to_usb_interface(hdev->dev.parent));
 325        hid_set_drvdata(hdev, tm_wheel);
 326
 327        thrustmaster_interrupts(hdev);
 328
 329        usb_fill_control_urb(
 330                tm_wheel->urb,
 331                tm_wheel->usb_dev,
 332                usb_rcvctrlpipe(tm_wheel->usb_dev, 0),
 333                (char *)tm_wheel->model_request,
 334                tm_wheel->response,
 335                sizeof(struct tm_wheel_response),
 336                thrustmaster_model_handler,
 337                hdev
 338        );
 339
 340        ret = usb_submit_urb(tm_wheel->urb, GFP_ATOMIC);
 341        if (ret) {
 342                hid_err(hdev, "Error %d while submitting the URB. I am unable to initialize this wheel...\n", ret);
 343                goto error6;
 344        }
 345
 346        return ret;
 347
 348error6: kfree(tm_wheel->change_request);
 349error5: kfree(tm_wheel->response);
 350error4: kfree(tm_wheel->model_request);
 351error3: usb_free_urb(tm_wheel->urb);
 352error2: kfree(tm_wheel);
 353error1: hid_hw_stop(hdev);
 354error0:
 355        return ret;
 356}
 357
 358static const struct hid_device_id thrustmaster_devices[] = {
 359        { HID_USB_DEVICE(0x044f, 0xb65d)},
 360        {}
 361};
 362
 363MODULE_DEVICE_TABLE(hid, thrustmaster_devices);
 364
 365static struct hid_driver thrustmaster_driver = {
 366        .name = "hid-thrustmaster",
 367        .id_table = thrustmaster_devices,
 368        .probe = thrustmaster_probe,
 369        .remove = thrustmaster_remove,
 370};
 371
 372module_hid_driver(thrustmaster_driver);
 373
 374MODULE_AUTHOR("Dario Pagani <dario.pagani.146+linuxk@gmail.com>");
 375MODULE_LICENSE("GPL");
 376MODULE_DESCRIPTION("Driver to initialize some steering wheel joysticks from Thrustmaster");
 377
 378