2 * WINE Platform native bus driver
4 * Copyright 2016 Aric Stewart
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
23 #define NONAMELESSUNION
24 #define NONAMELESSSTRUCT
27 #define WIN32_NO_STATUS
32 #include "ddk/hidport.h"
33 #include "ddk/hidtypes.h"
35 #include "wine/debug.h"
36 #include "wine/unicode.h"
37 #include "wine/list.h"
40 #include "controller.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(plugplay
);
43 WINE_DECLARE_DEBUG_CHANNEL(hid_report
);
45 #if defined(__i386__) && !defined(_WIN32)
47 extern void * WINAPI
wrap_fastcall_func1( void *func
, const void *a
);
48 __ASM_STDCALL_FUNC( wrap_fastcall_func1
, 8,
51 "xchgl (%esp),%ecx\n\t"
54 #define call_fastcall_func1(func,a) wrap_fastcall_func1(func,a)
58 #define call_fastcall_func1(func,a) func(a)
66 const WCHAR
* manufacturer
;
68 const WCHAR
* serialnumber
;
71 #define VID_MICROSOFT 0x045e
73 static const WCHAR xbox360_product_string
[] = {
74 '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
77 static const WCHAR xboxone_product_string
[] = {
78 '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
81 static const struct product_desc XBOX_CONTROLLERS
[] = {
82 {VID_MICROSOFT
, 0x0202, NULL
, NULL
, NULL
}, /* Xbox Controller */
83 {VID_MICROSOFT
, 0x0285, NULL
, NULL
, NULL
}, /* Xbox Controller S */
84 {VID_MICROSOFT
, 0x0289, NULL
, NULL
, NULL
}, /* Xbox Controller S */
85 {VID_MICROSOFT
, 0x028e, NULL
, xbox360_product_string
, NULL
}, /* Xbox360 Controller */
86 {VID_MICROSOFT
, 0x028f, NULL
, xbox360_product_string
, NULL
}, /* Xbox360 Wireless Controller */
87 {VID_MICROSOFT
, 0x02d1, NULL
, xboxone_product_string
, NULL
}, /* Xbox One Controller */
88 {VID_MICROSOFT
, 0x02dd, NULL
, xboxone_product_string
, NULL
}, /* Xbox One Controller (Covert Forces/Firmware 2015) */
89 {VID_MICROSOFT
, 0x02e0, NULL
, NULL
, NULL
}, /* Xbox One X Controller */
90 {VID_MICROSOFT
, 0x02e3, NULL
, xboxone_product_string
, NULL
}, /* Xbox One Elite Controller */
91 {VID_MICROSOFT
, 0x02e6, NULL
, NULL
, NULL
}, /* Wireless XBox Controller Dongle */
92 {VID_MICROSOFT
, 0x02ea, NULL
, xboxone_product_string
, NULL
}, /* Xbox One S Controller */
93 {VID_MICROSOFT
, 0x02fd, NULL
, xboxone_product_string
, NULL
}, /* Xbox One S Controller (Firmware 2017) */
94 {VID_MICROSOFT
, 0x0719, NULL
, xbox360_product_string
, NULL
}, /* Xbox 360 Wireless Adapter */
97 static DRIVER_OBJECT
*driver_obj
;
99 static DEVICE_OBJECT
*mouse_obj
;
100 static DEVICE_OBJECT
*keyboard_obj
;
102 /* The root-enumerated device stack. */
103 DEVICE_OBJECT
*bus_pdo
;
104 static DEVICE_OBJECT
*bus_fdo
;
111 DEVICE_OBJECT
*device
;
114 struct device_extension
120 struct pnp_device
*pnp_device
;
122 WORD vid
, pid
, input
;
123 DWORD uid
, version
, index
;
126 const WCHAR
*busid
; /* Expected to be a static constant */
128 const platform_vtbl
*vtbl
;
131 DWORD last_report_size
;
132 BOOL last_report_read
;
134 LIST_ENTRY irp_queue
;
136 BYTE platform_private
[1];
139 static CRITICAL_SECTION device_list_cs
;
140 static CRITICAL_SECTION_DEBUG critsect_debug
=
142 0, 0, &device_list_cs
,
143 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
144 0, 0, { (DWORD_PTR
)(__FILE__
": device_list_cs") }
146 static CRITICAL_SECTION device_list_cs
= { &critsect_debug
, -1, 0, 0, 0, 0 };
148 static struct list pnp_devset
= LIST_INIT(pnp_devset
);
150 static const WCHAR zero_serialW
[]= {'0','0','0','0',0};
151 static const WCHAR miW
[] = {'M','I',0};
152 static const WCHAR igW
[] = {'I','G',0};
154 static inline WCHAR
*strdupW(const WCHAR
*src
)
157 if (!src
) return NULL
;
158 dst
= HeapAlloc(GetProcessHeap(), 0, (strlenW(src
) + 1)*sizeof(WCHAR
));
159 if (dst
) strcpyW(dst
, src
);
163 void *get_platform_private(DEVICE_OBJECT
*device
)
165 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
166 return ext
->platform_private
;
169 static DWORD
get_device_index(WORD vid
, WORD pid
, WORD input
)
171 struct pnp_device
*ptr
;
174 LIST_FOR_EACH_ENTRY(ptr
, &pnp_devset
, struct pnp_device
, entry
)
176 struct device_extension
*ext
= (struct device_extension
*)ptr
->device
->DeviceExtension
;
177 if (ext
->vid
== vid
&& ext
->pid
== pid
&& ext
->input
== input
)
178 index
= max(ext
->index
+ 1, index
);
184 static WCHAR
*get_instance_id(DEVICE_OBJECT
*device
)
186 static const WCHAR formatW
[] = {'%','i','&','%','s','&','%','x','&','%','i',0};
187 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
188 const WCHAR
*serial
= ext
->serial
? ext
->serial
: zero_serialW
;
189 DWORD len
= strlenW(serial
) + 33;
192 if ((dst
= ExAllocatePool(PagedPool
, len
* sizeof(WCHAR
))))
193 sprintfW(dst
, formatW
, ext
->version
, serial
, ext
->uid
, ext
->index
);
198 static WCHAR
*get_device_id(DEVICE_OBJECT
*device
)
200 static const WCHAR formatW
[] = {'%','s','\\','v','i','d','_','%','0','4','x',
201 '&','p','i','d','_','%','0','4','x',0};
202 static const WCHAR format_inputW
[] = {'%','s','\\','v','i','d','_','%','0','4','x',
203 '&','p','i','d','_','%','0','4','x','&','%','s','_','%','0','2','i',0};
204 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
205 DWORD len
= strlenW(ext
->busid
) + 34;
208 if ((dst
= ExAllocatePool(PagedPool
, len
* sizeof(WCHAR
))))
210 if (ext
->input
== (WORD
)-1)
212 sprintfW(dst
, formatW
, ext
->busid
, ext
->vid
, ext
->pid
);
216 sprintfW(dst
, format_inputW
, ext
->busid
, ext
->vid
, ext
->pid
,
217 ext
->is_gamepad
? igW
: miW
, ext
->input
);
224 static WCHAR
*get_compatible_ids(DEVICE_OBJECT
*device
)
226 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
229 if ((dst
= ExAllocatePool(PagedPool
, (strlenW(ext
->busid
) + 2) * sizeof(WCHAR
))))
231 strcpyW(dst
, ext
->busid
);
232 dst
[strlenW(dst
) + 1] = 0;
238 DEVICE_OBJECT
*bus_create_hid_device(const WCHAR
*busidW
, WORD vid
, WORD pid
,
239 WORD input
, DWORD version
, DWORD uid
, const WCHAR
*serialW
, BOOL is_gamepad
,
240 const platform_vtbl
*vtbl
, DWORD platform_data_size
)
242 static const WCHAR device_name_fmtW
[] = {'\\','D','e','v','i','c','e','\\','%','s','#','%','p',0};
243 struct device_extension
*ext
;
244 struct pnp_device
*pnp_dev
;
245 DEVICE_OBJECT
*device
;
246 UNICODE_STRING nameW
;
251 TRACE("(%s, %04x, %04x, %04x, %u, %u, %s, %u, %p, %u)\n",
252 debugstr_w(busidW
), vid
, pid
, input
, version
, uid
, debugstr_w(serialW
),
253 is_gamepad
, vtbl
, platform_data_size
);
255 if (!(pnp_dev
= HeapAlloc(GetProcessHeap(), 0, sizeof(*pnp_dev
))))
258 sprintfW(dev_name
, device_name_fmtW
, busidW
, pnp_dev
);
259 RtlInitUnicodeString(&nameW
, dev_name
);
260 length
= FIELD_OFFSET(struct device_extension
, platform_private
[platform_data_size
]);
261 status
= IoCreateDevice(driver_obj
, length
, &nameW
, 0, 0, FALSE
, &device
);
264 FIXME("failed to create device error %x\n", status
);
265 HeapFree(GetProcessHeap(), 0, pnp_dev
);
269 EnterCriticalSection(&device_list_cs
);
271 /* fill out device_extension struct */
272 ext
= (struct device_extension
*)device
->DeviceExtension
;
273 ext
->pnp_device
= pnp_dev
;
278 ext
->version
= version
;
279 ext
->index
= get_device_index(vid
, pid
, input
);
280 ext
->is_gamepad
= is_gamepad
;
281 ext
->serial
= strdupW(serialW
);
284 ext
->last_report
= NULL
;
285 ext
->last_report_size
= 0;
286 ext
->last_report_read
= TRUE
;
287 ext
->buffer_size
= 0;
289 memset(ext
->platform_private
, 0, platform_data_size
);
291 InitializeListHead(&ext
->irp_queue
);
292 InitializeCriticalSection(&ext
->cs
);
293 ext
->cs
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": cs");
295 /* add to list of pnp devices */
296 pnp_dev
->device
= device
;
297 list_add_tail(&pnp_devset
, &pnp_dev
->entry
);
299 LeaveCriticalSection(&device_list_cs
);
303 DEVICE_OBJECT
*bus_find_hid_device(const platform_vtbl
*vtbl
, void *platform_dev
)
305 struct pnp_device
*dev
;
306 DEVICE_OBJECT
*ret
= NULL
;
308 TRACE("(%p, %p)\n", vtbl
, platform_dev
);
310 EnterCriticalSection(&device_list_cs
);
311 LIST_FOR_EACH_ENTRY(dev
, &pnp_devset
, struct pnp_device
, entry
)
313 struct device_extension
*ext
= (struct device_extension
*)dev
->device
->DeviceExtension
;
314 if (ext
->vtbl
!= vtbl
) continue;
315 if (ext
->vtbl
->compare_platform_device(dev
->device
, platform_dev
) == 0)
321 LeaveCriticalSection(&device_list_cs
);
323 TRACE("returning %p\n", ret
);
327 DEVICE_OBJECT
* bus_enumerate_hid_devices(const platform_vtbl
*vtbl
, enum_func function
, void* context
)
329 struct pnp_device
*dev
, *dev_next
;
330 DEVICE_OBJECT
*ret
= NULL
;
333 TRACE("(%p)\n", vtbl
);
335 EnterCriticalSection(&device_list_cs
);
336 LIST_FOR_EACH_ENTRY_SAFE(dev
, dev_next
, &pnp_devset
, struct pnp_device
, entry
)
338 struct device_extension
*ext
= (struct device_extension
*)dev
->device
->DeviceExtension
;
339 if (ext
->vtbl
!= vtbl
) continue;
340 LeaveCriticalSection(&device_list_cs
);
341 cont
= function(dev
->device
, context
);
342 EnterCriticalSection(&device_list_cs
);
349 LeaveCriticalSection(&device_list_cs
);
353 void bus_unlink_hid_device(DEVICE_OBJECT
*device
)
355 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
356 struct pnp_device
*pnp_device
= ext
->pnp_device
;
358 EnterCriticalSection(&device_list_cs
);
359 list_remove(&pnp_device
->entry
);
360 LeaveCriticalSection(&device_list_cs
);
362 EnterCriticalSection(&ext
->cs
);
364 LeaveCriticalSection(&ext
->cs
);
367 void bus_remove_hid_device(DEVICE_OBJECT
*device
)
369 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
370 struct pnp_device
*pnp_device
= ext
->pnp_device
;
372 TRACE("(%p)\n", device
);
374 ext
->cs
.DebugInfo
->Spare
[0] = 0;
375 DeleteCriticalSection(&ext
->cs
);
377 HeapFree(GetProcessHeap(), 0, ext
->serial
);
378 HeapFree(GetProcessHeap(), 0, ext
->last_report
);
379 IoDeleteDevice(device
);
381 /* pnp_device must be released after the device is gone */
382 HeapFree(GetProcessHeap(), 0, pnp_device
);
385 static NTSTATUS
build_device_relations(DEVICE_RELATIONS
**devices
)
388 struct pnp_device
*ptr
;
390 EnterCriticalSection(&device_list_cs
);
391 *devices
= ExAllocatePool(PagedPool
, offsetof(DEVICE_RELATIONS
, Objects
[list_count(&pnp_devset
)]));
395 LeaveCriticalSection(&device_list_cs
);
396 return STATUS_INSUFFICIENT_RESOURCES
;
400 LIST_FOR_EACH_ENTRY(ptr
, &pnp_devset
, struct pnp_device
, entry
)
402 (*devices
)->Objects
[i
] = ptr
->device
;
403 call_fastcall_func1(ObfReferenceObject
, ptr
->device
);
406 LeaveCriticalSection(&device_list_cs
);
407 (*devices
)->Count
= i
;
408 return STATUS_SUCCESS
;
411 static NTSTATUS
handle_IRP_MN_QUERY_DEVICE_RELATIONS(IRP
*irp
)
413 NTSTATUS status
= irp
->IoStatus
.u
.Status
;
414 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation( irp
);
416 TRACE("IRP_MN_QUERY_DEVICE_RELATIONS\n");
417 switch (irpsp
->Parameters
.QueryDeviceRelations
.Type
)
419 case EjectionRelations
:
420 case RemovalRelations
:
421 case TargetDeviceRelation
:
423 FIXME("Unhandled Device Relation %x\n",irpsp
->Parameters
.QueryDeviceRelations
.Type
);
426 status
= build_device_relations((DEVICE_RELATIONS
**)&irp
->IoStatus
.Information
);
429 FIXME("Unknown Device Relation %x\n",irpsp
->Parameters
.QueryDeviceRelations
.Type
);
436 static NTSTATUS
handle_IRP_MN_QUERY_ID(DEVICE_OBJECT
*device
, IRP
*irp
)
438 NTSTATUS status
= irp
->IoStatus
.u
.Status
;
439 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation( irp
);
440 BUS_QUERY_ID_TYPE type
= irpsp
->Parameters
.QueryId
.IdType
;
442 TRACE("(%p, %p)\n", device
, irp
);
446 case BusQueryHardwareIDs
:
447 TRACE("BusQueryHardwareIDs\n");
448 irp
->IoStatus
.Information
= (ULONG_PTR
)get_compatible_ids(device
);
450 case BusQueryCompatibleIDs
:
451 TRACE("BusQueryCompatibleIDs\n");
452 irp
->IoStatus
.Information
= (ULONG_PTR
)get_compatible_ids(device
);
454 case BusQueryDeviceID
:
455 TRACE("BusQueryDeviceID\n");
456 irp
->IoStatus
.Information
= (ULONG_PTR
)get_device_id(device
);
458 case BusQueryInstanceID
:
459 TRACE("BusQueryInstanceID\n");
460 irp
->IoStatus
.Information
= (ULONG_PTR
)get_instance_id(device
);
463 FIXME("Unhandled type %08x\n", type
);
467 status
= irp
->IoStatus
.Information
? STATUS_SUCCESS
: STATUS_NO_MEMORY
;
471 static void mouse_free_device(DEVICE_OBJECT
*device
)
475 static NTSTATUS
mouse_get_reportdescriptor(DEVICE_OBJECT
*device
, BYTE
*buffer
, DWORD length
, DWORD
*ret_length
)
477 TRACE("buffer %p, length %u.\n", buffer
, length
);
479 *ret_length
= sizeof(REPORT_HEADER
) + sizeof(REPORT_TAIL
);
480 if (length
< sizeof(REPORT_HEADER
) + sizeof(REPORT_TAIL
))
481 return STATUS_BUFFER_TOO_SMALL
;
483 memcpy(buffer
, REPORT_HEADER
, sizeof(REPORT_HEADER
));
484 memcpy(buffer
+ sizeof(REPORT_HEADER
), REPORT_TAIL
, sizeof(REPORT_TAIL
));
485 buffer
[IDX_HEADER_PAGE
] = HID_USAGE_PAGE_GENERIC
;
486 buffer
[IDX_HEADER_USAGE
] = HID_USAGE_GENERIC_MOUSE
;
488 return STATUS_SUCCESS
;
491 static NTSTATUS
mouse_get_string(DEVICE_OBJECT
*device
, DWORD index
, WCHAR
*buffer
, DWORD length
)
493 static const WCHAR nameW
[] = {'W','i','n','e',' ','H','I','D',' ','m','o','u','s','e',0};
494 if (index
!= HID_STRING_ID_IPRODUCT
)
495 return STATUS_NOT_IMPLEMENTED
;
496 if (length
< ARRAY_SIZE(nameW
))
497 return STATUS_BUFFER_TOO_SMALL
;
498 strcpyW(buffer
, nameW
);
499 return STATUS_SUCCESS
;
502 static NTSTATUS
mouse_begin_report_processing(DEVICE_OBJECT
*device
)
504 return STATUS_SUCCESS
;
507 static NTSTATUS
mouse_set_output_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*ret_length
)
509 FIXME("id %u, stub!\n", id
);
510 return STATUS_NOT_IMPLEMENTED
;
513 static NTSTATUS
mouse_get_feature_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*ret_length
)
515 FIXME("id %u, stub!\n", id
);
516 return STATUS_NOT_IMPLEMENTED
;
519 static NTSTATUS
mouse_set_feature_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*ret_length
)
521 FIXME("id %u, stub!\n", id
);
522 return STATUS_NOT_IMPLEMENTED
;
525 static const platform_vtbl mouse_vtbl
=
527 .free_device
= mouse_free_device
,
528 .get_reportdescriptor
= mouse_get_reportdescriptor
,
529 .get_string
= mouse_get_string
,
530 .begin_report_processing
= mouse_begin_report_processing
,
531 .set_output_report
= mouse_set_output_report
,
532 .get_feature_report
= mouse_get_feature_report
,
533 .set_feature_report
= mouse_set_feature_report
,
536 static void mouse_device_create(void)
538 static const WCHAR busidW
[] = {'W','I','N','E','M','O','U','S','E',0};
540 mouse_obj
= bus_create_hid_device(busidW
, 0, 0, -1, 0, 0, busidW
, FALSE
, &mouse_vtbl
, 0);
541 IoInvalidateDeviceRelations(bus_pdo
, BusRelations
);
544 static NTSTATUS
keyboard_get_reportdescriptor(DEVICE_OBJECT
*device
, BYTE
*buffer
, DWORD length
, DWORD
*ret_length
)
546 TRACE("buffer %p, length %u.\n", buffer
, length
);
548 *ret_length
= sizeof(REPORT_HEADER
) + sizeof(REPORT_TAIL
);
549 if (length
< sizeof(REPORT_HEADER
) + sizeof(REPORT_TAIL
))
550 return STATUS_BUFFER_TOO_SMALL
;
552 memcpy(buffer
, REPORT_HEADER
, sizeof(REPORT_HEADER
));
553 memcpy(buffer
+ sizeof(REPORT_HEADER
), REPORT_TAIL
, sizeof(REPORT_TAIL
));
554 buffer
[IDX_HEADER_PAGE
] = HID_USAGE_PAGE_GENERIC
;
555 buffer
[IDX_HEADER_USAGE
] = HID_USAGE_GENERIC_KEYBOARD
;
557 return STATUS_SUCCESS
;
560 static NTSTATUS
keyboard_get_string(DEVICE_OBJECT
*device
, DWORD index
, WCHAR
*buffer
, DWORD length
)
562 static const WCHAR nameW
[] = {'W','i','n','e',' ','H','I','D',' ','k','e','y','b','o','a','r','d',0};
563 if (index
!= HID_STRING_ID_IPRODUCT
)
564 return STATUS_NOT_IMPLEMENTED
;
565 if (length
< ARRAY_SIZE(nameW
))
566 return STATUS_BUFFER_TOO_SMALL
;
567 strcpyW(buffer
, nameW
);
568 return STATUS_SUCCESS
;
571 static NTSTATUS
keyboard_begin_report_processing(DEVICE_OBJECT
*device
)
573 return STATUS_SUCCESS
;
576 static NTSTATUS
keyboard_set_output_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*ret_length
)
578 FIXME("id %u, stub!\n", id
);
579 return STATUS_NOT_IMPLEMENTED
;
582 static NTSTATUS
keyboard_get_feature_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*ret_length
)
584 FIXME("id %u, stub!\n", id
);
585 return STATUS_NOT_IMPLEMENTED
;
588 static NTSTATUS
keyboard_set_feature_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*ret_length
)
590 FIXME("id %u, stub!\n", id
);
591 return STATUS_NOT_IMPLEMENTED
;
594 static const platform_vtbl keyboard_vtbl
=
596 .get_reportdescriptor
= keyboard_get_reportdescriptor
,
597 .get_string
= keyboard_get_string
,
598 .begin_report_processing
= keyboard_begin_report_processing
,
599 .set_output_report
= keyboard_set_output_report
,
600 .get_feature_report
= keyboard_get_feature_report
,
601 .set_feature_report
= keyboard_set_feature_report
,
604 static void keyboard_device_create(void)
606 static const WCHAR busidW
[] = {'W','I','N','E','K','E','Y','B','O','A','R','D',0};
608 keyboard_obj
= bus_create_hid_device(busidW
, 0, 0, -1, 0, 0, busidW
, FALSE
, &keyboard_vtbl
, 0);
609 IoInvalidateDeviceRelations(bus_pdo
, BusRelations
);
612 static NTSTATUS
fdo_pnp_dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
614 static const WCHAR SDL_enabledW
[] = {'E','n','a','b','l','e',' ','S','D','L',0};
615 static const UNICODE_STRING SDL_enabled
= {sizeof(SDL_enabledW
) - sizeof(WCHAR
), sizeof(SDL_enabledW
), (WCHAR
*)SDL_enabledW
};
616 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation(irp
);
619 switch (irpsp
->MinorFunction
)
621 case IRP_MN_QUERY_DEVICE_RELATIONS
:
622 irp
->IoStatus
.u
.Status
= handle_IRP_MN_QUERY_DEVICE_RELATIONS(irp
);
624 case IRP_MN_START_DEVICE
:
625 mouse_device_create();
626 keyboard_device_create();
628 if (check_bus_option(&SDL_enabled
, 1))
630 if (sdl_driver_init() == STATUS_SUCCESS
)
632 irp
->IoStatus
.u
.Status
= STATUS_SUCCESS
;
638 irp
->IoStatus
.u
.Status
= STATUS_SUCCESS
;
640 case IRP_MN_SURPRISE_REMOVAL
:
641 irp
->IoStatus
.u
.Status
= STATUS_SUCCESS
;
643 case IRP_MN_REMOVE_DEVICE
:
644 udev_driver_unload();
645 iohid_driver_unload();
648 irp
->IoStatus
.u
.Status
= STATUS_SUCCESS
;
649 IoSkipCurrentIrpStackLocation(irp
);
650 ret
= IoCallDriver(bus_pdo
, irp
);
651 IoDetachDevice(bus_pdo
);
652 IoDeleteDevice(device
);
655 FIXME("Unhandled minor function %#x.\n", irpsp
->MinorFunction
);
658 IoSkipCurrentIrpStackLocation(irp
);
659 return IoCallDriver(bus_pdo
, irp
);
662 static NTSTATUS
pdo_pnp_dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
664 struct device_extension
*ext
= device
->DeviceExtension
;
665 NTSTATUS status
= irp
->IoStatus
.u
.Status
;
666 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation(irp
);
668 TRACE("device %p, irp %p, minor function %#x.\n", device
, irp
, irpsp
->MinorFunction
);
670 switch (irpsp
->MinorFunction
)
672 case IRP_MN_QUERY_ID
:
673 status
= handle_IRP_MN_QUERY_ID(device
, irp
);
676 case IRP_MN_QUERY_CAPABILITIES
:
677 status
= STATUS_SUCCESS
;
680 case IRP_MN_START_DEVICE
:
681 status
= STATUS_SUCCESS
;
684 case IRP_MN_REMOVE_DEVICE
:
686 struct pnp_device
*pnp_device
= ext
->pnp_device
;
689 EnterCriticalSection(&ext
->cs
);
690 while ((entry
= RemoveHeadList(&ext
->irp_queue
)) != &ext
->irp_queue
)
692 IRP
*queued_irp
= CONTAINING_RECORD(entry
, IRP
, Tail
.Overlay
.s
.ListEntry
);
693 queued_irp
->IoStatus
.u
.Status
= STATUS_DELETE_PENDING
;
694 queued_irp
->IoStatus
.Information
= 0;
695 IoCompleteRequest(queued_irp
, IO_NO_INCREMENT
);
697 LeaveCriticalSection(&ext
->cs
);
699 ext
->vtbl
->free_device(device
);
701 ext
->cs
.DebugInfo
->Spare
[0] = 0;
702 DeleteCriticalSection(&ext
->cs
);
704 HeapFree(GetProcessHeap(), 0, ext
->serial
);
705 HeapFree(GetProcessHeap(), 0, ext
->last_report
);
707 irp
->IoStatus
.u
.Status
= STATUS_SUCCESS
;
708 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
710 IoDeleteDevice(device
);
712 /* pnp_device must be released after the device is gone */
713 HeapFree(GetProcessHeap(), 0, pnp_device
);
715 return STATUS_SUCCESS
;
719 FIXME("Unhandled function %08x\n", irpsp
->MinorFunction
);
722 case IRP_MN_QUERY_DEVICE_RELATIONS
:
726 irp
->IoStatus
.u
.Status
= status
;
727 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
731 static NTSTATUS WINAPI
common_pnp_dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
733 if (device
== bus_fdo
)
734 return fdo_pnp_dispatch(device
, irp
);
735 return pdo_pnp_dispatch(device
, irp
);
738 static NTSTATUS
deliver_last_report(struct device_extension
*ext
, DWORD buffer_length
, BYTE
* buffer
, ULONG_PTR
*out_length
)
740 if (buffer_length
< ext
->last_report_size
)
743 return STATUS_BUFFER_TOO_SMALL
;
747 if (ext
->last_report
)
748 memcpy(buffer
, ext
->last_report
, ext
->last_report_size
);
749 *out_length
= ext
->last_report_size
;
750 return STATUS_SUCCESS
;
754 static NTSTATUS
hid_get_native_string(DEVICE_OBJECT
*device
, DWORD index
, WCHAR
*buffer
, DWORD length
)
756 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
757 const struct product_desc
*vendor_products
;
758 unsigned int i
, vendor_products_size
= 0;
760 if (ext
->vid
== VID_MICROSOFT
)
762 vendor_products
= XBOX_CONTROLLERS
;
763 vendor_products_size
= ARRAY_SIZE(XBOX_CONTROLLERS
);
766 for (i
= 0; i
< vendor_products_size
; i
++)
768 if (ext
->pid
== vendor_products
[i
].pid
)
772 if (i
>= vendor_products_size
)
773 return STATUS_UNSUCCESSFUL
;
777 case HID_STRING_ID_IPRODUCT
:
778 if (vendor_products
[i
].product
)
780 strcpyW(buffer
, vendor_products
[i
].product
);
781 return STATUS_SUCCESS
;
784 case HID_STRING_ID_IMANUFACTURER
:
785 if (vendor_products
[i
].manufacturer
)
787 strcpyW(buffer
, vendor_products
[i
].manufacturer
);
788 return STATUS_SUCCESS
;
791 case HID_STRING_ID_ISERIALNUMBER
:
792 if (vendor_products
[i
].serialnumber
)
794 strcpyW(buffer
, vendor_products
[i
].serialnumber
);
795 return STATUS_SUCCESS
;
800 return STATUS_UNSUCCESSFUL
;
803 static NTSTATUS WINAPI
hid_internal_dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
805 NTSTATUS status
= irp
->IoStatus
.u
.Status
;
806 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation(irp
);
807 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
809 TRACE("(%p, %p)\n", device
, irp
);
811 if (device
== bus_fdo
)
813 IoSkipCurrentIrpStackLocation(irp
);
814 return IoCallDriver(bus_pdo
, irp
);
817 EnterCriticalSection(&ext
->cs
);
821 LeaveCriticalSection(&ext
->cs
);
822 irp
->IoStatus
.u
.Status
= STATUS_DELETE_PENDING
;
823 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
824 return STATUS_DELETE_PENDING
;
827 switch (irpsp
->Parameters
.DeviceIoControl
.IoControlCode
)
829 case IOCTL_HID_GET_DEVICE_ATTRIBUTES
:
831 HID_DEVICE_ATTRIBUTES
*attr
= (HID_DEVICE_ATTRIBUTES
*)irp
->UserBuffer
;
832 TRACE("IOCTL_HID_GET_DEVICE_ATTRIBUTES\n");
834 if (irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(*attr
))
836 irp
->IoStatus
.u
.Status
= status
= STATUS_BUFFER_TOO_SMALL
;
840 memset(attr
, 0, sizeof(*attr
));
841 attr
->Size
= sizeof(*attr
);
842 attr
->VendorID
= ext
->vid
;
843 attr
->ProductID
= ext
->pid
;
844 attr
->VersionNumber
= ext
->version
;
846 irp
->IoStatus
.u
.Status
= status
= STATUS_SUCCESS
;
847 irp
->IoStatus
.Information
= sizeof(*attr
);
850 case IOCTL_HID_GET_DEVICE_DESCRIPTOR
:
852 HID_DESCRIPTOR
*descriptor
= (HID_DESCRIPTOR
*)irp
->UserBuffer
;
854 TRACE("IOCTL_HID_GET_DEVICE_DESCRIPTOR\n");
856 if (irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(*descriptor
))
858 irp
->IoStatus
.u
.Status
= status
= STATUS_BUFFER_TOO_SMALL
;
862 status
= ext
->vtbl
->get_reportdescriptor(device
, NULL
, 0, &length
);
863 if (status
!= STATUS_SUCCESS
&& status
!= STATUS_BUFFER_TOO_SMALL
)
865 WARN("Failed to get platform report descriptor length\n");
866 irp
->IoStatus
.u
.Status
= status
;
870 memset(descriptor
, 0, sizeof(*descriptor
));
871 descriptor
->bLength
= sizeof(*descriptor
);
872 descriptor
->bDescriptorType
= HID_HID_DESCRIPTOR_TYPE
;
873 descriptor
->bcdHID
= HID_REVISION
;
874 descriptor
->bCountry
= 0;
875 descriptor
->bNumDescriptors
= 1;
876 descriptor
->DescriptorList
[0].bReportType
= HID_REPORT_DESCRIPTOR_TYPE
;
877 descriptor
->DescriptorList
[0].wReportLength
= length
;
879 irp
->IoStatus
.u
.Status
= status
= STATUS_SUCCESS
;
880 irp
->IoStatus
.Information
= sizeof(*descriptor
);
883 case IOCTL_HID_GET_REPORT_DESCRIPTOR
:
885 DWORD length
= irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
;
886 TRACE("IOCTL_HID_GET_REPORT_DESCRIPTOR\n");
888 irp
->IoStatus
.u
.Status
= status
= ext
->vtbl
->get_reportdescriptor(device
, irp
->UserBuffer
, length
, &length
);
889 irp
->IoStatus
.Information
= length
;
892 case IOCTL_HID_GET_STRING
:
894 DWORD length
= irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
/ sizeof(WCHAR
);
895 DWORD index
= (ULONG_PTR
)irpsp
->Parameters
.DeviceIoControl
.Type3InputBuffer
;
896 TRACE("IOCTL_HID_GET_STRING[%08x]\n", index
);
898 irp
->IoStatus
.u
.Status
= status
= hid_get_native_string(device
, index
, (WCHAR
*)irp
->UserBuffer
, length
);
899 if (status
!= STATUS_SUCCESS
)
900 irp
->IoStatus
.u
.Status
= status
= ext
->vtbl
->get_string(device
, index
, (WCHAR
*)irp
->UserBuffer
, length
);
901 if (status
== STATUS_SUCCESS
)
902 irp
->IoStatus
.Information
= (strlenW((WCHAR
*)irp
->UserBuffer
) + 1) * sizeof(WCHAR
);
905 case IOCTL_HID_GET_INPUT_REPORT
:
907 HID_XFER_PACKET
*packet
= (HID_XFER_PACKET
*)(irp
->UserBuffer
);
908 TRACE_(hid_report
)("IOCTL_HID_GET_INPUT_REPORT\n");
909 status
= ext
->vtbl
->begin_report_processing(device
);
910 if (status
!= STATUS_SUCCESS
)
912 irp
->IoStatus
.u
.Status
= status
;
913 LeaveCriticalSection(&ext
->cs
);
917 irp
->IoStatus
.u
.Status
= status
= deliver_last_report(ext
,
918 packet
->reportBufferLen
, packet
->reportBuffer
,
919 &irp
->IoStatus
.Information
);
921 if (status
== STATUS_SUCCESS
)
922 packet
->reportBufferLen
= irp
->IoStatus
.Information
;
925 case IOCTL_HID_READ_REPORT
:
927 TRACE_(hid_report
)("IOCTL_HID_READ_REPORT\n");
928 status
= ext
->vtbl
->begin_report_processing(device
);
929 if (status
!= STATUS_SUCCESS
)
931 irp
->IoStatus
.u
.Status
= status
;
932 LeaveCriticalSection(&ext
->cs
);
935 if (!ext
->last_report_read
)
937 irp
->IoStatus
.u
.Status
= status
= deliver_last_report(ext
,
938 irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
,
939 irp
->UserBuffer
, &irp
->IoStatus
.Information
);
940 ext
->last_report_read
= TRUE
;
944 InsertTailList(&ext
->irp_queue
, &irp
->Tail
.Overlay
.s
.ListEntry
);
945 status
= STATUS_PENDING
;
949 case IOCTL_HID_SET_OUTPUT_REPORT
:
950 case IOCTL_HID_WRITE_REPORT
:
952 HID_XFER_PACKET
*packet
= (HID_XFER_PACKET
*)(irp
->UserBuffer
);
953 TRACE_(hid_report
)("IOCTL_HID_WRITE_REPORT / IOCTL_HID_SET_OUTPUT_REPORT\n");
954 irp
->IoStatus
.u
.Status
= status
= ext
->vtbl
->set_output_report(
955 device
, packet
->reportId
, packet
->reportBuffer
,
956 packet
->reportBufferLen
, &irp
->IoStatus
.Information
);
959 case IOCTL_HID_GET_FEATURE
:
961 HID_XFER_PACKET
*packet
= (HID_XFER_PACKET
*)(irp
->UserBuffer
);
962 TRACE_(hid_report
)("IOCTL_HID_GET_FEATURE\n");
963 irp
->IoStatus
.u
.Status
= status
= ext
->vtbl
->get_feature_report(
964 device
, packet
->reportId
, packet
->reportBuffer
,
965 packet
->reportBufferLen
, &irp
->IoStatus
.Information
);
966 packet
->reportBufferLen
= irp
->IoStatus
.Information
;
969 case IOCTL_HID_SET_FEATURE
:
971 HID_XFER_PACKET
*packet
= (HID_XFER_PACKET
*)(irp
->UserBuffer
);
972 TRACE_(hid_report
)("IOCTL_HID_SET_FEATURE\n");
973 irp
->IoStatus
.u
.Status
= status
= ext
->vtbl
->set_feature_report(
974 device
, packet
->reportId
, packet
->reportBuffer
,
975 packet
->reportBufferLen
, &irp
->IoStatus
.Information
);
980 ULONG code
= irpsp
->Parameters
.DeviceIoControl
.IoControlCode
;
981 FIXME("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
982 code
, code
>> 16, (code
>> 14) & 3, (code
>> 2) & 0xfff, code
& 3);
987 LeaveCriticalSection(&ext
->cs
);
989 if (status
!= STATUS_PENDING
)
990 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
995 void process_hid_report(DEVICE_OBJECT
*device
, BYTE
*report
, DWORD length
)
997 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
1001 if (!length
|| !report
)
1004 EnterCriticalSection(&ext
->cs
);
1005 if (length
> ext
->buffer_size
)
1007 HeapFree(GetProcessHeap(), 0, ext
->last_report
);
1008 ext
->last_report
= HeapAlloc(GetProcessHeap(), 0, length
);
1009 if (!ext
->last_report
)
1011 ERR_(hid_report
)("Failed to alloc last report\n");
1012 ext
->buffer_size
= 0;
1013 ext
->last_report_size
= 0;
1014 ext
->last_report_read
= TRUE
;
1015 LeaveCriticalSection(&ext
->cs
);
1019 ext
->buffer_size
= length
;
1022 memcpy(ext
->last_report
, report
, length
);
1023 ext
->last_report_size
= length
;
1024 ext
->last_report_read
= FALSE
;
1026 while ((entry
= RemoveHeadList(&ext
->irp_queue
)) != &ext
->irp_queue
)
1028 IO_STACK_LOCATION
*irpsp
;
1029 TRACE_(hid_report
)("Processing Request\n");
1030 irp
= CONTAINING_RECORD(entry
, IRP
, Tail
.Overlay
.s
.ListEntry
);
1031 irpsp
= IoGetCurrentIrpStackLocation(irp
);
1032 irp
->IoStatus
.u
.Status
= deliver_last_report(ext
,
1033 irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
,
1034 irp
->UserBuffer
, &irp
->IoStatus
.Information
);
1035 ext
->last_report_read
= TRUE
;
1036 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
1038 LeaveCriticalSection(&ext
->cs
);
1041 DWORD
check_bus_option(const UNICODE_STRING
*option
, DWORD default_value
)
1043 char buffer
[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
[sizeof(DWORD
)])];
1044 KEY_VALUE_PARTIAL_INFORMATION
*info
= (KEY_VALUE_PARTIAL_INFORMATION
*)buffer
;
1047 if (NtQueryValueKey(driver_key
, option
, KeyValuePartialInformation
, info
, sizeof(buffer
), &size
) == STATUS_SUCCESS
)
1049 if (info
->Type
== REG_DWORD
)
1050 return *(DWORD
*)info
->Data
;
1053 return default_value
;
1056 BOOL
is_xbox_gamepad(WORD vid
, WORD pid
)
1060 if (vid
!= VID_MICROSOFT
)
1063 for (i
= 0; i
< ARRAY_SIZE(XBOX_CONTROLLERS
); i
++)
1064 if (pid
== XBOX_CONTROLLERS
[i
].pid
) return TRUE
;
1069 static NTSTATUS WINAPI
driver_add_device(DRIVER_OBJECT
*driver
, DEVICE_OBJECT
*pdo
)
1073 TRACE("driver %p, pdo %p.\n", driver
, pdo
);
1075 if ((ret
= IoCreateDevice(driver
, 0, NULL
, FILE_DEVICE_BUS_EXTENDER
, 0, FALSE
, &bus_fdo
)))
1077 ERR("Failed to create FDO, status %#x.\n", ret
);
1081 IoAttachDeviceToDeviceStack(bus_fdo
, pdo
);
1084 bus_fdo
->Flags
&= ~DO_DEVICE_INITIALIZING
;
1086 return STATUS_SUCCESS
;
1089 static void WINAPI
driver_unload(DRIVER_OBJECT
*driver
)
1091 NtClose(driver_key
);
1094 NTSTATUS WINAPI
DriverEntry( DRIVER_OBJECT
*driver
, UNICODE_STRING
*path
)
1096 OBJECT_ATTRIBUTES attr
= {0};
1099 TRACE( "(%p, %s)\n", driver
, debugstr_w(path
->Buffer
) );
1101 attr
.Length
= sizeof(attr
);
1102 attr
.ObjectName
= path
;
1103 attr
.Attributes
= OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
;
1104 if ((ret
= NtOpenKey(&driver_key
, KEY_ALL_ACCESS
, &attr
)) != STATUS_SUCCESS
)
1105 ERR("Failed to open driver key, status %#x.\n", ret
);
1107 driver_obj
= driver
;
1109 driver
->MajorFunction
[IRP_MJ_PNP
] = common_pnp_dispatch
;
1110 driver
->MajorFunction
[IRP_MJ_INTERNAL_DEVICE_CONTROL
] = hid_internal_dispatch
;
1111 driver
->DriverExtension
->AddDevice
= driver_add_device
;
1112 driver
->DriverUnload
= driver_unload
;
1114 return STATUS_SUCCESS
;