Skip to content

Commit

Permalink
hid-xpadneo: Move SDL work-arounds to parent device
Browse files Browse the repository at this point in the history
This prevents SDL from presenting our controller device twice when
HIDAPI is used. This is especially visible in Proton after the Xinput
implementation enumerated all the devices:
```
# grep 'Found sdl' steam-1091500.log | grep 'xinput_hack: 0'
0080:trace:plugplay:try_add_device Found sdl game controller 0x0 (vid 045e, pid 02fd, version 0, serial L"030000005e040000fd02000000006800", xinput_hack: 0)
0080:trace:plugplay:try_add_device Found sdl game controller 0x3 (vid 045e, pid 02e0, version 2307, serial L"050000005e040000e002000003090000", xinput_hack: 0)
```

Many games only support one controller anyways, so the issue is not
visible for most games but Cyberpunk 2077 since patch 1.2 seems to
support multiple controllers at the same time.

See-also: ValveSoftware/Proton#4707
Maybe-affects: atar-axis#180
Signed-off-by: Kai Krakow <[email protected]>
  • Loading branch information
kakra committed Apr 3, 2021
1 parent 34968b6 commit b1389d6
Showing 1 changed file with 54 additions and 50 deletions.
104 changes: 54 additions & 50 deletions hid-xpadneo/src/hid-xpadneo.c
Original file line number Diff line number Diff line change
Expand Up @@ -859,56 +859,6 @@ static int xpadneo_input_configured(struct hid_device *hdev, struct hid_input *h

xdata->idev = hi->input;

/*
* Pretend that we are in Windows pairing mode as we are actually
* exposing the Windows mapping. This prevents SDL and other layers
* (probably browser game controller APIs) from treating our driver
* unnecessarily with button and axis mapping fixups, and it seems
* this is actually a firmware mode meant for Android usage only:
*
* Xbox One S:
* 0x2E0 wireless Windows mode (non-Android mode)
* 0x2EA USB Windows and Linux mode
* 0x2FD wireless Linux mode (Android mode)
*
* Xbox Elite 2:
* 0xB00 USB Windows and Linux mode
* 0xB05 wireless Linux mode (Android mode)
*
* Xbox Series X|S:
* 0xB12 Dongle, USB Windows and USB Linux mode
* 0xB13 wireless Linux mode (Android mode)
*
* TODO: We should find a better way of doing this so SDL2 could
* still detect our driver as the correct model. Currently this
* maps all controllers to the same model.
*/
switch (xdata->idev->id.product) {
case 0x02E0:
xdata->idev->id.version = 0x00000903;
break;
case 0x02FD:
xdata->idev->id.product = 0x02E0;
break;
case 0x0B05:
case 0x0B13:
xdata->idev->id.product = 0x02E0;
xdata->idev->id.version = 0x00000903;
break;
}

if (hdev->product != xdata->idev->id.product)
hid_info(hdev,
"pretending XB1S Windows wireless mode "
"(changed PID from 0x%04X to 0x%04X)\n", hdev->product,
(u16)xdata->idev->id.product);

if (hdev->version != xdata->idev->id.version)
hid_info(hdev,
"working around wrong SDL2 mappings "
"(changed version from 0x%08X to 0x%08X)\n", hdev->version,
xdata->idev->id.version);

if (param_disable_deadzones) {
hid_warn(hdev, "disabling dead zones\n");
deadzone = 0;
Expand Down Expand Up @@ -1090,6 +1040,8 @@ static int xpadneo_probe(struct hid_device *hdev, const struct hid_device_id *id
{
int ret;
struct xpadneo_devdata *xdata;
u16 product;
u32 version;

xdata = devm_kzalloc(&hdev->dev, sizeof(*xdata), GFP_KERNEL);
if (xdata == NULL)
Expand All @@ -1101,6 +1053,58 @@ static int xpadneo_probe(struct hid_device *hdev, const struct hid_device_id *id
xdata->hdev = hdev;
hid_set_drvdata(hdev, xdata);

/*
* Pretend that we are in Windows pairing mode as we are actually
* exposing the Windows mapping. This prevents SDL and other layers
* (probably browser game controller APIs) from treating our driver
* unnecessarily with button and axis mapping fixups, and it seems
* this is actually a firmware mode meant for Android usage only:
*
* Xbox One S:
* 0x2E0 wireless Windows mode (non-Android mode)
* 0x2EA USB Windows and Linux mode
* 0x2FD wireless Linux mode (Android mode)
*
* Xbox Elite 2:
* 0xB00 USB Windows and Linux mode
* 0xB05 wireless Linux mode (Android mode)
*
* Xbox Series X|S:
* 0xB12 Dongle, USB Windows and USB Linux mode
* 0xB13 wireless Linux mode (Android mode)
*
* TODO: We should find a better way of doing this so SDL2 could
* still detect our driver as the correct model. Currently this
* maps all controllers to the same model.
*/
product = hdev->product;
version = hdev->version;
switch (product) {
case 0x02E0:
hdev->version = 0x00000903;
break;
case 0x02FD:
hdev->product = 0x02E0;
break;
case 0x0B05:
case 0x0B13:
hdev->product = 0x02E0;
hdev->version = 0x00000903;
break;
}

if (hdev->product != product)
hid_info(hdev,
"pretending XB1S Windows wireless mode "
"(changed PID from 0x%04X to 0x%04X)\n", product,
hdev->product);

if (hdev->version != version)
hid_info(hdev,
"working around wrong SDL2 mappings "
"(changed version from 0x%08X to 0x%08X)\n", version,
hdev->version);

ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "parse failed\n");
Expand Down

0 comments on commit b1389d6

Please sign in to comment.