diff --git a/.gitignore b/.gitignore index 7ad1d72..3cc59d0 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ compile_commands.json Module.symvers modules.order +.idea +.vscode \ No newline at end of file diff --git a/Kbuild b/Kbuild index 3f4d740..6bdfb17 100644 --- a/Kbuild +++ b/Kbuild @@ -4,4 +4,5 @@ hid-tmff-new-y := \ src/tmt300rs/hid-tmt300rs.o \ src/tmt248/hid-tmt248.o \ src/tmtx/hid-tmtx.o \ - src/tmtsxw/hid-tmtsxw.o + src/tmtsxw/hid-tmtsxw.o \ + src/tmt818/hid-tmt818.o diff --git a/src/hid-tmff2.c b/src/hid-tmff2.c index e691af4..227c36e 100644 --- a/src/hid-tmff2.c +++ b/src/hid-tmff2.c @@ -43,11 +43,19 @@ MODULE_PARM_DESC(alt_mode, "Alternate mode, eg. F1 mode"); #define GAIN_MAX 65535 -int gain = 40000; +int gain = 65535; module_param(gain, int, 0); MODULE_PARM_DESC(gain, "Level of gain (0-65535)"); +int mode = 3; +module_param(mode, int, 0); +MODULE_PARM_DESC(mode, "FFB Mode of the Wheel (0-3; 0: Comfort, 1: Sport, 2: Performance, 3: Extreme)"); + +int color = 0xff26cff; +module_param(color, int, 0); +MODULE_PARM_DESC(color, "Color of the RGB LEDs on the Wheel. Values are provided in RGBA Hex."); + static spinlock_t lock; static unsigned long lock_flags; @@ -225,6 +233,72 @@ static ssize_t alternate_modes_show(struct device *dev, } static DEVICE_ATTR_RW(alternate_modes); +static ssize_t mode_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 mode_store: %i", ret); + return ret; + } + + mode = value; + if (tmff2->set_mode) /* if we can, update mode immediately */ + tmff2->set_mode(tmff2->data, mode); + + return count; +} + +static ssize_t mode_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->mode_show) + return tmff2->mode_show(tmff2->data, buf); + + return -ENODEV; +} +static DEVICE_ATTR_RW(mode); + +static ssize_t color_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 color_store: %i", ret); + return ret; + } + + color = value; + if (tmff2->set_color) /* if we can, update color immediately */ + tmff2->set_color(tmff2->data, color); + + return count; +} + +static ssize_t color_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%i\n", color); +} +static DEVICE_ATTR_RW(color); + static ssize_t gain_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -541,6 +615,20 @@ static int tmff2_create_files(struct tmff2_device_entry *tmff2) } } + if (tmff2->params & PARAM_MODE) { + if ((ret = device_create_file(dev, &dev_attr_mode))) { + hid_warn(tmff2->hdev, "unable to create sysfs for mode\n"); + goto mode_err; + } + } + + if (tmff2->params & PARAM_COLOR) { + if ((ret = device_create_file(dev, &dev_attr_color))) { + hid_warn(tmff2->hdev, "unable to create sysfs for color\n"); + goto color_err; + } + } + return 0; friction_err: @@ -553,6 +641,10 @@ static int tmff2_create_files(struct tmff2_device_entry *tmff2) device_remove_file(dev, &dev_attr_alternate_modes); alt_err: device_remove_file(dev, &dev_attr_gain); +mode_err: + device_remove_file(dev, &dev_attr_mode); +color_err: + device_remove_file(dev, &dev_attr_color); gain_err: return ret; } @@ -618,6 +710,10 @@ static int tmff2_wheel_init(struct tmff2_device_entry *tmff2) tmff2->set_gain(tmff2->data, (GAIN_MAX * gain) / GAIN_MAX); } + if(tmff2->set_mode) { + tmff2->set_mode(tmff2->data, mode); + } + if (tmff2->set_autocenter) ff->set_autocenter = tmff2_set_autocenter; @@ -627,6 +723,9 @@ static int tmff2_wheel_init(struct tmff2_device_entry *tmff2) if (tmff2->switch_mode) tmff2->switch_mode(tmff2->data, alt_mode); + if (tmff2->set_color) + tmff2->set_color(tmff2->data, color); + /* create files */ if ((ret = tmff2_create_files(tmff2))) goto file_err; @@ -666,9 +765,17 @@ 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; + case TMAR_PC_ID: + switch (tmff2->hdev->version) { + case TMT818_VERSION: + if ((ret = t818_populate_api(tmff2))) + goto wheel_err; + break; + default: + if ((ret = t248_populate_api(tmff2))) + goto wheel_err; + break; + } break; case TX_ACTIVE: @@ -762,6 +869,12 @@ static void tmff2_remove(struct hid_device *hdev) if (tmff2->params & PARAM_GAIN) device_remove_file(dev, &dev_attr_gain); + if (tmff2->params & PARAM_MODE) + device_remove_file(dev, &dev_attr_mode); + + if (tmff2->params & PARAM_COLOR) + device_remove_file(dev, &dev_attr_color); + hid_hw_stop(hdev); tmff2->wheel_destroy(tmff2->data); @@ -775,7 +888,7 @@ static const struct hid_device_id tmff2_devices[] = { {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)}, + {HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMAR_PC_ID)}, /* tx */ {HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TX_ACTIVE)}, /* tsxw */ diff --git a/src/hid-tmff2.h b/src/hid-tmff2.h index 666d249..fe9c0d2 100644 --- a/src/hid-tmff2.h +++ b/src/hid-tmff2.h @@ -13,6 +13,8 @@ extern int friction_level; extern int range; extern int gain; extern int alt_mode; +extern int mode; +extern int color; #define USB_VENDOR_ID_THRUSTMASTER 0x044f @@ -36,6 +38,8 @@ extern int alt_mode; #define PARAM_RANGE (1 << 3) #define PARAM_ALT_MODE (1 << 4) #define PARAM_GAIN (1 << 5) +#define PARAM_MODE (1 << 6) +#define PARAM_COLOR (1 << 7) #undef fixp_sin16 #define fixp_sin16(v) (((v % 360) > 180) ?\ @@ -86,10 +90,13 @@ 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); + int (*set_mode)(void *data, uint); + int (*set_color)(void *data, uint32_t rgba); /* 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_show)(void *data, char *buf); + ssize_t (*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); @@ -103,12 +110,14 @@ 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 t818_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 TMT248_PC_ID 0xb696 +#define TMAR_PC_ID 0xb696 +#define TMT818_VERSION 0x111 #define TX_ACTIVE 0xb669 diff --git a/src/tmt818/hid-tmt818.c b/src/tmt818/hid-tmt818.c new file mode 100644 index 0000000..e1fee45 --- /dev/null +++ b/src/tmt818/hid-tmt818.c @@ -0,0 +1,621 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include "../hid-tmff2.h" +#include + +#define t818_MAX_EFFECTS 16 +#define t818_BUFFER_LENGTH 63 + +static const u8 setup_0[64] = {0x0a, 0x04, 0x00, 0x20}; +static const u8 setup_1[64] = {0x0a, 0x04, 0x00, 0x0c}; +static const u8 setup_2[64] = {0x0a, 0x04, 0x12, 0x10}; +static const u8 setup_3[64] = {0x0a, 0x04, 0x00, 0x16}; +static const u8 setup_4[64] = {0x0a, 0x04, 0x00, 0x17, 0x01}; +static const u8 setup_5[64] = {0x0a, 0x04, 0x00, 0x2a, 0x01}; +static const u8 setup_6[64] = {0x0a, 0x04, 0x00, 0x2f, 0x01}; +static const u8 setup_7[64] = {0x0a, 0x04, 0x03}; +static const u8 setup_8[64] = {0x0a, 0x04, 0x00, 0x2f, 0x00, 0xcd, 0xcc, 0x4c, 0x3d}; // Set Linearity to 5% +static const u8 setup_9[64] = {0x0a, 0x04, 0x00, 0x17, 0x00, 0x0a, 0xd7, 0xa3, 0x3c}; // Set Linearity to 5% +static const u8 *const setup_arr[] = {setup_0, setup_1, setup_2, setup_3, setup_4, setup_5, setup_6, setup_7}; +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), + ARRAY_SIZE(setup_7), + ARRAY_SIZE(setup_8), + ARRAY_SIZE(setup_9) + }; + +static const u8 init_0[64] = {0x49, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00}; + +static const unsigned long t818_params = + PARAM_SPRING_LEVEL | PARAM_DAMPER_LEVEL | PARAM_FRICTION_LEVEL | PARAM_RANGE | PARAM_GAIN | PARAM_MODE | PARAM_COLOR; + +static const signed short t818_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 t818_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, 0x31, /* Usage (Y) TODO: clutch? */ + 0x26, 0xff, 0x03, /* Logical maximum (1023) */ + 0x46, 0xff, 0x03, /* Physical maximum (1023) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x09, 0x35, /* Usage (Rz) TODO: brake? */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x09, 0x36, /* Usage (Slider) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x75, 0x08, /* Report size (8) */ + 0x26, 0xff, 0x00, /* Logical maximum (255) */ + 0x46, 0xff, 0x00, /* Physical maximum (255) */ + 0x09, 0x40, /* Usage (Vx) TODO: what is this? */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x09, 0x41, /* Usage (Vy) TODO: --||-- */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x09, 0x33, /* Usage (Rx) TODO: --||-- */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x09, 0x34, /* Usage (Ry) TODO: --||-- */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x09, 0x32, /* Usage (Z) TODO: --||-- (gas?) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x09, 0x37, /* Usage (Dial) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x05, 0x09, /* Usage page (Button) */ + 0x19, 0x01, /* Usage minimum (1) */ + 0x29, 0x1a, /* Usage maximum (13) */ + 0x25, 0x01, /* Logical maximum (1) */ + 0x45, 0x01, /* Physical maximum (1) */ + 0x75, 0x01, /* Report size (1) */ + 0x95, 0x1a, /* Report count (26) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x75, 0x06, /* Report size (6) */ + 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, /* Input (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, 0x00, /* Logical maximum (256) */ + 0x46, 0xff, 0x00, /* Physical maximum (256) */ + 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 t818_interrupts(struct t300rs_device_entry *t818) +{ + u8 *send_buf = kmalloc(256, GFP_KERNEL); + struct usb_interface *usbif = to_usb_interface(t818->hdev->dev.parent); + struct usb_host_endpoint *ep; + int ret, trans, b_ep, i; + if (!send_buf) + { + hid_err(t818->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(t818->usbdev, + usb_sndintpipe(t818->usbdev, b_ep), + send_buf, setup_arr_sizes[i], + &trans, + USB_CTRL_SET_TIMEOUT); + + if (ret) + { + hid_err(t818->hdev, "setup data couldn't be sent\n"); + goto err; + } + } + +err: + kfree(send_buf); + return ret; +} + +struct __packed t818_fw_response +{ + uint8_t unused1[16]; +}; + +// Request 0x56 points to Microsoft Extended Properties Feature Descriptor +struct __packed t818_ext_props_response +{ + uint8_t unused1[4]; +}; + +struct __packed t818_short_response +{ + uint8_t unused1[3]; +}; + +struct __packed t818_twobyte_response +{ + uint8_t unused1[2]; +}; + + +struct __packed t818_long_response +{ + uint8_t unused1[0x40]; +}; + + +static int t818_controls(struct t300rs_device_entry *t818) +{ + int ret, trans, b_ep, i; + struct t818_fw_response *first_response = kzalloc(sizeof(struct t818_fw_response), GFP_KERNEL); + if (!first_response) + { + hid_err(t818->hdev, "could not allocate fw_response\n"); + return -ENOMEM; + } + + // Wait for wheel to calibrate + do + { + msleep(500); + ret = usb_control_msg_recv(t818->usbdev, 0, 0x49, + 0xc1, 0, 0, + first_response, 16, USB_CTRL_SET_TIMEOUT, + GFP_KERNEL); + } while (first_response->unused1[2] == 0xFF); + + // Response somehow identifies the wheel + // Indices 3,6,14 differentiate between attached wheel + // Round Wheel + Adapter: 0x01, 0x06, 0x00 + // GT3 Wheel 0x07, 0x0a, 0x53 + + // Since I have no T248 to compare, I suspect indices 2, 4, 7, 8, 9, 12, 13 somehow show it's a T818 + // Values [0x03, 0x01, 0x0c, 0xb5, 0x0c, 0x0c, 0x0b] + + struct t818_ext_props_response *ext_props_response = kzalloc(sizeof(struct t818_ext_props_response), GFP_KERNEL); + if (!ext_props_response) + { + hid_err(t818->hdev, "could not allocate fw_response\n"); + return -ENOMEM; + } + + ret = usb_control_msg_recv(t818->usbdev, 0, 0x56, + 0xc1, 0, 0, + ext_props_response, 4, USB_CTRL_SET_TIMEOUT, + GFP_KERNEL); + + ret = usb_control_msg_recv(t818->usbdev, 0, 0x55, + 0xc1, 0, 0, + first_response, 16, USB_CTRL_SET_TIMEOUT, + GFP_KERNEL); + + struct t818_long_response *long_response = kzalloc(sizeof(struct t818_long_response), GFP_KERNEL); + if (!long_response) + { + hid_err(t818->hdev, "could not allocate fw_response\n"); + return -ENOMEM; + } + + ret = usb_control_msg_recv(t818->usbdev, 0, 0x48, + 0xc1, 0, 0, + long_response, 0x40, USB_CTRL_SET_TIMEOUT, + GFP_KERNEL); + + struct t818_short_response *short_response = kzalloc(sizeof(struct t818_short_response), GFP_KERNEL); + if (!short_response) + { + hid_err(t818->hdev, "could not allocate fw_response\n"); + return -ENOMEM; + } + ret = usb_control_msg_recv(t818->usbdev, 0, 0x42, + 0xc1, 0, 0, + short_response, 3, USB_CTRL_SET_TIMEOUT, + GFP_KERNEL); + + struct t818_twobyte_response *twobyte_response = kzalloc(sizeof(struct t818_twobyte_response), GFP_KERNEL); + if (!twobyte_response) + { + hid_err(t818->hdev, "could not allocate fw_response\n"); + return -ENOMEM; + } + ret = usb_control_msg_recv(t818->usbdev, 0, 0x4e, + 0xc1, 0, 0, + twobyte_response, 2, USB_CTRL_SET_TIMEOUT, + GFP_KERNEL); + + ret = usb_control_msg_recv(t818->usbdev, 0, 0x56, + 0xc1, 0, 0, + ext_props_response, 4, USB_CTRL_SET_TIMEOUT, + GFP_KERNEL); + + // for (i = 0; i < ARRAY_SIZE(ext_props_response->unused1); i++) + // { + // hid_info(t818->hdev, "0x%02X\n", ext_props_response->unused1[i]); + // } + + if (ret < 0) + { + hid_err(t818->hdev, "Initialization data couldn't be sent\n"); + goto err; + } + +err: + return ret; +} + +int t818_wheel_destroy(void *data) +{ + struct t300rs_device_entry *t300rs = data; + + if (!t300rs) + return -ENODEV; + + kfree(t300rs->send_buffer); + kfree(t300rs); + return 0; +} + +int t818_set_range(void *data, uint16_t value) +{ + struct t300rs_device_entry *t818 = data; + + if (value < 140) + { + hid_info(t818->hdev, "value %i too small, clamping to 140\n", value); + value = 140; + } + + if (value > 1080) + { + hid_info(t818->hdev, "value %i too large, clamping to 1080\n", value); + value = 1080; + } + + return t300rs_set_range(data, value); +} + +// 0: Comfort, 1: Sport, 2: Performance, 3: Extreme +static struct t818_ffb_modes +{ + int id; + char *label; +} t818_ffbmodes[] = { + {0, "Comfort"}, + {1, "Sport"}, + {2, "Performance"}, + {3, "Extreme"}}; + +int t818_set_mode(void *data, uint value) +{ + struct t300rs_device_entry *t818 = data; + int ret; + + if (value > 3) + { + hid_info(t818->hdev, "value %i too large, clamping to 3\n", value); + value = 3; + } + + u8 *send_buf = kmalloc(256, GFP_KERNEL); + struct usb_interface *usbif = to_usb_interface(t818->hdev->dev.parent); + struct usb_host_endpoint *ep; + int ret2, trans, b_ep; + + if (!send_buf) + { + hid_err(t818->hdev, "failed allocating send buffer\n"); + return -ENOMEM; + } + + ep = &usbif->cur_altsetting->endpoint[1]; + b_ep = ep->desc.bEndpointAddress; + + if (!t818) + return -ENODEV; + + u8 setMode[64] = {0x0a, 0x04, 0x00, 0x2a, 0x00, 0x01, 0x01}; + setMode[5] = value; + + memcpy(send_buf, setMode, ARRAY_SIZE(setMode)); + + ret2 = usb_interrupt_msg(t818->usbdev, + usb_sndintpipe(t818->usbdev, b_ep), + send_buf, ARRAY_SIZE(setMode), + &trans, + USB_CTRL_SET_TIMEOUT); + + if (ret2) + { + hid_err(t818->hdev, "mode could not be set\n"); + goto err; + } + + // Store new mode, as everything worked out fine + t818->mode = value; + return ret; + +err: + kfree(send_buf); + return ret2; +} + +ssize_t t818_ffb_mode_show(void *data, char *buf) +{ + struct t300rs_device_entry *t818 = data; + ssize_t count = 0; + int i; + if (!t818) + return -ENODEV; + + for (i = 0; i < ARRAY_SIZE(t818_ffbmodes); i++) + { + count += scnprintf(buf + count, PAGE_SIZE - count, "%i: %s", + t818_ffbmodes[i].id, t818_ffbmodes[i].label); + + if (count >= PAGE_SIZE - 1) + return count; + + if (t818_ffbmodes[i].id == t818->mode) + count += scnprintf(buf + count, PAGE_SIZE - count, " *\n"); + else + count += scnprintf(buf + count, PAGE_SIZE - count, "\n"); + + if (count >= PAGE_SIZE - 1) + return count; + } + return count; +} + +int t818_set_color(void *data, uint value) +{ + struct t300rs_device_entry *t818 = data; + int ret; + + u8 *send_buf = kmalloc(256, GFP_KERNEL); + struct usb_interface *usbif = to_usb_interface(t818->hdev->dev.parent); + struct usb_host_endpoint *ep; + int ret2, trans, b_ep; + + if (!send_buf) + { + hid_err(t818->hdev, "failed allocating send buffer\n"); + return -ENOMEM; + } + + ep = &usbif->cur_altsetting->endpoint[1]; + b_ep = ep->desc.bEndpointAddress; + + if (!t818) + return -ENODEV; + + uint8_t rgba[4]; + memcpy(rgba, &value, sizeof(value)); + u8 setColor[64] = {0x0a, 0x04, 0x00, 0x24, 0xfe, 0x00, 0x00, 0x00, 0x00}; + setColor[5] = rgba[3]; + setColor[6] = rgba[2]; + setColor[7] = rgba[1]; + setColor[8] = rgba[0]; + + memcpy(send_buf, setColor, ARRAY_SIZE(setColor)); + + ret2 = usb_interrupt_msg(t818->usbdev, + usb_sndintpipe(t818->usbdev, b_ep), + send_buf, ARRAY_SIZE(setColor), + &trans, + USB_CTRL_SET_TIMEOUT); + + if (ret2) + { + hid_err(t818->hdev, "color could not be set\n"); + goto err; + } + + // Store new mode, as everything worked out fine + mode = value; + return ret; + +err: + kfree(send_buf); + return ret2; +} + +static int t818_send_open(struct t300rs_device_entry *t818) +{ + int r1, r2; + t818->send_buffer[0] = 0x01; + t818->send_buffer[1] = 0x04; + if ((r1 = t300rs_send_int(t818))) + return r1; + + t818->send_buffer[0] = 0x01; + t818->send_buffer[1] = 0x05; + if ((r2 = t300rs_send_int(t818))) + return r2; + + return 0; +} + +static int t818_open(void *data, int open_mode) +{ + struct t300rs_device_entry *t818 = data; + + if (!t818) + return -ENODEV; + + if (open_mode) + t818_send_open(t818); + + return t818->open(t818->input_dev); +} + +static int t818_send_close(struct t300rs_device_entry *t818) +{ + int r1, r2; + t818->send_buffer[0] = 0x01; + t818->send_buffer[1] = 0x05; + if ((r1 = t300rs_send_int(t818))) + return r1; + + t818->send_buffer[0] = 0x01; + t818->send_buffer[1] = 0x00; + if ((r2 = t300rs_send_int(t818))) + return r2; + + return 0; +} + +static int t818_close(void *data, int open_mode) +{ + struct t300rs_device_entry *t818 = data; + + if (!t818) + return -ENODEV; + + if (open_mode) + t818_send_close(t818); + + t818->close(t818->input_dev); + return 0; +} + +int t818_wheel_init(struct tmff2_device_entry *tmff2, int open_mode) +{ + struct t300rs_device_entry *t818 = kzalloc(sizeof(struct t300rs_device_entry), GFP_KERNEL); + struct list_head *report_list; + int ret; + + if (!t818) + { + ret = -ENOMEM; + goto t818_err; + } + + t818->hdev = tmff2->hdev; + t818->input_dev = tmff2->input_dev; + t818->usbdev = to_usb_device(tmff2->hdev->dev.parent->parent); + t818->buffer_length = t818_BUFFER_LENGTH; + + t818->send_buffer = kzalloc(t818->buffer_length, GFP_KERNEL); + if (!t818->send_buffer) + { + ret = -ENOMEM; + goto send_err; + } + + report_list = &t818->hdev->report_enum[HID_OUTPUT_REPORT].report_list; + t818->report = list_entry(report_list->next, struct hid_report, list); + t818->ff_field = t818->report->field[0]; + + t818->open = t818->input_dev->open; + t818->close = t818->input_dev->close; + + if ((ret = t818_controls(t818))) + goto controls_err; + + if ((ret = t818_interrupts(t818))) + goto interrupt_err; + + /* everything went OK */ + tmff2->data = t818; + tmff2->params = t818_params; + tmff2->max_effects = t818_MAX_EFFECTS; + memcpy(tmff2->supported_effects, t818_effects, sizeof(t818_effects)); + + if (!open_mode) + t818_send_open(t818); + + hid_info(t818->hdev, "force feedback for T818\n"); + return 0; + +interrupt_err: +controls_err: +send_err: + kfree(t818); +t818_err: + hid_err(tmff2->hdev, "failed initializing t818\n"); + return ret; +} + +static __u8 *t818_wheel_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + rdesc = t818_pc_rdesc_fixed; + *rsize = sizeof(t818_pc_rdesc_fixed); + return rdesc; +} + +int t818_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; + tmff2->set_range = t818_set_range; + tmff2->wheel_fixup = t818_wheel_fixup; + tmff2->set_mode = t818_set_mode; + tmff2->mode_show = t818_ffb_mode_show; + tmff2->set_color = t818_set_color; + + tmff2->open = t818_open; + tmff2->close = t818_close; + + tmff2->wheel_init = t818_wheel_init; + tmff2->wheel_destroy = t818_wheel_destroy; + + return 0; +} diff --git a/udev/99-thrustmaster.rules b/udev/99-thrustmaster.rules index 3b469c0..cbce91e 100644 --- a/udev/99-thrustmaster.rules +++ b/udev/99-thrustmaster.rules @@ -18,9 +18,9 @@ KERNEL=="hidraw*", ATTRS{idVendor}=="044f", ATTRS{idProduct}=="b66d", MODE="0660 SUBSYSTEM=="input", ATTRS{idVendor}=="044f", ATTRS{idProduct}=="b66d", RUN+="/usr/bin/evdev-joystick --evdev %E{DEVNAME} --deadzone 0" SUBSYSTEM=="input", ATTRS{idVendor}=="044f", ATTRS{idProduct}=="b66d", RUN+="/usr/bin/jscal -s 6,1,1,32767,32767,16384,16384,1,3,448,574,1394469,1394469,1,3,448,574,1394469,1394469,1,3,448,574,1394469,1394469,1,0,0,0,536870912,536870912,1,0,0,0,536870912,536870912 /dev/input/js%n" -# T248 + T128 +# T248 + T128 + T818 KERNEL=="hidraw*", ATTRS{idVendor}=="044f", ATTRS{idProduct}=="b696", MODE="0660", TAG+="uaccess" -SUBSYSTEM=="input", ATTRS{idVendor}=="044f", ATTRS{idProduct}=="b696", RUN+="/usr/bin/evdev-joystick --evdev %E{DEVNAME} --deadzone 0" +SUBSYSTEM=="input", ATTRS{idVendor}=="044f", ATTRS{idProduct}=="b696", RUN+="/usr/bin/evdev-joystick --evdev %E{DEVNAME} --deadzone 0 --fuzz 16" SUBSYSTEM=="input", ATTRS{idVendor}=="044f", ATTRS{idProduct}=="b696", RUN+="/usr/bin/jscal -s 11,1,1,32767,32767,16384,16384,1,3,448,574,1394469,1394469,1,3,448,574,1394469,1394469,1,3,448,574,1394469,1394469,1,3,448,574,1394469,1394469,1,3,448,574,1394469,1394469,1,3,448,574,1394469,1394469,1,0,0,0,536870912,536870912,1,0,0,0,536870912,536870912,1,0,0,0,536870912,536870912,1,0,0,0,536870912,536870912 /dev/input/js%n" # TX