|
| 1 | +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| 2 | +From: GE-Proton <git@localhost> |
| 3 | +Date: Sun, 16 Aug 2026 00:00:00 -0600 |
| 4 | +Subject: [PATCH] winebus: Match DualSense hotplug cleanup by physical identity. |
| 5 | + |
| 6 | +The DualSense hidraw preference patch removes stale devices before exposing a |
| 7 | +new hidraw node. Matching those devices only by VID, PID, and HID usage also |
| 8 | +matches every other connected controller of the same model. Adding a second |
| 9 | +DualSense therefore unlinks the first controller and leaves its reports without |
| 10 | +a Wine device. |
| 11 | + |
| 12 | +Restrict DualSense cleanup to the same physical controller. Prefer the stable |
| 13 | +serial reported by USB or HID_UNIQ reported by Bluetooth. If a USB device has |
| 14 | +no usable serial, only match interfaces carrying the same bus container ID, |
| 15 | +which identifies the current USB connection. If neither identity is available, |
| 16 | +leave the existing device for the normal udev removal path instead of risking |
| 17 | +the removal of a different controller. |
| 18 | + |
| 19 | +This preserves stale reconnect and evdev sibling cleanup without preventing |
| 20 | +multiple identical DualSense controllers from being connected concurrently. |
| 21 | +--- |
| 22 | + dlls/winebus.sys/main.c | 47 +++++++++++++++++++++++++++++++++++++----- |
| 23 | + 1 file changed, 42 insertions(+), 5 deletions(-) |
| 24 | + |
| 25 | +diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c |
| 26 | +--- a/dlls/winebus.sys/main.c |
| 27 | ++++ b/dlls/winebus.sys/main.c |
| 28 | +@@ -696,16 +696,57 @@ static void bus_unlink_hid_device(DEVICE_OBJECT *device) |
| 29 | + RtlLeaveCriticalSection(&device_list_cs); |
| 30 | + } |
| 31 | + |
| 32 | +-static void bus_unlink_devices_from_vid_pid(struct device_desc *desc, USAGE_AND_PAGE *usages) |
| 33 | ++static BOOL device_desc_has_stable_serial(const struct device_desc *desc) |
| 34 | ++{ |
| 35 | ++ return desc->serialnumber[0] && wcscmp(desc->serialnumber, L"0000"); |
| 36 | ++} |
| 37 | ++ |
| 38 | ++static BOOL device_desc_matches_physical_device(const struct device_desc *left, |
| 39 | ++ const struct device_desc *right) |
| 40 | ++{ |
| 41 | ++ if (left->bus_type != right->bus_type) return FALSE; |
| 42 | ++ |
| 43 | ++ if (device_desc_has_stable_serial(left) && device_desc_has_stable_serial(right)) |
| 44 | ++ return !wcscmp(left->serialnumber, right->serialnumber); |
| 45 | ++ |
| 46 | ++ if (left->bus_type == BUS_TYPE_USB && |
| 47 | ++ !IsEqualGUID(&left->bus_container_id, &GUID_NULL) && |
| 48 | ++ !IsEqualGUID(&right->bus_container_id, &GUID_NULL)) |
| 49 | ++ return IsEqualGUID(&left->bus_container_id, &right->bus_container_id); |
| 50 | ++ |
| 51 | ++ return FALSE; |
| 52 | ++} |
| 53 | ++ |
| 54 | ++static DEVICE_OBJECT *bus_find_device_from_identity(const BOOL is_hidraw, |
| 55 | ++ struct device_desc *desc, USAGE_AND_PAGE *usages) |
| 56 | ++{ |
| 57 | ++ struct device_extension *ext; |
| 58 | ++ UINT buttons; |
| 59 | ++ USAGE_AND_PAGE found_usages; |
| 60 | ++ |
| 61 | ++ LIST_FOR_EACH_ENTRY(ext, &device_list, struct device_extension, entry) |
| 62 | ++ { |
| 63 | ++ found_usages = get_device_usages(ext->unix_device, &buttons); |
| 64 | ++ if (ext->desc.is_hidraw == is_hidraw && ext->desc.vid == desc->vid && |
| 65 | ++ ext->desc.pid == desc->pid && found_usages.UsagePage == usages->UsagePage && |
| 66 | ++ found_usages.Usage == usages->Usage && |
| 67 | ++ device_desc_matches_physical_device(&ext->desc, desc)) |
| 68 | ++ return ext->device; |
| 69 | ++ } |
| 70 | ++ |
| 71 | ++ return NULL; |
| 72 | ++} |
| 73 | ++ |
| 74 | ++static void bus_unlink_devices_from_identity(struct device_desc *desc, USAGE_AND_PAGE *usages) |
| 75 | + { |
| 76 | + DEVICE_OBJECT *device; |
| 77 | + |
| 78 | +- while ((device = bus_find_device_from_vid_pid(FALSE, desc, usages))) |
| 79 | ++ while ((device = bus_find_device_from_identity(FALSE, desc, usages))) |
| 80 | + bus_unlink_hid_device(device); |
| 81 | + |
| 82 | + /* Fast USB reconnect can create a new hidraw node before the old removal event |
| 83 | + * is processed. Remove stale matching hidraw devices before exposing the new one. */ |
| 84 | +- while ((device = bus_find_device_from_vid_pid(TRUE, desc, usages))) |
| 85 | ++ while ((device = bus_find_device_from_identity(TRUE, desc, usages))) |
| 86 | + bus_unlink_hid_device(device); |
| 87 | + } |
| 88 | + |
| 89 | +@@ -1576,7 +1617,7 @@ static DWORD CALLBACK bus_main_thread(void *args) |
| 90 | + { |
| 91 | + RtlEnterCriticalSection(&device_list_cs); |
| 92 | + if (is_dualsense_gamepad(desc.vid, desc.pid)) |
| 93 | +- bus_unlink_devices_from_vid_pid(&event->device_created.desc, &usages); |
| 94 | ++ bus_unlink_devices_from_identity(&event->device_created.desc, &usages); |
| 95 | + else if ((device = bus_find_device_from_vid_pid(FALSE, &event->device_created.desc, &usages))) |
| 96 | + bus_unlink_hid_device(device); |
| 97 | + device = bus_create_hid_device(&event->device_created.desc, event->device); |
| 98 | +-- |
| 99 | +2.51.0 |
0 commit comments