Skip to content

Commit 765b8aa

Browse files
author
Jiri Kosina
committed
Merge branch 'for-6.15/pidff' into for-linus
From: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com> This patch series is focused on improving the compatibility and usability of the hid-pidff force feedback driver. Last patch introduces a new, universal driver for PID devices that need some special handling like report fixups, remapping the button range, managing new pidff quirks and setting desirable fuzz/flat values. This work has been done in the span of the past months with the help of the great Linux simracing community, with a little input from sim flight fans from FFBeast. No changes interfere with compliant and currently working PID devices. "Generic" codepath was tested as well with Moza and Simxperience AccuForce v2. I'm not married to the name. It's what we used previously, but if "universal" is confusing (pidff is already the generic driver), we can come up with something better like "hid-quirky-pidff" :) With v8 and tiny finx in v9, all the outstanding issues were resolved, additional pidff issues were fixed and hid-pidff defines moved to a dedicated header file. This patch series could be considered done bar any comments and requests from input maintainers. I could save more then a dozen lines of code by changing simple if statements to only occupy on line instead of two in there's a need for that.
2 parents 867bc16 + e2fa0bd commit 765b8aa

File tree

9 files changed

+668
-203
lines changed

9 files changed

+668
-203
lines changed

MAINTAINERS

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10316,6 +10316,14 @@ F: drivers/hid/hid-sensor-*
1031610316
F: drivers/iio/*/hid-*
1031710317
F: include/linux/hid-sensor-*
1031810318

10319+
HID UNIVERSAL PIDFF DRIVER
10320+
M: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
10321+
M: Oleg Makarenko <oleg@makarenk.ooo>
10322+
L: linux-input@vger.kernel.org
10323+
S: Maintained
10324+
B: https://github.com/JacKeTUs/universal-pidff/issues
10325+
F: drivers/hid/hid-universal-pidff.c
10326+
1031910327
HID VRC-2 CAR CONTROLLER DRIVER
1032010328
M: Marcus Folkesson <marcus.folkesson@gmail.com>
1032110329
L: linux-input@vger.kernel.org

drivers/hid/Kconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1246,6 +1246,20 @@ config HID_U2FZERO
12461246
allow setting the brightness to anything but 1, which will
12471247
trigger a single blink and immediately reset back to 0.
12481248

1249+
config HID_UNIVERSAL_PIDFF
1250+
tristate "universal-pidff: extended USB PID driver compatibility and usage"
1251+
depends on USB_HID
1252+
depends on HID_PID
1253+
help
1254+
Extended PID support for selected devices.
1255+
1256+
Contains report fixups, extended usable button range and
1257+
pidff quirk management to extend compatibility with slightly
1258+
non-compliant USB PID devices and better fuzz/flat values for
1259+
high precision direct drive devices.
1260+
1261+
Supports Moza Racing, Cammus, VRS, FFBeast and more.
1262+
12491263
config HID_WACOM
12501264
tristate "Wacom Intuos/Graphire tablet support (USB)"
12511265
depends on USB_HID

drivers/hid/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ hid-uclogic-objs := hid-uclogic-core.o \
142142
hid-uclogic-params.o
143143
obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o
144144
obj-$(CONFIG_HID_UDRAW_PS3) += hid-udraw-ps3.o
145+
obj-$(CONFIG_HID_UNIVERSAL_PIDFF) += hid-universal-pidff.o
145146
obj-$(CONFIG_HID_LED) += hid-led.o
146147
obj-$(CONFIG_HID_XIAOMI) += hid-xiaomi.o
147148
obj-$(CONFIG_HID_XINMO) += hid-xinmo.o

drivers/hid/hid-ids.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,12 @@
190190
#define USB_DEVICE_ID_APPLE_TOUCHBAR_BACKLIGHT 0x8102
191191
#define USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY 0x8302
192192

193+
#define USB_VENDOR_ID_ASETEK 0x2433
194+
#define USB_DEVICE_ID_ASETEK_INVICTA 0xf300
195+
#define USB_DEVICE_ID_ASETEK_FORTE 0xf301
196+
#define USB_DEVICE_ID_ASETEK_LA_PRIMA 0xf303
197+
#define USB_DEVICE_ID_ASETEK_TONY_KANAAN 0xf306
198+
193199
#define USB_VENDOR_ID_ASUS 0x0486
194200
#define USB_DEVICE_ID_ASUS_T91MT 0x0185
195201
#define USB_DEVICE_ID_ASUSTEK_MULTITOUCH_YFO 0x0186
@@ -262,6 +268,10 @@
262268
#define USB_DEVICE_ID_BTC_EMPREX_REMOTE 0x5578
263269
#define USB_DEVICE_ID_BTC_EMPREX_REMOTE_2 0x5577
264270

271+
#define USB_VENDOR_ID_CAMMUS 0x3416
272+
#define USB_DEVICE_ID_CAMMUS_C5 0x0301
273+
#define USB_DEVICE_ID_CAMMUS_C12 0x0302
274+
265275
#define USB_VENDOR_ID_CANDO 0x2087
266276
#define USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH 0x0703
267277
#define USB_DEVICE_ID_CANDO_MULTI_TOUCH 0x0a01
@@ -453,6 +463,11 @@
453463
#define USB_VENDOR_ID_EVISION 0x320f
454464
#define USB_DEVICE_ID_EVISION_ICL01 0x5041
455465

466+
#define USB_VENDOR_ID_FFBEAST 0x045b
467+
#define USB_DEVICE_ID_FFBEAST_JOYSTICK 0x58f9
468+
#define USB_DEVICE_ID_FFBEAST_RUDDER 0x5968
469+
#define USB_DEVICE_ID_FFBEAST_WHEEL 0x59d7
470+
456471
#define USB_VENDOR_ID_FLATFROG 0x25b5
457472
#define USB_DEVICE_ID_MULTITOUCH_3200 0x0002
458473

@@ -817,6 +832,13 @@
817832
#define I2C_DEVICE_ID_LG_8001 0x8001
818833
#define I2C_DEVICE_ID_LG_7010 0x7010
819834

835+
#define USB_VENDOR_ID_LITE_STAR 0x11ff
836+
#define USB_DEVICE_ID_PXN_V10 0x3245
837+
#define USB_DEVICE_ID_PXN_V12 0x1212
838+
#define USB_DEVICE_ID_PXN_V12_LITE 0x1112
839+
#define USB_DEVICE_ID_PXN_V12_LITE_2 0x1211
840+
#define USB_DEVICE_LITE_STAR_GT987_FF 0x2141
841+
820842
#define USB_VENDOR_ID_LOGITECH 0x046d
821843
#define USB_DEVICE_ID_LOGITECH_Z_10_SPK 0x0a07
822844
#define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e
@@ -964,6 +986,18 @@
964986
#define USB_VENDOR_ID_MONTEREY 0x0566
965987
#define USB_DEVICE_ID_GENIUS_KB29E 0x3004
966988

989+
#define USB_VENDOR_ID_MOZA 0x346e
990+
#define USB_DEVICE_ID_MOZA_R3 0x0005
991+
#define USB_DEVICE_ID_MOZA_R3_2 0x0015
992+
#define USB_DEVICE_ID_MOZA_R5 0x0004
993+
#define USB_DEVICE_ID_MOZA_R5_2 0x0014
994+
#define USB_DEVICE_ID_MOZA_R9 0x0002
995+
#define USB_DEVICE_ID_MOZA_R9_2 0x0012
996+
#define USB_DEVICE_ID_MOZA_R12 0x0006
997+
#define USB_DEVICE_ID_MOZA_R12_2 0x0016
998+
#define USB_DEVICE_ID_MOZA_R16_R21 0x0000
999+
#define USB_DEVICE_ID_MOZA_R16_R21_2 0x0010
1000+
9671001
#define USB_VENDOR_ID_MSI 0x1770
9681002
#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00
9691003

@@ -1377,6 +1411,9 @@
13771411
#define USB_DEVICE_ID_VELLEMAN_K8061_FIRST 0x8061
13781412
#define USB_DEVICE_ID_VELLEMAN_K8061_LAST 0x8068
13791413

1414+
#define USB_VENDOR_ID_VRS 0x0483
1415+
#define USB_DEVICE_ID_VRS_DFP 0xa355
1416+
13801417
#define USB_VENDOR_ID_VTL 0x0306
13811418
#define USB_DEVICE_ID_VTL_MULTITOUCH_FF3F 0xff3f
13821419

drivers/hid/hid-universal-pidff.c

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
/*
3+
* HID UNIVERSAL PIDFF
4+
* hid-pidff wrapper for PID-enabled devices
5+
* Handles device reports, quirks and extends usable button range
6+
*
7+
* Copyright (c) 2024, 2025 Oleg Makarenko
8+
* Copyright (c) 2024, 2025 Tomasz Pakuła
9+
*/
10+
11+
#include <linux/device.h>
12+
#include <linux/hid.h>
13+
#include <linux/module.h>
14+
#include <linux/input-event-codes.h>
15+
#include "hid-ids.h"
16+
#include "usbhid/hid-pidff.h"
17+
18+
#define JOY_RANGE (BTN_DEAD - BTN_JOYSTICK + 1)
19+
20+
/*
21+
* Map buttons manually to extend the default joystick button limit
22+
*/
23+
static int universal_pidff_input_mapping(struct hid_device *hdev,
24+
struct hid_input *hi, struct hid_field *field, struct hid_usage *usage,
25+
unsigned long **bit, int *max)
26+
{
27+
if ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
28+
return 0;
29+
30+
if (field->application != HID_GD_JOYSTICK)
31+
return 0;
32+
33+
int button = ((usage->hid - 1) & HID_USAGE);
34+
int code = button + BTN_JOYSTICK;
35+
36+
/* Detect the end of JOYSTICK buttons range */
37+
if (code > BTN_DEAD)
38+
code = button + KEY_NEXT_FAVORITE - JOY_RANGE;
39+
40+
/*
41+
* Map overflowing buttons to KEY_RESERVED to not ignore
42+
* them and let them still trigger MSC_SCAN
43+
*/
44+
if (code > KEY_MAX)
45+
code = KEY_RESERVED;
46+
47+
hid_map_usage(hi, usage, bit, max, EV_KEY, code);
48+
hid_dbg(hdev, "Button %d: usage %d", button, code);
49+
return 1;
50+
}
51+
52+
/*
53+
* Check if the device is PID and initialize it
54+
* Add quirks after initialisation
55+
*/
56+
static int universal_pidff_probe(struct hid_device *hdev,
57+
const struct hid_device_id *id)
58+
{
59+
int i, error;
60+
error = hid_parse(hdev);
61+
if (error) {
62+
hid_err(hdev, "HID parse failed\n");
63+
goto err;
64+
}
65+
66+
error = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
67+
if (error) {
68+
hid_err(hdev, "HID hw start failed\n");
69+
goto err;
70+
}
71+
72+
/* Check if device contains PID usage page */
73+
error = 1;
74+
for (i = 0; i < hdev->collection_size; i++)
75+
if ((hdev->collection[i].usage & HID_USAGE_PAGE) == HID_UP_PID) {
76+
error = 0;
77+
hid_dbg(hdev, "PID usage page found\n");
78+
break;
79+
}
80+
81+
/*
82+
* Do not fail as this might be the second "device"
83+
* just for additional buttons/axes. Exit cleanly if force
84+
* feedback usage page wasn't found (included devices were
85+
* tested and confirmed to be USB PID after all).
86+
*/
87+
if (error) {
88+
hid_dbg(hdev, "PID usage page not found in the descriptor\n");
89+
return 0;
90+
}
91+
92+
/* Check if HID_PID support is enabled */
93+
int (*init_function)(struct hid_device *, u32);
94+
init_function = hid_pidff_init_with_quirks;
95+
96+
if (!init_function) {
97+
hid_warn(hdev, "HID_PID support not enabled!\n");
98+
return 0;
99+
}
100+
101+
error = init_function(hdev, id->driver_data);
102+
if (error) {
103+
hid_warn(hdev, "Error initialising force feedback\n");
104+
goto err;
105+
}
106+
107+
hid_info(hdev, "Universal pidff driver loaded successfully!");
108+
109+
return 0;
110+
err:
111+
return error;
112+
}
113+
114+
static int universal_pidff_input_configured(struct hid_device *hdev,
115+
struct hid_input *hidinput)
116+
{
117+
int axis;
118+
struct input_dev *input = hidinput->input;
119+
120+
if (!input->absinfo)
121+
return 0;
122+
123+
/* Decrease fuzz and deadzone on available axes */
124+
for (axis = ABS_X; axis <= ABS_BRAKE; axis++) {
125+
if (!test_bit(axis, input->absbit))
126+
continue;
127+
128+
input_set_abs_params(input, axis,
129+
input->absinfo[axis].minimum,
130+
input->absinfo[axis].maximum,
131+
axis == ABS_X ? 0 : 8, 0);
132+
}
133+
134+
/* Remove fuzz and deadzone from the second joystick axis */
135+
if (hdev->vendor == USB_VENDOR_ID_FFBEAST &&
136+
hdev->product == USB_DEVICE_ID_FFBEAST_JOYSTICK)
137+
input_set_abs_params(input, ABS_Y,
138+
input->absinfo[ABS_Y].minimum,
139+
input->absinfo[ABS_Y].maximum, 0, 0);
140+
141+
return 0;
142+
}
143+
144+
static const struct hid_device_id universal_pidff_devices[] = {
145+
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R3),
146+
.driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
147+
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R3_2),
148+
.driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
149+
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R5),
150+
.driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
151+
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R5_2),
152+
.driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
153+
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R9),
154+
.driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
155+
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R9_2),
156+
.driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
157+
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R12),
158+
.driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
159+
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R12_2),
160+
.driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
161+
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R16_R21),
162+
.driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
163+
{ HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R16_R21_2),
164+
.driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
165+
{ HID_USB_DEVICE(USB_VENDOR_ID_CAMMUS, USB_DEVICE_ID_CAMMUS_C5) },
166+
{ HID_USB_DEVICE(USB_VENDOR_ID_CAMMUS, USB_DEVICE_ID_CAMMUS_C12) },
167+
{ HID_USB_DEVICE(USB_VENDOR_ID_VRS, USB_DEVICE_ID_VRS_DFP),
168+
.driver_data = HID_PIDFF_QUIRK_PERMISSIVE_CONTROL },
169+
{ HID_USB_DEVICE(USB_VENDOR_ID_FFBEAST, USB_DEVICE_ID_FFBEAST_JOYSTICK), },
170+
{ HID_USB_DEVICE(USB_VENDOR_ID_FFBEAST, USB_DEVICE_ID_FFBEAST_RUDDER), },
171+
{ HID_USB_DEVICE(USB_VENDOR_ID_FFBEAST, USB_DEVICE_ID_FFBEAST_WHEEL) },
172+
{ HID_USB_DEVICE(USB_VENDOR_ID_LITE_STAR, USB_DEVICE_ID_PXN_V10),
173+
.driver_data = HID_PIDFF_QUIRK_PERIODIC_SINE_ONLY },
174+
{ HID_USB_DEVICE(USB_VENDOR_ID_LITE_STAR, USB_DEVICE_ID_PXN_V12),
175+
.driver_data = HID_PIDFF_QUIRK_PERIODIC_SINE_ONLY },
176+
{ HID_USB_DEVICE(USB_VENDOR_ID_LITE_STAR, USB_DEVICE_ID_PXN_V12_LITE),
177+
.driver_data = HID_PIDFF_QUIRK_PERIODIC_SINE_ONLY },
178+
{ HID_USB_DEVICE(USB_VENDOR_ID_LITE_STAR, USB_DEVICE_ID_PXN_V12_LITE_2),
179+
.driver_data = HID_PIDFF_QUIRK_PERIODIC_SINE_ONLY },
180+
{ HID_USB_DEVICE(USB_VENDOR_ID_LITE_STAR, USB_DEVICE_LITE_STAR_GT987_FF),
181+
.driver_data = HID_PIDFF_QUIRK_PERIODIC_SINE_ONLY },
182+
{ HID_USB_DEVICE(USB_VENDOR_ID_ASETEK, USB_DEVICE_ID_ASETEK_INVICTA) },
183+
{ HID_USB_DEVICE(USB_VENDOR_ID_ASETEK, USB_DEVICE_ID_ASETEK_FORTE) },
184+
{ HID_USB_DEVICE(USB_VENDOR_ID_ASETEK, USB_DEVICE_ID_ASETEK_LA_PRIMA) },
185+
{ HID_USB_DEVICE(USB_VENDOR_ID_ASETEK, USB_DEVICE_ID_ASETEK_TONY_KANAAN) },
186+
{ }
187+
};
188+
MODULE_DEVICE_TABLE(hid, universal_pidff_devices);
189+
190+
static struct hid_driver universal_pidff = {
191+
.name = "hid-universal-pidff",
192+
.id_table = universal_pidff_devices,
193+
.input_mapping = universal_pidff_input_mapping,
194+
.probe = universal_pidff_probe,
195+
.input_configured = universal_pidff_input_configured
196+
};
197+
module_hid_driver(universal_pidff);
198+
199+
MODULE_DESCRIPTION("Universal driver for USB PID Force Feedback devices");
200+
MODULE_LICENSE("GPL");
201+
MODULE_AUTHOR("Oleg Makarenko <oleg@makarenk.ooo>");
202+
MODULE_AUTHOR("Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>");

drivers/hid/usbhid/hid-core.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include <linux/hid-debug.h>
3636
#include <linux/hidraw.h>
3737
#include "usbhid.h"
38+
#include "hid-pidff.h"
3839

3940
/*
4041
* Version Information

0 commit comments

Comments
 (0)