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
25 #define WIN32_NO_STATUS
33 #include "ddk/hidport.h"
34 #include "ddk/hidpddi.h"
35 #include "ddk/hidtypes.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(xinput
);
42 #ifdef __ASM_USE_FASTCALL_WRAPPER
43 extern void *WINAPI
wrap_fastcall_func1(void *func
, const void *a
);
44 __ASM_STDCALL_FUNC(wrap_fastcall_func1
, 8,
47 "xchgl (%esp),%ecx\n\t"
49 #define call_fastcall_func1(func,a) wrap_fastcall_func1(func,a)
51 #define call_fastcall_func1(func,a) func(a)
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
),
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),
69 INPUT(1, Data
|Var
|Abs
),
72 COLLECTION(1, Physical
),
73 USAGE(1, HID_USAGE_GENERIC_RX
),
74 USAGE(1, HID_USAGE_GENERIC_RY
),
76 INPUT(1, Data
|Var
|Abs
),
79 COLLECTION(1, Physical
),
80 USAGE(1, HID_USAGE_GENERIC_Z
),
82 INPUT(1, Data
|Var
|Abs
),
85 USAGE_PAGE(1, HID_USAGE_PAGE_BUTTON
),
88 LOGICAL_MAXIMUM(1, 1),
89 PHYSICAL_MAXIMUM(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),
101 UNIT(1, 0x0e /* none */),
102 INPUT(1, Data
|Var
|Abs
|Null
),
106 INPUT(1, Cnst
|Var
|Abs
),
110 #include "pop_hid_macros.h"
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
;
139 struct func_device
*fdo
;
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 */
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
;
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 %#lx\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 %#lx\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 %#lx\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 %#lx\n", status
);
217 status
= HidP_GetUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_Z
,
218 <
, preparsed
, fdo
->report_buf
, fdo
->report_len
);
219 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_GetUsageValue z returned %#lx\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 %#lx\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 %#lx\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 %#lx\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
))
287 else if ((pending
= fdo
->pending_read
))
288 fdo
->pending_read
= NULL
;
291 fdo
->pending_read
= irp
;
292 fdo
->pending_is_gamepad
= impl
->is_gamepad
;
293 IoMarkIrpPending(irp
);
295 RtlLeaveCriticalSection(&fdo
->cs
);
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 %#lx, bus_device %p.\n", device
, irp
, code
, fdo
->bus_device
);
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
;
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 %#lx, 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
) 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
);
415 if ((dst
= ExAllocatePool(PagedPool
, size
)))
416 memcpy(dst
, fdo
->instance_id
, size
);
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
);
427 if ((dst
= ExAllocatePool(PagedPool
, size
)))
428 memcpy(dst
, impl
->device_id
, size
);
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
);
439 if ((dst
= ExAllocatePool(PagedPool
, size
+ sizeof(WCHAR
))))
441 memcpy(dst
, impl
->device_id
, size
);
442 dst
[size
/ sizeof(WCHAR
)] = 0;
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
);
454 if ((dst
= ExAllocatePool(PagedPool
, size
+ sizeof(WCHAR
))))
456 memcpy(dst
, hid_compat
, sizeof(hid_compat
));
457 dst
[size
/ sizeof(WCHAR
)] = 0;
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 UCHAR code
= stack
->MinorFunction
;
472 TRACE("device %p, irp %p, code %#x, bus_device %p.\n", device
, irp
, code
, fdo
->bus_device
);
476 case IRP_MN_START_DEVICE
:
477 status
= STATUS_SUCCESS
;
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
);
491 pending
->IoStatus
.Status
= STATUS_DELETE_PENDING
;
492 pending
->IoStatus
.Information
= 0;
493 IoCompleteRequest(pending
, IO_NO_INCREMENT
);
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
;
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
;
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
;
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
;
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
;
529 FIXME("IRP_MN_QUERY_ID type %u, not implemented!\n", type
);
530 status
= irp
->IoStatus
.Status
;
536 case IRP_MN_QUERY_CAPABILITIES
:
537 status
= STATUS_SUCCESS
;
540 case IRP_MN_QUERY_DEVICE_RELATIONS
:
541 status
= irp
->IoStatus
.Status
;
545 FIXME("code %#x, not implemented!\n", code
);
546 status
= irp
->IoStatus
.Status
;
550 irp
->IoStatus
.Status
= status
;
551 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
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];
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 %#lx.\n", 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 %#lx.\n", status
);
583 IoDeleteDevice(gamepad_device
);
587 fdo
->gamepad_device
= gamepad_device
;
588 pdo
= gamepad_device
->DeviceExtension
;
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
;
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
)
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
);
628 static void check_value_caps(struct func_device
*fdo
, USHORT usage
, HIDP_VALUE_CAPS
*caps
)
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 UINT 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 HID_DESCRIPTOR hid_desc
= {0};
649 HIDP_VALUE_CAPS
*value_caps
;
650 HIDP_REPORT_IDS
*reports
;
654 if ((status
= sync_ioctl(fdo
->bus_device
, IOCTL_HID_GET_DEVICE_DESCRIPTOR
, NULL
, 0, &hid_desc
, sizeof(hid_desc
))))
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
);
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
;
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 %#lx\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
);
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 %#lx\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
);
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 UCHAR code
= stack
->MinorFunction
;
725 DEVICE_RELATIONS
*devices
;
726 DEVICE_OBJECT
*child
;
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
;
745 if ((child
= fdo
->xinput_device
))
747 devices
->Objects
[devices
->Count
] = child
;
748 call_fastcall_func1(ObfReferenceObject
, child
);
751 if ((child
= fdo
->gamepad_device
))
753 devices
->Objects
[devices
->Count
] = child
;
754 call_fastcall_func1(ObfReferenceObject
, child
);
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
);
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
);
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
;
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
);
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
;
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 %#lx.\n", status
);
846 if ((device_id
= wcsrchr(bus_id
, '\\'))) *device_id
++ = 0;
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 %#lx.\n", 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 %#lx.\n", 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
;