]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
HID: Add hid-universal-pidff driver and supported device ids
authorTomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>
Sat, 1 Feb 2025 11:38:54 +0000 (12:38 +0100)
committerJiri Kosina <jkosina@suse.com>
Mon, 3 Feb 2025 14:17:02 +0000 (15:17 +0100)
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>
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-ids.h
drivers/hid/hid-universal-pidff.c [new file with mode: 0644]

index b53eb569bd4957bd3c6741e1d54c7638e6f79a95..e135f3962198e79040df3c6c2bbf81910d56919b 100644 (file)
@@ -1217,6 +1217,20 @@ config HID_U2FZERO
          allow setting the brightness to anything but 1, which will
          trigger a single blink and immediately reset back to 0.
 
+config HID_UNIVERSAL_PIDFF
+       tristate "universal-pidff: extended USB PID driver compatibility and usage"
+       depends on USB_HID
+       depends on HID_PID
+       help
+         Extended PID support for selected devices.
+
+         Contains report fixups, extended usable button range and
+         pidff quirk management to extend compatibility with slightly
+         non-compliant USB PID devices and better fuzz/flat values for
+         high precision direct drive devices.
+
+         Supports Moza Racing, Cammus, VRS, FFBeast and more.
+
 config HID_WACOM
        tristate "Wacom Intuos/Graphire tablet support (USB)"
        depends on USB_HID
index 482b096eea28087b01d667c0e2c83382c132f017..d19f96efa9cda0719ed64bc05c420ad5f12fcf96 100644 (file)
@@ -140,6 +140,7 @@ hid-uclogic-objs            := hid-uclogic-core.o \
                                   hid-uclogic-params.o
 obj-$(CONFIG_HID_UCLOGIC)      += hid-uclogic.o
 obj-$(CONFIG_HID_UDRAW_PS3)    += hid-udraw-ps3.o
+obj-$(CONFIG_HID_UNIVERSAL_PIDFF)      += hid-universal-pidff.o
 obj-$(CONFIG_HID_LED)          += hid-led.o
 obj-$(CONFIG_HID_XIAOMI)       += hid-xiaomi.o
 obj-$(CONFIG_HID_XINMO)                += hid-xinmo.o
index c448de53bf91e8645cd1976c414e650d820cd0af..dbac05649e9d2934f06f4283c5d5dff6263c091c 100644 (file)
 #define USB_DEVICE_ID_BTC_EMPREX_REMOTE        0x5578
 #define USB_DEVICE_ID_BTC_EMPREX_REMOTE_2      0x5577
 
+#define USB_VENDOR_ID_CAMMUS           0x3416
+#define USB_DEVICE_ID_CAMMUS_C5                0x0301
+#define USB_DEVICE_ID_CAMMUS_C12       0x0302
+
 #define USB_VENDOR_ID_CANDO            0x2087
 #define USB_DEVICE_ID_CANDO_PIXCIR_MULTI_TOUCH 0x0703
 #define USB_DEVICE_ID_CANDO_MULTI_TOUCH        0x0a01
 #define USB_VENDOR_ID_EVISION           0x320f
 #define USB_DEVICE_ID_EVISION_ICL01     0x5041
 
+#define USB_VENDOR_ID_FFBEAST          0x045b
+#define USB_DEVICE_ID_FFBEAST_JOYSTICK 0x58f9
+#define USB_DEVICE_ID_FFBEAST_RUDDER   0x5968
+#define USB_DEVICE_ID_FFBEAST_WHEEL    0x59d7
+
 #define USB_VENDOR_ID_FLATFROG         0x25b5
 #define USB_DEVICE_ID_MULTITOUCH_3200  0x0002
 
 #define I2C_DEVICE_ID_LG_8001          0x8001
 #define I2C_DEVICE_ID_LG_7010          0x7010
 
+#define USB_VENDOR_ID_LITE_STAR                0x11ff
+#define USB_DEVICE_ID_PXN_V10          0x3245
+#define USB_DEVICE_ID_PXN_V12          0x1212
+#define USB_DEVICE_ID_PXN_V12_LITE     0x1112
+#define USB_DEVICE_ID_PXN_V12_LITE_2   0x1211
+#define USB_DEVICE_LITE_STAR_GT987_FF  0x2141
+
 #define USB_VENDOR_ID_LOGITECH         0x046d
 #define USB_DEVICE_ID_LOGITECH_Z_10_SPK        0x0a07
 #define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e
 #define USB_VENDOR_ID_MONTEREY         0x0566
 #define USB_DEVICE_ID_GENIUS_KB29E     0x3004
 
