37 Commits

Author SHA1 Message Date
Oleg
11ed3dd91e Fix NO_DELAY_EFFECT quirk implementation 2024-07-15 04:50:08 +03:00
Makarenko Oleg
eb3766ac30 Merge pull request #8 from JacKeTUs/docs
Fix module names in README.md
2024-07-15 04:38:01 +03:00
Oleg
70fa7742f7 Fix module names in README.md 2024-07-15 04:36:27 +03:00
Makarenko Oleg
e9a0c9ac6b Merge pull request #3 from JacKeTUs/universal-quirks
Move from MOZA only driver to Universal implementation.
This can enable more wheelbases with just one driver
2024-07-15 04:04:19 +03:00
Oleg
d9ac2dbbb1 Fix DEST_MODULE_NAME 2024-07-15 04:01:19 +03:00
Oleg
b3ab9e9989 More clarifications in README.md 2024-07-15 03:59:02 +03:00
Oleg
94547c9bfc Pretty comments 2024-07-15 03:38:33 +03:00
Oleg
d449f45904 Clarify pidff_clamp function usage 2024-07-15 03:34:10 +03:00
Oleg
79f47bd686 Universal README file 2024-07-15 03:27:01 +03:00
Oleg
03d7fe30c7 Rename main source file, fix KBuild 2024-07-15 03:26:50 +03:00
Oleg
b0867e2e6c Fix BUILD_MODULE_NAME in dkms.conf 2024-07-15 03:26:34 +03:00
Makarenko Oleg
55cc69064d Merge pull request #5 from Lawstorant/universal-quirks
Add envelope fix quirk and value clamping functions
2024-07-14 23:08:21 +03:00
Lawstorant
dea4e51be5 Fix dkms build 2024-07-13 16:00:07 +02:00
Lawstorant
83d1374b63 Mention Boxflat in the README 2024-07-12 15:47:24 +02:00
Lawstorant
1da641aee4 Explain WHEEL_DIRECTION_FIX value 2024-07-12 15:46:42 +02:00
Tomasz Pakuła
ffa4a05a1b Update attack/fade logging 2024-07-02 11:55:36 +02:00
Tomasz Pakuła
33a99c7458 Add field value clamping functionality 2024-07-02 11:54:10 +02:00
Tomasz Pakuła
9d9e4c47bd Update build configs 2024-07-02 10:47:29 +02:00
Tomasz Pakuła
a6ec74f90d Remove unneeded paramaters 2024-07-02 10:47:22 +02:00
Tomasz Pakuła
db0eb52449 Add Moza periodic envelope and direction fixup 2024-07-02 01:24:15 +02:00
Oleg
93a696d1c8 Added code for no_delay quirk 2024-05-30 12:06:14 +03:00
Oleg
4826a9d4ef Remove infinite length quirk 2024-05-30 11:42:36 +03:00
Oleg
fe6100696e Update dkms.conf 2024-05-29 23:53:08 +03:00
Oleg
50a4b4fcd5 Update README.md 2024-05-29 23:46:34 +03:00
Oleg
ceaf164adf Change naming 2024-05-29 23:45:05 +03:00
Oleg
e5c6f7079c Rename pidff function 2024-05-29 23:44:21 +03:00
Oleg
2a63e469ea Fix missing semicolon 2024-05-29 23:40:37 +03:00
Oleg
b5f6015304 Fix build 2024-05-29 23:38:46 +03:00
Oleg
23255595f7 Small changes to initialization of quirks 2024-05-29 23:38:38 +03:00
Oleg
377eb942bf Rename Moza to newpidff 2024-05-29 23:38:16 +03:00
Oleg
fb5c25ccf5 Move MOZA id's 2024-05-29 23:38:00 +03:00
Makarenko Oleg
1ac6938ed4 Merge pull request #2 from Lawstorant/main
fix: direction for friction/damper/spring/inertia
Added quirks for wheels
2024-05-29 23:00:23 +03:00
Tomasz Pakuła
d449d1c4e0 Mention AUR package in README.md
Add MOZA RS V2 to the supported rims list
as tested by myself
Update contributor info
2024-05-28 23:30:02 +02:00
Tomasz Pakuła
70c5d3fa88 Fix direction for friction/damper/spring/inertia
Add quirks

