aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2020-07-10 22:04:12 +0300
committerGitHub <noreply@github.com>2020-07-10 22:04:12 +0300
commit675cad6d635be00c166b5ce23a79e32edd694bc8 (patch)
tree6c679e543dbf55e87eb7946a4b3b972e59dc3984
parent6faa9ae6a8cf242679ce4e493a66fcb31d231c09 (diff)
parent4d82a0fdfb95cdea2fd904c3ee33c1c6f603d153 (diff)
downloadhid-tmff2-675cad6d635be00c166b5ce23a79e32edd694bc8.tar.gz
hid-tmff2-675cad6d635be00c166b5ce23a79e32edd694bc8.zip
Merge pull request #2 from Kimplul/memory
Memory
-rw-r--r--Makefile3
-rw-r--r--README.md51
-rw-r--r--compile_commands.json12
-rw-r--r--hid-tm.h48
-rw-r--r--hid-tmff2.c872
-rw-r--r--hid-tminit.c121
-rw-r--r--hid-tminit.h18
-rw-r--r--hid-tmt300rs.c992
-rw-r--r--hid-tmt300rs.h86
-rw-r--r--todo.txt3
-rw-r--r--usbhid.h95
11 files changed, 1409 insertions, 892 deletions
diff --git a/Makefile b/Makefile
index ba704a9..6675c23 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,5 @@
-obj-m += hid-tmff2.o
+obj-m += hid-tminit.o
+obj-m += hid-tmt300rs.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(shell pwd) modules
diff --git a/README.md b/README.md
index 589a59f..8de3d4e 100644
--- a/README.md
+++ b/README.md
@@ -1,20 +1,45 @@
# Linux kernel module for the Thrustmaster T300RS wheel
-## General information for the curious:
-* This driver is heavily under development, and is currently missing some major features, such as force feedback among many other things.
+## Current state
-* I try to only push to Github whenever I'm relatively sure the driver won't freeze up the kernel, but I absolutely do not recommend running this driver on your main computer. Set up a virtual machine if you want to test this out. I personally recommend QEMU/libvirt: https://www.qemu.org/download/
+ Playable. Dynamic updating of effects isn't baked into the wheel quite as
+ well as I'd hoped, and so the driver in its current state doesn't really
+ support it. At some point in the future I'd like to add it to the feature
+ list, but it will need a lot more research and might take a while.
-* In case you want to try installing this driver, it should be fairly simple:
- ```
- make
- sudo make install
- ```
- And possibly some ```sudo depmod -a && sudo modprobe hid-tmff2```
- To see if the driver is being loaded, run ```dmesg```. The feed should be filled with debugging messages that honestly probably should be improved. A lot.
+ One other issue is that if you have the T3PA pedals, the clutch doesn't seem
+ to be registered by most(all?) games. It is registered and works as expected
+ in jstest and jstest-gtk, no clue what that is about.
-Update as of 27.06.2020 21:21
+ Anycase, *this version is usable in most force feedback games, supports
+ rangesetting as well as gain and autocentering along with most force feedback effects.**
+## Small note
+
+ While I haven't personally come across any crashes or lockups with this
+ version, I can't promise that they won't occur under any circumstances.
-I managed to somewhat successfully play DiRT Rally. The force feedback works, but isn't anywhere near as high quality as on windows. I've experiences some odd cases where the wheel decides to lock up, but it doesn't seem to affect the host computer in any way. No clue why it happens, but they seem to be relatively rare and reseating the USB plug has so far always returned the wheel to a usable state.
+ With that in mind,
-NOTE: I still don't necessarily recommend using this driver, unless you're really desperate for some ffb. I can't promise that this driver won't freeze up your kernel, corrupt all your data and kick your dog.
+## Installation
+
+ + Unplug wheel from computer
+ + `git clone this-repo`
+ + `make`
+ + `sudo make install`
+ + Plug wheel back in
+ + reboot (not strictly necessary, but recommended)
+
+ Done!
+
+## Additional tidbits
+
+ + Change range:
+
+ Write a value between 0 and 65535 to
+ /sys/bus/devices/XXXX:044F:B66E.XXX/range
+
+ + Change autocenter and gain:
+
+ Use the default evdev ways, i.e. https://www.kernel.org/doc/html/v5.1/input/ff.html
+
+ + Currently there is no support for this driver in oversteer(https://github.com/berarma/oversteer), but ffbwrap(https://github.com/berarma/ffbtools) should, at least in theory, work. Although I have to say that I haven't got it to work so far.
diff --git a/compile_commands.json b/compile_commands.json
index 0ac6efe..a75ead3 100644
--- a/compile_commands.json
+++ b/compile_commands.json
@@ -3,7 +3,7 @@
"arguments": [
"gcc-9",
"-c",
- "-Wp,-MD,/home/kimi/Documents/Projects/hid-tmff2/.hid-tmff2.o.d",
+ "-Wp,-MD,/home/kimi/Documents/Projects/hid-tmff2/.hid-tmt300rs.o.d",
"-nostdinc",
"-isystem",
"/usr/lib/gcc/x86_64-linux-gnu/9/include",
@@ -92,13 +92,13 @@
"-fcf-protection=none",
"-Wno-packed-not-aligned",
"-DMODULE",
- "-DKBUILD_BASENAME=\"hid_tmff2\"",
- "-DKBUILD_MODNAME=\"hid_tmff2\"",
+ "-DKBUILD_BASENAME=\"hid_tmt300rs\"",
+ "-DKBUILD_MODNAME=\"hid_tmt300rs\"",
"-o",
- "/home/kimi/Documents/Projects/hid-tmff2/hid-tmff2.o",
- "../../../home/kimi/Documents/Projects/hid-tmff2/hid-tmff2.c"
+ "/home/kimi/Documents/Projects/hid-tmff2/hid-tmt300rs.o",
+ "../../../home/kimi/Documents/Projects/hid-tmff2/hid-tmt300rs.c"
],
"directory": "/usr/src/linux-headers-5.7.0-1-amd64",
- "file": "../../../home/kimi/Documents/Projects/hid-tmff2/hid-tmff2.c"
+ "file": "../../../home/kimi/Documents/Projects/hid-tmff2/hid-tmt300rs.c"
}
] \ No newline at end of file
diff --git a/hid-tm.h b/hid-tm.h
new file mode 100644
index 0000000..e0117ae
--- /dev/null
+++ b/hid-tm.h
@@ -0,0 +1,48 @@
+#include <linux/hid.h>
+#include <linux/usb.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#define USB_VENDOR_ID_THRUSTMASTER 0x044f
+
+struct api_context {
+ struct completion done;
+ int status;
+};
+
+struct api_context ctx;
+
+int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
+{
+ unsigned long expire;
+ int retval;
+
+ init_completion(&ctx.done);
+ urb->context = &ctx;
+ urb->actual_length = 0;
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (unlikely(retval))
+ goto out;
+
+ expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;
+ if (!wait_for_completion_timeout(&ctx.done, expire)) {
+ usb_kill_urb(urb);
+ retval = (ctx.status == -ENOENT ? -ETIMEDOUT : ctx.status);
+
+ dev_dbg(&urb->dev->dev,
+ "%s timed out on ep%d%s len=%u/%u\n",
+ current->comm,
+ usb_endpoint_num(&urb->ep->desc),
+ usb_urb_dir_in(urb) ? "in" : "out",
+ urb->actual_length,
+ urb->transfer_buffer_length);
+ } else
+ retval = ctx.status;
+out:
+ if (actual_length)
+ *actual_length = urb->actual_length;
+
+ usb_free_urb(urb);
+ return retval;
+}
diff --git a/hid-tmff2.c b/hid-tmff2.c
deleted file mode 100644
index b26185f..0000000
--- a/hid-tmff2.c
+++ /dev/null
@@ -1,872 +0,0 @@
-/*This file has been modified from the Linux kernel driver drivers/hid/hid-tmff.c, starting from May 7 2020*/
-
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Force feedback support for various HID compliant devices by ThrustMaster:
- * ThrustMaster FireStorm Dual Power 2
- * and possibly others whose device ids haven't been added.
- *
- * Modified to support ThrustMaster devices by Zinx Verituse
- * on 2003-01-25 from the Logitech force feedback driver,
- * which is by Johann Deneux.
- *
- * Copyright (c) 2003 Zinx Verituse <zinx@epicsol.org>
- * Copyright (c) 2002 Johann Deneux
- */
-
-/*
-*/
-
-#include <linux/hid.h>
-#include <linux/usb.h>
-#include <linux/input.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-
-#define USB_VENDOR_ID_THRUSTMASTER 0x044f
-#define THRUSTMASTER_DEVICE_ID_2_IN_1_DT 0xb320
-#define URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL 0x001e
-
-unsigned long last = 0;
-
-static const signed short ff_rumble[] = {
- FF_RUMBLE,
- -1
-};
-
-static const signed short ff_constant[] = {
- FF_CONSTANT,
- -1
-};
-
-static const signed short ff_joystick[] = {
- FF_CONSTANT,
- -1
-};
-
-#ifdef CONFIG_THRUSTMASTER_FF
-
-/* Usages for thrustmaster devices I know about */
-#define THRUSTMASTER_USAGE_FF (HID_UP_GENDESK | 0xbb)
-
-u16 tmff_gain = 0xffff;
-
-static u8 setup_0[] = { 0x42, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
-static u8 setup_1[] = { 0x0a, 0x04, 0x90, 0x03, 0x00, 0x00, 0x00, 0x00 };
-static u8 setup_2[] = { 0x0a, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00 };
-static u8 setup_3[] = { 0x0a, 0x04, 0x12, 0x10, 0x00, 0x00, 0x00, 0x00 };
-static u8 setup_4[] = { 0x0a, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00 };
-static u8 *setup_arr[] = { setup_0, setup_1, setup_2, setup_3, setup_4 };
-static 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)
-};
-
-static u8 hw_rq_in[] = { 0xc1, 0x49, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00 };
-static u8 hw_rq_out[] = { 0x41, 0x53, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00 };
-static u8 ff_constant_array[] = {
- 0x60, 0x00, 0x01, 0x0a, 0xff, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-static u8 ff_stop_array[] = {
- 0x60, 0x00, 0x01, 0x0a, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-static u8 t300rs_0[] = {
- 0x42, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-
-};
-
-static u8 t300rs_1[] = {
- 0x0a, 0x04, 0x90, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-
-};
-
-static u8 t300rs_2[] = {
- 0x0a, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-
-};
-
-static u8 t300rs_3[] = {
- 0x0a, 0x04, 0x12, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-
-};
-
-static u8 t300rs_4[] = {
- 0x0a, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-
-};
-
-static u8 t300rs_re[] = {
- 0x0a, 0x04, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-static u8 *t300rs_arr[] = { t300rs_0, t300rs_1, t300rs_2, t300rs_3, t300rs_4, };
-static unsigned int t300rs_arr_sizes[] = {
- ARRAY_SIZE(t300rs_0),
- ARRAY_SIZE(t300rs_1),
- ARRAY_SIZE(t300rs_2),
- ARRAY_SIZE(t300rs_3),
- ARRAY_SIZE(t300rs_4),
-};
-
-static u8 t300rs_ctrl_out[] = { 0x41, 0x48, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, };
-
-static u8 t300rs_ctrl_in_0[] = { 0xc1, 0x42, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, };
-static u8 t300rs_ctrl_in_1[] = { 0xc1, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, };
-static u8 t300rs_ctrl_in_2[] = { 0xc1, 0x56, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, };
-static u8 t300rs_ctrl_in_re[] = { 0xc1, 0x49, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00 };
-static u8 *t300rs_ctrl_in_arr[] = {t300rs_ctrl_in_0, t300rs_ctrl_in_1, t300rs_ctrl_in_2 };
-static unsigned int t300rs_ctrl_in_sizes[] = {
- ARRAY_SIZE(t300rs_ctrl_in_0),
- ARRAY_SIZE(t300rs_ctrl_in_1),
- ARRAY_SIZE(t300rs_ctrl_in_2)
-};
-
-static u8 t300rs_in_0[] = { 0xc1, 0x56, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, };
-static u8 t300rs_in_1[] = { 0xc1, 0x49, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, };
-static u8 t300rs_in_2[] = { 0xc1, 0x48, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, };
-static u8 t300rs_in_3[] = { 0xc1, 0x55, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, };
-static u8 *t300rs_in_arr[] = {t300rs_in_0, t300rs_in_1, t300rs_in_2, t300rs_in_3};
-static unsigned int t300rs_in_sizes[] = {
- ARRAY_SIZE(t300rs_in_0),
- ARRAY_SIZE(t300rs_in_1),
- ARRAY_SIZE(t300rs_in_2),
- ARRAY_SIZE(t300rs_in_3)
-};
-
-static u8 t300rs_fin_0[] = {
- 0x60, 0x09, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-static u8 t300rs_fin_1[] = {
- 0x60, 0x0b, 0x05, 0xbf, 0x03, 0xc3, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-static u8 t300rs_fin_2[] = {
- 0x60, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-static u8 t300rs_fin_3[] = {
- 0x60, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-static u8 t300rs_fin_4[] = {
- 0x60, 0x00, 0x01, 0x6a, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f,
- 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-static u8 t300rs_fin_5[] = {
- 0x60, 0x00, 0x02, 0x6b, 0xfc, 0x7f, 0xfe, 0xff, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x80, 0x00, 0x00,
- 0xfc, 0x7f, 0x00, 0x00, 0xfc, 0x7f, 0x03, 0x4f, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-static u8 t300rs_fin_6[] = {
- 0x60, 0x00, 0x03, 0x64, 0xfd, 0x3f, 0xfd, 0x3f, 0x31, 0x03, 0xcb, 0xfc, 0xfd, 0x5f, 0xfd, 0x5f,
- 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xdf, 0x58, 0xa6, 0x6a, 0x06, 0x4f, 0xff, 0xff,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-static u8 t300rs_fin_7[] = {
- 0x60, 0x00, 0x01, 0x89, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-static u8 *t300rs_fin_arr[] = {t300rs_fin_0, t300rs_fin_1, t300rs_fin_2, t300rs_fin_3, t300rs_fin_4, t300rs_fin_5, t300rs_fin_6, t300rs_fin_7 };
-static unsigned int t300rs_fin_sizes[] = {
- ARRAY_SIZE(t300rs_fin_0),
- ARRAY_SIZE(t300rs_fin_1),
- ARRAY_SIZE(t300rs_fin_2),
- ARRAY_SIZE(t300rs_fin_3),
- ARRAY_SIZE(t300rs_fin_4),
- ARRAY_SIZE(t300rs_fin_5),
- ARRAY_SIZE(t300rs_fin_6),
- ARRAY_SIZE(t300rs_fin_7)
-};
-
-struct tmff_device {
- struct hid_report *report;
- struct hid_field *ff_field;
- spinlock_t report_lock;
-};
-
-static spinlock_t *report_lock;
-static int flags;
-
-struct api_context {
- struct completion done;
- int status;
-};
-
-static struct api_context ctx;
-
-/* Changes values from 0 to 0xffff into values from minimum to maximum */
-static inline int tmff_scale_u16(unsigned int in, int minimum, int maximum)
-{
- int ret;
-
- ret = (in * (maximum - minimum) / 0xffff) + minimum;
- if (ret < minimum)
- return minimum;
- if (ret > maximum)
- return maximum;
- return ret;
-}
-
-/* Changes values from -0x80 to 0x7f into values from minimum to maximum */
-static inline int tmff_scale_s8(int in, int minimum, int maximum)
-{
- int ret;
-
- ret = (((in + 0x80) * (maximum - minimum)) / 0xff) + minimum;
- if (ret < minimum)
- return minimum;
- if (ret > maximum)
- return maximum;
- return ret;
-}
-bool transferred = true;
-static void tmff_ctrl(struct urb *urb){
- if(urb->status){
- hid_warn(urb->dev, "urb status %d received\n", urb->status);
- }
- usb_free_urb(urb);
- transferred = true;
-}
-
-static void tmff_init_ctrl(struct urb *urb){
- if(urb->status){
- hid_warn(urb->dev, "urb status %d received\n", urb->status);
- ctx.status = urb->status;
- }
-}
-
-
-static void tmff_t300rs_ctrl(struct urb *urb){
- if(urb->status){
- hid_warn(urb->dev, "urb status %d received\n", urb->status);
- }
-}
-
-static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
-{
-
- unsigned long expire;
- int retval;
-
- printk("1");
- init_completion(&ctx.done);
- urb->context = &ctx;
- urb->actual_length = 0;
- retval = usb_submit_urb(urb, GFP_ATOMIC);
- if (unlikely(retval))
- goto out;
-
- printk("2");
-
- expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;
- if (!wait_for_completion_timeout(&ctx.done, expire)) {
- usb_kill_urb(urb);
- retval = (ctx.status == -ENOENT ? -ETIMEDOUT : ctx.status);
-
- dev_dbg(&urb->dev->dev,
- "%s timed out on ep%d%s len=%u/%u\n",
- current->comm,
- usb_endpoint_num(&urb->ep->desc),
- usb_urb_dir_in(urb) ? "in" : "out",
- urb->actual_length,
- urb->transfer_buffer_length);
- } else
- retval = ctx.status;
-out:
- printk("3");
- if (actual_length)
- *actual_length = urb->actual_length;
-
- usb_free_urb(urb);
- return retval;
-}
-
-bool last_stop = false;
-
-static int tmff_play(struct input_dev *dev, void *data,
- struct ff_effect *effect)
-{
-
- //spin_lock_irqsave(report_lock, flags);
- struct hid_device *hid = input_get_drvdata(dev);
- struct device *d_dev = &hid->dev;
- struct usb_interface *usbif = to_usb_interface(d_dev->parent);
- struct usb_device *usbdev = interface_to_usbdev(usbif);
- struct usb_host_endpoint *ep;
- struct tmff_device *tmff = data;
- struct hid_field *ff_field = tmff->ff_field;
- struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
- u8 *send_buf = kmalloc(1024, GFP_ATOMIC);
- __s16 x;
-
- if(!transferred){
- return 0;
- }
-
-
- memcpy(send_buf, ff_constant_array, ARRAY_SIZE(ff_constant_array));
-
- ep = &usbif->cur_altsetting->endpoint[1];
-
- switch (effect->type) {
- case FF_CONSTANT:
- // this only works with input_ff_create_memless, which clamps
- // constant forces to s8 for some reason
- x = tmff_scale_s8(effect->u.constant.level,
- ff_field->logical_minimum,
- ff_field->logical_maximum);
-
- send_buf[4] = x & 0xff;
- send_buf[5] = x >> 8;
-
- usb_fill_int_urb(
- urb,
- usbdev,
- usb_sndintpipe(usbdev, 1),
- send_buf,
- ARRAY_SIZE(ff_constant_array),
- tmff_ctrl,
- hid,
- ep->desc.bInterval
- );
- transferred = false;
- usb_submit_urb(urb, GFP_ATOMIC);
-
-
- break;
- default:
- break;
- }
-
-// msleep(2);
- kfree(send_buf);
- return 0;
-}
-
-static int tmff_init_t300rs(struct hid_device *hid){
-
- int trans, b_ep, bb_ep, i, err;
- u8 *send_buf, *rq_buf;
- struct device *dev = &hid->dev;
- struct usb_interface *usbif = to_usb_interface(dev->parent);
- struct usb_device *usbdev = interface_to_usbdev(usbif);
- struct usb_host_endpoint *ep;
- struct urb *urb;
-
- ep = &usbif->cur_altsetting->endpoint[0];
- bb_ep = ep->desc.bEndpointAddress;
-
- ep = &usbif->cur_altsetting->endpoint[1];
- b_ep = ep->desc.bEndpointAddress;
-
- send_buf = kmalloc(256, GFP_ATOMIC); /* overkill but whatever */
- rq_buf = kmalloc(256, GFP_ATOMIC);
-
- for(i = 0; i < ARRAY_SIZE(t300rs_in_arr); ++i){
- memcpy(rq_buf, t300rs_in_arr[i], t300rs_in_sizes[i]);
- urb = usb_alloc_urb(0, GFP_KERNEL);
- usb_fill_control_urb(urb,
- usbdev,
- usb_rcvctrlpipe(usbdev, 0),
- rq_buf,
- send_buf,
- 0,
- tmff_t300rs_ctrl,
- hid);
-
- err = usb_start_wait_urb(urb, 1, &trans);
- if(err != 0){
- hid_err(hid, "Failed sending pre-ctrin with ERRNO: %i", err);
- goto error;
- }
- }
-
- for(i = 0; i < ARRAY_SIZE(t300rs_ctrl_in_arr); ++i){
- memcpy(rq_buf, t300rs_ctrl_in_arr[i], t300rs_ctrl_in_sizes[i]);
- urb = usb_alloc_urb(0, GFP_KERNEL);
- usb_fill_control_urb(urb,
- usbdev,
- usb_rcvctrlpipe(usbdev, 0),
- rq_buf,
- send_buf,
- 0,
- tmff_t300rs_ctrl,
- hid
- );
-
- err = usb_start_wait_urb(urb, 1, &trans);
- if(err != 0){
- hid_err(hid, "Failed sending T300RS ctrl out with ERRNO: %i", err);
- goto error;
- }
-
-
- }
- printk("Sent initial ctrlouts");
-
- for(i = 0; i < ARRAY_SIZE(t300rs_arr); ++i){
- memcpy(send_buf, t300rs_arr[i], t300rs_arr_sizes[i]);
-
- err = usb_interrupt_msg(usbdev,
- usb_sndintpipe(usbdev, b_ep),
- send_buf,
- t300rs_arr_sizes[i],
- &trans,
- USB_CTRL_SET_TIMEOUT);
-
- if(err){
- hid_err(hid, "T300RS setup data at index %i couldn't be sent, ERRNO: %i\n", i, err);
- goto error;
- }
-
- err = usb_interrupt_msg(usbdev,
- usb_rcvintpipe(usbdev, bb_ep),
- send_buf,
- 64,
- &trans,
- USB_CTRL_SET_TIMEOUT);
- if(err != 0){
- hid_err(hid, "FUCK, ERRNO: %i", err);
- goto error;
- }
- }
-
- memcpy(rq_buf, t300rs_ctrl_in_re, ARRAY_SIZE(t300rs_ctrl_in_re));
- for(i = 0; i < 16; ++i){
- urb = usb_alloc_urb(0, GFP_KERNEL);
- usb_fill_control_urb(urb,
- usbdev,
- usb_rcvctrlpipe(usbdev, 0),
- rq_buf,
- send_buf,
- 0,
- tmff_t300rs_ctrl,
- hid
- );
-
- err = usb_start_wait_urb(urb, 1, &trans);
- if(err != 0){
- hid_err(hid, "Failed sending T300RS ctrl out with ERRNO: %i", err);
- goto error;
- }
- }
-
- printk("Sent first 16 ctrlins");
-
- memcpy(rq_buf, t300rs_ctrl_out, ARRAY_SIZE(t300rs_ctrl_out));
- urb = usb_alloc_urb(0, GFP_KERNEL);
- usb_fill_control_urb(urb,
- usbdev,
- usb_sndctrlpipe(usbdev, 0),
- rq_buf,
- send_buf,
- 0,
- tmff_t300rs_ctrl,
- hid
- );
-
- err = usb_start_wait_urb(urb, 1, &trans);
- if(err != 0){
- hid_err(hid, "Failed sending ctrl out with ERRNO: %i", err);
- goto error;
- }
-
- memcpy(rq_buf, t300rs_ctrl_in_re, ARRAY_SIZE(t300rs_ctrl_in_re));
- for(i = 0; i < 8; ++i){
- urb = usb_alloc_urb(0, GFP_KERNEL);
- usb_fill_control_urb(urb,
- usbdev,
- usb_rcvctrlpipe(usbdev, 0),
- rq_buf,
- send_buf,
- 0,
- tmff_t300rs_ctrl,
- hid
- );
-
- err = usb_start_wait_urb(urb, 1, &trans);
- if(err != 0){
- hid_err(hid, "Failed sending T300RS ctrl out with ERRNO: %i", err);
- goto error;
- }
- }
-
- memcpy(send_buf, t300rs_re, ARRAY_SIZE(t300rs_re));
-
- for(i = 0; i < 3; ++i){
- err = usb_interrupt_msg(usbdev,
- usb_sndintpipe(usbdev, b_ep),
- send_buf,
- ARRAY_SIZE(t300rs_re),
- &trans,
- USB_CTRL_SET_TIMEOUT);
-
- if(err){
- hid_err(hid, "T300RS after-init data couldn't be sent, ERRNO: %i", err);
- goto error;
- }
- }
-
-
-error:
- kfree(send_buf);
- kfree(rq_buf);
-
- return 0;
-}
-
-static int tmff_clear_init(struct hid_device *hid){
- /* die */
-
- int trans, b_ep, i, err;
- u8 *send_buf, *rq_buf;
- struct device *dev = &hid->dev;
- struct usb_interface *usbif = to_usb_interface(dev->parent);
- struct usb_device *usbdev = interface_to_usbdev(usbif);
- struct usb_host_endpoint *ep;
- struct urb *urb;
-
- ep = &usbif->cur_altsetting->endpoint[1];
- b_ep = ep->desc.bEndpointAddress;
-
- send_buf = kmalloc(256, GFP_KERNEL); /* overkill but whatever */
- rq_buf = kmalloc(256, GFP_KERNEL);
-
- for(i = 0; i < ARRAY_SIZE(setup_arr); ++i){
- memcpy(send_buf, setup_arr[i], setup_arr_sizes[i]);
- hid_info(hid, "Sent interrupt at %i\n", i);
- err = usb_interrupt_msg(usbdev,
- usb_sndintpipe(usbdev, b_ep),
- send_buf,
- setup_arr_sizes[i],
- &trans,
- USB_CTRL_SET_TIMEOUT);
-
- if(err){
- hid_err(hid, "Setup data at index %i couldn't be sent, ERRNO: %i\n", i, err);
- goto error;
- }
- }
-
-
- urb = usb_alloc_urb(0, GFP_ATOMIC);
-
-
- memcpy(send_buf, hw_rq_in, 8);
- memcpy(rq_buf, hw_rq_out, 8);
-
- usb_fill_control_urb(urb,
- usbdev,
- usb_sndctrlpipe(usbdev, 0),
- rq_buf,
- send_buf,
- 0,
- tmff_init_ctrl,
- hid
- );
-
- err = usb_start_wait_urb(urb, 5, &trans);
- if(err != 0){
- hid_err(hid, "Failed sending ctrl out with ERRNO: %i", err);
- goto error;
- }
- hid_info(hid, "URB submission didn't fail?");
-
-error:
- kfree(rq_buf);
- kfree(send_buf);
- return err;
-}
-
-static int tmff_delete(struct kref *kref){
- /* reserved for future use */
- /* currently the errors we get aren't pretty */
-
- return 0;
-};
-
-static void tmff_set_gain(struct input_dev *dev, u16 gain){
- tmff_gain = gain;
-}
-
-static int tmff_init(struct hid_device *hid, const signed short *ff_bits)
-{
-
- struct tmff_device *tmff;
- struct hid_report *report;
- struct list_head *report_list;
- struct hid_input *hidinput = list_entry(hid->inputs.next,
- struct hid_input, list);
- struct input_dev *input_dev = hidinput->input;
- struct ff_device *ff;
- int error;
- int i;
-
- if(hid->product == 0xb65d)
- return tmff_clear_init(hid);
-
- tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL);
- if (!tmff)
- return -ENOMEM;
-
- hid_info(hid, "what is going ong here???");
- spin_lock_init(&tmff->report_lock);
- report_lock = &tmff->report_lock;
-
- /* Find the report to use */
- report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
- list_for_each_entry(report, report_list, list) {
- int fieldnum;
-
- for (fieldnum = 0; fieldnum < report->maxfield; ++fieldnum) {
- struct hid_field *field = report->field[fieldnum];
- printk("found field at fieldnum %i\n", fieldnum);
-
- if (field->maxusage <= 0)
- continue;
-
- switch (field->usage[0].hid) {
- case 0xff00000a:
- case THRUSTMASTER_USAGE_FF:
- if (field->report_count < 2) {
- hid_warn(hid, "ignoring FF field with report_count < 2\n");
- continue;
- }
-
- if (field->logical_maximum ==
- field->logical_minimum) {
- hid_warn(hid, "ignoring FF field with logical_maximum == logical_minimum\n");
- continue;
- }
-
- if (tmff->report && tmff->report != report) {
- hid_warn(hid, "ignoring FF field in other report\n");
- continue;
- }
-
- if (tmff->ff_field && tmff->ff_field != field) {
- hid_warn(hid, "ignoring duplicate FF field\n");
- continue;
- }
-
- tmff->report = report;
- tmff->ff_field = field;
-
- for (i = 0; ff_bits[i] >= 0; i++)
- set_bit(ff_bits[i], input_dev->ffbit);
-
- break;
-
- default:
- hid_warn(hid, "ignoring unknown output usage %08x\n",
- field->usage[0].hid);
- continue;
- }
- }
- }
-
- if (!tmff->report) {
- hid_err(hid, "can't find FF field in output reports\n");
- error = -ENODEV;
- goto fail;
- }
-
- error = input_ff_create_memless(input_dev, tmff, tmff_play);
- if (error)
- goto fail;
-
- ff = input_dev->ff;
- ff->set_gain = tmff_set_gain;
-
- hid_info(hid, "force feedback for ThrustMaster devices by Zinx Verituse <zinx@epicsol.org>\n");
- return 0;
-
-fail:
- hid_err(hid, "We have failed creating a force feedback memless device!!!\n");
- kfree(tmff);
- return error;
-}
-#else
-static inline int tmff_init(struct hid_device *hid, const signed short *ff_bits)
-{
- return 0;
-}
-#endif
-static int tmff_afterthought_t300rs(struct hid_device *hid){
- int trans, b_ep, bb_ep, i, err;
- u8 *send_buf, *rq_buf;
- struct device *dev = &hid->dev;
- struct usb_interface *usbif = to_usb_interface(dev->parent);
- struct usb_device *usbdev = interface_to_usbdev(usbif);
- struct usb_host_endpoint *ep;
-
- ep = &usbif->cur_altsetting->endpoint[0];
- bb_ep = ep->desc.bEndpointAddress;
-
- ep = &usbif->cur_altsetting->endpoint[1];
- b_ep = ep->desc.bEndpointAddress;
-
- send_buf = kmalloc(256, GFP_ATOMIC); /* overkill but whatever */
- rq_buf = kmalloc(256, GFP_ATOMIC);
-
- for(i = 0; i < ARRAY_SIZE(t300rs_fin_sizes); ++i){
- memcpy(send_buf, t300rs_fin_arr[i], t300rs_fin_sizes[i]);
-
- err = usb_interrupt_msg(usbdev,
- usb_sndintpipe(usbdev, b_ep),
- send_buf,
- t300rs_fin_sizes[i],
- &trans,
- USB_CTRL_SET_TIMEOUT);
- hid_info(hid, "sent %i:th message", i);
- if(err){
- hid_err(hid, "T300RS setup data at index %i couldn't be sent, ERRNO: %i\n", i, err);
- goto error;
- }
- }
-
-
-error:
- kfree(send_buf);
- kfree(rq_buf);
- return err;
-}
-
-
-static int tm_probe(struct hid_device *hdev, const struct hid_device_id *id)
-{
- int ret;
-
- ret = hid_parse(hdev);
- if (ret) {
- hid_err(hdev, "parse failed\n");
- goto err;
- }
-
- if(hdev->product == 0xb66e){
- ret = tmff_init_t300rs(hdev);
-
- if(ret){
- hid_err(hdev, "t300rs init failed\n");
- goto err;
- }
- }
-
- printk("woot\n");
- ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
- if (ret) {
- hid_err(hdev, "hw start failed\n");
- goto err;
- }
- printk("waat\n");
-
- ret = tmff_init(hdev, (void *)id->driver_data);
-
- if(ret){
- hid_err(hdev, "what the fuck is going on?\n");
- if(ret == -32){
- //hid_hw_stop(hdev);
- return 0;
- }
- goto err;
- }
-
- if(hdev->product == 0xb66e){
- ret = tmff_afterthought_t300rs(hdev);
-
- if(ret){
- hid_err(hdev, "t300rs afterinit failed\n");
- goto err;
- }
- }
- return 0;
-err:
- return ret;
-}
-
-static void tm_remove(struct hid_device *hdev){
- hid_hw_stop(hdev);
-}
-
-static __u8 t300rs_rdesc_fixed[] = {0x05, 0x01, 0x09, 0x04, 0xa1, 0x01, 0x09, 0x01, 0xa1, 0x00, 0x85, 0x07, 0x09, 0x30, 0x15, 0x00, 0x27, 0xff, 0xff, 0x00, 0x00, 0x35, 0x00, 0x47, 0xff, 0xff, 0x00, 0x00, 0x75, 0x10, 0x95, 0x01, 0x81, 0x02, 0x09, 0x31, 0x26, 0xff, 0x03, 0x46, 0xff, 0x03, 0x81, 0x02, 0x09, 0x35, 0x81, 0x02, 0x09, 0x36, 0x81, 0x02, 0x81, 0x03, 0x05, 0x09, 0x19, 0x01, 0x29, 0x0d, 0x25, 0x01, 0x45, 0x01, 0x75, 0x01, 0x95, 0x0d, 0x81, 0x02, 0x75, 0x0b, 0x95, 0x01, 0x81, 0x03, 0x05, 0x01, 0x09, 0x39, 0x25, 0x07, 0x46, 0x3b, 0x01, 0x55, 0x00, 0x65, 0x14, 0x75, 0x04, 0x81, 0x42, 0x65, 0x00, 0x81, 0x03, 0x85, 0x0a, 0x06, 0x00, 0xff, 0x09, 0x0a, 0x75, 0x08, 0x95, 0x3f,
- 0x26, 0xff, 0x7f,
- 0x16, 0x00, 0x80,
- 0x46, 0xff, 0x7f,
- 0x36, 0x00, 0x80,
- 0x91, 0x02, 0x85, 0x02, 0x09, 0x02, 0x81, 0x02, 0x09, 0x14, 0x85, 0x14, 0x81, 0x02, 0xc0, 0xc0,};
-
-static __u8 *tmff_t300rs_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize){
- if(hdev->product != 0xb66e){
- return rdesc;
- }
- printk("wahey");
- rdesc = t300rs_rdesc_fixed;
- *rsize = sizeof(t300rs_rdesc_fixed);
- return rdesc;
-};
-
-static const struct hid_device_id tm_devices[] = {
- { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb65d),
- .driver_data = (unsigned long)ff_constant }, /* die */
- { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb66e),
- .driver_data = (unsigned long)ff_constant },
- { }
-};
-MODULE_DEVICE_TABLE(hid, tm_devices);
-
-static struct hid_driver tm_driver = {
- .name = "thrustmaster",
- .id_table = tm_devices,
- .probe = tm_probe,
- .remove = tm_remove,
- .report_fixup = tmff_t300rs_report_fixup,
-};
-module_hid_driver(tm_driver);
-
-MODULE_LICENSE("GPL");
diff --git a/hid-tminit.c b/hid-tminit.c
new file mode 100644
index 0000000..9b894f5
--- /dev/null
+++ b/hid-tminit.c
@@ -0,0 +1,121 @@
+#include "hid-tminit.h"
+
+static void tminit_callback(struct urb *urb){
+ hid_info(urb->dev, "urb status %d received\n", urb->status);
+}
+
+/* for some godawful reason these interrupts are absolutely necessary, otherwise
+ * the whole kernel crashes. I have no idea why.
+ * */
+static void tminit_interrupts(struct hid_device *hdev){
+ int ret, trans, i, b_ep;
+ u8 *send_buf = kmalloc(256, GFP_KERNEL);
+
+ struct usb_host_endpoint *ep;
+ struct device *dev = &hdev->dev;
+ struct usb_interface *usbif = to_usb_interface(dev->parent);
+ struct usb_device *usbdev = interface_to_usbdev(usbif);
+
+ 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(usbdev,
+ usb_sndintpipe(usbdev, b_ep),
+ send_buf,
+ setup_arr_sizes[i],
+ &trans,
+ USB_CTRL_SET_TIMEOUT);
+
+ if(ret){
+ hid_err(hdev, "setup data couldn't be sent\n");
+ return;
+ }
+ }
+}
+
+int tminit(struct hid_device *hdev){
+ struct urb *urb;
+ u8 *setup_packet, *transfer_buffer;
+ struct device *dev = &hdev->dev;
+ struct usb_interface *usbif = to_usb_interface(dev->parent);
+ struct usb_device *usbdev = interface_to_usbdev(usbif);
+ int ret;
+
+ tminit_interrupts(hdev);
+
+ setup_packet = kmalloc(8, GFP_ATOMIC);
+ transfer_buffer = kmalloc(8, GFP_ATOMIC);
+
+ memcpy(setup_packet, hw_rq_out, 8);
+ memcpy(transfer_buffer, hw_rq_in, 8);
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+ usb_fill_control_urb(urb,
+ usbdev,
+ usb_sndctrlpipe(usbdev, 0),
+ setup_packet,
+ transfer_buffer,
+ 8,
+ tminit_callback,
+ hdev);
+
+ /* we sort of have to go on faith that the message is sent, because the
+ * wheel usually completely dies as soon as it receives the message.
+ * No need to even check the return value.
+ */
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ kfree(setup_packet);
+ kfree(transfer_buffer);
+ return ret;
+}
+
+static void tminit_remove(struct hid_device *hdev){
+ /* we are dead, hopefully without any serious side effects */
+ hid_hw_stop(hdev);
+}
+
+static int tminit_probe(struct hid_device *hdev, const struct hid_device_id *id){
+ int ret;
+
+ ret = hid_parse(hdev);
+ if(ret){
+ hid_err(hdev, "parse failed\n");
+ goto err;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
+ if(ret){
+ hid_err(hdev, "hw start failed\n");
+ goto err;
+ }
+
+ ret = tminit(hdev);
+ if(ret){
+ hid_err(hdev, "tminit exited, error might be intended behaviour\n");
+ goto err;
+ }
+
+err:
+ return ret;
+}
+
+static const struct hid_device_id tminit_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb65d)},
+ {}
+};
+
+MODULE_DEVICE_TABLE(hid, tminit_devices);
+
+static struct hid_driver tminit_driver = {
+ .name = "thrustmaster init",
+ .id_table = tminit_devices,
+ .probe = tminit_probe,
+ .remove = tminit_remove,
+};
+module_hid_driver(tminit_driver);
+
+MODULE_LICENSE("GPL");
diff --git a/hid-tminit.h b/hid-tminit.h
new file mode 100644
index 0000000..cd12f9b
--- /dev/null
+++ b/hid-tminit.h
@@ -0,0 +1,18 @@
+#include "hid-tm.h"
+
+u8 hw_rq_in[] = { 0xc1, 0x49, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00 };
+u8 hw_rq_out[] = { 0x41, 0x53, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+u8 setup_0[] = { 0x42, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+u8 setup_1[] = { 0x0a, 0x04, 0x90, 0x03, 0x00, 0x00, 0x00, 0x00 };
+u8 setup_2[] = { 0x0a, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00 };
+u8 setup_3[] = { 0x0a, 0x04, 0x12, 0x10, 0x00, 0x00, 0x00, 0x00 };
+u8 setup_4[] = { 0x0a, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00 };
+u8 *setup_arr[] = { setup_0, setup_1, setup_2, setup_3, setup_4 };
+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)
+};
diff --git a/hid-tmt300rs.c b/hid-tmt300rs.c
new file mode 100644
index 0000000..39747cd
--- /dev/null
+++ b/hid-tmt300rs.c
@@ -0,0 +1,992 @@
+#include "hid-tmt300rs.h"
+
+static void t300rs_int_callback(struct urb *urb){
+ if(urb->status){
+ hid_warn(urb->dev, "urb status %i received\n", urb->status);
+ }
+
+ usb_free_urb(urb);
+}
+
+static struct t300rs_device_entry *t300rs_get_device(struct hid_device *hdev){
+ struct t300rs_data *drv_data;
+ struct t300rs_device_entry *t300rs;
+
+ spin_lock_irqsave(&lock, lock_flags);
+ drv_data = hid_get_drvdata(hdev);
+ if(!drv_data){
+ hid_err(hdev, "private data not found\n");
+ return NULL;
+ }
+
+ t300rs = drv_data->device_props;
+ if(!t300rs){
+ hid_err(hdev, "device properties not found\n");
+ return NULL;
+ }
+ spin_unlock_irqrestore(&lock, lock_flags);
+
+ return t300rs;
+}
+
+static int t300rs_send_int(struct input_dev *dev, u8 *send_buffer, int *trans){
+ struct hid_device *hdev = input_get_drvdata(dev);
+ struct t300rs_device_entry *t300rs;
+ struct usb_device *usbdev;
+ struct usb_interface *usbif;
+ struct usb_host_endpoint *ep;
+ struct urb *urb = usb_alloc_urb(0, GFP_ATOMIC);
+
+ t300rs = t300rs_get_device(hdev);
+ if(!t300rs){
+ hid_err(hdev, "could not get device\n");
+ }
+
+ usbdev = t300rs->usbdev;
+ usbif = t300rs->usbif;
+ ep = &usbif->cur_altsetting->endpoint[1];
+
+ usb_fill_int_urb(
+ urb,
+ usbdev,
+ usb_sndintpipe(usbdev, 1),
+ send_buffer,
+ T300RS_BUFFER_LENGTH,
+ t300rs_int_callback,
+ hdev,
+ ep->desc.bInterval
+ );
+
+ return usb_submit_urb(urb, GFP_ATOMIC);
+}
+
+static int t300rs_play_effect(struct t300rs_device_entry *t300rs, struct t300rs_effect_state *state){
+ u8 *send_buffer = kzalloc(T300RS_BUFFER_LENGTH, GFP_ATOMIC);
+ int ret, trans;
+
+ send_buffer[0] = 0x60;
+ send_buffer[2] = state->effect.id + 1;
+ send_buffer[3] = 0x89;
+ send_buffer[4] = 0x01;
+
+ ret = t300rs_send_int(t300rs->input_dev, send_buffer, &trans);
+ if(ret){
+ hid_err(t300rs->hdev, "failed starting effect play\n");
+ }
+
+ kfree(send_buffer);
+ return ret;
+}
+
+static int t300rs_stop_effect(struct t300rs_device_entry *t300rs, struct t300rs_effect_state *state){
+ u8 *send_buffer = kzalloc(T300RS_BUFFER_LENGTH, GFP_ATOMIC);
+ int ret, trans;
+
+ send_buffer[0] = 0x60;
+ send_buffer[2] = state->effect.id + 1;
+ send_buffer[3] = 0x89;
+
+ ret = t300rs_send_int(t300rs->input_dev, send_buffer, &trans);
+ if(ret){
+ hid_err(t300rs->hdev, "failed stopping effect play\n");
+ }
+
+ kfree(send_buffer);
+ return ret;
+}
+
+static void t300rs_fill_envelope(u8 *send_buffer, int i, s16 level, u16 duration, struct ff_envelope *envelope){
+ u16 attack_length = (duration * envelope->attack_length) / 0x7fff;
+ u16 attack_level = (level * envelope->attack_level) / 0x7fff;
+ u16 fade_length = (duration * envelope->fade_length) / 0x7fff;
+ u16 fade_level = (level * envelope->fade_level) / 0x7fff;
+
+ attack_length = cpu_to_le16(attack_length);
+ attack_level = cpu_to_le16(attack_level);
+ fade_length = cpu_to_le16(fade_length);
+ fade_level = cpu_to_le16(fade_level);
+
+ send_buffer[i ] = attack_length & 0xff;
+ send_buffer[i + 1] = attack_length >> 8;
+ send_buffer[i + 2] = attack_level & 0xff;
+ send_buffer[i + 3] = attack_level >> 8;
+ send_buffer[i + 4] = fade_length & 0xff;
+ send_buffer[i + 5] = fade_length >> 8;
+ send_buffer[i + 6] = fade_level & 0xff;
+ send_buffer[i + 7] = fade_level >> 8;
+}
+
+static int t300rs_upload_constant(struct t300rs_device_entry *t300rs, struct t300rs_effect_state *state){
+ u8 *send_buffer = kzalloc(T300RS_BUFFER_LENGTH, GFP_ATOMIC);
+ struct ff_effect effect = state->effect;
+ struct ff_constant_effect constant = state->effect.u.constant;
+ s16 level, le_level;
+ u16 duration, le_offset, le_duration;
+
+ int ret, trans;
+
+ /* currently this driver doesn't support dynamic effect updating quite like
+ * many games would assume. There's not a huge drawback to this method, the
+ * wheel maybe feels a bit more jittery because of this but it's not a
+ * massive downside. At some point in the future I'd like to improve the
+ * dynamic updating, but this will do for now.
+ * */
+ if(test_bit(FF_EFFECT_PLAYING, &state->flags)){
+ t300rs_stop_effect(t300rs, state);
+ }
+
+ level = (constant.level * fixp_sin16(effect.direction * 360 / 0x10000)) / 0x7fff;
+ duration = effect.replay.length;
+
+ le_level = cpu_to_le16(level);
+ le_offset = cpu_to_le16(effect.replay.delay);
+ le_duration = cpu_to_le16(duration);
+
+ send_buffer[0] = 0x60;
+ send_buffer[2] = effect.id + 1;
+ send_buffer[3] = 0x6a;
+
+ send_buffer[4] = le_level & 0xff;
+ send_buffer[5] = le_level >> 8;
+
+ t300rs_fill_envelope(send_buffer, 6, level,
+ duration, &constant.envelope);
+
+ send_buffer[15] = 0x4f;
+
+ send_buffer[16] = le_duration & 0xff;
+ send_buffer[17] = le_duration >> 8;
+
+ send_buffer[20] = le_offset & 0xff;
+ send_buffer[21] = le_offset >> 8;
+
+ send_buffer[23] = 0xff;
+ send_buffer[24] = 0xff;
+
+ ret = t300rs_send_int(t300rs->input_dev, send_buffer, &trans);
+ if(ret){
+ hid_err(t300rs->hdev, "failed uploading constant effect\n");
+ }
+
+ if(test_bit(FF_EFFECT_PLAYING, &state->flags)){
+ t300rs_play_effect(t300rs, state);
+ }
+
+ kfree(send_buffer);
+ return ret;
+}
+
+static int t300rs_upload_ramp(struct t300rs_device_entry *t300rs, struct t300rs_effect_state *state){
+ u8 *send_buffer = kzalloc(T300RS_BUFFER_LENGTH, GFP_ATOMIC);
+ struct ff_effect effect = state->effect;
+ struct ff_ramp_effect ramp = state->effect.u.ramp;
+ int ret, trans;
+ u16 difference, le_difference, top, bottom, le_duration, le_offset;
+ s16 level, le_level;
+
+ if(test_bit(FF_EFFECT_PLAYING, &state->flags)){
+ t300rs_stop_effect(t300rs, state);
+ }
+
+ top = ramp.end_level > ramp.start_level ? ramp.end_level : ramp.start_level;
+ bottom = ramp.end_level > ramp.start_level ? ramp.start_level : ramp.end_level;
+
+
+ difference = ((top - bottom) * fixp_sin16(effect.direction * 360 / 0x10000)) / 0x7fff;
+ level = (top * fixp_sin16(effect.direction * 360 / 0x10000)) / 0x7fff;
+
+ le_difference = cpu_to_le16(difference);
+ le_level = cpu_to_le16(level);
+ le_duration = cpu_to_le16(effect.replay.length);
+ le_offset = cpu_to_le16(effect.replay.delay);
+
+ send_buffer[0] = 0x60;
+ send_buffer[1] = effect.id + 1;
+ send_buffer[2] = 0x6b;
+
+ send_buffer[3] = le_difference & 0xff;
+ send_buffer[4] = le_difference >> 8;
+
+ send_buffer[5] = le_level & 0xff;
+ send_buffer[6] = le_level >> 8;
+
+ send_buffer[9] = le_duration & 0xff;
+ send_buffer[10] = le_duration >> 8;
+
+ send_buffer[12] = 0x80;
+
+ t300rs_fill_envelope(send_buffer, 14, level,
+ effect.replay.length, &ramp.envelope);
+
+ send_buffer[22] = ramp.end_level > ramp.start_level ? 0x04 : 0x05;
+ send_buffer[23] = 0x4f;
+
+ send_buffer[24] = le_duration & 0xff;
+ send_buffer[25] = le_duration >> 8;
+
+ send_buffer[28] = le_offset & 0xff;
+ send_buffer[29] = le_offset >> 8;
+
+ send_buffer[31] = 0xff;
+ send_buffer[32] = 0xff;
+
+ ret = t300rs_send_int(t300rs->input_dev, send_buffer, &trans);
+ if(ret){
+ hid_err(t300rs->hdev, "failed uploading ramp");
+ }
+
+ if(test_bit(FF_EFFECT_PLAYING, &state->flags)){
+ t300rs_play_effect(t300rs, state);
+ }
+
+ kfree(send_buffer);
+ return ret;
+}
+
+static int t300rs_upload_spring(struct t300rs_device_entry *t300rs, struct t300rs_effect_state *state){
+ u8 *send_buffer = kzalloc(T300RS_BUFFER_LENGTH, GFP_ATOMIC);
+ struct ff_effect effect = state->effect;
+ /* we only care about the first axis */
+ struct ff_condition_effect spring = state->effect.u.condition[0];
+ int ret, trans;
+ u16 le_right_coeff, le_left_coeff, le_deadband_right, le_deadband_left,
+ le_duration, le_offset;
+
+ if(test_bit(FF_EFFECT_PLAYING, &state->flags)){
+ t300rs_stop_effect(t300rs, state);
+ }
+
+ send_buffer[0] = 0x60;
+ send_buffer[2] = effect.id + 1;
+ send_buffer[3] = 0x64;
+
+ le_right_coeff = cpu_to_le16(spring.right_coeff);
+ le_left_coeff = cpu_to_le16(spring.left_coeff);
+
+ le_deadband_right = cpu_to_le16(0xfffe - spring.deadband - spring.center);
+ le_deadband_left = cpu_to_le16(0xfffe - spring.deadband + spring.center);
+
+ le_duration = cpu_to_le16(effect.replay.length);
+ le_offset = cpu_to_le16(effect.replay.delay);
+
+ send_buffer[4] = le_right_coeff & 0xff;
+ send_buffer[5] = le_right_coeff >> 8;
+
+ send_buffer[6] = le_left_coeff & 0xff;
+ send_buffer[7] = le_left_coeff >> 8;
+
+ send_buffer[8] = le_deadband_right & 0xff;
+ send_buffer[9] = le_deadband_right >> 8;
+
+ send_buffer[10] = le_deadband_left & 0xff;
+ send_buffer[11] = le_deadband_left >> 8;
+
+ memcpy(&send_buffer[12], spring_values, ARRAY_SIZE(spring_values));
+ send_buffer[29] = 0x4f;
+
+ send_buffer[30] = le_duration & 0xff;
+ send_buffer[31] = le_duration >> 8;
+
+ send_buffer[34] = le_offset & 0xff;
+ send_buffer[35] = le_offset >> 8;
+
+ send_buffer[37] = 0xff;
+ send_buffer[38] = 0xff;
+
+ ret = t300rs_send_int(t300rs->input_dev, send_buffer, &trans);
+ if(ret){
+ hid_err(t300rs->hdev, "failed uploading spring\n");
+ }
+
+ if(test_bit(FF_EFFECT_PLAYING, &state->flags)){
+ t300rs_play_effect(t300rs, state);
+ }
+
+ kfree(send_buffer);
+ return ret;
+}
+
+static int t300rs_upload_damper(struct t300rs_device_entry *t300rs, struct t300rs_effect_state *state){
+ u8 *send_buffer = kzalloc(T300RS_BUFFER_LENGTH, GFP_ATOMIC);
+ struct ff_effect effect = state->effect;
+ /* we only care about the first axis */
+ struct ff_condition_effect spring = state->effect.u.condition[0];
+ int ret, trans;
+ u16 le_right_coeff, le_left_coeff, le_deadband_right, le_deadband_left,
+ le_duration, le_offset;
+
+ if(test_bit(FF_EFFECT_PLAYING, &state->flags)){
+ t300rs_stop_effect(t300rs, state);
+ }
+
+ send_buffer[0] = 0x60;
+ send_buffer[2] = effect.id + 1;
+ send_buffer[3] = 0x64;
+
+ le_right_coeff = cpu_to_le16(spring.right_coeff);
+ le_left_coeff = cpu_to_le16(spring.left_coeff);
+
+ le_deadband_right = cpu_to_le16(0xfffe - spring.deadband - spring.center);
+ le_deadband_left = cpu_to_le16(0xfffe - spring.deadband + spring.center);
+
+ le_duration = cpu_to_le16(effect.replay.length);
+ le_offset = cpu_to_le16(effect.replay.delay);
+
+ send_buffer[4] = le_right_coeff & 0xff;
+ send_buffer[5] = le_right_coeff >> 8;
+
+ send_buffer[6] = le_left_coeff & 0xff;
+ send_buffer[7] = le_left_coeff >> 8;
+
+ send_buffer[8] = le_deadband_right & 0xff;
+ send_buffer[9] = le_deadband_right >> 8;
+
+ send_buffer[10] = le_deadband_left & 0xff;
+ send_buffer[11] = le_deadband_left >> 8;
+
+ memcpy(&send_buffer[12], damper_values, ARRAY_SIZE(damper_values));
+ send_buffer[29] = 0x4f;
+
+ send_buffer[30] = le_duration & 0xff;
+ send_buffer[31] = le_duration >> 8;
+
+ send_buffer[34] = le_offset & 0xff;
+ send_buffer[35] = le_offset >> 8;
+
+ send_buffer[37] = 0xff;
+ send_buffer[38] = 0xff;
+
+ ret = t300rs_send_int(t300rs->input_dev, send_buffer, &trans);
+ if(ret){
+ hid_err(t300rs->hdev, "failed uploading spring\n");
+ }
+
+ if(test_bit(FF_EFFECT_PLAYING, &state->flags)){
+ t300rs_play_effect(t300rs, state);
+ }
+
+ kfree(send_buffer);
+ return ret;
+}
+
+static int t300rs_upload_periodic(struct t300rs_device_entry *t300rs, struct t300rs_effect_state *state){
+ u8 *send_buffer = kzalloc(T300RS_BUFFER_LENGTH, GFP_ATOMIC);
+ struct ff_effect effect = state->effect;
+ struct ff_periodic_effect periodic = state->effect.u.periodic;
+ int ret, trans;
+ u16 magnitude, le_magnitude, le_phase, le_period, le_offset, le_duration;
+ s16 le_periodic_offset;
+
+ if(test_bit(FF_EFFECT_PLAYING, &state->flags)){
+ t300rs_stop_effect(t300rs, state);
+ }
+
+ magnitude = (periodic.magnitude * fixp_sin16(effect.direction * 360 / 0x10000)) / 0x7fff;
+
+ le_magnitude = cpu_to_le16(magnitude);
+ le_phase = cpu_to_le16(periodic.phase);
+ le_periodic_offset = cpu_to_le16(periodic.offset);
+ le_period = cpu_to_le16(periodic.period);
+ le_offset = cpu_to_le16(effect.replay.delay);
+ le_duration = cpu_to_le16(effect.replay.length);
+
+ send_buffer[0] = 0x60;
+ send_buffer[2] = effect.id + 1;
+ send_buffer[3] = 0x6b;
+
+ send_buffer[4] = le_magnitude & 0xff;
+ send_buffer[5] = le_magnitude >> 8;
+
+ send_buffer[6] = le_phase & 0xff;
+ send_buffer[7] = le_phase >> 8;
+
+ send_buffer[8] = le_periodic_offset & 0xff;
+ send_buffer[9] = le_periodic_offset >> 8;
+
+ send_buffer[10] = le_period & 0xff;
+ send_buffer[11] = le_period >> 8;
+
+ send_buffer[13] = 0x80;
+
+ t300rs_fill_envelope(send_buffer, 14, magnitude,
+ effect.replay.length, &periodic.envelope);
+
+ send_buffer[22] = periodic.waveform - 0x57;
+ send_buffer[23] = 0x4f;
+
+ send_buffer[24] = le_duration & 0xff;
+ send_buffer[25] = le_duration >> 8;
+
+ send_buffer[27] = le_offset & 0xff;
+ send_buffer[28] = le_offset >> 8;
+
+ send_buffer[30] = 0xff;
+ send_buffer[31] = 0xff;
+
+ ret = t300rs_send_int(t300rs->input_dev, send_buffer, &trans);
+ if(ret){
+ hid_err(t300rs->hdev, "failed uploading periodic effect");
+ }
+
+ if(test_bit(FF_EFFECT_PLAYING, &state->flags)){
+ t300rs_play_effect(t300rs, state);
+ }
+
+ kfree(send_buffer);
+ return ret;
+}
+
+static int t300rs_upload_effect(struct t300rs_device_entry *t300rs, struct t300rs_effect_state *state){
+ switch(state->effect.type){
+ case FF_CONSTANT:
+ return t300rs_upload_constant(t300rs, state);
+ case FF_RAMP:
+ return t300rs_upload_ramp(t300rs, state);
+ case FF_SPRING:
+ return t300rs_upload_spring(t300rs, state);
+ case FF_DAMPER:
+ case FF_FRICTION:
+ case FF_INERTIA:
+ return t300rs_upload_damper(t300rs, state);
+ case FF_PERIODIC:
+ return t300rs_upload_periodic(t300rs, state);
+ default:
+ hid_err(t300rs->hdev, "invalid effect type: %x", state->effect.type);
+ return -1;
+ }
+}
+
+static int t300rs_timer_helper(struct t300rs_device_entry *t300rs){
+ struct t300rs_effect_state *state;
+ unsigned long jiffies_now = JIFFIES2MS(jiffies);
+ int max_count = 0, effect_id, ret;
+
+ for(effect_id = 0; effect_id < T300RS_MAX_EFFECTS; ++effect_id){
+
+ state = &t300rs->states[effect_id];
+
+ if(test_bit(FF_EFFECT_PLAYING, &state->flags)){
+ if((jiffies_now - state->start_time) >= state->effect.replay.length){
+ __clear_bit(FF_EFFECT_PLAYING, &state->flags);
+
+ if(state->count){
+ __set_bit(FF_EFFECT_QUEUE_START, &state->flags);
+ }
+ }
+ }
+
+ if(test_bit(FF_EFFECT_QUEUE_UPLOAD, &state->flags)){
+ __clear_bit(FF_EFFECT_QUEUE_UPLOAD, &state->flags);
+
+ ret = t300rs_upload_effect(t300rs, state);
+ if(ret){
+ hid_err(t300rs->hdev, "failed uploading effects");
+ return ret;
+ }
+ }
+
+ if(test_bit(FF_EFFECT_QUEUE_START, &state->flags)){
+ __clear_bit(FF_EFFECT_QUEUE_START, &state->flags);
+ __set_bit(FF_EFFECT_PLAYING, &state->flags);
+
+ ret = t300rs_play_effect(t300rs, state);
+ if(ret){
+ hid_err(t300rs->hdev, "failed starting effects\n");
+ return ret;
+ }
+
+ if(state->count){
+ state->count--;
+ }
+ }
+
+ if(test_bit(FF_EFFECT_QUEUE_STOP, &state->flags)){
+ __clear_bit(FF_EFFECT_QUEUE_STOP, &state->flags);
+ __clear_bit(FF_EFFECT_PLAYING, &state->flags);
+
+ ret = t300rs_stop_effect(t300rs, state);
+ if(ret){
+ hid_err(t300rs->hdev, "failed stopping effect\n");
+ return ret;
+ }
+ }
+
+ if(state->count > max_count){
+ max_count = state->count;
+ }
+ }
+
+ return max_count;
+}
+
+static enum hrtimer_restart t300rs_timer(struct hrtimer *t){
+ struct t300rs_device_entry *t300rs = container_of(t, struct t300rs_device_entry, hrtimer);
+ int max_count;
+
+ max_count = t300rs_timer_helper(t300rs);
+
+ if(max_count > 0){
+ hrtimer_forward_now(&t300rs->hrtimer, ms_to_ktime(timer_msecs));
+ return HRTIMER_RESTART;
+ } else {
+ return HRTIMER_NORESTART;
+ }
+}
+
+
+static int t300rs_upload(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old){
+ struct hid_device *hdev = input_get_drvdata(dev);
+ struct t300rs_device_entry *t300rs;
+ struct t300rs_effect_state *state;
+
+ t300rs = t300rs_get_device(hdev);
+
+ if(effect->type == FF_PERIODIC && effect->u.periodic.period == 0){
+ return -EINVAL;
+ }
+
+ if(effect->id > t300rs->max_id){
+ t300rs->max_id = effect->id;
+ }
+
+ state = &t300rs->states[effect->id];
+
+ spin_lock_irqsave(&t300rs->lock, t300rs->lock_flags);
+
+ state->effect = *effect;
+ __set_bit(FF_EFFECT_QUEUE_UPLOAD, &state->flags);
+
+ spin_unlock_irqrestore(&t300rs->lock, t300rs->lock_flags);
+
+ return 0;
+}
+
+static int t300rs_play(struct input_dev *dev, int effect_id, int value){
+ struct hid_device *hdev = input_get_drvdata(dev);
+ struct t300rs_device_entry *t300rs;
+ struct t300rs_effect_state *state;
+
+ t300rs = t300rs_get_device(hdev);
+
+ state = &t300rs->states[effect_id];
+
+ spin_lock_irqsave(&t300rs->lock, t300rs->lock_flags);
+
+ if(value > 0){
+ if(test_bit(FF_EFFECT_QUEUE_START, &state->flags)){
+ __set_bit(FF_EFFECT_QUEUE_STOP, &state->flags);
+ } else {
+ t300rs->effects_used++;
+ }
+
+ state->count = value;
+ state->start_time = JIFFIES2MS(jiffies);
+ __set_bit(FF_EFFECT_QUEUE_START, &state->flags);
+
+ } else {
+ __set_bit(FF_EFFECT_QUEUE_STOP, &state->flags);
+ t300rs->effects_used--;
+ }
+
+ if(!hrtimer_active(&t300rs->hrtimer)){
+ hrtimer_start(&t300rs->hrtimer, ms_to_ktime(timer_msecs), HRTIMER_MODE_REL);
+ }
+
+ spin_unlock_irqrestore(&t300rs->lock, t300rs->lock_flags);
+ return 0;
+}
+
+/* we should set a default range */
+static ssize_t t300rs_range_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count){
+ struct hid_device *hdev = to_hid_device(dev);
+ struct t300rs_device_entry *t300rs;
+ u8 *send_buffer = kzalloc(T300RS_BUFFER_LENGTH, GFP_ATOMIC);
+ u16 range = simple_strtoul(buf, NULL, 10);
+ int ret, trans;
+
+ t300rs = t300rs_get_device(hdev);
+
+ if(range < 0x097b){
+ range = 0x097b;
+ }
+
+ range = cpu_to_le16(range);
+
+ send_buffer[0] = 0x60;
+ send_buffer[1] = 0x08;
+ send_buffer[2] = 0x11;
+ send_buffer[3] = range & 0xff;
+ send_buffer[4] = range >> 8;
+
+ ret = t300rs_send_int(t300rs->input_dev, send_buffer, &trans);
+ if(ret){
+ hid_err(hdev, "failed sending interrupts\n");
+ return -1;
+ }
+
+ t300rs->range = range;
+ kfree(send_buffer);
+ return count;
+}
+
+static ssize_t t300rs_range_show(struct device *dev, struct device_attribute *attr,
+ char *buf){
+ struct hid_device *hdev = to_hid_device(dev);
+ struct t300rs_device_entry *t300rs;
+
+ t300rs = t300rs_get_device(hdev);
+
+ return t300rs->range;
+}
+
+static DEVICE_ATTR(range, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, t300rs_range_show, t300rs_range_store);
+
+static void t300rs_set_autocenter(struct input_dev *dev, u16 value){
+ struct hid_device *hdev = input_get_drvdata(dev);
+ u8 *send_buffer = kzalloc(T300RS_BUFFER_LENGTH, GFP_ATOMIC);
+ u16 le_value = cpu_to_le16(value);
+ int ret, trans;
+
+ send_buffer[0] = 0x60;
+ send_buffer[1] = 0x08;
+ send_buffer[2] = 0x03;
+
+ send_buffer[3] = le_value & 0xff;
+ send_buffer[4] = le_value >> 8;
+
+ ret = t300rs_send_int(dev, send_buffer, &trans);
+ if(ret){
+ hid_err(hdev, "failed setting autocenter");
+ }
+
+ kfree(send_buffer);
+}
+
+static void t300rs_set_gain(struct input_dev *dev, u16 gain){
+ struct hid_device *hdev = input_get_drvdata(dev);
+ u8 *send_buffer = kzalloc(T300RS_BUFFER_LENGTH, GFP_ATOMIC);
+ int ret, trans;
+
+ send_buffer[0] = 0x60;
+ send_buffer[1] = 0x02;
+ send_buffer[2] = SCALE_VALUE_U16(gain, 8);
+
+ ret = t300rs_send_int(dev, send_buffer, &trans);
+ if(ret){
+ hid_err(hdev, "failed setting gain\n");
+ }
+
+ kfree(send_buffer);
+}
+
+static void t300rs_destroy(struct ff_device *ff){
+ /* maybe not temp? */
+ return;
+}
+
+
+static int t300rs_open(struct input_dev *dev){
+ struct t300rs_device_entry *t300rs;
+ struct hid_device *hdev = input_get_drvdata(dev);
+ u8 *send_buffer = kzalloc(T300RS_BUFFER_LENGTH, GFP_ATOMIC);
+ int ret, trans;
+
+ t300rs = t300rs_get_device(hdev);
+
+ send_buffer[0] = 0x60;
+ send_buffer[1] = 0x01;
+ send_buffer[2] = 0x04;
+
+ ret = t300rs_send_int(dev, send_buffer, &trans);
+ if(ret){
+ hid_err(hdev, "failed sending interrupts\n");
+ goto err;
+ }
+ memset(send_buffer, 0, T300RS_BUFFER_LENGTH);
+
+ send_buffer[0] = 0x60;
+ send_buffer[1] = 0x12;
+ send_buffer[2] = 0xbf;
+ send_buffer[3] = 0x04;
+ send_buffer[6] = 0x03;
+ send_buffer[7] = 0xb7;
+ send_buffer[8] = 0x1e;
+
+ ret = t300rs_send_int(dev, send_buffer, &trans);
+ if(ret){
+ hid_err(hdev, "failed sending interrupts\n");
+ goto err;
+ }
+ memset(send_buffer, 0, T300RS_BUFFER_LENGTH);
+
+ send_buffer[0] = 0x60;
+ send_buffer[1] = 0x01;
+ send_buffer[2] = 0x05;
+
+ ret = t300rs_send_int(dev, send_buffer, &trans);
+ if(ret){
+ hid_err(hdev, "failed sending interrupts\n");
+ goto err;
+ }
+
+err:
+ kfree(send_buffer);
+ return t300rs->open(dev);
+}
+
+static void t300rs_close(struct input_dev *dev){
+ int ret, trans;
+ struct hid_device *hdev = input_get_drvdata(dev);
+ struct t300rs_device_entry *t300rs;
+ u8 *send_buffer = kzalloc(T300RS_BUFFER_LENGTH, GFP_ATOMIC);
+
+ t300rs = t300rs_get_device(hdev);
+
+ send_buffer[0] = 0x60;
+ send_buffer[1] = 0x01;
+
+ ret = t300rs_send_int(dev, send_buffer, &trans);
+ if(ret){
+ hid_err(hdev, "failed sending interrupts\n");
+ goto err;
+ }
+err:
+ kfree(send_buffer);
+ t300rs->close(dev);
+ return;
+}
+
+int t300rs_init(struct hid_device *hdev, const signed short *ff_bits){
+ struct t300rs_device_entry *t300rs;
+ struct t300rs_data *drv_data;
+ struct list_head *report_list;
+ struct hid_input *hidinput = list_entry(hdev->inputs.next,
+ struct hid_input, list);
+ struct input_dev *input_dev = hidinput->input;
+ struct device *dev = &hdev->dev;
+ struct usb_interface *usbif = to_usb_interface(dev->parent);
+ struct usb_device *usbdev = interface_to_usbdev(usbif);
+ struct hid_report *report;
+ struct ff_device *ff;
+ char range[10] = "54000"; /* approx 900 degress */
+ int i, ret;
+
+ drv_data = hid_get_drvdata(hdev);
+ if(!drv_data){
+ hid_err(hdev, "private driver data not allocated\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ t300rs = kzalloc(sizeof(struct t300rs_device_entry), GFP_ATOMIC);
+ if(!t300rs){
+ hid_err(hdev, "device entry could not be created\n");
+ ret = -ENOMEM;
+ goto t300rs_err;
+ }
+
+ t300rs->input_dev = input_dev;
+ t300rs->hdev = hdev;
+ t300rs->usbdev = usbdev;
+ t300rs->usbif = usbif;
+ t300rs->max_id = 0;
+ t300rs->states = kmalloc(sizeof(struct t300rs_effect_state) * 0x60, GFP_ATOMIC);
+
+ if(!t300rs->states){
+ hid_err(hdev, "effect states could not be created\n");
+ ret = -ENOMEM;
+ goto states_err;
+ }
+
+ spin_lock_init(&t300rs->lock);
+
+ drv_data->device_props = t300rs;
+
+ report_list = &hdev->report_enum[HID_OUTPUT_REPORT].report_list;
+ list_for_each_entry(report, report_list, list){
+ int fieldnum;
+
+ for(fieldnum = 0; fieldnum < report->maxfield; ++fieldnum){
+ struct hid_field *field = report->field[fieldnum];
+
+ if(field->maxusage <= 0){
+ continue;
+ }
+
+ switch(field->usage[0].hid){
+ case 0xff00000a:
+ if(field->report_count < 2){
+ hid_warn(hdev, "ignoring FF field with report_count < 2\n");
+ continue;
+ }
+
+ if(field->logical_maximum == field->logical_minimum){
+ hid_warn(hdev, "ignoring FF field with l_max == l_min");
+ continue;
+ }
+
+ if(t300rs->report && t300rs->report != report){
+ hid_warn(hdev, "ignoring FF field in other report\n");
+ continue;
+ }
+
+ if(t300rs->ff_field && t300rs->ff_field != field){
+ hid_warn(hdev, "ignoring duplicate FF field\n");
+ continue;
+ }
+
+ t300rs->report = report;
+ t300rs->ff_field = field;
+
+ for(i = 0; ff_bits[i] >= 0; ++i){
+ set_bit(ff_bits[i], input_dev->ffbit);
+ }
+
+ break;
+
+ default:
+ hid_warn(hdev, "ignoring unknown output usage\n");
+ continue;
+ }
+ }
+ }
+
+ if(!t300rs->report){
+ hid_err(hdev, "can't find FF field in output reports\n");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ ret = input_ff_create(input_dev, T300RS_MAX_EFFECTS);
+ if(ret){
+ hid_err(hdev, "could not create input_ff\n");
+ goto out;
+ }
+
+ ff = input_dev->ff;
+ ff->upload = t300rs_upload;
+ ff->playback = t300rs_play;
+ ff->set_gain = t300rs_set_gain;
+ ff->set_autocenter = t300rs_set_autocenter;
+ ff->destroy = t300rs_destroy;
+
+ t300rs->open = input_dev->open;
+ t300rs->close = input_dev->close;
+
+ input_dev->open = t300rs_open;
+ input_dev->close = t300rs_close;
+
+ ret = device_create_file(&hdev->dev, &dev_attr_range);
+ if(ret){
+ hid_warn(hdev, "unable to create sysfs interface for range\n");
+ goto out;
+ }
+
+
+ hrtimer_init(&t300rs->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ t300rs->hrtimer.function = t300rs_timer;
+
+ spin_unlock_irqrestore(&lock, lock_flags);
+
+ t300rs_range_store(dev, &dev_attr_range, range, 10);
+ t300rs_set_gain(input_dev, 0xffff);
+
+ hid_info(hdev, "force feedback for T300RS\n");
+ return 0;
+
+out:
+ kfree(t300rs->states);
+states_err:
+ kfree(t300rs);
+t300rs_err:
+ kfree(drv_data);
+err:
+ hid_err(hdev, "failed creating force feedback device\n");
+ return ret;
+
+}
+
+static int t300rs_probe(struct hid_device *hdev, const struct hid_device_id *id){
+ int ret;
+ struct t300rs_data *drv_data;
+
+ spin_lock_init(&lock);
+ spin_lock_irqsave(&lock, lock_flags);
+
+ drv_data = kzalloc(sizeof(struct t300rs_data), GFP_ATOMIC);
+ if(!drv_data){
+ hid_err(hdev, "out of memory\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ drv_data->quirks = id->driver_data;
+ hid_set_drvdata(hdev, (void*)drv_data);
+
+ ret = hid_parse(hdev);
+ if(ret){
+ hid_err(hdev, "parse failed\n");
+ goto err;
+ }
+
+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
+ if(ret){
+ hid_err(hdev, "hw start failed\n");
+ goto err;
+ }
+
+ ret = t300rs_init(hdev, (void*)id->driver_data);
+ if(ret){
+ hid_err(hdev, "t300rs_init failed\n");
+ goto err;
+ }
+ return 0;
+err:
+ kfree(drv_data);
+ spin_unlock_irqrestore(&lock, lock_flags);
+ return ret;
+}
+
+static void t300rs_remove(struct hid_device *hdev){
+ struct t300rs_device_entry *t300rs;
+ struct t300rs_data *drv_data;
+
+ device_remove_file(&hdev->dev, &dev_attr_range);
+
+ drv_data = hid_get_drvdata(hdev);
+ t300rs = t300rs_get_device(hdev);
+
+ hrtimer_cancel(&t300rs->hrtimer);
+
+ hid_hw_stop(hdev);
+ kfree(t300rs->states);
+ kfree(drv_data);
+ kfree(t300rs);
+
+ return;
+}
+
+static __u8 *t300rs_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize){
+ rdesc = t300rs_rdesc_fixed;
+ *rsize = sizeof(t300rs_rdesc_fixed);
+ return rdesc;
+}
+
+static const struct hid_device_id t300rs_devices[] = {
+ {HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb66e),
+ .driver_data = (unsigned long)t300rs_ff_effects},
+ {}
+};
+MODULE_DEVICE_TABLE(hid, t300rs_devices);
+
+static struct hid_driver t300rs_driver = {
+ .name = "t300rs",
+ .id_table = t300rs_devices,
+ .probe = t300rs_probe,
+ .remove = t300rs_remove,
+ .report_fixup = t300rs_report_fixup,
+};
+module_hid_driver(t300rs_driver);
+
+MODULE_LICENSE("GPL");
diff --git a/hid-tmt300rs.h b/hid-tmt300rs.h
new file mode 100644
index 0000000..ccdea78
--- /dev/null
+++ b/hid-tmt300rs.h
@@ -0,0 +1,86 @@
+#include "hid-tm.h"
+#include "usbhid.h"
+#include <linux/hrtimer.h>
+#include <linux/ktime.h>
+#include <linux/fixp-arith.h>
+
+#define T300RS_MAX_EFFECTS 16
+#define T300RS_BUFFER_LENGTH 64
+
+#define DEFAULT_TIMER_PERIOD 4 /* the wheel seems to be bad at stuff*/
+
+#define FF_EFFECT_QUEUE_UPLOAD 0
+#define FF_EFFECT_QUEUE_START 1
+#define FF_EFFECT_QUEUE_STOP 2
+#define FF_EFFECT_PLAYING 3
+
+#define CLAMP_VALUE_U16(x) ((unsigned short)((x) > 0xffff ? 0xffff : (x)))
+#define SCALE_VALUE_U16(x, bits) (CLAMP_VALUE_U16(x) >> (16 - bits))
+#define JIFFIES2MS(jiffies) ((jiffies) * 1000 / HZ)
+
+spinlock_t lock;
+unsigned long lock_flags;
+
+static int timer_msecs = DEFAULT_TIMER_PERIOD;
+
+static const signed short t300rs_ff_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
+};
+
+struct t300rs_effect_state {
+ struct ff_effect effect;
+ unsigned long flags;
+ unsigned long start_time;
+ unsigned long count;
+};
+
+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;
+ struct usb_interface *usbif;
+ struct t300rs_effect_state *states;
+ struct hrtimer hrtimer;
+
+ int (*open)(struct input_dev *dev);
+ void (*close)(struct input_dev *dev);
+
+ spinlock_t lock;
+ unsigned long lock_flags;
+
+ int max_id;
+ u16 range;
+ u8 effects_used;
+};
+
+
+struct t300rs_data{
+ unsigned long quirks;
+ void *device_props;
+};
+
+static __u8 t300rs_rdesc_fixed[] = {0x05, 0x01, 0x09, 0x04, 0xa1, 0x01, 0x09, 0x01, 0xa1, 0x00, 0x85, 0x07, 0x09, 0x30, 0x15, 0x00, 0x27, 0xff, 0xff, 0x00, 0x00, 0x35, 0x00, 0x47, 0xff, 0xff, 0x00, 0x00, 0x75, 0x10, 0x95, 0x01, 0x81, 0x02, 0x09, 0x31, 0x26, 0xff, 0x03, 0x46, 0xff, 0x03, 0x81, 0x02, 0x09, 0x35, 0x81, 0x02, 0x09, 0x36, 0x81, 0x02, 0x81, 0x03, 0x05, 0x09, 0x19, 0x01, 0x29, 0x0d, 0x25, 0x01, 0x45, 0x01, 0x75, 0x01, 0x95, 0x0d, 0x81, 0x02, 0x75, 0x0b, 0x95, 0x01, 0x81, 0x03, 0x05, 0x01, 0x09, 0x39, 0x25, 0x07, 0x46, 0x3b, 0x01, 0x55, 0x00, 0x65, 0x14, 0x75, 0x04, 0x81, 0x42, 0x65, 0x00, 0x81, 0x03, 0x85, 0x0a, 0x06, 0x00, 0xff, 0x09, 0x0a, 0x75, 0x08, 0x95, 0x3f, 0x26, 0xff, 0x7f, 0x16, 0x00, 0x80, 0x46, 0xff, 0x7f, 0x36, 0x00, 0x80, 0x91, 0x02, 0x85, 0x02, 0x09, 0x02, 0x81, 0x02, 0x09, 0x14, 0x85, 0x14, 0x81, 0x02, 0xc0, 0xc0,};
+
+static u8 spring_values[] = {
+ 0xa6, 0x6a, 0xa6, 0x6a, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xdf, 0x58, 0xa6, 0x6a, 0x06
+};
+
+static u8 damper_values[] = {
+ 0xfc, 0x7f, 0xfc, 0x7f, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfc, 0x7f, 0xfc, 0x7f, 0x07
+};
diff --git a/todo.txt b/todo.txt
new file mode 100644
index 0000000..ea2310d
--- /dev/null
+++ b/todo.txt
@@ -0,0 +1,3 @@
+1. set a default range for the wheel so that it syncs up correctly
+2. figure out the bug where the effects suddenly stop being sent (happens in
+dirt rally at least)
diff --git a/usbhid.h b/usbhid.h
new file mode 100644
index 0000000..8ab2917
--- /dev/null
+++ b/usbhid.h
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __USBHID_H
+#define __USBHID_H
+
+/*
+ * Copyright (c) 1999 Andreas Gal
+ * Copyright (c) 2000-2001 Vojtech Pavlik
+ * Copyright (c) 2006 Jiri Kosina
+ */
+
+/*
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/input.h>
+
+/* API provided by hid-core.c for USB HID drivers */
+void usbhid_init_reports(struct hid_device *hid);
+struct usb_interface *usbhid_find_interface(int minor);
+
+/* iofl flags */
+#define HID_CTRL_RUNNING 1
+#define HID_OUT_RUNNING 2
+#define HID_IN_RUNNING 3
+#define HID_RESET_PENDING 4
+#define HID_SUSPENDED 5
+#define HID_CLEAR_HALT 6
+#define HID_DISCONNECTED 7
+#define HID_STARTED 8
+#define HID_KEYS_PRESSED 10
+#define HID_NO_BANDWIDTH 11
+#define HID_RESUME_RUNNING 12
+/*
+ * The device is opened, meaning there is a client that is interested
+ * in data coming from the device.
+ */
+#define HID_OPENED 13
+/*
+ * We are polling input endpoint by [re]submitting IN URB, because
+ * either HID device is opened or ALWAYS POLL quirk is set for the
+ * device.
+ */
+#define HID_IN_POLLING 14
+
+/*
+ * USB-specific HID struct, to be pointed to
+ * from struct hid_device->driver_data
+ */
+
+struct usbhid_device {
+ struct hid_device *hid; /* pointer to corresponding HID dev */
+
+ struct usb_interface *intf; /* USB interface */
+ int ifnum; /* USB interface number */
+
+ unsigned int bufsize; /* URB buffer size */
+
+ struct urb *urbin; /* Input URB */
+ char *inbuf; /* Input buffer */
+ dma_addr_t inbuf_dma; /* Input buffer dma */
+
+ struct urb *urbctrl; /* Control URB */
+ struct usb_ctrlrequest *cr; /* Control request struct */
+ struct hid_control_fifo ctrl[HID_CONTROL_FIFO_SIZE]; /* Control fifo */
+ unsigned char ctrlhead, ctrltail; /* Control fifo head & tail */
+ char *ctrlbuf; /* Control buffer */
+ dma_addr_t ctrlbuf_dma; /* Control buffer dma */
+ unsigned long last_ctrl; /* record of last output for timeouts */
+
+ struct urb *urbout; /* Output URB */
+ struct hid_output_fifo out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */
+ unsigned char outhead, outtail; /* Output pipe fifo head & tail */
+ char *outbuf; /* Output buffer */
+ dma_addr_t outbuf_dma; /* Output buffer dma */
+ unsigned long last_out; /* record of last output for timeouts */
+
+ spinlock_t lock; /* fifo spinlock */
+ unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */
+ struct timer_list io_retry; /* Retry timer */
+ unsigned long stop_retry; /* Time to give up, in jiffies */
+ unsigned int retry_delay; /* Delay length in ms */
+ struct work_struct reset_work; /* Task context for resets */
+ wait_queue_head_t wait; /* For sleeping */
+};
+
+#define hid_to_usb_dev(hid_dev) \
+ to_usb_device(hid_dev->dev.parent->parent)
+
+#endif