Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winexinput.sys/main.c | 68 ++++++++++++++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 7 deletions(-)
diff --git a/dlls/winexinput.sys/main.c b/dlls/winexinput.sys/main.c index 60f7a8ed5dd..330c9203b7d 100644 --- a/dlls/winexinput.sys/main.c +++ b/dlls/winexinput.sys/main.c @@ -82,6 +82,14 @@ struct func_device
WCHAR instance_id[MAX_DEVICE_ID_LEN];
+ HIDP_VALUE_CAPS lx_caps; + HIDP_VALUE_CAPS ly_caps; + HIDP_VALUE_CAPS lt_caps; + HIDP_VALUE_CAPS rx_caps; + HIDP_VALUE_CAPS ry_caps; + HIDP_VALUE_CAPS rt_caps; + HIDP_DEVICE_DESC device_desc; + /* everything below requires holding the cs */ CRITICAL_SECTION cs; ULONG report_len; @@ -442,13 +450,27 @@ static NTSTATUS sync_ioctl(DEVICE_OBJECT *device, DWORD code, void *in_buf, DWOR return io.Status; }
+static void check_value_caps(struct func_device *fdo, USHORT usage, HIDP_VALUE_CAPS *caps) +{ + switch (usage) + { + case HID_USAGE_GENERIC_X: fdo->lx_caps = *caps; break; + case HID_USAGE_GENERIC_Y: fdo->ly_caps = *caps; break; + case HID_USAGE_GENERIC_Z: fdo->lt_caps = *caps; break; + case HID_USAGE_GENERIC_RX: fdo->rx_caps = *caps; break; + case HID_USAGE_GENERIC_RY: fdo->ry_caps = *caps; break; + case HID_USAGE_GENERIC_RZ: fdo->rt_caps = *caps; break; + } +} + static NTSTATUS initialize_device(DEVICE_OBJECT *device) { struct func_device *fdo = fdo_from_DEVICE_OBJECT(device); - ULONG i, report_desc_len, report_count; + ULONG i, u, button_count, report_desc_len, report_count; PHIDP_REPORT_DESCRIPTOR report_desc; PHIDP_PREPARSED_DATA preparsed; - HIDP_DEVICE_DESC device_desc; + HIDP_BUTTON_CAPS *button_caps; + HIDP_VALUE_CAPS *value_caps; HIDP_REPORT_IDS *reports; HID_DESCRIPTOR hid_desc; NTSTATUS status; @@ -461,16 +483,48 @@ static NTSTATUS initialize_device(DEVICE_OBJECT *device) if (!(report_desc = malloc(report_desc_len))) return STATUS_NO_MEMORY;
status = sync_ioctl(fdo->bus_device, IOCTL_HID_GET_REPORT_DESCRIPTOR, NULL, 0, report_desc, report_desc_len); - if (!status) status = HidP_GetCollectionDescription(report_desc, report_desc_len, PagedPool, &device_desc); + if (!status) status = HidP_GetCollectionDescription(report_desc, report_desc_len, PagedPool, &fdo->device_desc); free(report_desc); if (status != HIDP_STATUS_SUCCESS) return status;
- preparsed = device_desc.CollectionDesc->PreparsedData; + preparsed = fdo->device_desc.CollectionDesc->PreparsedData; status = HidP_GetCaps(preparsed, &caps); if (status != HIDP_STATUS_SUCCESS) return status;
- reports = device_desc.ReportIDs; - report_count = device_desc.ReportIDsLength; + button_count = 0; + if (!(button_caps = malloc(sizeof(*button_caps) * caps.NumberInputButtonCaps))) return STATUS_NO_MEMORY; + status = HidP_GetButtonCaps(HidP_Input, button_caps, &caps.NumberInputButtonCaps, preparsed); + if (status != HIDP_STATUS_SUCCESS) return status; + else for (i = 0; i < caps.NumberInputButtonCaps; i++) + { + if (button_caps[i].UsagePage != HID_USAGE_PAGE_BUTTON) continue; + if (button_caps[i].IsRange) button_count = max(button_count, button_caps[i].Range.UsageMax); + else button_count = max(button_count, button_caps[i].NotRange.Usage); + } + free(button_caps); + if (button_count < 10) WARN("only %u buttons found\n", button_count); + + if (!(value_caps = malloc(sizeof(*value_caps) * caps.NumberInputValueCaps))) return STATUS_NO_MEMORY; + status = HidP_GetValueCaps(HidP_Input, value_caps, &caps.NumberInputValueCaps, preparsed); + if (status != HIDP_STATUS_SUCCESS) return status; + else for (i = 0; i < caps.NumberInputValueCaps; i++) + { + HIDP_VALUE_CAPS *caps = value_caps + i; + if (caps->UsagePage != HID_USAGE_PAGE_GENERIC) continue; + if (!caps->IsRange) check_value_caps(fdo, caps->NotRange.Usage, caps); + else for (u = caps->Range.UsageMin; u <=caps->Range.UsageMax; u++) check_value_caps(fdo, u, value_caps + i); + } + free(value_caps); + + if (!fdo->lx_caps.UsagePage) WARN("missing lx axis\n"); + if (!fdo->ly_caps.UsagePage) WARN("missing ly axis\n"); + if (!fdo->lt_caps.UsagePage) WARN("missing lt axis\n"); + if (!fdo->rx_caps.UsagePage) WARN("missing rx axis\n"); + if (!fdo->ry_caps.UsagePage) WARN("missing ry axis\n"); + if (!fdo->rt_caps.UsagePage) WARN("missing rt axis\n"); + + reports = fdo->device_desc.ReportIDs; + report_count = fdo->device_desc.ReportIDsLength; for (i = 0; i < report_count; ++i) if (!reports[i].ReportID || reports[i].InputLength) break; if (i == report_count) i = 0; /* no input report?!, just use first ID */
@@ -478,7 +532,6 @@ static NTSTATUS initialize_device(DEVICE_OBJECT *device) if (!(fdo->report_buf = malloc(fdo->report_len))) return STATUS_NO_MEMORY; fdo->report_buf[0] = reports[i].ReportID;
- HidP_FreeCollectionDescription(&device_desc); return STATUS_SUCCESS; }
@@ -556,6 +609,7 @@ static NTSTATUS WINAPI fdo_pnp(DEVICE_OBJECT *device, IRP *irp) status = IoCallDriver(fdo->bus_device, irp); IoDetachDevice(fdo->bus_device); RtlDeleteCriticalSection(&fdo->cs); + HidP_FreeCollectionDescription(&fdo->device_desc); free(fdo->report_buf); IoDeleteDevice(device); return status;
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winexinput.sys/main.c | 188 ++++++++++++++++++++++++++- dlls/winexinput.sys/pop_hid_macros.h | 83 ++++++++++++ dlls/winexinput.sys/psh_hid_macros.h | 85 ++++++++++++ dlls/xinput1_3/tests/xinput.c | 25 ---- 4 files changed, 349 insertions(+), 32 deletions(-) create mode 100644 dlls/winexinput.sys/pop_hid_macros.h create mode 100644 dlls/winexinput.sys/psh_hid_macros.h
diff --git a/dlls/winexinput.sys/main.c b/dlls/winexinput.sys/main.c index 330c9203b7d..724552cfcac 100644 --- a/dlls/winexinput.sys/main.c +++ b/dlls/winexinput.sys/main.c @@ -50,6 +50,75 @@ __ASM_STDCALL_FUNC(wrap_fastcall_func1, 8, #define call_fastcall_func1(func,a) func(a) #endif
+#include "psh_hid_macros.h" + +const BYTE xinput_report_desc[] = +{ + USAGE_PAGE(1, HID_USAGE_PAGE_GENERIC), + USAGE(1, HID_USAGE_GENERIC_GAMEPAD), + COLLECTION(1, Application), + USAGE(1, 0), + COLLECTION(1, Physical), + USAGE(1, HID_USAGE_GENERIC_X), + USAGE(1, HID_USAGE_GENERIC_Y), + LOGICAL_MAXIMUM(2, 0xffff), + PHYSICAL_MAXIMUM(2, 0xffff), + REPORT_SIZE(1, 16), + REPORT_COUNT(1, 2), + INPUT(1, Data|Var|Abs), + END_COLLECTION, + + COLLECTION(1, Physical), + USAGE(1, HID_USAGE_GENERIC_RX), + USAGE(1, HID_USAGE_GENERIC_RY), + REPORT_COUNT(1, 2), + INPUT(1, Data|Var|Abs), + END_COLLECTION, + + COLLECTION(1, Physical), + USAGE(1, HID_USAGE_GENERIC_Z), + REPORT_COUNT(1, 1), + INPUT(1, Data|Var|Abs), + END_COLLECTION, + + USAGE_PAGE(1, HID_USAGE_PAGE_BUTTON), + USAGE_MINIMUM(1, 1), + USAGE_MAXIMUM(1, 10), + LOGICAL_MAXIMUM(1, 1), + PHYSICAL_MAXIMUM(1, 1), + REPORT_COUNT(1, 10), + REPORT_SIZE(1, 1), + INPUT(1, Data|Var|Abs), + + USAGE_PAGE(1, HID_USAGE_PAGE_GENERIC), + USAGE(1, HID_USAGE_GENERIC_HATSWITCH), + LOGICAL_MINIMUM(1, 1), + LOGICAL_MAXIMUM(1, 8), + PHYSICAL_MAXIMUM(2, 0x103b), + REPORT_SIZE(1, 4), + REPORT_COUNT(4, 1), + UNIT(1, 0x0e /* none */), + INPUT(1, Data|Var|Abs|Null), + + REPORT_COUNT(1, 18), + REPORT_SIZE(1, 1), + INPUT(1, Cnst|Var|Abs), + END_COLLECTION, +}; + +#include "pop_hid_macros.h" + +struct xinput_state +{ + WORD lx_axis; + WORD ly_axis; + WORD rx_axis; + WORD ry_axis; + WORD trigger; + WORD buttons; + WORD padding; +}; + struct device { BOOL is_fdo; @@ -96,6 +165,7 @@ struct func_device char *report_buf; IRP *pending_read; BOOL pending_is_gamepad; + struct xinput_state xinput_state; };
static inline struct func_device *fdo_from_DEVICE_OBJECT(DEVICE_OBJECT *device) @@ -105,6 +175,73 @@ static inline struct func_device *fdo_from_DEVICE_OBJECT(DEVICE_OBJECT *device) else return CONTAINING_RECORD(impl, struct phys_device, base)->fdo; }
+static LONG sign_extend(ULONG value, const HIDP_VALUE_CAPS *caps) +{ + UINT sign = 1 << (caps->BitSize - 1); + if (sign <= 1 || caps->LogicalMin >= 0) return value; + return value - ((value & sign) << 1); +} + +static LONG scale_value(ULONG value, const HIDP_VALUE_CAPS *caps, LONG min, LONG max) +{ + LONG tmp = sign_extend(value, caps); + if (caps->LogicalMin > caps->LogicalMax) return 0; + if (caps->LogicalMin > tmp || caps->LogicalMax < tmp) return 0; + return min + MulDiv(tmp - caps->LogicalMin, max - min, caps->LogicalMax - caps->LogicalMin); +} + +static void translate_report_to_xinput_state(struct func_device *fdo) +{ + ULONG lx = 0, ly = 0, rx = 0, ry = 0, lt = 0, rt = 0, hat = 0; + PHIDP_PREPARSED_DATA preparsed; + USAGE usages[10]; + NTSTATUS status; + ULONG i, count; + + preparsed = fdo->device_desc.CollectionDesc->PreparsedData; + + count = ARRAY_SIZE(usages); + status = HidP_GetUsages(HidP_Input, HID_USAGE_PAGE_BUTTON, 0, usages, + &count, preparsed, fdo->report_buf, fdo->report_len); + if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsages returned %#x\n", status); + status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_HATSWITCH, + &hat, preparsed, fdo->report_buf, fdo->report_len); + if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue hat returned %#x\n", status); + status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_X, + &lx, preparsed, fdo->report_buf, fdo->report_len); + if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue x returned %#x\n", status); + status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Y, + &ly, preparsed, fdo->report_buf, fdo->report_len); + if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue y returned %#x\n", status); + status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z, + <, preparsed, fdo->report_buf, fdo->report_len); + if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue z returned %#x\n", status); + status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RX, + &rx, preparsed, fdo->report_buf, fdo->report_len); + if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue rx returned %#x\n", status); + status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RY, + &ry, preparsed, fdo->report_buf, fdo->report_len); + if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue ry returned %#x\n", status); + status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RZ, + &rt, preparsed, fdo->report_buf, fdo->report_len); + if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue rz returned %#x\n", status); + + if (hat < 1 || hat > 8) fdo->xinput_state.buttons = 0; + else fdo->xinput_state.buttons = hat << 10; + for (i = 0; i < count; i++) + { + if (usages[i] < 1 || usages[i] > 10) continue; + fdo->xinput_state.buttons |= (1 << (usages[i] - 1)); + } + fdo->xinput_state.lx_axis = scale_value(lx, &fdo->lx_caps, 0, 65535); + fdo->xinput_state.ly_axis = scale_value(ly, &fdo->ly_caps, 0, 65535); + fdo->xinput_state.rx_axis = scale_value(rx, &fdo->rx_caps, 0, 65535); + fdo->xinput_state.ry_axis = scale_value(ry, &fdo->ry_caps, 0, 65535); + rt = scale_value(rt, &fdo->rt_caps, 0, 255); + lt = scale_value(lt, &fdo->lt_caps, 0, 255); + fdo->xinput_state.trigger = 0x8000 + (lt - rt) * 128; +} + static NTSTATUS WINAPI read_completion(DEVICE_OBJECT *device, IRP *xinput_irp, void *context) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(xinput_irp); @@ -121,7 +258,9 @@ static NTSTATUS WINAPI read_completion(DEVICE_OBJECT *device, IRP *xinput_irp, v RtlEnterCriticalSection(&fdo->cs); offset = fdo->report_buf[0] ? 0 : 1; memcpy(fdo->report_buf + offset, read_buf, read_len); - memcpy(gamepad_irp->UserBuffer, read_buf, read_len); + translate_report_to_xinput_state(fdo); + memcpy(gamepad_irp->UserBuffer, &fdo->xinput_state, sizeof(fdo->xinput_state)); + gamepad_irp->IoStatus.Information = sizeof(fdo->xinput_state); RtlLeaveCriticalSection(&fdo->cs); }
@@ -178,6 +317,7 @@ static NTSTATUS try_complete_pending_read(DEVICE_OBJECT *device, IRP *irp) static NTSTATUS WINAPI gamepad_internal_ioctl(DEVICE_OBJECT *device, IRP *irp) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); + ULONG output_len = stack->Parameters.DeviceIoControl.OutputBufferLength; ULONG code = stack->Parameters.DeviceIoControl.IoControlCode; struct func_device *fdo = fdo_from_DEVICE_OBJECT(device);
@@ -185,20 +325,54 @@ static NTSTATUS WINAPI gamepad_internal_ioctl(DEVICE_OBJECT *device, IRP *irp)
switch (code) { - case IOCTL_HID_GET_INPUT_REPORT: + case IOCTL_HID_GET_DEVICE_DESCRIPTOR: { - HID_XFER_PACKET *packet = (HID_XFER_PACKET *)irp->UserBuffer; + HID_DESCRIPTOR *descriptor = (HID_DESCRIPTOR *)irp->UserBuffer;
- RtlEnterCriticalSection(&fdo->cs); - memcpy(packet->reportBuffer, fdo->report_buf, fdo->report_len); - irp->IoStatus.Information = fdo->report_len; - RtlLeaveCriticalSection(&fdo->cs); + irp->IoStatus.Information = sizeof(*descriptor); + if (output_len < sizeof(*descriptor)) + { + irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + IoCompleteRequest(irp, IO_NO_INCREMENT); + return STATUS_BUFFER_TOO_SMALL; + } + + memset(descriptor, 0, sizeof(*descriptor)); + descriptor->bLength = sizeof(*descriptor); + descriptor->bDescriptorType = HID_HID_DESCRIPTOR_TYPE; + descriptor->bcdHID = HID_REVISION; + descriptor->bCountry = 0; + descriptor->bNumDescriptors = 1; + descriptor->DescriptorList[0].bReportType = HID_REPORT_DESCRIPTOR_TYPE; + descriptor->DescriptorList[0].wReportLength = sizeof(xinput_report_desc);
irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(irp, IO_NO_INCREMENT); return STATUS_SUCCESS; }
+ case IOCTL_HID_GET_REPORT_DESCRIPTOR: + irp->IoStatus.Information = sizeof(xinput_report_desc); + if (output_len < sizeof(xinput_report_desc)) + { + irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + IoCompleteRequest(irp, IO_NO_INCREMENT); + return STATUS_BUFFER_TOO_SMALL; + } + + memcpy(irp->UserBuffer, xinput_report_desc, sizeof(xinput_report_desc)); + irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; + + case IOCTL_HID_GET_INPUT_REPORT: + case IOCTL_HID_SET_OUTPUT_REPORT: + case IOCTL_HID_GET_FEATURE: + case IOCTL_HID_SET_FEATURE: + irp->IoStatus.Status = STATUS_INVALID_PARAMETER; + IoCompleteRequest(irp, IO_NO_INCREMENT); + return STATUS_INVALID_PARAMETER; + default: IoSkipCurrentIrpStackLocation(irp); return IoCallDriver(fdo->bus_device, irp); diff --git a/dlls/winexinput.sys/pop_hid_macros.h b/dlls/winexinput.sys/pop_hid_macros.h new file mode 100644 index 00000000000..767c26e8ecb --- /dev/null +++ b/dlls/winexinput.sys/pop_hid_macros.h @@ -0,0 +1,83 @@ +/* + * HID report helper macros. + * + * 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 + */ + +#undef Data +#undef Cnst +#undef Array +#undef Var +#undef Abs +#undef Rel +#undef NoWrap +#undef Wrap +#undef NonLin +#undef Lin +#undef NoPref +#undef Pref +#undef NoNull +#undef Null +#undef NonVol +#undef Vol +#undef Bits +#undef Buff + +#undef Physical +#undef Application +#undef Logical +#undef Report +#undef NamedArray +#undef UsageSwitch +#undef UsageModifier + +#undef SHORT_ITEM_0 +#undef SHORT_ITEM_1 +#undef SHORT_ITEM_2 +#undef SHORT_ITEM_4 + +#undef LONG_ITEM + +#undef INPUT +#undef OUTPUT +#undef FEATURE +#undef COLLECTION +#undef END_COLLECTION + +#undef USAGE_PAGE +#undef LOGICAL_MINIMUM +#undef LOGICAL_MAXIMUM +#undef PHYSICAL_MINIMUM +#undef PHYSICAL_MAXIMUM +#undef UNIT_EXPONENT +#undef UNIT +#undef REPORT_SIZE +#undef REPORT_ID +#undef REPORT_COUNT +#undef PUSH +#undef POP + +#undef USAGE +#undef USAGE_MINIMUM +#undef USAGE_MAXIMUM +#undef DESIGNATOR_INDEX +#undef DESIGNATOR_MINIMUM +#undef DESIGNATOR_MAXIMUM +#undef STRING_INDEX +#undef STRING_MINIMUM +#undef STRING_MAXIMUM +#undef DELIMITER diff --git a/dlls/winexinput.sys/psh_hid_macros.h b/dlls/winexinput.sys/psh_hid_macros.h new file mode 100644 index 00000000000..4623af20598 --- /dev/null +++ b/dlls/winexinput.sys/psh_hid_macros.h @@ -0,0 +1,85 @@ +/* + * HID report helper macros. + * + * 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 <hidusage.h> + +#define Data 0 +#define Cnst 0x01 +#define Ary 0 +#define Var 0x02 +#define Abs 0 +#define Rel 0x04 +#define NoWrap 0 +#define Wrap 0x08 +#define NonLin 0 +#define Lin 0x10 +#define NoPref 0 +#define Pref 0x20 +#define NoNull 0 +#define Null 0x40 +#define NonVol 0 +#define Vol 0x80 +#define Bits 0 +#define Buff 0x100 + +#define Physical 0x00 +#define Application 0x01 +#define Logical 0x02 +#define Report 0x03 +#define NamedArray 0x04 +#define UsageSwitch 0x05 +#define UsageModifier 0x06 + +#define SHORT_ITEM_0(tag,type) (((tag)<<4)|((type)<<2)|0) +#define SHORT_ITEM_1(tag,type,data) (((tag)<<4)|((type)<<2)|1),((data)&0xff) +#define SHORT_ITEM_2(tag,type,data) (((tag)<<4)|((type)<<2)|2),((data)&0xff),(((data)>>8)&0xff) +#define SHORT_ITEM_4(tag,type,data) (((tag)<<4)|((type)<<2)|3),((data)&0xff),(((data)>>8)&0xff),(((data)>>16)&0xff),(((data)>>24)&0xff) + +#define LONG_ITEM(tag,size) SHORT_ITEM_2(0xf,0x3,((tag)<<8)|(size)) + +#define INPUT(n,data) SHORT_ITEM_##n(0x8,0,data) +#define OUTPUT(n,data) SHORT_ITEM_##n(0x9,0,data) +#define FEATURE(n,data) SHORT_ITEM_##n(0xb,0,data) +#define COLLECTION(n,data) SHORT_ITEM_##n(0xa,0,data) +#define END_COLLECTION SHORT_ITEM_0(0xc,0) + +#define USAGE_PAGE(n,data) SHORT_ITEM_##n(0x0,1,data) +#define LOGICAL_MINIMUM(n,data) SHORT_ITEM_##n(0x1,1,data) +#define LOGICAL_MAXIMUM(n,data) SHORT_ITEM_##n(0x2,1,data) +#define PHYSICAL_MINIMUM(n,data) SHORT_ITEM_##n(0x3,1,data) +#define PHYSICAL_MAXIMUM(n,data) SHORT_ITEM_##n(0x4,1,data) +#define UNIT_EXPONENT(n,data) SHORT_ITEM_##n(0x5,1,data) +#define UNIT(n,data) SHORT_ITEM_##n(0x6,1,data) +#define REPORT_SIZE(n,data) SHORT_ITEM_##n(0x7,1,data) +#define REPORT_ID(n,data) SHORT_ITEM_##n(0x8,1,data) +#define REPORT_COUNT(n,data) SHORT_ITEM_##n(0x9,1,data) +#define PUSH(n,data) SHORT_ITEM_##n(0xa,1,data) +#define POP(n,data) SHORT_ITEM_##n(0xb,1,data) + +#define USAGE(n,data) SHORT_ITEM_##n(0x0,2,data) +#define USAGE_MINIMUM(n,data) SHORT_ITEM_##n(0x1,2,data) +#define USAGE_MAXIMUM(n,data) SHORT_ITEM_##n(0x2,2,data) +#define DESIGNATOR_INDEX(n,data) SHORT_ITEM_##n(0x3,2,data) +#define DESIGNATOR_MINIMUM(n,data) SHORT_ITEM_##n(0x4,2,data) +#define DESIGNATOR_MAXIMUM(n,data) SHORT_ITEM_##n(0x5,2,data) +#define STRING_INDEX(n,data) SHORT_ITEM_##n(0x6,2,data) +#define STRING_MINIMUM(n,data) SHORT_ITEM_##n(0x7,2,data) +#define STRING_MAXIMUM(n,data) SHORT_ITEM_##n(0x8,2,data) +#define DELIMITER(n,data) SHORT_ITEM_##n(0x9,2,data) diff --git a/dlls/xinput1_3/tests/xinput.c b/dlls/xinput1_3/tests/xinput.c index 811fe045d10..60c5817c79a 100644 --- a/dlls/xinput1_3/tests/xinput.c +++ b/dlls/xinput1_3/tests/xinput.c @@ -455,21 +455,15 @@ static void check_hid_caps(DWORD index, HANDLE device, PHIDP_PREPARSED_DATA pre
check_member(*hid_caps, expect_hid_caps, "%04x", Usage); check_member(*hid_caps, expect_hid_caps, "%04x", UsagePage); - todo_wine check_member(*hid_caps, expect_hid_caps, "%d", InputReportByteLength); - todo_wine_if(xi_caps.Flags & XINPUT_CAPS_FFB_SUPPORTED) check_member(*hid_caps, expect_hid_caps, "%d", OutputReportByteLength); check_member(*hid_caps, expect_hid_caps, "%d", FeatureReportByteLength); check_member(*hid_caps, expect_hid_caps, "%d", NumberLinkCollectionNodes); check_member(*hid_caps, expect_hid_caps, "%d", NumberInputButtonCaps); - todo_wine check_member(*hid_caps, expect_hid_caps, "%d", NumberInputValueCaps); - todo_wine check_member(*hid_caps, expect_hid_caps, "%d", NumberInputDataIndices); check_member(*hid_caps, expect_hid_caps, "%d", NumberOutputButtonCaps); - todo_wine_if(xi_caps.Flags & XINPUT_CAPS_FFB_SUPPORTED) check_member(*hid_caps, expect_hid_caps, "%d", NumberOutputValueCaps); - todo_wine_if(xi_caps.Flags & XINPUT_CAPS_FFB_SUPPORTED) check_member(*hid_caps, expect_hid_caps, "%d", NumberOutputDataIndices); check_member(*hid_caps, expect_hid_caps, "%d", NumberFeatureButtonCaps); check_member(*hid_caps, expect_hid_caps, "%d", NumberFeatureValueCaps); @@ -522,11 +516,8 @@ static void check_hid_caps(DWORD index, HANDLE device, PHIDP_PREPARSED_DATA pre else if (button_caps[i].IsRange && expect_button_caps[i].IsRange) { check_member(button_caps[i], expect_button_caps[i], "%04x", Range.UsageMin); - todo_wine check_member(button_caps[i], expect_button_caps[i], "%04x", Range.UsageMax); - todo_wine check_member(button_caps[i], expect_button_caps[i], "%d", Range.DataIndexMin); - todo_wine check_member(button_caps[i], expect_button_caps[i], "%d", Range.DataIndexMax); }
@@ -551,7 +542,6 @@ static void check_hid_caps(DWORD index, HANDLE device, PHIDP_PREPARSED_DATA pre count = hid_caps->NumberInputValueCaps; status = HidP_GetValueCaps(HidP_Input, value_caps, &count, preparsed); ok(status == HIDP_STATUS_SUCCESS, "HidP_GetValueCaps returned %#x\n", status); - todo_wine ok(count == ARRAY_SIZE(expect_value_caps), "got %d value caps\n", count);
for (i = 0; i < min(count, ARRAY_SIZE(expect_value_caps)); ++i) @@ -560,11 +550,8 @@ static void check_hid_caps(DWORD index, HANDLE device, PHIDP_PREPARSED_DATA pre check_member(value_caps[i], expect_value_caps[i], "%04x", UsagePage); check_member(value_caps[i], expect_value_caps[i], "%d", ReportID); check_member(value_caps[i], expect_value_caps[i], "%d", IsAlias); - todo_wine_if(i == 5) check_member(value_caps[i], expect_value_caps[i], "%d", BitField); - todo_wine_if(i == 5) check_member(value_caps[i], expect_value_caps[i], "%d", LinkCollection); - todo_wine_if(i == 5) check_member(value_caps[i], expect_value_caps[i], "%d", LinkUsage); check_member(value_caps[i], expect_value_caps[i], "%d", LinkUsagePage); check_member(value_caps[i], expect_value_caps[i], "%d", IsRange); @@ -572,27 +559,19 @@ static void check_hid_caps(DWORD index, HANDLE device, PHIDP_PREPARSED_DATA pre check_member(value_caps[i], expect_value_caps[i], "%d", IsDesignatorRange); check_member(value_caps[i], expect_value_caps[i], "%d", IsAbsolute);
- todo_wine_if(i == 5) check_member(value_caps[i], expect_value_caps[i], "%d", HasNull); - todo_wine_if(i == 5) check_member(value_caps[i], expect_value_caps[i], "%d", BitSize); check_member(value_caps[i], expect_value_caps[i], "%d", ReportCount); check_member(value_caps[i], expect_value_caps[i], "%d", UnitsExp); - todo_wine_if(i == 5) check_member(value_caps[i], expect_value_caps[i], "%d", Units); - todo_wine_if(i == 5) check_member(value_caps[i], expect_value_caps[i], "%d", LogicalMin); - todo_wine check_member(value_caps[i], expect_value_caps[i], "%d", LogicalMax); check_member(value_caps[i], expect_value_caps[i], "%d", PhysicalMin); - todo_wine check_member(value_caps[i], expect_value_caps[i], "%d", PhysicalMax);
if (!value_caps[i].IsRange && !expect_value_caps[i].IsRange) { - todo_wine_if(i >= 4) check_member(value_caps[i], expect_value_caps[i], "%04x", NotRange.Usage); - todo_wine_if(i == 5) check_member(value_caps[i], expect_value_caps[i], "%d", NotRange.DataIndex); } else if (value_caps[i].IsRange && expect_value_caps[i].IsRange) @@ -627,9 +606,7 @@ static void check_hid_caps(DWORD index, HANDLE device, PHIDP_PREPARSED_DATA pre SetLastError(0xdeadbeef); memset(buffer, 0, sizeof(buffer)); ret = HidD_GetInputReport(device, buffer, hid_caps->InputReportByteLength); - todo_wine ok(!ret, "HidD_GetInputReport succeeded\n"); - todo_wine ok(GetLastError() == ERROR_INVALID_PARAMETER, "HidD_GetInputReport returned error %u\n", GetLastError());
if (!winetest_interactive) skip("skipping interactive tests\n"); @@ -737,12 +714,10 @@ static void check_hid_caps(DWORD index, HANDLE device, PHIDP_PREPARSED_DATA pre value = 0; status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z, &value, preparsed, buffer, hid_caps->InputReportByteLength); ok(status == HIDP_STATUS_SUCCESS, "HidP_GetUsageValue returned %#x\n", status); - todo_wine ok(value == 32768 + (state.Gamepad.bLeftTrigger - state.Gamepad.bRightTrigger) * 128, "got Z value %d (RT %d, LT %d)\n", value, state.Gamepad.bRightTrigger, state.Gamepad.bLeftTrigger); value = 0; status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RZ, &value, preparsed, buffer, hid_caps->InputReportByteLength); - todo_wine ok(status == HIDP_STATUS_USAGE_NOT_FOUND, "HidP_GetUsageValue returned %#x\n", status); } while (ret && (state.Gamepad.bRightTrigger != 255 || state.Gamepad.bLeftTrigger != 255)); }
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=97342
Your paranoid android.
=== debiant2 (32 bit Hindi:India report) ===
xinput1_3: xinput: Timeout
On 9/6/21 8:49 AM, Marvin wrote:
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=97342
Your paranoid android.
=== debiant2 (32 bit Hindi:India report) ===
xinput1_3: xinput: Timeout
According to [1] it already happens sometimes. Doesn't seem to happen often but I'll have a look. It's probably unrelated to this patch though.
[1] https://test.winehq.org/data/patterns.html#xinput1_3:xinput
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winexinput.sys/winexinput.inf | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/dlls/winexinput.sys/winexinput.inf b/dlls/winexinput.sys/winexinput.inf index 2f388c190ca..d19548019e1 100644 --- a/dlls/winexinput.sys/winexinput.inf +++ b/dlls/winexinput.sys/winexinput.inf @@ -8,6 +8,19 @@ Wine=mfg_section
[mfg_section] Wine XInput compatible device=device_section,WINEBUS\WINE_COMP_XINPUT +Xbox Controller=device_section,WINEBUS\VID_045E&PID_0202 +Xbox Controller S=device_section,WINEBUS\VID_045E&PID_0285 +Xbox Controller S=device_section,WINEBUS\VID_045E&PID_0289 +Xbox360 Controller=device_section,WINEBUS\VID_045E&PID_028E +Xbox360 Wireless Controller=device_section,WINEBUS\VID_045E&PID_028F +Xbox One Controller=device_section,WINEBUS\VID_045E&PID_02D1 +Xbox One Controller (Covert Forces/Firmware 2015)=device_section,WINEBUS\VID_045E&PID_02DD +Xbox One X Controller=device_section,WINEBUS\VID_045E&PID_02E0 +Xbox One Elite Controller=device_section,WINEBUS\VID_045E&PID_02E3 +Wireless XBox Controller Dongle=device_section,WINEBUS\VID_045E&PID_02E6 +Xbox One S Controller=device_section,WINEBUS\VID_045E&PID_02EA +Xbox One S Controller (Firmware 2017)=device_section,WINEBUS\VID_045E&PID_02FD +Xbox 360 Wireless Adapter=device_section,WINEBUS\VID_045E&PID_0719
[device_section.Services] AddService = xinput,0x2,svc_section
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winebus.sys/main.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c index 6093a0b79eb..785fcd46cb5 100644 --- a/dlls/winebus.sys/main.c +++ b/dlls/winebus.sys/main.c @@ -289,13 +289,26 @@ static WCHAR *get_device_id(DEVICE_OBJECT *device)
static WCHAR *get_hardware_ids(DEVICE_OBJECT *device) { + static const WCHAR input_formatW[] = {'&','M','I','_','%','0','2','u',0}; + static const WCHAR winebus_formatW[] = + { + 'W','I','N','E','B','U','S','\','V','I','D','_','%','0','4','X', + '&','P','I','D','_','%','0','4','X',0 + }; struct device_extension *ext = (struct device_extension *)device->DeviceExtension; + DWORD pos = 0, len = 0, input_len = 0, winebus_len = 25; WCHAR *dst;
- if ((dst = ExAllocatePool(PagedPool, (strlenW(ext->desc.busid) + 2) * sizeof(WCHAR)))) + if (ext->desc.input != -1) input_len = 14; + + len += winebus_len + input_len + 1; + + if ((dst = ExAllocatePool(PagedPool, (len + 1) * sizeof(WCHAR)))) { - strcpyW(dst, ext->desc.busid); - dst[strlenW(dst) + 1] = 0; + pos += snprintfW(dst + pos, len - pos, winebus_formatW, ext->desc.vid, ext->desc.pid); + if (input_len) pos += snprintfW(dst + pos, len - pos, input_formatW, ext->desc.input); + pos += 1; + dst[pos] = 0; }
return dst;
Depending on the internal bus, we may find the same kind of device with different input number. For instance SDL bus has no way of figuring an input number, and it will not have any, but UDEV will have one for every device.
This will help matching devices with their VID_XXXX&PID_XXXX ids, by matching both the &MI_XX suffixed (if any) and non-suffixed IDs.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winebus.sys/main.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c index 785fcd46cb5..d6a16a9a0b2 100644 --- a/dlls/winebus.sys/main.c +++ b/dlls/winebus.sys/main.c @@ -301,12 +301,18 @@ static WCHAR *get_hardware_ids(DEVICE_OBJECT *device)
if (ext->desc.input != -1) input_len = 14;
- len += winebus_len + input_len + 1; + if (input_len) len += winebus_len + input_len + 1; + len += winebus_len + 1;
if ((dst = ExAllocatePool(PagedPool, (len + 1) * sizeof(WCHAR)))) { + if (input_len) + { + pos += snprintfW(dst + pos, len - pos, winebus_formatW, ext->desc.vid, ext->desc.pid); + pos += snprintfW(dst + pos, len - pos, input_formatW, ext->desc.input); + pos += 1; + } pos += snprintfW(dst + pos, len - pos, winebus_formatW, ext->desc.vid, ext->desc.pid); - if (input_len) pos += snprintfW(dst + pos, len - pos, input_formatW, ext->desc.input); pos += 1; dst[pos] = 0; }
On 9/6/21 1:40 AM, Rémi Bernon wrote:
Depending on the internal bus, we may find the same kind of device with different input number. For instance SDL bus has no way of figuring an input number, and it will not have any, but UDEV will have one for every device.
This will help matching devices with their VID_XXXX&PID_XXXX ids, by matching both the &MI_XX suffixed (if any) and non-suffixed IDs.
Native doesn't do this for USB devices, which bothers me. Can you give a specific example of where we'd need to do this? Can we just match both variations in the .inf file instead?
On 9/6/21 7:03 PM, Zebediah Figura wrote:
On 9/6/21 1:40 AM, Rémi Bernon wrote:
Depending on the internal bus, we may find the same kind of device with different input number. For instance SDL bus has no way of figuring an input number, and it will not have any, but UDEV will have one for every device.
This will help matching devices with their VID_XXXX&PID_XXXX ids, by matching both the &MI_XX suffixed (if any) and non-suffixed IDs.
Native doesn't do this for USB devices, which bothers me. Can you give a specific example of where we'd need to do this? Can we just match both variations in the .inf file instead?
The example is with that XBox controller will have a &MI_00 suffix when using UDEV bus (with input subsystem), and not when using SDL bus.
I looked closer to the UDEV input number parsing and there doesn't seem to be a good way of telling if the device really has multiple interfaces or not. Also the xpad linux driver seems to actually have multiple interfaces.
We could just make sure to add an input number on every device on the SDL bus, or not on the UDEV bus with input subsystem, or duplicate the lines in the inf. All solutions seemed equally bad.
I don't think the solution here matters very much either, as it's all under the WINEBUS\ namespace, which is separate from USB\ prefix and unlikely to match any actual driver. The more general hardware IDs are also added last, so they would have a lower ranking in the driver matching score.
On 9/6/21 1:03 PM, Rémi Bernon wrote:
On 9/6/21 7:03 PM, Zebediah Figura wrote:
On 9/6/21 1:40 AM, Rémi Bernon wrote:
Depending on the internal bus, we may find the same kind of device with different input number. For instance SDL bus has no way of figuring an input number, and it will not have any, but UDEV will have one for every device.
This will help matching devices with their VID_XXXX&PID_XXXX ids, by matching both the &MI_XX suffixed (if any) and non-suffixed IDs.
Native doesn't do this for USB devices, which bothers me. Can you give a specific example of where we'd need to do this? Can we just match both variations in the .inf file instead?
The example is with that XBox controller will have a &MI_00 suffix when using UDEV bus (with input subsystem), and not when using SDL bus.
I looked closer to the UDEV input number parsing and there doesn't seem to be a good way of telling if the device really has multiple interfaces or not. Also the xpad linux driver seems to actually have multiple interfaces.
We could just make sure to add an input number on every device on the SDL bus, or not on the UDEV bus with input subsystem, or duplicate the lines in the inf. All solutions seemed equally bad.
I don't think the solution here matters very much either, as it's all under the WINEBUS\ namespace, which is separate from USB\ prefix and unlikely to match any actual driver. The more general hardware IDs are also added last, so they would have a lower ranking in the driver matching score.
Yeah, it's probably not worth worrying about very much...
Native XBox controllers should now be listed in winexinput.inf, and the axis or button checks should be enough to set the compatible id anyway.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winebus.sys/bus.h | 2 -- dlls/winebus.sys/bus_iohid.c | 59 +++++++++++++++++------------------- dlls/winebus.sys/bus_udev.c | 13 ++------ dlls/winebus.sys/main.c | 13 -------- 4 files changed, 30 insertions(+), 57 deletions(-)
diff --git a/dlls/winebus.sys/bus.h b/dlls/winebus.sys/bus.h index 15238538328..6affb47c9ed 100644 --- a/dlls/winebus.sys/bus.h +++ b/dlls/winebus.sys/bus.h @@ -35,7 +35,5 @@ DEVICE_OBJECT *bus_find_hid_device(const WCHAR *bus_id, void *platform_dev) DECL void process_hid_report(DEVICE_OBJECT *device, BYTE *report, DWORD length) DECLSPEC_HIDDEN;
/* General Bus Functions */ -BOOL is_xbox_gamepad(WORD vid, WORD pid) DECLSPEC_HIDDEN; - extern HANDLE driver_key DECLSPEC_HIDDEN; extern DEVICE_OBJECT *bus_pdo DECLSPEC_HIDDEN; diff --git a/dlls/winebus.sys/bus_iohid.c b/dlls/winebus.sys/bus_iohid.c index 9a2a6f1ec8b..1e64bb52c17 100644 --- a/dlls/winebus.sys/bus_iohid.c +++ b/dlls/winebus.sys/bus_iohid.c @@ -317,46 +317,41 @@ static void handle_DeviceMatchingCallback(void *context, IOReturn result, void * if (IOHIDDeviceConformsTo(IOHIDDevice, kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad) || IOHIDDeviceConformsTo(IOHIDDevice, kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick)) { - if (is_xbox_gamepad(desc.vid, desc.pid)) - desc.is_gamepad = TRUE; - else - { - int axes=0, buttons=0; - CFArrayRef element_array = IOHIDDeviceCopyMatchingElements( - IOHIDDevice, NULL, kIOHIDOptionsTypeNone); - - if (element_array) { - CFIndex index; - CFIndex count = CFArrayGetCount(element_array); - for (index = 0; index < count; index++) + int axes=0, buttons=0; + CFArrayRef element_array = IOHIDDeviceCopyMatchingElements( + IOHIDDevice, NULL, kIOHIDOptionsTypeNone); + + if (element_array) { + CFIndex index; + CFIndex count = CFArrayGetCount(element_array); + for (index = 0; index < count; index++) + { + IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(element_array, index); + if (element) { - IOHIDElementRef element = (IOHIDElementRef)CFArrayGetValueAtIndex(element_array, index); - if (element) + int type = IOHIDElementGetType(element); + if (type == kIOHIDElementTypeInput_Button) buttons++; + if (type == kIOHIDElementTypeInput_Axis) axes++; + if (type == kIOHIDElementTypeInput_Misc) { - int type = IOHIDElementGetType(element); - if (type == kIOHIDElementTypeInput_Button) buttons++; - if (type == kIOHIDElementTypeInput_Axis) axes++; - if (type == kIOHIDElementTypeInput_Misc) + uint32_t usage = IOHIDElementGetUsage(element); + switch (usage) { - uint32_t usage = IOHIDElementGetUsage(element); - switch (usage) - { - case kHIDUsage_GD_X: - case kHIDUsage_GD_Y: - case kHIDUsage_GD_Z: - case kHIDUsage_GD_Rx: - case kHIDUsage_GD_Ry: - case kHIDUsage_GD_Rz: - case kHIDUsage_GD_Slider: - axes ++; - } + case kHIDUsage_GD_X: + case kHIDUsage_GD_Y: + case kHIDUsage_GD_Z: + case kHIDUsage_GD_Rx: + case kHIDUsage_GD_Ry: + case kHIDUsage_GD_Rz: + case kHIDUsage_GD_Slider: + axes ++; } } } - CFRelease(element_array); } - desc.is_gamepad = (axes == 6 && buttons >= 14); + CFRelease(element_array); } + desc.is_gamepad = (axes == 6 && buttons >= 14); }
TRACE("dev %p, desc %s.\n", IOHIDDevice, debugstr_device_desc(&desc)); diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c index cca1bf5e168..f74cab0e505 100644 --- a/dlls/winebus.sys/bus_udev.c +++ b/dlls/winebus.sys/bus_udev.c @@ -1139,6 +1139,7 @@ static void udev_add_device(struct udev_device *dev) else if (!strcmp(subsystem, "input")) { struct input_id device_id = {0}; + int axes = 0, buttons = 0; char device_uid[255];
desc.busid = lnxev_busidW; @@ -1155,23 +1156,15 @@ static void udev_add_device(struct udev_device *dev) device_uid[0] = 0; if (ioctl(fd, EVIOCGUNIQ(254), device_uid) >= 0 && device_uid[0]) MultiByteToWideChar(CP_UNIXCP, 0, device_uid, -1, desc.serial, ARRAY_SIZE(desc.serial)); - } -#endif
- if (!desc.serial[0]) lstrcpyW(desc.serial, base_serial); - - if (is_xbox_gamepad(desc.vid, desc.pid)) - desc.is_gamepad = TRUE; -#ifdef HAS_PROPER_INPUT_HEADER - else - { - int axes=0, buttons=0; axes = count_abs_axis(fd); buttons = count_buttons(fd, NULL); desc.is_gamepad = (axes == 6 && buttons >= 14); } #endif
+ if (!desc.serial[0]) lstrcpyW(desc.serial, base_serial); + TRACE("dev %p, node %s, desc %s.\n", dev, debugstr_a(devnode), debugstr_device_desc(&desc));
if (strcmp(subsystem, "hidraw") == 0) diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c index d6a16a9a0b2..aab4eb27a54 100644 --- a/dlls/winebus.sys/main.c +++ b/dlls/winebus.sys/main.c @@ -1090,19 +1090,6 @@ void process_hid_report(DEVICE_OBJECT *device, BYTE *report, DWORD length) LeaveCriticalSection(&ext->cs); }
-BOOL is_xbox_gamepad(WORD vid, WORD pid) -{ - int i; - - if (vid != VID_MICROSOFT) - return FALSE; - - for (i = 0; i < ARRAY_SIZE(XBOX_CONTROLLERS); i++) - if (pid == XBOX_CONTROLLERS[i].pid) return TRUE; - - return FALSE; -} - static NTSTATUS WINAPI driver_add_device(DRIVER_OBJECT *driver, DEVICE_OBJECT *pdo) { NTSTATUS ret;
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winexinput.sys/main.c | 53 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+)
diff --git a/dlls/winexinput.sys/main.c b/dlls/winexinput.sys/main.c index 724552cfcac..c61839b4a88 100644 --- a/dlls/winexinput.sys/main.c +++ b/dlls/winexinput.sys/main.c @@ -32,6 +32,7 @@ #include "ddk/wdm.h" #include "ddk/hidport.h" #include "ddk/hidpddi.h" +#include "ddk/hidtypes.h"
#include "wine/asm.h" #include "wine/debug.h" @@ -314,17 +315,69 @@ static NTSTATUS try_complete_pending_read(DEVICE_OBJECT *device, IRP *irp) return IoCallDriver(fdo->bus_device, xinput_irp); }
+struct device_strings +{ + const WCHAR *id; + const WCHAR *product; +}; + +static const struct device_strings device_strings[] = +{ + { .id = L"VID_045E&PID_028E&IG_00", .product = L"Controller (XBOX 360 For Windows)" }, + { .id = L"VID_045E&PID_028F&IG_00", .product = L"Controller (XBOX 360 For Windows)" }, + { .id = L"VID_045E&PID_02D1&IG_00", .product = L"Controller (XBOX One For Windows)" }, + { .id = L"VID_045E&PID_02DD&IG_00", .product = L"Controller (XBOX One For Windows)" }, + { .id = L"VID_045E&PID_02E3&IG_00", .product = L"Controller (XBOX One For Windows)" }, + { .id = L"VID_045E&PID_02EA&IG_00", .product = L"Controller (XBOX One For Windows)" }, + { .id = L"VID_045E&PID_02FD&IG_00", .product = L"Controller (XBOX One For Windows)" }, + { .id = L"VID_045E&PID_0719&IG_00", .product = L"Controller (XBOX 360 For Windows)" }, +}; + static NTSTATUS WINAPI gamepad_internal_ioctl(DEVICE_OBJECT *device, IRP *irp) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); ULONG output_len = stack->Parameters.DeviceIoControl.OutputBufferLength; ULONG code = stack->Parameters.DeviceIoControl.IoControlCode; struct func_device *fdo = fdo_from_DEVICE_OBJECT(device); + struct device *impl = impl_from_DEVICE_OBJECT(device); + const WCHAR *str = NULL, *match_id; + DWORD i;
TRACE("device %p, irp %p, code %#x, bus_device %p.\n", device, irp, code, fdo->bus_device);
switch (code) { + case IOCTL_HID_GET_STRING: + switch ((ULONG_PTR)stack->Parameters.DeviceIoControl.Type3InputBuffer) + { + case HID_STRING_ID_IPRODUCT: + match_id = wcsrchr(impl->device_id, '\') + 1; + for (i = 0; i < ARRAY_SIZE(device_strings); ++i) + if (!wcsicmp(device_strings[i].id, match_id)) + break; + if (i < ARRAY_SIZE(device_strings)) str = device_strings[i].product; + break; + } + + if (!str) + { + IoSkipCurrentIrpStackLocation(irp); + return IoCallDriver(fdo->bus_device, irp); + } + + irp->IoStatus.Information = (wcslen(str) + 1) * sizeof(WCHAR); + if (output_len < irp->IoStatus.Information) + { + irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; + IoCompleteRequest(irp, IO_NO_INCREMENT); + return STATUS_BUFFER_TOO_SMALL; + } + + wcscpy(irp->UserBuffer, str); + irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(irp, IO_NO_INCREMENT); + return STATUS_SUCCESS; + case IOCTL_HID_GET_DEVICE_DESCRIPTOR: { HID_DESCRIPTOR *descriptor = (HID_DESCRIPTOR *)irp->UserBuffer;
On 9/6/21 1:40 AM, Rémi Bernon wrote:
Signed-off-by: Rémi Bernon rbernon@codeweavers.com
dlls/winexinput.sys/main.c | 53 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+)
diff --git a/dlls/winexinput.sys/main.c b/dlls/winexinput.sys/main.c index 724552cfcac..c61839b4a88 100644 --- a/dlls/winexinput.sys/main.c +++ b/dlls/winexinput.sys/main.c @@ -32,6 +32,7 @@ #include "ddk/wdm.h" #include "ddk/hidport.h" #include "ddk/hidpddi.h" +#include "ddk/hidtypes.h"
#include "wine/asm.h" #include "wine/debug.h" @@ -314,17 +315,69 @@ static NTSTATUS try_complete_pending_read(DEVICE_OBJECT *device, IRP *irp) return IoCallDriver(fdo->bus_device, xinput_irp); }
+struct device_strings +{
- const WCHAR *id;
- const WCHAR *product;
+};
+static const struct device_strings device_strings[] = +{
- { .id = L"VID_045E&PID_028E&IG_00", .product = L"Controller (XBOX 360 For Windows)" },
- { .id = L"VID_045E&PID_028F&IG_00", .product = L"Controller (XBOX 360 For Windows)" },
- { .id = L"VID_045E&PID_02D1&IG_00", .product = L"Controller (XBOX One For Windows)" },
- { .id = L"VID_045E&PID_02DD&IG_00", .product = L"Controller (XBOX One For Windows)" },
- { .id = L"VID_045E&PID_02E3&IG_00", .product = L"Controller (XBOX One For Windows)" },
- { .id = L"VID_045E&PID_02EA&IG_00", .product = L"Controller (XBOX One For Windows)" },
- { .id = L"VID_045E&PID_02FD&IG_00", .product = L"Controller (XBOX One For Windows)" },
- { .id = L"VID_045E&PID_0719&IG_00", .product = L"Controller (XBOX 360 For Windows)" },
+};
static NTSTATUS WINAPI gamepad_internal_ioctl(DEVICE_OBJECT *device, IRP *irp) { IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp); ULONG output_len = stack->Parameters.DeviceIoControl.OutputBufferLength; ULONG code = stack->Parameters.DeviceIoControl.IoControlCode; struct func_device *fdo = fdo_from_DEVICE_OBJECT(device);
struct device *impl = impl_from_DEVICE_OBJECT(device);
const WCHAR *str = NULL, *match_id;
DWORD i;
TRACE("device %p, irp %p, code %#x, bus_device %p.\n", device, irp, code, fdo->bus_device);
switch (code) {
case IOCTL_HID_GET_STRING:
switch ((ULONG_PTR)stack->Parameters.DeviceIoControl.Type3InputBuffer)
{
case HID_STRING_ID_IPRODUCT:
match_id = wcsrchr(impl->device_id, '\\') + 1;
for (i = 0; i < ARRAY_SIZE(device_strings); ++i)
if (!wcsicmp(device_strings[i].id, match_id))
break;
if (i < ARRAY_SIZE(device_strings)) str = device_strings[i].product;
break;
Just a suggestion, really, but this might be cleaner as a helper function?
}
if (!str)
{
IoSkipCurrentIrpStackLocation(irp);
return IoCallDriver(fdo->bus_device, irp);
}
irp->IoStatus.Information = (wcslen(str) + 1) * sizeof(WCHAR);
if (output_len < irp->IoStatus.Information)
{
irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
IoCompleteRequest(irp, IO_NO_INCREMENT);
return STATUS_BUFFER_TOO_SMALL;
}
wcscpy(irp->UserBuffer, str);
irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
case IOCTL_HID_GET_DEVICE_DESCRIPTOR: { HID_DESCRIPTOR *descriptor = (HID_DESCRIPTOR *)irp->UserBuffer;
On 9/6/21 7:06 PM, Zebediah Figura wrote:
On 9/6/21 1:40 AM, Rémi Bernon wrote:
+ case IOCTL_HID_GET_STRING: + switch ((ULONG_PTR)stack->Parameters.DeviceIoControl.Type3InputBuffer) + { + case HID_STRING_ID_IPRODUCT: + match_id = wcsrchr(impl->device_id, '\') + 1; + for (i = 0; i < ARRAY_SIZE(device_strings); ++i) + if (!wcsicmp(device_strings[i].id, match_id)) + break; + if (i < ARRAY_SIZE(device_strings)) str = device_strings[i].product; + break;
Just a suggestion, really, but this might be cleaner as a helper function?
Well, I don't plan on adding anything more there, so maybe it could be split later if we need to.
Also AFAICS Windows gets the device product string from the driver inf file, and never calls IOCTL_HID_GET_STRING, so this could just go away when we have a similar implementation.
On 9/6/21 1:34 PM, Rémi Bernon wrote:
On 9/6/21 7:06 PM, Zebediah Figura wrote:
On 9/6/21 1:40 AM, Rémi Bernon wrote:
+ case IOCTL_HID_GET_STRING: + switch ((ULONG_PTR)stack->Parameters.DeviceIoControl.Type3InputBuffer) + { + case HID_STRING_ID_IPRODUCT: + match_id = wcsrchr(impl->device_id, '\') + 1; + for (i = 0; i < ARRAY_SIZE(device_strings); ++i) + if (!wcsicmp(device_strings[i].id, match_id)) + break; + if (i < ARRAY_SIZE(device_strings)) str = device_strings[i].product; + break;
Just a suggestion, really, but this might be cleaner as a helper function?
Well, I don't plan on adding anything more there, so maybe it could be split later if we need to.
I don't necessarily mean anything needs to be added, it's just that I really don't like the "for followed by if" pattern that this exemplifies.
Also AFAICS Windows gets the device product string from the driver inf file, and never calls IOCTL_HID_GET_STRING, so this could just go away when we have a similar implementation.
If I had to guess, it's probably retrieving it from the DeviceDesc registry key.
On 9/6/21 10:52 PM, Zebediah Figura wrote:
On 9/6/21 1:34 PM, Rémi Bernon wrote:
On 9/6/21 7:06 PM, Zebediah Figura wrote:
On 9/6/21 1:40 AM, Rémi Bernon wrote:
+ case IOCTL_HID_GET_STRING: + switch ((ULONG_PTR)stack->Parameters.DeviceIoControl.Type3InputBuffer) + { + case HID_STRING_ID_IPRODUCT: + match_id = wcsrchr(impl->device_id, '\') + 1; + for (i = 0; i < ARRAY_SIZE(device_strings); ++i) + if (!wcsicmp(device_strings[i].id, match_id)) + break; + if (i < ARRAY_SIZE(device_strings)) str = device_strings[i].product; + break;
Just a suggestion, really, but this might be cleaner as a helper function?
Well, I don't plan on adding anything more there, so maybe it could be split later if we need to.
I don't necessarily mean anything needs to be added, it's just that I really don't like the "for followed by if" pattern that this exemplifies.
Well, it's probably a matter of taste. I find this pattern
for (it; it != end; ++it) { if (matching) break } if (it != end) { do something with it }
cleaner and more readable than the alternative that's often used:
for (it; it != end; ++it) { if (matching) { do something with it break } }
especially when "do something with it" is long: the break may be easily missed.
Of course, a "find_xxx" helper could make it even clearer, but I think the former pattern can be read as an implicit "find xxx" pattern, especially for small loops.
Cheers
On 9/6/21 4:08 PM, Rémi Bernon wrote:
On 9/6/21 10:52 PM, Zebediah Figura wrote:
On 9/6/21 1:34 PM, Rémi Bernon wrote:
On 9/6/21 7:06 PM, Zebediah Figura wrote:
On 9/6/21 1:40 AM, Rémi Bernon wrote:
+ case IOCTL_HID_GET_STRING: + switch ((ULONG_PTR)stack->Parameters.DeviceIoControl.Type3InputBuffer) + { + case HID_STRING_ID_IPRODUCT: + match_id = wcsrchr(impl->device_id, '\') + 1; + for (i = 0; i < ARRAY_SIZE(device_strings); ++i) + if (!wcsicmp(device_strings[i].id, match_id)) + break; + if (i < ARRAY_SIZE(device_strings)) str = device_strings[i].product; + break;
Just a suggestion, really, but this might be cleaner as a helper function?
Well, I don't plan on adding anything more there, so maybe it could be split later if we need to.
I don't necessarily mean anything needs to be added, it's just that I really don't like the "for followed by if" pattern that this exemplifies.
Well, it's probably a matter of taste. I find this pattern
for (it; it != end; ++it) { if (matching) break } if (it != end) { do something with it }
cleaner and more readable than the alternative that's often used:
for (it; it != end; ++it) { if (matching) { do something with it break } }
especially when "do something with it" is long: the break may be easily missed.
Of course, a "find_xxx" helper could make it even clearer, but I think the former pattern can be read as an implicit "find xxx" pattern, especially for small loops.
Right, the helper was what I was advocating for.
It's probably not worth arguing about, though.
This should now be handled by xusb22.sys.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/winebus.sys/main.c | 79 +---------------------------------------- 1 file changed, 1 insertion(+), 78 deletions(-)
diff --git a/dlls/winebus.sys/main.c b/dlls/winebus.sys/main.c index aab4eb27a54..304ede1e0d9 100644 --- a/dlls/winebus.sys/main.c +++ b/dlls/winebus.sys/main.c @@ -66,32 +66,6 @@ struct product_desc const WCHAR* serialnumber; };
-#define VID_MICROSOFT 0x045e - -static const WCHAR xbox360_product_string[] = { - 'C','o','n','t','r','o','l','l','e','r',' ','(','X','B','O','X',' ','3','6','0',' ','F','o','r',' ','W','i','n','d','o','w','s',')',0 -}; - -static const WCHAR xboxone_product_string[] = { - 'C','o','n','t','r','o','l','l','e','r',' ','(','X','B','O','X',' ','O','n','e',' ','F','o','r',' ','W','i','n','d','o','w','s',')',0 -}; - -static const struct product_desc XBOX_CONTROLLERS[] = { - {VID_MICROSOFT, 0x0202, NULL, NULL, NULL}, /* Xbox Controller */ - {VID_MICROSOFT, 0x0285, NULL, NULL, NULL}, /* Xbox Controller S */ - {VID_MICROSOFT, 0x0289, NULL, NULL, NULL}, /* Xbox Controller S */ - {VID_MICROSOFT, 0x028e, NULL, xbox360_product_string, NULL}, /* Xbox360 Controller */ - {VID_MICROSOFT, 0x028f, NULL, xbox360_product_string, NULL}, /* Xbox360 Wireless Controller */ - {VID_MICROSOFT, 0x02d1, NULL, xboxone_product_string, NULL}, /* Xbox One Controller */ - {VID_MICROSOFT, 0x02dd, NULL, xboxone_product_string, NULL}, /* Xbox One Controller (Covert Forces/Firmware 2015) */ - {VID_MICROSOFT, 0x02e0, NULL, NULL, NULL}, /* Xbox One X Controller */ - {VID_MICROSOFT, 0x02e3, NULL, xboxone_product_string, NULL}, /* Xbox One Elite Controller */ - {VID_MICROSOFT, 0x02e6, NULL, NULL, NULL}, /* Wireless XBox Controller Dongle */ - {VID_MICROSOFT, 0x02ea, NULL, xboxone_product_string, NULL}, /* Xbox One S Controller */ - {VID_MICROSOFT, 0x02fd, NULL, xboxone_product_string, NULL}, /* Xbox One S Controller (Firmware 2017) */ - {VID_MICROSOFT, 0x0719, NULL, xbox360_product_string, NULL}, /* Xbox 360 Wireless Adapter */ -}; - static DRIVER_OBJECT *driver_obj;
static DEVICE_OBJECT *mouse_obj; @@ -834,55 +808,6 @@ static NTSTATUS deliver_last_report(struct device_extension *ext, DWORD buffer_l } }
-static NTSTATUS hid_get_native_string(DEVICE_OBJECT *device, DWORD index, WCHAR *buffer, DWORD length) -{ - struct device_extension *ext = (struct device_extension *)device->DeviceExtension; - const struct product_desc *vendor_products; - unsigned int i, vendor_products_size = 0; - - if (ext->desc.vid == VID_MICROSOFT) - { - vendor_products = XBOX_CONTROLLERS; - vendor_products_size = ARRAY_SIZE(XBOX_CONTROLLERS); - } - - for (i = 0; i < vendor_products_size; i++) - { - if (ext->desc.pid == vendor_products[i].pid) - break; - } - - if (i >= vendor_products_size) - return STATUS_UNSUCCESSFUL; - - switch (index) - { - case HID_STRING_ID_IPRODUCT: - if (vendor_products[i].product) - { - strcpyW(buffer, vendor_products[i].product); - return STATUS_SUCCESS; - } - break; - case HID_STRING_ID_IMANUFACTURER: - if (vendor_products[i].manufacturer) - { - strcpyW(buffer, vendor_products[i].manufacturer); - return STATUS_SUCCESS; - } - break; - case HID_STRING_ID_ISERIALNUMBER: - if (vendor_products[i].serialnumber) - { - strcpyW(buffer, vendor_products[i].serialnumber); - return STATUS_SUCCESS; - } - break; - } - - return STATUS_UNSUCCESSFUL; -} - static NTSTATUS WINAPI hid_internal_dispatch(DEVICE_OBJECT *device, IRP *irp) { IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp); @@ -974,9 +899,7 @@ static NTSTATUS WINAPI hid_internal_dispatch(DEVICE_OBJECT *device, IRP *irp) DWORD index = (ULONG_PTR)irpsp->Parameters.DeviceIoControl.Type3InputBuffer; TRACE("IOCTL_HID_GET_STRING[%08x]\n", index);
- irp->IoStatus.Status = hid_get_native_string(device, index, (WCHAR *)irp->UserBuffer, buffer_len / sizeof(WCHAR)); - if (irp->IoStatus.Status != STATUS_SUCCESS) - irp->IoStatus.Status = unix_device_get_string(device, index, (WCHAR *)irp->UserBuffer, buffer_len / sizeof(WCHAR)); + irp->IoStatus.Status = unix_device_get_string(device, index, (WCHAR *)irp->UserBuffer, buffer_len / sizeof(WCHAR)); if (irp->IoStatus.Status == STATUS_SUCCESS) irp->IoStatus.Information = (strlenW((WCHAR *)irp->UserBuffer) + 1) * sizeof(WCHAR); break;
On 9/6/21 8:40 AM, Rémi Bernon wrote:
This should now be handled by xusb22.sys.
Saw that, planned on editing the comment, and then after final check that everything worked fine, forgot about it...