Update info about contributors
2024-05-28 21:23:09 +02:00
Oleg
615c31a9f7 Clarify Moza PitHouse situation 2024-05-26 23:51:43 +03:00
Oleg
32a55b6f99 Added report fixup for Moza
Fixed header on PID Device Control
2024-05-26 04:37:41 +03:00
Oleg
0864cb67ef More debugging 2024-05-26 03:29:37 +03:00
9 changed files with 320 additions and 161 deletions

4
Kbuild
View File

@@ -1,3 +1,3 @@
obj-m := hid-moza-ff.o obj-m := hid-universal-pidff.o
hid-moza-ff-y := hid-moza.o hid-pidff.o hid-universal-pidff-y := hid-pidff-wrapper.o hid-pidff.o
ccflags-y := -Idrivers/hid ccflags-y := -Idrivers/hid

View File

@@ -1,88 +1,69 @@
# Force feedback support for MOZA steering wheels # Universal Force Feedback driver for Linux
Linux module driver for MOZA driving wheels. Linux PIDFF driver with useful patches for initialization of FFB devices. Primarily targeting Direct Drive wheelbases.
MOZA wheels are basically DirectInput wheels. ## What's different between this and native pidff driver?
In that repository - copy of pidff driver from 6.6 kernel with some changes around infinite length effects (like that https://github.com/berarma/ffbtools/issues/26) That driver allows most DirectDrive wheelbases to initialize and work.
Most of the DirectDrive wheelbases are basically DirectInput wheels, but with some caveats, which Windows allows, but pidff doesn't.
In that repository - pidff driver with some changes, which allows most of the DirectDrive wheelbases to work.
1. Added quirks for better initialization rules for different wheelbases (MOZA, VRS, Cammus)
2. Fixes for infinite-length effects
3. Fixes for out-of-bounds values (no more spam in kernel logs)
And that's basically it And that's basically it
## What devices are supported? ## What devices are supported?
### Bases: ### Bases:
1. MOZA R3 1. MOZA R3, R5, R9, R12, R16, R21
1. MOZA R5 2. ...
1. MOZA R9
1. MOZA R12
1. MOZA R16
1. MOZA R21
### Wheel rims:
Not tested
## What works? ## What works?
1. FFB (all effects from device descriptor) 1. FFB (all effects from device descriptor)
2. All inputs (wheel axis, buttons) 2. All inputs (wheel axis, buttons)
3. Setup through MOZA PitHouse with [some tweaking](#how-to-set-up-a-base-parameters))
## What does not work? ## What does not work?
1. Telemetry functions (Shift lights, etc), mostly because telemetry works only with proprietary soft, which can't get access to shared memory chunks from games. 1. Telemetry functions (Shift lights, displays, SimHub, etc), mostly because telemetry works only with proprietary soft, which can't get access to shared memory chunks from games.
2. `Firmware Update` function. Use Windows PC or Windows VM at the moment. 2. `Firmware Update` function. Use Windows PC or Windows VM at the moment.
3. Setup through proprietary software. May require [some tweaking](#how-to-set-up-a-base-parameters))
## How to use that driver? ## How to use this driver?
You can install it through DKMS or manually. There's an [AUR packege](https://aur.archlinux.org/packages/universal-ff-dkms-git) for Arch Linux maintained by @Lawstorant
Alternatively, you can install it through DKMS or manually.
### DKMS ### DKMS
1. Install `dkms` 1. Install `dkms`
2. Clone repository to `/usr/src/moza-ff` 2. Clone repository to `/usr/src/universal-pidff`
3. Install the module: 3. Install the module:
`sudo dkms install /usr/src/moza-ff` `sudo dkms install /usr/src/universal-pidff`
4. Update initramfs: 4. Update initramfs:
`sudo update-initramfs -u` `sudo update-initramfs -u`
5. Reboot 5. Reboot
To remove module: To remove module:
`sudo dkms remove moza-ff/<version> --all` `sudo dkms remove universal-pidff/<version> --all`
### Manually ### Manually
1. Install `linux-headers-$(uname -r)` 1. Install `linux-headers-$(uname -r)`
2. Clone repository 2. Clone repository
3. `make` 3. `make`
4. `sudo insmod hid-moza-ff.ko` 4. `sudo insmod hid-universal-pidff.ko`
To unload module: To unload module:
`sudo rmmod hid_moza_ff` `sudo rmmod hid_universal_pidff`
## How to set up a base parameters? ## How to set up a base parameters?
### MOZA
**[Boxflat](https://github.com/Lawstorant/boxflat)** is a Linux Pit House alternative made by [@Lawstorant](https://github.com/Lawstorant)
You can do it through MOZA PitHouse with Wine. You need to tweak Wine prefix for them. **[Android App](https://play.google.com/store/apps/details?id=com.gudsen.mozapithouse)**
That soft uses hidraw to set up a base. You need to create `udev` rule for allow access to hidraw device:
```
echo 'KERNEL=="hidraw*", ATTRS{idVendor}=="346e", MODE="0660", TAG+="uaccess"' | sudo tee /etc/udev/rules.d/11-moza.rules
udevadm control --reload-rules && udevadm trigger
```
Then you need to force MOZA soft to use hidraw, not SDL, to find devices:
1. Create new Wine prefix for them:
`mkdir ~/moza-wine`
2. Launch regedit in prefix:
`WINEPREFIX=$HOME/moza-wine wine regedit`
3. Create two keys in
`HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\winebus`:
* `DisableInput` (DWORD) - set to `1`;
* `Enable SDL` (DWORD) - set to `0`; (yes, variable name contains space)
4. Now you can launch soft through that WINEPREFIX:
`WINEPREFIX=$HOME/moza-wine wine MOZA\ Pit\ House.exe` - launch your PitHouse from installation directory.
## Known issues with the driver ## Known issues with the driver
1. Firmware update does not work. Please use Windows machine or Windows VM for any firmware updates - Buttons above 80 simply don't show up. This is a Linux limitation and we're trying to fix that in the upstream
## Known issues with the firmware ## Known issues with the firmware
You tell me please You tell me please
## DISCLAIMER ## DISCLAIMER
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,8 +1,8 @@
PACKAGE_NAME="moza-ff" PACKAGE_NAME="universal-pidff"
PACKAGE_VERSION="0.0.1" PACKAGE_VERSION="0.0.3"
MAKE[0]="make KVERSION=$kernelver" MAKE[0]="make KVERSION=$kernelver"
CLEAN="make clean" CLEAN="make clean"
BUILT_MODULE_NAME[0]="hid-moza-ff" BUILT_MODULE_NAME[0]="hid-universal-pidff"
DEST_MODULE_NAME[0]="hid-moza-ff" DEST_MODULE_NAME[0]="hid-universal-pidff"
DEST_MODULE_LOCATION[0]="/kernel/drivers/hid" DEST_MODULE_LOCATION[0]="/kernel/drivers/hid"
AUTOINSTALL="yes" AUTOINSTALL="yes"

12
hid-ids.h Normal file
View File

@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __HID_IDS_H
#define __HID_IDS_H
#define USB_VENDOR_ID_MOZA 0x346e
#define USB_DEVICE_ID_MOZA_R3 0x0005
#define USB_DEVICE_ID_MOZA_R5 0x0004
#define USB_DEVICE_ID_MOZA_R9 0x0002
#define USB_DEVICE_ID_MOZA_R12 0x0006
#define USB_DEVICE_ID_MOZA_R16_R21 0x0000
#endif

View File

@@ -1,71 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* HID driver for Moza Steering Wheels
*
* Copyright (c) 2024 Makarenko Oleg
*/
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#include "hid-moza.h"
static const struct hid_device_id moza_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R5) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R9) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R12) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R16) },
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R21) },
{ }
};
MODULE_DEVICE_TABLE(hid, moza_devices);
static int moza_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
int ret;
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "parse failed\n");
goto err;
}
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
if (ret) {
hid_err(hdev, "hw start failed\n");
goto err;
}
ret = hid_pidff_init_moza(hdev);
if (ret) {
hid_warn(hdev, "Force feedback not supported\n");
goto err;
}
return 0;
err:
return ret;
}
static int moza_input_configured(struct hid_device *hdev,
struct hid_input *hidinput)
{
struct input_dev *input = hidinput->input;
input_set_abs_params(input, ABS_X,
input->absinfo[ABS_X].minimum, input->absinfo[ABS_X].maximum, 0, 0);
return 0;
}
static struct hid_driver moza_ff = {
.name = "moza-ff",
.id_table = moza_devices,
.probe = moza_probe,
.input_configured = moza_input_configured,
};
module_hid_driver(moza_ff);
MODULE_AUTHOR("Oleg Makarenko <oleg@makarenk.ooo>");
MODULE_DESCRIPTION("MOZA HID FF Driver");
MODULE_LICENSE("GPL");

