winevulkan: Update to VK spec version 1.2.195.
[wine.git] / dlls / winexinput.sys / main.c
blobd9a6fd5dd708d9570cb2a6d3c5422d0ac62453b2
1 /*
2 * WINE XInput device driver
4 * Copyright 2021 RĂ©mi Bernon for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
22 #include <stdlib.h>
24 #include "ntstatus.h"
25 #define WIN32_NO_STATUS
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winternl.h"
29 #include "winioctl.h"
31 #include "cfgmgr32.h"
32 #include "ddk/wdm.h"
33 #include "ddk/hidport.h"
34 #include "ddk/hidpddi.h"
35 #include "ddk/hidtypes.h"
37 #include "wine/asm.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(winexinput);
42 #if defined(__i386__) && !defined(_WIN32)
43 extern void *WINAPI wrap_fastcall_func1(void *func, const void *a);
44 __ASM_STDCALL_FUNC(wrap_fastcall_func1, 8,
45 "popl %ecx\n\t"
46 "popl %eax\n\t"
47 "xchgl (%esp),%ecx\n\t"
48 "jmp *%eax");
49 #define call_fastcall_func1(func,a) wrap_fastcall_func1(func,a)
50 #else
51 #define call_fastcall_func1(func,a) func(a)
52 #endif
54 #include "psh_hid_macros.h"
56 const BYTE xinput_report_desc[] =
58 USAGE_PAGE(1, HID_USAGE_PAGE_GENERIC),
59 USAGE(1, HID_USAGE_GENERIC_GAMEPAD),
60 COLLECTION(1, Application),
61 USAGE(1, 0),
62 COLLECTION(1, Physical),
63 USAGE(1, HID_USAGE_GENERIC_X),
64 USAGE(1, HID_USAGE_GENERIC_Y),
65 LOGICAL_MAXIMUM(2, 0xffff),
66 PHYSICAL_MAXIMUM(2, 0xffff),
67 REPORT_SIZE(1, 16),
68 REPORT_COUNT(1, 2),
69 INPUT(1, Data|Var|Abs),
70 END_COLLECTION,
72 COLLECTION(1, Physical),
73 USAGE(1, HID_USAGE_GENERIC_RX),
74 USAGE(1, HID_USAGE_GENERIC_RY),
75 REPORT_COUNT(1, 2),
76 INPUT(1, Data|Var|Abs),
77 END_COLLECTION,
79 COLLECTION(1, Physical),
80 USAGE(1, HID_USAGE_GENERIC_Z),
81 REPORT_COUNT(1, 1),
82 INPUT(1, Data|Var|Abs),
83 END_COLLECTION,
85 USAGE_PAGE(1, HID_USAGE_PAGE_BUTTON),
86 USAGE_MINIMUM(1, 1),
87 USAGE_MAXIMUM(1, 10),
88 LOGICAL_MAXIMUM(1, 1),
89 PHYSICAL_MAXIMUM(1, 1),
90 REPORT_COUNT(1, 10),
91 REPORT_SIZE(1, 1),
92 INPUT(1, Data|Var|Abs),
94 USAGE_PAGE(1, HID_USAGE_PAGE_GENERIC),
95 USAGE(1, HID_USAGE_GENERIC_HATSWITCH),
96 LOGICAL_MINIMUM(1, 1),
97 LOGICAL_MAXIMUM(1, 8),
98 PHYSICAL_MAXIMUM(2, 0x103b),
99 REPORT_SIZE(1, 4),
100 REPORT_COUNT(4, 1),
101 UNIT(1, 0x0e /* none */),
102 INPUT(1, Data|Var|Abs|Null),
104 REPORT_COUNT(1, 18),
105 REPORT_SIZE(1, 1),
106 INPUT(1, Cnst|Var|Abs),
107 END_COLLECTION,
110 #include "pop_hid_macros.h"
112 struct xinput_state
114 WORD lx_axis;
115 WORD ly_axis;
116 WORD rx_axis;
117 WORD ry_axis;
118 WORD trigger;
119 WORD buttons;
120 WORD padding;
123 struct device
125 BOOL is_fdo;
126 BOOL is_gamepad;
127 BOOL removed;
128 WCHAR device_id[MAX_DEVICE_ID_LEN];
131 static inline struct device *impl_from_DEVICE_OBJECT(DEVICE_OBJECT *device)
133 return (struct device *)device->DeviceExtension;
136 struct phys_device
138 struct device base;
139 struct func_device *fdo;
142 struct func_device
144 struct device base;
145 DEVICE_OBJECT *bus_device;
147 /* the bogus HID gamepad, as exposed by native XUSB */
148 DEVICE_OBJECT *gamepad_device;
150 /* the Wine-specific hidden HID device, used by XInput */
151 DEVICE_OBJECT *xinput_device;
153 WCHAR instance_id[MAX_DEVICE_ID_LEN];
155 HIDP_VALUE_CAPS lx_caps;
156 HIDP_VALUE_CAPS ly_caps;
157 HIDP_VALUE_CAPS lt_caps;
158 HIDP_VALUE_CAPS rx_caps;
159 HIDP_VALUE_CAPS ry_caps;
160 HIDP_VALUE_CAPS rt_caps;
161 HIDP_DEVICE_DESC device_desc;
163 /* everything below requires holding the cs */
164 CRITICAL_SECTION cs;
165 ULONG report_len;
166 char *report_buf;
167 IRP *pending_read;
168 BOOL pending_is_gamepad;
169 struct xinput_state xinput_state;
172 static inline struct func_device *fdo_from_DEVICE_OBJECT(DEVICE_OBJECT *device)
174 struct device *impl = impl_from_DEVICE_OBJECT(device);
175 if (impl->is_fdo) return CONTAINING_RECORD(impl, struct func_device, base);
176 else return CONTAINING_RECORD(impl, struct phys_device, base)->fdo;
179 static LONG sign_extend(ULONG value, const HIDP_VALUE_CAPS *caps)
181 UINT sign = 1 << (caps->BitSize - 1);
182 if (sign <= 1 || caps->LogicalMin >= 0) return value;
183 return value - ((value & sign) << 1);
186 static LONG scale_value(ULONG value, const HIDP_VALUE_CAPS *caps, LONG min, LONG max)
188 LONG tmp = sign_extend(value, caps);
189 if (caps->LogicalMin > caps->LogicalMax) return 0;
190 if (caps->LogicalMin > tmp || caps->LogicalMax < tmp) return 0;
191 return min + MulDiv(tmp - caps->LogicalMin, max - min, caps->LogicalMax - caps->LogicalMin);
194 static void translate_report_to_xinput_state(struct func_device *fdo)
196 ULONG lx = 0, ly = 0, rx = 0, ry = 0, lt = 0, rt = 0, hat = 0;
197 PHIDP_PREPARSED_DATA preparsed;
198 USAGE usages[10];
199 NTSTATUS status;
200 ULONG i, count;
202 preparsed = fdo->device_desc.CollectionDesc->PreparsedData;
204 count = ARRAY_SIZE(usages);
205 status = HidP_GetUsages(HidP_Input, HID_USAGE_PAGE_BUTTON, 0, usages,
206 &count, preparsed, fdo->report_buf, fdo->report_len);
207 if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsages returned %#x\n", status);
208 status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_HATSWITCH,
209 &hat, preparsed, fdo->report_buf, fdo->report_len);
210 if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue hat returned %#x\n", status);
211 status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_X,
212 &lx, preparsed, fdo->report_buf, fdo->report_len);
213 if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue x returned %#x\n", status);
214 status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Y,
215 &ly, preparsed, fdo->report_buf, fdo->report_len);
216 if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue y returned %#x\n", status);
217 status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z,
218 &lt, preparsed, fdo->report_buf, fdo->report_len);
219 if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue z returned %#x\n", status);
220 status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RX,
221 &rx, preparsed, fdo->report_buf, fdo->report_len);
222 if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue rx returned %#x\n", status);
223 status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RY,
224 &ry, preparsed, fdo->report_buf, fdo->report_len);
225 if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue ry returned %#x\n", status);
226 status = HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RZ,
227 &rt, preparsed, fdo->report_buf, fdo->report_len);
228 if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetUsageValue rz returned %#x\n", status);
230 if (hat < 1 || hat > 8) fdo->xinput_state.buttons = 0;
231 else fdo->xinput_state.buttons = hat << 10;
232 for (i = 0; i < count; i++)
234 if (usages[i] < 1 || usages[i] > 10) continue;
235 fdo->xinput_state.buttons |= (1 << (usages[i] - 1));
237 fdo->xinput_state.lx_axis = scale_value(lx, &fdo->lx_caps, 0, 65535);
238 fdo->xinput_state.ly_axis = scale_value(ly, &fdo->ly_caps, 0, 65535);
239 fdo->xinput_state.rx_axis = scale_value(rx, &fdo->rx_caps, 0, 65535);
240 fdo->xinput_state.ry_axis = scale_value(ry, &fdo->ry_caps, 0, 65535);
241 rt = scale_value(rt, &fdo->rt_caps, 0, 255);
242 lt = scale_value(lt, &fdo->lt_caps, 0, 255);
243 fdo->xinput_state.trigger = 0x8000 + (lt - rt) * 128;
246 static NTSTATUS WINAPI read_completion(DEVICE_OBJECT *device, IRP *xinput_irp, void *context)
248 IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(xinput_irp);
249 ULONG offset, read_len = stack->Parameters.DeviceIoControl.OutputBufferLength;
250 struct func_device *fdo = fdo_from_DEVICE_OBJECT(device);
251 char *read_buf = xinput_irp->UserBuffer;
252 IRP *gamepad_irp = context;
254 gamepad_irp->IoStatus.Status = xinput_irp->IoStatus.Status;
255 gamepad_irp->IoStatus.Information = xinput_irp->IoStatus.Information;
257 if (!xinput_irp->IoStatus.Status)
259 RtlEnterCriticalSection(&fdo->cs);
260 offset = fdo->report_buf[0] ? 0 : 1;
261 memcpy(fdo->report_buf + offset, read_buf, read_len);
262 translate_report_to_xinput_state(fdo);
263 memcpy(gamepad_irp->UserBuffer, &fdo->xinput_state, sizeof(fdo->xinput_state));
264 gamepad_irp->IoStatus.Information = sizeof(fdo->xinput_state);
265 RtlLeaveCriticalSection(&fdo->cs);
268 IoCompleteRequest(gamepad_irp, IO_NO_INCREMENT);
269 if (xinput_irp->PendingReturned) IoMarkIrpPending(xinput_irp);
270 return STATUS_SUCCESS;
273 /* check for a pending read from the other PDO, and complete both at a time.
274 * if there's none, save irp as pending, the other PDO will complete it.
275 * if the device is being removed, complete irp with an error. */
276 static NTSTATUS try_complete_pending_read(DEVICE_OBJECT *device, IRP *irp)
278 struct func_device *fdo = fdo_from_DEVICE_OBJECT(device);
279 struct device *impl = impl_from_DEVICE_OBJECT(device);
280 IRP *pending, *xinput_irp, *gamepad_irp;
281 BOOL removed, pending_is_gamepad;
283 RtlEnterCriticalSection(&fdo->cs);
284 pending_is_gamepad = fdo->pending_is_gamepad;
285 if ((removed = impl->removed))
286 pending = NULL;
287 else if ((pending = fdo->pending_read))
288 fdo->pending_read = NULL;
289 else
291 fdo->pending_read = irp;
292 fdo->pending_is_gamepad = impl->is_gamepad;
293 IoMarkIrpPending(irp);
295 RtlLeaveCriticalSection(&fdo->cs);
297 if (removed)
299 irp->IoStatus.Status = STATUS_DELETE_PENDING;
300 irp->IoStatus.Information = 0;
301 IoCompleteRequest(irp, IO_NO_INCREMENT);
302 return STATUS_DELETE_PENDING;
305 if (!pending) return STATUS_PENDING;
307 /* only one read at a time per device from hidclass.sys design */
308 if (pending_is_gamepad == impl->is_gamepad) ERR("multiple read requests!\n");
309 gamepad_irp = impl->is_gamepad ? irp : pending;
310 xinput_irp = impl->is_gamepad ? pending : irp;
312 /* pass xinput irp down, and complete gamepad irp on its way back */
313 IoCopyCurrentIrpStackLocationToNext(xinput_irp);
314 IoSetCompletionRoutine(xinput_irp, read_completion, gamepad_irp, TRUE, TRUE, TRUE);
315 return IoCallDriver(fdo->bus_device, xinput_irp);
318 static NTSTATUS WINAPI gamepad_internal_ioctl(DEVICE_OBJECT *device, IRP *irp)
320 IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
321 ULONG output_len = stack->Parameters.DeviceIoControl.OutputBufferLength;
322 ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
323 struct func_device *fdo = fdo_from_DEVICE_OBJECT(device);
325 TRACE("device %p, irp %p, code %#x, bus_device %p.\n", device, irp, code, fdo->bus_device);
327 switch (code)
329 case IOCTL_HID_GET_DEVICE_DESCRIPTOR:
331 HID_DESCRIPTOR *descriptor = (HID_DESCRIPTOR *)irp->UserBuffer;
333 irp->IoStatus.Information = sizeof(*descriptor);
334 if (output_len < sizeof(*descriptor))
336 irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
337 IoCompleteRequest(irp, IO_NO_INCREMENT);
338 return STATUS_BUFFER_TOO_SMALL;
341 memset(descriptor, 0, sizeof(*descriptor));
342 descriptor->bLength = sizeof(*descriptor);
343 descriptor->bDescriptorType = HID_HID_DESCRIPTOR_TYPE;
344 descriptor->bcdHID = HID_REVISION;
345 descriptor->bCountry = 0;
346 descriptor->bNumDescriptors = 1;
347 descriptor->DescriptorList[0].bReportType = HID_REPORT_DESCRIPTOR_TYPE;
348 descriptor->DescriptorList[0].wReportLength = sizeof(xinput_report_desc);
350 irp->IoStatus.Status = STATUS_SUCCESS;
351 IoCompleteRequest(irp, IO_NO_INCREMENT);
352 return STATUS_SUCCESS;
355 case IOCTL_HID_GET_REPORT_DESCRIPTOR:
356 irp->IoStatus.Information = sizeof(xinput_report_desc);
357 if (output_len < sizeof(xinput_report_desc))
359 irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
360 IoCompleteRequest(irp, IO_NO_INCREMENT);
361 return STATUS_BUFFER_TOO_SMALL;
364 memcpy(irp->UserBuffer, xinput_report_desc, sizeof(xinput_report_desc));
365 irp->IoStatus.Status = STATUS_SUCCESS;
366 IoCompleteRequest(irp, IO_NO_INCREMENT);
367 return STATUS_SUCCESS;
369 case IOCTL_HID_GET_INPUT_REPORT:
370 case IOCTL_HID_SET_OUTPUT_REPORT:
371 case IOCTL_HID_GET_FEATURE:
372 case IOCTL_HID_SET_FEATURE:
373 irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
374 IoCompleteRequest(irp, IO_NO_INCREMENT);
375 return STATUS_INVALID_PARAMETER;
377 default:
378 IoSkipCurrentIrpStackLocation(irp);
379 return IoCallDriver(fdo->bus_device, irp);
382 return STATUS_SUCCESS;
385 static NTSTATUS WINAPI internal_ioctl(DEVICE_OBJECT *device, IRP *irp)
387 IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
388 ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
389 struct func_device *fdo = fdo_from_DEVICE_OBJECT(device);
390 struct device *impl = impl_from_DEVICE_OBJECT(device);
392 if (InterlockedOr(&impl->removed, FALSE))
394 irp->IoStatus.Status = STATUS_DELETE_PENDING;
395 irp->IoStatus.Information = 0;
396 IoCompleteRequest(irp, IO_NO_INCREMENT);
397 return STATUS_DELETE_PENDING;
400 TRACE("device %p, irp %p, code %#x, bus_device %p.\n", device, irp, code, fdo->bus_device);
402 if (code == IOCTL_HID_READ_REPORT) return try_complete_pending_read(device, irp);
403 if (impl->is_gamepad || code == IOCTL_HID_GET_STRING) return gamepad_internal_ioctl(device, irp);
405 IoSkipCurrentIrpStackLocation(irp);
406 return IoCallDriver(fdo->bus_device, irp);
409 static WCHAR *query_instance_id(DEVICE_OBJECT *device)
411 struct func_device *fdo = fdo_from_DEVICE_OBJECT(device);
412 DWORD size = (wcslen(fdo->instance_id) + 1) * sizeof(WCHAR);
413 WCHAR *dst;
415 if ((dst = ExAllocatePool(PagedPool, size)))
416 memcpy(dst, fdo->instance_id, size);
418 return dst;
421 static WCHAR *query_device_id(DEVICE_OBJECT *device)
423 struct device *impl = impl_from_DEVICE_OBJECT(device);
424 DWORD size = (wcslen(impl->device_id) + 1) * sizeof(WCHAR);
425 WCHAR *dst;
427 if ((dst = ExAllocatePool(PagedPool, size)))
428 memcpy(dst, impl->device_id, size);
430 return dst;
433 static WCHAR *query_hardware_ids(DEVICE_OBJECT *device)
435 struct device *impl = impl_from_DEVICE_OBJECT(device);
436 DWORD size = (wcslen(impl->device_id) + 1) * sizeof(WCHAR);
437 WCHAR *dst;
439 if ((dst = ExAllocatePool(PagedPool, size + sizeof(WCHAR))))
441 memcpy(dst, impl->device_id, size);
442 dst[size / sizeof(WCHAR)] = 0;
445 return dst;
448 static WCHAR *query_compatible_ids(DEVICE_OBJECT *device)
450 static const WCHAR hid_compat[] = L"WINEBUS\\WINE_COMP_HID";
451 DWORD size = sizeof(hid_compat);
452 WCHAR *dst;
454 if ((dst = ExAllocatePool(PagedPool, size + sizeof(WCHAR))))
456 memcpy(dst, hid_compat, sizeof(hid_compat));
457 dst[size / sizeof(WCHAR)] = 0;
460 return dst;
463 static NTSTATUS WINAPI pdo_pnp(DEVICE_OBJECT *device, IRP *irp)
465 IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
466 struct func_device *fdo = fdo_from_DEVICE_OBJECT(device);
467 struct device *impl = impl_from_DEVICE_OBJECT(device);
468 ULONG code = stack->MinorFunction;
469 NTSTATUS status;
470 IRP *pending;
472 TRACE("device %p, irp %p, code %#x, bus_device %p.\n", device, irp, code, fdo->bus_device);
474 switch (code)
476 case IRP_MN_START_DEVICE:
477 status = STATUS_SUCCESS;
478 break;
480 case IRP_MN_SURPRISE_REMOVAL:
481 status = STATUS_SUCCESS;
482 if (InterlockedExchange(&impl->removed, TRUE)) break;
484 RtlEnterCriticalSection(&fdo->cs);
485 pending = fdo->pending_read;
486 fdo->pending_read = NULL;
487 RtlLeaveCriticalSection(&fdo->cs);
489 if (pending)
491 pending->IoStatus.Status = STATUS_DELETE_PENDING;
492 pending->IoStatus.Information = 0;
493 IoCompleteRequest(pending, IO_NO_INCREMENT);
495 break;
497 case IRP_MN_REMOVE_DEVICE:
498 irp->IoStatus.Status = STATUS_SUCCESS;
499 IoCompleteRequest(irp, IO_NO_INCREMENT);
500 IoDeleteDevice(device);
501 return STATUS_SUCCESS;
503 case IRP_MN_QUERY_ID:
505 BUS_QUERY_ID_TYPE type = stack->Parameters.QueryId.IdType;
506 switch (type)
508 case BusQueryHardwareIDs:
509 irp->IoStatus.Information = (ULONG_PTR)query_hardware_ids(device);
510 if (!irp->IoStatus.Information) status = STATUS_NO_MEMORY;
511 else status = STATUS_SUCCESS;
512 break;
513 case BusQueryCompatibleIDs:
514 irp->IoStatus.Information = (ULONG_PTR)query_compatible_ids(device);
515 if (!irp->IoStatus.Information) status = STATUS_NO_MEMORY;
516 else status = STATUS_SUCCESS;
517 break;
518 case BusQueryDeviceID:
519 irp->IoStatus.Information = (ULONG_PTR)query_device_id(device);
520 if (!irp->IoStatus.Information) status = STATUS_NO_MEMORY;
521 else status = STATUS_SUCCESS;
522 break;
523 case BusQueryInstanceID:
524 irp->IoStatus.Information = (ULONG_PTR)query_instance_id(device);
525 if (!irp->IoStatus.Information) status = STATUS_NO_MEMORY;
526 else status = STATUS_SUCCESS;
527 break;
528 default:
529 FIXME("IRP_MN_QUERY_ID type %u, not implemented!\n", type);
530 status = irp->IoStatus.Status;
531 break;
533 break;
536 case IRP_MN_QUERY_CAPABILITIES:
537 status = STATUS_SUCCESS;
538 break;
540 case IRP_MN_QUERY_DEVICE_RELATIONS:
541 status = irp->IoStatus.Status;
542 break;
544 default:
545 FIXME("code %#x, not implemented!\n", code);
546 status = irp->IoStatus.Status;
547 break;
550 irp->IoStatus.Status = status;
551 IoCompleteRequest(irp, IO_NO_INCREMENT);
552 return status;
555 static NTSTATUS create_child_pdos(DEVICE_OBJECT *device)
557 struct func_device *fdo = fdo_from_DEVICE_OBJECT(device);
558 DEVICE_OBJECT *gamepad_device, *xinput_device;
559 struct phys_device *pdo;
560 UNICODE_STRING name_str;
561 WCHAR *tmp, name[255];
562 NTSTATUS status;
564 swprintf(name, ARRAY_SIZE(name), L"\\Device\\WINEXINPUT#%p&%p&0",
565 device->DriverObject, fdo->bus_device);
566 RtlInitUnicodeString(&name_str, name);
568 if ((status = IoCreateDevice(device->DriverObject, sizeof(struct phys_device),
569 &name_str, 0, 0, FALSE, &gamepad_device)))
571 ERR("failed to create gamepad device, status %#x.\n", status);
572 return status;
575 swprintf(name, ARRAY_SIZE(name), L"\\Device\\WINEXINPUT#%p&%p&1",
576 device->DriverObject, fdo->bus_device);
577 RtlInitUnicodeString(&name_str, name);
579 if ((status = IoCreateDevice(device->DriverObject, sizeof(struct phys_device),
580 &name_str, 0, 0, FALSE, &xinput_device)))
582 ERR("failed to create xinput device, status %#x.\n", status);
583 IoDeleteDevice(gamepad_device);
584 return status;
587 fdo->gamepad_device = gamepad_device;
588 pdo = gamepad_device->DeviceExtension;
589 pdo->fdo = fdo;
590 pdo->base.is_fdo = FALSE;
591 pdo->base.is_gamepad = TRUE;
592 wcscpy(pdo->base.device_id, fdo->base.device_id);
594 if ((tmp = wcsstr(pdo->base.device_id, L"&MI_"))) memcpy(tmp, L"&IG", 6);
595 else wcscat(pdo->base.device_id, L"&IG_00");
597 TRACE("device %p, gamepad device %p.\n", device, gamepad_device);
599 fdo->xinput_device = xinput_device;
600 pdo = xinput_device->DeviceExtension;
601 pdo->fdo = fdo;
602 pdo->base.is_fdo = FALSE;
603 pdo->base.is_gamepad = FALSE;
604 wcscpy(pdo->base.device_id, fdo->base.device_id);
606 if ((tmp = wcsstr(pdo->base.device_id, L"&MI_"))) memcpy(tmp, L"&XI", 6);
607 else wcscat(pdo->base.device_id, L"&XI_00");
609 TRACE("device %p, xinput device %p.\n", device, xinput_device);
611 IoInvalidateDeviceRelations(fdo->bus_device, BusRelations);
612 return STATUS_SUCCESS;
615 static NTSTATUS sync_ioctl(DEVICE_OBJECT *device, DWORD code, void *in_buf, DWORD in_len, void *out_buf, DWORD out_len)
617 IO_STATUS_BLOCK io;
618 KEVENT event;
619 IRP *irp;
621 KeInitializeEvent(&event, NotificationEvent, FALSE);
622 irp = IoBuildDeviceIoControlRequest(code, device, in_buf, in_len, out_buf, out_len, TRUE, &event, &io);
623 if (IoCallDriver(device, irp) == STATUS_PENDING) KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
625 return io.Status;
628 static void check_value_caps(struct func_device *fdo, USHORT usage, HIDP_VALUE_CAPS *caps)
630 switch (usage)
632 case HID_USAGE_GENERIC_X: fdo->lx_caps = *caps; break;
633 case HID_USAGE_GENERIC_Y: fdo->ly_caps = *caps; break;
634 case HID_USAGE_GENERIC_Z: fdo->lt_caps = *caps; break;
635 case HID_USAGE_GENERIC_RX: fdo->rx_caps = *caps; break;
636 case HID_USAGE_GENERIC_RY: fdo->ry_caps = *caps; break;
637 case HID_USAGE_GENERIC_RZ: fdo->rt_caps = *caps; break;
641 static NTSTATUS initialize_device(DEVICE_OBJECT *device)
643 struct func_device *fdo = fdo_from_DEVICE_OBJECT(device);
644 ULONG i, u, button_count, report_desc_len, report_count;
645 PHIDP_REPORT_DESCRIPTOR report_desc;
646 PHIDP_PREPARSED_DATA preparsed;
647 HIDP_BUTTON_CAPS *button_caps;
648 HIDP_VALUE_CAPS *value_caps;
649 HIDP_REPORT_IDS *reports;
650 HID_DESCRIPTOR hid_desc;
651 NTSTATUS status;
652 HIDP_CAPS caps;
654 if ((status = sync_ioctl(fdo->bus_device, IOCTL_HID_GET_DEVICE_DESCRIPTOR, NULL, 0, &hid_desc, sizeof(hid_desc))))
655 return status;
657 if (!(report_desc_len = hid_desc.DescriptorList[0].wReportLength)) return STATUS_UNSUCCESSFUL;
658 if (!(report_desc = malloc(report_desc_len))) return STATUS_NO_MEMORY;
660 status = sync_ioctl(fdo->bus_device, IOCTL_HID_GET_REPORT_DESCRIPTOR, NULL, 0, report_desc, report_desc_len);
661 if (!status) status = HidP_GetCollectionDescription(report_desc, report_desc_len, PagedPool, &fdo->device_desc);
662 free(report_desc);
663 if (status != HIDP_STATUS_SUCCESS) return status;
665 preparsed = fdo->device_desc.CollectionDesc->PreparsedData;
666 status = HidP_GetCaps(preparsed, &caps);
667 if (status != HIDP_STATUS_SUCCESS) return status;
669 button_count = 0;
670 if (!(button_caps = malloc(sizeof(*button_caps) * caps.NumberInputButtonCaps))) return STATUS_NO_MEMORY;
671 status = HidP_GetButtonCaps(HidP_Input, button_caps, &caps.NumberInputButtonCaps, preparsed);
672 if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetButtonCaps returned %#x\n", status);
673 else for (i = 0; i < caps.NumberInputButtonCaps; i++)
675 if (button_caps[i].UsagePage != HID_USAGE_PAGE_BUTTON) continue;
676 if (button_caps[i].IsRange) button_count = max(button_count, button_caps[i].Range.UsageMax);
677 else button_count = max(button_count, button_caps[i].NotRange.Usage);
679 free(button_caps);
680 if (status != HIDP_STATUS_SUCCESS) return status;
681 if (button_count < 10) WARN("only %u buttons found\n", button_count);
683 if (!(value_caps = malloc(sizeof(*value_caps) * caps.NumberInputValueCaps))) return STATUS_NO_MEMORY;
684 status = HidP_GetValueCaps(HidP_Input, value_caps, &caps.NumberInputValueCaps, preparsed);
685 if (status != HIDP_STATUS_SUCCESS) WARN("HidP_GetValueCaps returned %#x\n", status);
686 else for (i = 0; i < caps.NumberInputValueCaps; i++)
688 HIDP_VALUE_CAPS *caps = value_caps + i;
689 if (caps->UsagePage != HID_USAGE_PAGE_GENERIC) continue;
690 if (!caps->IsRange) check_value_caps(fdo, caps->NotRange.Usage, caps);
691 else for (u = caps->Range.UsageMin; u <=caps->Range.UsageMax; u++) check_value_caps(fdo, u, value_caps + i);
693 free(value_caps);
694 if (status != HIDP_STATUS_SUCCESS) return status;
695 if (!fdo->lx_caps.UsagePage) WARN("missing lx axis\n");
696 if (!fdo->ly_caps.UsagePage) WARN("missing ly axis\n");
697 if (!fdo->lt_caps.UsagePage) WARN("missing lt axis\n");
698 if (!fdo->rx_caps.UsagePage) WARN("missing rx axis\n");
699 if (!fdo->ry_caps.UsagePage) WARN("missing ry axis\n");
700 if (!fdo->rt_caps.UsagePage) WARN("missing rt axis\n");
702 reports = fdo->device_desc.ReportIDs;
703 report_count = fdo->device_desc.ReportIDsLength;
704 for (i = 0; i < report_count; ++i) if (!reports[i].ReportID || reports[i].InputLength) break;
705 if (i == report_count) i = 0; /* no input report?!, just use first ID */
707 fdo->report_len = caps.InputReportByteLength;
708 if (!(fdo->report_buf = malloc(fdo->report_len))) return STATUS_NO_MEMORY;
709 fdo->report_buf[0] = reports[i].ReportID;
711 return STATUS_SUCCESS;
714 static NTSTATUS WINAPI set_event_completion(DEVICE_OBJECT *device, IRP *irp, void *context)
716 if (irp->PendingReturned) KeSetEvent((KEVENT *)context, IO_NO_INCREMENT, FALSE);
717 return STATUS_MORE_PROCESSING_REQUIRED;
720 static NTSTATUS WINAPI fdo_pnp(DEVICE_OBJECT *device, IRP *irp)
722 IO_STACK_LOCATION *stack = IoGetCurrentIrpStackLocation(irp);
723 struct func_device *fdo = fdo_from_DEVICE_OBJECT(device);
724 ULONG code = stack->MinorFunction;
725 DEVICE_RELATIONS *devices;
726 DEVICE_OBJECT *child;
727 NTSTATUS status;
728 KEVENT event;
730 TRACE("device %p, irp %p, code %#x, bus_device %p.\n", device, irp, code, fdo->bus_device);
732 switch (stack->MinorFunction)
734 case IRP_MN_QUERY_DEVICE_RELATIONS:
735 if (stack->Parameters.QueryDeviceRelations.Type == BusRelations)
737 if (!(devices = ExAllocatePool(PagedPool, offsetof(DEVICE_RELATIONS, Objects[2]))))
739 irp->IoStatus.Status = STATUS_NO_MEMORY;
740 IoCompleteRequest(irp, IO_NO_INCREMENT);
741 return STATUS_NO_MEMORY;
744 devices->Count = 0;
745 if ((child = fdo->xinput_device))
747 devices->Objects[devices->Count] = child;
748 call_fastcall_func1(ObfReferenceObject, child);
749 devices->Count++;
751 if ((child = fdo->gamepad_device))
753 devices->Objects[devices->Count] = child;
754 call_fastcall_func1(ObfReferenceObject, child);
755 devices->Count++;
758 irp->IoStatus.Information = (ULONG_PTR)devices;
759 irp->IoStatus.Status = STATUS_SUCCESS;
762 IoSkipCurrentIrpStackLocation(irp);
763 return IoCallDriver(fdo->bus_device, irp);
765 case IRP_MN_START_DEVICE:
766 KeInitializeEvent(&event, NotificationEvent, FALSE);
767 IoCopyCurrentIrpStackLocationToNext(irp);
768 IoSetCompletionRoutine(irp, set_event_completion, &event, TRUE, TRUE, TRUE);
770 status = IoCallDriver(fdo->bus_device, irp);
771 if (status == STATUS_PENDING)
773 KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
774 status = irp->IoStatus.Status;
776 if (!status) status = initialize_device(device);
777 if (!status) status = create_child_pdos(device);
779 if (status) irp->IoStatus.Status = status;
780 IoCompleteRequest(irp, IO_NO_INCREMENT);
781 return status;
783 case IRP_MN_REMOVE_DEVICE:
784 IoSkipCurrentIrpStackLocation(irp);
785 status = IoCallDriver(fdo->bus_device, irp);
786 IoDetachDevice(fdo->bus_device);
787 RtlDeleteCriticalSection(&fdo->cs);
788 HidP_FreeCollectionDescription(&fdo->device_desc);
789 free(fdo->report_buf);
790 IoDeleteDevice(device);
791 return status;
793 default:
794 IoSkipCurrentIrpStackLocation(irp);
795 return IoCallDriver(fdo->bus_device, irp);
798 return STATUS_SUCCESS;
801 static NTSTATUS WINAPI driver_pnp(DEVICE_OBJECT *device, IRP *irp)
803 struct device *impl = impl_from_DEVICE_OBJECT(device);
804 if (impl->is_fdo) return fdo_pnp(device, irp);
805 return pdo_pnp(device, irp);
808 static NTSTATUS get_device_id(DEVICE_OBJECT *device, BUS_QUERY_ID_TYPE type, WCHAR *id)
810 IO_STACK_LOCATION *stack;
811 IO_STATUS_BLOCK io;
812 KEVENT event;
813 IRP *irp;
815 KeInitializeEvent(&event, NotificationEvent, FALSE);
816 irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, device, NULL, 0, NULL, &event, &io);
817 if (irp == NULL) return STATUS_NO_MEMORY;
819 stack = IoGetNextIrpStackLocation(irp);
820 stack->MinorFunction = IRP_MN_QUERY_ID;
821 stack->Parameters.QueryId.IdType = type;
823 if (IoCallDriver(device, irp) == STATUS_PENDING)
824 KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
826 wcscpy(id, (WCHAR *)io.Information);
827 ExFreePool((WCHAR *)io.Information);
828 return io.Status;
831 static NTSTATUS WINAPI add_device(DRIVER_OBJECT *driver, DEVICE_OBJECT *bus_device)
833 WCHAR bus_id[MAX_DEVICE_ID_LEN], *device_id, instance_id[MAX_DEVICE_ID_LEN];
834 struct func_device *fdo;
835 DEVICE_OBJECT *device;
836 NTSTATUS status;
838 TRACE("driver %p, bus_device %p.\n", driver, bus_device);
840 if ((status = get_device_id(bus_device, BusQueryDeviceID, bus_id)))
842 ERR("failed to get bus device id, status %#x.\n", status);
843 return status;
846 if ((device_id = wcsrchr(bus_id, '\\'))) *device_id++ = 0;
847 else
849 ERR("unexpected device id %s\n", debugstr_w(bus_id));
850 return STATUS_UNSUCCESSFUL;
853 if ((status = get_device_id(bus_device, BusQueryInstanceID, instance_id)))
855 ERR("failed to get bus device instance id, status %#x.\n", status);
856 return status;
859 if ((status = IoCreateDevice(driver, sizeof(struct func_device), NULL,
860 FILE_DEVICE_BUS_EXTENDER, 0, FALSE, &device)))
862 ERR("failed to create bus FDO, status %#x.\n", status);
863 return status;
866 fdo = device->DeviceExtension;
867 fdo->base.is_fdo = TRUE;
868 swprintf(fdo->base.device_id, MAX_DEVICE_ID_LEN, L"WINEXINPUT\\%s", device_id);
870 fdo->bus_device = bus_device;
871 wcscpy(fdo->instance_id, instance_id);
873 RtlInitializeCriticalSection(&fdo->cs);
874 fdo->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": func_device.cs");
876 TRACE("device %p, bus_id %s, device_id %s, instance_id %s.\n", device, debugstr_w(bus_id),
877 debugstr_w(fdo->base.device_id), debugstr_w(fdo->instance_id));
879 IoAttachDeviceToDeviceStack(device, bus_device);
880 device->Flags &= ~DO_DEVICE_INITIALIZING;
881 return STATUS_SUCCESS;
884 static void WINAPI driver_unload(DRIVER_OBJECT *driver)
886 TRACE("driver %p\n", driver);
889 NTSTATUS WINAPI DriverEntry(DRIVER_OBJECT *driver, UNICODE_STRING *path)
891 TRACE("driver %p, path %s.\n", driver, debugstr_w(path->Buffer));
893 driver->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = internal_ioctl;
894 driver->MajorFunction[IRP_MJ_PNP] = driver_pnp;
895 driver->DriverExtension->AddDevice = add_device;
896 driver->DriverUnload = driver_unload;
898 return STATUS_SUCCESS;