[GH-ISSUE #267] Controller reconnected with wrong mappings (Steam hot-plugging) #186

Open
opened 2026-10-15 12:57:54 +00:00 by bsakaguchi · 3 comments
Owner

Originally created by @ironoa on GitHub (Oct 12, 2025).
Original GitHub issue: https://github.com/games-on-whales/wolf/issues/267

Furthermore, maybe related, maybe another issue: it happened to me a couple of times only (just tested and I couldn't replicate). When the PS5 controller is passed from Moonlight (detected as DualSense Wireless Controller by the Steam Controller settings), if the controller loses connection and then it gets reconnected, it is then detected as PS5 Controller (different name), and all the buttons mappings are flipped/broken/unusable. Even in that case, one needs to restart Steam.

I know what's going on, we just set a random Mac address for our virtual devices. We've recently pushed a fix for Sunshine that I should probably backport to Wolf too; can you open up a different issue for this so that we don't forget about it?

Originally posted by @ABeltramo in #264

it happened to me a couple of times only (just tested and I couldn't replicate). When the PS5 controller is passed from Moonlight (detected as DualSense Wireless Controller by the Steam Controller settings), if the controller loses connection and then it gets reconnected, it is then detected as PS5 Controller (different name), and all the buttons mappings are flipped/broken/unusable. In that case, one needs to restart Steam.

Issue opened as a reminder, probably @ABeltramo knows already what's going on ;)

Originally created by @ironoa on GitHub (Oct 12, 2025). Original GitHub issue: https://github.com/games-on-whales/wolf/issues/267 > Furthermore, maybe related, maybe another issue: it happened to me a couple of times only (just tested and I couldn't replicate). When the PS5 controller is passed from Moonlight (detected as `DualSense Wireless Controller` by the Steam Controller settings), if the controller loses connection and then it gets reconnected, it is then detected as `PS5 Controller` (different name), and all the buttons mappings are flipped/broken/unusable. Even in that case, one needs to restart Steam. > > > I know what's going on, we just set a random Mac address for our virtual devices. We've recently pushed a fix for Sunshine that I should probably backport to Wolf too; can you open up a different issue for this so that we don't forget about it? > _Originally posted by @ABeltramo in [#264](https://github.com/games-on-whales/wolf/issues/264#issuecomment-3392569455)_ it happened to me a couple of times only (just tested and I couldn't replicate). When the PS5 controller is passed from Moonlight (detected as `DualSense Wireless Controller` by the Steam Controller settings), if the controller loses connection and then it gets reconnected, it is then detected as `PS5 Controller` (different name), and all the buttons mappings are flipped/broken/unusable. In that case, one needs to restart Steam. Issue opened as a reminder, probably @ABeltramo knows already what's going on ;)
Author
Owner

@ABeltramo commented on GitHub (Oct 12, 2025):

Most likely related to https://github.com/games-on-whales/inputtino/pull/31

<!-- gh-comment-id:3393992064 --> @ABeltramo commented on GitHub (Oct 12, 2025): Most likely related to https://github.com/games-on-whales/inputtino/pull/31
Author
Owner

@piotrminkina commented on GitHub (Aug 28, 2026):

Ran into this and traced it end to end on stable (7f416ec). Two independent things have to change, and I think the first one is why the backport @ABeltramo mentioned never landed in practice.

1. The inputtino fix is merged, but Wolf pins a commit that predates it.

src/core/CMakeLists.txt:52-55 pins:

FetchContent_Declare(
        inputtino
        GIT_REPOSITORY https://github.com/games-on-whales/inputtino.git
        GIT_TAG fd136cf)

fd136cf is from 2025-03-24; games-on-whales/inputtino#31 ("Fix DualSense MAC address") merged 2025-08-16. At the pinned commit, PS5Joypad::create reads:

auto joypad = PS5Joypad(device.vendor_id);      // MAC generated internally, always random
if (def.phys.empty()) { def.phys = joypad.get_mac_address(); }
if (def.uniq.empty()) { def.uniq = joypad.get_mac_address(); }

so DeviceDefinition::device_uniq is accepted, but it only ever reaches the sysfs uniq string — the MAC actually used in the HID reports is unconditionally random. Passing a device_uniq at this pin would produce a device whose uniq and whose HID MAC disagree.

On inputtino stable the same function is the fixed version:

std::array<unsigned char, 6> mac_address = {};
if (def.uniq.empty()) { mac_address = generate_mac_address(); }
else { /* parse def.uniq as xx:xx:xx:xx:xx:xx */ }
auto joypad = PS5Joypad(device.vendor_id, mac_address);

2. Wolf never populates device_uniq.

src/moonlight-server/control/input_handler.cpp:125:

auto result = PS5Joypad::create(
    {.name = "Wolf DualSense (virtual) pad", .vendor_id = 0x054C, .product_id = 0x0CE6, .version = 0x8111});

device_phys and device_uniq stay at their "" defaults, so even after bumping the pin the MAC would still be random on every create_new_joypad() call — which is every controller-arrival packet, not just every session.

What it looks like in a log. Five different identities for one physical pad inside 50 seconds, each one a fresh Creating PS joypad for controller 0:

UNIQ=d4:4e:f9:e9:c3:96
UNIQ=20:02:24:5e:57:38
UNIQ=a5:fa:f2:4e:23:c7
UNIQ=fd:8d:9e:a0:fe:2c
UNIQ=50:89:cd:67:54:43

uniq → /sys/.../uniq → udev → ID_SERIAL, which is what Steam keys its per-controller config on, so each of those is a brand-new unconfigured DualSense as far as Steam is concerned.

Possible derivation for a stable value. Wolf already indexes per-client controller settings by slot (client_settings->controllers_override[controller_number]), so a MAC derived deterministically from (paired client id, controller number) would give "same client, same slot ⇒ same identity" without introducing any new persistent state.

Xbox pads are a separate, larger gap. The uinput path (input_handler.cpp:109 → joypad_xbox.cpp) does not set uniq at all — the udev events Wolf emits for an Xbox pad carry no UNIQ= and report ID_SERIAL=noserial. So a stable identity there needs uinput-side support first, if that is in scope for this issue or should be split off.

Happy to open a PR for the pin bump plus the device_uniq plumbing if that direction sounds right.

<!-- gh-comment-id:5453126000 --> @piotrminkina commented on GitHub (Aug 28, 2026): Ran into this and traced it end to end on `stable` (7f416ec). Two independent things have to change, and I think the first one is why the backport @ABeltramo mentioned never landed in practice. **1. The inputtino fix is merged, but Wolf pins a commit that predates it.** `src/core/CMakeLists.txt:52-55` pins: ```cmake FetchContent_Declare( inputtino GIT_REPOSITORY https://github.com/games-on-whales/inputtino.git GIT_TAG fd136cf) ``` `fd136cf` is from 2025-03-24; games-on-whales/inputtino#31 ("Fix DualSense MAC address") merged 2025-08-16. At the pinned commit, `PS5Joypad::create` reads: ```cpp auto joypad = PS5Joypad(device.vendor_id); // MAC generated internally, always random if (def.phys.empty()) { def.phys = joypad.get_mac_address(); } if (def.uniq.empty()) { def.uniq = joypad.get_mac_address(); } ``` so `DeviceDefinition::device_uniq` is accepted, but it only ever reaches the sysfs `uniq` string — the MAC actually used in the HID reports is unconditionally random. Passing a `device_uniq` at this pin would produce a device whose `uniq` and whose HID MAC disagree. On inputtino `stable` the same function is the fixed version: ```cpp std::array<unsigned char, 6> mac_address = {}; if (def.uniq.empty()) { mac_address = generate_mac_address(); } else { /* parse def.uniq as xx:xx:xx:xx:xx:xx */ } auto joypad = PS5Joypad(device.vendor_id, mac_address); ``` **2. Wolf never populates `device_uniq`.** `src/moonlight-server/control/input_handler.cpp:125`: ```cpp auto result = PS5Joypad::create( {.name = "Wolf DualSense (virtual) pad", .vendor_id = 0x054C, .product_id = 0x0CE6, .version = 0x8111}); ``` `device_phys` and `device_uniq` stay at their `""` defaults, so even after bumping the pin the MAC would still be random on every `create_new_joypad()` call — which is every controller-arrival packet, not just every session. **What it looks like in a log.** Five different identities for one physical pad inside 50 seconds, each one a fresh `Creating PS joypad for controller 0`: ``` UNIQ=d4:4e:f9:e9:c3:96 UNIQ=20:02:24:5e:57:38 UNIQ=a5:fa:f2:4e:23:c7 UNIQ=fd:8d:9e:a0:fe:2c UNIQ=50:89:cd:67:54:43 ``` `uniq` → `/sys/.../uniq` → udev → `ID_SERIAL`, which is what Steam keys its per-controller config on, so each of those is a brand-new unconfigured DualSense as far as Steam is concerned. **Possible derivation for a stable value.** Wolf already indexes per-client controller settings by slot (`client_settings->controllers_override[controller_number]`), so a MAC derived deterministically from (paired client id, controller number) would give "same client, same slot ⇒ same identity" without introducing any new persistent state. **Xbox pads are a separate, larger gap.** The uinput path (`input_handler.cpp:109` → `joypad_xbox.cpp`) does not set `uniq` at all — the udev events Wolf emits for an Xbox pad carry no `UNIQ=` and report `ID_SERIAL=noserial`. So a stable identity there needs uinput-side support first, if that is in scope for this issue or should be split off. Happy to open a PR for the pin bump plus the `device_uniq` plumbing if that direction sounds right.
Author
Owner

@piotrminkina commented on GitHub (Aug 28, 2026):

Cross-reference, since it changes what "backport the inputtino fix" means in practice: games-on-whales/inputtino#43 ([2/5] of the roadmap in games-on-whales/inputtino#44) is open and reworking PS5 MAC handling — currently a monotonic counter with an is_active_on_system collision check.

That is orthogonal to this issue rather than a fix for it: a counter still hands out a different address on every create, and it resets with the process, so Steam still sees a new controller. The part that would close this one is a caller-supplied stable address — DeviceDefinition::device_uniq, already available since games-on-whales/inputtino#31 — which Wolf would populate from something like (paired client id, controller slot).

Two things worth deciding here rather than upstream:

  • Whether flipping the bus to BUS_USB is preferable to plumbing a stable MAC at all. Steam keys DualSense config by address only for pads presenting as Bluetooth — a single profile here holds both configset_DS<mac>.vdf and configset_54c-ce6-<hash>.vdf for the same pad — so a bus flip would sidestep the address question entirely. PS5Joypad::create hardcodes use_bluetooth = true with an unreachable BUS_USB branch.
  • If a stable address is the chosen route, it interacts badly with #490: a pad destroyed and immediately re-created in the same slot would try to reuse an address the kernel may not have released yet, and uhid creation would fail outright. Details in games-on-whales/inputtino#43 (comment). Worth fixing #490 first in that case.
<!-- gh-comment-id:5453450867 --> @piotrminkina commented on GitHub (Aug 28, 2026): Cross-reference, since it changes what "backport the inputtino fix" means in practice: games-on-whales/inputtino#43 (`[2/5]` of the roadmap in games-on-whales/inputtino#44) is open and reworking PS5 MAC handling — currently a monotonic counter with an `is_active_on_system` collision check. That is orthogonal to this issue rather than a fix for it: a counter still hands out a *different* address on every `create`, and it resets with the process, so Steam still sees a new controller. The part that would close this one is a caller-supplied stable address — `DeviceDefinition::device_uniq`, already available since games-on-whales/inputtino#31 — which Wolf would populate from something like (paired client id, controller slot). Two things worth deciding here rather than upstream: - Whether flipping the bus to `BUS_USB` is preferable to plumbing a stable MAC at all. Steam keys DualSense config by address *only* for pads presenting as Bluetooth — a single profile here holds both `configset_DS<mac>.vdf` and `configset_54c-ce6-<hash>.vdf` for the same pad — so a bus flip would sidestep the address question entirely. `PS5Joypad::create` hardcodes `use_bluetooth = true` with an unreachable `BUS_USB` branch. - If a stable address *is* the chosen route, it interacts badly with #490: a pad destroyed and immediately re-created in the same slot would try to reuse an address the kernel may not have released yet, and uhid creation would fail outright. Details in games-on-whales/inputtino#43 (comment). Worth fixing #490 first in that case.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
games-on-whales/wolf#186
No description provided.