From 75006183d0a379a2b0fa844fc32b3e27d229f368 Mon Sep 17 00:00:00 2001 From: RaySlash Date: Mon, 2 Oct 2023 13:08:52 +1000 Subject: src: initial refactoring --- .gitmodules | 6 +- Kbuild | 2 - Makefile | 16 - README.md | 65 +- TODO | 7 - dkms-install.sh | 19 - dkms.conf | 18 - dkms/dkms-install.sh | 19 + dkms/dkms.conf | 18 + docs/CONTRIBUTING.md | 41 + docs/FFBEffects-T248.md | 7 + docs/FFBEffects-T300RS.md | 542 +++++++++++++ docs/TODO.md | 10 + force-effects.txt | 462 ----------- hid-tmff2.c | 749 ----------------- hid-tmff2.h | 147 ---- hid-tminit | 1 - hid-tmt248.c | 335 -------- hid-tmt300rs.c | 1714 --------------------------------------- hid-tmt300rs.h | 9 - hid-tmtx.c | 323 -------- ps4-input.txt | 54 -- src/Kbuild | 2 + src/Makefile | 16 + src/hid-tmff2.c | 749 +++++++++++++++++ src/hid-tmff2.h | 147 ++++ src/hid-tminit | 1 + src/hid-tmt248/hid-tmt248.c | 335 ++++++++ src/hid-tmt300rs/hid-tmt300rs.c | 1714 +++++++++++++++++++++++++++++++++++++++ src/hid-tmt300rs/hid-tmt300rs.h | 9 + src/hid-tmtx/hid-tmtx.c | 323 ++++++++ t248-effects.txt | 2 - 32 files changed, 3962 insertions(+), 3900 deletions(-) delete mode 100644 Kbuild delete mode 100644 Makefile delete mode 100644 TODO delete mode 100755 dkms-install.sh delete mode 100644 dkms.conf create mode 100755 dkms/dkms-install.sh create mode 100644 dkms/dkms.conf create mode 100644 docs/CONTRIBUTING.md create mode 100644 docs/FFBEffects-T248.md create mode 100644 docs/FFBEffects-T300RS.md create mode 100644 docs/TODO.md delete mode 100644 force-effects.txt delete mode 100644 hid-tmff2.c delete mode 100644 hid-tmff2.h delete mode 160000 hid-tminit delete mode 100644 hid-tmt248.c delete mode 100644 hid-tmt300rs.c delete mode 100644 hid-tmt300rs.h delete mode 100644 hid-tmtx.c delete mode 100644 ps4-input.txt create mode 100644 src/Kbuild create mode 100644 src/Makefile create mode 100644 src/hid-tmff2.c create mode 100644 src/hid-tmff2.h create mode 160000 src/hid-tminit create mode 100644 src/hid-tmt248/hid-tmt248.c create mode 100644 src/hid-tmt300rs/hid-tmt300rs.c create mode 100644 src/hid-tmt300rs/hid-tmt300rs.h create mode 100644 src/hid-tmtx/hid-tmtx.c delete mode 100644 t248-effects.txt diff --git a/.gitmodules b/.gitmodules index 9415bdf..6038e0c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "hid-tminit"] - path = hid-tminit - url = https://github.com/scarburato/hid-tminit.git +[submodule "src/hid-tminit"] + path = src/hid-tminit + url = https://github.com/scarburato/hid-tminit diff --git a/Kbuild b/Kbuild deleted file mode 100644 index 619792c..0000000 --- a/Kbuild +++ /dev/null @@ -1,2 +0,0 @@ -obj-m := hid-tmff-new.o -hid-tmff-new-y := hid-tmff2.o hid-tmt300rs.o hid-tmt248.o hid-tmtx.o diff --git a/Makefile b/Makefile deleted file mode 100644 index 5e2ab40..0000000 --- a/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -KDIR ?= /lib/modules/$(shell uname -r)/build - -all: hid-tminit - $(MAKE) -C $(KDIR) M=$(shell pwd) modules - -install: hid-tminit - $(MAKE) -C $(KDIR) M=$(shell pwd) modules_install - depmod -A - -clean: hid-tminit - $(MAKE) -C $(KDIR) M=$(shell pwd) clean - - -.PHONY: hid-tminit -hid-tminit: - $(MAKE) -C hid-tminit KDIR="$(KDIR)" $(MAKECMDGOALS) diff --git a/README.md b/README.md index bd06fc8..a0e075c 100644 --- a/README.md +++ b/README.md @@ -1,67 +1,57 @@ -# Linux kernel module for Thrustmaster T300RS, T248 and (experimental) TX wheels +# Linux kernel module for Thrustmaster T300RS, T248 and TX(experimental) wheels -## Current state -Playable. I've made some improvements to the dynamic updating of effects, and -while still far for perfect, the experience is slowly getting better and better. -Some drawbacks include possible effect inaccuracies in comparison with the Windows driver and in some -games inconsistent pedal mapping. Meaning that all pedals should be detected in games, but may be mapped incorrectly. +## Description +A Linux kernel module for Thrustmaster T300RS, T248, and TX wheels is a software component designed to enhance the compatibility and functionality of these popular racing wheel peripherals when used with Linux-based operating systems. This kernel module allows seamless integration of Thrustmaster wheels with the Linux kernel, providing users with a robust and feature-rich experience for gaming and other applications. -Anycase, **this version is usable in most force feedback games, supports -rangesetting as well as gain and autocentering along with most force feedback effects.** +I've made some improvements to the dynamic updating of effects, and while still far for perfect, the experience is slowly getting better and better. Some drawbacks include possible effect inaccuracies in comparison with the Windows driver and in some games inconsistent pedal mapping. Meaning that all pedals should be detected in games, but may be mapped incorrectly. -### Help wanted with adding more Thrustmaster wheels +> **Current version ``0.8`` is USABLE in most force feedback games, supports rangesetting as well as gain and autocentering along with most force feedback effects.** 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. +### Help wanted with adding more Thrustmaster wheels Currently open requests for wheels: - + [T500 RS](https://github.com/Kimplul/hid-tmff2/issues/18) + [T818](https://github.com/Kimplul/hid-tmff2/issues/58) + [T-GT II](https://github.com/Kimplul/hid-tmff2/issues/55) -If you would like to help add a wheel to this driver, please have a look through the -[wiki](https://github.com/Kimplul/hid-tmff2/wiki#how-to-add-in-support-for-a-new-t-series-wheel) for what might need to be done. -If you have a wheel that's not on the list, but suspect it might fit into the driver, please feel free to open up an issue about it. - -## 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. - -With that in mind, +If you would like to help add a wheel to this driver, please have a look through the [wiki](https://github.com/Kimplul/hid-tmff2/wiki#how-to-add-in-support-for-a-new-t-series-wheel) and/or [CONTRIBUTING.md](./docs/CONTRIBUTING.md) for what might need to be done. If you have a wheel that's not on the list, but suspect it might fit into the driver, please feel free to open up an issue about it. ## Installation - ### Dependencies - Kernel modules require kernel headers to be installed. -+ Debian-based: `apt install linux-headers-$(uname -r)` -+ Arch-based: `pacman -S linux-headers` -+ Fedora-based: `yum install kernel-devel kernel-headers` ++ Debian-based: ``sudo apt install linux-headers-$(uname -r)`` ++ Arch-based: ``sudo pacman -S linux-headers`` ++ Fedora-based: ``sudo yum install kernel-devel kernel-headers`` +> **NOTE:** See [here](https://github.com/Kimplul/hid-tmff2/wiki/Integrating-driver-into-distros) for install instructions for other linux distributions. ### Manual installation - + Unplug wheel from computer -+ `git clone --recurse-submodules https://github.com/Kimplul/hid-tmff2.git` -+ `make` -+ `sudo make install` +```shell +git clone --recurse-submodules https://github.com/Kimplul/hid-tmff2.git +cd hid-tmff2/src +make +sudo make install +``` + Plug wheel back in -+ reboot (not strictly necessary, but definitely recommended) ++ Reboot (not strictly necessary, but definitely recommended) Done! -> Note: On some systems, you will get an error/warning about SSL. This is normal for unsigned modules. For info on signing modules yourself (completely optional), see [here](https://www.kernel.org/doc/html/latest/admin-guide/module-signing.html?highlight=module%20signing). +> **NOTE:** On some systems, you will get an error/warning about SSL. This is normal for unsigned modules. For info on signing modules yourself (completely optional), see [here](https://www.kernel.org/doc/html/latest/admin-guide/module-signing.html?highlight=module%20signing). -> Note: Thrustmaster TX wheels aren't supported by `hid-tminit` as of yet, meaning that TX wheels have to be initialized with `tmrd`. Please see https://github.com/Kimplul/hid-tmff2/issues/48. +> **NOTE:** Thrustmaster TX wheels aren't supported by `hid-tminit` as of yet, meaning that TX wheels have to be initialized with `tmrd`. Please see https://github.com/Kimplul/hid-tmff2/issues/48. ### DKMS - + Unplug wheel from computer -+ `sudo ./dkms-install.sh` +```shell +sudo bash ./dkms/dkms-install.sh +``` + Plug wheel back in + reboot (not strictly necessary, but definitely recommended) Done! + > :warning: Warning: There have been reports that this driver does not work if the wheel's firmware version is older than v. 31. > To update the firmware, you will have to fire up a Windows installation and update the firmware using the official Thrustmaster tools. @@ -69,13 +59,10 @@ Done! > and you may have to uninstall the older version of the driver. ## Additional tidbits - -+ Reportedly some games running under Wine/Proton won't recognize wheels without the official Thrustmaster drivers installed within the prefix. See [#46](https://github.com/Kimplul/hid-tmff2/issues/46#issuecomment-1199080845). For installation instructions, see [wiki](https://github.com/Kimplul/hid-tmff2/wiki) - - Note that you will still need the Linux driver, the Windows driver just installs some files needed by games to correctly recognize the Linux driver. The Windows driver itself does not work under Wine/Proton. ++ Reportedly some games running under Wine/Proton won't recognize wheels without the official Thrustmaster drivers installed within the prefix. See [#46](https://github.com/Kimplul/hid-tmff2/issues/46#issuecomment-1199080845). For installation instructions, see [wiki](https://github.com/Kimplul/hid-tmff2/wiki). Note that you will still need the Linux driver, the Windows driver just installs some files needed by games to correctly recognize the Linux driver. The Windows driver itself does not work under Wine/Proton. + Until the updated `hid-tminit` is upstreamed, you might want to blacklist the kernel module `hid-thrustmaster`. Do this with - ``` + ```shell echo 'blacklist hid_thrustmaster' > /etc/modprobe.d/hid_thrustmaster.con ``` diff --git a/TODO b/TODO deleted file mode 100644 index 70d3652..0000000 --- a/TODO +++ /dev/null @@ -1,7 +0,0 @@ -+ Make code more general to allow for stuff like T150/T248/T500 to be relatively -easily included. - -+ Use workqueues instead of doing everything in t300rs_timer - -+ T248 open/close seems to crash the wheel and the kernel along with it, check how Windows handles it -+ T248 fix rdesc, 0x0a somewhere needs to be set to 0x60 diff --git a/dkms-install.sh b/dkms-install.sh deleted file mode 100755 index 6e77e80..0000000 --- a/dkms-install.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -if ! [ $(id -u) = 0 ]; then - echo "You must run this script as root!" - exit 1 -fi - -TM_NAME=hid-tmff2 -TM_VER=0.8 -DST="/usr/src/${TM_NAME}-${TM_VER}" - -mkdir -p "${DST}" -cp -r ./* "${DST}" - -dkms add -m "${TM_NAME}" -v "${TM_VER}" -dkms build -m "${TM_NAME}" -v "${TM_VER}" -dkms install -m "${TM_NAME}" -v "${TM_VER}" - -exit $? diff --git a/dkms.conf b/dkms.conf deleted file mode 100644 index 25ef2ca..0000000 --- a/dkms.conf +++ /dev/null @@ -1,18 +0,0 @@ -PACKAGE_NAME="hid-tmff2" -PACKAGE_VERSION="0.8" -AUTOINSTALL="yes" - -# By default, DKMS will add KERNELRELEASE to the make command line; however, -# this will cause the kernel module build to infer that it was invoked via -# Kbuild directly instead of DKMS. The dkms(8) manual page recommends quoting -# the 'make' command name to suppress this behavior. -MAKE[0]="'make' KDIR=\"$kernel_source_dir\"" -CLEAN[0]="'make' KDIR=\"$kernel_source_dir\" clean" - -# The list of kernel modules. -#__DKMS_MODULES -BUILT_MODULE_NAME[0]="hid-tminit" -BUILT_MODULE_LOCATION="hid-tminit/" -DEST_MODULE_LOCATION[0]="/kernel/drivers/hid" -BUILT_MODULE_NAME[1]="hid-tmff-new" -DEST_MODULE_LOCATION[1]="/kernel/drivers/hid" diff --git a/dkms/dkms-install.sh b/dkms/dkms-install.sh new file mode 100755 index 0000000..f77eec4 --- /dev/null +++ b/dkms/dkms-install.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +if ! [ $(id -u) = 0 ]; then + echo "You must run this script as root!" + exit 1 +fi + +TM_NAME=hid-tmff2 +TM_VER=0.8 +DST="/usr/src/${TM_NAME}-${TM_VER}" + +mkdir -p "${DST}" +cp -r ../src/* "${DST}" + +dkms add -m "${TM_NAME}" -v "${TM_VER}" +dkms build -m "${TM_NAME}" -v "${TM_VER}" +dkms install -m "${TM_NAME}" -v "${TM_VER}" + +exit $? diff --git a/dkms/dkms.conf b/dkms/dkms.conf new file mode 100644 index 0000000..25ef2ca --- /dev/null +++ b/dkms/dkms.conf @@ -0,0 +1,18 @@ +PACKAGE_NAME="hid-tmff2" +PACKAGE_VERSION="0.8" +AUTOINSTALL="yes" + +# By default, DKMS will add KERNELRELEASE to the make command line; however, +# this will cause the kernel module build to infer that it was invoked via +# Kbuild directly instead of DKMS. The dkms(8) manual page recommends quoting +# the 'make' command name to suppress this behavior. +MAKE[0]="'make' KDIR=\"$kernel_source_dir\"" +CLEAN[0]="'make' KDIR=\"$kernel_source_dir\" clean" + +# The list of kernel modules. +#__DKMS_MODULES +BUILT_MODULE_NAME[0]="hid-tminit" +BUILT_MODULE_LOCATION="hid-tminit/" +DEST_MODULE_LOCATION[0]="/kernel/drivers/hid" +BUILT_MODULE_NAME[1]="hid-tmff-new" +DEST_MODULE_LOCATION[1]="/kernel/drivers/hid" diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md new file mode 100644 index 0000000..0e2c7b6 --- /dev/null +++ b/docs/CONTRIBUTING.md @@ -0,0 +1,41 @@ +# Contributing + +## Overview +This file contains general contributing information, but individual parts also may have more specific information to them, linked here: + - ``[FFBEffects-T300RS](./FFBEffects-T300RS.md)``: Force Feedback Effects example for T300RS + - ``[FFBEffects-T248](./FFBEffects-T248.md)``: Force Feedback Effects example for T300RS + - ``[To-Do](./TODO.md)``: TO-DO list for maintainers + - ``[Structure](https://github.com/Kimplul/hid-tmff2/wiki#structure-of-the-thrustmaster-device-stack)``: Structure of Thrustmaster device stack + +## How to add in support for a new T-series wheel? +Should probably not be too often that you need this info, but essentially use wireshark like in the previous example, but spin up a Windows virtual machine and install the Thrustmaster drivers on to it, and pass the device to the virtual machine. I prefer to use ``qemu`` with ``virt-manager`` as a frontend. With the wheel working under Windows, install [fedit.exe](https://gimx.fr/download/b882e209a0ac023d03abbf560dfc3f25fe6367ca/fedit.zip) and methodically go through all effects the device supports and compare the USB packets the driver sends out. You should be able to build up a table of what each value in the USB packet means, see [FFBEffects-T300RS.md](./FFBEffects-T300RS.md) for an example of what I found out about the T300. + +## How to capture what effects a game sends to the driver? +Use ffbwrap from ffbtools: [github:berarma/ffbtools](https://github.com/berarma/ffbtools) +The documentation gives good examples, but tl;dr; For steam, insert the following into a game's launch options: +```shell +$ ffbwrap --logger=/home/$USER/game.log /dev/input/by-id/usb-Thrustmaster_Thrustmaster_T300RS_Racing_wheel-event-joystick -- %command% +``` +This will create a file called ``game.log`` (with additional timestamp) in your home directory. Preferably change the name to suit the game, but you do you. + +> **NOTE:** Most fixes presented in the documentation are more or less obsolete by now, but the tool is still very useful for logging purposes. + +## How to capture what USB packets the driver sends to the device? +I'd recommend [wireshark](https://www.wireshark.org/) + +Usb capture setup is fairly straightforward: [wireshark/CaptureSetup](https://wiki.wireshark.org/CaptureSetup/USB#linux) + +Here's what I typically do when starting capturing: + +- Run ``sudo modprobe usbmon``. This will load a kernel module that allows Wireshark to read the USB packets. +- Open wireshark with root privileges. There are some ways to allow wireshark to access the packets with regular user privileges, I just haven't bothered with it. +- Select ``usbmon0`` from the view that opens up by default. +- The screen will quickly fill up with noise from other devices connected to the computer, so you will have to filter out the noise. + - Run ``lsusb`` in a terminal. Look for the T300 in the list, you should see something like ``Bus 001 Device 006: ID 044f:b66e ThrustMaster, Inc. Thrustmaster T300RS Racing wheel`` + - From the previous command, the Bus and Device fields can be used to filter out only packets from/to the device. To see all packets coming from the wheel, add in a filter ``usb.src ~ "1\.6\..*"``, where ``1`` is in this case from the ``Bus`` field and ``6`` from the ``Device`` field. To see all packets coming from the device, use ``usb.dst``. + - This data is probably also filled with a lot of cruft. Data to/from endpoint 2 is button state info, which is probably unnecessary. To see FFB data being sent to the device, use endpoint 1, i.e. ``usb.dst == "1.6.1"``. ``~`` is a Perl-compliant regex operator, whereas ``==`` just matches the string directly. To see both data coming from the device and going to it, use ``usb.dst == "..." || usb.src == "..."``. + - There is also endpoint 0, ``usb.src == "1.6.0"`` but it doesn't seem to be used for much. +- Use the three buttons in the top left of the screen to start, stop and restart captures. +- Do whatever you want with the device, packets should automatically be captured. When you want to save your capture to a file, stop the recording and go to ``File > Export Specified Packets`` and make sure ``Displayed`` is selected. This will apply the filter you've been using, and will only include the packets that are visible in Wireshark, i.e. it applies the filter you've specified. + +> **NOTE:** Every time you unplug and replug your wheel, its ``Device`` field will probably change. diff --git a/docs/FFBEffects-T248.md b/docs/FFBEffects-T248.md new file mode 100644 index 0000000..dfd5971 --- /dev/null +++ b/docs/FFBEffects-T248.md @@ -0,0 +1,7 @@ +# Force Feedback Effects - Thrustmaster T248 + +## Open: +Identical to T300RS + +## Close: +Identical to T300RS diff --git a/docs/FFBEffects-T300RS.md b/docs/FFBEffects-T300RS.md new file mode 100644 index 0000000..1770bdd --- /dev/null +++ b/docs/FFBEffects-T300RS.md @@ -0,0 +1,542 @@ +# Force Feedback Effects - Thrustmaster T300RS (incl. PS3/PS4 modes) +Here I will gather all info I can find out about the different force feedback effects and their USB commands. All values seem to be little-endian. Also, general note, it seems like some effects are affected by the direction of the force, denoted by ``***``. As far as I can tell, ``fade_level`` and ``attack_level`` values are calculated by mapping the ``u16`` into ``s16`` and taking the absolute value, and after that multiplying by the direction. + +> **NOTE:** All values are examples of actual commands that I captured on the USB interface, not the only available ones. + + +## GENERAL +### Playing +``` + 60 00 - standard header + 01 - ID + 89 - playing options + 01 - play + 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +``` + +### Stopping/removing (why are they rolled into one?) +``` + 60 00 - standard header + 01 - ID + 89 - playing options + 00 - stop + 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +``` + +## Open: +``` + 60 01 - standard header? + 04 - ? + 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + + (this command doesn't appear every time, odd) + 60 12 - ? + bf 04 00 00 03 b7 1e - ? /* wow, this is different on different wheels? */ + 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + + 60 01 - ? + 05 - ? + 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +``` + +## Close: +``` + 60 01 - standard header? + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +``` + +## Rotation angle: +``` + 60 08 - header plus settings? + 11 - set rotation angle + 55 d5 - angle (between ff ff and 7b 09, one degree is roughly 3c) + 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +``` + +## FF_AUTOCENTER: +``` + 60 08 - header plus settings? + 03 - set autocenter force + 8f 02 - force (between ff ff and 00 00) + 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +``` + +## FF_GAIN: +``` + 60 02 - header plus gain? + bf - gain (between ff and 00) + 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +``` + +## FF_CONSTANT +### Initialization +``` + 60 00 - standard header + 01 - ID + 6a - new constant effect(?) + fe ff - strength *** + 00 00 - attack_length + 00 00 - attack_level *** + 00 00 - fade_length + 00 00 - fade_level *** + 00 4f - ? + f7 17 - time in milliseconds, unsigned + 00 00 + 07 00 - offset from start(?) + 00 ff ff - end of new effect(?) + 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +``` + +### Modifying (For dynamic updating of effects) + +#### Direction: ``N/A`` + +#### Constant force: +``` +60 00 - standard header +01 - ID +0a - modify constant force +05 16 - constant force *** +00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +``` + +#### Envelope: +``` +60 00 - standard header +01 - ID + 31 - modify envelope +84 - ID of envelope attribute ( attack_level 82, + attack_length 81, + fade_level 88, + fade_length 84 ) + 63 04 - value attribute should be set to ?***? + 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +``` + +#### Duration: +``` + 60 00 - standard header + 01 - ID + 49 00 41 - modify timing? + 6c 20 - length in milliseconds + 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +``` + +#### Offset: ``N/A ?`` + +## FF_RAMP +(Why on earth is it this difficult to figure out how a ramp works?) +### Init: +``` + 60 00 - standard header + 01 - ID + 6b - new ramp effect + f6 7f - difference *** + fe ff - level? *** (level = (if end_level > start_level, end_level, else + start_level) - difference?) (signed) + 00 00 + f7 17 - time + 00 80 - some kind of marker + 00 00 - attack_length + f6 7f - attack_level *** + 00 00 - fade_length + f6 7f - fade_level*** + 04 - invert (going "down", 05, going "up", 04) + 4f + f7 17 - time again? + 00 00 + 00 00 - offset + 00 ff ff - end of init + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +``` +### Modifying: +(Sweet baby jesus. I'll figure this out later ``Ramp(?)``) +``` + 60 00 - standard header + 01 - ID + 0e - ??? + 03 - ramp? + e3 04 - difference? + 7d 75 - "level"? + 05 - ???? + 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +``` +#### Envelope: +``` + 60 00 - standard header + 01 - ID + 31 - modify envelope + 84 - ID of envelope attribute ( attack_level 82, + attack_length 81, + fade_level 88, + fade_length 84 ) + 63 04 - value attribute should be set to + 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +``` +#### Duration: +``` + 60 00 + 01 - ID + 4e - ? + 08 - ? + 01 00 - time in milliseconds + 05 - ? + 41 - ? + 01 00 - time in milliseconds + 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +``` +#### Offset: ``N/A ?`` + +## FF_SPRING: +### Init: +``` + 60 00 - standard header + 01 - ID + 64 - new conditional effect + fc 7f - positive coefficient (right?) + fc 7f - negative coefficient (left?) + fe ff - deadband (left?) (deadband + offset) (fe ff means no deadband) + fe ff - deadband (right?) (deadband + offset) + a6 6a a6 6a fe ff fe ff fe ff fe ff df 58 a6 6a 06 - some weird hard-coded + values to do with springs? + + 4f + f7 17 - duration + 00 00 + 00 00 - offset + 00 ff ff - end of init + 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +``` + +### Modifying: +Same as below (FF_DAMPER + FF_FRICTION + FF_INERTIA) + +## FF_DAMPER + FF_FRICTION + FF_INERTIA: +### Init: +``` + 60 00 - standard header + 02 -ID + 64 - new conditional effect + fc 7f - positive coefficient (right?) + fc 7f - negative coefficient (left?) + fe ff - deadband (left?) (deadband + offset) (fe ff means no deadband) + fe ff - deadband (right?) (deadband + offset) + fc 7f fc 7f fe ff fe ff fe ff fe ff fc 7f fc 7f 07 - some weird hard-coded + values to do with friction? + 4f + f7 17 - duration + 00 00 + 00 00 - offset + 00 ff ff - end of init + 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +``` +### Modifying: +``` + positive coefficient: + 60 00 + 02 - ID + 0e 41 + 64 35 - value, signed (between 00 00 and 01 80?) + 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + + negative coefficient: + 60 00 + 02 - ID + 0e 42 + 64 35 - value, signed (between 00 00 and ff 7f?) + 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + + deadband/offset: + 60 00 + 02 - ID + 0e 4c + 64 35 - deadband (right?) (deadband + offset) (fe ff means no deadband) + 00 00 - deadband (left?) (deadband + offset) + 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +``` + +### Duration: +``` + 60 00 - standard header + 01 - ID + 49 06 41 - modify timing? + 6c 20 - length in milliseconds + 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +``` + +### Offset: ``N/A ?`` + +## FF_PERIODIC: +### Init +``` + 60 00 - standard header + 01 - ID + 6b - new periodic effect + 00 00 - magnitude *** + fe ff - offset (up/down) + 00 00 - phase (left/right) + e8 03 - period + 00 80 + 00 00 - attack_length + 00 00 - attack_level *** + 00 00 - fade_length + 00 00 - fade_level *** + 01 - type of periodic ( square 01, + sine 03, + triangle 02, + sawtooth down 05, + sawtooth up 04 ) + 4f + f7 17 - duration + 00 00 + 00 00 - offset + 00 ff ff - end of init + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +``` + +### Modifying: +``` + magnitude: + 60 00 + 02 - ID + 0e 01 + 64 35 - value, signed (between 00 00 and ff 7f?) + 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + + offset of effect: + 60 00 + 02 - ID + 0e 02 + 64 35 - value, signed (between 00 00 and ff 7f?) + 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + + phase: + 60 00 + 02 - ID + 0e 04 + 64 35 - value, signed (between 80 01 and ff 7f?) + 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + + period: + 60 00 + 02 - ID + 0e 08 + 64 35 - value, signed (between 00 00 and I guess ff 7f?) (max value in + wireshark is 0d 07, but I don't know if that's a driver or program issue) + 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +``` + +### Envelope: +``` + 60 00 - standard header + 01 - ID + 31 - modify envelope + 84 - ID of envelope attribute ( attack_level 82, + attack_length 81, + fade_level 88, + fade_length 84 ) + 63 04 - value attribute should be set to + 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +``` + +### Duration: +``` + Square: + 60 00 + 01 - ID + 49 - ? + 01 - Effect type + 41 - ? + 4e 0c - Time in milliseconds + 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + + Sine: + 60 00 + 01 - ID + 49 - ? + 03 - Effect type + 41 - ? + ec 13 - Time in milliseconds + 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + + Triangle: + 60 00 + 01 - ID + 49 - ? + 02 - Effect type + 41 - ? + 4e 0c - Time in milliseconds + 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + + Sawtooth up: + 60 00 + 01 - ID + 49 - ? + 04 - Effect type + 41 - ? + 4e 0c - Time in milliseconds + 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + + Sawtooth down: + 60 00 + 01 - ID + 49 - ? + 05 - Effect type + 41 - ? + ec 13 - Time in milliseconds + 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +``` + +### Offset: ``N/A ?`` + +## PS4 Input +``` +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 226 /* wheel */ +ff00.0021 = 125 /* wheel */ +ff00.0021 = 255 /* gas */ +ff00.0021 = 255 /* gas */ +ff00.0021 = 255 /* brake */ +ff00.0021 = 255 /* brake */ +ff00.0021 = 255 /* clutch */ +ff00.0021 = 255 /* clutch */ +ff00.0021 = 0 +ff00.0021 = 255 +ff00.0021 = 255 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +ff00.0021 = 0 +``` diff --git a/docs/TODO.md b/docs/TODO.md new file mode 100644 index 0000000..bd73609 --- /dev/null +++ b/docs/TODO.md @@ -0,0 +1,10 @@ +# TO-DO list + +- Make code more general to allow for stuff like T150/T248/T500 to be relatively +easily included. + +- Use workqueues instead of doing everything in t300rs_timer + +### T248 + - open/close seems to crash the wheel and the kernel along with it, check how Windows handles it + - fix rdesc, 0x0a somewhere needs to be set to 0x60 diff --git a/force-effects.txt b/force-effects.txt deleted file mode 100644 index 0595611..0000000 --- a/force-effects.txt +++ /dev/null @@ -1,462 +0,0 @@ -Here I will gather all info I can find out about the different force effects and -their USB commands. All values seem to be little-endian. -Also, general note, it seems like some effects are affected by the direction of -the force, denoted by *** - -As far as I can tell, fade_level and attack_level values are calculated by -mapping the u16 into s16 and taking the absolute value, and after that -multiplying by the direction. - -All values are examples of actual commands that I captured on the USB interface, -not the only ones available. - -GENERAL: - - Playing: - 60 00 - standard header - 01 - ID - 89 - playing options - 01 - play - 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - - Stopping/removing: (why are they rolled into one?) - 60 00 - standard header - 01 - ID - 89 - playing options - 00 - stop - 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - -FF_CONSTANT: - Initialization: - 60 00 - standard header - 01 - ID - 6a - new constant effect(?) - fe ff - strength *** - 00 00 - attack_length - 00 00 - attack_level *** - 00 00 - fade_length - 00 00 - fade_level *** - 00 4f - ? - f7 17 - time in milliseconds, unsigned - 00 00 - 07 00 - offset from start(?) - 00 ff ff - end of new effect(?) - 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - - Modifying: (For dynamic updating of effects) - Direction: n/a - - Constant force: - 60 00 - standard header - 01 - ID - 0a - modify constant force - 05 16 - constant force *** - 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - Envelope: - 60 00 - standard header - 01 - ID - 31 - modify envelope - 84 - ID of envelope attribute ( attack_level 82, - attack_length 81, - fade_level 88, - fade_length 84 ) - 63 04 - value attribute should be set to ?***? - 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - Duration: - 60 00 - standard header - 01 - ID - 49 00 41 - modify timing? - 6c 20 - length in milliseconds - 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - Offset: - Not supported? - - -FF_RAMP: - (Why on earth is it this difficult to figure out how a fucking ramp works?) - Init: - 60 00 - standard header - 01 - ID - 6b - new ramp effect - f6 7f - difference *** - fe ff - level? *** (level = (if end_level > start_level, end_level, else - start_level) - difference?) (signed) - 00 00 - f7 17 - time - 00 80 - some kind of marker - 00 00 - attack_length - f6 7f - attack_level *** - 00 00 - fade_length - f6 7f - fade_level*** - 04 - invert (going "down", 05, going "up", 04) - 4f - f7 17 - time again? - 00 00 - 00 00 - offset - 00 ff ff - end of init - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - Modifying: - Sweet baby jesus - I'll figure this out later - Ramp(?): - 60 00 - standard header - 01 - ID - 0e - ??? - 03 - ramp? - e3 04 - difference? - 7d 75 - "level"? - 05 - ???? - 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - - Envelope: - 60 00 - standard header - 01 - ID - 31 - modify envelope - 84 - ID of envelope attribute ( attack_level 82, - attack_length 81, - fade_level 88, - fade_length 84 ) - 63 04 - value attribute should be set to - 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - Duration: - - 60 00 - 01 - ID - 4e - ? - 08 - ? - 01 00 - time in milliseconds - 05 - ? - 41 - ? - 01 00 - time in milliseconds - 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - Offset: - Not supported? - -FF_SPRING: - Init: - 60 00 - standard header - 01 - ID - 64 - new conditional effect - fc 7f - positive coefficient (right?) - fc 7f - negative coefficient (left?) - fe ff - deadband (left?) (deadband + offset) (fe ff means no deadband) - fe ff - deadband (right?) (deadband + offset) - a6 6a a6 6a fe ff fe ff fe ff fe ff df 58 a6 6a 06 - some weird hard-coded - values to do with springs? - - 4f - f7 17 - duration - 00 00 - 00 00 - offset - 00 ff ff - end of init - 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - Modifying: - Same as under - -FF_DAMPER + FF_FRICTION + FF_INERTIA: - Init: - 60 00 - standard header - 02 -ID - 64 - new conditional effect - fc 7f - positive coefficient (right?) - fc 7f - negative coefficient (left?) - fe ff - deadband (left?) (deadband + offset) (fe ff means no deadband) - fe ff - deadband (right?) (deadband + offset) - fc 7f fc 7f fe ff fe ff fe ff fe ff fc 7f fc 7f 07 - some weird hard-coded - values to do with friction? - 4f - f7 17 - duration - 00 00 - 00 00 - offset - 00 ff ff - end of init - 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - Modifying: - positive coefficient: - 60 00 - 02 - ID - 0e 41 - 64 35 - value, signed (between 00 00 and 01 80?) - 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - negative coefficient: - 60 00 - 02 - ID - 0e 42 - 64 35 - value, signed (between 00 00 and ff 7f?) - 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - deadband/offset: - 60 00 - 02 - ID - 0e 4c - 64 35 - deadband (right?) (deadband + offset) (fe ff means no deadband) - 00 00 - deadband (left?) (deadband + offset) - 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - Duration: - 60 00 - standard header - 01 - ID - 49 06 41 - modify timing? - 6c 20 - length in milliseconds - 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - Offset: - Not supported? - -FF_PERIODIC: - 60 00 - standard header - 01 - ID - 6b - new periodic effect - 00 00 - magnitude *** - fe ff - offset (up/down) - 00 00 - phase (left/right) - e8 03 - period - 00 80 - 00 00 - attack_length - 00 00 - attack_level *** - 00 00 - fade_length - 00 00 - fade_level *** - 01 - type of periodic ( square 01, - sine 03, - triangle 02, - sawtooth down 05, - sawtooth up 04 ) - 4f - f7 17 - duration - 00 00 - 00 00 - offset - 00 ff ff - end of init - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - Modifying: - magnitude: - 60 00 - 02 - ID - 0e 01 - 64 35 - value, signed (between 00 00 and ff 7f?) - 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - offset of effect: - 60 00 - 02 - ID - 0e 02 - 64 35 - value, signed (between 00 00 and ff 7f?) - 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - phase: - 60 00 - 02 - ID - 0e 04 - 64 35 - value, signed (between 80 01 and ff 7f?) - 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - period: - 60 00 - 02 - ID - 0e 08 - 64 35 - value, signed (between 00 00 and I guess ff 7f?) (max value in - wireshark is 0d 07, but I don't know if that's a driver or program issue) - 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - - Envelope: - 60 00 - standard header - 01 - ID - 31 - modify envelope - 84 - ID of envelope attribute ( attack_level 82, - attack_length 81, - fade_level 88, - fade_length 84 ) - 63 04 - value attribute should be set to - 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - Duration: - - Square: - 60 00 - 01 - ID - 49 - ? - 01 - Effect type - 41 - ? - 4e 0c - Time in milliseconds - 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - Sine: - 60 00 - 01 - ID - 49 - ? - 03 - Effect type - 41 - ? - ec 13 - Time in milliseconds - 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - Triangle: - 60 00 - 01 - ID - 49 - ? - 02 - Effect type - 41 - ? - 4e 0c - Time in milliseconds - 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - Sawtooth up: - 60 00 - 01 - ID - 49 - ? - 04 - Effect type - 41 - ? - 4e 0c - Time in milliseconds - 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - Sawtooth down: - 60 00 - 01 - ID - 49 - ? - 05 - Effect type - 41 - ? - ec 13 - Time in milliseconds - 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - Offset: - Not supported? - - -FF_AUTOCENTER: - 60 08 - header plus settings? - 03 - set autocenter force - 8f 02 - force (between ff ff and 00 00) - 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - -FF_GAIN: - 60 02 - header plus gain? - bf - gain (between ff and 00) - 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - -Rotation angle: - 60 08 - header plus settings? - 11 - set rotation angle - 55 d5 - angle (between ff ff and 7b 09, one degree is roughly 3c) - 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - -Open: - 60 01 - standard header? - 04 - ? - 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - (this command doesn't appear every time, odd) - 60 12 - ? - bf 04 00 00 03 b7 1e - ? /* wow, this is different on different wheels? */ - 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - - 60 01 - ? - 05 - ? - 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - -Close: - 60 01 - standard header? - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - diff --git a/hid-tmff2.c b/hid-tmff2.c deleted file mode 100644 index 44df1e3..0000000 --- a/hid-tmff2.c +++ /dev/null @@ -1,749 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#include -#include -#include -#include "hid-tmff2.h" - - -int open_mode = 1; -module_param(open_mode, int, 0660); -MODULE_PARM_DESC(open_mode, - "Whether to send mode change commands on open/close"); - -int timer_msecs = DEFAULT_TIMER_PERIOD; -module_param(timer_msecs, int, 0660); -MODULE_PARM_DESC(timer_msecs, - "Timer resolution in msecs"); - -/* should these be removed and just rely on /sys? */ -int spring_level = 30; -module_param(spring_level, int, 0); -MODULE_PARM_DESC(spring_level, - "Level of spring force (0-100), as per Oversteer standards"); - -int damper_level = 30; -module_param(damper_level, int, 0); -MODULE_PARM_DESC(damper_level, - "Level of damper force (0-100), as per Oversteer standards"); - -int friction_level = 30; -module_param(friction_level, int, 0); -MODULE_PARM_DESC(friction_level, - "Level of friction force (0-100), as per Oversteer standards"); - -int range = 900; -module_param(range, int, 0); -MODULE_PARM_DESC(range, - "Range of wheel, depends on the wheel. Invalid values are ignored"); - -int alt_mode = 0; -module_param(alt_mode, int, 0); -MODULE_PARM_DESC(alt_mode, - "Alternate mode, eg. F1 mode"); - -#define GAIN_MAX 65535 -int gain = 40000; -module_param(gain, int, 0); -MODULE_PARM_DESC(gain, - "Level of gain (0-65535)"); - -static spinlock_t lock; -static unsigned long lock_flags; - -static struct tmff2_device_entry *tmff2_from_hdev(struct hid_device *hdev) -{ - struct tmff2_device_entry *tmff2; - spin_lock_irqsave(&lock, lock_flags); - - if (!(tmff2 = hid_get_drvdata(hdev))) - dev_err(&hdev->dev, "hdev private data not found\n"); - - spin_unlock_irqrestore(&lock, lock_flags); - - return tmff2; -} - -static struct tmff2_device_entry *tmff2_from_input(struct input_dev *input_dev) -{ - struct hid_device *hdev; - spin_lock_irqsave(&lock, lock_flags); - - if (!(hdev = input_get_drvdata(input_dev))) - dev_err(&input_dev->dev, "input_dev private data not found\n"); - - spin_unlock_irqrestore(&lock, lock_flags); - - return tmff2_from_hdev(hdev); -} - -static ssize_t spring_level_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - unsigned int value; - int ret; - - ret = kstrtouint(buf, 0, &value); - if (ret) { - dev_err(dev, "kstrtouint failed at spring_level_store: %i", ret); - return ret; - } - - if (value > 100) { - dev_info(dev, "value %i larger than max 100, clamping to 100.\n", value); - value = 100; - } - - spring_level = value; - - return count; -} - -static ssize_t spring_level_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - - return scnprintf(buf, PAGE_SIZE, "%u\n", spring_level); -} -static DEVICE_ATTR_RW(spring_level); - -static ssize_t damper_level_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - unsigned int value; - int ret; - - - ret = kstrtouint(buf, 0, &value); - if (ret) { - dev_err(dev, "kstrtouint failed at damper_level_store: %i", ret); - return ret; - } - - if (value > 100) { - dev_info(dev, "value %i larger than max 100, clamping to 100.\n", value); - value = 100; - } - - damper_level = value; - - return count; -} - -static ssize_t damper_level_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - - return scnprintf(buf, PAGE_SIZE, "%u\n", damper_level); -} -static DEVICE_ATTR_RW(damper_level); - -static ssize_t friction_level_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - unsigned int value; - int ret; - - - ret = kstrtouint(buf, 0, &value); - if (ret) { - dev_err(dev, "kstrtouint failed at friction_level_store: %i", ret); - return ret; - } - - if (value > 100) { - dev_info(dev, "value %i larger than max 100, clamping to 100.\n", value); - value = 100; - } - - friction_level = value; - - return count; -} - -static ssize_t friction_level_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - size_t count; - - - count = scnprintf(buf, PAGE_SIZE, "%u\n", friction_level); - - return count; -} -static DEVICE_ATTR_RW(friction_level); - -static ssize_t range_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))) { - hid_err(tmff2->hdev, "kstrtouint failed at range_store: %i", ret); - return ret; - } - - if (tmff2->set_range) { - if ((ret = tmff2->set_range(tmff2->data, value))) - return ret; - } - - return count; -} - -static ssize_t range_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - - return scnprintf(buf, PAGE_SIZE, "%u\n", range); -} -static DEVICE_ATTR_RW(range); - -static ssize_t alternate_modes_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)); - - if (!tmff2) - return -ENODEV; - - if (tmff2->alt_mode_store) - return tmff2->alt_mode_store(tmff2->data, buf, count); - - return 0; -} - -static ssize_t alternate_modes_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->alt_mode_show) - return tmff2->alt_mode_show(tmff2->data, buf); - - return 0; -} -static DEVICE_ATTR_RW(alternate_modes); - -static ssize_t gain_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 gain_store: %i", ret); - return ret; - } - - gain = value; - if (tmff2->set_gain) /* if we can, update gain immediately */ - tmff2->set_gain(tmff2->data, (GAIN_MAX * gain) / GAIN_MAX); - - return count; -} - -static ssize_t gain_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return scnprintf(buf, PAGE_SIZE, "%i\n", gain); -} -static DEVICE_ATTR_RW(gain); - -static void tmff2_set_gain(struct input_dev *dev, uint16_t value) -{ - struct tmff2_device_entry *tmff2 = tmff2_from_input(dev); - - if (!tmff2) - return; - - if (!tmff2->set_gain) { - hid_err(tmff2->hdev, "missing set_gain\n"); - return; - } - - if (tmff2->set_gain(tmff2->data, (value * gain) / GAIN_MAX)) - hid_warn(tmff2->hdev, "unable to set gain\n"); -} - -static void tmff2_set_autocenter(struct input_dev *dev, uint16_t value) -{ - struct tmff2_device_entry *tmff2 = tmff2_from_input(dev); - - if (!tmff2) - return; - - if (!tmff2->set_autocenter) { - hid_err(tmff2->hdev, "missing set_autocenter\n"); - return; - } - - if (tmff2->set_autocenter(tmff2->data, value)) - hid_warn(tmff2->hdev, "unable to set autocenter\n"); -} - -static void tmff2_work_handler(struct work_struct *w) -{ - struct delayed_work *dw = container_of(w, struct delayed_work, work); - struct tmff2_device_entry *tmff2 = container_of(dw, struct tmff2_device_entry, work); - struct tmff2_effect_state *state; - int max_count = 0, effect_id; - unsigned long time_now; - __u16 effect_length; - - - if (!tmff2) - return; - - for (effect_id = 0; effect_id < tmff2->max_effects; ++effect_id) { - spin_lock(&tmff2->lock); - - time_now = JIFFIES2MS(jiffies); - state = &tmff2->states[effect_id]; - - effect_length = state->effect.replay.length; - if (test_bit(FF_EFFECT_PLAYING, &state->flags) && effect_length) { - if ((time_now - state->start_time) >= effect_length) { - __clear_bit(FF_EFFECT_PLAYING, &state->flags); - __clear_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags); - - if (state->count) - state->count--; - - if (state->count) - __set_bit(FF_EFFECT_QUEUE_START, &state->flags); - } - } - - if (test_bit(FF_EFFECT_QUEUE_UPLOAD, &state->flags)) { - if (tmff2->upload_effect(tmff2->data, state)) { - hid_warn(tmff2->hdev, "failed uploading effect\n"); - } else { - __clear_bit(FF_EFFECT_QUEUE_UPLOAD, &state->flags); - /* if we're uploading an effect, it's bound to be the up - * to date available */ - __clear_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags); - } - } - - if (test_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags)) { - if (tmff2->update_effect(tmff2->data, state)) - hid_warn(tmff2->hdev, "failed updating effect\n"); - else - __clear_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags); - } - - if (test_bit(FF_EFFECT_QUEUE_START, &state->flags)) { - if (tmff2->play_effect(tmff2->data, state)) { - hid_warn(tmff2->hdev, "failed starting effect\n"); - } else { - __clear_bit(FF_EFFECT_QUEUE_START, &state->flags); - __set_bit(FF_EFFECT_PLAYING, &state->flags); - } - - } - - if (test_bit(FF_EFFECT_QUEUE_STOP, &state->flags)) { - if (tmff2->stop_effect(tmff2->data, state)) { - hid_warn(tmff2->hdev, "failed stopping effect\n"); - } else { - __clear_bit(FF_EFFECT_PLAYING, &state->flags); - __clear_bit(FF_EFFECT_QUEUE_STOP, &state->flags); - } - } - - if (state->count > max_count) - max_count = state->count; - - spin_unlock(&tmff2->lock); - } - - if (max_count && tmff2->allow_scheduling) - schedule_delayed_work(&tmff2->work, msecs_to_jiffies(timer_msecs)); -} - -static int tmff2_upload(struct input_dev *dev, - struct ff_effect *effect, struct ff_effect *old) -{ - struct tmff2_effect_state *state; - struct tmff2_device_entry *tmff2 = tmff2_from_input(dev); - - if (!tmff2) - return -ENODEV; - - if (effect->type == FF_PERIODIC && effect->u.periodic.period == 0) - return -EINVAL; - - state = &tmff2->states[effect->id]; - - spin_lock(&tmff2->lock); - - state->effect = *effect; - - if (old) { - if (!test_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags)) - state->old = *old; - - __set_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags); - } else { - __set_bit(FF_EFFECT_QUEUE_UPLOAD, &state->flags); - } - - spin_unlock(&tmff2->lock); - return 0; -} - -static int tmff2_play(struct input_dev *dev, int effect_id, int value) -{ - struct tmff2_effect_state *state; - struct tmff2_device_entry *tmff2 = tmff2_from_input(dev); - - if (!tmff2) - return -ENODEV; - - state = &tmff2->states[effect_id]; - if (!state) - return 0; - - spin_lock(&tmff2->lock); - if (value > 0) { - state->count = value; - state->start_time = JIFFIES2MS(jiffies); - __set_bit(FF_EFFECT_QUEUE_START, &state->flags); - __clear_bit(FF_EFFECT_QUEUE_STOP, &state->flags); - } else { - __set_bit(FF_EFFECT_QUEUE_STOP, &state->flags); - __clear_bit(FF_EFFECT_QUEUE_START, &state->flags); - } - - spin_unlock(&tmff2->lock); - - if (!delayed_work_pending(&tmff2->work) && tmff2->allow_scheduling) - schedule_delayed_work(&tmff2->work, 0); - - return 0; -} - -static int tmff2_open(struct input_dev *dev) -{ - struct tmff2_device_entry *tmff2 = tmff2_from_input(dev); - - if (!tmff2) - return -ENODEV; - - if (tmff2->open) - return tmff2->open(tmff2->data, open_mode); - - hid_err(tmff2->hdev, "no open callback set\n"); - return -EINVAL; -} - -static void tmff2_close(struct input_dev *dev) -{ - struct tmff2_device_entry *tmff2 = tmff2_from_input(dev); - - if (!tmff2) - return; - - /* since we're closing the device, no need to continue feeding it new data */ - cancel_delayed_work_sync(&tmff2->work); - - if (tmff2->close) { - tmff2->close(tmff2->data, open_mode); - return; - } - - hid_err(tmff2->hdev, "no close callback set\n"); -} - -static int tmff2_create_files(struct tmff2_device_entry *tmff2) -{ - struct device *dev = &tmff2->hdev->dev; - int ret; - - - /* could use short circuiting but this is more explicit */ - if (tmff2->params & PARAM_GAIN) { - if ((ret = device_create_file(dev, &dev_attr_gain))) { - hid_err(tmff2->hdev, "unable to create sysfs for gain\n"); - goto gain_err; - } - } - - if (tmff2->params & PARAM_ALT_MODE) { - if ((ret = device_create_file(dev, &dev_attr_alternate_modes))) { - hid_err(tmff2->hdev, "unable to create sysfs for alternate_modes\n"); - goto alt_err; - } - } - - if (tmff2->params & PARAM_RANGE) { - if ((ret = device_create_file(dev, &dev_attr_range))) { - hid_warn(tmff2->hdev, "unable to create sysfs for range\n"); - goto range_err; - } - } - - if (tmff2->params & PARAM_SPRING_LEVEL) { - if ((ret = device_create_file(dev, &dev_attr_spring_level))) { - hid_warn(tmff2->hdev, "unable to create sysfs for spring_level\n"); - goto spring_err; - } - } - - if (tmff2->params & PARAM_DAMPER_LEVEL) { - if ((ret = device_create_file(dev, &dev_attr_damper_level))) { - hid_warn(tmff2->hdev, "unable to create sysfs for damper_level\n"); - goto damper_err; - } - } - - if (tmff2->params & PARAM_FRICTION_LEVEL) { - if ((ret = device_create_file(dev, &dev_attr_friction_level))) { - hid_warn(tmff2->hdev, "unable to create sysfs for friction_level\n"); - goto friction_err; - } - } - - return 0; - -friction_err: - device_remove_file(dev, &dev_attr_damper_level); -damper_err: - device_remove_file(dev, &dev_attr_spring_level); -spring_err: - device_remove_file(dev, &dev_attr_range); -range_err: - device_remove_file(dev, &dev_attr_alternate_modes); -alt_err: - device_remove_file(dev, &dev_attr_gain); -gain_err: - return ret; -} - -static int tmff2_wheel_init(struct tmff2_device_entry *tmff2) -{ - int ret, i; - struct ff_device *ff; - - spin_lock_init(&lock); - spin_lock_init(&tmff2->lock); - INIT_DELAYED_WORK(&tmff2->work, tmff2_work_handler); - - /* get parameters etc from backend */ - if ((ret = tmff2->wheel_init(tmff2, open_mode))) - goto err; - - - tmff2->states = kzalloc(sizeof(struct tmff2_effect_state) * tmff2->max_effects, - GFP_KERNEL); - - if (!tmff2->states) { - ret = -ENOMEM; - goto err; - } - - /* set supported effects into input_dev->ffbit */ - for (i = 0; tmff2->supported_effects[i] >= 0; ++i) - __set_bit(tmff2->supported_effects[i], tmff2->input_dev->ffbit); - - /* create actual ff device*/ - if ((ret = input_ff_create(tmff2->input_dev, tmff2->max_effects))) { - hid_err(tmff2->hdev, "could not create input_ff\n"); - goto err; - } - - /* set ff callbacks */ - ff = tmff2->input_dev->ff; - ff->upload = tmff2_upload; - ff->playback = tmff2_play; - - if (tmff2->open) - tmff2->input_dev->open = tmff2_open; - - if (tmff2->close) - tmff2->input_dev->close = tmff2_close; - - /* set defaults wherever possible */ - if (tmff2->set_gain) { - ff->set_gain = tmff2_set_gain; - tmff2->set_gain(tmff2->data, (GAIN_MAX * gain) / GAIN_MAX); - } - - if (tmff2->set_autocenter) - ff->set_autocenter = tmff2_set_autocenter; - - if (tmff2->set_range) - tmff2->set_range(tmff2->data, range); - - if (tmff2->switch_mode) - tmff2->switch_mode(tmff2->data, alt_mode); - - /* create files */ - if ((ret = tmff2_create_files(tmff2))) - goto err; - - tmff2->allow_scheduling = 1; - return 0; - - input_ff_destroy(tmff2->input_dev); -err: - return ret; -} - -static int tmff2_probe(struct hid_device *hdev, const struct hid_device_id *id) -{ - struct tmff2_device_entry *tmff2 = - kzalloc(sizeof(struct tmff2_device_entry), GFP_KERNEL); - - int ret; - - - if (!tmff2) { - ret = -ENOMEM; - goto oom_err; - } - - tmff2->hdev = hdev; - hid_set_drvdata(tmff2->hdev, tmff2); - - switch (tmff2->hdev->product) { - /* t300rs */ - case TMT300RS_PS3_NORM_ID: - case TMT300RS_PS3_ADV_ID: - case TMT300RS_PS4_NORM_ID: - if ((ret = t300rs_populate_api(tmff2))) - goto wheel_err; - break; - - case TMT248_PC_ID: - if ((ret = t248_populate_api(tmff2))) - goto wheel_err; - break; - - case TX_ACTIVE: - if ((ret = tx_populate_api(tmff2))) - goto wheel_err; - break; - - default: - ret = -ENODEV; - goto wheel_err; - } - - if ((ret = hid_parse(tmff2->hdev))) { - hid_err(hdev, "parse failed\n"); - goto hid_err; - } - - if ((ret = hid_hw_start(tmff2->hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF))) { - hid_err(hdev, "hw start failed\n"); - goto hid_err; - } - - tmff2->input_dev = list_entry(hdev->inputs.next, struct hid_input, list)->input; - - if ((ret = tmff2_wheel_init(tmff2))) { - hid_err(hdev, "init failed\n"); - goto init_err; - } - - return 0; - -init_err: - hid_hw_stop(hdev); -hid_err: - tmff2->wheel_destroy(tmff2->data); -wheel_err: - kfree(tmff2); -oom_err: - return ret; -} - -static __u8 *tmff2_report_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) -{ - struct tmff2_device_entry *tmff2 = tmff2_from_hdev(hdev); - - if (!tmff2) /* not entirely sure what the best course of action would be here */ - return rdesc; - - if (tmff2->wheel_fixup) - return tmff2->wheel_fixup(hdev, rdesc, rsize); - - return rdesc; -} - -static void tmff2_remove(struct hid_device *hdev) -{ - struct tmff2_device_entry *tmff2 = tmff2_from_hdev(hdev); - struct device *dev; - - if (!tmff2) - return; - - tmff2->allow_scheduling = 0; - cancel_delayed_work_sync(&tmff2->work); - - dev = &tmff2->hdev->dev; - if (tmff2->params & PARAM_FRICTION_LEVEL) - device_remove_file(dev, &dev_attr_friction_level); - - if (tmff2->params & PARAM_DAMPER_LEVEL) - device_remove_file(dev, &dev_attr_damper_level); - - if (tmff2->params & PARAM_SPRING_LEVEL) - device_remove_file(dev, &dev_attr_spring_level); - - if (tmff2->params & PARAM_RANGE) - device_remove_file(dev, &dev_attr_range); - - if (tmff2->params & PARAM_ALT_MODE) - device_remove_file(dev, &dev_attr_alternate_modes); - - if (tmff2->params & PARAM_GAIN) - device_remove_file(dev, &dev_attr_gain); - - hid_hw_stop(hdev); - tmff2->wheel_destroy(tmff2->data); - - kfree(tmff2->states); - kfree(tmff2); -} - -static const struct hid_device_id tmff2_devices[] = { - /* t300rs and variations */ - {HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMT300RS_PS3_NORM_ID)}, - {HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMT300RS_PS3_ADV_ID)}, - {HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMT300RS_PS4_NORM_ID)}, - /* t248 PC*/ - {HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMT248_PC_ID)}, - /* tx */ - {HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TX_ACTIVE)}, - - {} -}; -MODULE_DEVICE_TABLE(hid, tmff2_devices); - -static struct hid_driver tmff2_driver = { - .name = "tmff2", - .id_table = tmff2_devices, - .probe = tmff2_probe, - .remove = tmff2_remove, - .report_fixup = tmff2_report_fixup, -}; -module_hid_driver(tmff2_driver); - -MODULE_LICENSE("GPL"); diff --git a/hid-tmff2.h b/hid-tmff2.h deleted file mode 100644 index 97d52a1..0000000 --- a/hid-tmff2.h +++ /dev/null @@ -1,147 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef __HID_TMFF2_H -#define __HID_TMFF2_H - -#include -#include -#include - -extern int timer_msecs; -extern int spring_level; -extern int damper_level; -extern int friction_level; -extern int range; -extern int gain; -extern int alt_mode; - -#define USB_VENDOR_ID_THRUSTMASTER 0x044f - -/* the wheel seems to only be capable of processing a certain number of - * interrupts per second, and if this value is too low the kernel urb buffer(or - * some buffer at least) fills up. Optimally I would figure out some way to - * space out the interrupts so that they all leave at regular intervals, but - * for now this is good enough, go slow enough that everything works. - */ -#define DEFAULT_TIMER_PERIOD 8 - -#define FF_EFFECT_QUEUE_UPLOAD 0 -#define FF_EFFECT_QUEUE_START 1 -#define FF_EFFECT_QUEUE_STOP 2 -#define FF_EFFECT_QUEUE_UPDATE 3 -#define FF_EFFECT_PLAYING 4 - -#define PARAM_SPRING_LEVEL (1 << 0) -#define PARAM_DAMPER_LEVEL (1 << 1) -#define PARAM_FRICTION_LEVEL (1 << 2) -#define PARAM_RANGE (1 << 3) -#define PARAM_ALT_MODE (1 << 4) -#define PARAM_GAIN (1 << 5) - -#undef fixp_sin16 -#define fixp_sin16(v) (((v % 360) > 180) ?\ - -(fixp_sin32((v % 360) - 180) >> 16)\ - : fixp_sin32(v) >> 16) - -#define JIFFIES2MS(jiffies) ((jiffies) * 1000 / HZ) - -struct tmff2_effect_state { - struct ff_effect effect; - struct ff_effect old; - - unsigned long flags; - unsigned long count; - unsigned long start_time; -}; - -struct tmff2_device_entry { - struct hid_device *hdev; - struct input_dev *input_dev; - - /* pointer to array */ - struct tmff2_effect_state *states; - - struct delayed_work work; - - spinlock_t lock; - - int allow_scheduling; - - /* fields relevant to each actual device (T300, T150...) */ - void *data; - unsigned long params; - unsigned long max_effects; - signed short supported_effects[FF_CNT]; - - /* obligatory callbacks */ - int (*play_effect)(void *data, struct tmff2_effect_state *state); - int (*upload_effect)(void *data, struct tmff2_effect_state *state); - int (*update_effect)(void *data, struct tmff2_effect_state *state); - int (*stop_effect)(void *data, struct tmff2_effect_state *state); - - int (*wheel_init)(struct tmff2_device_entry *tmff2, int open_mode); - int (*wheel_destroy)(void *data); - - /* optional callbacks */ - int (*open)(void *data, int); - int (*close)(void *data, int); - int (*set_gain)(void *data, uint16_t gain); - int (*set_range)(void *data, uint16_t range); - /* switch_mode has 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_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); - - /* void pointers are dangerous, I know, but in this case likely the best option... */ -}; - -/* external */ -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); - -#define TMT300RS_PS3_NORM_ID 0xb66e -#define TMT300RS_PS3_ADV_ID 0xb66f -#define TMT300RS_PS4_NORM_ID 0xb66d - -#define TMT248_PC_ID 0xb696 - -#define TX_ACTIVE 0xb669 - -/* apis to different wheel families */ -/* T248 at least uses the T300RS api, not sure if there are other wheels but that's - * why these functions are given global linkage */ - -struct t300rs_device_entry { - struct hid_device *hdev; - struct input_dev *input_dev; - struct hid_report *report; - struct hid_field *ff_field; - struct usb_device *usbdev; - - int (*open)(struct input_dev *dev); - void (*close)(struct input_dev *dev); - - int mode; - int attachment; - u8 buffer_length; - u8 *send_buffer; -}; - -int t300rs_play_effect(void *, struct tmff2_effect_state *); -int t300rs_upload_effect(void *, struct tmff2_effect_state *); -int t300rs_update_effect(void *, struct tmff2_effect_state *); -int t300rs_stop_effect(void *, struct tmff2_effect_state *); - -int t300rs_open(void *, int); -int t300rs_close(void *, int); -int t300rs_set_gain(void *, uint16_t); -int t300rs_set_range(void *, uint16_t); -int t300rs_set_autocenter(void *, uint16_t); - -int t300rs_send_buf(struct t300rs_device_entry *t300rs, u8 *send_buffer, size_t len); -int t300rs_send_int(struct t300rs_device_entry *t300rs); - -#endif /* __HID_TMFF2_H */ diff --git a/hid-tminit b/hid-tminit deleted file mode 160000 index 53fde05..0000000 --- a/hid-tminit +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 53fde059e88a4314d987ab75518edb8ce86cf8a3 diff --git a/hid-tmt248.c b/hid-tmt248.c deleted file mode 100644 index 386f6d8..0000000 --- a/hid-tmt248.c +++ /dev/null @@ -1,335 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#include -#include -#include "hid-tmff2.h" - -#define T248_MAX_EFFECTS 16 -#define T248_BUFFER_LENGTH 63 - -static const u8 setup_0[64] = { 0x42, 0x01 }; -static const u8 setup_1[64] = { 0x0a, 0x04, 0x90, 0x03 }; -static const u8 setup_2[64] = { 0x0a, 0x04, 0x00, 0x0c }; -static const u8 setup_3[64] = { 0x0a, 0x04, 0x12, 0x10 }; -static const u8 setup_4[64] = { 0x0a, 0x04, 0x00, 0x06 }; -static const u8 setup_5[64] = { 0x0a, 0x04, 0x00, 0x0e }; -static const u8 setup_6[64] = { 0x0a, 0x04, 0x00, 0x0e, 0x01 }; -static const u8 *const setup_arr[] = { setup_0, setup_1, setup_2, setup_3, setup_4, setup_5, setup_6 }; -static const unsigned int setup_arr_sizes[] = { - ARRAY_SIZE(setup_0), - ARRAY_SIZE(setup_1), - ARRAY_SIZE(setup_2), - ARRAY_SIZE(setup_3), - ARRAY_SIZE(setup_4), - ARRAY_SIZE(setup_5), - ARRAY_SIZE(setup_6) -}; - -static const unsigned long t248_params = - PARAM_SPRING_LEVEL - | PARAM_DAMPER_LEVEL - | PARAM_FRICTION_LEVEL - | PARAM_RANGE - | PARAM_GAIN - ; - -static const signed short t248_effects[] = { - FF_CONSTANT, - FF_RAMP, - FF_SPRING, - FF_DAMPER, - FF_FRICTION, - FF_INERTIA, - FF_PERIODIC, - FF_SINE, - FF_TRIANGLE, - FF_SQUARE, - FF_SAW_UP, - FF_SAW_DOWN, - FF_AUTOCENTER, - FF_GAIN, - -1 -}; - -/* TODO: sort through this stuff */ -static u8 t248_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 t248_interrupts(struct t300rs_device_entry *t248) -{ - u8 *send_buf = kmalloc(256, GFP_KERNEL); - struct usb_interface *usbif = to_usb_interface(t248->hdev->dev.parent); - struct usb_host_endpoint *ep; - int ret, trans, b_ep, i; - - if (!send_buf) { - hid_err(t248->hdev, "failed allocating send buffer\n"); - return -ENOMEM; - } - - ep = &usbif->cur_altsetting->endpoint[1]; - b_ep = ep->desc.bEndpointAddress; - - for (i = 0; i < ARRAY_SIZE(setup_arr); ++i) { - memcpy(send_buf, setup_arr[i], setup_arr_sizes[i]); - - ret = usb_interrupt_msg(t248->usbdev, - usb_sndintpipe(t248->usbdev, b_ep), - send_buf, setup_arr_sizes[i], - &trans, - USB_CTRL_SET_TIMEOUT); - - if (ret) { - hid_err(t248->hdev, "setup data couldn't be sent\n"); - goto err; - } - } - -err: - kfree(send_buf); - return ret; -} - -int t248_wheel_destroy(void *data) -{ - struct t300rs_device_entry *t300rs = data; - - if (!t300rs) - return -ENODEV; - - kfree(t300rs->send_buffer); - kfree(t300rs); - return 0; -} - -int t248_set_range(void *data, uint16_t value) -{ - struct t300rs_device_entry *t248 = data; - - if (value < 140) { - hid_info(t248->hdev, "value %i too small, clamping to 140\n", value); - value = 140; - } - - if (value > 900) { - hid_info(t248->hdev, "value %i too large, clamping to 900\n", value); - value = 900; - } - - return t300rs_set_range(data, value); -} - -static int t248_send_open(struct t300rs_device_entry *t248) -{ - int r1, r2; - t248->send_buffer[0] = 0x01; - t248->send_buffer[1] = 0x04; - if ((r1 = t300rs_send_int(t248))) - return r1; - - t248->send_buffer[0] = 0x01; - t248->send_buffer[1] = 0x05; - if ((r2 = t300rs_send_int(t248))) - return r2; - - return 0; -} - -static int t248_open(void *data, int open_mode) -{ - struct t300rs_device_entry *t248 = data; - - if (!t248) - return -ENODEV; - - if (open_mode) - t248_send_open(t248); - - return t248->open(t248->input_dev); -} - -static int t248_send_close(struct t300rs_device_entry *t248) -{ - int r1, r2; - t248->send_buffer[0] = 0x01; - t248->send_buffer[1] = 0x05; - if ((r1 = t300rs_send_int(t248))) - return r1; - - t248->send_buffer[0] = 0x01; - t248->send_buffer[1] = 0x00; - if ((r2 = t300rs_send_int(t248))) - return r2; - - return 0; -} - -static int t248_close(void *data, int open_mode) -{ - struct t300rs_device_entry *t248 = data; - - if (!t248) - return -ENODEV; - - if (open_mode) - t248_send_close(t248); - - t248->close(t248->input_dev); - return 0; -} - -int t248_wheel_init(struct tmff2_device_entry *tmff2, int open_mode) -{ - struct t300rs_device_entry *t248 = kzalloc(sizeof(struct t300rs_device_entry), GFP_KERNEL); - struct list_head *report_list; - int ret; - - - if (!t248) { - ret = -ENOMEM; - goto t248_err; - } - - t248->hdev = tmff2->hdev; - t248->input_dev = tmff2->input_dev; - t248->usbdev = to_usb_device(tmff2->hdev->dev.parent->parent); - t248->buffer_length = T248_BUFFER_LENGTH; - - t248->send_buffer = kzalloc(t248->buffer_length, GFP_KERNEL); - if (!t248->send_buffer) { - ret = -ENOMEM; - goto send_err; - } - - report_list = &t248->hdev->report_enum[HID_OUTPUT_REPORT].report_list; - t248->report = list_entry(report_list->next, struct hid_report, list); - t248->ff_field = t248->report->field[0]; - - t248->open = t248->input_dev->open; - t248->close = t248->input_dev->close; - - if ((ret = t248_interrupts(t248))) - goto interrupt_err; - - /* everything went OK */ - tmff2->data = t248; - tmff2->params = t248_params; - tmff2->max_effects = T248_MAX_EFFECTS; - memcpy(tmff2->supported_effects, t248_effects, sizeof(t248_effects)); - - if (!open_mode) - t248_send_open(t248); - - hid_info(t248->hdev, "force feedback for T248\n"); - return 0; - -interrupt_err: -send_err: - kfree(t248); -t248_err: - hid_err(tmff2->hdev, "failed initializing T248\n"); - return ret; -} - -static __u8 *t248_wheel_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) -{ - rdesc = t248_pc_rdesc_fixed; - *rsize = sizeof(t248_pc_rdesc_fixed); - return rdesc; -} - -int t248_populate_api(struct tmff2_device_entry *tmff2) -{ - tmff2->play_effect = t300rs_play_effect; - tmff2->upload_effect = t300rs_upload_effect; - tmff2->update_effect = t300rs_update_effect; - tmff2->stop_effect = t300rs_stop_effect; - - tmff2->set_gain = t300rs_set_gain; - tmff2->set_autocenter = t300rs_set_autocenter; - /* T248 only has 900 degree range, instead of T300RS 1080 */ - tmff2->set_range = t248_set_range; - tmff2->wheel_fixup = t248_wheel_fixup; - - tmff2->open = t248_open; - tmff2->close = t248_close; - - tmff2->wheel_init = t248_wheel_init; - tmff2->wheel_destroy = t248_wheel_destroy; - - return 0; -} diff --git a/hid-tmt300rs.c b/hid-tmt300rs.c deleted file mode 100644 index 4499687..0000000 --- a/hid-tmt300rs.c +++ /dev/null @@ -1,1714 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#include -#include -#include "hid-tmff2.h" - -#define T300RS_MAX_EFFECTS 16 -#define T300RS_NORM_BUFFER_LENGTH 63 -#define T300RS_PS4_BUFFER_LENGTH 31 - -#define T300RS_DEFAULT_ATTACHMENT 0x06 -#define T300RS_F1_ATTACHMENT 0x03 - -static const unsigned long t300rs_params = - PARAM_SPRING_LEVEL - | PARAM_DAMPER_LEVEL - | PARAM_FRICTION_LEVEL - | PARAM_GAIN - | PARAM_RANGE - | PARAM_ALT_MODE - ; - -static const signed short t300rs_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 __packed t300rs_fw_response { - uint8_t unused1[2]; - uint8_t fw_version; - uint8_t unused2; -}; - - -struct __packed t300rs_packet_header { - uint8_t zero1; - uint8_t id; - uint8_t code; -}; - -struct __packed t300rs_setup_header { - uint8_t cmd; - uint8_t code; -}; - -struct __packed t300rs_packet_envelope { - uint16_t attack_length; - uint16_t attack_level; - uint16_t fade_length; - uint16_t fade_level; -}; - -struct __packed t300rs_packet_timing { - uint8_t start_marker; - uint16_t duration; - uint8_t zero1[2]; - uint16_t offset; - uint8_t zero2; - uint16_t end_marker; -}; - -struct usb_ctrlrequest t300rs_fw_request = { - .bRequestType = 0xc1, - .bRequest = 86, - .wValue = 0, - .wIndex = 0, - .wLength = 8 -}; - -struct t300rs_data { - unsigned long quirks; - void *device_props; -}; - -static u8 t300rs_rdesc_nrm_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, 0x35, /* Usage (Rz) (Brake) */ - 0x26, 0xff, 0x03, /* Logical maximum (1023) */ - 0x46, 0xff, 0x03, /* Physical maximum (1023) */ - 0x81, 0x02, /* Input (Variable, Absolute) */ - 0x09, 0x32, /* Usage (Z) (Gas) */ - 0x81, 0x02, /* Input (Variable, Absolute) */ - 0x09, 0x31, /* Usage (Y) (Clutch) */ - 0x81, 0x02, /* Input (Variable, Absolute) */ - 0x81, 0x03, /* Input (Variable, Absolute, Constant) */ - 0x05, 0x09, /* Usage page (Button) */ - 0x19, 0x01, /* Usage minimum (1) */ - 0x29, 0x0d, /* Usage maximum (13) */ - 0x25, 0x01, /* Logical maximum (1) */ - 0x45, 0x01, /* Physical maximum (1) */ - 0x75, 0x01, /* Report size (1) */ - 0x95, 0x0d, /* Report count (13) */ - 0x81, 0x02, /* Input (Variable, Absolute) */ - 0x75, 0x0b, /* Report size (13) */ - 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, /* Unit (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, 0x7f, /* Logical maximum (32767) */ - 0x15, 0x00, /* Logical minimum (0) */ - 0x46, 0xff, 0x7f, /* Physical maximum (32767) */ - 0x36, 0x00, 0x80, /* Physical minimum (-32768) */ - 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 u8 t300rs_rdesc_adv_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) */ - 0x26, 0xff, 0x03, /* Logical maximum (1023) */ - 0x46, 0xff, 0x03, /* Physical maximum (1023) */ - 0x81, 0x02, /* Input (Variable, Absolute) */ - 0x09, 0x35, /* Usage (Rz) */ - 0x81, 0x02, /* Input (Variable, Absolute) */ - 0x09, 0x36, /* Usage (Slider) */ - 0x81, 0x02, /* Input (Variable, Absolute) */ - 0x81, 0x03, /* Input (Variable, Absolute, Constant) ? */ - 0x05, 0x09, /* Usage page (Button) */ - 0x19, 0x01, /* Usage minimum (1) */ - 0x29, 0x19, /* Usage maximum (25) */ - 0x25, 0x01, /* Logical maximum (1) */ - 0x45, 0x01, /* Physical maximum (1) */ - 0x75, 0x01, /* Report size (1) */ - 0x95, 0x19, /* Report count (25) */ - 0x81, 0x02, /* Input (Variable, Absolute) */ - 0x75, 0x03, /* Report size (3) */ - 0x95, 0x01, /* Report count (1) */ - 0x81, 0x03, /* Input (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, /* Unit (None) */ - 0x85, 0x60, /* Report ID (96), prev 10 */ - 0x06, 0x00, 0xff, /* Usage page (Vendor 1) */ - 0x09, 0x60, /* Usage (96), prev 10 (why?) */ - 0x75, 0x08, /* Report size (8) */ - 0x95, 0x3f, /* Report count (63) */ - 0x26, 0xff, 0x00, /* Logical maximum (255) */ - 0x46, 0xff, 0x00, /* Physical maximum (255) */ - 0x91, 0x02, /* Output (Variable, Absolute) */ - 0x85, 0x02, /* Report ID (2) */ - 0x09, 0x02, /* Usage (Mouse) */ - 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 u8 t300rs_rdesc_ps4_fixed[] = { - 0x05, 0x01, /* Usage page (Generic Desktop) */ - 0x09, 0x05, /* Usage (GamePad) */ - 0xa1, 0x01, /* Collection (Application) */ - 0x85, 0x01, /* Report ID (1) */ - 0x09, 0x00, /* Usage (U) (was X) */ - 0x09, 0x00, /* Usage (U) (was Y) */ - 0x09, 0x00, /* Usage (U) (was Z) */ - 0x09, 0x00, /* Usage (U) (was Rz)*/ - 0x15, 0x00, /* Logical minimum (0) */ - 0x26, 0xff, 0x00, /* Logical maximum (255) */ - 0x75, 0x08, /* Report size (8) */ - 0x95, 0x04, /* Report count (4) */ - 0x81, 0x02, /* Input (Variable, Absolute) */ - 0x09, 0x39, /* Usage (Hat Switch) */ - 0x15, 0x00, /* Logical minimum (0) */ - 0x25, 0x07, /* Logical maximum (7)*/ - 0x35, 0x00, /* Physical minimum (0) */ - 0x46, 0x3b, 0x01, /* Physical maximum (315) */ - 0x65, 0x14, /* Unit (Eng Rot, Angular Pos) */ - 0x75, 0x04, /* Report size (4) */ - 0x95, 0x01, /* Report count (1) */ - 0x81, 0x42, /* Input (Variable, Absolute, NullState) */ - 0x65, 0x00, /* Input (None) */ - 0x05, 0x09, /* Usage page (Button) */ - 0x19, 0x01, /* Usage minimum (1) */ - 0x29, 0x0e, /* Usage maximum (14) */ - 0x15, 0x00, /* Logical minimum (0) */ - 0x25, 0x01, /* Logical maximum (1) */ - 0x75, 0x01, /* Report size (1) */ - 0x95, 0x0e, /* Report size (14) */ - 0x81, 0x02, /* Input (Variable, Absolute) */ - 0x06, 0x00, 0xff, /* Usage page (Vendor 1) */ - 0x09, 0x20, /* Usage (32) */ - 0x75, 0x06, /* Report size (6) */ - 0x95, 0x01, /* Report count (1) */ - 0x81, 0x02, /* Input (Variable, Absolute) */ - 0x05, 0x01, /* Usage page (Generic Desktop) */ - 0x09, 0x00, /* Usage (U) (was Rx)*/ - 0x09, 0x00, /* Usage (U) (was Ry) */ - 0x15, 0x00, /* Logical minimum (0) */ - 0x26, 0xff, 0x00, /* Logical maximum (255) */ - 0x75, 0x08, /* Report size (8) */ - 0x95, 0x02, /* Report count (2) */ - 0x81, 0x02, /* Input (Variable, Absolute) */ - 0x05, 0x01, /* Usage page (Vendor 1) */ - /* constant zero? */ - 0x09, 0x00, /* Usage (33) */ - 0x95, 0x21, /* Report count (33) */ - 0x81, 0x02, /* Input (Variable, Absolute) */ - /* wheel */ - 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) */ - /* gas */ - 0x09, 0x31, /* Usage (Y) */ - 0x81, 0x02, /* Input (Variable, Absolute) */ - /* brake */ - 0x09, 0x32, /* Usage (Z) */ - 0x81, 0x02, /* Input (Variable, Absolute) */ - /* clutch */ - 0x09, 0x35, /* Usage (Rz) */ - 0x81, 0x02, /* Input (Variable, Absolute) */ - /* stick shifter (check model no) */ - 0x05, 0x09, /* Usage page (Button) */ - 0x19, 0x0f, /* Usage minimum (15) */ - 0x29, 0x17, /* Usage maximum (23) */ - 0x15, 0x00, /* Logical minimum (1) */ - 0x25, 0x01, /* Logical maximum (1) */ - 0x75, 0x01, /* Report size (1) */ - 0x95, 0x08, /* Report count (8) */ - 0x81, 0x02, /* Input (Variable, Absolute) */ - /* no clue */ - 0x05, 0x01, /* Usage page (Generic Desktop) */ - 0x09, 0x00, /* Usage (U) */ - 0x75, 0x08, /* Report size (8) */ - 0x95, 0x0c, /* Report count (12) */ - 0x81, 0x02, /* Input (Variable, Absolute) */ - /* continue unmodified */ - 0x06, 0x00, 0xff, /* Usage page (Vendor defined 1) */ - 0x85, 0x60, /* Report ID (5) (change to 0x60?) */ - 0x09, 0x60, /* Usage (34) (change to 0x60?) */ - 0x95, 0x1f, /* Report count (31) () */ - 0x91, 0x02, /* Output (Variable, Absolute) */ - 0x85, 0x03, /* Report ID (3) */ - 0x0a, 0x21, 0x27, /* ??? */ - 0x95, 0x2f, /* Report count (47) */ - 0xb1, 0x02, /* Feature (Data, Var, Abs) */ - 0xc0, /* End collection */ - - /* From here on out no clue */ - 0x06, 0xf0, - 0xff, 0x09, - 0x40, 0xa1, - 0x01, 0x85, - 0xf0, 0x09, - 0x47, 0x95, - 0x3f, 0xb1, - 0x02, 0x85, - 0xf1, 0x09, - 0x48, 0x95, - 0x3f, 0xb1, - 0x02, 0x85, - 0xf2, 0x09, - 0x49, 0x95, - 0x0f, 0xb1, - 0x02, 0x85, - 0xf3, 0x0a, - 0x01, 0x47, - 0x95, 0x07, - 0xb1, 0x02, - 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 -}; - -int t300rs_send_buf(struct t300rs_device_entry *t300rs, u8 *send_buffer, size_t len) -{ - int i; - /* check that send_buffer fits into our report */ - if (len > t300rs->buffer_length) - return -EINVAL; - - /* fill with actual data */ - for (i = 0; i < len; ++i) - t300rs->ff_field->value[i] = send_buffer[i]; - - /* fill the rest with zeroes */ - for (i = len; i < t300rs->buffer_length; ++i) - t300rs->ff_field->value[i] = 0; - - hid_hw_request(t300rs->hdev, t300rs->report, HID_REQ_SET_REPORT); - return 0; -} - -int t300rs_send_int(struct t300rs_device_entry *t300rs) -{ - t300rs_send_buf(t300rs, t300rs->send_buffer, t300rs->buffer_length); - memset(t300rs->send_buffer, 0, t300rs->buffer_length); - - return 0; -} - -static void t300rs_fill_header(struct t300rs_packet_header *packet_header, - uint8_t id, uint8_t code) -{ - packet_header->id = id + 1; - packet_header->code = code; -} - -int t300rs_play_effect(void *data, struct tmff2_effect_state *state) -{ - struct t300rs_device_entry *t300rs = data; - struct __packed t300rs_packet_play { - struct t300rs_packet_header header; - uint8_t value; - } *play_packet = (struct t300rs_packet_play *)t300rs->send_buffer; - - int ret; - - - t300rs_fill_header(&play_packet->header, state->effect.id, 0x89); - play_packet->value = 0x01; - - ret = t300rs_send_int(t300rs); - if (ret) - hid_err(t300rs->hdev, "failed starting effect play\n"); - - return ret; -} - -int t300rs_stop_effect(void *data, struct tmff2_effect_state *state) -{ - struct t300rs_device_entry *t300rs = data; - struct __packed t300rs_packet_stop { - struct t300rs_packet_header header; - uint8_t value; - } *stop_packet = (struct t300rs_packet_stop *)t300rs->send_buffer; - - int ret; - - - t300rs_fill_header(&stop_packet->header, state->effect.id, 0x89); - - ret = t300rs_send_int(t300rs); - if (ret) - hid_err(t300rs->hdev, "failed stopping effect play\n"); - - return ret; -} - -static void t300rs_fill_envelope(struct t300rs_packet_envelope *packet_envelope, - int16_t level, uint16_t duration, struct ff_envelope *envelope) -{ - uint16_t attack_length = (duration * envelope->attack_length) / 0x7fff; - uint16_t attack_level = (level * envelope->attack_level) / 0x7fff; - uint16_t fade_length = (duration * envelope->fade_length) / 0x7fff; - uint16_t fade_level = (level * envelope->fade_level) / 0x7fff; - - packet_envelope->attack_length = cpu_to_le16(attack_length); - packet_envelope->attack_level = cpu_to_le16(attack_level); - packet_envelope->fade_length = cpu_to_le16(fade_length); - packet_envelope->fade_level = cpu_to_le16(fade_level); -} - -static void t300rs_fill_timing(struct t300rs_packet_timing *packet_timing, - uint16_t duration, uint16_t offset){ - packet_timing->start_marker = 0x4f; - - packet_timing->duration = cpu_to_le16(duration); - packet_timing->offset = cpu_to_le16(offset); - - packet_timing->end_marker = 0xffff; -} - -static int t300rs_update_envelope(struct t300rs_device_entry *t300rs, - struct tmff2_effect_state *state, - int16_t level, - uint16_t duration, - uint8_t id, - struct ff_envelope envelope, - struct ff_envelope envelope_old - ) -{ - struct __packed t300rs_packet_mod_envelope { - struct t300rs_packet_header header; - uint8_t attribute; - uint16_t value; - } *packet_mod_envelope = (struct t300rs_packet_mod_envelope *)t300rs->send_buffer; - - uint16_t attack_length, attack_level, fade_length, fade_level; - int ret = 0; - - duration = duration - 1; - - attack_length = (duration * envelope.attack_length) / 0x7fff; - attack_level = (level * envelope.attack_level) / 0x7fff; - fade_length = (duration * envelope.fade_length) / 0x7fff; - fade_level = (level * envelope.fade_level) / 0x7fff; - - if (envelope.attack_length != envelope_old.attack_length) { - t300rs_fill_header(&packet_mod_envelope->header, id, 0x31); - packet_mod_envelope->attribute = 0x81; - packet_mod_envelope->value = cpu_to_le16(attack_length); - - ret = t300rs_send_int(t300rs); - if (ret) { - hid_err(t300rs->hdev, "failed modifying effect envelope\n"); - goto error; - } - } - - if (envelope.attack_level != envelope_old.attack_level) { - t300rs_fill_header(&packet_mod_envelope->header, id, 0x31); - packet_mod_envelope->attribute = 0x82; - packet_mod_envelope->value = cpu_to_le16(attack_level); - - ret = t300rs_send_int(t300rs); - if (ret) { - hid_err(t300rs->hdev, "failed modifying effect envelope\n"); - goto error; - } - } - - if (envelope.fade_length != envelope_old.fade_length) { - t300rs_fill_header(&packet_mod_envelope->header, id, 0x31); - packet_mod_envelope->attribute = 0x84; - packet_mod_envelope->value = cpu_to_le16(fade_length); - - ret = t300rs_send_int(t300rs); - if (ret) { - hid_err(t300rs->hdev, "failed modifying effect envelope\n"); - goto error; - } - } - - if (envelope.fade_level != envelope_old.fade_level) { - t300rs_fill_header(&packet_mod_envelope->header, id, 0x31); - packet_mod_envelope->attribute = 0x88; - packet_mod_envelope->value = cpu_to_le16(fade_level); - - ret = t300rs_send_int(t300rs); - if (ret) { - hid_err(t300rs->hdev, "failed modifying effect envelope\n"); - goto error; - } - } - -error: - return ret; -} - -static int t300rs_update_simple_duration(struct t300rs_device_entry *t300rs, - struct tmff2_effect_state *state, unsigned type) -{ - struct ff_effect effect = state->effect; - struct __packed t300rs_packet_mod_duration { - struct t300rs_packet_header header; - uint16_t marker; - uint16_t duration; - } *packet_mod_duration = (struct t300rs_packet_mod_duration *)t300rs->send_buffer; - - uint16_t duration; - int ret = 0; - - duration = effect.replay.length - 1; - - t300rs_fill_header(&packet_mod_duration->header, effect.id, 0x49); - packet_mod_duration->marker = cpu_to_le16(0x4100 + type); - packet_mod_duration->duration = cpu_to_le16(duration); - - ret = t300rs_send_int(t300rs); - if (ret) { - hid_err(t300rs->hdev, "failed modifying duration\n"); - goto error; - } - -error: - return ret; -} - -static int t300rs_update_periodic_duration(struct t300rs_device_entry *t300rs, - struct tmff2_effect_state *state, unsigned type) -{ - struct ff_effect effect = state->effect; - struct __packed t300rs_packet_mod_duration { - struct t300rs_packet_header header; - uint8_t type; - uint8_t marker; - uint16_t duration; - } *packet_mod_duration = (struct t300rs_packet_mod_duration *)t300rs->send_buffer; - - uint16_t duration; - int ret = 0; - - duration = effect.replay.length - 1; - - t300rs_fill_header(&packet_mod_duration->header, effect.id, 0x49); - packet_mod_duration->type = type; - packet_mod_duration->marker = 0x41; - packet_mod_duration->duration = cpu_to_le16(duration); - - ret = t300rs_send_int(t300rs); - if (ret) { - hid_err(t300rs->hdev, "failed modifying duration\n"); - goto error; - } - -error: - return ret; -} - -static int t300rs_update_ramp_duration(struct t300rs_device_entry *t300rs, - struct tmff2_effect_state *state) -{ - struct ff_effect effect = state->effect; - struct __packed t300rs_packet_mod_duration { - struct t300rs_packet_header header; - uint8_t marker0; - uint16_t duration0; - uint8_t marker1; - uint8_t marker2; - uint16_t duration1; - } *packet_mod_duration = (struct t300rs_packet_mod_duration *)t300rs->send_buffer; - - uint16_t duration; - int ret = 0; - - duration = effect.replay.length - 1; - - t300rs_fill_header(&packet_mod_duration->header, effect.id, 0x4e); - packet_mod_duration->marker0 = 0x08; - packet_mod_duration->duration0 = cpu_to_le16(duration); - packet_mod_duration->marker1 = 0x05; - packet_mod_duration->marker2 = 0x41; - packet_mod_duration->duration1 = cpu_to_le16(duration); - - ret = t300rs_send_int(t300rs); - if (ret) { - hid_err(t300rs->hdev, "failed modifying duration\n"); - goto error; - } - -error: - return ret; -} - -static int t300rs_update_constant(struct t300rs_device_entry *t300rs, - struct tmff2_effect_state *state) -{ - struct ff_effect effect = state->effect; - struct ff_effect old = state->old; - struct ff_constant_effect constant = effect.u.constant; - struct ff_constant_effect constant_old = old.u.constant; - struct __packed t300rs_packet_mod_constant { - struct t300rs_packet_header header; - uint16_t level; - } *packet_mod_constant = (struct t300rs_packet_mod_constant *)t300rs->send_buffer; - int ret; - int16_t level; - - level = (constant.level * fixp_sin16(effect.direction * 360 / 0x10000)) / 0x7fff; - - if ((constant.level != constant_old.level) || (effect.direction != old.direction)) { - - t300rs_fill_header(&packet_mod_constant->header, effect.id, 0x0a); - packet_mod_constant->level = cpu_to_le16(level); - - ret = t300rs_send_int(t300rs); - if (ret) { - hid_err(t300rs->hdev, "failed modifying constant effect\n"); - goto error; - } - - } - - ret = t300rs_update_envelope(t300rs, - state, - level, - effect.replay.length, - effect.id, - constant.envelope, - constant_old.envelope - ); - - if (ret) { - hid_err(t300rs->hdev, "failed modifying constant envelope\n"); - goto error; - } - - ret = t300rs_update_simple_duration(t300rs, state, 0x00); - if (ret) { - hid_err(t300rs->hdev, "failed modifying constant duration\n"); - goto error; - } - -error: - return ret; -} - -static int t300rs_update_ramp(struct t300rs_device_entry *t300rs, - struct tmff2_effect_state *state) -{ - struct ff_effect effect = state->effect; - struct ff_effect old = state->old; - struct ff_ramp_effect ramp = effect.u.ramp; - struct ff_ramp_effect ramp_old = old.u.ramp; - struct __packed t300rs_packet_mod_ramp { - struct t300rs_packet_header header; - uint8_t attribute; - uint16_t difference; - uint16_t level; - } *packet_mod_ramp = (struct t300rs_packet_mod_ramp *)t300rs->send_buffer; - - int ret; - - uint16_t difference, top, bottom; - int16_t level; - - 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; - - if ((ramp.start_level != ramp_old.start_level) - || (ramp.end_level != ramp_old.end_level) - || (effect.direction != old.direction)) { - - t300rs_fill_header(&packet_mod_ramp->header, effect.id, 0x0e); - packet_mod_ramp->attribute = 0x03; - packet_mod_ramp->difference = cpu_to_le16(difference); - packet_mod_ramp->level = cpu_to_le16(level); - - ret = t300rs_send_int(t300rs); - if (ret) { - hid_err(t300rs->hdev, "failed modifying ramp effect\n"); - goto error; - } - - } - - ret = t300rs_update_envelope(t300rs, - state, - level, - effect.replay.length, - effect.id, - ramp.envelope, - ramp_old.envelope - ); - - - if (ret) { - hid_err(t300rs->hdev, "failed modifying ramp envelope\n"); - goto error; - } - - ret = t300rs_update_ramp_duration(t300rs, state); - if (ret) { - hid_err(t300rs->hdev, "failed modifying ramp duration\n"); - goto error; - } - -error: - return ret; -} - -static int t300rs_update_damper(struct t300rs_device_entry *t300rs, - struct tmff2_effect_state *state) -{ - struct ff_effect effect = state->effect; - struct ff_effect old = state->old; - struct ff_condition_effect damper = effect.u.condition[0]; - struct ff_condition_effect damper_old = old.u.condition[0]; - struct __packed t300rs_packet_mod_damper { - struct t300rs_packet_header header; - uint8_t attribute; - uint16_t value0; - uint16_t value1; - } *packet_mod_damper = (struct t300rs_packet_mod_damper *)t300rs->send_buffer; - - int ret, input_level; - - input_level = damper_level; - if (state->effect.type == FF_FRICTION) - input_level = friction_level; - - if (state->effect.type == FF_SPRING) - input_level = spring_level; - - if (damper.right_coeff != damper_old.right_coeff) { - int16_t coeff = damper.right_coeff * input_level / 100; - - t300rs_fill_header(&packet_mod_damper->header, effect.id, 0x0e); - packet_mod_damper->attribute = 0x41; - packet_mod_damper->value0 = cpu_to_le16(coeff); - - ret = t300rs_send_int(t300rs); - if (ret) { - hid_err(t300rs->hdev, "failed modifying damper rc\n"); - goto error; - } - - } - - if (damper.left_coeff != damper_old.left_coeff) { - int16_t coeff = damper.left_coeff * input_level / 100; - - t300rs_fill_header(&packet_mod_damper->header, effect.id, 0x0e); - packet_mod_damper->attribute = 0x42; - packet_mod_damper->value0 = cpu_to_le16(coeff); - - ret = t300rs_send_int(t300rs); - if (ret) { - hid_err(t300rs->hdev, "failed modifying damper lc\n"); - goto error; - } - - } - - if ((damper.deadband != damper_old.deadband) - || (damper.center != damper_old.center)) { - - uint16_t right_deadband = 0xfffe - damper.deadband - damper.center; - uint16_t left_deadband = 0xfffe - damper.deadband + damper.center; - - t300rs_fill_header(&packet_mod_damper->header, effect.id, 0x0e); - packet_mod_damper->attribute = 0x4c; - packet_mod_damper->value0 = cpu_to_le16(right_deadband); - packet_mod_damper->value1 = cpu_to_le16(left_deadband); - - ret = t300rs_send_int(t300rs); - if (ret) { - hid_err(t300rs->hdev, "failed modifying damper deadband\n"); - goto error; - } - - } - - ret = t300rs_update_simple_duration(t300rs, state, 0x06); - if (ret) { - hid_err(t300rs->hdev, "failed modifying damper duration\n"); - goto error; - } - -error: - return ret; -} - -static int t300rs_update_spring(struct t300rs_device_entry *t300rs, - struct tmff2_effect_state *state) -{ - return t300rs_update_damper(t300rs, state); -} - - -static int t300rs_update_periodic(struct t300rs_device_entry *t300rs, - struct tmff2_effect_state *state) -{ - struct ff_effect effect = state->effect; - struct ff_effect old = state->old; - struct ff_periodic_effect periodic = effect.u.periodic; - struct ff_periodic_effect periodic_old = old.u.periodic; - struct __packed t300rs_packet_mod_periodic { - struct t300rs_packet_header header; - uint8_t attribute; - uint16_t value; - } *packet_mod_periodic = (struct t300rs_packet_mod_periodic *)t300rs->send_buffer; - - int ret; - int16_t magnitude, phase; - - magnitude = (periodic.magnitude * fixp_sin16(effect.direction * 360 / 0x10000)) / 0x7fff; - phase = periodic.phase; - - if ((periodic.magnitude != periodic_old.magnitude) - || (effect.direction != old.direction)) { - - t300rs_fill_header(&packet_mod_periodic->header, effect.id, 0x0e); - packet_mod_periodic->attribute = 0x01; - packet_mod_periodic->value = cpu_to_le16(magnitude); - - ret = t300rs_send_int(t300rs); - if (ret) { - hid_err(t300rs->hdev, "failed modifying periodic magnitude\n"); - goto error; - } - - } - - if (periodic.offset != periodic_old.offset) { - uint16_t offset = periodic.offset; - - t300rs_fill_header(&packet_mod_periodic->header, effect.id, 0x0e); - packet_mod_periodic->attribute = 0x02; - packet_mod_periodic->value = cpu_to_le16(offset); - - ret = t300rs_send_int(t300rs); - if (ret) { - hid_err(t300rs->hdev, "failed modifying periodic offset\n"); - goto error; - } - - } - - if (periodic.phase != periodic_old.phase) { - - t300rs_fill_header(&packet_mod_periodic->header, effect.id, 0x0e); - packet_mod_periodic->attribute = 0x04; - packet_mod_periodic->value = cpu_to_le16(phase); - - ret = t300rs_send_int(t300rs); - if (ret) { - hid_err(t300rs->hdev, "failed modifying periodic phase\n"); - goto error; - } - - } - - if (periodic.period != periodic_old.period) { - int16_t period = periodic.period; - - t300rs_fill_header(&packet_mod_periodic->header, effect.id, 0x0e); - packet_mod_periodic->attribute = 0x08; - packet_mod_periodic->value = cpu_to_le16(period); - - ret = t300rs_send_int(t300rs); - if (ret) { - hid_err(t300rs->hdev, "failed modifying periodic period\n"); - goto error; - } - - } - - ret = t300rs_update_envelope(t300rs, - state, - magnitude, - effect.replay.length, - effect.id, - periodic.envelope, - periodic_old.envelope - ); - - if (ret) { - hid_err(t300rs->hdev, "failed modifying periodic envelope\n"); - goto error; - } - - ret = t300rs_update_periodic_duration(t300rs, state, periodic.waveform - 0x57); - if (ret) { - hid_err(t300rs->hdev, "failed modifying periodic duration\n"); - goto error; - } - -error: - return ret; -} - -static int t300rs_upload_constant(struct t300rs_device_entry *t300rs, - struct tmff2_effect_state *state) -{ - struct ff_effect effect = state->effect; - struct ff_constant_effect constant = state->effect.u.constant; - struct __packed t300rs_packet_constant { - struct t300rs_packet_header header; - uint16_t level; - struct t300rs_packet_envelope envelope; - uint8_t zero; - struct t300rs_packet_timing timing; - } *packet_constant = (struct t300rs_packet_constant *)t300rs->send_buffer; - - int16_t level; - uint16_t duration, offset; - - int ret; - - level = (constant.level * fixp_sin16(effect.direction * 360 / 0x10000)) / 0x7fff; - duration = effect.replay.length - 1; - - offset = effect.replay.delay; - - t300rs_fill_header(&packet_constant->header, effect.id, 0x6a); - - packet_constant->level = cpu_to_le16(level); - - t300rs_fill_envelope(&packet_constant->envelope, level, duration, - &constant.envelope); - t300rs_fill_timing(&packet_constant->timing, duration, offset); - - ret = t300rs_send_int(t300rs); - if (ret) - hid_err(t300rs->hdev, "failed uploading constant effect\n"); - - return ret; -} - -static int t300rs_upload_ramp(struct t300rs_device_entry *t300rs, - struct tmff2_effect_state *state) -{ - struct ff_effect effect = state->effect; - struct ff_ramp_effect ramp = state->effect.u.ramp; - struct __packed t300rs_packet_ramp { - struct t300rs_packet_header header; - uint16_t difference; - uint16_t level; - uint8_t zero1[2]; - uint16_t duration; - uint16_t marker; - struct t300rs_packet_envelope envelope; - uint8_t direction; - struct t300rs_packet_timing timing; - } *packet_ramp = (struct t300rs_packet_ramp *)t300rs->send_buffer; - - int ret; - uint16_t difference, offset, top, bottom, duration; - int16_t level; - - duration = effect.replay.length - 1; - - 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; - offset = effect.replay.delay; - - - t300rs_fill_header(&packet_ramp->header, effect.id, 0x6b); - - packet_ramp->difference = cpu_to_le16(difference); - packet_ramp->level = cpu_to_le16(level); - packet_ramp->duration = cpu_to_le16(duration); - - packet_ramp->marker = cpu_to_le16(0x8000); - - t300rs_fill_envelope(&packet_ramp->envelope, level, duration, - &ramp.envelope); - - packet_ramp->direction = ramp.end_level > ramp.start_level ? 0x04 : 0x05; - t300rs_fill_timing(&packet_ramp->timing, duration, offset); - - ret = t300rs_send_int(t300rs); - if (ret) - hid_err(t300rs->hdev, "failed uploading ramp"); - - return ret; -} - -static int t300rs_upload_spring(struct t300rs_device_entry *t300rs, - struct tmff2_effect_state *state) -{ - struct ff_effect effect = state->effect; - /* we only care about the first axis */ - struct ff_condition_effect spring = state->effect.u.condition[0]; - struct __packed t300rs_packet_spring { - struct t300rs_packet_header header; - uint16_t right_coeff; - uint16_t left_coeff; - uint16_t right_deadband; - uint16_t left_deadband; - uint8_t spring_start[17]; - struct t300rs_packet_timing timing; - } *packet_spring = (struct t300rs_packet_spring *)t300rs->send_buffer; - - int ret; - uint16_t duration, right_coeff, left_coeff, right_deadband, left_deadband, offset; - - duration = effect.replay.length - 1; - - right_coeff = spring.right_coeff * spring_level / 100; - left_coeff = spring.left_coeff * spring_level / 100; - - right_deadband = 0xfffe - spring.deadband - spring.center; - left_deadband = 0xfffe - spring.deadband + spring.center; - - offset = effect.replay.delay; - - t300rs_fill_header(&packet_spring->header, effect.id, 0x64); - - packet_spring->right_coeff = cpu_to_le16(right_coeff); - packet_spring->left_coeff = cpu_to_le16(left_coeff); - packet_spring->right_deadband = cpu_to_le16(right_deadband); - packet_spring->left_deadband = cpu_to_le16(left_deadband); - - memcpy(&packet_spring->spring_start, spring_values, ARRAY_SIZE(spring_values)); - t300rs_fill_timing(&packet_spring->timing, duration, offset); - - ret = t300rs_send_int(t300rs); - if (ret) - hid_err(t300rs->hdev, "failed uploading spring\n"); - - return ret; -} - -static int t300rs_upload_damper(struct t300rs_device_entry *t300rs, - struct tmff2_effect_state *state) -{ - - struct ff_effect effect = state->effect; - /* we only care about the first axis */ - struct ff_condition_effect spring = state->effect.u.condition[0]; - struct __packed t300rs_packet_damper { - struct t300rs_packet_header header; - uint16_t right_coeff; - uint16_t left_coeff; - uint16_t right_deadband; - uint16_t left_deadband; - uint8_t damper_start[17]; - struct t300rs_packet_timing timing; - } *packet_damper = (struct t300rs_packet_damper *)t300rs->send_buffer; - - int ret, input_level; - uint16_t duration, right_coeff, left_coeff, right_deadband, left_deadband, offset; - - duration = effect.replay.length - 1; - - input_level = damper_level; - if (state->effect.type == FF_FRICTION) - input_level = friction_level; - - right_coeff = spring.right_coeff * input_level / 100; - left_coeff = spring.left_coeff * input_level / 100; - - right_deadband = 0xfffe - spring.deadband - spring.center; - left_deadband = 0xfffe - spring.deadband + spring.center; - - offset = effect.replay.delay; - - t300rs_fill_header(&packet_damper->header, effect.id, 0x64); - - packet_damper->right_coeff = cpu_to_le16(right_coeff); - packet_damper->left_coeff = cpu_to_le16(left_coeff); - packet_damper->right_deadband = cpu_to_le16(right_deadband); - packet_damper->left_deadband = cpu_to_le16(left_deadband); - - memcpy(&packet_damper->damper_start, damper_values, ARRAY_SIZE(damper_values)); - t300rs_fill_timing(&packet_damper->timing, duration, offset); - - ret = t300rs_send_int(t300rs); - if (ret) - hid_err(t300rs->hdev, "failed uploading spring\n"); - - return ret; -} - -static int t300rs_upload_periodic(struct t300rs_device_entry *t300rs, - struct tmff2_effect_state *state) -{ - struct ff_effect effect = state->effect; - struct ff_periodic_effect periodic = state->effect.u.periodic; - struct __packed t300rs_packet_periodic { - struct t300rs_packet_header header; - uint16_t magnitude; - uint16_t periodic_offset; - uint16_t phase; - uint16_t period; - uint16_t marker; - struct t300rs_packet_envelope envelope; - uint8_t waveform; - struct t300rs_packet_timing timing; - } *packet_periodic = (struct t300rs_packet_periodic *)t300rs->send_buffer; - - int ret; - uint16_t duration, period, offset, periodic_offset; - int16_t phase, magnitude; - - duration = effect.replay.length - 1; - magnitude = (periodic.magnitude * fixp_sin16(effect.direction * 360 / 0x10000)) / 0x7fff; - - phase = periodic.phase; - periodic_offset = periodic.offset; - - period = periodic.period; - offset = effect.replay.delay; - - t300rs_fill_header(&packet_periodic->header, effect.id, 0x6b); - - packet_periodic->magnitude = cpu_to_le16(magnitude); - packet_periodic->periodic_offset = cpu_to_le16(periodic_offset); - packet_periodic->phase = cpu_to_le16(phase); - packet_periodic->period = cpu_to_le16(period); - - packet_periodic->marker = cpu_to_le16(0x8000); - - t300rs_fill_envelope(&packet_periodic->envelope, magnitude, duration, - &periodic.envelope); - - packet_periodic->waveform = periodic.waveform - 0x57; - - t300rs_fill_timing(&packet_periodic->timing, duration, offset); - - ret = t300rs_send_int(t300rs); - if (ret) - hid_err(t300rs->hdev, "failed uploading periodic effect"); - - return ret; -} - -int t300rs_update_effect(void *data, struct tmff2_effect_state *state) -{ - struct t300rs_device_entry *t300rs = data; - switch (state->effect.type) { - case FF_CONSTANT: - return t300rs_update_constant(t300rs, state); - case FF_RAMP: - return t300rs_update_ramp(t300rs, state); - case FF_SPRING: - return t300rs_update_spring(t300rs, state); - case FF_DAMPER: - case FF_FRICTION: - case FF_INERTIA: - return t300rs_update_damper(t300rs, state); - case FF_PERIODIC: - return t300rs_update_periodic(t300rs, state); - default: - hid_err(t300rs->hdev, "invalid effect type: %x", state->effect.type); - return -1; - } -} - -int t300rs_upload_effect(void *data, struct tmff2_effect_state *state) -{ - struct t300rs_device_entry *t300rs = data; - 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_switch_mode(void *data, uint16_t mode) -{ - struct t300rs_device_entry *t300rs = data; - if (!t300rs) - return -ENODEV; - - if(t300rs->mode == mode) /* already in specified mode */ - return 0; - - if (mode == 0) - /* go to normal mode */ - usb_control_msg(t300rs->usbdev, - usb_sndctrlpipe(t300rs->usbdev, 0), - 83, 0x41, 5, 0, 0, 0, - USB_CTRL_SET_TIMEOUT - ); - else if (mode == 1) - /* go to advanced mode */ - usb_control_msg(t300rs->usbdev, - usb_sndctrlpipe(t300rs->usbdev, 0), - 83, 0x41, 3, 0, 0, 0, - USB_CTRL_SET_TIMEOUT - ); - else - hid_warn(t300rs->hdev, "mode %i not supported\n", mode); - - - return 0; -} - -static struct t300rs_alt_modes { - char *id; - char *label; - uint16_t mode; -} t300rs_modes[] = { - {"base", "T300RS base", 0}, - {"F1", "T300RS with F1 wheel attachment", 1} -}; - -static ssize_t t300rs_alt_mode_show(void *data, char *buf) -{ - struct t300rs_device_entry *t300rs = data; - ssize_t count = 0; - int i; - if (!t300rs) - return -ENODEV; - - if (t300rs->attachment != T300RS_F1_ATTACHMENT) - /* we only support one base mode */ - return scnprintf(buf, PAGE_SIZE, "%s: %s *\n", - t300rs_modes[0].id, t300rs_modes[0].label); - - for (i = 0; i < ARRAY_SIZE(t300rs_modes); ++i) { - count += scnprintf(buf + count, PAGE_SIZE - count, "%s: %s", - t300rs_modes[i].id, t300rs_modes[i].label); - - if (count >= PAGE_SIZE - 1) - return count; - - if (t300rs_modes[i].mode == t300rs->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; -} - -static ssize_t t300rs_alt_mode_store(void *data, const char *buf, size_t count) -{ - struct t300rs_device_entry *t300rs = data; - int i, len, mode_len; - char *lbuf; - if (!t300rs) - return -ENODEV; - - if (t300rs->attachment != T300RS_F1_ATTACHMENT) - return count; /* don't do anything */ - - lbuf = kasprintf(GFP_KERNEL, "%s", buf); - if (!lbuf) - return -ENOMEM; - - len = strlen(buf); - for (i = 0; i < ARRAY_SIZE(t300rs_modes); ++i) { - mode_len = strlen(t300rs_modes[i].id); - if (mode_len > len) - continue; - - if (strncmp(lbuf, t300rs_modes[i].id, mode_len) == 0) { - t300rs_switch_mode(data, t300rs_modes[i].mode); - break; - } - } - - kfree(lbuf); - return count; -} - -int t300rs_set_autocenter(void *data, uint16_t value) -{ - struct t300rs_device_entry *t300rs = data; - struct __packed t300rs_packet_autocenter { - struct t300rs_setup_header header; - uint16_t value; - } *autocenter_packet; - int ret; - - if (!t300rs) - return -ENODEV; - - /* TODO: this should probably also use a separately allocated buffer? */ - autocenter_packet = (struct t300rs_packet_autocenter *)t300rs->send_buffer; - - autocenter_packet->header.cmd = 0x08; - autocenter_packet->header.code = 0x04; - autocenter_packet->value = cpu_to_le16(0x01); - - if ((ret = t300rs_send_int(t300rs))) { - hid_err(t300rs->hdev, "failed setting autocenter"); - return ret; - } - - autocenter_packet->header.cmd = 0x08; - autocenter_packet->header.code = 0x03; - - autocenter_packet->value = cpu_to_le16(value); - - if ((ret = t300rs_send_int(t300rs))) - hid_err(t300rs->hdev, "failed setting autocenter"); - - return ret; -} - -int t300rs_set_gain(void *data, uint16_t gain) -{ - struct t300rs_device_entry *t300rs = data; - struct __packed t300rs_packet_gain { - struct t300rs_setup_header header; - } *gain_packet; - int ret; - - if (!t300rs) - return -ENODEV; - - gain_packet = (struct t300rs_packet_gain *)t300rs->send_buffer; - gain_packet->header.cmd = 0x02; - gain_packet->header.code = (gain >> 8) & 0xff; - - if ((ret = t300rs_send_int(t300rs))) - hid_err(t300rs->hdev, "failed setting gain: %i\n", ret); - - return ret; -} - -int t300rs_set_range(void *data, uint16_t value) -{ - struct t300rs_device_entry *t300rs = data; - /* it's important that we don't use t300rs->send_buffer, as range can be - * set from outside of the FFB environment, and we don't want to - * accidentally overwrite any data. */ - u8 *send_buffer = kzalloc(t300rs->buffer_length, GFP_KERNEL); - uint16_t scaled_value; - int ret; - - if (value < 40) { - hid_info(t300rs->hdev, "value %i too small, clamping to 40\n", value); - value = 40; - } - - if (value > 1080) { - hid_info(t300rs->hdev, "value %i too large, clamping to 1080\n", value); - value = 1080; - } - - if (!send_buffer) { - ret = -EINVAL; - hid_err(t300rs->hdev, "could not allocate send_buffer\n"); - goto err; - } - - scaled_value = value * 0x3c; - send_buffer[0] = 0x08; - send_buffer[1] = 0x11; - send_buffer[2] = scaled_value & 0xff; - send_buffer[3] = scaled_value >> 8; - - if ((ret = t300rs_send_buf(t300rs, send_buffer, t300rs->buffer_length))) - hid_warn(t300rs->hdev, "failed setting range\n"); - - /* since everythin went OK, update the current range */ - range = value; -err: - kfree(send_buffer); - return ret; -} - -static int t300rs_send_open(struct t300rs_device_entry *t300rs) -{ - struct __packed t300rs_packet_open { - struct t300rs_setup_header header; - } *open_packet; - - open_packet = (struct t300rs_packet_open *)t300rs->send_buffer; - open_packet->header.cmd = 0x01; - open_packet->header.code = 0x05; - - return t300rs_send_int(t300rs); -} - -static int t300rs_send_close(struct t300rs_device_entry *t300rs) -{ - struct __packed t300rs_packet_open { - struct t300rs_setup_header header; - } *open_packet; - - open_packet = (struct t300rs_packet_open *)t300rs->send_buffer; - open_packet->header.cmd = 0x01; - - return t300rs_send_int(t300rs); -} - -int t300rs_open(void *data, int open_mode) -{ - struct t300rs_device_entry *t300rs = data; - if (!t300rs) - return -ENODEV; - - if (open_mode && t300rs_send_open(t300rs)) - hid_warn(t300rs->hdev, "failed sending open command\n"); - - return t300rs->open(t300rs->input_dev); -} - -int t300rs_close(void *data, int open_mode) -{ - struct t300rs_device_entry *t300rs = data; - int ret; - - if (!t300rs) - return -ENODEV; - - if (open_mode && (ret = t300rs_send_close(t300rs))) - hid_warn(t300rs->hdev, "failed sending close command\n"); - - t300rs->close(t300rs->input_dev); - return ret; -} - -static int t300rs_check_firmware(struct t300rs_device_entry *t300rs) -{ - int ret; - struct t300rs_fw_response *fw_response = - kzalloc(sizeof(struct t300rs_fw_response), GFP_KERNEL); - - if (!fw_response) { - hid_err(t300rs->hdev, "could not allocate fw_response\n"); - return -ENOMEM; - } - - /* Fetch firmware version */ - ret = usb_control_msg(t300rs->usbdev, - usb_rcvctrlpipe(t300rs->usbdev, 0), - t300rs_fw_request.bRequest, - t300rs_fw_request.bRequestType, - t300rs_fw_request.wValue, - t300rs_fw_request.wIndex, - fw_response, - t300rs_fw_request.wLength, - USB_CTRL_SET_TIMEOUT - ); - - if (ret < 0) { - hid_err(t300rs->hdev, "could not fetch firmware version: %i\n", ret); - goto out; - } - - /* Educated guess */ - if (fw_response->fw_version < 31 && ret >= 0) { - hid_err(t300rs->hdev, - "firmware version %i is too old, please update.\n", - fw_response->fw_version - ); - - hid_info(t300rs->hdev, "note: this has to be done through Windows.\n"); - - ret = -EINVAL; - goto out; - } - - /* everything OK */ - ret = 0; - -out: - kfree(fw_response); - return ret; -} - -static int t300rs_get_attachment(struct t300rs_device_entry *t300rs) -{ - /* taken directly from hid_tminit */ - struct __packed t300rs_attachment_response - { - uint16_t type; - - union { - struct __packed { - uint16_t field0; - uint16_t field1; - uint8_t attachment; - uint8_t model; - uint16_t field2; - uint16_t field3; - uint16_t field4; - uint16_t field5; - } a; - - struct __packed { - uint16_t field0; - uint16_t field1; - uint8_t attachment; - uint8_t model; - } b; - }; - } *response = kzalloc(GFP_KERNEL, sizeof(struct t300rs_attachment_response)); - struct usb_ctrlrequest t300rs_attachment_rq = { - .bRequestType = 0xc1, - .bRequest = 73, - .wValue = 0, - .wIndex = 0, - .wLength = sizeof(struct t300rs_attachment_response) - }; - int ret, attachment; - if (!response) - return -ENODEV; - - ret = usb_control_msg(t300rs->usbdev, - usb_rcvctrlpipe(t300rs->usbdev, 0), - t300rs_attachment_rq.bRequest, - t300rs_attachment_rq.bRequestType, - t300rs_attachment_rq.wValue, - t300rs_attachment_rq.wIndex, - response, - sizeof(struct t300rs_attachment_response), - USB_CTRL_SET_TIMEOUT - ); - - if (ret < 0) { - hid_err(t300rs->hdev, "could not fetch attachment: %i\n", ret); - goto out; - } - - if (response->type == cpu_to_le16(0x49)) { - attachment = response->a.attachment; - } else if (response->type == cpu_to_le16(0x47)) { - attachment = response->b.attachment; - } else { - hid_err(t300rs->hdev, "unknown packet type %hx\n, please contact a maintainer", - response->type); - ret = -EINVAL; - goto out; - } - - kfree(response); - return attachment; - -out: - kfree(response); - return ret; -} - -static int t300rs_wheel_init(struct tmff2_device_entry *tmff2, int open_mode) -{ - struct t300rs_device_entry *t300rs = kzalloc(sizeof(struct t300rs_device_entry), GFP_KERNEL); - struct list_head *report_list; - int ret; - - if (!t300rs) { - ret = -ENOMEM; - goto t300rs_err; - } - - t300rs->hdev = tmff2->hdev; - t300rs->input_dev = tmff2->input_dev; - t300rs->usbdev = to_usb_device(tmff2->hdev->dev.parent->parent); - - if(t300rs->hdev->product == TMT300RS_PS4_NORM_ID) - t300rs->buffer_length = T300RS_PS4_BUFFER_LENGTH; - else - t300rs->buffer_length = T300RS_NORM_BUFFER_LENGTH; - - t300rs->send_buffer = kzalloc(t300rs->buffer_length, GFP_KERNEL); - if (!t300rs->send_buffer) { - ret = -ENOMEM; - goto send_err; - } - - if ((ret = t300rs_check_firmware(t300rs))) - goto firmware_err; - - report_list = &t300rs->hdev->report_enum[HID_OUTPUT_REPORT].report_list; - - /* because we set the rdesc, we know exactly which report and field to use */ - t300rs->report = list_entry(report_list->next, struct hid_report, list); - t300rs->ff_field = t300rs->report->field[0]; - - t300rs->open = t300rs->input_dev->open; - t300rs->close = t300rs->input_dev->close; - - /* TODO: PS4 advanced mode? */ - alt_mode = (t300rs->mode = (t300rs->hdev->product == TMT300RS_PS3_ADV_ID)); - if ((t300rs->attachment = t300rs_get_attachment(t300rs)) < 0) - t300rs->attachment = T300RS_DEFAULT_ATTACHMENT; - - /* everythin went OK */ - tmff2->data = t300rs; - tmff2->params = t300rs_params; - tmff2->max_effects = T300RS_MAX_EFFECTS; - memcpy(tmff2->supported_effects, t300rs_effects, sizeof(t300rs_effects)); - - if (!open_mode) - t300rs_send_open(t300rs); - - hid_info(t300rs->hdev, "force feedback for T300RS\n"); - return 0; - -firmware_err: - kfree(t300rs->send_buffer); -send_err: - kfree(t300rs); -t300rs_err: - hid_err(tmff2->hdev, "failed initializing T300RS\n"); - return ret; -} - -static int t300rs_wheel_destroy(void *data) -{ - struct t300rs_device_entry *t300rs = data; - if (!t300rs) - return -ENODEV; - - kfree(t300rs->send_buffer); - kfree(t300rs); - return 0; -} - -static __u8 *t300rs_wheel_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) -{ - switch (hdev->product) { - case TMT300RS_PS3_NORM_ID: - /* normal PS3 mode */ - rdesc = t300rs_rdesc_nrm_fixed; - *rsize = sizeof(t300rs_rdesc_nrm_fixed); - break; - - case TMT300RS_PS4_NORM_ID: - /* PS4 normal mode */ - rdesc = t300rs_rdesc_ps4_fixed; - *rsize = sizeof(t300rs_rdesc_ps4_fixed); - break; - - case TMT300RS_PS3_ADV_ID: - /* PS3 advanced mode */ - rdesc = t300rs_rdesc_adv_fixed; - *rsize = sizeof(t300rs_rdesc_adv_fixed); - break; - } - - return rdesc; -} - -int t300rs_populate_api(struct tmff2_device_entry *tmff2) -{ - /* set callbacks */ - tmff2->play_effect = t300rs_play_effect; - tmff2->upload_effect = t300rs_upload_effect; - tmff2->update_effect = t300rs_update_effect; - tmff2->stop_effect = t300rs_stop_effect; - - tmff2->wheel_init = t300rs_wheel_init; - tmff2->wheel_destroy = t300rs_wheel_destroy; - - tmff2->open = t300rs_open; - tmff2->close = t300rs_close; - tmff2->set_gain = t300rs_set_gain; - tmff2->set_range = t300rs_set_range; - tmff2->switch_mode = t300rs_switch_mode; - tmff2->alt_mode_show = t300rs_alt_mode_show; - tmff2->alt_mode_store = t300rs_alt_mode_store; - tmff2->set_autocenter = t300rs_set_autocenter; - tmff2->wheel_fixup = t300rs_wheel_fixup; - - return 0; -} diff --git a/hid-tmt300rs.h b/hid-tmt300rs.h deleted file mode 100644 index 9045341..0000000 --- a/hid-tmt300rs.h +++ /dev/null @@ -1,9 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef __HID_TMT300RS_H -#define __HID_TMT300RS_H - -#include "hid-tmff2.h" - -int t300rs_populate_api(struct tmff2_device_entry *tmff2); - -#endif /* __HID_TM300RS_H */ diff --git a/hid-tmtx.c b/hid-tmtx.c deleted file mode 100644 index 47d59ad..0000000 --- a/hid-tmtx.c +++ /dev/null @@ -1,323 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -#include -#include -#include "hid-tmff2.h" - -#define T248_MAX_EFFECTS 16 -#define T248_BUFFER_LENGTH 63 - -static const u8 setup_0[64] = { 0x42, 0x01 }; -static const u8 setup_1[64] = { 0x0a, 0x04, 0x90, 0x03 }; -static const u8 setup_2[64] = { 0x0a, 0x04, 0x00, 0x0c }; -static const u8 setup_3[64] = { 0x0a, 0x04, 0x12, 0x10 }; -static const u8 setup_4[64] = { 0x0a, 0x04, 0x00, 0x06 }; -static const u8 setup_5[64] = { 0x0a, 0x04, 0x00, 0x0e }; -static const u8 setup_6[64] = { 0x0a, 0x04, 0x00, 0x0e, 0x01 }; -static const u8 *const setup_arr[] = { setup_0, setup_1, setup_2, setup_3, setup_4, setup_5, setup_6 }; -static const unsigned int setup_arr_sizes[] = { - ARRAY_SIZE(setup_0), - ARRAY_SIZE(setup_1), - ARRAY_SIZE(setup_2), - ARRAY_SIZE(setup_3), - ARRAY_SIZE(setup_4), - ARRAY_SIZE(setup_5), - ARRAY_SIZE(setup_6) -}; - -static const unsigned long tx_params = - PARAM_SPRING_LEVEL - | PARAM_DAMPER_LEVEL - | PARAM_FRICTION_LEVEL - | PARAM_RANGE - | PARAM_GAIN - ; - -static const signed short tx_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 tx_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, 0x35, /* Usage (Rz) (Brake) */ - 0x26, 0xff, 0x03, /* Logical maximum (1023) */ - 0x46, 0xff, 0x03, /* Physical maximum (1023) */ - 0x81, 0x02, /* Input (Variable, Absolute) */ - 0x09, 0x32, /* Usage (Z) (Gas) */ - 0x81, 0x02, /* Input (Variable, Absolute) */ - 0x09, 0x31, /* Usage (Y) (Clutch) */ - 0x81, 0x02, /* Input (Variable, Absolute) */ - 0x81, 0x03, /* Input (Variable, Absolute, Constant) */ - 0x05, 0x09, /* Usage page (Button) */ - 0x19, 0x01, /* Usage minimum (1) */ - 0x29, 0x0d, /* Usage maximum (13) */ - 0x25, 0x01, /* Logical maximum (1) */ - 0x45, 0x01, /* Physical maximum (1) */ - 0x75, 0x01, /* Report size (1) */ - 0x95, 0x0d, /* Report count (13) */ - 0x81, 0x02, /* Input (Variable, Absolute) */ - 0x75, 0x0b, /* Report size (13) */ - 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, /* Unit (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, 0x7f, /* Logical maximum (32767) */ - 0x15, 0x00, /* Logical minimum (0) */ - 0x46, 0xff, 0x7f, /* Physical maximum (32767) */ - 0x36, 0x00, 0x80, /* Physical minimum (-32768) */ - 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 tx_interrupts(struct t300rs_device_entry *tx) -{ - u8 *send_buf = kmalloc(256, GFP_KERNEL); - struct usb_interface *usbif = to_usb_interface(tx->hdev->dev.parent); - struct usb_host_endpoint *ep; - int ret, trans, b_ep, i; - - if (!send_buf) { - hid_err(tx->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(tx->usbdev, - usb_sndintpipe(tx->usbdev, b_ep), - send_buf, setup_arr_sizes[i], - &trans, - USB_CTRL_SET_TIMEOUT); - - if (ret) { - hid_err(tx->hdev, "setup data couldn't be sent\n"); - goto err; - } - } - -err: - kfree(send_buf); - return ret; -} - -int tx_wheel_destroy(void *data) -{ - struct t300rs_device_entry *t300rs = data; - - if (!t300rs) - return -ENODEV; - - kfree(t300rs->send_buffer); - kfree(t300rs); - return 0; -} - -int tx_set_range(void *data, uint16_t value) -{ - struct t300rs_device_entry *tx = data; - - if (value < 140) { - hid_info(tx->hdev, "value %i too small, clamping to 140\n", value); - value = 140; - } - - if (value > 900) { - hid_info(tx->hdev, "value %i too large, clamping to 900\n", value); - value = 900; - } - - return t300rs_set_range(data, value); -} - -static int tx_send_open(struct t300rs_device_entry *tx) -{ - int r1, r2; - tx->send_buffer[0] = 0x01; - tx->send_buffer[1] = 0x04; - if ((r1 = t300rs_send_int(tx))) - return r1; - - tx->send_buffer[0] = 0x01; - tx->send_buffer[1] = 0x05; - if ((r2 = t300rs_send_int(tx))) - return r2; - - return 0; -} - -static int tx_open(void *data, int open_mode) -{ - struct t300rs_device_entry *tx = data; - - if (!tx) - return -ENODEV; - - if (open_mode) - tx_send_open(tx); - - return tx->open(tx->input_dev); -} - -static int tx_send_close(struct t300rs_device_entry *tx) -{ - int r1, r2; - tx->send_buffer[0] = 0x01; - tx->send_buffer[1] = 0x05; - if ((r1 = t300rs_send_int(tx))) - return r1; - - tx->send_buffer[0] = 0x01; - tx->send_buffer[1] = 0x00; - if ((r2 = t300rs_send_int(tx))) - return r2; - - return 0; -} - -static int tx_close(void *data, int open_mode) -{ - struct t300rs_device_entry *tx = data; - - if (!tx) - return -ENODEV; - - if (open_mode) - tx_send_close(tx); - - tx->close(tx->input_dev); - return 0; -} - -int tx_wheel_init(struct tmff2_device_entry *tmff2, int open_mode) -{ - struct t300rs_device_entry *tx = kzalloc(sizeof(struct t300rs_device_entry), GFP_KERNEL); - struct list_head *report_list; - int ret; - - - if (!tx) { - ret = -ENOMEM; - goto tx_err; - } - - tx->hdev = tmff2->hdev; - tx->input_dev = tmff2->input_dev; - tx->usbdev = to_usb_device(tmff2->hdev->dev.parent->parent); - tx->buffer_length = T248_BUFFER_LENGTH; - - tx->send_buffer = kzalloc(tx->buffer_length, GFP_KERNEL); - if (!tx->send_buffer) { - ret = -ENOMEM; - goto send_err; - } - - report_list = &tx->hdev->report_enum[HID_OUTPUT_REPORT].report_list; - tx->report = list_entry(report_list->next, struct hid_report, list); - tx->ff_field = tx->report->field[0]; - - tx->open = tx->input_dev->open; - tx->close = tx->input_dev->close; - - if ((ret = tx_interrupts(tx))) - goto interrupt_err; - - /* everything went OK */ - tmff2->data = tx; - tmff2->params = tx_params; - tmff2->max_effects = T248_MAX_EFFECTS; - memcpy(tmff2->supported_effects, tx_effects, sizeof(tx_effects)); - - if (!open_mode) - tx_send_open(tx); - - hid_info(tx->hdev, "force feedback for TX\n"); - return 0; - -interrupt_err: -send_err: - kfree(tx); -tx_err: - hid_err(tmff2->hdev, "failed initializing TX\n"); - return ret; -} - -static __u8 *tx_wheel_fixup(struct hid_device *hdev, __u8 *rdesc, - unsigned int *rsize) -{ - rdesc = tx_pc_rdesc_fixed; - *rsize = sizeof(tx_pc_rdesc_fixed); - return rdesc; -} - -int tx_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; - /* TX only has 900 degree range, instead of T300RS 1080 */ - tmff2->set_range = tx_set_range; - tmff2->wheel_fixup = tx_wheel_fixup; - - tmff2->open = tx_open; - tmff2->close = tx_close; - - tmff2->wheel_init = tx_wheel_init; - tmff2->wheel_destroy = tx_wheel_destroy; - - return 0; -} diff --git a/ps4-input.txt b/ps4-input.txt deleted file mode 100644 index 9286c7e..0000000 --- a/ps4-input.txt +++ /dev/null @@ -1,54 +0,0 @@ -1 ff00.0021 = 0 -2 ff00.0021 = 0 -3 ff00.0021 = 0 -4 ff00.0021 = 0 -5 ff00.0021 = 0 -6 ff00.0021 = 0 -7 ff00.0021 = 0 -8 ff00.0021 = 0 -9 ff00.0021 = 0 -10 ff00.0021 = 0 -11 ff00.0021 = 0 -12 ff00.0021 = 0 -13 ff00.0021 = 0 -14 ff00.0021 = 0 -15 ff00.0021 = 0 -16 ff00.0021 = 0 -17 ff00.0021 = 0 -18 ff00.0021 = 0 -19 ff00.0021 = 0 -20 ff00.0021 = 0 -21 ff00.0021 = 0 -22 ff00.0021 = 0 -23 ff00.0021 = 0 -24 ff00.0021 = 0 -25 ff00.0021 = 0 -26 ff00.0021 = 0 -27 ff00.0021 = 0 -28 ff00.0021 = 0 -29 ff00.0021 = 0 -30 ff00.0021 = 0 -31 ff00.0021 = 0 -32 ff00.0021 = 0 -33 ff00.0021 = 0 -34 ff00.0021 = 226 /* wheel */ -35 ff00.0021 = 125 /* wheel */ -36 ff00.0021 = 255 /* gas */ -37 ff00.0021 = 255 /* gas */ -38 ff00.0021 = 255 /* brake */ -39 ff00.0021 = 255 /* brake */ -40 ff00.0021 = 255 /* clutch */ -41 ff00.0021 = 255 /* clutch */ -42 ff00.0021 = 0 -43 ff00.0021 = 255 -44 ff00.0021 = 255 -45 ff00.0021 = 0 -46 ff00.0021 = 0 -47 ff00.0021 = 0 -48 ff00.0021 = 0 -49 ff00.0021 = 0 -50 ff00.0021 = 0 -51 ff00.0021 = 0 -52 ff00.0021 = 0 -53 ff00.0021 = 0 -54 ff00.0021 = 0 diff --git a/src/Kbuild b/src/Kbuild new file mode 100644 index 0000000..b8541e0 --- /dev/null +++ b/src/Kbuild @@ -0,0 +1,2 @@ +obj-m := hid-tmff-new.o +hid-tmff-new-y := hid-tmff2.o hid-tmt300rs/hid-tmt300rs.o hid-tmt248/hid-tmt248.o hid-tmtx/hid-tmtx.o diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..5e2ab40 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,16 @@ +KDIR ?= /lib/modules/$(shell uname -r)/build + +all: hid-tminit + $(MAKE) -C $(KDIR) M=$(shell pwd) modules + +install: hid-tminit + $(MAKE) -C $(KDIR) M=$(shell pwd) modules_install + depmod -A + +clean: hid-tminit + $(MAKE) -C $(KDIR) M=$(shell pwd) clean + + +.PHONY: hid-tminit +hid-tminit: + $(MAKE) -C hid-tminit KDIR="$(KDIR)" $(MAKECMDGOALS) diff --git a/src/hid-tmff2.c b/src/hid-tmff2.c new file mode 100644 index 0000000..44df1e3 --- /dev/null +++ b/src/hid-tmff2.c @@ -0,0 +1,749 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include +#include "hid-tmff2.h" + + +int open_mode = 1; +module_param(open_mode, int, 0660); +MODULE_PARM_DESC(open_mode, + "Whether to send mode change commands on open/close"); + +int timer_msecs = DEFAULT_TIMER_PERIOD; +module_param(timer_msecs, int, 0660); +MODULE_PARM_DESC(timer_msecs, + "Timer resolution in msecs"); + +/* should these be removed and just rely on /sys? */ +int spring_level = 30; +module_param(spring_level, int, 0); +MODULE_PARM_DESC(spring_level, + "Level of spring force (0-100), as per Oversteer standards"); + +int damper_level = 30; +module_param(damper_level, int, 0); +MODULE_PARM_DESC(damper_level, + "Level of damper force (0-100), as per Oversteer standards"); + +int friction_level = 30; +module_param(friction_level, int, 0); +MODULE_PARM_DESC(friction_level, + "Level of friction force (0-100), as per Oversteer standards"); + +int range = 900; +module_param(range, int, 0); +MODULE_PARM_DESC(range, + "Range of wheel, depends on the wheel. Invalid values are ignored"); + +int alt_mode = 0; +module_param(alt_mode, int, 0); +MODULE_PARM_DESC(alt_mode, + "Alternate mode, eg. F1 mode"); + +#define GAIN_MAX 65535 +int gain = 40000; +module_param(gain, int, 0); +MODULE_PARM_DESC(gain, + "Level of gain (0-65535)"); + +static spinlock_t lock; +static unsigned long lock_flags; + +static struct tmff2_device_entry *tmff2_from_hdev(struct hid_device *hdev) +{ + struct tmff2_device_entry *tmff2; + spin_lock_irqsave(&lock, lock_flags); + + if (!(tmff2 = hid_get_drvdata(hdev))) + dev_err(&hdev->dev, "hdev private data not found\n"); + + spin_unlock_irqrestore(&lock, lock_flags); + + return tmff2; +} + +static struct tmff2_device_entry *tmff2_from_input(struct input_dev *input_dev) +{ + struct hid_device *hdev; + spin_lock_irqsave(&lock, lock_flags); + + if (!(hdev = input_get_drvdata(input_dev))) + dev_err(&input_dev->dev, "input_dev private data not found\n"); + + spin_unlock_irqrestore(&lock, lock_flags); + + return tmff2_from_hdev(hdev); +} + +static ssize_t spring_level_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int value; + int ret; + + ret = kstrtouint(buf, 0, &value); + if (ret) { + dev_err(dev, "kstrtouint failed at spring_level_store: %i", ret); + return ret; + } + + if (value > 100) { + dev_info(dev, "value %i larger than max 100, clamping to 100.\n", value); + value = 100; + } + + spring_level = value; + + return count; +} + +static ssize_t spring_level_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + return scnprintf(buf, PAGE_SIZE, "%u\n", spring_level); +} +static DEVICE_ATTR_RW(spring_level); + +static ssize_t damper_level_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int value; + int ret; + + + ret = kstrtouint(buf, 0, &value); + if (ret) { + dev_err(dev, "kstrtouint failed at damper_level_store: %i", ret); + return ret; + } + + if (value > 100) { + dev_info(dev, "value %i larger than max 100, clamping to 100.\n", value); + value = 100; + } + + damper_level = value; + + return count; +} + +static ssize_t damper_level_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + return scnprintf(buf, PAGE_SIZE, "%u\n", damper_level); +} +static DEVICE_ATTR_RW(damper_level); + +static ssize_t friction_level_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int value; + int ret; + + + ret = kstrtouint(buf, 0, &value); + if (ret) { + dev_err(dev, "kstrtouint failed at friction_level_store: %i", ret); + return ret; + } + + if (value > 100) { + dev_info(dev, "value %i larger than max 100, clamping to 100.\n", value); + value = 100; + } + + friction_level = value; + + return count; +} + +static ssize_t friction_level_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + size_t count; + + + count = scnprintf(buf, PAGE_SIZE, "%u\n", friction_level); + + return count; +} +static DEVICE_ATTR_RW(friction_level); + +static ssize_t range_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))) { + hid_err(tmff2->hdev, "kstrtouint failed at range_store: %i", ret); + return ret; + } + + if (tmff2->set_range) { + if ((ret = tmff2->set_range(tmff2->data, value))) + return ret; + } + + return count; +} + +static ssize_t range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + return scnprintf(buf, PAGE_SIZE, "%u\n", range); +} +static DEVICE_ATTR_RW(range); + +static ssize_t alternate_modes_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)); + + if (!tmff2) + return -ENODEV; + + if (tmff2->alt_mode_store) + return tmff2->alt_mode_store(tmff2->data, buf, count); + + return 0; +} + +static ssize_t alternate_modes_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->alt_mode_show) + return tmff2->alt_mode_show(tmff2->data, buf); + + return 0; +} +static DEVICE_ATTR_RW(alternate_modes); + +static ssize_t gain_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 gain_store: %i", ret); + return ret; + } + + gain = value; + if (tmff2->set_gain) /* if we can, update gain immediately */ + tmff2->set_gain(tmff2->data, (GAIN_MAX * gain) / GAIN_MAX); + + return count; +} + +static ssize_t gain_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%i\n", gain); +} +static DEVICE_ATTR_RW(gain); + +static void tmff2_set_gain(struct input_dev *dev, uint16_t value) +{ + struct tmff2_device_entry *tmff2 = tmff2_from_input(dev); + + if (!tmff2) + return; + + if (!tmff2->set_gain) { + hid_err(tmff2->hdev, "missing set_gain\n"); + return; + } + + if (tmff2->set_gain(tmff2->data, (value * gain) / GAIN_MAX)) + hid_warn(tmff2->hdev, "unable to set gain\n"); +} + +static void tmff2_set_autocenter(struct input_dev *dev, uint16_t value) +{ + struct tmff2_device_entry *tmff2 = tmff2_from_input(dev); + + if (!tmff2) + return; + + if (!tmff2->set_autocenter) { + hid_err(tmff2->hdev, "missing set_autocenter\n"); + return; + } + + if (tmff2->set_autocenter(tmff2->data, value)) + hid_warn(tmff2->hdev, "unable to set autocenter\n"); +} + +static void tmff2_work_handler(struct work_struct *w) +{ + struct delayed_work *dw = container_of(w, struct delayed_work, work); + struct tmff2_device_entry *tmff2 = container_of(dw, struct tmff2_device_entry, work); + struct tmff2_effect_state *state; + int max_count = 0, effect_id; + unsigned long time_now; + __u16 effect_length; + + + if (!tmff2) + return; + + for (effect_id = 0; effect_id < tmff2->max_effects; ++effect_id) { + spin_lock(&tmff2->lock); + + time_now = JIFFIES2MS(jiffies); + state = &tmff2->states[effect_id]; + + effect_length = state->effect.replay.length; + if (test_bit(FF_EFFECT_PLAYING, &state->flags) && effect_length) { + if ((time_now - state->start_time) >= effect_length) { + __clear_bit(FF_EFFECT_PLAYING, &state->flags); + __clear_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags); + + if (state->count) + state->count--; + + if (state->count) + __set_bit(FF_EFFECT_QUEUE_START, &state->flags); + } + } + + if (test_bit(FF_EFFECT_QUEUE_UPLOAD, &state->flags)) { + if (tmff2->upload_effect(tmff2->data, state)) { + hid_warn(tmff2->hdev, "failed uploading effect\n"); + } else { + __clear_bit(FF_EFFECT_QUEUE_UPLOAD, &state->flags); + /* if we're uploading an effect, it's bound to be the up + * to date available */ + __clear_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags); + } + } + + if (test_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags)) { + if (tmff2->update_effect(tmff2->data, state)) + hid_warn(tmff2->hdev, "failed updating effect\n"); + else + __clear_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags); + } + + if (test_bit(FF_EFFECT_QUEUE_START, &state->flags)) { + if (tmff2->play_effect(tmff2->data, state)) { + hid_warn(tmff2->hdev, "failed starting effect\n"); + } else { + __clear_bit(FF_EFFECT_QUEUE_START, &state->flags); + __set_bit(FF_EFFECT_PLAYING, &state->flags); + } + + } + + if (test_bit(FF_EFFECT_QUEUE_STOP, &state->flags)) { + if (tmff2->stop_effect(tmff2->data, state)) { + hid_warn(tmff2->hdev, "failed stopping effect\n"); + } else { + __clear_bit(FF_EFFECT_PLAYING, &state->flags); + __clear_bit(FF_EFFECT_QUEUE_STOP, &state->flags); + } + } + + if (state->count > max_count) + max_count = state->count; + + spin_unlock(&tmff2->lock); + } + + if (max_count && tmff2->allow_scheduling) + schedule_delayed_work(&tmff2->work, msecs_to_jiffies(timer_msecs)); +} + +static int tmff2_upload(struct input_dev *dev, + struct ff_effect *effect, struct ff_effect *old) +{ + struct tmff2_effect_state *state; + struct tmff2_device_entry *tmff2 = tmff2_from_input(dev); + + if (!tmff2) + return -ENODEV; + + if (effect->type == FF_PERIODIC && effect->u.periodic.period == 0) + return -EINVAL; + + state = &tmff2->states[effect->id]; + + spin_lock(&tmff2->lock); + + state->effect = *effect; + + if (old) { + if (!test_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags)) + state->old = *old; + + __set_bit(FF_EFFECT_QUEUE_UPDATE, &state->flags); + } else { + __set_bit(FF_EFFECT_QUEUE_UPLOAD, &state->flags); + } + + spin_unlock(&tmff2->lock); + return 0; +} + +static int tmff2_play(struct input_dev *dev, int effect_id, int value) +{ + struct tmff2_effect_state *state; + struct tmff2_device_entry *tmff2 = tmff2_from_input(dev); + + if (!tmff2) + return -ENODEV; + + state = &tmff2->states[effect_id]; + if (!state) + return 0; + + spin_lock(&tmff2->lock); + if (value > 0) { + state->count = value; + state->start_time = JIFFIES2MS(jiffies); + __set_bit(FF_EFFECT_QUEUE_START, &state->flags); + __clear_bit(FF_EFFECT_QUEUE_STOP, &state->flags); + } else { + __set_bit(FF_EFFECT_QUEUE_STOP, &state->flags); + __clear_bit(FF_EFFECT_QUEUE_START, &state->flags); + } + + spin_unlock(&tmff2->lock); + + if (!delayed_work_pending(&tmff2->work) && tmff2->allow_scheduling) + schedule_delayed_work(&tmff2->work, 0); + + return 0; +} + +static int tmff2_open(struct input_dev *dev) +{ + struct tmff2_device_entry *tmff2 = tmff2_from_input(dev); + + if (!tmff2) + return -ENODEV; + + if (tmff2->open) + return tmff2->open(tmff2->data, open_mode); + + hid_err(tmff2->hdev, "no open callback set\n"); + return -EINVAL; +} + +static void tmff2_close(struct input_dev *dev) +{ + struct tmff2_device_entry *tmff2 = tmff2_from_input(dev); + + if (!tmff2) + return; + + /* since we're closing the device, no need to continue feeding it new data */ + cancel_delayed_work_sync(&tmff2->work); + + if (tmff2->close) { + tmff2->close(tmff2->data, open_mode); + return; + } + + hid_err(tmff2->hdev, "no close callback set\n"); +} + +static int tmff2_create_files(struct tmff2_device_entry *tmff2) +{ + struct device *dev = &tmff2->hdev->dev; + int ret; + + + /* could use short circuiting but this is more explicit */ + if (tmff2->params & PARAM_GAIN) { + if ((ret = device_create_file(dev, &dev_attr_gain))) { + hid_err(tmff2->hdev, "unable to create sysfs for gain\n"); + goto gain_err; + } + } + + if (tmff2->params & PARAM_ALT_MODE) { + if ((ret = device_create_file(dev, &dev_attr_alternate_modes))) { + hid_err(tmff2->hdev, "unable to create sysfs for alternate_modes\n"); + goto alt_err; + } + } + + if (tmff2->params & PARAM_RANGE) { + if ((ret = device_create_file(dev, &dev_attr_range))) { + hid_warn(tmff2->hdev, "unable to create sysfs for range\n"); + goto range_err; + } + } + + if (tmff2->params & PARAM_SPRING_LEVEL) { + if ((ret = device_create_file(dev, &dev_attr_spring_level))) { + hid_warn(tmff2->hdev, "unable to create sysfs for spring_level\n"); + goto spring_err; + } + } + + if (tmff2->params & PARAM_DAMPER_LEVEL) { + if ((ret = device_create_file(dev, &dev_attr_damper_level))) { + hid_warn(tmff2->hdev, "unable to create sysfs for damper_level\n"); + goto damper_err; + } + } + + if (tmff2->params & PARAM_FRICTION_LEVEL) { + if ((ret = device_create_file(dev, &dev_attr_friction_level))) { + hid_warn(tmff2->hdev, "unable to create sysfs for friction_level\n"); + goto friction_err; + } + } + + return 0; + +friction_err: + device_remove_file(dev, &dev_attr_damper_level); +damper_err: + device_remove_file(dev, &dev_attr_spring_level); +spring_err: + device_remove_file(dev, &dev_attr_range); +range_err: + device_remove_file(dev, &dev_attr_alternate_modes); +alt_err: + device_remove_file(dev, &dev_attr_gain); +gain_err: + return ret; +} + +static int tmff2_wheel_init(struct tmff2_device_entry *tmff2) +{ + int ret, i; + struct ff_device *ff; + + spin_lock_init(&lock); + spin_lock_init(&tmff2->lock); + INIT_DELAYED_WORK(&tmff2->work, tmff2_work_handler); + + /* get parameters etc from backend */ + if ((ret = tmff2->wheel_init(tmff2, open_mode))) + goto err; + + + tmff2->states = kzalloc(sizeof(struct tmff2_effect_state) * tmff2->max_effects, + GFP_KERNEL); + + if (!tmff2->states) { + ret = -ENOMEM; + goto err; + } + + /* set supported effects into input_dev->ffbit */ + for (i = 0; tmff2->supported_effects[i] >= 0; ++i) + __set_bit(tmff2->supported_effects[i], tmff2->input_dev->ffbit); + + /* create actual ff device*/ + if ((ret = input_ff_create(tmff2->input_dev, tmff2->max_effects))) { + hid_err(tmff2->hdev, "could not create input_ff\n"); + goto err; + } + + /* set ff callbacks */ + ff = tmff2->input_dev->ff; + ff->upload = tmff2_upload; + ff->playback = tmff2_play; + + if (tmff2->open) + tmff2->input_dev->open = tmff2_open; + + if (tmff2->close) + tmff2->input_dev->close = tmff2_close; + + /* set defaults wherever possible */ + if (tmff2->set_gain) { + ff->set_gain = tmff2_set_gain; + tmff2->set_gain(tmff2->data, (GAIN_MAX * gain) / GAIN_MAX); + } + + if (tmff2->set_autocenter) + ff->set_autocenter = tmff2_set_autocenter; + + if (tmff2->set_range) + tmff2->set_range(tmff2->data, range); + + if (tmff2->switch_mode) + tmff2->switch_mode(tmff2->data, alt_mode); + + /* create files */ + if ((ret = tmff2_create_files(tmff2))) + goto err; + + tmff2->allow_scheduling = 1; + return 0; + + input_ff_destroy(tmff2->input_dev); +err: + return ret; +} + +static int tmff2_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + struct tmff2_device_entry *tmff2 = + kzalloc(sizeof(struct tmff2_device_entry), GFP_KERNEL); + + int ret; + + + if (!tmff2) { + ret = -ENOMEM; + goto oom_err; + } + + tmff2->hdev = hdev; + hid_set_drvdata(tmff2->hdev, tmff2); + + switch (tmff2->hdev->product) { + /* t300rs */ + case TMT300RS_PS3_NORM_ID: + case TMT300RS_PS3_ADV_ID: + case TMT300RS_PS4_NORM_ID: + if ((ret = t300rs_populate_api(tmff2))) + goto wheel_err; + break; + + case TMT248_PC_ID: + if ((ret = t248_populate_api(tmff2))) + goto wheel_err; + break; + + case TX_ACTIVE: + if ((ret = tx_populate_api(tmff2))) + goto wheel_err; + break; + + default: + ret = -ENODEV; + goto wheel_err; + } + + if ((ret = hid_parse(tmff2->hdev))) { + hid_err(hdev, "parse failed\n"); + goto hid_err; + } + + if ((ret = hid_hw_start(tmff2->hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF))) { + hid_err(hdev, "hw start failed\n"); + goto hid_err; + } + + tmff2->input_dev = list_entry(hdev->inputs.next, struct hid_input, list)->input; + + if ((ret = tmff2_wheel_init(tmff2))) { + hid_err(hdev, "init failed\n"); + goto init_err; + } + + return 0; + +init_err: + hid_hw_stop(hdev); +hid_err: + tmff2->wheel_destroy(tmff2->data); +wheel_err: + kfree(tmff2); +oom_err: + return ret; +} + +static __u8 *tmff2_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + struct tmff2_device_entry *tmff2 = tmff2_from_hdev(hdev); + + if (!tmff2) /* not entirely sure what the best course of action would be here */ + return rdesc; + + if (tmff2->wheel_fixup) + return tmff2->wheel_fixup(hdev, rdesc, rsize); + + return rdesc; +} + +static void tmff2_remove(struct hid_device *hdev) +{ + struct tmff2_device_entry *tmff2 = tmff2_from_hdev(hdev); + struct device *dev; + + if (!tmff2) + return; + + tmff2->allow_scheduling = 0; + cancel_delayed_work_sync(&tmff2->work); + + dev = &tmff2->hdev->dev; + if (tmff2->params & PARAM_FRICTION_LEVEL) + device_remove_file(dev, &dev_attr_friction_level); + + if (tmff2->params & PARAM_DAMPER_LEVEL) + device_remove_file(dev, &dev_attr_damper_level); + + if (tmff2->params & PARAM_SPRING_LEVEL) + device_remove_file(dev, &dev_attr_spring_level); + + if (tmff2->params & PARAM_RANGE) + device_remove_file(dev, &dev_attr_range); + + if (tmff2->params & PARAM_ALT_MODE) + device_remove_file(dev, &dev_attr_alternate_modes); + + if (tmff2->params & PARAM_GAIN) + device_remove_file(dev, &dev_attr_gain); + + hid_hw_stop(hdev); + tmff2->wheel_destroy(tmff2->data); + + kfree(tmff2->states); + kfree(tmff2); +} + +static const struct hid_device_id tmff2_devices[] = { + /* t300rs and variations */ + {HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMT300RS_PS3_NORM_ID)}, + {HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMT300RS_PS3_ADV_ID)}, + {HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMT300RS_PS4_NORM_ID)}, + /* t248 PC*/ + {HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TMT248_PC_ID)}, + /* tx */ + {HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, TX_ACTIVE)}, + + {} +}; +MODULE_DEVICE_TABLE(hid, tmff2_devices); + +static struct hid_driver tmff2_driver = { + .name = "tmff2", + .id_table = tmff2_devices, + .probe = tmff2_probe, + .remove = tmff2_remove, + .report_fixup = tmff2_report_fixup, +}; +module_hid_driver(tmff2_driver); + +MODULE_LICENSE("GPL"); diff --git a/src/hid-tmff2.h b/src/hid-tmff2.h new file mode 100644 index 0000000..c8019dd --- /dev/null +++ b/src/hid-tmff2.h @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef __HID_TMFF2_H +#define __HID_TMFF2_H + +#include +#include +#include + +extern int timer_msecs; +extern int spring_level; +extern int damper_level; +extern int friction_level; +extern int range; +extern int gain; +extern int alt_mode; + +#define USB_VENDOR_ID_THRUSTMASTER 0x044f + +/* the wheel seems to only be capable of processing a certain number of + * interrupts per second, and if this value is too low the kernel urb buffer(or + * some buffer at least) fills up. Optimally I would figure out some way to + * space out the interrupts so that they all leave at regular intervals, but + * for now this is good enough, go slow enough that everything works. + */ +#define DEFAULT_TIMER_PERIOD 8 + +#define FF_EFFECT_QUEUE_UPLOAD 0 +#define FF_EFFECT_QUEUE_START 1 +#define FF_EFFECT_QUEUE_STOP 2 +#define FF_EFFECT_QUEUE_UPDATE 3 +#define FF_EFFECT_PLAYING 4 + +#define PARAM_SPRING_LEVEL (1 << 0) +#define PARAM_DAMPER_LEVEL (1 << 1) +#define PARAM_FRICTION_LEVEL (1 << 2) +#define PARAM_RANGE (1 << 3) +#define PARAM_ALT_MODE (1 << 4) +#define PARAM_GAIN (1 << 5) + +#undef fixp_sin16 +#define fixp_sin16(v) (((v % 360) > 180) ?\ + -(fixp_sin32((v % 360) - 180) >> 16)\ + : fixp_sin32(v) >> 16) + +#define JIFFIES2MS(jiffies) ((jiffies) * 1000 / HZ) + +struct tmff2_effect_state { + struct ff_effect effect; + struct ff_effect old; + + unsigned long flags; + unsigned long count; + unsigned long start_time; +}; + +struct tmff2_device_entry { + struct hid_device *hdev; + struct input_dev *input_dev; + + /* pointer to array */ + struct tmff2_effect_state *states; + + struct delayed_work work; + + spinlock_t lock; + + int allow_scheduling; + + /* fields relevant to each actual device (T300, T150...) */ + void *data; + unsigned long params; + unsigned long max_effects; + signed short supported_effects[FF_CNT]; + + /* obligatory callbacks */ + int (*play_effect)(void *data, struct tmff2_effect_state *state); + int (*upload_effect)(void *data, struct tmff2_effect_state *state); + int (*update_effect)(void *data, struct tmff2_effect_state *state); + int (*stop_effect)(void *data, struct tmff2_effect_state *state); + + int (*wheel_init)(struct tmff2_device_entry *tmff2, int open_mode); + int (*wheel_destroy)(void *data); + + /* optional callbacks */ + int (*open)(void *data, int); + int (*close)(void *data, int); + int (*set_gain)(void *data, uint16_t gain); + int (*set_range)(void *data, uint16_t range); + /* switch_mode has 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_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); + + /* void pointers are dangerous, I know, but in this case likely the best option... */ +}; + +/* external */ +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); + +#define TMT300RS_PS3_NORM_ID 0xb66e +#define TMT300RS_PS3_ADV_ID 0xb66f +#define TMT300RS_PS4_NORM_ID 0xb66d + +#define TMT248_PC_ID 0xb696 + +#define TX_ACTIVE 0xb669 + +/* APIs to different wheel families */ +/* T248 at least uses the T300RS api, not sure if there are other wheels but that's + * why these functions are given global linkage */ + +struct t300rs_device_entry { + struct hid_device *hdev; + struct input_dev *input_dev; + struct hid_report *report; + struct hid_field *ff_field; + struct usb_device *usbdev; + + int (*open)(struct input_dev *dev); + void (*close)(struct input_dev *dev); + + int mode; + int attachment; + u8 buffer_length; + u8 *send_buffer; +}; + +int t300rs_play_effect(void *, struct tmff2_effect_state *); +int t300rs_upload_effect(void *, struct tmff2_effect_state *); +int t300rs_update_effect(void *, struct tmff2_effect_state *); +int t300rs_stop_effect(void *, struct tmff2_effect_state *); + +int t300rs_open(void *, int); +int t300rs_close(void *, int); +int t300rs_set_gain(void *, uint16_t); +int t300rs_set_range(void *, uint16_t); +int t300rs_set_autocenter(void *, uint16_t); + +int t300rs_send_buf(struct t300rs_device_entry *t300rs, u8 *send_buffer, size_t len); +int t300rs_send_int(struct t300rs_device_entry *t300rs); + +#endif diff --git a/src/hid-tminit b/src/hid-tminit new file mode 160000 index 0000000..9375f6c --- /dev/null +++ b/src/hid-tminit @@ -0,0 +1 @@ +Subproject commit 9375f6c7d83af5dd6c8b8fe30351d0f36043b20a diff --git a/src/hid-tmt248/hid-tmt248.c b/src/hid-tmt248/hid-tmt248.c new file mode 100644 index 0000000..af3aaa9 --- /dev/null +++ b/src/hid-tmt248/hid-tmt248.c @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include "../hid-tmff2.h" + +#define T248_MAX_EFFECTS 16 +#define T248_BUFFER_LENGTH 63 + +static const u8 setup_0[64] = { 0x42, 0x01 }; +static const u8 setup_1[64] = { 0x0a, 0x04, 0x90, 0x03 }; +static const u8 setup_2[64] = { 0x0a, 0x04, 0x00, 0x0c }; +static const u8 setup_3[64] = { 0x0a, 0x04, 0x12, 0x10 }; +static const u8 setup_4[64] = { 0x0a, 0x04, 0x00, 0x06 }; +static const u8 setup_5[64] = { 0x0a, 0x04, 0x00, 0x0e }; +static const u8 setup_6[64] = { 0x0a, 0x04, 0x00, 0x0e, 0x01 }; +static const u8 *const setup_arr[] = { setup_0, setup_1, setup_2, setup_3, setup_4, setup_5, setup_6 }; +static const unsigned int setup_arr_sizes[] = { + ARRAY_SIZE(setup_0), + ARRAY_SIZE(setup_1), + ARRAY_SIZE(setup_2), + ARRAY_SIZE(setup_3), + ARRAY_SIZE(setup_4), + ARRAY_SIZE(setup_5), + ARRAY_SIZE(setup_6) +}; + +static const unsigned long t248_params = + PARAM_SPRING_LEVEL + | PARAM_DAMPER_LEVEL + | PARAM_FRICTION_LEVEL + | PARAM_RANGE + | PARAM_GAIN + ; + +static const signed short t248_effects[] = { + FF_CONSTANT, + FF_RAMP, + FF_SPRING, + FF_DAMPER, + FF_FRICTION, + FF_INERTIA, + FF_PERIODIC, + FF_SINE, + FF_TRIANGLE, + FF_SQUARE, + FF_SAW_UP, + FF_SAW_DOWN, + FF_AUTOCENTER, + FF_GAIN, + -1 +}; + +/* TODO: sort through this stuff */ +static u8 t248_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 t248_interrupts(struct t300rs_device_entry *t248) +{ + u8 *send_buf = kmalloc(256, GFP_KERNEL); + struct usb_interface *usbif = to_usb_interface(t248->hdev->dev.parent); + struct usb_host_endpoint *ep; + int ret, trans, b_ep, i; + + if (!send_buf) { + hid_err(t248->hdev, "failed allocating send buffer\n"); + return -ENOMEM; + } + + ep = &usbif->cur_altsetting->endpoint[1]; + b_ep = ep->desc.bEndpointAddress; + + for (i = 0; i < ARRAY_SIZE(setup_arr); ++i) { + memcpy(send_buf, setup_arr[i], setup_arr_sizes[i]); + + ret = usb_interrupt_msg(t248->usbdev, + usb_sndintpipe(t248->usbdev, b_ep), + send_buf, setup_arr_sizes[i], + &trans, + USB_CTRL_SET_TIMEOUT); + + if (ret) { + hid_err(t248->hdev, "setup data couldn't be sent\n"); + goto err; + } + } + +err: + kfree(send_buf); + return ret; +} + +int t248_wheel_destroy(void *data) +{ + struct t300rs_device_entry *t300rs = data; + + if (!t300rs) + return -ENODEV; + + kfree(t300rs->send_buffer); + kfree(t300rs); + return 0; +} + +int t248_set_range(void *data, uint16_t value) +{ + struct t300rs_device_entry *t248 = data; + + if (value < 140) { + hid_info(t248->hdev, "value %i too small, clamping to 140\n", value); + value = 140; + } + + if (value > 900) { + hid_info(t248->hdev, "value %i too large, clamping to 900\n", value); + value = 900; + } + + return t300rs_set_range(data, value); +} + +static int t248_send_open(struct t300rs_device_entry *t248) +{ + int r1, r2; + t248->send_buffer[0] = 0x01; + t248->send_buffer[1] = 0x04; + if ((r1 = t300rs_send_int(t248))) + return r1; + + t248->send_buffer[0] = 0x01; + t248->send_buffer[1] = 0x05; + if ((r2 = t300rs_send_int(t248))) + return r2; + + return 0; +} + +static int t248_open(void *data, int open_mode) +{ + struct t300rs_device_entry *t248 = data; + + if (!t248) + return -ENODEV; + + if (open_mode) + t248_send_open(t248); + + return t248->open(t248->input_dev); +} + +static int t248_send_close(struct t300rs_device_entry *t248) +{ + int r1, r2; + t248->send_buffer[0] = 0x01; + t248->send_buffer[1] = 0x05; + if ((r1 = t300rs_send_int(t248))) + return r1; + + t248->send_buffer[0] = 0x01; + t248->send_buffer[1] = 0x00; + if ((r2 = t300rs_send_int(t248))) + return r2; + + return 0; +} + +static int t248_close(void *data, int open_mode) +{ + struct t300rs_device_entry *t248 = data; + + if (!t248) + return -ENODEV; + + if (open_mode) + t248_send_close(t248); + + t248->close(t248->input_dev); + return 0; +} + +int t248_wheel_init(struct tmff2_device_entry *tmff2, int open_mode) +{ + struct t300rs_device_entry *t248 = kzalloc(sizeof(struct t300rs_device_entry), GFP_KERNEL); + struct list_head *report_list; + int ret; + + + if (!t248) { + ret = -ENOMEM; + goto t248_err; + } + + t248->hdev = tmff2->hdev; + t248->input_dev = tmff2->input_dev; + t248->usbdev = to_usb_device(tmff2->hdev->dev.parent->parent); + t248->buffer_length = T248_BUFFER_LENGTH; + + t248->send_buffer = kzalloc(t248->buffer_length, GFP_KERNEL); + if (!t248->send_buffer) { + ret = -ENOMEM; + goto send_err; + } + + report_list = &t248->hdev->report_enum[HID_OUTPUT_REPORT].report_list; + t248->report = list_entry(report_list->next, struct hid_report, list); + t248->ff_field = t248->report->field[0]; + + t248->open = t248->input_dev->open; + t248->close = t248->input_dev->close; + + if ((ret = t248_interrupts(t248))) + goto interrupt_err; + + /* everything went OK */ + tmff2->data = t248; + tmff2->params = t248_params; + tmff2->max_effects = T248_MAX_EFFECTS; + memcpy(tmff2->supported_effects, t248_effects, sizeof(t248_effects)); + + if (!open_mode) + t248_send_open(t248); + + hid_info(t248->hdev, "force feedback for T248\n"); + return 0; + +interrupt_err: +send_err: + kfree(t248); +t248_err: + hid_err(tmff2->hdev, "failed initializing T248\n"); + return ret; +} + +static __u8 *t248_wheel_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + rdesc = t248_pc_rdesc_fixed; + *rsize = sizeof(t248_pc_rdesc_fixed); + return rdesc; +} + +int t248_populate_api(struct tmff2_device_entry *tmff2) +{ + tmff2->play_effect = t300rs_play_effect; + tmff2->upload_effect = t300rs_upload_effect; + tmff2->update_effect = t300rs_update_effect; + tmff2->stop_effect = t300rs_stop_effect; + + tmff2->set_gain = t300rs_set_gain; + tmff2->set_autocenter = t300rs_set_autocenter; + /* T248 only has 900 degree range, instead of T300RS 1080 */ + tmff2->set_range = t248_set_range; + tmff2->wheel_fixup = t248_wheel_fixup; + + tmff2->open = t248_open; + tmff2->close = t248_close; + + tmff2->wheel_init = t248_wheel_init; + tmff2->wheel_destroy = t248_wheel_destroy; + + return 0; +} diff --git a/src/hid-tmt300rs/hid-tmt300rs.c b/src/hid-tmt300rs/hid-tmt300rs.c new file mode 100644 index 0000000..20bb239 --- /dev/null +++ b/src/hid-tmt300rs/hid-tmt300rs.c @@ -0,0 +1,1714 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include "../hid-tmff2.h" + +#define T300RS_MAX_EFFECTS 16 +#define T300RS_NORM_BUFFER_LENGTH 63 +#define T300RS_PS4_BUFFER_LENGTH 31 + +#define T300RS_DEFAULT_ATTACHMENT 0x06 +#define T300RS_F1_ATTACHMENT 0x03 + +static const unsigned long t300rs_params = + PARAM_SPRING_LEVEL + | PARAM_DAMPER_LEVEL + | PARAM_FRICTION_LEVEL + | PARAM_GAIN + | PARAM_RANGE + | PARAM_ALT_MODE + ; + +static const signed short t300rs_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 __packed t300rs_fw_response { + uint8_t unused1[2]; + uint8_t fw_version; + uint8_t unused2; +}; + + +struct __packed t300rs_packet_header { + uint8_t zero1; + uint8_t id; + uint8_t code; +}; + +struct __packed t300rs_setup_header { + uint8_t cmd; + uint8_t code; +}; + +struct __packed t300rs_packet_envelope { + uint16_t attack_length; + uint16_t attack_level; + uint16_t fade_length; + uint16_t fade_level; +}; + +struct __packed t300rs_packet_timing { + uint8_t start_marker; + uint16_t duration; + uint8_t zero1[2]; + uint16_t offset; + uint8_t zero2; + uint16_t end_marker; +}; + +struct usb_ctrlrequest t300rs_fw_request = { + .bRequestType = 0xc1, + .bRequest = 86, + .wValue = 0, + .wIndex = 0, + .wLength = 8 +}; + +struct t300rs_data { + unsigned long quirks; + void *device_props; +}; + +static u8 t300rs_rdesc_nrm_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, 0x35, /* Usage (Rz) (Brake) */ + 0x26, 0xff, 0x03, /* Logical maximum (1023) */ + 0x46, 0xff, 0x03, /* Physical maximum (1023) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x09, 0x32, /* Usage (Z) (Gas) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x09, 0x31, /* Usage (Y) (Clutch) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x81, 0x03, /* Input (Variable, Absolute, Constant) */ + 0x05, 0x09, /* Usage page (Button) */ + 0x19, 0x01, /* Usage minimum (1) */ + 0x29, 0x0d, /* Usage maximum (13) */ + 0x25, 0x01, /* Logical maximum (1) */ + 0x45, 0x01, /* Physical maximum (1) */ + 0x75, 0x01, /* Report size (1) */ + 0x95, 0x0d, /* Report count (13) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x75, 0x0b, /* Report size (13) */ + 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, /* Unit (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, 0x7f, /* Logical maximum (32767) */ + 0x15, 0x00, /* Logical minimum (0) */ + 0x46, 0xff, 0x7f, /* Physical maximum (32767) */ + 0x36, 0x00, 0x80, /* Physical minimum (-32768) */ + 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 u8 t300rs_rdesc_adv_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) */ + 0x26, 0xff, 0x03, /* Logical maximum (1023) */ + 0x46, 0xff, 0x03, /* Physical maximum (1023) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x09, 0x35, /* Usage (Rz) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x09, 0x36, /* Usage (Slider) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x81, 0x03, /* Input (Variable, Absolute, Constant) ? */ + 0x05, 0x09, /* Usage page (Button) */ + 0x19, 0x01, /* Usage minimum (1) */ + 0x29, 0x19, /* Usage maximum (25) */ + 0x25, 0x01, /* Logical maximum (1) */ + 0x45, 0x01, /* Physical maximum (1) */ + 0x75, 0x01, /* Report size (1) */ + 0x95, 0x19, /* Report count (25) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x75, 0x03, /* Report size (3) */ + 0x95, 0x01, /* Report count (1) */ + 0x81, 0x03, /* Input (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, /* Unit (None) */ + 0x85, 0x60, /* Report ID (96), prev 10 */ + 0x06, 0x00, 0xff, /* Usage page (Vendor 1) */ + 0x09, 0x60, /* Usage (96), prev 10 (why?) */ + 0x75, 0x08, /* Report size (8) */ + 0x95, 0x3f, /* Report count (63) */ + 0x26, 0xff, 0x00, /* Logical maximum (255) */ + 0x46, 0xff, 0x00, /* Physical maximum (255) */ + 0x91, 0x02, /* Output (Variable, Absolute) */ + 0x85, 0x02, /* Report ID (2) */ + 0x09, 0x02, /* Usage (Mouse) */ + 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 u8 t300rs_rdesc_ps4_fixed[] = { + 0x05, 0x01, /* Usage page (Generic Desktop) */ + 0x09, 0x05, /* Usage (GamePad) */ + 0xa1, 0x01, /* Collection (Application) */ + 0x85, 0x01, /* Report ID (1) */ + 0x09, 0x00, /* Usage (U) (was X) */ + 0x09, 0x00, /* Usage (U) (was Y) */ + 0x09, 0x00, /* Usage (U) (was Z) */ + 0x09, 0x00, /* Usage (U) (was Rz)*/ + 0x15, 0x00, /* Logical minimum (0) */ + 0x26, 0xff, 0x00, /* Logical maximum (255) */ + 0x75, 0x08, /* Report size (8) */ + 0x95, 0x04, /* Report count (4) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x09, 0x39, /* Usage (Hat Switch) */ + 0x15, 0x00, /* Logical minimum (0) */ + 0x25, 0x07, /* Logical maximum (7)*/ + 0x35, 0x00, /* Physical minimum (0) */ + 0x46, 0x3b, 0x01, /* Physical maximum (315) */ + 0x65, 0x14, /* Unit (Eng Rot, Angular Pos) */ + 0x75, 0x04, /* Report size (4) */ + 0x95, 0x01, /* Report count (1) */ + 0x81, 0x42, /* Input (Variable, Absolute, NullState) */ + 0x65, 0x00, /* Input (None) */ + 0x05, 0x09, /* Usage page (Button) */ + 0x19, 0x01, /* Usage minimum (1) */ + 0x29, 0x0e, /* Usage maximum (14) */ + 0x15, 0x00, /* Logical minimum (0) */ + 0x25, 0x01, /* Logical maximum (1) */ + 0x75, 0x01, /* Report size (1) */ + 0x95, 0x0e, /* Report size (14) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x06, 0x00, 0xff, /* Usage page (Vendor 1) */ + 0x09, 0x20, /* Usage (32) */ + 0x75, 0x06, /* Report size (6) */ + 0x95, 0x01, /* Report count (1) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x05, 0x01, /* Usage page (Generic Desktop) */ + 0x09, 0x00, /* Usage (U) (was Rx)*/ + 0x09, 0x00, /* Usage (U) (was Ry) */ + 0x15, 0x00, /* Logical minimum (0) */ + 0x26, 0xff, 0x00, /* Logical maximum (255) */ + 0x75, 0x08, /* Report size (8) */ + 0x95, 0x02, /* Report count (2) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x05, 0x01, /* Usage page (Vendor 1) */ + /* constant zero? */ + 0x09, 0x00, /* Usage (33) */ + 0x95, 0x21, /* Report count (33) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + /* wheel */ + 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) */ + /* gas */ + 0x09, 0x31, /* Usage (Y) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + /* brake */ + 0x09, 0x32, /* Usage (Z) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + /* clutch */ + 0x09, 0x35, /* Usage (Rz) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + /* stick shifter (check model no) */ + 0x05, 0x09, /* Usage page (Button) */ + 0x19, 0x0f, /* Usage minimum (15) */ + 0x29, 0x17, /* Usage maximum (23) */ + 0x15, 0x00, /* Logical minimum (1) */ + 0x25, 0x01, /* Logical maximum (1) */ + 0x75, 0x01, /* Report size (1) */ + 0x95, 0x08, /* Report count (8) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + /* no clue */ + 0x05, 0x01, /* Usage page (Generic Desktop) */ + 0x09, 0x00, /* Usage (U) */ + 0x75, 0x08, /* Report size (8) */ + 0x95, 0x0c, /* Report count (12) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + /* continue unmodified */ + 0x06, 0x00, 0xff, /* Usage page (Vendor defined 1) */ + 0x85, 0x60, /* Report ID (5) (change to 0x60?) */ + 0x09, 0x60, /* Usage (34) (change to 0x60?) */ + 0x95, 0x1f, /* Report count (31) () */ + 0x91, 0x02, /* Output (Variable, Absolute) */ + 0x85, 0x03, /* Report ID (3) */ + 0x0a, 0x21, 0x27, /* ??? */ + 0x95, 0x2f, /* Report count (47) */ + 0xb1, 0x02, /* Feature (Data, Var, Abs) */ + 0xc0, /* End collection */ + + /* From here on out no clue */ + 0x06, 0xf0, + 0xff, 0x09, + 0x40, 0xa1, + 0x01, 0x85, + 0xf0, 0x09, + 0x47, 0x95, + 0x3f, 0xb1, + 0x02, 0x85, + 0xf1, 0x09, + 0x48, 0x95, + 0x3f, 0xb1, + 0x02, 0x85, + 0xf2, 0x09, + 0x49, 0x95, + 0x0f, 0xb1, + 0x02, 0x85, + 0xf3, 0x0a, + 0x01, 0x47, + 0x95, 0x07, + 0xb1, 0x02, + 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 +}; + +int t300rs_send_buf(struct t300rs_device_entry *t300rs, u8 *send_buffer, size_t len) +{ + int i; + /* check that send_buffer fits into our report */ + if (len > t300rs->buffer_length) + return -EINVAL; + + /* fill with actual data */ + for (i = 0; i < len; ++i) + t300rs->ff_field->value[i] = send_buffer[i]; + + /* fill the rest with zeroes */ + for (i = len; i < t300rs->buffer_length; ++i) + t300rs->ff_field->value[i] = 0; + + hid_hw_request(t300rs->hdev, t300rs->report, HID_REQ_SET_REPORT); + return 0; +} + +int t300rs_send_int(struct t300rs_device_entry *t300rs) +{ + t300rs_send_buf(t300rs, t300rs->send_buffer, t300rs->buffer_length); + memset(t300rs->send_buffer, 0, t300rs->buffer_length); + + return 0; +} + +static void t300rs_fill_header(struct t300rs_packet_header *packet_header, + uint8_t id, uint8_t code) +{ + packet_header->id = id + 1; + packet_header->code = code; +} + +int t300rs_play_effect(void *data, struct tmff2_effect_state *state) +{ + struct t300rs_device_entry *t300rs = data; + struct __packed t300rs_packet_play { + struct t300rs_packet_header header; + uint8_t value; + } *play_packet = (struct t300rs_packet_play *)t300rs->send_buffer; + + int ret; + + + t300rs_fill_header(&play_packet->header, state->effect.id, 0x89); + play_packet->value = 0x01; + + ret = t300rs_send_int(t300rs); + if (ret) + hid_err(t300rs->hdev, "failed starting effect play\n"); + + return ret; +} + +int t300rs_stop_effect(void *data, struct tmff2_effect_state *state) +{ + struct t300rs_device_entry *t300rs = data; + struct __packed t300rs_packet_stop { + struct t300rs_packet_header header; + uint8_t value; + } *stop_packet = (struct t300rs_packet_stop *)t300rs->send_buffer; + + int ret; + + + t300rs_fill_header(&stop_packet->header, state->effect.id, 0x89); + + ret = t300rs_send_int(t300rs); + if (ret) + hid_err(t300rs->hdev, "failed stopping effect play\n"); + + return ret; +} + +static void t300rs_fill_envelope(struct t300rs_packet_envelope *packet_envelope, + int16_t level, uint16_t duration, struct ff_envelope *envelope) +{ + uint16_t attack_length = (duration * envelope->attack_length) / 0x7fff; + uint16_t attack_level = (level * envelope->attack_level) / 0x7fff; + uint16_t fade_length = (duration * envelope->fade_length) / 0x7fff; + uint16_t fade_level = (level * envelope->fade_level) / 0x7fff; + + packet_envelope->attack_length = cpu_to_le16(attack_length); + packet_envelope->attack_level = cpu_to_le16(attack_level); + packet_envelope->fade_length = cpu_to_le16(fade_length); + packet_envelope->fade_level = cpu_to_le16(fade_level); +} + +static void t300rs_fill_timing(struct t300rs_packet_timing *packet_timing, + uint16_t duration, uint16_t offset){ + packet_timing->start_marker = 0x4f; + + packet_timing->duration = cpu_to_le16(duration); + packet_timing->offset = cpu_to_le16(offset); + + packet_timing->end_marker = 0xffff; +} + +static int t300rs_update_envelope(struct t300rs_device_entry *t300rs, + struct tmff2_effect_state *state, + int16_t level, + uint16_t duration, + uint8_t id, + struct ff_envelope envelope, + struct ff_envelope envelope_old + ) +{ + struct __packed t300rs_packet_mod_envelope { + struct t300rs_packet_header header; + uint8_t attribute; + uint16_t value; + } *packet_mod_envelope = (struct t300rs_packet_mod_envelope *)t300rs->send_buffer; + + uint16_t attack_length, attack_level, fade_length, fade_level; + int ret = 0; + + duration = duration - 1; + + attack_length = (duration * envelope.attack_length) / 0x7fff; + attack_level = (level * envelope.attack_level) / 0x7fff; + fade_length = (duration * envelope.fade_length) / 0x7fff; + fade_level = (level * envelope.fade_level) / 0x7fff; + + if (envelope.attack_length != envelope_old.attack_length) { + t300rs_fill_header(&packet_mod_envelope->header, id, 0x31); + packet_mod_envelope->attribute = 0x81; + packet_mod_envelope->value = cpu_to_le16(attack_length); + + ret = t300rs_send_int(t300rs); + if (ret) { + hid_err(t300rs->hdev, "failed modifying effect envelope\n"); + goto error; + } + } + + if (envelope.attack_level != envelope_old.attack_level) { + t300rs_fill_header(&packet_mod_envelope->header, id, 0x31); + packet_mod_envelope->attribute = 0x82; + packet_mod_envelope->value = cpu_to_le16(attack_level); + + ret = t300rs_send_int(t300rs); + if (ret) { + hid_err(t300rs->hdev, "failed modifying effect envelope\n"); + goto error; + } + } + + if (envelope.fade_length != envelope_old.fade_length) { + t300rs_fill_header(&packet_mod_envelope->header, id, 0x31); + packet_mod_envelope->attribute = 0x84; + packet_mod_envelope->value = cpu_to_le16(fade_length); + + ret = t300rs_send_int(t300rs); + if (ret) { + hid_err(t300rs->hdev, "failed modifying effect envelope\n"); + goto error; + } + } + + if (envelope.fade_level != envelope_old.fade_level) { + t300rs_fill_header(&packet_mod_envelope->header, id, 0x31); + packet_mod_envelope->attribute = 0x88; + packet_mod_envelope->value = cpu_to_le16(fade_level); + + ret = t300rs_send_int(t300rs); + if (ret) { + hid_err(t300rs->hdev, "failed modifying effect envelope\n"); + goto error; + } + } + +error: + return ret; +} + +static int t300rs_update_simple_duration(struct t300rs_device_entry *t300rs, + struct tmff2_effect_state *state, unsigned type) +{ + struct ff_effect effect = state->effect; + struct __packed t300rs_packet_mod_duration { + struct t300rs_packet_header header; + uint16_t marker; + uint16_t duration; + } *packet_mod_duration = (struct t300rs_packet_mod_duration *)t300rs->send_buffer; + + uint16_t duration; + int ret = 0; + + duration = effect.replay.length - 1; + + t300rs_fill_header(&packet_mod_duration->header, effect.id, 0x49); + packet_mod_duration->marker = cpu_to_le16(0x4100 + type); + packet_mod_duration->duration = cpu_to_le16(duration); + + ret = t300rs_send_int(t300rs); + if (ret) { + hid_err(t300rs->hdev, "failed modifying duration\n"); + goto error; + } + +error: + return ret; +} + +static int t300rs_update_periodic_duration(struct t300rs_device_entry *t300rs, + struct tmff2_effect_state *state, unsigned type) +{ + struct ff_effect effect = state->effect; + struct __packed t300rs_packet_mod_duration { + struct t300rs_packet_header header; + uint8_t type; + uint8_t marker; + uint16_t duration; + } *packet_mod_duration = (struct t300rs_packet_mod_duration *)t300rs->send_buffer; + + uint16_t duration; + int ret = 0; + + duration = effect.replay.length - 1; + + t300rs_fill_header(&packet_mod_duration->header, effect.id, 0x49); + packet_mod_duration->type = type; + packet_mod_duration->marker = 0x41; + packet_mod_duration->duration = cpu_to_le16(duration); + + ret = t300rs_send_int(t300rs); + if (ret) { + hid_err(t300rs->hdev, "failed modifying duration\n"); + goto error; + } + +error: + return ret; +} + +static int t300rs_update_ramp_duration(struct t300rs_device_entry *t300rs, + struct tmff2_effect_state *state) +{ + struct ff_effect effect = state->effect; + struct __packed t300rs_packet_mod_duration { + struct t300rs_packet_header header; + uint8_t marker0; + uint16_t duration0; + uint8_t marker1; + uint8_t marker2; + uint16_t duration1; + } *packet_mod_duration = (struct t300rs_packet_mod_duration *)t300rs->send_buffer; + + uint16_t duration; + int ret = 0; + + duration = effect.replay.length - 1; + + t300rs_fill_header(&packet_mod_duration->header, effect.id, 0x4e); + packet_mod_duration->marker0 = 0x08; + packet_mod_duration->duration0 = cpu_to_le16(duration); + packet_mod_duration->marker1 = 0x05; + packet_mod_duration->marker2 = 0x41; + packet_mod_duration->duration1 = cpu_to_le16(duration); + + ret = t300rs_send_int(t300rs); + if (ret) { + hid_err(t300rs->hdev, "failed modifying duration\n"); + goto error; + } + +error: + return ret; +} + +static int t300rs_update_constant(struct t300rs_device_entry *t300rs, + struct tmff2_effect_state *state) +{ + struct ff_effect effect = state->effect; + struct ff_effect old = state->old; + struct ff_constant_effect constant = effect.u.constant; + struct ff_constant_effect constant_old = old.u.constant; + struct __packed t300rs_packet_mod_constant { + struct t300rs_packet_header header; + uint16_t level; + } *packet_mod_constant = (struct t300rs_packet_mod_constant *)t300rs->send_buffer; + int ret; + int16_t level; + + level = (constant.level * fixp_sin16(effect.direction * 360 / 0x10000)) / 0x7fff; + + if ((constant.level != constant_old.level) || (effect.direction != old.direction)) { + + t300rs_fill_header(&packet_mod_constant->header, effect.id, 0x0a); + packet_mod_constant->level = cpu_to_le16(level); + + ret = t300rs_send_int(t300rs); + if (ret) { + hid_err(t300rs->hdev, "failed modifying constant effect\n"); + goto error; + } + + } + + ret = t300rs_update_envelope(t300rs, + state, + level, + effect.replay.length, + effect.id, + constant.envelope, + constant_old.envelope + ); + + if (ret) { + hid_err(t300rs->hdev, "failed modifying constant envelope\n"); + goto error; + } + + ret = t300rs_update_simple_duration(t300rs, state, 0x00); + if (ret) { + hid_err(t300rs->hdev, "failed modifying constant duration\n"); + goto error; + } + +error: + return ret; +} + +static int t300rs_update_ramp(struct t300rs_device_entry *t300rs, + struct tmff2_effect_state *state) +{ + struct ff_effect effect = state->effect; + struct ff_effect old = state->old; + struct ff_ramp_effect ramp = effect.u.ramp; + struct ff_ramp_effect ramp_old = old.u.ramp; + struct __packed t300rs_packet_mod_ramp { + struct t300rs_packet_header header; + uint8_t attribute; + uint16_t difference; + uint16_t level; + } *packet_mod_ramp = (struct t300rs_packet_mod_ramp *)t300rs->send_buffer; + + int ret; + + uint16_t difference, top, bottom; + int16_t level; + + 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; + + if ((ramp.start_level != ramp_old.start_level) + || (ramp.end_level != ramp_old.end_level) + || (effect.direction != old.direction)) { + + t300rs_fill_header(&packet_mod_ramp->header, effect.id, 0x0e); + packet_mod_ramp->attribute = 0x03; + packet_mod_ramp->difference = cpu_to_le16(difference); + packet_mod_ramp->level = cpu_to_le16(level); + + ret = t300rs_send_int(t300rs); + if (ret) { + hid_err(t300rs->hdev, "failed modifying ramp effect\n"); + goto error; + } + + } + + ret = t300rs_update_envelope(t300rs, + state, + level, + effect.replay.length, + effect.id, + ramp.envelope, + ramp_old.envelope + ); + + + if (ret) { + hid_err(t300rs->hdev, "failed modifying ramp envelope\n"); + goto error; + } + + ret = t300rs_update_ramp_duration(t300rs, state); + if (ret) { + hid_err(t300rs->hdev, "failed modifying ramp duration\n"); + goto error; + } + +error: + return ret; +} + +static int t300rs_update_damper(struct t300rs_device_entry *t300rs, + struct tmff2_effect_state *state) +{ + struct ff_effect effect = state->effect; + struct ff_effect old = state->old; + struct ff_condition_effect damper = effect.u.condition[0]; + struct ff_condition_effect damper_old = old.u.condition[0]; + struct __packed t300rs_packet_mod_damper { + struct t300rs_packet_header header; + uint8_t attribute; + uint16_t value0; + uint16_t value1; + } *packet_mod_damper = (struct t300rs_packet_mod_damper *)t300rs->send_buffer; + + int ret, input_level; + + input_level = damper_level; + if (state->effect.type == FF_FRICTION) + input_level = friction_level; + + if (state->effect.type == FF_SPRING) + input_level = spring_level; + + if (damper.right_coeff != damper_old.right_coeff) { + int16_t coeff = damper.right_coeff * input_level / 100; + + t300rs_fill_header(&packet_mod_damper->header, effect.id, 0x0e); + packet_mod_damper->attribute = 0x41; + packet_mod_damper->value0 = cpu_to_le16(coeff); + + ret = t300rs_send_int(t300rs); + if (ret) { + hid_err(t300rs->hdev, "failed modifying damper rc\n"); + goto error; + } + + } + + if (damper.left_coeff != damper_old.left_coeff) { + int16_t coeff = damper.left_coeff * input_level / 100; + + t300rs_fill_header(&packet_mod_damper->header, effect.id, 0x0e); + packet_mod_damper->attribute = 0x42; + packet_mod_damper->value0 = cpu_to_le16(coeff); + + ret = t300rs_send_int(t300rs); + if (ret) { + hid_err(t300rs->hdev, "failed modifying damper lc\n"); + goto error; + } + + } + + if ((damper.deadband != damper_old.deadband) + || (damper.center != damper_old.center)) { + + uint16_t right_deadband = 0xfffe - damper.deadband - damper.center; + uint16_t left_deadband = 0xfffe - damper.deadband + damper.center; + + t300rs_fill_header(&packet_mod_damper->header, effect.id, 0x0e); + packet_mod_damper->attribute = 0x4c; + packet_mod_damper->value0 = cpu_to_le16(right_deadband); + packet_mod_damper->value1 = cpu_to_le16(left_deadband); + + ret = t300rs_send_int(t300rs); + if (ret) { + hid_err(t300rs->hdev, "failed modifying damper deadband\n"); + goto error; + } + + } + + ret = t300rs_update_simple_duration(t300rs, state, 0x06); + if (ret) { + hid_err(t300rs->hdev, "failed modifying damper duration\n"); + goto error; + } + +error: + return ret; +} + +static int t300rs_update_spring(struct t300rs_device_entry *t300rs, + struct tmff2_effect_state *state) +{ + return t300rs_update_damper(t300rs, state); +} + + +static int t300rs_update_periodic(struct t300rs_device_entry *t300rs, + struct tmff2_effect_state *state) +{ + struct ff_effect effect = state->effect; + struct ff_effect old = state->old; + struct ff_periodic_effect periodic = effect.u.periodic; + struct ff_periodic_effect periodic_old = old.u.periodic; + struct __packed t300rs_packet_mod_periodic { + struct t300rs_packet_header header; + uint8_t attribute; + uint16_t value; + } *packet_mod_periodic = (struct t300rs_packet_mod_periodic *)t300rs->send_buffer; + + int ret; + int16_t magnitude, phase; + + magnitude = (periodic.magnitude * fixp_sin16(effect.direction * 360 / 0x10000)) / 0x7fff; + phase = periodic.phase; + + if ((periodic.magnitude != periodic_old.magnitude) + || (effect.direction != old.direction)) { + + t300rs_fill_header(&packet_mod_periodic->header, effect.id, 0x0e); + packet_mod_periodic->attribute = 0x01; + packet_mod_periodic->value = cpu_to_le16(magnitude); + + ret = t300rs_send_int(t300rs); + if (ret) { + hid_err(t300rs->hdev, "failed modifying periodic magnitude\n"); + goto error; + } + + } + + if (periodic.offset != periodic_old.offset) { + uint16_t offset = periodic.offset; + + t300rs_fill_header(&packet_mod_periodic->header, effect.id, 0x0e); + packet_mod_periodic->attribute = 0x02; + packet_mod_periodic->value = cpu_to_le16(offset); + + ret = t300rs_send_int(t300rs); + if (ret) { + hid_err(t300rs->hdev, "failed modifying periodic offset\n"); + goto error; + } + + } + + if (periodic.phase != periodic_old.phase) { + + t300rs_fill_header(&packet_mod_periodic->header, effect.id, 0x0e); + packet_mod_periodic->attribute = 0x04; + packet_mod_periodic->value = cpu_to_le16(phase); + + ret = t300rs_send_int(t300rs); + if (ret) { + hid_err(t300rs->hdev, "failed modifying periodic phase\n"); + goto error; + } + + } + + if (periodic.period != periodic_old.period) { + int16_t period = periodic.period; + + t300rs_fill_header(&packet_mod_periodic->header, effect.id, 0x0e); + packet_mod_periodic->attribute = 0x08; + packet_mod_periodic->value = cpu_to_le16(period); + + ret = t300rs_send_int(t300rs); + if (ret) { + hid_err(t300rs->hdev, "failed modifying periodic period\n"); + goto error; + } + + } + + ret = t300rs_update_envelope(t300rs, + state, + magnitude, + effect.replay.length, + effect.id, + periodic.envelope, + periodic_old.envelope + ); + + if (ret) { + hid_err(t300rs->hdev, "failed modifying periodic envelope\n"); + goto error; + } + + ret = t300rs_update_periodic_duration(t300rs, state, periodic.waveform - 0x57); + if (ret) { + hid_err(t300rs->hdev, "failed modifying periodic duration\n"); + goto error; + } + +error: + return ret; +} + +static int t300rs_upload_constant(struct t300rs_device_entry *t300rs, + struct tmff2_effect_state *state) +{ + struct ff_effect effect = state->effect; + struct ff_constant_effect constant = state->effect.u.constant; + struct __packed t300rs_packet_constant { + struct t300rs_packet_header header; + uint16_t level; + struct t300rs_packet_envelope envelope; + uint8_t zero; + struct t300rs_packet_timing timing; + } *packet_constant = (struct t300rs_packet_constant *)t300rs->send_buffer; + + int16_t level; + uint16_t duration, offset; + + int ret; + + level = (constant.level * fixp_sin16(effect.direction * 360 / 0x10000)) / 0x7fff; + duration = effect.replay.length - 1; + + offset = effect.replay.delay; + + t300rs_fill_header(&packet_constant->header, effect.id, 0x6a); + + packet_constant->level = cpu_to_le16(level); + + t300rs_fill_envelope(&packet_constant->envelope, level, duration, + &constant.envelope); + t300rs_fill_timing(&packet_constant->timing, duration, offset); + + ret = t300rs_send_int(t300rs); + if (ret) + hid_err(t300rs->hdev, "failed uploading constant effect\n"); + + return ret; +} + +static int t300rs_upload_ramp(struct t300rs_device_entry *t300rs, + struct tmff2_effect_state *state) +{ + struct ff_effect effect = state->effect; + struct ff_ramp_effect ramp = state->effect.u.ramp; + struct __packed t300rs_packet_ramp { + struct t300rs_packet_header header; + uint16_t difference; + uint16_t level; + uint8_t zero1[2]; + uint16_t duration; + uint16_t marker; + struct t300rs_packet_envelope envelope; + uint8_t direction; + struct t300rs_packet_timing timing; + } *packet_ramp = (struct t300rs_packet_ramp *)t300rs->send_buffer; + + int ret; + uint16_t difference, offset, top, bottom, duration; + int16_t level; + + duration = effect.replay.length - 1; + + 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; + offset = effect.replay.delay; + + + t300rs_fill_header(&packet_ramp->header, effect.id, 0x6b); + + packet_ramp->difference = cpu_to_le16(difference); + packet_ramp->level = cpu_to_le16(level); + packet_ramp->duration = cpu_to_le16(duration); + + packet_ramp->marker = cpu_to_le16(0x8000); + + t300rs_fill_envelope(&packet_ramp->envelope, level, duration, + &ramp.envelope); + + packet_ramp->direction = ramp.end_level > ramp.start_level ? 0x04 : 0x05; + t300rs_fill_timing(&packet_ramp->timing, duration, offset); + + ret = t300rs_send_int(t300rs); + if (ret) + hid_err(t300rs->hdev, "failed uploading ramp"); + + return ret; +} + +static int t300rs_upload_spring(struct t300rs_device_entry *t300rs, + struct tmff2_effect_state *state) +{ + struct ff_effect effect = state->effect; + /* we only care about the first axis */ + struct ff_condition_effect spring = state->effect.u.condition[0]; + struct __packed t300rs_packet_spring { + struct t300rs_packet_header header; + uint16_t right_coeff; + uint16_t left_coeff; + uint16_t right_deadband; + uint16_t left_deadband; + uint8_t spring_start[17]; + struct t300rs_packet_timing timing; + } *packet_spring = (struct t300rs_packet_spring *)t300rs->send_buffer; + + int ret; + uint16_t duration, right_coeff, left_coeff, right_deadband, left_deadband, offset; + + duration = effect.replay.length - 1; + + right_coeff = spring.right_coeff * spring_level / 100; + left_coeff = spring.left_coeff * spring_level / 100; + + right_deadband = 0xfffe - spring.deadband - spring.center; + left_deadband = 0xfffe - spring.deadband + spring.center; + + offset = effect.replay.delay; + + t300rs_fill_header(&packet_spring->header, effect.id, 0x64); + + packet_spring->right_coeff = cpu_to_le16(right_coeff); + packet_spring->left_coeff = cpu_to_le16(left_coeff); + packet_spring->right_deadband = cpu_to_le16(right_deadband); + packet_spring->left_deadband = cpu_to_le16(left_deadband); + + memcpy(&packet_spring->spring_start, spring_values, ARRAY_SIZE(spring_values)); + t300rs_fill_timing(&packet_spring->timing, duration, offset); + + ret = t300rs_send_int(t300rs); + if (ret) + hid_err(t300rs->hdev, "failed uploading spring\n"); + + return ret; +} + +static int t300rs_upload_damper(struct t300rs_device_entry *t300rs, + struct tmff2_effect_state *state) +{ + + struct ff_effect effect = state->effect; + /* we only care about the first axis */ + struct ff_condition_effect spring = state->effect.u.condition[0]; + struct __packed t300rs_packet_damper { + struct t300rs_packet_header header; + uint16_t right_coeff; + uint16_t left_coeff; + uint16_t right_deadband; + uint16_t left_deadband; + uint8_t damper_start[17]; + struct t300rs_packet_timing timing; + } *packet_damper = (struct t300rs_packet_damper *)t300rs->send_buffer; + + int ret, input_level; + uint16_t duration, right_coeff, left_coeff, right_deadband, left_deadband, offset; + + duration = effect.replay.length - 1; + + input_level = damper_level; + if (state->effect.type == FF_FRICTION) + input_level = friction_level; + + right_coeff = spring.right_coeff * input_level / 100; + left_coeff = spring.left_coeff * input_level / 100; + + right_deadband = 0xfffe - spring.deadband - spring.center; + left_deadband = 0xfffe - spring.deadband + spring.center; + + offset = effect.replay.delay; + + t300rs_fill_header(&packet_damper->header, effect.id, 0x64); + + packet_damper->right_coeff = cpu_to_le16(right_coeff); + packet_damper->left_coeff = cpu_to_le16(left_coeff); + packet_damper->right_deadband = cpu_to_le16(right_deadband); + packet_damper->left_deadband = cpu_to_le16(left_deadband); + + memcpy(&packet_damper->damper_start, damper_values, ARRAY_SIZE(damper_values)); + t300rs_fill_timing(&packet_damper->timing, duration, offset); + + ret = t300rs_send_int(t300rs); + if (ret) + hid_err(t300rs->hdev, "failed uploading spring\n"); + + return ret; +} + +static int t300rs_upload_periodic(struct t300rs_device_entry *t300rs, + struct tmff2_effect_state *state) +{ + struct ff_effect effect = state->effect; + struct ff_periodic_effect periodic = state->effect.u.periodic; + struct __packed t300rs_packet_periodic { + struct t300rs_packet_header header; + uint16_t magnitude; + uint16_t periodic_offset; + uint16_t phase; + uint16_t period; + uint16_t marker; + struct t300rs_packet_envelope envelope; + uint8_t waveform; + struct t300rs_packet_timing timing; + } *packet_periodic = (struct t300rs_packet_periodic *)t300rs->send_buffer; + + int ret; + uint16_t duration, period, offset, periodic_offset; + int16_t phase, magnitude; + + duration = effect.replay.length - 1; + magnitude = (periodic.magnitude * fixp_sin16(effect.direction * 360 / 0x10000)) / 0x7fff; + + phase = periodic.phase; + periodic_offset = periodic.offset; + + period = periodic.period; + offset = effect.replay.delay; + + t300rs_fill_header(&packet_periodic->header, effect.id, 0x6b); + + packet_periodic->magnitude = cpu_to_le16(magnitude); + packet_periodic->periodic_offset = cpu_to_le16(periodic_offset); + packet_periodic->phase = cpu_to_le16(phase); + packet_periodic->period = cpu_to_le16(period); + + packet_periodic->marker = cpu_to_le16(0x8000); + + t300rs_fill_envelope(&packet_periodic->envelope, magnitude, duration, + &periodic.envelope); + + packet_periodic->waveform = periodic.waveform - 0x57; + + t300rs_fill_timing(&packet_periodic->timing, duration, offset); + + ret = t300rs_send_int(t300rs); + if (ret) + hid_err(t300rs->hdev, "failed uploading periodic effect"); + + return ret; +} + +int t300rs_update_effect(void *data, struct tmff2_effect_state *state) +{ + struct t300rs_device_entry *t300rs = data; + switch (state->effect.type) { + case FF_CONSTANT: + return t300rs_update_constant(t300rs, state); + case FF_RAMP: + return t300rs_update_ramp(t300rs, state); + case FF_SPRING: + return t300rs_update_spring(t300rs, state); + case FF_DAMPER: + case FF_FRICTION: + case FF_INERTIA: + return t300rs_update_damper(t300rs, state); + case FF_PERIODIC: + return t300rs_update_periodic(t300rs, state); + default: + hid_err(t300rs->hdev, "invalid effect type: %x", state->effect.type); + return -1; + } +} + +int t300rs_upload_effect(void *data, struct tmff2_effect_state *state) +{ + struct t300rs_device_entry *t300rs = data; + 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_switch_mode(void *data, uint16_t mode) +{ + struct t300rs_device_entry *t300rs = data; + if (!t300rs) + return -ENODEV; + + if(t300rs->mode == mode) /* already in specified mode */ + return 0; + + if (mode == 0) + /* go to normal mode */ + usb_control_msg(t300rs->usbdev, + usb_sndctrlpipe(t300rs->usbdev, 0), + 83, 0x41, 5, 0, 0, 0, + USB_CTRL_SET_TIMEOUT + ); + else if (mode == 1) + /* go to advanced mode */ + usb_control_msg(t300rs->usbdev, + usb_sndctrlpipe(t300rs->usbdev, 0), + 83, 0x41, 3, 0, 0, 0, + USB_CTRL_SET_TIMEOUT + ); + else + hid_warn(t300rs->hdev, "mode %i not supported\n", mode); + + + return 0; +} + +static struct t300rs_alt_modes { + char *id; + char *label; + uint16_t mode; +} t300rs_modes[] = { + {"base", "T300RS base", 0}, + {"F1", "T300RS with F1 wheel attachment", 1} +}; + +static ssize_t t300rs_alt_mode_show(void *data, char *buf) +{ + struct t300rs_device_entry *t300rs = data; + ssize_t count = 0; + int i; + if (!t300rs) + return -ENODEV; + + if (t300rs->attachment != T300RS_F1_ATTACHMENT) + /* we only support one base mode */ + return scnprintf(buf, PAGE_SIZE, "%s: %s *\n", + t300rs_modes[0].id, t300rs_modes[0].label); + + for (i = 0; i < ARRAY_SIZE(t300rs_modes); ++i) { + count += scnprintf(buf + count, PAGE_SIZE - count, "%s: %s", + t300rs_modes[i].id, t300rs_modes[i].label); + + if (count >= PAGE_SIZE - 1) + return count; + + if (t300rs_modes[i].mode == t300rs->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; +} + +static ssize_t t300rs_alt_mode_store(void *data, const char *buf, size_t count) +{ + struct t300rs_device_entry *t300rs = data; + int i, len, mode_len; + char *lbuf; + if (!t300rs) + return -ENODEV; + + if (t300rs->attachment != T300RS_F1_ATTACHMENT) + return count; /* don't do anything */ + + lbuf = kasprintf(GFP_KERNEL, "%s", buf); + if (!lbuf) + return -ENOMEM; + + len = strlen(buf); + for (i = 0; i < ARRAY_SIZE(t300rs_modes); ++i) { + mode_len = strlen(t300rs_modes[i].id); + if (mode_len > len) + continue; + + if (strncmp(lbuf, t300rs_modes[i].id, mode_len) == 0) { + t300rs_switch_mode(data, t300rs_modes[i].mode); + break; + } + } + + kfree(lbuf); + return count; +} + +int t300rs_set_autocenter(void *data, uint16_t value) +{ + struct t300rs_device_entry *t300rs = data; + struct __packed t300rs_packet_autocenter { + struct t300rs_setup_header header; + uint16_t value; + } *autocenter_packet; + int ret; + + if (!t300rs) + return -ENODEV; + + /* TODO: this should probably also use a separately allocated buffer? */ + autocenter_packet = (struct t300rs_packet_autocenter *)t300rs->send_buffer; + + autocenter_packet->header.cmd = 0x08; + autocenter_packet->header.code = 0x04; + autocenter_packet->value = cpu_to_le16(0x01); + + if ((ret = t300rs_send_int(t300rs))) { + hid_err(t300rs->hdev, "failed setting autocenter"); + return ret; + } + + autocenter_packet->header.cmd = 0x08; + autocenter_packet->header.code = 0x03; + + autocenter_packet->value = cpu_to_le16(value); + + if ((ret = t300rs_send_int(t300rs))) + hid_err(t300rs->hdev, "failed setting autocenter"); + + return ret; +} + +int t300rs_set_gain(void *data, uint16_t gain) +{ + struct t300rs_device_entry *t300rs = data; + struct __packed t300rs_packet_gain { + struct t300rs_setup_header header; + } *gain_packet; + int ret; + + if (!t300rs) + return -ENODEV; + + gain_packet = (struct t300rs_packet_gain *)t300rs->send_buffer; + gain_packet->header.cmd = 0x02; + gain_packet->header.code = (gain >> 8) & 0xff; + + if ((ret = t300rs_send_int(t300rs))) + hid_err(t300rs->hdev, "failed setting gain: %i\n", ret); + + return ret; +} + +int t300rs_set_range(void *data, uint16_t value) +{ + struct t300rs_device_entry *t300rs = data; + /* it's important that we don't use t300rs->send_buffer, as range can be + * set from outside of the FFB environment, and we don't want to + * accidentally overwrite any data. */ + u8 *send_buffer = kzalloc(t300rs->buffer_length, GFP_KERNEL); + uint16_t scaled_value; + int ret; + + if (value < 40) { + hid_info(t300rs->hdev, "value %i too small, clamping to 40\n", value); + value = 40; + } + + if (value > 1080) { + hid_info(t300rs->hdev, "value %i too large, clamping to 1080\n", value); + value = 1080; + } + + if (!send_buffer) { + ret = -EINVAL; + hid_err(t300rs->hdev, "could not allocate send_buffer\n"); + goto err; + } + + scaled_value = value * 0x3c; + send_buffer[0] = 0x08; + send_buffer[1] = 0x11; + send_buffer[2] = scaled_value & 0xff; + send_buffer[3] = scaled_value >> 8; + + if ((ret = t300rs_send_buf(t300rs, send_buffer, t300rs->buffer_length))) + hid_warn(t300rs->hdev, "failed setting range\n"); + + /* since everythin went OK, update the current range */ + range = value; +err: + kfree(send_buffer); + return ret; +} + +static int t300rs_send_open(struct t300rs_device_entry *t300rs) +{ + struct __packed t300rs_packet_open { + struct t300rs_setup_header header; + } *open_packet; + + open_packet = (struct t300rs_packet_open *)t300rs->send_buffer; + open_packet->header.cmd = 0x01; + open_packet->header.code = 0x05; + + return t300rs_send_int(t300rs); +} + +static int t300rs_send_close(struct t300rs_device_entry *t300rs) +{ + struct __packed t300rs_packet_open { + struct t300rs_setup_header header; + } *open_packet; + + open_packet = (struct t300rs_packet_open *)t300rs->send_buffer; + open_packet->header.cmd = 0x01; + + return t300rs_send_int(t300rs); +} + +int t300rs_open(void *data, int open_mode) +{ + struct t300rs_device_entry *t300rs = data; + if (!t300rs) + return -ENODEV; + + if (open_mode && t300rs_send_open(t300rs)) + hid_warn(t300rs->hdev, "failed sending open command\n"); + + return t300rs->open(t300rs->input_dev); +} + +int t300rs_close(void *data, int open_mode) +{ + struct t300rs_device_entry *t300rs = data; + int ret; + + if (!t300rs) + return -ENODEV; + + if (open_mode && (ret = t300rs_send_close(t300rs))) + hid_warn(t300rs->hdev, "failed sending close command\n"); + + t300rs->close(t300rs->input_dev); + return ret; +} + +static int t300rs_check_firmware(struct t300rs_device_entry *t300rs) +{ + int ret; + struct t300rs_fw_response *fw_response = + kzalloc(sizeof(struct t300rs_fw_response), GFP_KERNEL); + + if (!fw_response) { + hid_err(t300rs->hdev, "could not allocate fw_response\n"); + return -ENOMEM; + } + + /* Fetch firmware version */ + ret = usb_control_msg(t300rs->usbdev, + usb_rcvctrlpipe(t300rs->usbdev, 0), + t300rs_fw_request.bRequest, + t300rs_fw_request.bRequestType, + t300rs_fw_request.wValue, + t300rs_fw_request.wIndex, + fw_response, + t300rs_fw_request.wLength, + USB_CTRL_SET_TIMEOUT + ); + + if (ret < 0) { + hid_err(t300rs->hdev, "could not fetch firmware version: %i\n", ret); + goto out; + } + + /* Educated guess */ + if (fw_response->fw_version < 31 && ret >= 0) { + hid_err(t300rs->hdev, + "firmware version %i is too old, please update.\n", + fw_response->fw_version + ); + + hid_info(t300rs->hdev, "note: this has to be done through Windows.\n"); + + ret = -EINVAL; + goto out; + } + + /* everything OK */ + ret = 0; + +out: + kfree(fw_response); + return ret; +} + +static int t300rs_get_attachment(struct t300rs_device_entry *t300rs) +{ + /* taken directly from hid_tminit */ + struct __packed t300rs_attachment_response + { + uint16_t type; + + union { + struct __packed { + uint16_t field0; + uint16_t field1; + uint8_t attachment; + uint8_t model; + uint16_t field2; + uint16_t field3; + uint16_t field4; + uint16_t field5; + } a; + + struct __packed { + uint16_t field0; + uint16_t field1; + uint8_t attachment; + uint8_t model; + } b; + }; + } *response = kzalloc(GFP_KERNEL, sizeof(struct t300rs_attachment_response)); + struct usb_ctrlrequest t300rs_attachment_rq = { + .bRequestType = 0xc1, + .bRequest = 73, + .wValue = 0, + .wIndex = 0, + .wLength = sizeof(struct t300rs_attachment_response) + }; + int ret, attachment; + if (!response) + return -ENODEV; + + ret = usb_control_msg(t300rs->usbdev, + usb_rcvctrlpipe(t300rs->usbdev, 0), + t300rs_attachment_rq.bRequest, + t300rs_attachment_rq.bRequestType, + t300rs_attachment_rq.wValue, + t300rs_attachment_rq.wIndex, + response, + sizeof(struct t300rs_attachment_response), + USB_CTRL_SET_TIMEOUT + ); + + if (ret < 0) { + hid_err(t300rs->hdev, "could not fetch attachment: %i\n", ret); + goto out; + } + + if (response->type == cpu_to_le16(0x49)) { + attachment = response->a.attachment; + } else if (response->type == cpu_to_le16(0x47)) { + attachment = response->b.attachment; + } else { + hid_err(t300rs->hdev, "unknown packet type %hx\n, please contact a maintainer", + response->type); + ret = -EINVAL; + goto out; + } + + kfree(response); + return attachment; + +out: + kfree(response); + return ret; +} + +static int t300rs_wheel_init(struct tmff2_device_entry *tmff2, int open_mode) +{ + struct t300rs_device_entry *t300rs = kzalloc(sizeof(struct t300rs_device_entry), GFP_KERNEL); + struct list_head *report_list; + int ret; + + if (!t300rs) { + ret = -ENOMEM; + goto t300rs_err; + } + + t300rs->hdev = tmff2->hdev; + t300rs->input_dev = tmff2->input_dev; + t300rs->usbdev = to_usb_device(tmff2->hdev->dev.parent->parent); + + if(t300rs->hdev->product == TMT300RS_PS4_NORM_ID) + t300rs->buffer_length = T300RS_PS4_BUFFER_LENGTH; + else + t300rs->buffer_length = T300RS_NORM_BUFFER_LENGTH; + + t300rs->send_buffer = kzalloc(t300rs->buffer_length, GFP_KERNEL); + if (!t300rs->send_buffer) { + ret = -ENOMEM; + goto send_err; + } + + if ((ret = t300rs_check_firmware(t300rs))) + goto firmware_err; + + report_list = &t300rs->hdev->report_enum[HID_OUTPUT_REPORT].report_list; + + /* because we set the rdesc, we know exactly which report and field to use */ + t300rs->report = list_entry(report_list->next, struct hid_report, list); + t300rs->ff_field = t300rs->report->field[0]; + + t300rs->open = t300rs->input_dev->open; + t300rs->close = t300rs->input_dev->close; + + /* TODO: PS4 advanced mode? */ + alt_mode = (t300rs->mode = (t300rs->hdev->product == TMT300RS_PS3_ADV_ID)); + if ((t300rs->attachment = t300rs_get_attachment(t300rs)) < 0) + t300rs->attachment = T300RS_DEFAULT_ATTACHMENT; + + /* everythin went OK */ + tmff2->data = t300rs; + tmff2->params = t300rs_params; + tmff2->max_effects = T300RS_MAX_EFFECTS; + memcpy(tmff2->supported_effects, t300rs_effects, sizeof(t300rs_effects)); + + if (!open_mode) + t300rs_send_open(t300rs); + + hid_info(t300rs->hdev, "force feedback for T300RS\n"); + return 0; + +firmware_err: + kfree(t300rs->send_buffer); +send_err: + kfree(t300rs); +t300rs_err: + hid_err(tmff2->hdev, "failed initializing T300RS\n"); + return ret; +} + +static int t300rs_wheel_destroy(void *data) +{ + struct t300rs_device_entry *t300rs = data; + if (!t300rs) + return -ENODEV; + + kfree(t300rs->send_buffer); + kfree(t300rs); + return 0; +} + +static __u8 *t300rs_wheel_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + switch (hdev->product) { + case TMT300RS_PS3_NORM_ID: + /* normal PS3 mode */ + rdesc = t300rs_rdesc_nrm_fixed; + *rsize = sizeof(t300rs_rdesc_nrm_fixed); + break; + + case TMT300RS_PS4_NORM_ID: + /* PS4 normal mode */ + rdesc = t300rs_rdesc_ps4_fixed; + *rsize = sizeof(t300rs_rdesc_ps4_fixed); + break; + + case TMT300RS_PS3_ADV_ID: + /* PS3 advanced mode */ + rdesc = t300rs_rdesc_adv_fixed; + *rsize = sizeof(t300rs_rdesc_adv_fixed); + break; + } + + return rdesc; +} + +int t300rs_populate_api(struct tmff2_device_entry *tmff2) +{ + /* set callbacks */ + tmff2->play_effect = t300rs_play_effect; + tmff2->upload_effect = t300rs_upload_effect; + tmff2->update_effect = t300rs_update_effect; + tmff2->stop_effect = t300rs_stop_effect; + + tmff2->wheel_init = t300rs_wheel_init; + tmff2->wheel_destroy = t300rs_wheel_destroy; + + tmff2->open = t300rs_open; + tmff2->close = t300rs_close; + tmff2->set_gain = t300rs_set_gain; + tmff2->set_range = t300rs_set_range; + tmff2->switch_mode = t300rs_switch_mode; + tmff2->alt_mode_show = t300rs_alt_mode_show; + tmff2->alt_mode_store = t300rs_alt_mode_store; + tmff2->set_autocenter = t300rs_set_autocenter; + tmff2->wheel_fixup = t300rs_wheel_fixup; + + return 0; +} diff --git a/src/hid-tmt300rs/hid-tmt300rs.h b/src/hid-tmt300rs/hid-tmt300rs.h new file mode 100644 index 0000000..2305f4b --- /dev/null +++ b/src/hid-tmt300rs/hid-tmt300rs.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef __HID_TMT300RS_H +#define __HID_TMT300RS_H + +#include "../hid-tmff2.h" + +int t300rs_populate_api(struct tmff2_device_entry *tmff2); + +#endif diff --git a/src/hid-tmtx/hid-tmtx.c b/src/hid-tmtx/hid-tmtx.c new file mode 100644 index 0000000..afca799 --- /dev/null +++ b/src/hid-tmtx/hid-tmtx.c @@ -0,0 +1,323 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include "../hid-tmff2.h" + +#define TMTX_MAX_EFFECTS 16 +#define TMTX_BUFFER_LENGTH 63 + +static const u8 setup_0[64] = { 0x42, 0x01 }; +static const u8 setup_1[64] = { 0x0a, 0x04, 0x90, 0x03 }; +static const u8 setup_2[64] = { 0x0a, 0x04, 0x00, 0x0c }; +static const u8 setup_3[64] = { 0x0a, 0x04, 0x12, 0x10 }; +static const u8 setup_4[64] = { 0x0a, 0x04, 0x00, 0x06 }; +static const u8 setup_5[64] = { 0x0a, 0x04, 0x00, 0x0e }; +static const u8 setup_6[64] = { 0x0a, 0x04, 0x00, 0x0e, 0x01 }; +static const u8 *const setup_arr[] = { setup_0, setup_1, setup_2, setup_3, setup_4, setup_5, setup_6 }; +static const unsigned int setup_arr_sizes[] = { + ARRAY_SIZE(setup_0), + ARRAY_SIZE(setup_1), + ARRAY_SIZE(setup_2), + ARRAY_SIZE(setup_3), + ARRAY_SIZE(setup_4), + ARRAY_SIZE(setup_5), + ARRAY_SIZE(setup_6) +}; + +static const unsigned long tx_params = + PARAM_SPRING_LEVEL + | PARAM_DAMPER_LEVEL + | PARAM_FRICTION_LEVEL + | PARAM_RANGE + | PARAM_GAIN + ; + +static const signed short tx_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 tx_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, 0x35, /* Usage (Rz) (Brake) */ + 0x26, 0xff, 0x03, /* Logical maximum (1023) */ + 0x46, 0xff, 0x03, /* Physical maximum (1023) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x09, 0x32, /* Usage (Z) (Gas) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x09, 0x31, /* Usage (Y) (Clutch) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x81, 0x03, /* Input (Variable, Absolute, Constant) */ + 0x05, 0x09, /* Usage page (Button) */ + 0x19, 0x01, /* Usage minimum (1) */ + 0x29, 0x0d, /* Usage maximum (13) */ + 0x25, 0x01, /* Logical maximum (1) */ + 0x45, 0x01, /* Physical maximum (1) */ + 0x75, 0x01, /* Report size (1) */ + 0x95, 0x0d, /* Report count (13) */ + 0x81, 0x02, /* Input (Variable, Absolute) */ + 0x75, 0x0b, /* Report size (13) */ + 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, /* Unit (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, 0x7f, /* Logical maximum (32767) */ + 0x15, 0x00, /* Logical minimum (0) */ + 0x46, 0xff, 0x7f, /* Physical maximum (32767) */ + 0x36, 0x00, 0x80, /* Physical minimum (-32768) */ + 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 tx_interrupts(struct t300rs_device_entry *tx) +{ + u8 *send_buf = kmalloc(256, GFP_KERNEL); + struct usb_interface *usbif = to_usb_interface(tx->hdev->dev.parent); + struct usb_host_endpoint *ep; + int ret, trans, b_ep, i; + + if (!send_buf) { + hid_err(tx->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(tx->usbdev, + usb_sndintpipe(tx->usbdev, b_ep), + send_buf, setup_arr_sizes[i], + &trans, + USB_CTRL_SET_TIMEOUT); + + if (ret) { + hid_err(tx->hdev, "setup data couldn't be sent\n"); + goto err; + } + } + +err: + kfree(send_buf); + return ret; +} + +int tx_wheel_destroy(void *data) +{ + struct t300rs_device_entry *t300rs = data; + + if (!t300rs) + return -ENODEV; + + kfree(t300rs->send_buffer); + kfree(t300rs); + return 0; +} + +int tx_set_range(void *data, uint16_t value) +{ + struct t300rs_device_entry *tx = data; + + if (value < 140) { + hid_info(tx->hdev, "value %i too small, clamping to 140\n", value); + value = 140; + } + + if (value > 900) { + hid_info(tx->hdev, "value %i too large, clamping to 900\n", value); + value = 900; + } + + return t300rs_set_range(data, value); +} + +static int tx_send_open(struct t300rs_device_entry *tx) +{ + int r1, r2; + tx->send_buffer[0] = 0x01; + tx->send_buffer[1] = 0x04; + if ((r1 = t300rs_send_int(tx))) + return r1; + + tx->send_buffer[0] = 0x01; + tx->send_buffer[1] = 0x05; + if ((r2 = t300rs_send_int(tx))) + return r2; + + return 0; +} + +static int tx_open(void *data, int open_mode) +{ + struct t300rs_device_entry *tx = data; + + if (!tx) + return -ENODEV; + + if (open_mode) + tx_send_open(tx); + + return tx->open(tx->input_dev); +} + +static int tx_send_close(struct t300rs_device_entry *tx) +{ + int r1, r2; + tx->send_buffer[0] = 0x01; + tx->send_buffer[1] = 0x05; + if ((r1 = t300rs_send_int(tx))) + return r1; + + tx->send_buffer[0] = 0x01; + tx->send_buffer[1] = 0x00; + if ((r2 = t300rs_send_int(tx))) + return r2; + + return 0; +} + +static int tx_close(void *data, int open_mode) +{ + struct t300rs_device_entry *tx = data; + + if (!tx) + return -ENODEV; + + if (open_mode) + tx_send_close(tx); + + tx->close(tx->input_dev); + return 0; +} + +int tx_wheel_init(struct tmff2_device_entry *tmff2, int open_mode) +{ + struct t300rs_device_entry *tx = kzalloc(sizeof(struct t300rs_device_entry), GFP_KERNEL); + struct list_head *report_list; + int ret; + + + if (!tx) { + ret = -ENOMEM; + goto tx_err; + } + + tx->hdev = tmff2->hdev; + tx->input_dev = tmff2->input_dev; + tx->usbdev = to_usb_device(tmff2->hdev->dev.parent->parent); + tx->buffer_length = TMTX_BUFFER_LENGTH; + + tx->send_buffer = kzalloc(tx->buffer_length, GFP_KERNEL); + if (!tx->send_buffer) { + ret = -ENOMEM; + goto send_err; + } + + report_list = &tx->hdev->report_enum[HID_OUTPUT_REPORT].report_list; + tx->report = list_entry(report_list->next, struct hid_report, list); + tx->ff_field = tx->report->field[0]; + + tx->open = tx->input_dev->open; + tx->close = tx->input_dev->close; + + if ((ret = tx_interrupts(tx))) + goto interrupt_err; + + /* everything went OK */ + tmff2->data = tx; + tmff2->params = tx_params; + tmff2->max_effects = TMTX_MAX_EFFECTS; + memcpy(tmff2->supported_effects, tx_effects, sizeof(tx_effects)); + + if (!open_mode) + tx_send_open(tx); + + hid_info(tx->hdev, "force feedback for TX\n"); + return 0; + +interrupt_err: +send_err: + kfree(tx); +tx_err: + hid_err(tmff2->hdev, "failed initializing TX\n"); + return ret; +} + +static __u8 *tx_wheel_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int *rsize) +{ + rdesc = tx_pc_rdesc_fixed; + *rsize = sizeof(tx_pc_rdesc_fixed); + return rdesc; +} + +int tx_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; + /* TX only has 900 degree range, instead of T300RS 1080 */ + tmff2->set_range = tx_set_range; + tmff2->wheel_fixup = tx_wheel_fixup; + + tmff2->open = tx_open; + tmff2->close = tx_close; + + tmff2->wheel_init = tx_wheel_init; + tmff2->wheel_destroy = tx_wheel_destroy; + + return 0; +} diff --git a/t248-effects.txt b/t248-effects.txt deleted file mode 100644 index 821fc7a..0000000 --- a/t248-effects.txt +++ /dev/null @@ -1,2 +0,0 @@ -Open: Identical to T300 -Close: Identical to T300 -- cgit v1.3