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"
37 #include "controller.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(plugplay
);
40 WINE_DECLARE_DEBUG_CHANNEL(hid_report
);
42 #if defined(__i386__) && !defined(_WIN32)
44 extern void * WINAPI
wrap_fastcall_func1( void *func
, const void *a
);
45 __ASM_STDCALL_FUNC( wrap_fastcall_func1
, 8,
48 "xchgl (%esp),%ecx\n\t"
51 #define call_fastcall_func1(func,a) wrap_fastcall_func1(func,a)
55 #define call_fastcall_func1(func,a) func(a)
63 const WCHAR
* manufacturer
;
65 const WCHAR
* serialnumber
;
68 #define VID_MICROSOFT 0x045e
70 static const WCHAR xbox360_product_string
[] = {
71 '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
74 static const WCHAR xboxone_product_string
[] = {
75 '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
78 static const struct product_desc XBOX_CONTROLLERS
[] = {
79 {VID_MICROSOFT
, 0x0202, NULL
, NULL
, NULL
}, /* Xbox Controller */
80 {VID_MICROSOFT
, 0x0285, NULL
, NULL
, NULL
}, /* Xbox Controller S */
81 {VID_MICROSOFT
, 0x0289, NULL
, NULL
, NULL
}, /* Xbox Controller S */
82 {VID_MICROSOFT
, 0x028e, NULL
, xbox360_product_string
, NULL
}, /* Xbox360 Controller */
83 {VID_MICROSOFT
, 0x028f, NULL
, xbox360_product_string
, NULL
}, /* Xbox360 Wireless Controller */
84 {VID_MICROSOFT
, 0x02d1, NULL
, xboxone_product_string
, NULL
}, /* Xbox One Controller */
85 {VID_MICROSOFT
, 0x02dd, NULL
, xboxone_product_string
, NULL
}, /* Xbox One Controller (Covert Forces/Firmware 2015) */
86 {VID_MICROSOFT
, 0x02e0, NULL
, NULL
, NULL
}, /* Xbox One X Controller */
87 {VID_MICROSOFT
, 0x02e3, NULL
, xboxone_product_string
, NULL
}, /* Xbox One Elite Controller */
88 {VID_MICROSOFT
, 0x02e6, NULL
, NULL
, NULL
}, /* Wireless XBox Controller Dongle */
89 {VID_MICROSOFT
, 0x02ea, NULL
, xboxone_product_string
, NULL
}, /* Xbox One S Controller */
90 {VID_MICROSOFT
, 0x02fd, NULL
, xboxone_product_string
, NULL
}, /* Xbox One S Controller (Firmware 2017) */
91 {VID_MICROSOFT
, 0x0719, NULL
, xbox360_product_string
, NULL
}, /* Xbox 360 Wireless Adapter */
94 static DRIVER_OBJECT
*driver_obj
;
96 static DEVICE_OBJECT
*mouse_obj
;
97 static DEVICE_OBJECT
*keyboard_obj
;
99 /* The root-enumerated device stack. */
100 DEVICE_OBJECT
*bus_pdo
;
101 static DEVICE_OBJECT
*bus_fdo
;
108 DEVICE_OBJECT
*device
;
111 struct device_extension
117 struct pnp_device
*pnp_device
;
119 WORD vid
, pid
, input
;
120 DWORD uid
, version
, index
;
123 const WCHAR
*busid
; /* Expected to be a static constant */
125 const platform_vtbl
*vtbl
;
128 DWORD last_report_size
;
129 BOOL last_report_read
;
131 LIST_ENTRY irp_queue
;
133 BYTE platform_private
[1];
136 static CRITICAL_SECTION device_list_cs
;
137 static CRITICAL_SECTION_DEBUG critsect_debug
=
139 0, 0, &device_list_cs
,
140 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
141 0, 0, { (DWORD_PTR
)(__FILE__
": device_list_cs") }
143 static CRITICAL_SECTION device_list_cs
= { &critsect_debug
, -1, 0, 0, 0, 0 };
145 static struct list pnp_devset
= LIST_INIT(pnp_devset
);
147 static const WCHAR zero_serialW
[]= {'0','0','0','0',0};
148 static const WCHAR miW
[] = {'M','I',0};
149 static const WCHAR igW
[] = {'I','G',0};
151 static inline WCHAR
*strdupW(const WCHAR
*src
)
154 if (!src
) return NULL
;
155 dst
= HeapAlloc(GetProcessHeap(), 0, (strlenW(src
) + 1)*sizeof(WCHAR
));
156 if (dst
) strcpyW(dst
, src
);
160 void *get_platform_private(DEVICE_OBJECT
*device
)
162 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
163 return ext
->platform_private
;
166 static DWORD
get_device_index(WORD vid
, WORD pid
, WORD input
)
168 struct pnp_device
*ptr
;
171 LIST_FOR_EACH_ENTRY(ptr
, &pnp_devset
, struct pnp_device
, entry
)
173 struct device_extension
*ext
= (struct device_extension
*)ptr
->device
->DeviceExtension
;
174 if (ext
->vid
== vid
&& ext
->pid
== pid
&& ext
->input
== input
)
175 index
= max(ext
->index
+ 1, index
);
181 static WCHAR
*get_instance_id(DEVICE_OBJECT
*device
)
183 static const WCHAR formatW
[] = {'%','i','&','%','s','&','%','x','&','%','i',0};
184 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
185 const WCHAR
*serial
= ext
->serial
? ext
->serial
: zero_serialW
;
186 DWORD len
= strlenW(serial
) + 33;
189 if ((dst
= ExAllocatePool(PagedPool
, len
* sizeof(WCHAR
))))
190 sprintfW(dst
, formatW
, ext
->version
, serial
, ext
->uid
, ext
->index
);
195 static WCHAR
*get_device_id(DEVICE_OBJECT
*device
)
197 static const WCHAR formatW
[] = {'%','s','\\','v','i','d','_','%','0','4','x',
198 '&','p','i','d','_','%','0','4','x',0};
199 static const WCHAR format_inputW
[] = {'%','s','\\','v','i','d','_','%','0','4','x',
200 '&','p','i','d','_','%','0','4','x','&','%','s','_','%','0','2','i',0};
201 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
202 DWORD len
= strlenW(ext
->busid
) + 34;
205 if ((dst
= ExAllocatePool(PagedPool
, len
* sizeof(WCHAR
))))
207 if (ext
->input
== (WORD
)-1)
209 sprintfW(dst
, formatW
, ext
->busid
, ext
->vid
, ext
->pid
);
213 sprintfW(dst
, format_inputW
, ext
->busid
, ext
->vid
, ext
->pid
,
214 ext
->is_gamepad
? igW
: miW
, ext
->input
);
221 static WCHAR
*get_compatible_ids(DEVICE_OBJECT
*device
)
223 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
226 if ((dst
= ExAllocatePool(PagedPool
, (strlenW(ext
->busid
) + 2) * sizeof(WCHAR
))))
228 strcpyW(dst
, ext
->busid
);
229 dst
[strlenW(dst
) + 1] = 0;
235 static void remove_pending_irps(DEVICE_OBJECT
*device
)
237 struct device_extension
*ext
= device
->DeviceExtension
;
240 while ((entry
= RemoveHeadList(&ext
->irp_queue
)) != &ext
->irp_queue
)
242 IRP
*queued_irp
= CONTAINING_RECORD(entry
, IRP
, Tail
.Overlay
.ListEntry
);
243 queued_irp
->IoStatus
.Status
= STATUS_DELETE_PENDING
;
244 queued_irp
->IoStatus
.Information
= 0;
245 IoCompleteRequest(queued_irp
, IO_NO_INCREMENT
);
249 DEVICE_OBJECT
*bus_create_hid_device(const WCHAR
*busidW
, WORD vid
, WORD pid
,
250 WORD input
, DWORD version
, DWORD uid
, const WCHAR
*serialW
, BOOL is_gamepad
,
251 const platform_vtbl
*vtbl
, DWORD platform_data_size
)
253 static const WCHAR device_name_fmtW
[] = {'\\','D','e','v','i','c','e','\\','%','s','#','%','p',0};
254 struct device_extension
*ext
;
255 struct pnp_device
*pnp_dev
;
256 DEVICE_OBJECT
*device
;
257 UNICODE_STRING nameW
;
262 TRACE("(%s, %04x, %04x, %04x, %u, %u, %s, %u, %p, %u)\n",
263 debugstr_w(busidW
), vid
, pid
, input
, version
, uid
, debugstr_w(serialW
),
264 is_gamepad
, vtbl
, platform_data_size
);
266 if (!(pnp_dev
= HeapAlloc(GetProcessHeap(), 0, sizeof(*pnp_dev
))))
269 sprintfW(dev_name
, device_name_fmtW
, busidW
, pnp_dev
);
270 RtlInitUnicodeString(&nameW
, dev_name
);
271 length
= FIELD_OFFSET(struct device_extension
, platform_private
[platform_data_size
]);
272 status
= IoCreateDevice(driver_obj
, length
, &nameW
, 0, 0, FALSE
, &device
);
275 FIXME("failed to create device error %x\n", status
);
276 HeapFree(GetProcessHeap(), 0, pnp_dev
);
280 EnterCriticalSection(&device_list_cs
);
282 /* fill out device_extension struct */
283 ext
= (struct device_extension
*)device
->DeviceExtension
;
284 ext
->pnp_device
= pnp_dev
;
289 ext
->version
= version
;
290 ext
->index
= get_device_index(vid
, pid
, input
);
291 ext
->is_gamepad
= is_gamepad
;
292 ext
->serial
= strdupW(serialW
);
295 ext
->last_report
= NULL
;
296 ext
->last_report_size
= 0;
297 ext
->last_report_read
= TRUE
;
298 ext
->buffer_size
= 0;
300 memset(ext
->platform_private
, 0, platform_data_size
);
302 InitializeListHead(&ext
->irp_queue
);
303 InitializeCriticalSection(&ext
->cs
);
304 ext
->cs
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": cs");
306 /* add to list of pnp devices */
307 pnp_dev
->device
= device
;
308 list_add_tail(&pnp_devset
, &pnp_dev
->entry
);
310 LeaveCriticalSection(&device_list_cs
);
314 DEVICE_OBJECT
*bus_find_hid_device(const platform_vtbl
*vtbl
, void *platform_dev
)
316 struct pnp_device
*dev
;
317 DEVICE_OBJECT
*ret
= NULL
;
319 TRACE("(%p, %p)\n", vtbl
, platform_dev
);
321 EnterCriticalSection(&device_list_cs
);
322 LIST_FOR_EACH_ENTRY(dev
, &pnp_devset
, struct pnp_device
, entry
)
324 struct device_extension
*ext
= (struct device_extension
*)dev
->device
->DeviceExtension
;
325 if (ext
->vtbl
!= vtbl
) continue;
326 if (ext
->vtbl
->compare_platform_device(dev
->device
, platform_dev
) == 0)
332 LeaveCriticalSection(&device_list_cs
);
334 TRACE("returning %p\n", ret
);
338 DEVICE_OBJECT
* bus_enumerate_hid_devices(const platform_vtbl
*vtbl
, enum_func function
, void* context
)
340 struct pnp_device
*dev
, *dev_next
;
341 DEVICE_OBJECT
*ret
= NULL
;
344 TRACE("(%p)\n", vtbl
);
346 EnterCriticalSection(&device_list_cs
);
347 LIST_FOR_EACH_ENTRY_SAFE(dev
, dev_next
, &pnp_devset
, struct pnp_device
, entry
)
349 struct device_extension
*ext
= (struct device_extension
*)dev
->device
->DeviceExtension
;
350 if (ext
->vtbl
!= vtbl
) continue;
351 LeaveCriticalSection(&device_list_cs
);
352 cont
= function(dev
->device
, context
);
353 EnterCriticalSection(&device_list_cs
);
360 LeaveCriticalSection(&device_list_cs
);
364 void bus_unlink_hid_device(DEVICE_OBJECT
*device
)
366 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
367 struct pnp_device
*pnp_device
= ext
->pnp_device
;
369 EnterCriticalSection(&device_list_cs
);
370 list_remove(&pnp_device
->entry
);
371 LeaveCriticalSection(&device_list_cs
);
374 void bus_remove_hid_device(DEVICE_OBJECT
*device
)
376 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
377 struct pnp_device
*pnp_device
= ext
->pnp_device
;
379 TRACE("(%p)\n", device
);
381 ext
->cs
.DebugInfo
->Spare
[0] = 0;
382 DeleteCriticalSection(&ext
->cs
);
384 HeapFree(GetProcessHeap(), 0, ext
->serial
);
385 HeapFree(GetProcessHeap(), 0, ext
->last_report
);
386 IoDeleteDevice(device
);
388 /* pnp_device must be released after the device is gone */
389 HeapFree(GetProcessHeap(), 0, pnp_device
);
392 static NTSTATUS
build_device_relations(DEVICE_RELATIONS
**devices
)
395 struct pnp_device
*ptr
;
397 EnterCriticalSection(&device_list_cs
);
398 *devices
= ExAllocatePool(PagedPool
, offsetof(DEVICE_RELATIONS
, Objects
[list_count(&pnp_devset
)]));
402 LeaveCriticalSection(&device_list_cs
);
403 return STATUS_INSUFFICIENT_RESOURCES
;
407 LIST_FOR_EACH_ENTRY(ptr
, &pnp_devset
, struct pnp_device
, entry
)
409 (*devices
)->Objects
[i
] = ptr
->device
;
410 call_fastcall_func1(ObfReferenceObject
, ptr
->device
);
413 LeaveCriticalSection(&device_list_cs
);
414 (*devices
)->Count
= i
;
415 return STATUS_SUCCESS
;
418 static NTSTATUS
handle_IRP_MN_QUERY_DEVICE_RELATIONS(IRP
*irp
)
420 NTSTATUS status
= irp
->IoStatus
.Status
;
421 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation( irp
);
423 TRACE("IRP_MN_QUERY_DEVICE_RELATIONS\n");
424 switch (irpsp
->Parameters
.QueryDeviceRelations
.Type
)
426 case EjectionRelations
:
427 case RemovalRelations
:
428 case TargetDeviceRelation
:
430 FIXME("Unhandled Device Relation %x\n",irpsp
->Parameters
.QueryDeviceRelations
.Type
);
433 status
= build_device_relations((DEVICE_RELATIONS
**)&irp
->IoStatus
.Information
);
436 FIXME("Unknown Device Relation %x\n",irpsp
->Parameters
.QueryDeviceRelations
.Type
);
443 static NTSTATUS
handle_IRP_MN_QUERY_ID(DEVICE_OBJECT
*device
, IRP
*irp
)
445 NTSTATUS status
= irp
->IoStatus
.Status
;
446 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation( irp
);
447 BUS_QUERY_ID_TYPE type
= irpsp
->Parameters
.QueryId
.IdType
;
449 TRACE("(%p, %p)\n", device
, irp
);
453 case BusQueryHardwareIDs
:
454 TRACE("BusQueryHardwareIDs\n");
455 irp
->IoStatus
.Information
= (ULONG_PTR
)get_compatible_ids(device
);
457 case BusQueryCompatibleIDs
:
458 TRACE("BusQueryCompatibleIDs\n");
459 irp
->IoStatus
.Information
= (ULONG_PTR
)get_compatible_ids(device
);
461 case BusQueryDeviceID
:
462 TRACE("BusQueryDeviceID\n");
463 irp
->IoStatus
.Information
= (ULONG_PTR
)get_device_id(device
);
465 case BusQueryInstanceID
:
466 TRACE("BusQueryInstanceID\n");
467 irp
->IoStatus
.Information
= (ULONG_PTR
)get_instance_id(device
);
470 FIXME("Unhandled type %08x\n", type
);
474 status
= irp
->IoStatus
.Information
? STATUS_SUCCESS
: STATUS_NO_MEMORY
;
478 static void mouse_free_device(DEVICE_OBJECT
*device
)
482 static NTSTATUS
mouse_get_reportdescriptor(DEVICE_OBJECT
*device
, BYTE
*buffer
, DWORD length
, DWORD
*ret_length
)
484 TRACE("buffer %p, length %u.\n", buffer
, length
);
486 *ret_length
= sizeof(REPORT_HEADER
) + sizeof(REPORT_BUTTONS
) + sizeof(REPORT_TAIL
);
487 if (length
< sizeof(REPORT_HEADER
) + sizeof(REPORT_BUTTONS
) + sizeof(REPORT_TAIL
))
488 return STATUS_BUFFER_TOO_SMALL
;
490 memcpy(buffer
, REPORT_HEADER
, sizeof(REPORT_HEADER
));
491 add_button_block(buffer
+ sizeof(REPORT_HEADER
), 1, 3);
492 memcpy(buffer
+ sizeof(REPORT_HEADER
) + sizeof(REPORT_BUTTONS
), REPORT_TAIL
, sizeof(REPORT_TAIL
));
493 buffer
[IDX_HEADER_PAGE
] = HID_USAGE_PAGE_GENERIC
;
494 buffer
[IDX_HEADER_USAGE
] = HID_USAGE_GENERIC_MOUSE
;
496 return STATUS_SUCCESS
;
499 static NTSTATUS
mouse_get_string(DEVICE_OBJECT
*device
, DWORD index
, WCHAR
*buffer
, DWORD length
)
501 static const WCHAR nameW
[] = {'W','i','n','e',' ','H','I','D',' ','m','o','u','s','e',0};
502 if (index
!= HID_STRING_ID_IPRODUCT
)
503 return STATUS_NOT_IMPLEMENTED
;
504 if (length
< ARRAY_SIZE(nameW
))
505 return STATUS_BUFFER_TOO_SMALL
;
506 strcpyW(buffer
, nameW
);
507 return STATUS_SUCCESS
;
510 static NTSTATUS
mouse_begin_report_processing(DEVICE_OBJECT
*device
)
512 return STATUS_SUCCESS
;
515 static NTSTATUS
mouse_set_output_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*ret_length
)
517 FIXME("id %u, stub!\n", id
);
518 return STATUS_NOT_IMPLEMENTED
;
521 static NTSTATUS
mouse_get_feature_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*ret_length
)
523 FIXME("id %u, stub!\n", id
);
524 return STATUS_NOT_IMPLEMENTED
;
527 static NTSTATUS
mouse_set_feature_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*ret_length
)
529 FIXME("id %u, stub!\n", id
);
530 return STATUS_NOT_IMPLEMENTED
;
533 static const platform_vtbl mouse_vtbl
=
535 .free_device
= mouse_free_device
,
536 .get_reportdescriptor
= mouse_get_reportdescriptor
,
537 .get_string
= mouse_get_string
,
538 .begin_report_processing
= mouse_begin_report_processing
,
539 .set_output_report
= mouse_set_output_report
,
540 .get_feature_report
= mouse_get_feature_report
,
541 .set_feature_report
= mouse_set_feature_report
,
544 static void mouse_device_create(void)
546 static const WCHAR busidW
[] = {'W','I','N','E','M','O','U','S','E',0};
548 mouse_obj
= bus_create_hid_device(busidW
, 0, 0, -1, 0, 0, busidW
, FALSE
, &mouse_vtbl
, 0);
549 IoInvalidateDeviceRelations(bus_pdo
, BusRelations
);
552 static void keyboard_free_device(DEVICE_OBJECT
*device
)
556 static NTSTATUS
keyboard_get_reportdescriptor(DEVICE_OBJECT
*device
, BYTE
*buffer
, DWORD length
, DWORD
*ret_length
)
558 TRACE("buffer %p, length %u.\n", buffer
, length
);
560 *ret_length
= sizeof(REPORT_HEADER
) + sizeof(REPORT_BUTTONS
) + sizeof(REPORT_TAIL
);
561 if (length
< sizeof(REPORT_HEADER
) + sizeof(REPORT_BUTTONS
) + sizeof(REPORT_TAIL
))
562 return STATUS_BUFFER_TOO_SMALL
;
564 memcpy(buffer
, REPORT_HEADER
, sizeof(REPORT_HEADER
));
565 add_button_block(buffer
+ sizeof(REPORT_HEADER
), 0, 101);
566 buffer
[sizeof(REPORT_HEADER
) + IDX_BUTTON_USAGE_PAGE
] = HID_USAGE_PAGE_KEYBOARD
;
567 memcpy(buffer
+ sizeof(REPORT_HEADER
) + sizeof(REPORT_BUTTONS
), REPORT_TAIL
, sizeof(REPORT_TAIL
));
568 buffer
[IDX_HEADER_PAGE
] = HID_USAGE_PAGE_GENERIC
;
569 buffer
[IDX_HEADER_USAGE
] = HID_USAGE_GENERIC_KEYBOARD
;
571 return STATUS_SUCCESS
;
574 static NTSTATUS
keyboard_get_string(DEVICE_OBJECT
*device
, DWORD index
, WCHAR
*buffer
, DWORD length
)
576 static const WCHAR nameW
[] = {'W','i','n','e',' ','H','I','D',' ','k','e','y','b','o','a','r','d',0};
577 if (index
!= HID_STRING_ID_IPRODUCT
)
578 return STATUS_NOT_IMPLEMENTED
;
579 if (length
< ARRAY_SIZE(nameW
))
580 return STATUS_BUFFER_TOO_SMALL
;
581 strcpyW(buffer
, nameW
);
582 return STATUS_SUCCESS
;
585 static NTSTATUS
keyboard_begin_report_processing(DEVICE_OBJECT
*device
)
587 return STATUS_SUCCESS
;
590 static NTSTATUS
keyboard_set_output_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*ret_length
)
592 FIXME("id %u, stub!\n", id
);
593 return STATUS_NOT_IMPLEMENTED
;
596 static NTSTATUS
keyboard_get_feature_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*ret_length
)
598 FIXME("id %u, stub!\n", id
);
599 return STATUS_NOT_IMPLEMENTED
;
602 static NTSTATUS
keyboard_set_feature_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*ret_length
)
604 FIXME("id %u, stub!\n", id
);
605 return STATUS_NOT_IMPLEMENTED
;
608 static const platform_vtbl keyboard_vtbl
=
610 .free_device
= keyboard_free_device
,
611 .get_reportdescriptor
= keyboard_get_reportdescriptor
,
612 .get_string
= keyboard_get_string
,
613 .begin_report_processing
= keyboard_begin_report_processing
,
614 .set_output_report
= keyboard_set_output_report
,
615 .get_feature_report
= keyboard_get_feature_report
,
616 .set_feature_report
= keyboard_set_feature_report
,
619 static void keyboard_device_create(void)
621 static const WCHAR busidW
[] = {'W','I','N','E','K','E','Y','B','O','A','R','D',0};
623 keyboard_obj
= bus_create_hid_device(busidW
, 0, 0, -1, 0, 0, busidW
, FALSE
, &keyboard_vtbl
, 0);
624 IoInvalidateDeviceRelations(bus_pdo
, BusRelations
);
627 static NTSTATUS
fdo_pnp_dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
629 static const WCHAR SDL_enabledW
[] = {'E','n','a','b','l','e',' ','S','D','L',0};
630 static const UNICODE_STRING SDL_enabled
= {sizeof(SDL_enabledW
) - sizeof(WCHAR
), sizeof(SDL_enabledW
), (WCHAR
*)SDL_enabledW
};
631 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation(irp
);
634 switch (irpsp
->MinorFunction
)
636 case IRP_MN_QUERY_DEVICE_RELATIONS
:
637 irp
->IoStatus
.Status
= handle_IRP_MN_QUERY_DEVICE_RELATIONS(irp
);
639 case IRP_MN_START_DEVICE
:
640 mouse_device_create();
641 keyboard_device_create();
643 if (check_bus_option(&SDL_enabled
, 1))
645 if (sdl_driver_init() == STATUS_SUCCESS
)
647 irp
->IoStatus
.Status
= STATUS_SUCCESS
;
653 irp
->IoStatus
.Status
= STATUS_SUCCESS
;
655 case IRP_MN_SURPRISE_REMOVAL
:
656 irp
->IoStatus
.Status
= STATUS_SUCCESS
;
658 case IRP_MN_REMOVE_DEVICE
:
659 udev_driver_unload();
660 iohid_driver_unload();
663 irp
->IoStatus
.Status
= STATUS_SUCCESS
;
664 IoSkipCurrentIrpStackLocation(irp
);
665 ret
= IoCallDriver(bus_pdo
, irp
);
666 IoDetachDevice(bus_pdo
);
667 IoDeleteDevice(device
);
670 FIXME("Unhandled minor function %#x.\n", irpsp
->MinorFunction
);
673 IoSkipCurrentIrpStackLocation(irp
);
674 return IoCallDriver(bus_pdo
, irp
);
677 static NTSTATUS
pdo_pnp_dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
679 struct device_extension
*ext
= device
->DeviceExtension
;
680 NTSTATUS status
= irp
->IoStatus
.Status
;
681 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation(irp
);
683 TRACE("device %p, irp %p, minor function %#x.\n", device
, irp
, irpsp
->MinorFunction
);
685 switch (irpsp
->MinorFunction
)
687 case IRP_MN_QUERY_ID
:
688 status
= handle_IRP_MN_QUERY_ID(device
, irp
);
691 case IRP_MN_QUERY_CAPABILITIES
:
692 status
= STATUS_SUCCESS
;
695 case IRP_MN_START_DEVICE
:
696 status
= STATUS_SUCCESS
;
699 case IRP_MN_SURPRISE_REMOVAL
:
700 EnterCriticalSection(&ext
->cs
);
701 remove_pending_irps(device
);
703 LeaveCriticalSection(&ext
->cs
);
704 status
= STATUS_SUCCESS
;
707 case IRP_MN_REMOVE_DEVICE
:
709 struct pnp_device
*pnp_device
= ext
->pnp_device
;
711 remove_pending_irps(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 NTSTATUS status
= irp
->IoStatus
.Status
;
820 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation(irp
);
821 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
822 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
);
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
= 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
= 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
= STATUS_BUFFER_TOO_SMALL
;
877 status
= ext
->vtbl
->get_reportdescriptor(device
, NULL
, 0, &length
);
878 if (status
!= STATUS_SUCCESS
&& status
!= STATUS_BUFFER_TOO_SMALL
)
880 WARN("Failed to get platform report descriptor length\n");
881 irp
->IoStatus
.Status
= status
;
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
= 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
= 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
= status
= hid_get_native_string(device
, index
, (WCHAR
*)irp
->UserBuffer
, buffer_len
/ sizeof(WCHAR
));
909 if (status
!= STATUS_SUCCESS
)
910 irp
->IoStatus
.Status
= status
= ext
->vtbl
->get_string(device
, index
, (WCHAR
*)irp
->UserBuffer
, buffer_len
/ sizeof(WCHAR
));
911 if (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 status
= ext
->vtbl
->begin_report_processing(device
);
920 if (status
!= STATUS_SUCCESS
)
922 irp
->IoStatus
.Status
= status
;
926 irp
->IoStatus
.Status
= status
= deliver_last_report(ext
,
927 packet
->reportBufferLen
, packet
->reportBuffer
,
928 &irp
->IoStatus
.Information
);
930 if (status
== STATUS_SUCCESS
)
931 packet
->reportBufferLen
= irp
->IoStatus
.Information
;
934 case IOCTL_HID_READ_REPORT
:
936 TRACE_(hid_report
)("IOCTL_HID_READ_REPORT\n");
937 status
= ext
->vtbl
->begin_report_processing(device
);
938 if (status
!= STATUS_SUCCESS
)
940 irp
->IoStatus
.Status
= status
;
943 if (!ext
->last_report_read
)
945 irp
->IoStatus
.Status
= status
= deliver_last_report(ext
,
946 buffer_len
, irp
->UserBuffer
, &irp
->IoStatus
.Information
);
947 ext
->last_report_read
= TRUE
;
951 InsertTailList(&ext
->irp_queue
, &irp
->Tail
.Overlay
.ListEntry
);
952 status
= STATUS_PENDING
;
956 case IOCTL_HID_SET_OUTPUT_REPORT
:
957 case IOCTL_HID_WRITE_REPORT
:
959 HID_XFER_PACKET
*packet
= (HID_XFER_PACKET
*)(irp
->UserBuffer
);
960 TRACE_(hid_report
)("IOCTL_HID_WRITE_REPORT / IOCTL_HID_SET_OUTPUT_REPORT\n");
961 irp
->IoStatus
.Status
= status
= ext
->vtbl
->set_output_report(
962 device
, packet
->reportId
, packet
->reportBuffer
,
963 packet
->reportBufferLen
, &irp
->IoStatus
.Information
);
966 case IOCTL_HID_GET_FEATURE
:
968 HID_XFER_PACKET
*packet
= (HID_XFER_PACKET
*)(irp
->UserBuffer
);
969 TRACE_(hid_report
)("IOCTL_HID_GET_FEATURE\n");
970 irp
->IoStatus
.Status
= status
= ext
->vtbl
->get_feature_report(
971 device
, packet
->reportId
, packet
->reportBuffer
,
972 packet
->reportBufferLen
, &irp
->IoStatus
.Information
);
973 packet
->reportBufferLen
= irp
->IoStatus
.Information
;
976 case IOCTL_HID_SET_FEATURE
:
978 HID_XFER_PACKET
*packet
= (HID_XFER_PACKET
*)(irp
->UserBuffer
);
979 TRACE_(hid_report
)("IOCTL_HID_SET_FEATURE\n");
980 irp
->IoStatus
.Status
= status
= ext
->vtbl
->set_feature_report(
981 device
, packet
->reportId
, packet
->reportBuffer
,
982 packet
->reportBufferLen
, &irp
->IoStatus
.Information
);
986 FIXME("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
987 code
, code
>> 16, (code
>> 14) & 3, (code
>> 2) & 0xfff, code
& 3);
991 LeaveCriticalSection(&ext
->cs
);
993 if (status
!= STATUS_PENDING
)
994 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
999 void process_hid_report(DEVICE_OBJECT
*device
, BYTE
*report
, DWORD length
)
1001 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
1005 if (!length
|| !report
)
1008 EnterCriticalSection(&ext
->cs
);
1009 if (length
> ext
->buffer_size
)
1011 HeapFree(GetProcessHeap(), 0, ext
->last_report
);
1012 ext
->last_report
= HeapAlloc(GetProcessHeap(), 0, length
);
1013 if (!ext
->last_report
)
1015 ERR_(hid_report
)("Failed to alloc last report\n");
1016 ext
->buffer_size
= 0;
1017 ext
->last_report_size
= 0;
1018 ext
->last_report_read
= TRUE
;
1019 LeaveCriticalSection(&ext
->cs
);
1023 ext
->buffer_size
= length
;
1026 memcpy(ext
->last_report
, report
, length
);
1027 ext
->last_report_size
= length
;
1028 ext
->last_report_read
= FALSE
;
1030 while ((entry
= RemoveHeadList(&ext
->irp_queue
)) != &ext
->irp_queue
)
1032 IO_STACK_LOCATION
*irpsp
;
1033 TRACE_(hid_report
)("Processing Request\n");
1034 irp
= CONTAINING_RECORD(entry
, IRP
, Tail
.Overlay
.ListEntry
);
1035 irpsp
= IoGetCurrentIrpStackLocation(irp
);
1036 irp
->IoStatus
.Status
= deliver_last_report(ext
,
1037 irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
,
1038 irp
->UserBuffer
, &irp
->IoStatus
.Information
);
1039 ext
->last_report_read
= TRUE
;
1040 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
1042 LeaveCriticalSection(&ext
->cs
);
1045 DWORD
check_bus_option(const UNICODE_STRING
*option
, DWORD default_value
)
1047 char buffer
[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
[sizeof(DWORD
)])];
1048 KEY_VALUE_PARTIAL_INFORMATION
*info
= (KEY_VALUE_PARTIAL_INFORMATION
*)buffer
;
1051 if (NtQueryValueKey(driver_key
, option
, KeyValuePartialInformation
, info
, sizeof(buffer
), &size
) == STATUS_SUCCESS
)
1053 if (info
->Type
== REG_DWORD
)
1054 return *(DWORD
*)info
->Data
;
1057 return default_value
;
1060 BOOL
is_xbox_gamepad(WORD vid
, WORD pid
)
1064 if (vid
!= VID_MICROSOFT
)
1067 for (i
= 0; i
< ARRAY_SIZE(XBOX_CONTROLLERS
); i
++)
1068 if (pid
== XBOX_CONTROLLERS
[i
].pid
) return TRUE
;
1073 static NTSTATUS WINAPI
driver_add_device(DRIVER_OBJECT
*driver
, DEVICE_OBJECT
*pdo
)
1077 TRACE("driver %p, pdo %p.\n", driver
, pdo
);
1079 if ((ret
= IoCreateDevice(driver
, 0, NULL
, FILE_DEVICE_BUS_EXTENDER
, 0, FALSE
, &bus_fdo
)))
1081 ERR("Failed to create FDO, status %#x.\n", ret
);
1085 IoAttachDeviceToDeviceStack(bus_fdo
, pdo
);
1088 bus_fdo
->Flags
&= ~DO_DEVICE_INITIALIZING
;
1090 return STATUS_SUCCESS
;
1093 static void WINAPI
driver_unload(DRIVER_OBJECT
*driver
)
1095 NtClose(driver_key
);
1098 NTSTATUS WINAPI
DriverEntry( DRIVER_OBJECT
*driver
, UNICODE_STRING
*path
)
1100 OBJECT_ATTRIBUTES attr
= {0};
1103 TRACE( "(%p, %s)\n", driver
, debugstr_w(path
->Buffer
) );
1105 attr
.Length
= sizeof(attr
);
1106 attr
.ObjectName
= path
;
1107 attr
.Attributes
= OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
;
1108 if ((ret
= NtOpenKey(&driver_key
, KEY_ALL_ACCESS
, &attr
)) != STATUS_SUCCESS
)
1109 ERR("Failed to open driver key, status %#x.\n", ret
);
1111 driver_obj
= driver
;
1113 driver
->MajorFunction
[IRP_MJ_PNP
] = common_pnp_dispatch
;
1114 driver
->MajorFunction
[IRP_MJ_INTERNAL_DEVICE_CONTROL
] = hid_internal_dispatch
;
1115 driver
->DriverExtension
->AddDevice
= driver_add_device
;
1116 driver
->DriverUnload
= driver_unload
;
1118 return STATUS_SUCCESS
;