Skip to content

Commit f45f26a

Browse files
Lawstorantgregkh
authored andcommitted
HID: Add hid-universal-pidff driver and supported device ids
[ Upstream commit f06bf8d ] 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 <[email protected]> Signed-off-by: Makarenko Oleg <[email protected]> Signed-off-by: Tomasz Pakuła <[email protected]> Reviewed-by: Michał Kopeć <[email protected]> Reviewed-by: Paul Dino Jones <[email protected]> Tested-by: Cristóferson Bueno <[email protected]> Tested-by: Pablo Cisneros <[email protected]> Signed-off-by: Jiri Kosina <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent 116d4f6 commit f45f26a

File tree

4 files changed

+238
-0
lines changed

4 files changed

+238
-0
lines changed

drivers/hid/Kconfig

+14
Original file line numberDiff line numberDiff line change
@@ -1205,6 +1205,20 @@ config HID_U2FZERO
12051205
allow setting the brightness to anything but 1, which will
12061206
trigger a single blink and immediately reset back to 0.
12071207

1208+
config HID_UNIVERSAL_PIDFF
1209+
tristate "universal-pidff: extended USB PID driver compatibility and usage"
1210+
depends on USB_HID
1211+
depends on HID_PID
1212+
help
1213+
Extended PID support for selected devices.
1214+
1215+
Contains report fixups, extended usable button range and
1216+
pidff quirk management to extend compatibility with slightly
1217+
non-compliant USB PID devices and better fuzz/flat values for
1218+
high precision direct drive devices.
1219+
1220+
Supports Moza Racing, Cammus, VRS, FFBeast and more.
1221+
12081222
config HID_WACOM
12091223
tristate "Wacom Intuos/Graphire tablet support (USB)"
12101224
depends on USB_HID

drivers/hid/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ hid-uclogic-objs := hid-uclogic-core.o \
139139
hid-uclogic-params.o
140140
obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o
141141
obj-$(CONFIG_HID_UDRAW_PS3) += hid-udraw-ps3.o
142+
obj-$(CONFIG_HID_UNIVERSAL_PIDFF) += hid-universal-pidff.o
142143
obj-$(CONFIG_HID_LED) += hid-led.o
143144
obj-$(CONFIG_HID_XIAOMI) += hid-xiaomi.o
144145
obj-$(CONFIG_HID_XINMO) += hid-xinmo.o

drivers/hid/hid-ids.h

+31
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,10 @@
262262
#define USB_DEVICE_ID_BTC_EMPREX_REMOTE 0x5578
263263
#define USB_DEVICE_ID_BTC_EMPREX_REMOTE_2 0x5577
264264

265+
#define USB_VENDOR_ID_CAMMUS 0x3416
266+
#define USB_DEVICE_ID_CAMMUS_C5 0x0301
267+
#define USB_DEVICE_ID_CAMMUS_C12 0x0302
268+
265269
#define USB_VENDOR_ID_CANDO 0x2087
266270
#define USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH 0x0703
267271
#define USB_DEVICE_ID_CANDO_MULTI_TOUCH 0x0a01
@@ -453,6 +457,11 @@
453457
#define USB_VENDOR_ID_EVISION 0x320f
454458
#define USB_DEVICE_ID_EVISION_ICL01 0x5041
455459

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

@@ -813,6 +822,13 @@
813822
#define I2C_DEVICE_ID_LG_8001 0x8001
814823
#define I2C_DEVICE_ID_LG_7010 0x7010
815824

825+
#define USB_VENDOR_ID_LITE_STAR 0x11ff
826+
#define USB_DEVICE_ID_PXN_V10 0x3245
827+
#define USB_DEVICE_ID_PXN_V12 0x1212
828+
#define USB_DEVICE_ID_PXN_V12_LITE 0x1112
829+
#define USB_DEVICE_ID_PXN_V12_LITE_2 0x1211
830+
#define USB_DEVICE_LITE_STAR_GT987_FF 0x2141
831+
816832
#define USB_VENDOR_ID_LOGITECH 0x046d
817833
#define USB_DEVICE_ID_LOGITECH_Z_10_SPK 0x0a07
818834
#define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e
@@ -960,6 +976,18 @@
960976
#define USB_VENDOR_ID_MONTEREY 0x0566
961977
#define USB_DEVICE_ID_GENIUS_KB29E 0x3004
962978

979+
#define USB_VENDOR_ID_MOZA 0x346e
980+
#define USB_DEVICE_ID_MOZA_R3 0x0005
981+
#define USB_DEVICE_ID_MOZA_R3_2 0x0015
982+
#define USB_DEVICE_ID_MOZA_R5 0x0004
983+
#define USB_DEVICE_ID_MOZA_R5_2 0x0014
984+
#define USB_DEVICE_ID_MOZA_R9 0x0002
985+
#define USB_DEVICE_ID_MOZA_R9_2 0x0012
986+
#define USB_DEVICE_ID_MOZA_R12 0x0006
987+
#define USB_DEVICE_ID_MOZA_R12_2 0x0016
988+
#define USB_DEVICE_ID_MOZA_R16_R21 0x0000
989+
#define USB_DEVICE_ID_MOZA_R16_R21_2 0x0010
990+
963991
#define USB_VENDOR_ID_MSI 0x1770
964992
#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00
965993

@@ -1371,6 +1399,9 @@
13711399
#define USB_DEVICE_ID_VELLEMAN_K8061_FIRST 0x8061
13721400
#define USB_DEVICE_ID_VELLEMAN_K8061_LAST 0x8068
13731401

1402+
#define USB_VENDOR_ID_VRS 0x0483
1403+
#define USB_DEVICE_ID_VRS_DFP 0xa355
1404+
13741405
#define USB_VENDOR_ID_VTL 0x0306
13751406
#define USB_DEVICE_ID_VTL_MULTITOUCH_FF3F 0xff3f
13761407

drivers/hid/hid-universal-pidff.c

+192
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 <[email protected]>");
192+
MODULE_AUTHOR("Tomasz Pakuła <[email protected]>");

0 commit comments

Comments
 (0)