Skip to content

Commit f06bf8d

Browse files
LawstorantJiri Kosina
authored andcommitted
HID: Add hid-universal-pidff driver and supported device ids
Extend pidff compatibility, usable button range, manage pidff quirks and set improved fuzz/flat default for high precision devices. Possibility of fixing device descriptors in the future if such needs arises. As many of PID devices are quite similar and not dependent on custom drivers, this one can handle all of PID devices which need special care. Numerous sim racing/sim flight bases report a lot of buttons in excess of 100. Moza Racing exposes 128 of them and thus the need to extend the available range. All the included devices were tested and confirmed working with the help of the sim racing community. Changes in v6: - Support "split" devices with a separate "input device" for buttons - Fixed comment styling Co-developed-by: Makarenko Oleg <oleg@makarenk.ooo> Signed-off-by: Makarenko Oleg <oleg@makarenk.ooo> Signed-off-by: Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com> Reviewed-by: Michał Kopeć <michal@nozomi.space> Reviewed-by: Paul Dino Jones <paul@spacefreak18.xyz> Tested-by: Cristóferson Bueno <cbueno81@gmail.com> Tested-by: Pablo Cisneros <patchkez@protonmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.com>
1 parent ce52c0c commit f06bf8d

4 files changed

Lines changed: 238 additions & 0 deletions

File tree

drivers/hid/Kconfig

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

1220+
config HID_UNIVERSAL_PIDFF
1221+
tristate "universal-pidff: extended USB PID driver compatibility and usage"
1222+
depends on USB_HID
1223+
depends on HID_PID
1224+
help
1225+
Extended PID support for selected devices.
1226+
1227+
Contains report fixups, extended usable button range and
1228+
pidff quirk management to extend compatibility with slightly
1229+
non-compliant USB PID devices and better fuzz/flat values for
1230+
high precision direct drive devices.
1231+
1232+
Supports Moza Racing, Cammus, VRS, FFBeast and more.
1233+
12201234
config HID_WACOM
12211235
tristate "Wacom Intuos/Graphire tablet support (USB)"
12221236
depends on USB_HID

drivers/hid/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ hid-uclogic-objs := hid-uclogic-core.o \
140140
hid-uclogic-params.o
141141
obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o
142142
obj-$(CONFIG_HID_UDRAW_PS3) += hid-udraw-ps3.o
143+
obj-$(CONFIG_HID_UNIVERSAL_PIDFF) += hid-universal-pidff.o
143144
obj-$(CONFIG_HID_LED) += hid-led.o
144145
obj-$(CONFIG_HID_XIAOMI) += hid-xiaomi.o
145146
obj-$(CONFIG_HID_XINMO) += hid-xinmo.o

drivers/hid/hid-ids.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,10 @@
261261
#define USB_DEVICE_ID_BTC_EMPREX_REMOTE 0x5578
262262
#define USB_DEVICE_ID_BTC_EMPREX_REMOTE_2 0x5577
263263

264+
#define USB_VENDOR_ID_CAMMUS 0x3416
265+
#define USB_DEVICE_ID_CAMMUS_C5 0x0301
266+
#define USB_DEVICE_ID_CAMMUS_C12 0x0302
267+
264268
#define USB_VENDOR_ID_CANDO 0x2087
265269
#define USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH 0x0703
266270
#define USB_DEVICE_ID_CANDO_MULTI_TOUCH 0x0a01
@@ -452,6 +456,11 @@
452456
#define USB_VENDOR_ID_EVISION 0x320f
453457
#define USB_DEVICE_ID_EVISION_ICL01 0x5041
454458

459+
#define USB_VENDOR_ID_FFBEAST 0x045b
460+
#define USB_DEVICE_ID_FFBEAST_JOYSTICK 0x58f9
461+
#define USB_DEVICE_ID_FFBEAST_RUDDER 0x5968
462+
#define USB_DEVICE_ID_FFBEAST_WHEEL 0x59d7
463+
455464
#define USB_VENDOR_ID_FLATFROG 0x25b5
456465
#define USB_DEVICE_ID_MULTITOUCH_3200 0x0002
457466

@@ -816,6 +825,13 @@
816825
#define I2C_DEVICE_ID_LG_8001 0x8001
817826
#define I2C_DEVICE_ID_LG_7010 0x7010
818827

828+
#define USB_VENDOR_ID_LITE_STAR 0x11ff
829+
#define USB_DEVICE_ID_PXN_V10 0x3245
830+
#define USB_DEVICE_ID_PXN_V12 0x1212
831+
#define USB_DEVICE_ID_PXN_V12_LITE 0x1112
832+
#define USB_DEVICE_ID_PXN_V12_LITE_2 0x1211
833+
#define USB_DEVICE_LITE_STAR_GT987_FF 0x2141
834+
819835
#define USB_VENDOR_ID_LOGITECH 0x046d
820836
#define USB_DEVICE_ID_LOGITECH_Z_10_SPK 0x0a07
821837
#define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e
@@ -963,6 +979,18 @@
963979
#define USB_VENDOR_ID_MONTEREY 0x0566
964980
#define USB_DEVICE_ID_GENIUS_KB29E 0x3004
965981

982+
#define USB_VENDOR_ID_MOZA 0x346e
983+
#define USB_DEVICE_ID_MOZA_R3 0x0005
984+
#define USB_DEVICE_ID_MOZA_R3_2 0x0015
985+
#define USB_DEVICE_ID_MOZA_R5 0x0004
986+
#define USB_DEVICE_ID_MOZA_R5_2 0x0014
987+
#define USB_DEVICE_ID_MOZA_R9 0x0002
988+
#define USB_DEVICE_ID_MOZA_R9_2 0x0012
989+
#define USB_DEVICE_ID_MOZA_R12 0x0006
990+
#define USB_DEVICE_ID_MOZA_R12_2 0x0016
991+
#define USB_DEVICE_ID_MOZA_R16_R21 0x0000
992+
#define USB_DEVICE_ID_MOZA_R16_R21_2 0x0010
993+
966994
#define USB_VENDOR_ID_MSI 0x1770
967995
#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00
968996

@@ -1374,6 +1402,9 @@
13741402
#define USB_DEVICE_ID_VELLEMAN_K8061_FIRST 0x8061
13751403
#define USB_DEVICE_ID_VELLEMAN_K8061_LAST 0x8068
13761404

1405+
#define USB_VENDOR_ID_VRS 0x0483
1406+
#define USB_DEVICE_ID_VRS_DFP 0xa355
1407+
13771408
#define USB_VENDOR_ID_VTL 0x0306
13781409
#define USB_DEVICE_ID_VTL_MULTITOUCH_FF3F 0xff3f
13791410

drivers/hid/hid-universal-pidff.c

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

0 commit comments

Comments
 (0)