+#define USB_VENDOR_ID_MOZA             0x346e
+#define USB_DEVICE_ID_MOZA_R3          0x0005
+#define USB_DEVICE_ID_MOZA_R3_2                0x0015
+#define USB_DEVICE_ID_MOZA_R5          0x0004
+#define USB_DEVICE_ID_MOZA_R5_2                0x0014
+#define USB_DEVICE_ID_MOZA_R9          0x0002
+#define USB_DEVICE_ID_MOZA_R9_2                0x0012
+#define USB_DEVICE_ID_MOZA_R12         0x0006
+#define USB_DEVICE_ID_MOZA_R12_2       0x0016
+#define USB_DEVICE_ID_MOZA_R16_R21     0x0000
+#define USB_DEVICE_ID_MOZA_R16_R21_2   0x0010
+
 #define USB_VENDOR_ID_MSI              0x1770
 #define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00
 
 #define USB_DEVICE_ID_VELLEMAN_K8061_FIRST     0x8061
 #define USB_DEVICE_ID_VELLEMAN_K8061_LAST      0x8068
 
+#define USB_VENDOR_ID_VRS      0x0483
+#define USB_DEVICE_ID_VRS_DFP  0xa355
+
 #define USB_VENDOR_ID_VTL              0x0306
 #define USB_DEVICE_ID_VTL_MULTITOUCH_FF3F      0xff3f
 
