And match it in winehid.sys instead of individual bus ids.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com ---
v3: * Use memcpy whenever possible, fix the missing NULL terminators.
* Renamed everything. I kept the "gamepad" naming, as I think it'll be eventually quite accurate. Later patches will use "xinput" for the device to be used by xinput.
* Create new devices on a WINEXINPUT\ bus, instead of re-using the underlying bus. This helps with the transitions from old devices to new ones, as setupapi keeps a cache of compatible ids.
The internal device would use an "&XI_" suffix for hidclass.sys to distinguish it from the "&IG_" one.
Otherwise we would have to change the patch ordering so that the cache don't mess up the driver matching (the old &IG_00 device would retain its COMP_XINPUT id, which would make winexinput device match with itself recursively).
I'm just sending the first patches for now, but I also rewritten the next ones to store what was "device_state" directly on the func_device, initializing it on the fdo creation instead of the gamepad, as well as to use a completion instead of a synchronous call to the lower driver.
dlls/winebus.sys/main.c | 22 ++++++++++++++++++++-- dlls/winehid.sys/winehid.inf | 7 +------ 2 files changed, 21 insertions(+), 8 deletions(-)
diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c index e9c85deb846..49385576d62 100644 --- a/dlls/winebus.sys/main.c +++ b/dlls/winebus.sys/main.c @@ -226,7 +226,7 @@ static WCHAR *get_device_id(DEVICE_OBJECT *device) return dst; }
-static WCHAR *get_compatible_ids(DEVICE_OBJECT *device) +static WCHAR *get_hardware_ids(DEVICE_OBJECT *device) { struct device_extension *ext = (struct device_extension *)device->DeviceExtension; WCHAR *dst; @@ -240,6 +240,24 @@ static WCHAR *get_compatible_ids(DEVICE_OBJECT *device) return dst; }
+static WCHAR *get_compatible_ids(DEVICE_OBJECT *device) +{ + static const WCHAR hid_compat[] = + { + 'W','I','N','E','B','U','S','\','W','I','N','E','_','C','O','M','P','_','H','I','D',0 + }; + DWORD size = sizeof(hid_compat); + WCHAR *dst; + + if ((dst = ExAllocatePool(PagedPool, size + sizeof(WCHAR)))) + { + memcpy(dst, hid_compat, sizeof(hid_compat)); + dst[size / sizeof(WCHAR)] = 0; + } + + return dst; +} + static void remove_pending_irps(DEVICE_OBJECT *device) { struct device_extension *ext = device->DeviceExtension; @@ -444,7 +462,7 @@ static NTSTATUS handle_IRP_MN_QUERY_ID(DEVICE_OBJECT *device, IRP *irp) { case BusQueryHardwareIDs: TRACE("BusQueryHardwareIDs\n"); - irp->IoStatus.Information = (ULONG_PTR)get_compatible_ids(device); + irp->IoStatus.Information = (ULONG_PTR)get_hardware_ids(device); break; case BusQueryCompatibleIDs: TRACE("BusQueryCompatibleIDs\n"); diff --git a/dlls/winehid.sys/winehid.inf b/dlls/winehid.sys/winehid.inf index f9ed4091217..c1d8a1cf999 100644 --- a/dlls/winehid.sys/winehid.inf +++ b/dlls/winehid.sys/winehid.inf @@ -7,12 +7,7 @@ Class=HIDClass Wine=mfg_section
[mfg_section] -Wine hidraw device=device_section,HIDRAW -Wine IOHID device=device_section,IOHID -Wine libevent device=device_section,LNXEV -Wine SDL HID device=device_section,SDLJOY -Wine mouse device=device_section,WINEMOUSE -Wine keyboard device=device_section,WINEKEYBOARD +Wine HID compatible device=device_section,WINEBUS\WINE_COMP_HID
[device_section.Services] AddService = winehid,0x2,svc_section
In addition, and before WINEBUS\WINE_COMP_HID, so that winexinput.sys will match first as soon as it is introduced.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winebus.sys/main.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c index 49385576d62..e58bf873eac 100644 --- a/dlls/winebus.sys/main.c +++ b/dlls/winebus.sys/main.c @@ -242,16 +242,24 @@ static WCHAR *get_hardware_ids(DEVICE_OBJECT *device)
static WCHAR *get_compatible_ids(DEVICE_OBJECT *device) { + static const WCHAR xinput_compat[] = + { + 'W','I','N','E','B','U','S','\','W','I','N','E','_','C','O','M','P','_','X','I','N','P','U','T',0 + }; static const WCHAR hid_compat[] = { 'W','I','N','E','B','U','S','\','W','I','N','E','_','C','O','M','P','_','H','I','D',0 }; + struct device_extension *ext = (struct device_extension *)device->DeviceExtension; DWORD size = sizeof(hid_compat); WCHAR *dst;
+ if (ext->is_gamepad) size += sizeof(xinput_compat); + if ((dst = ExAllocatePool(PagedPool, size + sizeof(WCHAR)))) { - memcpy(dst, hid_compat, sizeof(hid_compat)); + if (ext->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; }
Currently only acting as a pass-through driver, matching any device with a WINEBUS\WINE_COMP_XINPUT compatible id.
This creates new WINEXINPUT\ bus and the gamepad PDO on it, adding the &IG_ device id suffix to the original device id (replacing an eventual &MI_ suffix), and removes the need to set it on winebus.sys side.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- configure.ac | 1 + dlls/winexinput.sys/Makefile.in | 8 + dlls/winexinput.sys/main.c | 429 ++++++++++++++++++++++++ dlls/winexinput.sys/winexinput.inf | 22 ++ dlls/winexinput.sys/winexinput.rc | 20 ++ dlls/winexinput.sys/winexinput.sys.spec | 1 + loader/wine.inf.in | 1 + 7 files changed, 482 insertions(+) create mode 100644 dlls/winexinput.sys/Makefile.in create mode 100644 dlls/winexinput.sys/main.c create mode 100644 dlls/winexinput.sys/winexinput.inf create mode 100644 dlls/winexinput.sys/winexinput.rc create mode 100644 dlls/winexinput.sys/winexinput.sys.spec
diff --git a/configure.ac b/configure.ac index 594794ed93c..e42ba039f18 100644 --- a/configure.ac +++ b/configure.ac @@ -3798,6 +3798,7 @@ WINE_CONFIG_MAKEFILE(dlls/wineqtdecoder) WINE_CONFIG_MAKEFILE(dlls/wineusb.sys) WINE_CONFIG_MAKEFILE(dlls/winevulkan) WINE_CONFIG_MAKEFILE(dlls/winex11.drv) +WINE_CONFIG_MAKEFILE(dlls/winexinput.sys) WINE_CONFIG_MAKEFILE(dlls/wing.dll16,enable_win16) WINE_CONFIG_MAKEFILE(dlls/wing32) WINE_CONFIG_MAKEFILE(dlls/winhttp) diff --git a/dlls/winexinput.sys/Makefile.in b/dlls/winexinput.sys/Makefile.in new file mode 100644 index 00000000000..592c90295b2 --- /dev/null +++ b/dlls/winexinput.sys/Makefile.in @@ -0,0 +1,8 @@ +MODULE = winexinput.sys +IMPORTS = ntoskrnl +EXTRADLLFLAGS = -mno-cygwin -Wl,--subsystem,native + +C_SRCS = \ + main.c + +RC_SRCS = winexinput.rc diff --git a/dlls/winexinput.sys/main.c b/dlls/winexinput.sys/main.c new file mode 100644 index 00000000000..1e18d7dacbc --- /dev/null +++ b/dlls/winexinput.sys/main.c @@ -0,0 +1,429 @@ +/* + * WINE XInput device driver + * + * Copyright 2021 Rémi Bernon for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <stdarg.h> +#include <stdlib.h> + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winternl.h" +#include "winioctl.h" + +#include "cfgmgr32.h" +#include "ddk/wdm.h" +#include "ddk/hidport.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(winexinput); + +#if defined(__i386__) && !defined(_WIN32) +extern void *WINAPI wrap_fastcall_func1(void *func, const void *a); +__ASM_STDCALL_FUNC(wrap_fastcall_func1, 8, + "popl %ecx\n\t" + "popl %eax\n\t" + "xchgl (%esp),%ecx\n\t" + "jmp *%eax"); +#define call_fastcall_func1(func,a) wrap_fastcall_func1(func,a) +#else +#define call_fastcall_func1(func,a) func(a) +#endif + +struct device +{ + BOOL is_fdo; + BOOL removed; + WCHAR device_id[MAX_DEVICE_ID_LEN]; +}; + +static inline struct device *impl_from_DEVICE_OBJECT(DEVICE_OBJECT *device) +{ + return (struct device *)device->DeviceExtension; +} + +struct phys_device +{ + struct device base; + struct func_device *fdo; +}; + +struct func_device +{ + struct device base; + DEVICE_OBJECT *bus_device; + + /* the bogus HID gamepad, as exposed by native XUSB */ + DEVICE_OBJECT *gamepad_device; + WCHAR instance_id[MAX_DEVICE_ID_LEN]; +}; + +static inline struct func_device *fdo_from_DEVICE_OBJECT(DEVICE_OBJECT *device) +{ + struct device *impl = impl_from_DEVICE_OBJECT(device); + if (impl->is_fdo) return CONTAINING_RECORD(impl, struct func_device, base); + else return CONTAINING_RECORD(impl, struct phys_device, base)->fdo; +} + +static NTSTATUS WINAPI internal_ioctl(DEVICE_OBJECT *device, IRP *irp) +{ + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); + ULONG code = stack->Parameters.DeviceIoControl.IoControlCode; + struct func_device *fdo = fdo_from_DEVICE_OBJECT(device); + struct device *impl = impl_from_DEVICE_OBJECT(device); + + if (InterlockedOr(&impl->removed, FALSE)) + { + irp->IoStatus.Status = STATUS_DELETE_PENDING; + irp->IoStatus.Information = 0; + IoCompleteRequest(irp, IO_NO_INCREMENT); + return STATUS_DELETE_PENDING; + } + + TRACE("device %p, irp %p, code %#x, bus_device %p.\n", device, irp, code, fdo->bus_device); + + IoSkipCurrentIrpStackLocation(irp); + return IoCallDriver(fdo->bus_device, irp); +} + +static WCHAR *query_instance_id(DEVICE_OBJECT *device) +{ + struct func_device *fdo = fdo_from_DEVICE_OBJECT(device); + DWORD size = (wcslen(fdo->instance_id) + 1) * sizeof(WCHAR); + WCHAR *dst; + + if ((dst = ExAllocatePool(PagedPool, size))) + memcpy(dst, fdo->instance_id, size); + + return dst; +} + +static WCHAR *query_device_id(DEVICE_OBJECT *device) +{ + struct device *impl = impl_from_DEVICE_OBJECT(device); + DWORD size = (wcslen(impl->device_id) + 1) * sizeof(WCHAR); + WCHAR *dst; + + if ((dst = ExAllocatePool(PagedPool, size))) + memcpy(dst, impl->device_id, size); + + return dst; +} + +static WCHAR *query_hardware_ids(DEVICE_OBJECT *device) +{ + struct device *impl = impl_from_DEVICE_OBJECT(device); + DWORD size = (wcslen(impl->device_id) + 1) * sizeof(WCHAR); + WCHAR *dst; + + if ((dst = ExAllocatePool(PagedPool, size + sizeof(WCHAR)))) + { + memcpy(dst, impl->device_id, size); + dst[size / sizeof(WCHAR)] = 0; + } + + return dst; +} + +static WCHAR *query_compatible_ids(DEVICE_OBJECT *device) +{ + static const WCHAR hid_compat[] = L"WINEBUS\WINE_COMP_HID"; + DWORD size = sizeof(hid_compat); + WCHAR *dst; + + if ((dst = ExAllocatePool(PagedPool, size + sizeof(WCHAR)))) + { + memcpy(dst, hid_compat, sizeof(hid_compat)); + dst[size / sizeof(WCHAR)] = 0; + } + + return dst; +} + +static NTSTATUS WINAPI pdo_pnp(DEVICE_OBJECT *device, IRP *irp) +{ + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); + struct func_device *fdo = fdo_from_DEVICE_OBJECT(device); + struct device *impl = impl_from_DEVICE_OBJECT(device); + ULONG code = stack->MinorFunction; + NTSTATUS status; + + TRACE("device %p, irp %p, code %#x, bus_device %p.\n", device, irp, code, fdo->bus_device); + + switch (code) + { + case IRP_MN_START_DEVICE: + status = STATUS_SUCCESS; + break; + + case IRP_MN_SURPRISE_REMOVAL: + status = STATUS_SUCCESS; + if (InterlockedExchange(&impl->removed, TRUE)) break; + break; + + case IRP_MN_REMOVE_DEVICE: + irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(irp, IO_NO_INCREMENT); + IoDeleteDevice(device); + return STATUS_SUCCESS; + + case IRP_MN_QUERY_ID: + switch (stack->Parameters.QueryId.IdType) + { + case BusQueryHardwareIDs: + irp->IoStatus.Information = (ULONG_PTR)query_hardware_ids(device); + break; + case BusQueryCompatibleIDs: + irp->IoStatus.Information = (ULONG_PTR)query_compatible_ids(device); + break; + case BusQueryDeviceID: + irp->IoStatus.Information = (ULONG_PTR)query_device_id(device); + break; + case BusQueryInstanceID: + irp->IoStatus.Information = (ULONG_PTR)query_instance_id(device); + break; + default: + IoSkipCurrentIrpStackLocation(irp); + return IoCallDriver(fdo->bus_device, irp); + } + + if (!irp->IoStatus.Information) status = STATUS_NO_MEMORY; + else status = STATUS_SUCCESS; + break; + + default: + IoSkipCurrentIrpStackLocation(irp); + return IoCallDriver(fdo->bus_device, irp); + } + + irp->IoStatus.Status = status; + IoCompleteRequest(irp, IO_NO_INCREMENT); + return status; +} + +static NTSTATUS create_child_pdos(DEVICE_OBJECT *device) +{ + struct func_device *fdo = fdo_from_DEVICE_OBJECT(device); + DEVICE_OBJECT *gamepad_device; + struct phys_device *pdo; + UNICODE_STRING name_str; + WCHAR *tmp, name[255]; + NTSTATUS status; + + swprintf(name, ARRAY_SIZE(name), L"\Device\WINEXINPUT#%p&%p&0", + device->DriverObject, fdo->bus_device); + RtlInitUnicodeString(&name_str, name); + + if ((status = IoCreateDevice(device->DriverObject, sizeof(struct phys_device), + &name_str, 0, 0, FALSE, &gamepad_device))) + { + ERR("failed to create gamepad device, status %#x.\n", status); + return status; + } + + fdo->gamepad_device = gamepad_device; + pdo = gamepad_device->DeviceExtension; + pdo->fdo = fdo; + pdo->base.is_fdo = FALSE; + wcscpy(pdo->base.device_id, fdo->base.device_id); + + if ((tmp = wcsstr(pdo->base.device_id, L"&MI_"))) memcpy(tmp, L"&IG", 6); + else wcscat(pdo->base.device_id, L"&IG_00"); + + TRACE("device %p, gamepad device %p.\n", device, gamepad_device); + + IoInvalidateDeviceRelations(fdo->bus_device, BusRelations); + return STATUS_SUCCESS; +} + +static NTSTATUS WINAPI set_event_completion(DEVICE_OBJECT *device, IRP *irp, void *context) +{ + if (irp->PendingReturned) KeSetEvent((KEVENT *)context, IO_NO_INCREMENT, FALSE); + return STATUS_MORE_PROCESSING_REQUIRED; +} + +static NTSTATUS WINAPI fdo_pnp(DEVICE_OBJECT *device, IRP *irp) +{ + IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); + struct func_device *fdo = fdo_from_DEVICE_OBJECT(device); + ULONG code = stack->MinorFunction; + DEVICE_RELATIONS *devices; + DEVICE_OBJECT *child; + NTSTATUS status; + KEVENT event; + + TRACE("device %p, irp %p, code %#x, bus_device %p.\n", device, irp, code, fdo->bus_device); + + switch (stack->MinorFunction) + { + case IRP_MN_QUERY_DEVICE_RELATIONS: + if (stack->Parameters.QueryDeviceRelations.Type == BusRelations) + { + if (!(devices = ExAllocatePool(PagedPool, offsetof(DEVICE_RELATIONS, Objects[2])))) + { + irp->IoStatus.Status = STATUS_NO_MEMORY; + IoCompleteRequest(irp, IO_NO_INCREMENT); + return STATUS_NO_MEMORY; + } + + devices->Count = 0; + if ((child = fdo->gamepad_device)) + { + devices->Objects[devices->Count] = child; + call_fastcall_func1(ObfReferenceObject, child); + devices->Count++; + } + + irp->IoStatus.Information = (ULONG_PTR)devices; + irp->IoStatus.Status = STATUS_SUCCESS; + } + + IoSkipCurrentIrpStackLocation(irp); + return IoCallDriver(fdo->bus_device, irp); + + case IRP_MN_START_DEVICE: + KeInitializeEvent(&event, NotificationEvent, FALSE); + IoCopyCurrentIrpStackLocationToNext(irp); + IoSetCompletionRoutine(irp, set_event_completion, &event, TRUE, TRUE, TRUE); + + status = IoCallDriver(fdo->bus_device, irp); + if (status == STATUS_PENDING) + { + KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); + status = irp->IoStatus.Status; + } + if (!status) status = create_child_pdos(device); + + if (status) irp->IoStatus.Status = status; + IoCompleteRequest(irp, IO_NO_INCREMENT); + return status; + + case IRP_MN_REMOVE_DEVICE: + IoSkipCurrentIrpStackLocation(irp); + status = IoCallDriver(fdo->bus_device, irp); + IoDetachDevice(fdo->bus_device); + IoDeleteDevice(device); + return status; + + default: + IoSkipCurrentIrpStackLocation(irp); + return IoCallDriver(fdo->bus_device, irp); + } + + return STATUS_SUCCESS; +} + +static NTSTATUS WINAPI driver_pnp(DEVICE_OBJECT *device, IRP *irp) +{ + struct device *impl = impl_from_DEVICE_OBJECT(device); + if (impl->is_fdo) return fdo_pnp(device, irp); + return pdo_pnp(device, irp); +} + +static NTSTATUS get_device_id(DEVICE_OBJECT *device, BUS_QUERY_ID_TYPE type, WCHAR *id) +{ + IO_STACK_LOCATION *stack; + IO_STATUS_BLOCK io; + KEVENT event; + IRP *irp; + + KeInitializeEvent(&event, NotificationEvent, FALSE); + irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, device, NULL, 0, NULL, &event, &io); + if (irp == NULL) return STATUS_NO_MEMORY; + + stack = IoGetNextIrpStackLocation(irp); + stack->MinorFunction = IRP_MN_QUERY_ID; + stack->Parameters.QueryId.IdType = type; + + if (IoCallDriver(device, irp) == STATUS_PENDING) + KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); + + wcscpy(id, (WCHAR *)io.Information); + ExFreePool((WCHAR *)io.Information); + return io.Status; +} + +static NTSTATUS WINAPI add_device(DRIVER_OBJECT *driver, DEVICE_OBJECT *bus_device) +{ + WCHAR bus_id[MAX_DEVICE_ID_LEN], *device_id, instance_id[MAX_DEVICE_ID_LEN]; + struct func_device *fdo; + DEVICE_OBJECT *device; + NTSTATUS status; + + TRACE("driver %p, bus_device %p.\n", driver, bus_device); + + if ((status = get_device_id(bus_device, BusQueryDeviceID, bus_id))) + { + ERR("failed to get bus device id, status %#x.\n", status); + return status; + } + + if ((device_id = wcsrchr(bus_id, '\'))) *device_id++ = 0; + else + { + ERR("unexpected device id %s\n", debugstr_w(bus_id)); + return STATUS_UNSUCCESSFUL; + } + + if ((status = get_device_id(bus_device, BusQueryInstanceID, instance_id))) + { + ERR("failed to get bus device instance id, status %#x.\n", status); + return status; + } + + if ((status = IoCreateDevice(driver, sizeof(struct func_device), NULL, + FILE_DEVICE_BUS_EXTENDER, 0, FALSE, &device))) + { + ERR("failed to create bus FDO, status %#x.\n", status); + return status; + } + + fdo = device->DeviceExtension; + fdo->base.is_fdo = TRUE; + swprintf(fdo->base.device_id, MAX_DEVICE_ID_LEN, L"WINEXINPUT\%s", device_id); + + fdo->bus_device = bus_device; + wcscpy(fdo->instance_id, instance_id); + + TRACE("device %p, bus_id %s, device_id %s, instance_id %s.\n", device, debugstr_w(bus_id), + debugstr_w(fdo->base.device_id), debugstr_w(fdo->instance_id)); + + IoAttachDeviceToDeviceStack(device, bus_device); + device->Flags &= ~DO_DEVICE_INITIALIZING; + return STATUS_SUCCESS; +} + +static void WINAPI driver_unload(DRIVER_OBJECT *driver) +{ + TRACE("driver %p\n", driver); +} + +NTSTATUS WINAPI DriverEntry(DRIVER_OBJECT *driver, UNICODE_STRING *path) +{ + TRACE("driver %p, path %s.\n", driver, debugstr_w(path->Buffer)); + + driver->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = internal_ioctl; + driver->MajorFunction[IRP_MJ_PNP] = driver_pnp; + driver->DriverExtension->AddDevice = add_device; + driver->DriverUnload = driver_unload; + + return STATUS_SUCCESS; +} diff --git a/dlls/winexinput.sys/winexinput.inf b/dlls/winexinput.sys/winexinput.inf new file mode 100644 index 00000000000..2f388c190ca --- /dev/null +++ b/dlls/winexinput.sys/winexinput.inf @@ -0,0 +1,22 @@ +[Version] +Signature="$CHICAGO$" +ClassGuid={4d36e97d-e325-11ce-bfc1-08002be10318} +Class=System + +[Manufacturer] +Wine=mfg_section + +[mfg_section] +Wine XInput compatible device=device_section,WINEBUS\WINE_COMP_XINPUT + +[device_section.Services] +AddService = xinput,0x2,svc_section + +[svc_section] +Description="Wine XInput device driver" +DisplayName="Wine XInput" +ServiceBinary="%12%\winexinput.sys" +LoadOrderGroup="WinePlugPlay" +ServiceType=1 +StartType=3 +ErrorControl=1 diff --git a/dlls/winexinput.sys/winexinput.rc b/dlls/winexinput.sys/winexinput.rc new file mode 100644 index 00000000000..8e5ed1261e5 --- /dev/null +++ b/dlls/winexinput.sys/winexinput.rc @@ -0,0 +1,20 @@ +/* + * Copyright 2021 Rémi Bernon for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* @makedep: winexinput.inf */ +1 WINE_DATA_FILE winexinput.inf diff --git a/dlls/winexinput.sys/winexinput.sys.spec b/dlls/winexinput.sys/winexinput.sys.spec new file mode 100644 index 00000000000..76421d7e35b --- /dev/null +++ b/dlls/winexinput.sys/winexinput.sys.spec @@ -0,0 +1 @@ +# nothing to export diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 49e4f45da27..adaa29a804b 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -4060,6 +4060,7 @@ services,"@%11%\ws2_32.dll,-4" winebus.inf,"@%12%\winebus.sys,-1" winehid.inf,"@%12%\winehid.sys,-1" wineusb.inf,"@%12%\wineusb.sys,-1" +winexinput.inf,"@%12%\winexinput.sys,-1"
[NlsFiles] c_037.nls
Sorry I missed this before, but...
On 8/30/21 4:06 AM, Rémi Bernon wrote:
+static NTSTATUS WINAPI pdo_pnp(DEVICE_OBJECT *device, IRP *irp) +{
- IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
- struct func_device *fdo = fdo_from_DEVICE_OBJECT(device);
- struct device *impl = impl_from_DEVICE_OBJECT(device);
- ULONG code = stack->MinorFunction;
- NTSTATUS status;
- TRACE("device %p, irp %p, code %#x, bus_device %p.\n", device, irp, code, fdo->bus_device);
- switch (code)
- {
- case IRP_MN_START_DEVICE:
status = STATUS_SUCCESS;
break;
- case IRP_MN_SURPRISE_REMOVAL:
status = STATUS_SUCCESS;
if (InterlockedExchange(&impl->removed, TRUE)) break;
break;
- case IRP_MN_REMOVE_DEVICE:
irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(irp, IO_NO_INCREMENT);
IoDeleteDevice(device);
return STATUS_SUCCESS;
- case IRP_MN_QUERY_ID:
switch (stack->Parameters.QueryId.IdType)
{
case BusQueryHardwareIDs:
irp->IoStatus.Information = (ULONG_PTR)query_hardware_ids(device);
break;
case BusQueryCompatibleIDs:
irp->IoStatus.Information = (ULONG_PTR)query_compatible_ids(device);
break;
case BusQueryDeviceID:
irp->IoStatus.Information = (ULONG_PTR)query_device_id(device);
break;
case BusQueryInstanceID:
irp->IoStatus.Information = (ULONG_PTR)query_instance_id(device);
break;
default:
IoSkipCurrentIrpStackLocation(irp);
return IoCallDriver(fdo->bus_device, irp);
}
if (!irp->IoStatus.Information) status = STATUS_NO_MEMORY;
else status = STATUS_SUCCESS;
break;
- default:
IoSkipCurrentIrpStackLocation(irp);
return IoCallDriver(fdo->bus_device, irp);
...this is wrong; you can't pass PnP IRPs down to the parent device stack. You should be completing them with the status already in irp->IoStatus.Status instead.
For the record, I think it's a good idea to also print a FIXME in this case—just in case we start sending out a new IRP from ntoskrnl that our drivers need to handle.
- }
- irp->IoStatus.Status = status;
- IoCompleteRequest(irp, IO_NO_INCREMENT);
- return status;
+}
Otherwise I think this patch looks good.
The &IG_00 suffix is now automatically added by winexinput.sys.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winebus.sys/bus_iohid.c | 5 +---- dlls/winebus.sys/bus_sdl.c | 5 +---- dlls/winebus.sys/bus_udev.c | 2 -- dlls/winebus.sys/main.c | 18 ++++-------------- 4 files changed, 6 insertions(+), 24 deletions(-)
diff --git a/dlls/winebus.sys/bus_iohid.c b/dlls/winebus.sys/bus_iohid.c index eb5eddd60fa..9b842dc81a5 100644 --- a/dlls/winebus.sys/bus_iohid.c +++ b/dlls/winebus.sys/bus_iohid.c @@ -299,7 +299,6 @@ static void handle_DeviceMatchingCallback(void *context, IOReturn result, void * CFStringRef str = NULL; WCHAR serial_string[256]; BOOL is_gamepad = FALSE; - WORD input = -1;
TRACE("OS/X IOHID Device Added %p\n", IOHIDDevice);
@@ -361,13 +360,11 @@ static void handle_DeviceMatchingCallback(void *context, IOReturn result, void * is_gamepad = (axes == 6 && buttons >= 14); } } - if (is_gamepad) - input = 0;
if (!(private = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct platform_private)))) return;
- device = bus_create_hid_device(busidW, vid, pid, input, version, uid, str ? serial_string : NULL, + device = bus_create_hid_device(busidW, vid, pid, -1, version, uid, str ? serial_string : NULL, is_gamepad, &iohid_vtbl, &private->unix_device); if (!device) HeapFree(GetProcessHeap(), 0, private); else diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index 61d1240e05d..8259531779b 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -744,7 +744,6 @@ static void try_add_device(unsigned int index) WCHAR serial[34] = {0}; char guid_str[34]; BOOL is_xbox_gamepad; - WORD input = -1;
SDL_Joystick* joystick; SDL_JoystickID id; @@ -795,12 +794,10 @@ static void try_add_device(unsigned int index) button_count = pSDL_JoystickNumButtons(joystick); is_xbox_gamepad = (axis_count == 6 && button_count >= 14); } - if (is_xbox_gamepad) - input = 0;
if (!(private = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*private)))) return;
- device = bus_create_hid_device(sdl_busidW, vid, pid, input, version, index, serial, is_xbox_gamepad, + device = bus_create_hid_device(sdl_busidW, vid, pid, -1, version, index, serial, is_xbox_gamepad, &sdl_vtbl, &private->unix_device); if (!device) HeapFree(GetProcessHeap(), 0, private); else diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index bd70a66f2cd..8d631fb928e 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -1131,8 +1131,6 @@ static void try_add_device(struct udev_device *dev) is_gamepad = (axes == 6 && buttons >= 14); } #endif - if (input == (WORD)-1 && is_gamepad) - input = 0;
TRACE("Found udev device %s (vid %04x, pid %04x, version %04x, input %d, serial %s)\n", debugstr_a(devnode), vid, pid, version, input, debugstr_w(serial)); diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c index e58bf873eac..28bbb5ff7a9 100644 --- a/dlls/winebus.sys/main.c +++ b/dlls/winebus.sys/main.c @@ -149,8 +149,6 @@ static CRITICAL_SECTION device_list_cs = { &critsect_debug, -1, 0, 0, 0, 0 }; static struct list device_list = LIST_INIT(device_list);
static const WCHAR zero_serialW[]= {'0','0','0','0',0}; -static const WCHAR miW[] = {'M','I',0}; -static const WCHAR igW[] = {'I','G',0};
static NTSTATUS winebus_call(unsigned int code, void *args) { @@ -202,25 +200,17 @@ static WCHAR *get_instance_id(DEVICE_OBJECT *device)
static WCHAR *get_device_id(DEVICE_OBJECT *device) { + static const WCHAR input_formatW[] = {'&','M','I','_','%','0','2','u',0}; static const WCHAR formatW[] = {'%','s','\','v','i','d','_','%','0','4','x', '&','p','i','d','_','%','0','4','x',0}; - static const WCHAR format_inputW[] = {'%','s','\','v','i','d','_','%','0','4','x', - '&','p','i','d','_','%','0','4','x','&','%','s','_','%','0','2','i',0}; struct device_extension *ext = (struct device_extension *)device->DeviceExtension; DWORD len = strlenW(ext->busid) + 34; - WCHAR *dst; + WCHAR *dst, *tmp;
if ((dst = ExAllocatePool(PagedPool, len * sizeof(WCHAR)))) { - if (ext->input == (WORD)-1) - { - sprintfW(dst, formatW, ext->busid, ext->vid, ext->pid); - } - else - { - sprintfW(dst, format_inputW, ext->busid, ext->vid, ext->pid, - ext->is_gamepad ? igW : miW, ext->input); - } + tmp = dst + sprintfW(dst, formatW, ext->busid, ext->vid, ext->pid); + if (ext->input != (WORD)-1) sprintfW(tmp, input_formatW, ext->input); }
return dst;