View File

@@ -1,15 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __HID_MOZA_H
#define __HID_MOZA_H
#define USB_VENDOR_ID_MOZA 0x346e
#define USB_DEVICE_ID_MOZA_R3 0x0005
#define USB_DEVICE_ID_MOZA_R5 0x0004
#define USB_DEVICE_ID_MOZA_R9 0x0002
#define USB_DEVICE_ID_MOZA_R12 0x0006
#define USB_DEVICE_ID_MOZA_R16 0x0000
#define USB_DEVICE_ID_MOZA_R21 0x0000
int hid_pidff_init_moza(struct hid_device *hdev);
#endif

105
hid-pidff-wrapper.c Normal file
View File

@@ -0,0 +1,105 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* HID PIDFF wrapper
* First of all targeting steering wheels and wheelbases
*
* Copyright (c) 2024 Makarenko Oleg
*/
#include <linux/device.h>
#include <linux/hid.h>
#include <linux/module.h>
#include "hid-ids.h"
#include "hid-pidff.h"
static const struct hid_device_id pidff_wheel_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R3),
.driver_data = PIDFF_QUIRK_FIX_WHEEL_DIRECTION | PIDFF_QUIRK_FIX_PERIODIC_ENVELOPE },
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R5),
.driver_data = PIDFF_QUIRK_FIX_WHEEL_DIRECTION | PIDFF_QUIRK_FIX_PERIODIC_ENVELOPE },
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R9),
.driver_data = PIDFF_QUIRK_FIX_WHEEL_DIRECTION | PIDFF_QUIRK_FIX_PERIODIC_ENVELOPE },
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R12),
.driver_data = PIDFF_QUIRK_FIX_WHEEL_DIRECTION | PIDFF_QUIRK_FIX_PERIODIC_ENVELOPE },
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R16_R21),
.driver_data = PIDFF_QUIRK_FIX_WHEEL_DIRECTION | PIDFF_QUIRK_FIX_PERIODIC_ENVELOPE },
{ }
};
MODULE_DEVICE_TABLE(hid, pidff_wheel_devices);
static u8 *moza_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
// Fix data type on PID Device Control
if (rdesc[1002] == 0x91 && rdesc[1003] == 0x02) {
rdesc[1003] = 0x00; // Fix header, it needs to be Array.
}
return rdesc;
}
static u8 *universal_pidff_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
if (hdev->vendor == USB_VENDOR_ID_MOZA) {
return moza_report_fixup(hdev, rdesc, rsize);
}
return rdesc;
}
/*
* Check if the device is PID and initialize it
* Add quirks after initialisation
*/
static int universal_pidff_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
int ret;
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "parse failed\n");
goto err;
}
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
if (ret) {
hid_err(hdev, "hw start failed\n");
goto err;
}
ret = hid_pidff_init_with_quirks(hdev, id);
if (ret) {
hid_warn(hdev, "Force feedback not supported\n");
goto err;
}
return 0;
err:
return ret;
}
static int universal_pidff_input_configured(struct hid_device *hdev,
struct hid_input *hidinput)
{
// Remove fuzz and deadzone
struct input_dev *input = hidinput->input;
input_set_abs_params(input, ABS_X,
input->absinfo[ABS_X].minimum, input->absinfo[ABS_X].maximum, 0, 0);
return 0;
}
static struct hid_driver universal_pidff = {
.name = "hid-universal-pidff",
.id_table = pidff_wheel_devices,
.probe = universal_pidff_probe,
.input_configured = universal_pidff_input_configured,
.report_fixup = universal_pidff_report_fixup
};
module_hid_driver(universal_pidff);
MODULE_AUTHOR("Oleg Makarenko <oleg@makarenk.ooo>");
MODULE_DESCRIPTION("Universal HID PIDFF Driver");
MODULE_LICENSE("GPL");