diff --git a/drivers/hid/hid-universal-pidff.c b/drivers/hid/hid-universal-pidff.c
new file mode 100644 (file)
index 0000000..55aad2e
--- /dev/null
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HID UNIVERSAL PIDFF
+ * hid-pidff wrapper for PID-enabled devices
+ * Handles device reports, quirks and extends usable button range
+ *
+ * Copyright (c) 2024, 2025 Makarenko Oleg
+ * Copyright (c) 2024, 2025 Tomasz Pakuła
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include <linux/input-event-codes.h>
+#include "hid-ids.h"
+
+#define JOY_RANGE (BTN_DEAD - BTN_JOYSTICK + 1)
+
+/*
+ * Map buttons manually to extend the default joystick button limit
+ */
+static int universal_pidff_input_mapping(struct hid_device *hdev,
+       struct hid_input *hi, struct hid_field *field, struct hid_usage *usage,
+       unsigned long **bit, int *max)
+{
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
+               return 0;
+
+       if (field->application != HID_GD_JOYSTICK)
+               return 0;
+
+       int button = ((usage->hid - 1) & HID_USAGE);
+       int code = button + BTN_JOYSTICK;
+
+       /* Detect the end of JOYSTICK buttons range */
+       if (code > BTN_DEAD)
+               code = button + KEY_NEXT_FAVORITE - JOY_RANGE;
+
+       /*
+        * Map overflowing buttons to KEY_RESERVED to not ignore
+        * them and let them still trigger MSC_SCAN
+        */
+       if (code > KEY_MAX)
+               code = KEY_RESERVED;
+
+       hid_map_usage(hi, usage, bit, max, EV_KEY, code);
+       hid_dbg(hdev, "Button %d: usage %d", button, code);
+       return 1;
+}
+
+/*
+ * 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 i, error;
+       error = hid_parse(hdev);
+       if (error) {
+               hid_err(hdev, "HID parse failed\n");
+               goto err;
+       }
+
+       error = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF);
+       if (error) {
+               hid_err(hdev, "HID hw start failed\n");
+               goto err;
+       }
+
+       /* Check if device contains PID usage page */
+       error = 1;
+       for (i = 0; i < hdev->collection_size; i++)
+               if ((hdev->collection[i].usage & HID_USAGE_PAGE) == HID_UP_PID) {
+                       error = 0;
+                       hid_dbg(hdev, "PID usage page found\n");
+                       break;
+               }
+
+       /*
+        * Do not fail as this might be the second "device"
+        * just for additional buttons/axes. Exit cleanly if force
+        * feedback usage page wasn't found (included devices were
+        * tested and confirmed to be USB PID after all).
+        */
+       if (error) {
+               hid_dbg(hdev, "PID usage page not found in the descriptor\n");
+               return 0;
+       }
+
+       /* Check if HID_PID support is enabled */
+       int (*init_function)(struct hid_device *, __u32);
+       init_function = hid_pidff_init_with_quirks;
+
+       if (!init_function) {
+               hid_warn(hdev, "HID_PID support not enabled!\n");
+               return 0;
+       }
+
+       error = init_function(hdev, id->driver_data);
+       if (error) {
+               hid_warn(hdev, "Error initialising force feedback\n");
+               goto err;
+       }
+
+       hid_info(hdev, "Universal pidff driver loaded sucesfully!");
+
+       return 0;
+err:
+       return error;
+}
+
+static int universal_pidff_input_configured(struct hid_device *hdev,
+                                           struct hid_input *hidinput)
+{
+       int axis;
+       struct input_dev *input = hidinput->input;
+
+       if (!input->absinfo)
+               return 0;
+
+       /* Decrease fuzz and deadzone on available axes */
+       for (axis = ABS_X; axis <= ABS_BRAKE; axis++) {
+               if (!test_bit(axis, input->absbit))
+                       continue;
+
+               input_set_abs_params(input, axis,
+                       input->absinfo[axis].minimum,
+                       input->absinfo[axis].maximum,
+                       axis == ABS_X ? 0 : 8, 0);
+       }
+
+       /* Remove fuzz and deadzone from the second joystick axis */
+       if (hdev->vendor == USB_VENDOR_ID_FFBEAST &&
+           hdev->product == USB_DEVICE_ID_FFBEAST_JOYSTICK)
+               input_set_abs_params(input, ABS_Y,
+                       input->absinfo[ABS_Y].minimum,
+                       input->absinfo[ABS_Y].maximum, 0, 0);
+
+       return 0;
+}
+
+static const struct hid_device_id universal_pidff_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R3),
+               .driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R3_2),
+               .driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R5),
+               .driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R5_2),
+               .driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R9),
+               .driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R9_2),
+               .driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R12),
+               .driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R12_2),
+               .driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R16_R21),
+               .driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MOZA, USB_DEVICE_ID_MOZA_R16_R21_2),
+               .driver_data = HID_PIDFF_QUIRK_FIX_WHEEL_DIRECTION },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CAMMUS, USB_DEVICE_ID_CAMMUS_C5) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_CAMMUS, USB_DEVICE_ID_CAMMUS_C12) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_VRS, USB_DEVICE_ID_VRS_DFP),
+               .driver_data = HID_PIDFF_QUIRK_PERMISSIVE_CONTROL },
+       { HID_USB_DEVICE(USB_VENDOR_ID_FFBEAST, USB_DEVICE_ID_FFBEAST_JOYSTICK), },
+       { HID_USB_DEVICE(USB_VENDOR_ID_FFBEAST, USB_DEVICE_ID_FFBEAST_RUDDER), },
+       { HID_USB_DEVICE(USB_VENDOR_ID_FFBEAST, USB_DEVICE_ID_FFBEAST_WHEEL) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LITE_STAR, USB_DEVICE_ID_PXN_V10) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LITE_STAR, USB_DEVICE_ID_PXN_V12) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LITE_STAR, USB_DEVICE_ID_PXN_V12_LITE) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LITE_STAR, USB_DEVICE_ID_PXN_V12_LITE_2) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_LITE_STAR, USB_DEVICE_LITE_STAR_GT987_FF) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, universal_pidff_devices);
+
+static struct hid_driver universal_pidff = {
+       .name = "hid-universal-pidff",
+       .id_table = universal_pidff_devices,
+       .input_mapping = universal_pidff_input_mapping,
+       .probe = universal_pidff_probe,
+       .input_configured = universal_pidff_input_configured
+};
+module_hid_driver(universal_pidff);
+
+MODULE_DESCRIPTION("Universal driver for USB PID Force Feedback devices");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Makarenko Oleg <oleg@makarenk.ooo>");
+MODULE_AUTHOR("Tomasz Pakuła <tomasz.pakula.oficjalny@gmail.com>");