From 6ce5ba8f2f0940273140fd13229a3ea0344bddaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Fri, 3 Sep 2021 09:30:50 +0200 Subject: [PATCH] winebus.sys: Look for duplicate UDEV devices on the unix side. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Devices are only added from a single thread but they may be destroyed concurrently so we need to guard the list against race conditions. Signed-off-by: RĂ©mi Bernon Signed-off-by: Alexandre Julliard --- dlls/winebus.sys/bus_udev.c | 73 +++++++++++++++++++++++---------- dlls/winebus.sys/unix_private.h | 1 + 2 files changed, 52 insertions(+), 22 deletions(-) diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index d87ee2c043e..cca1bf5e168 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -88,11 +88,21 @@ WINE_DEFAULT_DEBUG_CHANNEL(plugplay); WINE_DECLARE_DEBUG_CHANNEL(hid_report); +static CRITICAL_SECTION udev_cs; +static CRITICAL_SECTION_DEBUG udev_cs_debug = +{ + 0, 0, &udev_cs, + { &udev_cs_debug.ProcessLocksList, &udev_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": udev_cs") } +}; +static CRITICAL_SECTION udev_cs = { &udev_cs_debug, -1, 0, 0, 0, 0 }; + static struct udev *udev_context = NULL; static struct udev_monitor *udev_monitor; static int deviceloop_control[2]; static int udev_monitor_fd; static struct list event_queue = LIST_INIT(event_queue); +static struct list device_list = LIST_INIT(device_list); static const WCHAR hidraw_busidW[] = {'H','I','D','R','A','W',0}; static const WCHAR lnxev_busidW[] = {'L','N','X','E','V',0}; @@ -119,6 +129,29 @@ static inline struct platform_private *impl_from_DEVICE_OBJECT(DEVICE_OBJECT *de return impl_from_unix_device(get_unix_device(device)); } +static const char *get_device_syspath(struct udev_device *dev) +{ + struct udev_device *parent; + + if ((parent = udev_device_get_parent_with_subsystem_devtype(dev, "hid", NULL))) + return udev_device_get_syspath(parent); + + if ((parent = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"))) + return udev_device_get_syspath(parent); + + return ""; +} + +static struct platform_private *find_device_from_syspath(const char *path) +{ + struct platform_private *device; + + LIST_FOR_EACH_ENTRY(device, &device_list, struct platform_private, unix_device.entry) + if (!strcmp(get_device_syspath(device->udev_device), path)) return device; + + return NULL; +} + #ifdef HAS_PROPER_INPUT_HEADER static const BYTE ABS_TO_HID_MAP[][2] = { @@ -547,6 +580,10 @@ static void hidraw_device_destroy(struct unix_device *iface) { struct platform_private *private = impl_from_unix_device(iface); + EnterCriticalSection(&udev_cs); + list_remove(&private->unix_device.entry); + LeaveCriticalSection(&udev_cs); + if (private->report_thread) { write(private->control_pipe[1], "q", 1); @@ -858,6 +895,10 @@ static void lnxev_device_destroy(struct unix_device *iface) { struct wine_input_private *ext = input_impl_from_unix_device(iface); + EnterCriticalSection(&udev_cs); + list_remove(&ext->base.unix_device.entry); + LeaveCriticalSection(&udev_cs); + if (ext->base.report_thread) { write(ext->base.control_pipe[1], "q", 1); @@ -1005,25 +1046,6 @@ static const struct unix_device_vtbl lnxev_device_vtbl = }; #endif -static const char *get_device_syspath(struct udev_device *dev) -{ - struct udev_device *parent; - - if ((parent = udev_device_get_parent_with_subsystem_devtype(dev, "hid", NULL))) - return udev_device_get_syspath(parent); - - if ((parent = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"))) - return udev_device_get_syspath(parent); - - return ""; -} - -static int check_device_syspath(DEVICE_OBJECT *device, void* context) -{ - struct platform_private *private = impl_from_DEVICE_OBJECT(device); - return strcmp(get_device_syspath(private->udev_device), context); -} - static void get_device_subsystem_info(struct udev_device *dev, char const *subsystem, struct device_desc *desc) { struct udev_device *parent = NULL; @@ -1093,9 +1115,10 @@ static void udev_add_device(struct udev_device *dev) TRACE("udev %s syspath %s\n", debugstr_a(devnode), udev_device_get_syspath(dev)); #ifdef HAS_PROPER_INPUT_HEADER - device = bus_enumerate_hid_devices(lnxev_busidW, check_device_syspath, (void *)get_device_syspath(dev)); - if (!device) device = bus_enumerate_hid_devices(hidraw_busidW, check_device_syspath, (void *)get_device_syspath(dev)); - if (device) + EnterCriticalSection(&udev_cs); + private = find_device_from_syspath(get_device_syspath(dev)); + LeaveCriticalSection(&udev_cs); + if (private) { TRACE("duplicate device found, not adding the new one\n"); close(fd); @@ -1156,6 +1179,9 @@ static void udev_add_device(struct udev_device *dev) if (!(private = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct platform_private)))) return; private->unix_device.vtbl = &hidraw_device_vtbl; + EnterCriticalSection(&udev_cs); + list_add_tail(&device_list, &private->unix_device.entry); + LeaveCriticalSection(&udev_cs); device = bus_create_hid_device(&desc, &private->unix_device); if (!device) HeapFree(GetProcessHeap(), 0, private); @@ -1166,6 +1192,9 @@ static void udev_add_device(struct udev_device *dev) if (!(private = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct wine_input_private)))) return; private->unix_device.vtbl = &lnxev_device_vtbl; + EnterCriticalSection(&udev_cs); + list_add_tail(&device_list, &private->unix_device.entry); + LeaveCriticalSection(&udev_cs); device = bus_create_hid_device(&desc, &private->unix_device); if (!device) HeapFree(GetProcessHeap(), 0, private); diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h index 874a837deff..81c4658068b 100644 --- a/dlls/winebus.sys/unix_private.h +++ b/dlls/winebus.sys/unix_private.h @@ -44,6 +44,7 @@ struct unix_device_vtbl struct unix_device { const struct unix_device_vtbl *vtbl; + struct list entry; }; extern NTSTATUS sdl_bus_init(void *) DECLSPEC_HIDDEN;