View File

@@ -3,6 +3,8 @@
* Force feedback driver for USB HID PID compliant devices * Force feedback driver for USB HID PID compliant devices
* *
* Copyright (c) 2005, 2006 Anssi Hannula <anssi.hannula@gmail.com> * Copyright (c) 2005, 2006 Anssi Hannula <anssi.hannula@gmail.com>
* Copyright (c) 2024 Makarenko Oleg <oleg@makarenk.ooo>
* Copyright (c) 2024 Tomasz Pakuła <tomasz@pakula.org>
*/ */
/* /*
@@ -19,9 +21,18 @@
#include <linux/hid.h> #include <linux/hid.h>
//#include "usbhid.h" //#include "usbhid.h"
#include "hid-pidff.h"
#define PID_EFFECTS_MAX 64 #define PID_EFFECTS_MAX 64
/*
* This is 16384 or 90 degrees in polar coordinates (up)
* Racing games exclusively use polar coordinates but sometimes
* SDL/Proton mess with this value as they try to do
* some conversions to fix FFB for flight sticks
*/
#define PIDFF_FIXED_DIRECTION 0x4000
/* Report usage table used to put reports into an array */ /* Report usage table used to put reports into an array */
#define PID_SET_EFFECT 0 #define PID_SET_EFFECT 0
@@ -62,6 +73,10 @@ static const u8 pidff_set_effect[] = {
0x22, 0x50, 0x52, 0x53, 0x54, 0x56, 0xa7 0x22, 0x50, 0x52, 0x53, 0x54, 0x56, 0xa7
}; };
static const u8 pidff_set_effect_without_delay[] = {
0x22, 0x50, 0x52, 0x53, 0x54, 0x56
};
#define PID_ATTACK_LEVEL 1 #define PID_ATTACK_LEVEL 1
#define PID_ATTACK_TIME 2 #define PID_ATTACK_TIME 2
#define PID_FADE_LEVEL 3 #define PID_FADE_LEVEL 3
@@ -184,6 +199,7 @@ struct pidff_device {
int operation_id[sizeof(pidff_effect_operation_status)]; int operation_id[sizeof(pidff_effect_operation_status)];
int pid_id[PID_EFFECTS_MAX]; int pid_id[PID_EFFECTS_MAX];
unsigned quirks;
}; };
/* /*
@@ -205,6 +221,36 @@ static int pidff_rescale_signed(int i, struct hid_field *field)
field->logical_minimum / -0x8000; field->logical_minimum / -0x8000;
} }
/*
* Clamp minimum value for the given field
*/
static int pidff_clamp_min(int i, struct hid_field *field)
{
int ret = i < field->logical_minimum ? field->logical_minimum : i;
pr_debug("clamped min value from %d to %d\n", i, ret);
return ret;
}
/*
* Clamp maximum value for the given field
*/
static int pidff_clamp_max(int i, struct hid_field *field)
{
int ret = i > field->logical_maximum ? field->logical_maximum : i;
pr_debug("clamped max value from %d to %d\n", i, ret);
return ret;
}
/*
* Clamp value for the given field
*/
static int pidff_clamp(int i, struct hid_field *field)
{
i = pidff_clamp_min(i, field);
i = pidff_clamp_max(i, field);
return i;
}
static void pidff_set(struct pidff_usage *usage, u16 value) static void pidff_set(struct pidff_usage *usage, u16 value)
{ {
usage->value[0] = pidff_rescale(value, 0xffff, usage->field); usage->value[0] = pidff_rescale(value, 0xffff, usage->field);
@@ -247,7 +293,11 @@ static void pidff_set_envelope_report(struct pidff_device *pidff,
pidff->set_envelope[PID_ATTACK_TIME].value[0] = envelope->attack_length; pidff->set_envelope[PID_ATTACK_TIME].value[0] = envelope->attack_length;
pidff->set_envelope[PID_FADE_TIME].value[0] = envelope->fade_length; pidff->set_envelope[PID_FADE_TIME].value[0] = envelope->fade_length;
hid_dbg(pidff->hid, "attack %u => %d\n", hid_dbg(pidff->hid, "attack level %u => %d\n",
envelope->attack_level,
pidff->set_envelope[PID_ATTACK_LEVEL].value[0]);
hid_dbg(pidff->hid, "fade level %u => %d\n",
envelope->attack_level, envelope->attack_level,
pidff->set_envelope[PID_ATTACK_LEVEL].value[0]); pidff->set_envelope[PID_ATTACK_LEVEL].value[0]);
@@ -297,22 +347,31 @@ static int pidff_needs_set_constant(struct ff_effect *effect,
static void pidff_set_effect_report(struct pidff_device *pidff, static void pidff_set_effect_report(struct pidff_device *pidff,
struct ff_effect *effect) struct ff_effect *effect)
{ {
/* check for device quirks */
unsigned short direction = effect->direction;
if (pidff->quirks & PIDFF_QUIRK_FIX_WHEEL_DIRECTION)
direction = PIDFF_FIXED_DIRECTION;
pidff->set_effect[PID_EFFECT_BLOCK_INDEX].value[0] = pidff->set_effect[PID_EFFECT_BLOCK_INDEX].value[0] =
pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]; pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
pidff->set_effect_type->value[0] = pidff->set_effect_type->value[0] =
pidff->create_new_effect_type->value[0]; pidff->create_new_effect_type->value[0];
pidff->set_effect[PID_DURATION].value[0] = pidff->set_effect[PID_DURATION].value[0] =
effect->replay.length == 0 ? 0xffff: effect->replay.length; effect->replay.length == 0 ? 0xffff : effect->replay.length;
pidff->set_effect[PID_TRIGGER_BUTTON].value[0] = effect->trigger.button; pidff->set_effect[PID_TRIGGER_BUTTON].value[0] =
effect->trigger.button;
pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] = pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] =
effect->trigger.interval; effect->trigger.interval;
pidff->set_effect[PID_GAIN].value[0] = pidff->set_effect[PID_GAIN].value[0] =
pidff->set_effect[PID_GAIN].field->logical_maximum; pidff->set_effect[PID_GAIN].field->logical_maximum;
pidff->set_effect[PID_DIRECTION_ENABLE].value[0] = 1; pidff->set_effect[PID_DIRECTION_ENABLE].value[0] = 1;
pidff->effect_direction->value[0] = pidff->effect_direction->value[0] =
pidff_rescale(effect->direction, 0xffff, pidff_rescale(direction, 0xffff, pidff->effect_direction);
pidff->effect_direction);
pidff->set_effect[PID_START_DELAY].value[0] = effect->replay.delay; if (!(pidff->quirks & PIDFF_QUIRK_NO_DELAY_EFFECT)) {
pidff->set_effect[PID_START_DELAY].value[0] = effect->replay.delay;
}
hid_hw_request(pidff->hid, pidff->reports[PID_SET_EFFECT], hid_hw_request(pidff->hid, pidff->reports[PID_SET_EFFECT],
HID_REQ_SET_REPORT); HID_REQ_SET_REPORT);
@@ -344,7 +403,12 @@ static void pidff_set_periodic_report(struct pidff_device *pidff,
pidff_set_signed(&pidff->set_periodic[PID_OFFSET], pidff_set_signed(&pidff->set_periodic[PID_OFFSET],
effect->u.periodic.offset); effect->u.periodic.offset);
pidff_set(&pidff->set_periodic[PID_PHASE], effect->u.periodic.phase); pidff_set(&pidff->set_periodic[PID_PHASE], effect->u.periodic.phase);
pidff->set_periodic[PID_PERIOD].value[0] = effect->u.periodic.period; // Actually we just can use clamp macro
// from include/linux/kernel.h#L59
// But for the debug purposes we're leaving it as is
pidff->set_periodic[PID_PERIOD].value[0] =
pidff_clamp(effect->u.periodic.period,
pidff->set_periodic[PID_PERIOD].field);
hid_hw_request(pidff->hid, pidff->reports[PID_SET_PERIODIC], hid_hw_request(pidff->hid, pidff->reports[PID_SET_PERIODIC],
HID_REQ_SET_REPORT); HID_REQ_SET_REPORT);
@@ -499,12 +563,13 @@ static void pidff_playback_pid(struct pidff_device *pidff, int pid_id, int n)
} else { } else {
pidff->effect_operation_status->value[0] = pidff->effect_operation_status->value[0] =
pidff->operation_id[PID_EFFECT_START]; pidff->operation_id[PID_EFFECT_START];
n = clamp(n, pidff->effect_operation[PID_LOOP_COUNT].field->logical_minimum, n = clamp(n, pidff->effect_operation[PID_LOOP_COUNT].field->logical_minimum,
pidff->effect_operation[PID_LOOP_COUNT].field->logical_maximum); pidff->effect_operation[PID_LOOP_COUNT].field->logical_maximum);
pidff->effect_operation[PID_LOOP_COUNT].value[0] = n; pidff->effect_operation[PID_LOOP_COUNT].value[0] = n;
} }
hid_dbg(pidff->hid, "Playing effect %d %d times\n", pid_id, n);
hid_hw_request(pidff->hid, pidff->reports[PID_EFFECT_OPERATION], hid_hw_request(pidff->hid, pidff->reports[PID_EFFECT_OPERATION],
HID_REQ_SET_REPORT); HID_REQ_SET_REPORT);
} }
@@ -565,7 +630,7 @@ static int pidff_upload_effect(struct input_dev *dev, struct ff_effect *effect,
pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] = pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] =
pidff->pid_id[effect->id]; pidff->pid_id[effect->id];
} }
hid_dbg(pidff->hid, "Upload effect 0x%02x,", effect->type);
switch (effect->type) { switch (effect->type) {
case FF_CONSTANT: case FF_CONSTANT:
if (!old) { if (!old) {
@@ -617,6 +682,17 @@ static int pidff_upload_effect(struct input_dev *dev, struct ff_effect *effect,
pidff_set_effect_report(pidff, effect); pidff_set_effect_report(pidff, effect);
if (!old || pidff_needs_set_periodic(effect, old)) if (!old || pidff_needs_set_periodic(effect, old))
pidff_set_periodic_report(pidff, effect); pidff_set_periodic_report(pidff, effect);
if (pidff->quirks & PIDFF_QUIRK_FIX_PERIODIC_ENVELOPE)
{
effect->u.periodic.envelope.attack_level =
effect->u.periodic.envelope.attack_level == 0
? 0x7fff : effect->u.periodic.envelope.attack_level;
effect->u.periodic.envelope.fade_level =
effect->u.periodic.envelope.fade_level == 0
? 0x7fff : effect->u.periodic.envelope.fade_level;
}
if (!old || if (!old ||
pidff_needs_set_envelope(&effect->u.periodic.envelope, pidff_needs_set_envelope(&effect->u.periodic.envelope,
&old->u.periodic.envelope)) &old->u.periodic.envelope))
@@ -740,7 +816,9 @@ static void pidff_autocenter(struct pidff_device *pidff, u16 magnitude)
pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] = 0; pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] = 0;
pidff_set(&pidff->set_effect[PID_GAIN], magnitude); pidff_set(&pidff->set_effect[PID_GAIN], magnitude);
pidff->set_effect[PID_DIRECTION_ENABLE].value[0] = 1; pidff->set_effect[PID_DIRECTION_ENABLE].value[0] = 1;
pidff->set_effect[PID_START_DELAY].value[0] = 0; if (!(pidff->quirks & PIDFF_QUIRK_NO_DELAY_EFFECT)) {
pidff->set_effect[PID_START_DELAY].value[0] = 0;
}
hid_hw_request(pidff->hid, pidff->reports[PID_SET_EFFECT], hid_hw_request(pidff->hid, pidff->reports[PID_SET_EFFECT],
HID_REQ_SET_REPORT); HID_REQ_SET_REPORT);
@@ -1068,10 +1146,23 @@ static int pidff_init_fields(struct pidff_device *pidff, struct input_dev *dev)
{ {
int envelope_ok = 0; int envelope_ok = 0;
if (PIDFF_FIND_FIELDS(set_effect, PID_SET_EFFECT, 1)) { if (pidff->quirks & PIDFF_QUIRK_NO_DELAY_EFFECT) {
hid_err(pidff->hid, "unknown set_effect report layout\n"); hid_dbg(pidff->hid, "Find fields for set_effect without delay\n");
return -ENODEV; if (pidff_find_fields(pidff->set_effect,
pidff_set_effect_without_delay,
pidff->reports[PID_SET_EFFECT], \
sizeof(pidff_set_effect_without_delay), 1)) {
hid_err(pidff->hid, "unknown set_effect report layout\n");
return -ENODEV;
}
} }
else {
if (PIDFF_FIND_FIELDS(set_effect, PID_SET_EFFECT, 1)) {
hid_err(pidff->hid, "unknown set_effect report layout\n");
return -ENODEV;
}
}
PIDFF_FIND_FIELDS(block_load, PID_BLOCK_LOAD, 0); PIDFF_FIND_FIELDS(block_load, PID_BLOCK_LOAD, 0);
if (!pidff->block_load[PID_EFFECT_BLOCK_INDEX].value) { if (!pidff->block_load[PID_EFFECT_BLOCK_INDEX].value) {
@@ -1154,7 +1245,9 @@ static void pidff_reset(struct pidff_device *pidff)
struct hid_device *hid = pidff->hid; struct hid_device *hid = pidff->hid;
int i = 0; int i = 0;
hid_dbg(hid, "%s: Resetting the device...", __func__);
pidff->device_control->value[0] = pidff->control_id[PID_RESET]; pidff->device_control->value[0] = pidff->control_id[PID_RESET];
hid_dbg(hid, "%s: PID_RESET control_id: %02x", __func__, pidff->device_control->value[0]);
/* We reset twice as sometimes hid_wait_io isn't waiting long enough */ /* We reset twice as sometimes hid_wait_io isn't waiting long enough */
hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT); hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT);
hid_hw_wait(hid); hid_hw_wait(hid);
@@ -1163,6 +1256,7 @@ static void pidff_reset(struct pidff_device *pidff)
pidff->device_control->value[0] = pidff->device_control->value[0] =
pidff->control_id[PID_ENABLE_ACTUATORS]; pidff->control_id[PID_ENABLE_ACTUATORS];
hid_dbg(hid, "%s: PID_ENABLE_ACTUATORS control_id: %02x", __func__, pidff->device_control->value[0]);
hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT); hid_hw_request(hid, pidff->reports[PID_DEVICE_CONTROL], HID_REQ_SET_REPORT);
hid_hw_wait(hid); hid_hw_wait(hid);
@@ -1184,6 +1278,7 @@ static void pidff_reset(struct pidff_device *pidff)
hid_hw_wait(hid); hid_hw_wait(hid);
} }
} }
hid_dbg(hid, "%s: Resetting device complete", __func__);
} }
/* /*
@@ -1227,7 +1322,7 @@ static int pidff_check_autocenter(struct pidff_device *pidff,
/* /*
* Check if the device is PID and initialize it * Check if the device is PID and initialize it
*/ */
int hid_pidff_init_moza(struct hid_device *hid) int hid_pidff_init(struct hid_device *hid)
{ {
struct pidff_device *pidff; struct pidff_device *pidff;
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input *hidinput = list_entry(hid->inputs.next,
@@ -1314,7 +1409,7 @@ int hid_pidff_init_moza(struct hid_device *hid)
ff->set_autocenter = pidff_set_autocenter; ff->set_autocenter = pidff_set_autocenter;
ff->playback = pidff_playback; ff->playback = pidff_playback;
hid_info(dev, "Force feedback for Moza wheel\n"); hid_info(dev, "Force feedback for USB HID PID devices\n");
hid_device_io_stop(hid); hid_device_io_stop(hid);
@@ -1326,3 +1421,29 @@ int hid_pidff_init_moza(struct hid_device *hid)
kfree(pidff); kfree(pidff);
return error; return error;
} }
/*
* Check if the device is PID and initialize it
* Add quirks after initialisation
*/
int hid_pidff_init_with_quirks(struct hid_device *hid, const struct hid_device_id *id)
{
int error = hid_pidff_init(hid);
if (error)
return error;
struct hid_input *hidinput = list_entry(hid->inputs.next,
struct hid_input, list);
struct input_dev *dev = hidinput->input;
struct pidff_device *pidff = dev->ff->private;
/* set quirks on the pidff device */
hid_device_io_start(hid);
pidff->quirks |= id->driver_data;
hid_device_io_stop(hid);
hid_dbg(dev, "Device quirks: %d\n", pidff->quirks);
return 0;
}

26
hid-pidff.h Normal file
View File

@@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __HID_PIDFF_H
#define __HID_PIDFF_H
/* PIDFF Quirks to solve issues with certain devices */
/*
* Always set a value > 0 for PERIODIC envelope attack and fade level
*/
#define PIDFF_QUIRK_FIX_PERIODIC_ENVELOPE BIT(0)
/*
* Ignore direction and always set 16384 (0x4000)
*/
#define PIDFF_QUIRK_FIX_WHEEL_DIRECTION BIT(1)
/*
* Skip initialization of 0xA7 descriptor (Delay effect)
*/
#define PIDFF_QUIRK_NO_DELAY_EFFECT BIT(2)
int hid_pidff_init(struct hid_device *hid);
int hid_pidff_init_with_quirks(struct hid_device *hid, const struct hid_device_id *id);
#endif