possible fixing kernel module for mouse?
Closed this issue · 15 comments
on kernel 6.4.15-100
when using the kernel module to fix the mouse cursor it breaks the slider on the throttle
(when loaded)
S
is mapped to mouse vertical
Rs
is mapped to mouse horizontal
Slider
now sends random buttons like the mouse vertical axis before
(when not loaded)
S
is mapped to Slider
Rs
is mapped to mouse vertical axis
mouse horizontal axis is broken and pushes buttons
2023-09-27.11-08-19.mp4
this is a slightly modified version that fixes the button layout to how it is originally (
/*
* HID driver for Saitek X52 HOTAS
*
* Supported devices:
* - Saitek X52
* - Saitek X52 Pro
*
* Copyright (c) 2020 Nirenjan Krishnan
*
* SPDX-License-Identifier: GPL-2.0-only
*/
#include <linux/module.h>
#include <linux/hid.h>
#include <linux/bits.h>
#define VENDOR_SAITEK 0x06a3
#define DEV_X52_1 0x0255
#define DEV_X52_2 0x075c
#define DEV_X52_PRO 0x0762
static void _parse_axis_report(struct input_dev *input_dev,
int is_pro, u8 *data, int len)
{
static const s32 hat_to_axis[16][2] = {
{0, 0},
{0, -1},
{1, -1},
{1, 0},
{1, 1},
{0, 1},
{-1, 1},
{-1, 0},
{-1, -1},
};
u8 hat = (data[len - 2]) >> 4;
u32 axis = (data[3] << 24) |
(data[2] << 16) |
(data[1] << 8) |
data[0];
if (is_pro) {
input_report_abs(input_dev, ABS_X, (axis & 0x3ff));
input_report_abs(input_dev, ABS_Y, ((axis >> 10) & 0x3ff));
} else {
input_report_abs(input_dev, ABS_X, (axis & 0x7ff));
input_report_abs(input_dev, ABS_Y, ((axis >> 11) & 0x7ff));
}
input_report_abs(input_dev, ABS_RZ, ((axis >> 22) & 0x3ff));
input_report_abs(input_dev, ABS_Z, data[4]);
input_report_abs(input_dev, ABS_RX, data[5]);
input_report_abs(input_dev, ABS_RY, data[6]);
input_report_abs(input_dev, ABS_MISC, data[7]);
/* Mouse stick is always the last byte of the report */
input_report_abs(input_dev, ABS_TILT_X, data[len-1] & 0xf);
input_report_abs(input_dev, ABS_TILT_Y, data[len-1] >> 4);
/* Hat is always the upper nibble of the penultimate byte of the report */
input_report_abs(input_dev, ABS_HAT0X, hat_to_axis[hat][0]);
input_report_abs(input_dev, ABS_HAT0Y, hat_to_axis[hat][1]);
}
/**********************************************************************
* Mapping buttons
* ===============
*
* The X52 and X52 Pro report the buttons in different orders. In order
* to let a userspace application handle them in a generic fashion, we
* define a map for buttons to BTN_* defines, so that one button (eg. clutch)
* will send the same ID on both X52 and X52 Pro
*
* The map is defined below.
**********************************************************************
*/
#define X52_TRIGGER_1 BTN_TRIGGER_HAPPY1
#define X52_BTN_FIRE BTN_TRIGGER_HAPPY2
#define X52_BTN_A BTN_TRIGGER_HAPPY3
#define X52_BTN_B BTN_TRIGGER_HAPPY4
#define X52_BTN_C BTN_TRIGGER_HAPPY5
#define X52_BTN_PINKIE BTN_TRIGGER_HAPPY6
#define X52_BTN_D BTN_TRIGGER_HAPPY7
#define X52_BTN_E BTN_TRIGGER_HAPPY8
#define X52_BTN_T1_UP BTN_TRIGGER_HAPPY9
#define X52_BTN_T1_DN BTN_TRIGGER_HAPPY10
#define X52_BTN_T2_UP BTN_TRIGGER_HAPPY11
#define X52_BTN_T2_DN BTN_TRIGGER_HAPPY12
#define X52_BTN_T3_UP BTN_TRIGGER_HAPPY13
#define X52_BTN_T3_DN BTN_TRIGGER_HAPPY14
#define X52_TRIGGER_2 BTN_TRIGGER_HAPPY15
#define X52_MOUSE_LEFT BTN_TRIGGER_HAPPY16
#define X52_MOUSE_FORWARD BTN_TRIGGER_HAPPY17
#define X52_MOUSE_BACKWARD BTN_TRIGGER_HAPPY18
#define X52_MOUSE_RIGHT BTN_TRIGGER_HAPPY19
#define X52_STICK_POV_N BTN_TRIGGER_HAPPY20
#define X52_STICK_POV_E BTN_TRIGGER_HAPPY21
#define X52_STICK_POV_S BTN_TRIGGER_HAPPY22
#define X52_STICK_POV_W BTN_TRIGGER_HAPPY23
#define X52_THROT_POV_N BTN_TRIGGER_HAPPY24
#define X52_THROT_POV_E BTN_TRIGGER_HAPPY25
#define X52_THROT_POV_S BTN_TRIGGER_HAPPY26
#define X52_THROT_POV_W BTN_TRIGGER_HAPPY27
#define X52_MODE_1 BTN_TRIGGER_HAPPY28
#define X52_MODE_2 BTN_TRIGGER_HAPPY29
#define X52_MODE_3 BTN_TRIGGER_HAPPY30
#define X52_BTN_CLUTCH BTN_TRIGGER_HAPPY31
#define X52_BTN_FUNCTION BTN_TRIGGER_HAPPY32
#define X52_BTN_START_STOP BTN_TRIGGER_HAPPY33
#define X52_BTN_RESET BTN_TRIGGER_HAPPY34
#define X52_BTN_PG_UP BTN_TRIGGER_HAPPY35
#define X52_BTN_PG_DN BTN_TRIGGER_HAPPY36
#define X52_BTN_UP BTN_TRIGGER_HAPPY37
#define X52_BTN_DN BTN_TRIGGER_HAPPY38
#define X52_BTN_MFD_SELECT BTN_TRIGGER_HAPPY39
static void _parse_button_report(struct input_dev *input_dev,
int is_pro, u8 *data, int num_buttons)
{
int i;
int idx;
int btn;
// Map X52 buttons from report to fixed button ID
// This should be defined in the order provided in the report.
static const int x52_buttons[] = {
X52_TRIGGER_1,
X52_BTN_FIRE,
X52_BTN_A,
X52_BTN_B,
X52_BTN_C,
X52_BTN_PINKIE,
X52_BTN_D,
X52_BTN_E,
X52_BTN_T1_UP,
X52_BTN_T1_DN,
X52_BTN_T2_UP,
X52_BTN_T2_DN,
X52_BTN_T3_UP,
X52_BTN_T3_DN,
X52_TRIGGER_2,
X52_STICK_POV_N,
X52_STICK_POV_E,
X52_STICK_POV_S,
X52_STICK_POV_W,
X52_THROT_POV_N,
X52_THROT_POV_E,
X52_THROT_POV_S,
X52_THROT_POV_W,
X52_MODE_1,
X52_MODE_2,
X52_MODE_3,
X52_BTN_FUNCTION,
X52_BTN_START_STOP,
X52_BTN_RESET,
X52_BTN_CLUTCH,
X52_MOUSE_LEFT,
X52_MOUSE_RIGHT,
X52_MOUSE_FORWARD,
X52_MOUSE_BACKWARD,
};
static const int pro_buttons[] = {
X52_TRIGGER_1,
X52_BTN_FIRE,
X52_BTN_A,
X52_BTN_B,
X52_BTN_C,
X52_BTN_PINKIE,
X52_BTN_D,
X52_BTN_E,
X52_BTN_T1_UP,
X52_BTN_T1_DN,
X52_BTN_T2_UP,
X52_BTN_T2_DN,
X52_BTN_T3_UP,
X52_BTN_T3_DN,
X52_TRIGGER_2,
X52_MOUSE_LEFT,
X52_MOUSE_FORWARD,
X52_MOUSE_BACKWARD,
X52_MOUSE_RIGHT,
X52_STICK_POV_N,
X52_STICK_POV_E,
X52_STICK_POV_S,
X52_STICK_POV_W,
X52_THROT_POV_N,
X52_THROT_POV_E,
X52_THROT_POV_S,
X52_THROT_POV_W,
X52_MODE_1,
X52_MODE_2,
X52_MODE_3,
X52_BTN_CLUTCH,
X52_BTN_FUNCTION,
X52_BTN_START_STOP,
X52_BTN_RESET,
X52_BTN_PG_UP,
X52_BTN_PG_DN,
X52_BTN_UP,
X52_BTN_DN,
X52_BTN_MFD_SELECT,
};
const int *btn_map = is_pro ? pro_buttons : x52_buttons ;
for (i = 0; i < num_buttons; i++) {
idx = 8 + (i / BITS_PER_BYTE);
btn = !!(data[idx] & (1 << (i % BITS_PER_BYTE)));
input_report_key(input_dev, btn_map[i], btn);
}
}
static int _parse_x52_report(struct input_dev *input_dev,
u8 *data, int len)
{
if (len != 14) {
return -1;
}
_parse_axis_report(input_dev, 0, data, len);
_parse_button_report(input_dev, 0, data, 34);
return 0;
}
static int _parse_x52pro_report(struct input_dev *input_dev,
u8 *data, int len)
{
if (len != 15) {
return -1;
}
_parse_axis_report(input_dev, 1, data, len);
_parse_button_report(input_dev, 1, data, 39);
return 0;
}
static int x52_raw_event(struct hid_device *dev,
struct hid_report *report, u8 *data, int len)
{
struct input_dev *input_dev = hid_get_drvdata(dev);
int is_pro = (dev->product == DEV_X52_PRO);
int ret;
if (is_pro) {
ret = _parse_x52pro_report(input_dev, data, len);
} else {
ret = _parse_x52_report(input_dev, data, len);
}
input_sync(input_dev);
return ret;
}
static int x52_input_configured(struct hid_device *dev,
struct hid_input *input)
{
struct input_dev *input_dev = input->input;
int i;
int max_btn;
int is_pro = (dev->product == DEV_X52_PRO);
int max_stick;
hid_set_drvdata(dev, input_dev);
set_bit(EV_KEY, input_dev->evbit);
set_bit(EV_ABS, input_dev->evbit);
/*
* X52 has only 34 buttons, X52 Pro has 39. The first 34 buttons are common
* although the button order differs between the two.
*/
max_btn = is_pro ? 39 : 34 ;
for (i = 0; i < max_btn; i++) {
set_bit(BTN_TRIGGER_HAPPY1 + i, input_dev->keybit);
}
/* Both X52 and X52 Pro have the same number of axes, only the ranges vary */
set_bit(ABS_X, input_dev->absbit);
set_bit(ABS_Y, input_dev->absbit);
set_bit(ABS_Z, input_dev->absbit);
set_bit(ABS_RX, input_dev->absbit);
set_bit(ABS_RY, input_dev->absbit);
set_bit(ABS_RZ, input_dev->absbit);
set_bit(ABS_RZ, input_dev->absbit);
set_bit(ABS_HAT0X, input_dev->absbit);
set_bit(ABS_HAT0Y, input_dev->absbit);
set_bit(ABS_TILT_X, input_dev->absbit);
set_bit(ABS_TILT_Y, input_dev->absbit);
set_bit(ABS_MISC, input_dev->absbit);
max_stick = is_pro ? 1023 : 2047;
input_set_abs_params(input_dev, ABS_X, 0, max_stick, max_stick >> 8, max_stick >> 4);
input_set_abs_params(input_dev, ABS_Y, 0, max_stick, max_stick >> 8, max_stick >> 4);
input_set_abs_params(input_dev, ABS_RZ, 0, 1023, 3, 63);
input_set_abs_params(input_dev, ABS_RX, 0, 255, 0, 15);
input_set_abs_params(input_dev, ABS_RY, 0, 255, 0, 15);
input_set_abs_params(input_dev, ABS_Z, 0, 255, 0, 15);
input_set_abs_params(input_dev, ABS_MISC, 0, 255, 0, 15);
input_set_abs_params(input_dev, ABS_HAT0X, -1, 1, 0, 0);
input_set_abs_params(input_dev, ABS_HAT0Y, -1, 1, 0, 0);
input_set_abs_params(input_dev, ABS_TILT_X, 0, 15, 0, 0);
input_set_abs_params(input_dev, ABS_TILT_Y, 0, 15, 0, 0);
return 0;
}
static int x52_input_mapping(struct hid_device *dev,
struct hid_input *input,
struct hid_field *field,
struct hid_usage *usage,
unsigned long **bit,
int *max)
{
/*
* We are reporting the events in x52_raw_event.
* Skip the hid-input processing.
*/
return -1;
}
static const struct hid_device_id x52_devices[] = {
{ HID_USB_DEVICE(VENDOR_SAITEK, DEV_X52_1) },
{ HID_USB_DEVICE(VENDOR_SAITEK, DEV_X52_2) },
{ HID_USB_DEVICE(VENDOR_SAITEK, DEV_X52_PRO) },
{}
};
MODULE_DEVICE_TABLE(hid, x52_devices);
static struct hid_driver x52_driver = {
.name = "saitek-x52",
.id_table = x52_devices,
.input_mapping = x52_input_mapping,
.input_configured = x52_input_configured,
.raw_event = x52_raw_event,
};
module_hid_driver(x52_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Nirenjan Krishnan");
MODULE_DESCRIPTION("HID driver for Saitek X52 HOTAS devices");
You shouldn't need the kernel module anymore, the hid-quirks should already handle separating the thumb stick into two separate axes.
Didn't you use to play with the joystick firmware? My kernel driver makes some assumptions about the report format, so if that has changed, then it won't work as expected.
As usual, I need the output of lsusb -v and usbhid-dump, as I requested in #50
You shouldn't need the kernel module anymore, the hid-quirks should already handle separating the thumb stick into two separate axes.
in linux yes, in wine and all its versions still breaks
Didn't you use to play with the joystick firmware? My kernel driver makes some assumptions about the report format, so if that has changed, then it won't work as expected.
reprogrammed with original (dumped) firmware
❯ sudo lsusb -v -d 06a3:0762
Bus 001 Device 005: ID 06a3:0762 Saitek PLC Saitek X52 Pro Flight Control System
Device Descriptor:
bLength 18
bDescriptorType 1
bcdUSB 2.00
bDeviceClass 0
bDeviceSubClass 0
bDeviceProtocol 0
bMaxPacketSize0 8
idVendor 0x06a3 Saitek PLC
idProduct 0x0762 Saitek X52 Pro Flight Control System
bcdDevice 20.00
iManufacturer 1 Logitech
iProduct 2 X52 Professional H.O.T.A.S.
iSerial 0
bNumConfigurations 1
Configuration Descriptor:
bLength 9
bDescriptorType 2
wTotalLength 0x0022
bNumInterfaces 1
bConfigurationValue 1
iConfiguration 0
bmAttributes 0x80
(Bus Powered)
MaxPower 230mA
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 0
bAlternateSetting 0
bNumEndpoints 1
bInterfaceClass 3 Human Interface Device
bInterfaceSubClass 0
bInterfaceProtocol 0
iInterface 0
HID Device Descriptor:
bLength 9
bDescriptorType 33
bcdHID 1.11
bCountryCode 0 Not supported
bNumDescriptors 1
bDescriptorType 34 Report
wDescriptorLength 125
Report Descriptors:
** UNAVAILABLE **
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 3
Transfer Type Interrupt
Synch Type None
Usage Type Data
wMaxPacketSize 0x0010 1x 16 bytes
bInterval 10
Device Status: 0x0000
(Bus Powered)
❯ sudo usbhid-dump
005:003:002:DESCRIPTOR 1695813294.734790
06 00 FF 09 01 A1 01 85 10 95 06 75 08 15 00 26
FF 00 09 01 81 00 09 01 91 00 C0 06 00 FF 09 02
A1 01 85 11 95 13 75 08 15 00 26 FF 00 09 02 81
00 09 02 91 00 C0
005:003:001:DESCRIPTOR 1695813294.737774
05 01 09 02 A1 01 85 02 09 01 A1 00 95 10 75 01
15 00 25 01 05 09 19 01 29 10 81 02 95 02 75 10
16 01 80 26 FF 7F 05 01 09 30 09 31 81 06 95 01
75 08 15 81 25 7F 09 38 81 06 95 01 05 0C 0A 38
02 81 06 C0 C0 05 0C 09 01 A1 01 85 03 95 02 75
10 15 01 26 FF 02 19 01 2A FF 02 81 00 C0 05 01
09 80 A1 01 85 04 95 01 75 02 15 01 25 03 09 82
09 81 09 83 81 00 75 01 15 00 25 01 09 9B 81 06
75 05 81 03 C0
005:003:000:DESCRIPTOR 1695813294.740774
05 01 09 06 A1 01 05 07 19 E0 29 E7 15 00 25 01
75 01 95 08 81 02 95 05 05 08 19 01 29 05 91 02
95 01 75 03 91 03 95 70 75 01 05 07 19 04 29 73
81 02 95 05 19 87 29 8B 81 02 95 03 19 90 29 92
81 02 C0
001:005:000:DESCRIPTOR 1695813294.757880
05 01 09 04 A1 01 09 01 A1 00 09 30 09 31 15 00
26 FF 03 75 0A 95 02 81 02 75 02 95 01 81 01 09
35 15 00 26 FF 03 75 0A 95 01 81 02 09 32 09 33
09 34 09 36 15 00 26 FF 00 75 08 95 04 81 02 05
09 19 01 29 27 15 00 25 01 95 27 75 01 81 02 75
05 95 01 81 01 05 01 09 39 15 01 25 08 35 00 46
3B 01 66 14 00 75 04 95 01 81 42 05 05 09 24 09
26 15 00 25 0F 75 04 95 02 81 02 C0 C0
001:004:002:DESCRIPTOR 1695813294.761869
05 0C 09 01 A1 01 15 00 25 01 09 E9 09 EA 09 B5
09 B6 75 01 95 04 81 02 09 E2 09 B7 09 CD 95 03
81 06 05 0B 09 20 95 01 81 06 05 0C 26 FF 00 09
00 75 08 95 03 81 02 09 00 95 04 91 02 C0
001:003:001:DESCRIPTOR 1695813294.771740
05 01 09 06 A1 01 05 07 19 E0 29 E7 15 00 25 01
95 08 75 01 81 02 81 03 95 05 05 08 19 01 29 05
91 02 95 01 75 03 91 01 95 06 75 08 15 00 26 A4
00 05 07 19 00 2A A4 00 81 00 C0
001:003:000:DESCRIPTOR 1695813294.792746
05 01 09 02 A1 01 85 01 09 01 A1 00 05 09 19 01
29 05 15 00 25 01 95 05 75 01 81 02 95 01 75 03
81 01 05 01 09 30 09 31 15 81 25 7F 75 08 95 02
81 06 09 38 15 81 25 7F 75 08 95 01 81 06 C0 C0
05 0C 09 01 A1 01 85 03 75 10 95 02 15 01 26 8C
02 19 01 2A 8C 02 81 00 C0 05 01 09 80 A1 01 85
04 75 02 95 01 15 01 25 03 09 82 09 81 09 83 81
60 75 06 81 03 C0 05 01 09 00 A1 01 85 05 06 00
FF 09 01 15 81 25 7F 75 08 95 07 B1 02 C0
001:006:000:DESCRIPTOR 1695813294.797736
05 01 09 00 A1 01 85 01 15 00 26 FF 00 19 01 29
08 95 3F 75 08 81 02 19 01 29 08 91 02 85 02 15
00 26 FF 00 19 01 29 08 95 3F 75 08 81 02 19 01
29 08 91 02 85 D0 15 00 26 FF 00 19 01 29 08 95
3F 75 08 81 02 19 01 29 08 91 02 85 FA 15 00 26
FF 00 19 01 29 08 95 3F 75 08 81 02 19 01 29 08
91 02 85 FC 15 00 26 FF 00 19 01 29 08 95 3F 75
08 81 02 19 01 29 08 91 02 06 00 FF 09 01 A1 01
85 52 09 01 15 00 26 FF 00 75 08 95 B8 B1 02 85
53 09 01 15 00 26 FF 00 75 08 96 D4 02 B1 02 85
80 09 01 15 00 26 FF 00 75 08 95 F1 B1 02 85 81
09 01 15 00 26 FF 00 75 08 95 F1 B1 02 85 82 09
01 15 00 26 FF 00 75 08 95 F1 B1 02 85 A0 09 01
15 00 26 FF 00 75 08 96 F8 02 B1 02 85 A1 09 01
15 00 26 FF 00 75 08 96 F8 02 B1 02 85 A2 09 01
15 00 26 FF 00 75 08 96 F8 02 B1 02 C0 C0
(without module)
2023-09-27.12-46-31.mp4
I've updated the kernel module in the branch report-buttons-in-order
(commit 19b76d). Can you give this a try and let me know if it works?
looking at how it presses the buttons it might be that it overflows into the button sequence...
since you can see it go in steps when you move it slowly
i wonder if the bit location is misaligned
2023-09-27.20-19-05.mp4
I think the issue is that Proton/Wine is not using the kernel driver, but attempting to parse the USB report directly. The clue here is the Unhandled type 00000005
, which appears to correspond to this section in the descriptor.
oh yikes... i already made report on the wine side... ill add this to it
hmm this means the wine implementation might be flawed then
umm is it a option to for the time being block those 2 from reporting?
We can disable them from being reported in the kernel module, but I don't think that would help in your case, since Steam/Wine is handling the parsing internally. The virtual mouse is handled by x52d, which parses the raw report and sends the events through uinput
hmm it was working before though
i did report this in the wine bugzilla
well after a looooong silence its finally getting fixed 🥳
in the end it was wine... the 9th axis overflowed into the buttons and it caused false button presses
i will now close this issue since a fix is there