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
24 #define WIN32_NO_STATUS
29 #include "ddk/hidport.h"
30 #include "ddk/hidtypes.h"
32 #include "wine/debug.h"
33 #include "wine/unicode.h"
34 #include "wine/list.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(plugplay
);
39 WINE_DECLARE_DEBUG_CHANNEL(hid_report
);
41 #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,
47 "xchgl (%esp),%ecx\n\t"
50 #define call_fastcall_func1(func,a) wrap_fastcall_func1(func,a)
54 #define call_fastcall_func1(func,a) func(a)
62 const WCHAR
* manufacturer
;
64 const WCHAR
* serialnumber
;
67 #define VID_MICROSOFT 0x045e
69 static const WCHAR xbox360_product_string
[] = {
70 '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
73 static const WCHAR xboxone_product_string
[] = {
74 '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
77 static const struct product_desc XBOX_CONTROLLERS
[] = {
78 {VID_MICROSOFT
, 0x0202, NULL
, NULL
, NULL
}, /* Xbox Controller */
79 {VID_MICROSOFT
, 0x0285, NULL
, NULL
, NULL
}, /* Xbox Controller S */
80 {VID_MICROSOFT
, 0x0289, NULL
, NULL
, NULL
}, /* Xbox Controller S */
81 {VID_MICROSOFT
, 0x028e, NULL
, xbox360_product_string
, NULL
}, /* Xbox360 Controller */
82 {VID_MICROSOFT
, 0x028f, NULL
, xbox360_product_string
, NULL
}, /* Xbox360 Wireless Controller */
83 {VID_MICROSOFT
, 0x02d1, NULL
, xboxone_product_string
, NULL
}, /* Xbox One Controller */
84 {VID_MICROSOFT
, 0x02dd, NULL
, xboxone_product_string
, NULL
}, /* Xbox One Controller (Covert Forces/Firmware 2015) */
85 {VID_MICROSOFT
, 0x02e0, NULL
, NULL
, NULL
}, /* Xbox One X Controller */
86 {VID_MICROSOFT
, 0x02e3, NULL
, xboxone_product_string
, NULL
}, /* Xbox One Elite Controller */
87 {VID_MICROSOFT
, 0x02e6, NULL
, NULL
, NULL
}, /* Wireless XBox Controller Dongle */
88 {VID_MICROSOFT
, 0x02ea, NULL
, xboxone_product_string
, NULL
}, /* Xbox One S Controller */
89 {VID_MICROSOFT
, 0x02fd, NULL
, xboxone_product_string
, NULL
}, /* Xbox One S Controller (Firmware 2017) */
90 {VID_MICROSOFT
, 0x0719, NULL
, xbox360_product_string
, NULL
}, /* Xbox 360 Wireless Adapter */
93 static DRIVER_OBJECT
*driver_obj
;
95 static DEVICE_OBJECT
*mouse_obj
;
96 static struct hid_descriptor mouse_desc
;
97 static DEVICE_OBJECT
*keyboard_obj
;
98 static struct hid_descriptor keyboard_desc
;
100 /* The root-enumerated device stack. */
101 DEVICE_OBJECT
*bus_pdo
;
102 static DEVICE_OBJECT
*bus_fdo
;
109 DEVICE_OBJECT
*device
;
114 DEVICE_STATE_STOPPED
,
115 DEVICE_STATE_STARTED
,
116 DEVICE_STATE_REMOVED
,
119 struct device_extension
122 enum device_state state
;
124 struct pnp_device
*pnp_device
;
126 WORD vid
, pid
, input
;
127 DWORD uid
, version
, index
;
130 const WCHAR
*busid
; /* Expected to be a static constant */
132 const platform_vtbl
*vtbl
;
135 DWORD last_report_size
;
136 BOOL last_report_read
;
138 LIST_ENTRY irp_queue
;
140 BYTE platform_private
[1];
143 static CRITICAL_SECTION device_list_cs
;
144 static CRITICAL_SECTION_DEBUG critsect_debug
=
146 0, 0, &device_list_cs
,
147 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
148 0, 0, { (DWORD_PTR
)(__FILE__
": device_list_cs") }
150 static CRITICAL_SECTION device_list_cs
= { &critsect_debug
, -1, 0, 0, 0, 0 };
152 static struct list pnp_devset
= LIST_INIT(pnp_devset
);
154 static const WCHAR zero_serialW
[]= {'0','0','0','0',0};
155 static const WCHAR miW
[] = {'M','I',0};
156 static const WCHAR igW
[] = {'I','G',0};
158 static inline WCHAR
*strdupW(const WCHAR
*src
)
161 if (!src
) return NULL
;
162 dst
= HeapAlloc(GetProcessHeap(), 0, (strlenW(src
) + 1)*sizeof(WCHAR
));
163 if (dst
) strcpyW(dst
, src
);
167 void *get_platform_private(DEVICE_OBJECT
*device
)
169 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
170 return ext
->platform_private
;
173 static DWORD
get_device_index(WORD vid
, WORD pid
, WORD input
)
175 struct pnp_device
*ptr
;
178 LIST_FOR_EACH_ENTRY(ptr
, &pnp_devset
, struct pnp_device
, entry
)
180 struct device_extension
*ext
= (struct device_extension
*)ptr
->device
->DeviceExtension
;
181 if (ext
->vid
== vid
&& ext
->pid
== pid
&& ext
->input
== input
)
182 index
= max(ext
->index
+ 1, index
);
188 static WCHAR
*get_instance_id(DEVICE_OBJECT
*device
)
190 static const WCHAR formatW
[] = {'%','i','&','%','s','&','%','x','&','%','i',0};
191 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
192 const WCHAR
*serial
= ext
->serial
? ext
->serial
: zero_serialW
;
193 DWORD len
= strlenW(serial
) + 33;
196 if ((dst
= ExAllocatePool(PagedPool
, len
* sizeof(WCHAR
))))
197 sprintfW(dst
, formatW
, ext
->version
, serial
, ext
->uid
, ext
->index
);
202 static WCHAR
*get_device_id(DEVICE_OBJECT
*device
)
204 static const WCHAR formatW
[] = {'%','s','\\','v','i','d','_','%','0','4','x',
205 '&','p','i','d','_','%','0','4','x',0};
206 static const WCHAR format_inputW
[] = {'%','s','\\','v','i','d','_','%','0','4','x',
207 '&','p','i','d','_','%','0','4','x','&','%','s','_','%','0','2','i',0};
208 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
209 DWORD len
= strlenW(ext
->busid
) + 34;
212 if ((dst
= ExAllocatePool(PagedPool
, len
* sizeof(WCHAR
))))
214 if (ext
->input
== (WORD
)-1)
216 sprintfW(dst
, formatW
, ext
->busid
, ext
->vid
, ext
->pid
);
220 sprintfW(dst
, format_inputW
, ext
->busid
, ext
->vid
, ext
->pid
,
221 ext
->is_gamepad
? igW
: miW
, ext
->input
);
228 static WCHAR
*get_compatible_ids(DEVICE_OBJECT
*device
)
230 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
233 if ((dst
= ExAllocatePool(PagedPool
, (strlenW(ext
->busid
) + 2) * sizeof(WCHAR
))))
235 strcpyW(dst
, ext
->busid
);
236 dst
[strlenW(dst
) + 1] = 0;
242 static void remove_pending_irps(DEVICE_OBJECT
*device
)
244 struct device_extension
*ext
= device
->DeviceExtension
;
247 while ((entry
= RemoveHeadList(&ext
->irp_queue
)) != &ext
->irp_queue
)
249 IRP
*queued_irp
= CONTAINING_RECORD(entry
, IRP
, Tail
.Overlay
.ListEntry
);
250 queued_irp
->IoStatus
.Status
= STATUS_DELETE_PENDING
;
251 queued_irp
->IoStatus
.Information
= 0;
252 IoCompleteRequest(queued_irp
, IO_NO_INCREMENT
);
256 DEVICE_OBJECT
*bus_create_hid_device(const WCHAR
*busidW
, WORD vid
, WORD pid
,
257 WORD input
, DWORD version
, DWORD uid
, const WCHAR
*serialW
, BOOL is_gamepad
,
258 const platform_vtbl
*vtbl
, DWORD platform_data_size
)
260 static const WCHAR device_name_fmtW
[] = {'\\','D','e','v','i','c','e','\\','%','s','#','%','p',0};
261 struct device_extension
*ext
;
262 struct pnp_device
*pnp_dev
;
263 DEVICE_OBJECT
*device
;
264 UNICODE_STRING nameW
;
269 TRACE("(%s, %04x, %04x, %04x, %u, %u, %s, %u, %p, %u)\n",
270 debugstr_w(busidW
), vid
, pid
, input
, version
, uid
, debugstr_w(serialW
),
271 is_gamepad
, vtbl
, platform_data_size
);
273 if (!(pnp_dev
= HeapAlloc(GetProcessHeap(), 0, sizeof(*pnp_dev
))))
276 sprintfW(dev_name
, device_name_fmtW
, busidW
, pnp_dev
);
277 RtlInitUnicodeString(&nameW
, dev_name
);
278 length
= FIELD_OFFSET(struct device_extension
, platform_private
[platform_data_size
]);
279 status
= IoCreateDevice(driver_obj
, length
, &nameW
, 0, 0, FALSE
, &device
);
282 FIXME("failed to create device error %x\n", status
);
283 HeapFree(GetProcessHeap(), 0, pnp_dev
);
287 EnterCriticalSection(&device_list_cs
);
289 /* fill out device_extension struct */
290 ext
= (struct device_extension
*)device
->DeviceExtension
;
291 ext
->pnp_device
= pnp_dev
;
296 ext
->version
= version
;
297 ext
->index
= get_device_index(vid
, pid
, input
);
298 ext
->is_gamepad
= is_gamepad
;
299 ext
->serial
= strdupW(serialW
);
302 ext
->last_report
= NULL
;
303 ext
->last_report_size
= 0;
304 ext
->last_report_read
= TRUE
;
305 ext
->buffer_size
= 0;
307 memset(ext
->platform_private
, 0, platform_data_size
);
309 InitializeListHead(&ext
->irp_queue
);
310 InitializeCriticalSection(&ext
->cs
);
311 ext
->cs
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": cs");
313 /* add to list of pnp devices */
314 pnp_dev
->device
= device
;
315 list_add_tail(&pnp_devset
, &pnp_dev
->entry
);
317 LeaveCriticalSection(&device_list_cs
);
321 DEVICE_OBJECT
*bus_find_hid_device(const WCHAR
*bus_id
, void *platform_dev
)
323 struct pnp_device
*dev
;
324 DEVICE_OBJECT
*ret
= NULL
;
326 TRACE("bus_id %s, platform_dev %p\n", debugstr_w(bus_id
), platform_dev
);
328 EnterCriticalSection(&device_list_cs
);
329 LIST_FOR_EACH_ENTRY(dev
, &pnp_devset
, struct pnp_device
, entry
)
331 struct device_extension
*ext
= (struct device_extension
*)dev
->device
->DeviceExtension
;
332 if (strcmpW(ext
->busid
, bus_id
)) continue;
333 if (ext
->vtbl
->compare_platform_device(dev
->device
, platform_dev
) == 0)
339 LeaveCriticalSection(&device_list_cs
);
341 TRACE("returning %p\n", ret
);
345 DEVICE_OBJECT
*bus_enumerate_hid_devices(const WCHAR
*bus_id
, enum_func function
, void *context
)
347 struct pnp_device
*dev
, *dev_next
;
348 DEVICE_OBJECT
*ret
= NULL
;
351 TRACE("bus_id %p\n", debugstr_w(bus_id
));
353 EnterCriticalSection(&device_list_cs
);
354 LIST_FOR_EACH_ENTRY_SAFE(dev
, dev_next
, &pnp_devset
, struct pnp_device
, entry
)
356 struct device_extension
*ext
= (struct device_extension
*)dev
->device
->DeviceExtension
;
357 if (strcmpW(ext
->busid
, bus_id
)) continue;
358 LeaveCriticalSection(&device_list_cs
);
359 cont
= function(dev
->device
, context
);
360 EnterCriticalSection(&device_list_cs
);
367 LeaveCriticalSection(&device_list_cs
);
371 void bus_unlink_hid_device(DEVICE_OBJECT
*device
)
373 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
374 struct pnp_device
*pnp_device
= ext
->pnp_device
;
376 EnterCriticalSection(&device_list_cs
);
377 list_remove(&pnp_device
->entry
);
378 LeaveCriticalSection(&device_list_cs
);
381 static NTSTATUS
build_device_relations(DEVICE_RELATIONS
**devices
)
384 struct pnp_device
*ptr
;
386 EnterCriticalSection(&device_list_cs
);
387 *devices
= ExAllocatePool(PagedPool
, offsetof(DEVICE_RELATIONS
, Objects
[list_count(&pnp_devset
)]));
391 LeaveCriticalSection(&device_list_cs
);
392 return STATUS_INSUFFICIENT_RESOURCES
;
396 LIST_FOR_EACH_ENTRY(ptr
, &pnp_devset
, struct pnp_device
, entry
)
398 (*devices
)->Objects
[i
] = ptr
->device
;
399 call_fastcall_func1(ObfReferenceObject
, ptr
->device
);
402 LeaveCriticalSection(&device_list_cs
);
403 (*devices
)->Count
= i
;
404 return STATUS_SUCCESS
;
407 static NTSTATUS
handle_IRP_MN_QUERY_DEVICE_RELATIONS(IRP
*irp
)
409 NTSTATUS status
= irp
->IoStatus
.Status
;
410 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation( irp
);
412 TRACE("IRP_MN_QUERY_DEVICE_RELATIONS\n");
413 switch (irpsp
->Parameters
.QueryDeviceRelations
.Type
)
415 case EjectionRelations
:
416 case RemovalRelations
:
417 case TargetDeviceRelation
:
419 FIXME("Unhandled Device Relation %x\n",irpsp
->Parameters
.QueryDeviceRelations
.Type
);
422 status
= build_device_relations((DEVICE_RELATIONS
**)&irp
->IoStatus
.Information
);
425 FIXME("Unknown Device Relation %x\n",irpsp
->Parameters
.QueryDeviceRelations
.Type
);
432 static NTSTATUS
handle_IRP_MN_QUERY_ID(DEVICE_OBJECT
*device
, IRP
*irp
)
434 NTSTATUS status
= irp
->IoStatus
.Status
;
435 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation( irp
);
436 BUS_QUERY_ID_TYPE type
= irpsp
->Parameters
.QueryId
.IdType
;
438 TRACE("(%p, %p)\n", device
, irp
);
442 case BusQueryHardwareIDs
:
443 TRACE("BusQueryHardwareIDs\n");
444 irp
->IoStatus
.Information
= (ULONG_PTR
)get_compatible_ids(device
);
446 case BusQueryCompatibleIDs
:
447 TRACE("BusQueryCompatibleIDs\n");
448 irp
->IoStatus
.Information
= (ULONG_PTR
)get_compatible_ids(device
);
450 case BusQueryDeviceID
:
451 TRACE("BusQueryDeviceID\n");
452 irp
->IoStatus
.Information
= (ULONG_PTR
)get_device_id(device
);
454 case BusQueryInstanceID
:
455 TRACE("BusQueryInstanceID\n");
456 irp
->IoStatus
.Information
= (ULONG_PTR
)get_instance_id(device
);
459 FIXME("Unhandled type %08x\n", type
);
463 status
= irp
->IoStatus
.Information
? STATUS_SUCCESS
: STATUS_NO_MEMORY
;
467 static void mouse_free_device(DEVICE_OBJECT
*device
)
471 static NTSTATUS
mouse_start_device(DEVICE_OBJECT
*device
)
473 if (!hid_descriptor_begin(&mouse_desc
, HID_USAGE_PAGE_GENERIC
, HID_USAGE_GENERIC_MOUSE
))
474 return STATUS_NO_MEMORY
;
475 if (!hid_descriptor_add_buttons(&mouse_desc
, HID_USAGE_PAGE_BUTTON
, 1, 3))
476 return STATUS_NO_MEMORY
;
477 if (!hid_descriptor_end(&mouse_desc
))
478 return STATUS_NO_MEMORY
;
480 return STATUS_SUCCESS
;
483 static NTSTATUS
mouse_get_reportdescriptor(DEVICE_OBJECT
*device
, BYTE
*buffer
, DWORD length
, DWORD
*ret_length
)
485 TRACE("buffer %p, length %u.\n", buffer
, length
);
487 *ret_length
= mouse_desc
.size
;
488 if (length
< mouse_desc
.size
) return STATUS_BUFFER_TOO_SMALL
;
490 memcpy(buffer
, mouse_desc
.data
, mouse_desc
.size
);
491 return STATUS_SUCCESS
;
494 static NTSTATUS
mouse_get_string(DEVICE_OBJECT
*device
, DWORD index
, WCHAR
*buffer
, DWORD length
)
496 static const WCHAR nameW
[] = {'W','i','n','e',' ','H','I','D',' ','m','o','u','s','e',0};
497 if (index
!= HID_STRING_ID_IPRODUCT
)
498 return STATUS_NOT_IMPLEMENTED
;
499 if (length
< ARRAY_SIZE(nameW
))
500 return STATUS_BUFFER_TOO_SMALL
;
501 strcpyW(buffer
, nameW
);
502 return STATUS_SUCCESS
;
505 static void mouse_set_output_report(DEVICE_OBJECT
*device
, HID_XFER_PACKET
*packet
, IO_STATUS_BLOCK
*io
)
507 FIXME("id %u, stub!\n", packet
->reportId
);
509 io
->Status
= STATUS_NOT_IMPLEMENTED
;
512 static void mouse_get_feature_report(DEVICE_OBJECT
*device
, HID_XFER_PACKET
*packet
, IO_STATUS_BLOCK
*io
)
514 FIXME("id %u, stub!\n", packet
->reportId
);
516 io
->Status
= STATUS_NOT_IMPLEMENTED
;
519 static void mouse_set_feature_report(DEVICE_OBJECT
*device
, HID_XFER_PACKET
*packet
, IO_STATUS_BLOCK
*io
)
521 FIXME("id %u, stub!\n", packet
->reportId
);
523 io
->Status
= STATUS_NOT_IMPLEMENTED
;
526 static const platform_vtbl mouse_vtbl
=
528 .free_device
= mouse_free_device
,
529 .start_device
= mouse_start_device
,
530 .get_reportdescriptor
= mouse_get_reportdescriptor
,
531 .get_string
= mouse_get_string
,
532 .set_output_report
= mouse_set_output_report
,
533 .get_feature_report
= mouse_get_feature_report
,
534 .set_feature_report
= mouse_set_feature_report
,
537 static void mouse_device_create(void)
539 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 void keyboard_free_device(DEVICE_OBJECT
*device
)
548 static NTSTATUS
keyboard_start_device(DEVICE_OBJECT
*device
)
550 if (!hid_descriptor_begin(&keyboard_desc
, HID_USAGE_PAGE_GENERIC
, HID_USAGE_GENERIC_KEYBOARD
))
551 return STATUS_NO_MEMORY
;
552 if (!hid_descriptor_add_buttons(&keyboard_desc
, HID_USAGE_PAGE_KEYBOARD
, 0, 101))
553 return STATUS_NO_MEMORY
;
554 if (!hid_descriptor_end(&keyboard_desc
))
555 return STATUS_NO_MEMORY
;
557 return STATUS_SUCCESS
;
560 static NTSTATUS
keyboard_get_reportdescriptor(DEVICE_OBJECT
*device
, BYTE
*buffer
, DWORD length
, DWORD
*ret_length
)
562 TRACE("buffer %p, length %u.\n", buffer
, length
);
564 *ret_length
= keyboard_desc
.size
;
565 if (length
< keyboard_desc
.size
) return STATUS_BUFFER_TOO_SMALL
;
567 memcpy(buffer
, keyboard_desc
.data
, keyboard_desc
.size
);
568 return STATUS_SUCCESS
;
571 static NTSTATUS
keyboard_get_string(DEVICE_OBJECT
*device
, DWORD index
, WCHAR
*buffer
, DWORD length
)
573 static const WCHAR nameW
[] = {'W','i','n','e',' ','H','I','D',' ','k','e','y','b','o','a','r','d',0};
574 if (index
!= HID_STRING_ID_IPRODUCT
)
575 return STATUS_NOT_IMPLEMENTED
;
576 if (length
< ARRAY_SIZE(nameW
))
577 return STATUS_BUFFER_TOO_SMALL
;
578 strcpyW(buffer
, nameW
);
579 return STATUS_SUCCESS
;
582 static void keyboard_set_output_report(DEVICE_OBJECT
*device
, HID_XFER_PACKET
*packet
, IO_STATUS_BLOCK
*io
)
584 FIXME("id %u, stub!\n", packet
->reportId
);
586 io
->Status
= STATUS_NOT_IMPLEMENTED
;
589 static void keyboard_get_feature_report(DEVICE_OBJECT
*device
, HID_XFER_PACKET
*packet
, IO_STATUS_BLOCK
*io
)
591 FIXME("id %u, stub!\n", packet
->reportId
);
593 io
->Status
= STATUS_NOT_IMPLEMENTED
;
596 static void keyboard_set_feature_report(DEVICE_OBJECT
*device
, HID_XFER_PACKET
*packet
, IO_STATUS_BLOCK
*io
)
598 FIXME("id %u, stub!\n", packet
->reportId
);
600 io
->Status
= STATUS_NOT_IMPLEMENTED
;
603 static const platform_vtbl keyboard_vtbl
=
605 .free_device
= keyboard_free_device
,
606 .start_device
= keyboard_start_device
,
607 .get_reportdescriptor
= keyboard_get_reportdescriptor
,
608 .get_string
= keyboard_get_string
,
609 .set_output_report
= keyboard_set_output_report
,
610 .get_feature_report
= keyboard_get_feature_report
,
611 .set_feature_report
= keyboard_set_feature_report
,
614 static void keyboard_device_create(void)
616 static const WCHAR busidW
[] = {'W','I','N','E','K','E','Y','B','O','A','R','D',0};
617 keyboard_obj
= bus_create_hid_device(busidW
, 0, 0, -1, 0, 0, busidW
, FALSE
, &keyboard_vtbl
, 0);
618 IoInvalidateDeviceRelations(bus_pdo
, BusRelations
);
621 static NTSTATUS
fdo_pnp_dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
623 static const WCHAR SDL_enabledW
[] = {'E','n','a','b','l','e',' ','S','D','L',0};
624 static const UNICODE_STRING SDL_enabled
= {sizeof(SDL_enabledW
) - sizeof(WCHAR
), sizeof(SDL_enabledW
), (WCHAR
*)SDL_enabledW
};
625 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation(irp
);
628 switch (irpsp
->MinorFunction
)
630 case IRP_MN_QUERY_DEVICE_RELATIONS
:
631 irp
->IoStatus
.Status
= handle_IRP_MN_QUERY_DEVICE_RELATIONS(irp
);
633 case IRP_MN_START_DEVICE
:
634 mouse_device_create();
635 keyboard_device_create();
637 if (check_bus_option(&SDL_enabled
, 1))
639 if (sdl_driver_init() == STATUS_SUCCESS
)
641 irp
->IoStatus
.Status
= STATUS_SUCCESS
;
647 irp
->IoStatus
.Status
= STATUS_SUCCESS
;
649 case IRP_MN_SURPRISE_REMOVAL
:
650 irp
->IoStatus
.Status
= STATUS_SUCCESS
;
652 case IRP_MN_REMOVE_DEVICE
:
653 udev_driver_unload();
654 iohid_driver_unload();
657 irp
->IoStatus
.Status
= STATUS_SUCCESS
;
658 IoSkipCurrentIrpStackLocation(irp
);
659 ret
= IoCallDriver(bus_pdo
, irp
);
660 IoDetachDevice(bus_pdo
);
661 IoDeleteDevice(device
);
664 FIXME("Unhandled minor function %#x.\n", irpsp
->MinorFunction
);
667 IoSkipCurrentIrpStackLocation(irp
);
668 return IoCallDriver(bus_pdo
, irp
);
671 static NTSTATUS
pdo_pnp_dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
673 struct device_extension
*ext
= device
->DeviceExtension
;
674 NTSTATUS status
= irp
->IoStatus
.Status
;
675 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation(irp
);
677 TRACE("device %p, irp %p, minor function %#x.\n", device
, irp
, irpsp
->MinorFunction
);
679 switch (irpsp
->MinorFunction
)
681 case IRP_MN_QUERY_ID
:
682 status
= handle_IRP_MN_QUERY_ID(device
, irp
);
685 case IRP_MN_QUERY_CAPABILITIES
:
686 status
= STATUS_SUCCESS
;
689 case IRP_MN_START_DEVICE
:
690 EnterCriticalSection(&ext
->cs
);
691 if (ext
->state
!= DEVICE_STATE_STOPPED
) status
= STATUS_SUCCESS
;
692 else if (ext
->state
== DEVICE_STATE_REMOVED
) status
= STATUS_DELETE_PENDING
;
693 else if (!(status
= ext
->vtbl
->start_device(device
))) ext
->state
= DEVICE_STATE_STARTED
;
694 else ERR("failed to start device %p, status %#x\n", device
, status
);
695 LeaveCriticalSection(&ext
->cs
);
698 case IRP_MN_SURPRISE_REMOVAL
:
699 EnterCriticalSection(&ext
->cs
);
700 remove_pending_irps(device
);
701 ext
->state
= DEVICE_STATE_REMOVED
;
702 LeaveCriticalSection(&ext
->cs
);
703 status
= STATUS_SUCCESS
;
706 case IRP_MN_REMOVE_DEVICE
:
708 struct pnp_device
*pnp_device
= ext
->pnp_device
;
710 remove_pending_irps(device
);
712 bus_unlink_hid_device(device
);
713 ext
->vtbl
->free_device(device
);
715 ext
->cs
.DebugInfo
->Spare
[0] = 0;
716 DeleteCriticalSection(&ext
->cs
);
718 HeapFree(GetProcessHeap(), 0, ext
->serial
);
719 HeapFree(GetProcessHeap(), 0, ext
->last_report
);
721 irp
->IoStatus
.Status
= STATUS_SUCCESS
;
722 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
724 IoDeleteDevice(device
);
726 /* pnp_device must be released after the device is gone */
727 HeapFree(GetProcessHeap(), 0, pnp_device
);
729 return STATUS_SUCCESS
;
733 FIXME("Unhandled function %08x\n", irpsp
->MinorFunction
);
736 case IRP_MN_QUERY_DEVICE_RELATIONS
:
740 irp
->IoStatus
.Status
= status
;
741 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
745 static NTSTATUS WINAPI
common_pnp_dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
747 if (device
== bus_fdo
)
748 return fdo_pnp_dispatch(device
, irp
);
749 return pdo_pnp_dispatch(device
, irp
);
752 static NTSTATUS
deliver_last_report(struct device_extension
*ext
, DWORD buffer_length
, BYTE
* buffer
, ULONG_PTR
*out_length
)
754 if (buffer_length
< ext
->last_report_size
)
757 return STATUS_BUFFER_TOO_SMALL
;
761 if (ext
->last_report
)
762 memcpy(buffer
, ext
->last_report
, ext
->last_report_size
);
763 *out_length
= ext
->last_report_size
;
764 return STATUS_SUCCESS
;
768 static NTSTATUS
hid_get_native_string(DEVICE_OBJECT
*device
, DWORD index
, WCHAR
*buffer
, DWORD length
)
770 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
771 const struct product_desc
*vendor_products
;
772 unsigned int i
, vendor_products_size
= 0;
774 if (ext
->vid
== VID_MICROSOFT
)
776 vendor_products
= XBOX_CONTROLLERS
;
777 vendor_products_size
= ARRAY_SIZE(XBOX_CONTROLLERS
);
780 for (i
= 0; i
< vendor_products_size
; i
++)
782 if (ext
->pid
== vendor_products
[i
].pid
)
786 if (i
>= vendor_products_size
)
787 return STATUS_UNSUCCESSFUL
;
791 case HID_STRING_ID_IPRODUCT
:
792 if (vendor_products
[i
].product
)
794 strcpyW(buffer
, vendor_products
[i
].product
);
795 return STATUS_SUCCESS
;
798 case HID_STRING_ID_IMANUFACTURER
:
799 if (vendor_products
[i
].manufacturer
)
801 strcpyW(buffer
, vendor_products
[i
].manufacturer
);
802 return STATUS_SUCCESS
;
805 case HID_STRING_ID_ISERIALNUMBER
:
806 if (vendor_products
[i
].serialnumber
)
808 strcpyW(buffer
, vendor_products
[i
].serialnumber
);
809 return STATUS_SUCCESS
;
814 return STATUS_UNSUCCESSFUL
;
817 static NTSTATUS WINAPI
hid_internal_dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
819 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation(irp
);
820 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
821 ULONG code
, buffer_len
= irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
;
824 TRACE("(%p, %p)\n", device
, irp
);
826 if (device
== bus_fdo
)
828 IoSkipCurrentIrpStackLocation(irp
);
829 return IoCallDriver(bus_pdo
, irp
);
832 EnterCriticalSection(&ext
->cs
);
834 if (ext
->state
== DEVICE_STATE_REMOVED
)
836 LeaveCriticalSection(&ext
->cs
);
837 irp
->IoStatus
.Status
= STATUS_DELETE_PENDING
;
838 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
839 return STATUS_DELETE_PENDING
;
842 switch ((code
= irpsp
->Parameters
.DeviceIoControl
.IoControlCode
))
844 case IOCTL_HID_GET_DEVICE_ATTRIBUTES
:
846 HID_DEVICE_ATTRIBUTES
*attr
= (HID_DEVICE_ATTRIBUTES
*)irp
->UserBuffer
;
847 TRACE("IOCTL_HID_GET_DEVICE_ATTRIBUTES\n");
849 if (buffer_len
< sizeof(*attr
))
851 irp
->IoStatus
.Status
= STATUS_BUFFER_TOO_SMALL
;
855 memset(attr
, 0, sizeof(*attr
));
856 attr
->Size
= sizeof(*attr
);
857 attr
->VendorID
= ext
->vid
;
858 attr
->ProductID
= ext
->pid
;
859 attr
->VersionNumber
= ext
->version
;
861 irp
->IoStatus
.Status
= STATUS_SUCCESS
;
862 irp
->IoStatus
.Information
= sizeof(*attr
);
865 case IOCTL_HID_GET_DEVICE_DESCRIPTOR
:
867 HID_DESCRIPTOR
*descriptor
= (HID_DESCRIPTOR
*)irp
->UserBuffer
;
869 TRACE("IOCTL_HID_GET_DEVICE_DESCRIPTOR\n");
871 if (buffer_len
< sizeof(*descriptor
))
873 irp
->IoStatus
.Status
= STATUS_BUFFER_TOO_SMALL
;
877 irp
->IoStatus
.Status
= ext
->vtbl
->get_reportdescriptor(device
, NULL
, 0, &length
);
878 if (irp
->IoStatus
.Status
!= STATUS_SUCCESS
&&
879 irp
->IoStatus
.Status
!= STATUS_BUFFER_TOO_SMALL
)
881 WARN("Failed to get platform report descriptor length\n");
885 memset(descriptor
, 0, sizeof(*descriptor
));
886 descriptor
->bLength
= sizeof(*descriptor
);
887 descriptor
->bDescriptorType
= HID_HID_DESCRIPTOR_TYPE
;
888 descriptor
->bcdHID
= HID_REVISION
;
889 descriptor
->bCountry
= 0;
890 descriptor
->bNumDescriptors
= 1;
891 descriptor
->DescriptorList
[0].bReportType
= HID_REPORT_DESCRIPTOR_TYPE
;
892 descriptor
->DescriptorList
[0].wReportLength
= length
;
894 irp
->IoStatus
.Status
= STATUS_SUCCESS
;
895 irp
->IoStatus
.Information
= sizeof(*descriptor
);
898 case IOCTL_HID_GET_REPORT_DESCRIPTOR
:
899 TRACE("IOCTL_HID_GET_REPORT_DESCRIPTOR\n");
900 irp
->IoStatus
.Status
= ext
->vtbl
->get_reportdescriptor(device
, irp
->UserBuffer
, buffer_len
, &buffer_len
);
901 irp
->IoStatus
.Information
= buffer_len
;
903 case IOCTL_HID_GET_STRING
:
905 DWORD index
= (ULONG_PTR
)irpsp
->Parameters
.DeviceIoControl
.Type3InputBuffer
;
906 TRACE("IOCTL_HID_GET_STRING[%08x]\n", index
);
908 irp
->IoStatus
.Status
= hid_get_native_string(device
, index
, (WCHAR
*)irp
->UserBuffer
, buffer_len
/ sizeof(WCHAR
));
909 if (irp
->IoStatus
.Status
!= STATUS_SUCCESS
)
910 irp
->IoStatus
.Status
= ext
->vtbl
->get_string(device
, index
, (WCHAR
*)irp
->UserBuffer
, buffer_len
/ sizeof(WCHAR
));
911 if (irp
->IoStatus
.Status
== STATUS_SUCCESS
)
912 irp
->IoStatus
.Information
= (strlenW((WCHAR
*)irp
->UserBuffer
) + 1) * sizeof(WCHAR
);
915 case IOCTL_HID_GET_INPUT_REPORT
:
917 HID_XFER_PACKET
*packet
= (HID_XFER_PACKET
*)(irp
->UserBuffer
);
918 TRACE_(hid_report
)("IOCTL_HID_GET_INPUT_REPORT\n");
919 irp
->IoStatus
.Status
= deliver_last_report(ext
,
920 packet
->reportBufferLen
, packet
->reportBuffer
,
921 &irp
->IoStatus
.Information
);
923 if (irp
->IoStatus
.Status
== STATUS_SUCCESS
)
924 packet
->reportBufferLen
= irp
->IoStatus
.Information
;
927 case IOCTL_HID_READ_REPORT
:
929 TRACE_(hid_report
)("IOCTL_HID_READ_REPORT\n");
930 if (!ext
->last_report_read
)
932 irp
->IoStatus
.Status
= deliver_last_report(ext
,
933 buffer_len
, irp
->UserBuffer
, &irp
->IoStatus
.Information
);
934 ext
->last_report_read
= TRUE
;
938 InsertTailList(&ext
->irp_queue
, &irp
->Tail
.Overlay
.ListEntry
);
939 irp
->IoStatus
.Status
= STATUS_PENDING
;
943 case IOCTL_HID_SET_OUTPUT_REPORT
:
944 case IOCTL_HID_WRITE_REPORT
:
946 HID_XFER_PACKET
*packet
= (HID_XFER_PACKET
*)(irp
->UserBuffer
);
947 TRACE_(hid_report
)("IOCTL_HID_WRITE_REPORT / IOCTL_HID_SET_OUTPUT_REPORT\n");
948 ext
->vtbl
->set_output_report(device
, packet
, &irp
->IoStatus
);
951 case IOCTL_HID_GET_FEATURE
:
953 HID_XFER_PACKET
*packet
= (HID_XFER_PACKET
*)(irp
->UserBuffer
);
954 TRACE_(hid_report
)("IOCTL_HID_GET_FEATURE\n");
955 ext
->vtbl
->get_feature_report(device
, packet
, &irp
->IoStatus
);
958 case IOCTL_HID_SET_FEATURE
:
960 HID_XFER_PACKET
*packet
= (HID_XFER_PACKET
*)(irp
->UserBuffer
);
961 TRACE_(hid_report
)("IOCTL_HID_SET_FEATURE\n");
962 ext
->vtbl
->set_feature_report(device
, packet
, &irp
->IoStatus
);
966 FIXME("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
967 code
, code
>> 16, (code
>> 14) & 3, (code
>> 2) & 0xfff, code
& 3);
971 status
= irp
->IoStatus
.Status
;
972 LeaveCriticalSection(&ext
->cs
);
974 if (status
!= STATUS_PENDING
) IoCompleteRequest(irp
, IO_NO_INCREMENT
);
978 void process_hid_report(DEVICE_OBJECT
*device
, BYTE
*report
, DWORD length
)
980 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
984 if (!length
|| !report
)
987 EnterCriticalSection(&ext
->cs
);
988 if (length
> ext
->buffer_size
)
990 HeapFree(GetProcessHeap(), 0, ext
->last_report
);
991 ext
->last_report
= HeapAlloc(GetProcessHeap(), 0, length
);
992 if (!ext
->last_report
)
994 ERR_(hid_report
)("Failed to alloc last report\n");
995 ext
->buffer_size
= 0;
996 ext
->last_report_size
= 0;
997 ext
->last_report_read
= TRUE
;
998 LeaveCriticalSection(&ext
->cs
);
1002 ext
->buffer_size
= length
;
1005 memcpy(ext
->last_report
, report
, length
);
1006 ext
->last_report_size
= length
;
1007 ext
->last_report_read
= FALSE
;
1009 while ((entry
= RemoveHeadList(&ext
->irp_queue
)) != &ext
->irp_queue
)
1011 IO_STACK_LOCATION
*irpsp
;
1012 TRACE_(hid_report
)("Processing Request\n");
1013 irp
= CONTAINING_RECORD(entry
, IRP
, Tail
.Overlay
.ListEntry
);
1014 irpsp
= IoGetCurrentIrpStackLocation(irp
);
1015 irp
->IoStatus
.Status
= deliver_last_report(ext
,
1016 irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
,
1017 irp
->UserBuffer
, &irp
->IoStatus
.Information
);
1018 ext
->last_report_read
= TRUE
;
1019 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
1021 LeaveCriticalSection(&ext
->cs
);
1024 DWORD
check_bus_option(const UNICODE_STRING
*option
, DWORD default_value
)
1026 char buffer
[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
[sizeof(DWORD
)])];
1027 KEY_VALUE_PARTIAL_INFORMATION
*info
= (KEY_VALUE_PARTIAL_INFORMATION
*)buffer
;
1030 if (NtQueryValueKey(driver_key
, option
, KeyValuePartialInformation
, info
, sizeof(buffer
), &size
) == STATUS_SUCCESS
)
1032 if (info
->Type
== REG_DWORD
)
1033 return *(DWORD
*)info
->Data
;
1036 return default_value
;
1039 BOOL
is_xbox_gamepad(WORD vid
, WORD pid
)
1043 if (vid
!= VID_MICROSOFT
)
1046 for (i
= 0; i
< ARRAY_SIZE(XBOX_CONTROLLERS
); i
++)
1047 if (pid
== XBOX_CONTROLLERS
[i
].pid
) return TRUE
;
1052 static NTSTATUS WINAPI
driver_add_device(DRIVER_OBJECT
*driver
, DEVICE_OBJECT
*pdo
)
1056 TRACE("driver %p, pdo %p.\n", driver
, pdo
);
1058 if ((ret
= IoCreateDevice(driver
, 0, NULL
, FILE_DEVICE_BUS_EXTENDER
, 0, FALSE
, &bus_fdo
)))
1060 ERR("Failed to create FDO, status %#x.\n", ret
);
1064 IoAttachDeviceToDeviceStack(bus_fdo
, pdo
);
1067 bus_fdo
->Flags
&= ~DO_DEVICE_INITIALIZING
;
1069 return STATUS_SUCCESS
;
1072 static void WINAPI
driver_unload(DRIVER_OBJECT
*driver
)
1074 NtClose(driver_key
);
1077 NTSTATUS WINAPI
DriverEntry( DRIVER_OBJECT
*driver
, UNICODE_STRING
*path
)
1079 OBJECT_ATTRIBUTES attr
= {0};
1082 TRACE( "(%p, %s)\n", driver
, debugstr_w(path
->Buffer
) );
1084 attr
.Length
= sizeof(attr
);
1085 attr
.ObjectName
= path
;
1086 attr
.Attributes
= OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
;
1087 if ((ret
= NtOpenKey(&driver_key
, KEY_ALL_ACCESS
, &attr
)) != STATUS_SUCCESS
)
1088 ERR("Failed to open driver key, status %#x.\n", ret
);
1090 driver_obj
= driver
;
1092 driver
->MajorFunction
[IRP_MJ_PNP
] = common_pnp_dispatch
;
1093 driver
->MajorFunction
[IRP_MJ_INTERNAL_DEVICE_CONTROL
] = hid_internal_dispatch
;
1094 driver
->DriverExtension
->AddDevice
= driver_add_device
;
1095 driver
->DriverUnload
= driver_unload
;
1097 return STATUS_SUCCESS
;