From: Thomas Portal <portal.thomas@protonmail.com> Wine's synthesized HID devices only carry the WINEBUS\WINE_COMP_HID compatible ID. A real Windows USB-HID device also exposes the USB class compatible IDs (USB\Class_03&SubClass_xx&Prot_yy and the two shorter forms) on its USB interface parent. Applications that walk to that parent and validate the IDs, for example the Elgato Stream Deck app through CM_Get_Parent, reject the device when they are missing. winebus does not enumerate a separate USB interface device, so report the IDs on the HID device itself, which is what such an application reaches from the HID node. wineusb.sys already reports the same IDs in the same format for the devices it enumerates, so only the hosting device differs. The class, subclass and protocol come from the udev interface and are reported only for USB devices that carry interface information, which leaves the other backends unchanged. The existing WINE_COMP_XINPUT and WINE_COMP_HID ids stay ahead of them so driver binding does not change. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=59891 Signed-off-by: Thomas Portal <portal.thomas@protonmail.com> --- dlls/winebus.sys/bus_iohid.c | 2 +- dlls/winebus.sys/bus_sdl.c | 1 + dlls/winebus.sys/bus_udev.c | 17 +++++++++++++++- dlls/winebus.sys/main.c | 38 +++++++++++++++++++++++++++++++----- dlls/winebus.sys/unixlib.c | 2 ++ dlls/winebus.sys/unixlib.h | 5 +++-- 6 files changed, 56 insertions(+), 9 deletions(-) diff --git a/dlls/winebus.sys/bus_iohid.c b/dlls/winebus.sys/bus_iohid.c index 4a9fdc2715c..5aa24d0b2cf 100644 --- a/dlls/winebus.sys/bus_iohid.c +++ b/dlls/winebus.sys/bus_iohid.c @@ -270,7 +270,7 @@ static void handle_DeviceMatchingCallback(void *context, IOReturn result, void * { struct device_desc desc = { - .input = -1, .is_hidraw = TRUE, + .input = -1, .bus_id = -1, .is_hidraw = TRUE, .serialnumber = {'0','0','0','0',0}, }; struct iohid_device *impl; diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index 7da1a883f1a..8e343b9e574 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -930,6 +930,7 @@ static void sdl_add_device(unsigned int index) struct device_desc desc = { .input = -1, + .bus_id = -1, .manufacturer = {'S','D','L',0}, .serialnumber = {'0','0','0','0',0}, }; diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index b307124c3be..2fdea4f3da1 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -1174,6 +1174,19 @@ static void get_device_subsystem_info(struct udev_device *dev, const char *subsy } } +static void get_usb_interface_info(struct udev_device *dev, struct device_desc *desc) +{ + UINT class = 0, subclass = 0, protocol = 0; + struct udev_device *iface; + const char *tmp; + + if (!(iface = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_interface"))) return; + if ((tmp = udev_device_get_sysattr_value(iface, "bInterfaceClass"))) sscanf(tmp, "%x", &class); + if ((tmp = udev_device_get_sysattr_value(iface, "bInterfaceSubClass"))) sscanf(tmp, "%x", &subclass); + if ((tmp = udev_device_get_sysattr_value(iface, "bInterfaceProtocol"))) sscanf(tmp, "%x", &protocol); + desc->bus_id = (class << 16) | (subclass << 8) | protocol; +} + static NTSTATUS hidraw_device_create(struct udev_device *dev, int fd, const char *devnode, struct device_desc desc) { #ifdef HAVE_LINUX_HIDRAW_H @@ -1346,7 +1359,7 @@ static NTSTATUS lnxev_device_create(struct udev_device *dev, int fd, const char static void udev_add_device(struct udev_device *dev, int fd) { - struct device_desc desc = { .input = -1 }; + struct device_desc desc = { .input = -1, .bus_id = -1 }; const char *subsystem, *devnode; int bus = 0; @@ -1370,6 +1383,8 @@ static void udev_add_device(struct udev_device *dev, int fd) if (bus == BUS_BLUETOOTH) desc.bus_type = BUS_TYPE_BLUETOOTH; else if (bus == BUS_USB) desc.bus_type = BUS_TYPE_USB; + if (desc.bus_type == BUS_TYPE_USB) get_usb_interface_info(dev, &desc); + if (!(subsystem = udev_device_get_subsystem(dev))) { WARN("udev_device_get_subsystem failed for %s.\n", debugstr_a(devnode)); diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c index a6227be9908..7041b72f413 100644 --- a/dlls/winebus.sys/main.c +++ b/dlls/winebus.sys/main.c @@ -259,16 +259,44 @@ static WCHAR *get_compatible_ids(DEVICE_OBJECT *device) static const WCHAR xinput_compat[] = L"WINEBUS\\WINE_COMP_XINPUT"; static const WCHAR hid_compat[] = L"WINEBUS\\WINE_COMP_HID"; struct device_extension *ext = (struct device_extension *)device->DeviceExtension; - DWORD size = sizeof(hid_compat); - WCHAR *dst; + WCHAR usb_compat[71]; + DWORD usb_len = 0, size; + WCHAR *dst, *pos; + + /* A real Windows USB-HID device exposes the USB class compatible IDs on its USB + interface parent. winebus has no separate interface device, so report them on + the HID device, where an application reaches them from the HID node through + CM_Get_Parent. desc.bus_id stays -1 for backends that read no USB interface, + which leaves them unchanged. */ + if (ext->desc.bus_type == BUS_TYPE_USB && ext->desc.bus_id != -1) + { + UINT class = (ext->desc.bus_id >> 16) & 0xff, subclass = (ext->desc.bus_id >> 8) & 0xff, + protocol = ext->desc.bus_id & 0xff; + + usb_len += swprintf(usb_compat + usb_len, ARRAY_SIZE(usb_compat) - usb_len, + L"USB\\Class_%02x&SubClass_%02x&Prot_%02x", class, subclass, protocol) + 1; + usb_len += swprintf(usb_compat + usb_len, ARRAY_SIZE(usb_compat) - usb_len, + L"USB\\Class_%02x&SubClass_%02x", class, subclass) + 1; + usb_len += swprintf(usb_compat + usb_len, ARRAY_SIZE(usb_compat) - usb_len, + L"USB\\Class_%02x", class) + 1; + } + size = sizeof(hid_compat) + usb_len * sizeof(WCHAR); if (ext->desc.is_gamepad) size += sizeof(xinput_compat); if ((dst = ExAllocatePool(PagedPool, size + sizeof(WCHAR)))) { - if (ext->desc.is_gamepad) memcpy(dst, xinput_compat, sizeof(xinput_compat)); - memcpy((char *)dst + size - sizeof(hid_compat), hid_compat, sizeof(hid_compat)); - dst[size / sizeof(WCHAR)] = 0; + pos = dst; + if (ext->desc.is_gamepad) + { + memcpy(pos, xinput_compat, sizeof(xinput_compat)); + pos += sizeof(xinput_compat) / sizeof(WCHAR); + } + memcpy(pos, hid_compat, sizeof(hid_compat)); + pos += sizeof(hid_compat) / sizeof(WCHAR); + memcpy(pos, usb_compat, usb_len * sizeof(WCHAR)); + pos += usb_len; + *pos = 0; } return dst; diff --git a/dlls/winebus.sys/unixlib.c b/dlls/winebus.sys/unixlib.c index e3cabbc8c34..f2b887733bb 100644 --- a/dlls/winebus.sys/unixlib.c +++ b/dlls/winebus.sys/unixlib.c @@ -107,6 +107,7 @@ static const struct device_desc mouse_device_desc = .vid = 0x845e, .pid = 0x0001, .input = -1, + .bus_id = -1, .manufacturer = {'T','h','e',' ','W','i','n','e',' ','P','r','o','j','e','c','t',0}, .product = {'W','i','n','e',' ','H','I','D',' ','m','o','u','s','e',0}, .serialnumber = {'0','0','0','0',0}, @@ -202,6 +203,7 @@ static const struct device_desc keyboard_device_desc = .vid = 0x845e, .pid = 0x0002, .input = -1, + .bus_id = -1, .manufacturer = {'T','h','e',' ','W','i','n','e',' ','P','r','o','j','e','c','t',0}, .product = {'W','i','n','e',' ','H','I','D',' ','k','e','y','b','o','a','r','d',0}, .serialnumber = {'0','0','0','0',0}, diff --git a/dlls/winebus.sys/unixlib.h b/dlls/winebus.sys/unixlib.h index d2b17f4c6d3..06ad570fb46 100644 --- a/dlls/winebus.sys/unixlib.h +++ b/dlls/winebus.sys/unixlib.h @@ -39,6 +39,7 @@ struct device_desc UINT input; UINT uid; UINT bus_type; + UINT bus_id; /* USB: class << 16 | subclass << 8 | protocol, -1 when unset */ BOOL is_gamepad; BOOL is_hidraw; @@ -159,9 +160,9 @@ enum unix_funcs static inline const char *debugstr_device_desc(struct device_desc *desc) { if (!desc) return "(null)"; - return wine_dbg_sprintf("{vid %04x, pid %04x, version %04x, input %d, uid %08x, is_gamepad %u, is_hidraw %u, bus_type %u}", + return wine_dbg_sprintf("{vid %04x, pid %04x, version %04x, input %d, uid %08x, is_gamepad %u, is_hidraw %u, bus_type %u, bus_id %08x}", desc->vid, desc->pid, desc->version, desc->input, desc->uid, - desc->is_gamepad, desc->is_hidraw, desc->bus_type); + desc->is_gamepad, desc->is_hidraw, desc->bus_type, desc->bus_id); } static inline BOOL is_xbox_gamepad(WORD vid, WORD pid) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11205