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 static void remove_pending_irps(DEVICE_OBJECT
*device
)
240 struct device_extension
*ext
= device
->DeviceExtension
;
243 while ((entry
= RemoveHeadList(&ext
->irp_queue
)) != &ext
->irp_queue
)
245 IRP
*queued_irp
= CONTAINING_RECORD(entry
, IRP
, Tail
.Overlay
.s
.ListEntry
);
246 queued_irp
->IoStatus
.u
.Status
= STATUS_DELETE_PENDING
;
247 queued_irp
->IoStatus
.Information
= 0;
248 IoCompleteRequest(queued_irp
, IO_NO_INCREMENT
);
252 DEVICE_OBJECT
*bus_create_hid_device(const WCHAR
*busidW
, WORD vid
, WORD pid
,
253 WORD input
, DWORD version
, DWORD uid
, const WCHAR
*serialW
, BOOL is_gamepad
,
254 const platform_vtbl
*vtbl
, DWORD platform_data_size
)
256 static const WCHAR device_name_fmtW
[] = {'\\','D','e','v','i','c','e','\\','%','s','#','%','p',0};
257 struct device_extension
*ext
;
258 struct pnp_device
*pnp_dev
;
259 DEVICE_OBJECT
*device
;
260 UNICODE_STRING nameW
;
265 TRACE("(%s, %04x, %04x, %04x, %u, %u, %s, %u, %p, %u)\n",
266 debugstr_w(busidW
), vid
, pid
, input
, version
, uid
, debugstr_w(serialW
),
267 is_gamepad
, vtbl
, platform_data_size
);
269 if (!(pnp_dev
= HeapAlloc(GetProcessHeap(), 0, sizeof(*pnp_dev
))))
272 sprintfW(dev_name
, device_name_fmtW
, busidW
, pnp_dev
);
273 RtlInitUnicodeString(&nameW
, dev_name
);
274 length
= FIELD_OFFSET(struct device_extension
, platform_private
[platform_data_size
]);
275 status
= IoCreateDevice(driver_obj
, length
, &nameW
, 0, 0, FALSE
, &device
);
278 FIXME("failed to create device error %x\n", status
);
279 HeapFree(GetProcessHeap(), 0, pnp_dev
);
283 EnterCriticalSection(&device_list_cs
);
285 /* fill out device_extension struct */
286 ext
= (struct device_extension
*)device
->DeviceExtension
;
287 ext
->pnp_device
= pnp_dev
;
292 ext
->version
= version
;
293 ext
->index
= get_device_index(vid
, pid
, input
);
294 ext
->is_gamepad
= is_gamepad
;
295 ext
->serial
= strdupW(serialW
);
298 ext
->last_report
= NULL
;
299 ext
->last_report_size
= 0;
300 ext
->last_report_read
= TRUE
;
301 ext
->buffer_size
= 0;
303 memset(ext
->platform_private
, 0, platform_data_size
);
305 InitializeListHead(&ext
->irp_queue
);
306 InitializeCriticalSection(&ext
->cs
);
307 ext
->cs
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": cs");
309 /* add to list of pnp devices */
310 pnp_dev
->device
= device
;
311 list_add_tail(&pnp_devset
, &pnp_dev
->entry
);
313 LeaveCriticalSection(&device_list_cs
);
317 DEVICE_OBJECT
*bus_find_hid_device(const platform_vtbl
*vtbl
, void *platform_dev
)
319 struct pnp_device
*dev
;
320 DEVICE_OBJECT
*ret
= NULL
;
322 TRACE("(%p, %p)\n", vtbl
, platform_dev
);
324 EnterCriticalSection(&device_list_cs
);
325 LIST_FOR_EACH_ENTRY(dev
, &pnp_devset
, struct pnp_device
, entry
)
327 struct device_extension
*ext
= (struct device_extension
*)dev
->device
->DeviceExtension
;
328 if (ext
->vtbl
!= vtbl
) continue;
329 if (ext
->vtbl
->compare_platform_device(dev
->device
, platform_dev
) == 0)
335 LeaveCriticalSection(&device_list_cs
);
337 TRACE("returning %p\n", ret
);
341 DEVICE_OBJECT
* bus_enumerate_hid_devices(const platform_vtbl
*vtbl
, enum_func function
, void* context
)
343 struct pnp_device
*dev
, *dev_next
;
344 DEVICE_OBJECT
*ret
= NULL
;
347 TRACE("(%p)\n", vtbl
);
349 EnterCriticalSection(&device_list_cs
);
350 LIST_FOR_EACH_ENTRY_SAFE(dev
, dev_next
, &pnp_devset
, struct pnp_device
, entry
)
352 struct device_extension
*ext
= (struct device_extension
*)dev
->device
->DeviceExtension
;
353 if (ext
->vtbl
!= vtbl
) continue;
354 LeaveCriticalSection(&device_list_cs
);
355 cont
= function(dev
->device
, context
);
356 EnterCriticalSection(&device_list_cs
);
363 LeaveCriticalSection(&device_list_cs
);
367 void bus_unlink_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 EnterCriticalSection(&device_list_cs
);
373 list_remove(&pnp_device
->entry
);
374 LeaveCriticalSection(&device_list_cs
);
377 void bus_remove_hid_device(DEVICE_OBJECT
*device
)
379 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
380 struct pnp_device
*pnp_device
= ext
->pnp_device
;
382 TRACE("(%p)\n", device
);
384 ext
->cs
.DebugInfo
->Spare
[0] = 0;
385 DeleteCriticalSection(&ext
->cs
);
387 HeapFree(GetProcessHeap(), 0, ext
->serial
);
388 HeapFree(GetProcessHeap(), 0, ext
->last_report
);
389 IoDeleteDevice(device
);
391 /* pnp_device must be released after the device is gone */
392 HeapFree(GetProcessHeap(), 0, pnp_device
);
395 static NTSTATUS
build_device_relations(DEVICE_RELATIONS
**devices
)
398 struct pnp_device
*ptr
;
400 EnterCriticalSection(&device_list_cs
);
401 *devices
= ExAllocatePool(PagedPool
, offsetof(DEVICE_RELATIONS
, Objects
[list_count(&pnp_devset
)]));
405 LeaveCriticalSection(&device_list_cs
);
406 return STATUS_INSUFFICIENT_RESOURCES
;
410 LIST_FOR_EACH_ENTRY(ptr
, &pnp_devset
, struct pnp_device
, entry
)
412 (*devices
)->Objects
[i
] = ptr
->device
;
413 call_fastcall_func1(ObfReferenceObject
, ptr
->device
);
416 LeaveCriticalSection(&device_list_cs
);
417 (*devices
)->Count
= i
;
418 return STATUS_SUCCESS
;
421 static NTSTATUS
handle_IRP_MN_QUERY_DEVICE_RELATIONS(IRP
*irp
)
423 NTSTATUS status
= irp
->IoStatus
.u
.Status
;
424 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation( irp
);
426 TRACE("IRP_MN_QUERY_DEVICE_RELATIONS\n");
427 switch (irpsp
->Parameters
.QueryDeviceRelations
.Type
)
429 case EjectionRelations
:
430 case RemovalRelations
:
431 case TargetDeviceRelation
:
433 FIXME("Unhandled Device Relation %x\n",irpsp
->Parameters
.QueryDeviceRelations
.Type
);
436 status
= build_device_relations((DEVICE_RELATIONS
**)&irp
->IoStatus
.Information
);
439 FIXME("Unknown Device Relation %x\n",irpsp
->Parameters
.QueryDeviceRelations
.Type
);
446 static NTSTATUS
handle_IRP_MN_QUERY_ID(DEVICE_OBJECT
*device
, IRP
*irp
)
448 NTSTATUS status
= irp
->IoStatus
.u
.Status
;
449 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation( irp
);
450 BUS_QUERY_ID_TYPE type
= irpsp
->Parameters
.QueryId
.IdType
;
452 TRACE("(%p, %p)\n", device
, irp
);
456 case BusQueryHardwareIDs
:
457 TRACE("BusQueryHardwareIDs\n");
458 irp
->IoStatus
.Information
= (ULONG_PTR
)get_compatible_ids(device
);
460 case BusQueryCompatibleIDs
:
461 TRACE("BusQueryCompatibleIDs\n");
462 irp
->IoStatus
.Information
= (ULONG_PTR
)get_compatible_ids(device
);
464 case BusQueryDeviceID
:
465 TRACE("BusQueryDeviceID\n");
466 irp
->IoStatus
.Information
= (ULONG_PTR
)get_device_id(device
);
468 case BusQueryInstanceID
:
469 TRACE("BusQueryInstanceID\n");
470 irp
->IoStatus
.Information
= (ULONG_PTR
)get_instance_id(device
);
473 FIXME("Unhandled type %08x\n", type
);
477 status
= irp
->IoStatus
.Information
? STATUS_SUCCESS
: STATUS_NO_MEMORY
;
481 static void mouse_free_device(DEVICE_OBJECT
*device
)
485 static NTSTATUS
mouse_get_reportdescriptor(DEVICE_OBJECT
*device
, BYTE
*buffer
, DWORD length
, DWORD
*ret_length
)
487 TRACE("buffer %p, length %u.\n", buffer
, length
);
489 *ret_length
= sizeof(REPORT_HEADER
) + sizeof(REPORT_BUTTONS
) + sizeof(REPORT_TAIL
);
490 if (length
< sizeof(REPORT_HEADER
) + sizeof(REPORT_BUTTONS
) + sizeof(REPORT_TAIL
))
491 return STATUS_BUFFER_TOO_SMALL
;
493 memcpy(buffer
, REPORT_HEADER
, sizeof(REPORT_HEADER
));
494 add_button_block(buffer
+ sizeof(REPORT_HEADER
), 1, 3);
495 memcpy(buffer
+ sizeof(REPORT_HEADER
) + sizeof(REPORT_BUTTONS
), REPORT_TAIL
, sizeof(REPORT_TAIL
));
496 buffer
[IDX_HEADER_PAGE
] = HID_USAGE_PAGE_GENERIC
;
497 buffer
[IDX_HEADER_USAGE
] = HID_USAGE_GENERIC_MOUSE
;
499 return STATUS_SUCCESS
;
502 static NTSTATUS
mouse_get_string(DEVICE_OBJECT
*device
, DWORD index
, WCHAR
*buffer
, DWORD length
)
504 static const WCHAR nameW
[] = {'W','i','n','e',' ','H','I','D',' ','m','o','u','s','e',0};
505 if (index
!= HID_STRING_ID_IPRODUCT
)
506 return STATUS_NOT_IMPLEMENTED
;
507 if (length
< ARRAY_SIZE(nameW
))
508 return STATUS_BUFFER_TOO_SMALL
;
509 strcpyW(buffer
, nameW
);
510 return STATUS_SUCCESS
;
513 static NTSTATUS
mouse_begin_report_processing(DEVICE_OBJECT
*device
)
515 return STATUS_SUCCESS
;
518 static NTSTATUS
mouse_set_output_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*ret_length
)
520 FIXME("id %u, stub!\n", id
);
521 return STATUS_NOT_IMPLEMENTED
;
524 static NTSTATUS
mouse_get_feature_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*ret_length
)
526 FIXME("id %u, stub!\n", id
);
527 return STATUS_NOT_IMPLEMENTED
;
530 static NTSTATUS
mouse_set_feature_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*ret_length
)
532 FIXME("id %u, stub!\n", id
);
533 return STATUS_NOT_IMPLEMENTED
;
536 static const platform_vtbl mouse_vtbl
=
538 .free_device
= mouse_free_device
,
539 .get_reportdescriptor
= mouse_get_reportdescriptor
,
540 .get_string
= mouse_get_string
,
541 .begin_report_processing
= mouse_begin_report_processing
,
542 .set_output_report
= mouse_set_output_report
,
543 .get_feature_report
= mouse_get_feature_report
,
544 .set_feature_report
= mouse_set_feature_report
,
547 static void mouse_device_create(void)
549 static const WCHAR busidW
[] = {'W','I','N','E','M','O','U','S','E',0};
551 mouse_obj
= bus_create_hid_device(busidW
, 0, 0, -1, 0, 0, busidW
, FALSE
, &mouse_vtbl
, 0);
552 IoInvalidateDeviceRelations(bus_pdo
, BusRelations
);
555 static void keyboard_free_device(DEVICE_OBJECT
*device
)
559 static NTSTATUS
keyboard_get_reportdescriptor(DEVICE_OBJECT
*device
, BYTE
*buffer
, DWORD length
, DWORD
*ret_length
)
561 TRACE("buffer %p, length %u.\n", buffer
, length
);
563 *ret_length
= sizeof(REPORT_HEADER
) + sizeof(REPORT_BUTTONS
) + sizeof(REPORT_TAIL
);
564 if (length
< sizeof(REPORT_HEADER
) + sizeof(REPORT_BUTTONS
) + sizeof(REPORT_TAIL
))
565 return STATUS_BUFFER_TOO_SMALL
;
567 memcpy(buffer
, REPORT_HEADER
, sizeof(REPORT_HEADER
));
568 add_button_block(buffer
+ sizeof(REPORT_HEADER
), 0, 101);
569 buffer
[sizeof(REPORT_HEADER
) + IDX_BUTTON_USAGE_PAGE
] = HID_USAGE_PAGE_KEYBOARD
;
570 memcpy(buffer
+ sizeof(REPORT_HEADER
) + sizeof(REPORT_BUTTONS
), REPORT_TAIL
, sizeof(REPORT_TAIL
));
571 buffer
[IDX_HEADER_PAGE
] = HID_USAGE_PAGE_GENERIC
;
572 buffer
[IDX_HEADER_USAGE
] = HID_USAGE_GENERIC_KEYBOARD
;
574 return STATUS_SUCCESS
;
577 static NTSTATUS
keyboard_get_string(DEVICE_OBJECT
*device
, DWORD index
, WCHAR
*buffer
, DWORD length
)
579 static const WCHAR nameW
[] = {'W','i','n','e',' ','H','I','D',' ','k','e','y','b','o','a','r','d',0};
580 if (index
!= HID_STRING_ID_IPRODUCT
)
581 return STATUS_NOT_IMPLEMENTED
;
582 if (length
< ARRAY_SIZE(nameW
))
583 return STATUS_BUFFER_TOO_SMALL
;
584 strcpyW(buffer
, nameW
);
585 return STATUS_SUCCESS
;
588 static NTSTATUS
keyboard_begin_report_processing(DEVICE_OBJECT
*device
)
590 return STATUS_SUCCESS
;
593 static NTSTATUS
keyboard_set_output_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*ret_length
)
595 FIXME("id %u, stub!\n", id
);
596 return STATUS_NOT_IMPLEMENTED
;
599 static NTSTATUS
keyboard_get_feature_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*ret_length
)
601 FIXME("id %u, stub!\n", id
);
602 return STATUS_NOT_IMPLEMENTED
;
605 static NTSTATUS
keyboard_set_feature_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*ret_length
)
607 FIXME("id %u, stub!\n", id
);
608 return STATUS_NOT_IMPLEMENTED
;
611 static const platform_vtbl keyboard_vtbl
=
613 .free_device
= keyboard_free_device
,
614 .get_reportdescriptor
= keyboard_get_reportdescriptor
,
615 .get_string
= keyboard_get_string
,
616 .begin_report_processing
= keyboard_begin_report_processing
,
617 .set_output_report
= keyboard_set_output_report
,
618 .get_feature_report
= keyboard_get_feature_report
,
619 .set_feature_report
= keyboard_set_feature_report
,
622 static void keyboard_device_create(void)
624 static const WCHAR busidW
[] = {'W','I','N','E','K','E','Y','B','O','A','R','D',0};
626 keyboard_obj
= bus_create_hid_device(busidW
, 0, 0, -1, 0, 0, busidW
, FALSE
, &keyboard_vtbl
, 0);
627 IoInvalidateDeviceRelations(bus_pdo
, BusRelations
);
630 static NTSTATUS
fdo_pnp_dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
632 static const WCHAR SDL_enabledW
[] = {'E','n','a','b','l','e',' ','S','D','L',0};
633 static const UNICODE_STRING SDL_enabled
= {sizeof(SDL_enabledW
) - sizeof(WCHAR
), sizeof(SDL_enabledW
), (WCHAR
*)SDL_enabledW
};
634 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation(irp
);
637 switch (irpsp
->MinorFunction
)
639 case IRP_MN_QUERY_DEVICE_RELATIONS
:
640 irp
->IoStatus
.u
.Status
= handle_IRP_MN_QUERY_DEVICE_RELATIONS(irp
);
642 case IRP_MN_START_DEVICE
:
643 mouse_device_create();
644 keyboard_device_create();
646 if (check_bus_option(&SDL_enabled
, 1))
648 if (sdl_driver_init() == STATUS_SUCCESS
)
650 irp
->IoStatus
.u
.Status
= STATUS_SUCCESS
;
656 irp
->IoStatus
.u
.Status
= STATUS_SUCCESS
;
658 case IRP_MN_SURPRISE_REMOVAL
:
659 irp
->IoStatus
.u
.Status
= STATUS_SUCCESS
;
661 case IRP_MN_REMOVE_DEVICE
:
662 udev_driver_unload();
663 iohid_driver_unload();
666 irp
->IoStatus
.u
.Status
= STATUS_SUCCESS
;
667 IoSkipCurrentIrpStackLocation(irp
);
668 ret
= IoCallDriver(bus_pdo
, irp
);
669 IoDetachDevice(bus_pdo
);
670 IoDeleteDevice(device
);
673 FIXME("Unhandled minor function %#x.\n", irpsp
->MinorFunction
);
676 IoSkipCurrentIrpStackLocation(irp
);
677 return IoCallDriver(bus_pdo
, irp
);
680 static NTSTATUS
pdo_pnp_dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
682 struct device_extension
*ext
= device
->DeviceExtension
;
683 NTSTATUS status
= irp
->IoStatus
.u
.Status
;
684 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation(irp
);
686 TRACE("device %p, irp %p, minor function %#x.\n", device
, irp
, irpsp
->MinorFunction
);
688 switch (irpsp
->MinorFunction
)
690 case IRP_MN_QUERY_ID
:
691 status
= handle_IRP_MN_QUERY_ID(device
, irp
);
694 case IRP_MN_QUERY_CAPABILITIES
:
695 status
= STATUS_SUCCESS
;
698 case IRP_MN_START_DEVICE
:
699 status
= STATUS_SUCCESS
;
702 case IRP_MN_SURPRISE_REMOVAL
:
703 EnterCriticalSection(&ext
->cs
);
704 remove_pending_irps(device
);
706 LeaveCriticalSection(&ext
->cs
);
709 case IRP_MN_REMOVE_DEVICE
:
711 struct pnp_device
*pnp_device
= ext
->pnp_device
;
713 remove_pending_irps(device
);
715 ext
->vtbl
->free_device(device
);
717 ext
->cs
.DebugInfo
->Spare
[0] = 0;
718 DeleteCriticalSection(&ext
->cs
);
720 HeapFree(GetProcessHeap(), 0, ext
->serial
);
721 HeapFree(GetProcessHeap(), 0, ext
->last_report
);
723 irp
->IoStatus
.u
.Status
= STATUS_SUCCESS
;
724 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
726 IoDeleteDevice(device
);
728 /* pnp_device must be released after the device is gone */
729 HeapFree(GetProcessHeap(), 0, pnp_device
);
731 return STATUS_SUCCESS
;
735 FIXME("Unhandled function %08x\n", irpsp
->MinorFunction
);
738 case IRP_MN_QUERY_DEVICE_RELATIONS
:
742 irp
->IoStatus
.u
.Status
= status
;
743 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
747 static NTSTATUS WINAPI
common_pnp_dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
749 if (device
== bus_fdo
)
750 return fdo_pnp_dispatch(device
, irp
);
751 return pdo_pnp_dispatch(device
, irp
);
754 static NTSTATUS
deliver_last_report(struct device_extension
*ext
, DWORD buffer_length
, BYTE
* buffer
, ULONG_PTR
*out_length
)
756 if (buffer_length
< ext
->last_report_size
)
759 return STATUS_BUFFER_TOO_SMALL
;
763 if (ext
->last_report
)
764 memcpy(buffer
, ext
->last_report
, ext
->last_report_size
);
765 *out_length
= ext
->last_report_size
;
766 return STATUS_SUCCESS
;
770 static NTSTATUS
hid_get_native_string(DEVICE_OBJECT
*device
, DWORD index
, WCHAR
*buffer
, DWORD length
)
772 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
773 const struct product_desc
*vendor_products
;
774 unsigned int i
, vendor_products_size
= 0;
776 if (ext
->vid
== VID_MICROSOFT
)
778 vendor_products
= XBOX_CONTROLLERS
;
779 vendor_products_size
= ARRAY_SIZE(XBOX_CONTROLLERS
);
782 for (i
= 0; i
< vendor_products_size
; i
++)
784 if (ext
->pid
== vendor_products
[i
].pid
)
788 if (i
>= vendor_products_size
)
789 return STATUS_UNSUCCESSFUL
;
793 case HID_STRING_ID_IPRODUCT
:
794 if (vendor_products
[i
].product
)
796 strcpyW(buffer
, vendor_products
[i
].product
);
797 return STATUS_SUCCESS
;
800 case HID_STRING_ID_IMANUFACTURER
:
801 if (vendor_products
[i
].manufacturer
)
803 strcpyW(buffer
, vendor_products
[i
].manufacturer
);
804 return STATUS_SUCCESS
;
807 case HID_STRING_ID_ISERIALNUMBER
:
808 if (vendor_products
[i
].serialnumber
)
810 strcpyW(buffer
, vendor_products
[i
].serialnumber
);
811 return STATUS_SUCCESS
;
816 return STATUS_UNSUCCESSFUL
;
819 static NTSTATUS WINAPI
hid_internal_dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
821 NTSTATUS status
= irp
->IoStatus
.u
.Status
;
822 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation(irp
);
823 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
825 TRACE("(%p, %p)\n", device
, irp
);
827 if (device
== bus_fdo
)
829 IoSkipCurrentIrpStackLocation(irp
);
830 return IoCallDriver(bus_pdo
, irp
);
833 EnterCriticalSection(&ext
->cs
);
837 LeaveCriticalSection(&ext
->cs
);
838 irp
->IoStatus
.u
.Status
= STATUS_DELETE_PENDING
;
839 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
840 return STATUS_DELETE_PENDING
;
843 switch (irpsp
->Parameters
.DeviceIoControl
.IoControlCode
)
845 case IOCTL_HID_GET_DEVICE_ATTRIBUTES
:
847 HID_DEVICE_ATTRIBUTES
*attr
= (HID_DEVICE_ATTRIBUTES
*)irp
->UserBuffer
;
848 TRACE("IOCTL_HID_GET_DEVICE_ATTRIBUTES\n");
850 if (irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(*attr
))
852 irp
->IoStatus
.u
.Status
= status
= STATUS_BUFFER_TOO_SMALL
;
856 memset(attr
, 0, sizeof(*attr
));
857 attr
->Size
= sizeof(*attr
);
858 attr
->VendorID
= ext
->vid
;
859 attr
->ProductID
= ext
->pid
;
860 attr
->VersionNumber
= ext
->version
;
862 irp
->IoStatus
.u
.Status
= status
= STATUS_SUCCESS
;
863 irp
->IoStatus
.Information
= sizeof(*attr
);
866 case IOCTL_HID_GET_DEVICE_DESCRIPTOR
:
868 HID_DESCRIPTOR
*descriptor
= (HID_DESCRIPTOR
*)irp
->UserBuffer
;
870 TRACE("IOCTL_HID_GET_DEVICE_DESCRIPTOR\n");
872 if (irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(*descriptor
))
874 irp
->IoStatus
.u
.Status
= status
= STATUS_BUFFER_TOO_SMALL
;
878 status
= ext
->vtbl
->get_reportdescriptor(device
, NULL
, 0, &length
);
879 if (status
!= STATUS_SUCCESS
&& status
!= STATUS_BUFFER_TOO_SMALL
)
881 WARN("Failed to get platform report descriptor length\n");
882 irp
->IoStatus
.u
.Status
= status
;
886 memset(descriptor
, 0, sizeof(*descriptor
));
887 descriptor
->bLength
= sizeof(*descriptor
);
888 descriptor
->bDescriptorType
= HID_HID_DESCRIPTOR_TYPE
;
889 descriptor
->bcdHID
= HID_REVISION
;
890 descriptor
->bCountry
= 0;
891 descriptor
->bNumDescriptors
= 1;
892 descriptor
->DescriptorList
[0].bReportType
= HID_REPORT_DESCRIPTOR_TYPE
;
893 descriptor
->DescriptorList
[0].wReportLength
= length
;
895 irp
->IoStatus
.u
.Status
= status
= STATUS_SUCCESS
;
896 irp
->IoStatus
.Information
= sizeof(*descriptor
);
899 case IOCTL_HID_GET_REPORT_DESCRIPTOR
:
901 DWORD length
= irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
;
902 TRACE("IOCTL_HID_GET_REPORT_DESCRIPTOR\n");
904 irp
->IoStatus
.u
.Status
= status
= ext
->vtbl
->get_reportdescriptor(device
, irp
->UserBuffer
, length
, &length
);
905 irp
->IoStatus
.Information
= length
;
908 case IOCTL_HID_GET_STRING
:
910 DWORD length
= irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
/ sizeof(WCHAR
);
911 DWORD index
= (ULONG_PTR
)irpsp
->Parameters
.DeviceIoControl
.Type3InputBuffer
;
912 TRACE("IOCTL_HID_GET_STRING[%08x]\n", index
);
914 irp
->IoStatus
.u
.Status
= status
= hid_get_native_string(device
, index
, (WCHAR
*)irp
->UserBuffer
, length
);
915 if (status
!= STATUS_SUCCESS
)
916 irp
->IoStatus
.u
.Status
= status
= ext
->vtbl
->get_string(device
, index
, (WCHAR
*)irp
->UserBuffer
, length
);
917 if (status
== STATUS_SUCCESS
)
918 irp
->IoStatus
.Information
= (strlenW((WCHAR
*)irp
->UserBuffer
) + 1) * sizeof(WCHAR
);
921 case IOCTL_HID_GET_INPUT_REPORT
:
923 HID_XFER_PACKET
*packet
= (HID_XFER_PACKET
*)(irp
->UserBuffer
);
924 TRACE_(hid_report
)("IOCTL_HID_GET_INPUT_REPORT\n");
925 status
= ext
->vtbl
->begin_report_processing(device
);
926 if (status
!= STATUS_SUCCESS
)
928 irp
->IoStatus
.u
.Status
= status
;
929 LeaveCriticalSection(&ext
->cs
);
933 irp
->IoStatus
.u
.Status
= status
= deliver_last_report(ext
,
934 packet
->reportBufferLen
, packet
->reportBuffer
,
935 &irp
->IoStatus
.Information
);
937 if (status
== STATUS_SUCCESS
)
938 packet
->reportBufferLen
= irp
->IoStatus
.Information
;
941 case IOCTL_HID_READ_REPORT
:
943 TRACE_(hid_report
)("IOCTL_HID_READ_REPORT\n");
944 status
= ext
->vtbl
->begin_report_processing(device
);
945 if (status
!= STATUS_SUCCESS
)
947 irp
->IoStatus
.u
.Status
= status
;
948 LeaveCriticalSection(&ext
->cs
);
951 if (!ext
->last_report_read
)
953 irp
->IoStatus
.u
.Status
= status
= deliver_last_report(ext
,
954 irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
,
955 irp
->UserBuffer
, &irp
->IoStatus
.Information
);
956 ext
->last_report_read
= TRUE
;
960 InsertTailList(&ext
->irp_queue
, &irp
->Tail
.Overlay
.s
.ListEntry
);
961 status
= STATUS_PENDING
;
965 case IOCTL_HID_SET_OUTPUT_REPORT
:
966 case IOCTL_HID_WRITE_REPORT
:
968 HID_XFER_PACKET
*packet
= (HID_XFER_PACKET
*)(irp
->UserBuffer
);
969 TRACE_(hid_report
)("IOCTL_HID_WRITE_REPORT / IOCTL_HID_SET_OUTPUT_REPORT\n");
970 irp
->IoStatus
.u
.Status
= status
= ext
->vtbl
->set_output_report(
971 device
, packet
->reportId
, packet
->reportBuffer
,
972 packet
->reportBufferLen
, &irp
->IoStatus
.Information
);
975 case IOCTL_HID_GET_FEATURE
:
977 HID_XFER_PACKET
*packet
= (HID_XFER_PACKET
*)(irp
->UserBuffer
);
978 TRACE_(hid_report
)("IOCTL_HID_GET_FEATURE\n");
979 irp
->IoStatus
.u
.Status
= status
= ext
->vtbl
->get_feature_report(
980 device
, packet
->reportId
, packet
->reportBuffer
,
981 packet
->reportBufferLen
, &irp
->IoStatus
.Information
);
982 packet
->reportBufferLen
= irp
->IoStatus
.Information
;
985 case IOCTL_HID_SET_FEATURE
:
987 HID_XFER_PACKET
*packet
= (HID_XFER_PACKET
*)(irp
->UserBuffer
);
988 TRACE_(hid_report
)("IOCTL_HID_SET_FEATURE\n");
989 irp
->IoStatus
.u
.Status
= status
= ext
->vtbl
->set_feature_report(
990 device
, packet
->reportId
, packet
->reportBuffer
,
991 packet
->reportBufferLen
, &irp
->IoStatus
.Information
);
996 ULONG code
= irpsp
->Parameters
.DeviceIoControl
.IoControlCode
;
997 FIXME("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
998 code
, code
>> 16, (code
>> 14) & 3, (code
>> 2) & 0xfff, code
& 3);
1003 LeaveCriticalSection(&ext
->cs
);
1005 if (status
!= STATUS_PENDING
)
1006 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
1011 void process_hid_report(DEVICE_OBJECT
*device
, BYTE
*report
, DWORD length
)
1013 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
1017 if (!length
|| !report
)
1020 EnterCriticalSection(&ext
->cs
);
1021 if (length
> ext
->buffer_size
)
1023 HeapFree(GetProcessHeap(), 0, ext
->last_report
);
1024 ext
->last_report
= HeapAlloc(GetProcessHeap(), 0, length
);
1025 if (!ext
->last_report
)
1027 ERR_(hid_report
)("Failed to alloc last report\n");
1028 ext
->buffer_size
= 0;
1029 ext
->last_report_size
= 0;
1030 ext
->last_report_read
= TRUE
;
1031 LeaveCriticalSection(&ext
->cs
);
1035 ext
->buffer_size
= length
;
1038 memcpy(ext
->last_report
, report
, length
);
1039 ext
->last_report_size
= length
;
1040 ext
->last_report_read
= FALSE
;
1042 while ((entry
= RemoveHeadList(&ext
->irp_queue
)) != &ext
->irp_queue
)
1044 IO_STACK_LOCATION
*irpsp
;
1045 TRACE_(hid_report
)("Processing Request\n");
1046 irp
= CONTAINING_RECORD(entry
, IRP
, Tail
.Overlay
.s
.ListEntry
);
1047 irpsp
= IoGetCurrentIrpStackLocation(irp
);
1048 irp
->IoStatus
.u
.Status
= deliver_last_report(ext
,
1049 irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
,
1050 irp
->UserBuffer
, &irp
->IoStatus
.Information
);
1051 ext
->last_report_read
= TRUE
;
1052 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
1054 LeaveCriticalSection(&ext
->cs
);
1057 DWORD
check_bus_option(const UNICODE_STRING
*option
, DWORD default_value
)
1059 char buffer
[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
[sizeof(DWORD
)])];
1060 KEY_VALUE_PARTIAL_INFORMATION
*info
= (KEY_VALUE_PARTIAL_INFORMATION
*)buffer
;
1063 if (NtQueryValueKey(driver_key
, option
, KeyValuePartialInformation
, info
, sizeof(buffer
), &size
) == STATUS_SUCCESS
)
1065 if (info
->Type
== REG_DWORD
)
1066 return *(DWORD
*)info
->Data
;
1069 return default_value
;
1072 BOOL
is_xbox_gamepad(WORD vid
, WORD pid
)
1076 if (vid
!= VID_MICROSOFT
)
1079 for (i
= 0; i
< ARRAY_SIZE(XBOX_CONTROLLERS
); i
++)
1080 if (pid
== XBOX_CONTROLLERS
[i
].pid
) return TRUE
;
1085 static NTSTATUS WINAPI
driver_add_device(DRIVER_OBJECT
*driver
, DEVICE_OBJECT
*pdo
)
1089 TRACE("driver %p, pdo %p.\n", driver
, pdo
);
1091 if ((ret
= IoCreateDevice(driver
, 0, NULL
, FILE_DEVICE_BUS_EXTENDER
, 0, FALSE
, &bus_fdo
)))
1093 ERR("Failed to create FDO, status %#x.\n", ret
);
1097 IoAttachDeviceToDeviceStack(bus_fdo
, pdo
);
1100 bus_fdo
->Flags
&= ~DO_DEVICE_INITIALIZING
;
1102 return STATUS_SUCCESS
;
1105 static void WINAPI
driver_unload(DRIVER_OBJECT
*driver
)
1107 NtClose(driver_key
);
1110 NTSTATUS WINAPI
DriverEntry( DRIVER_OBJECT
*driver
, UNICODE_STRING
*path
)
1112 OBJECT_ATTRIBUTES attr
= {0};
1115 TRACE( "(%p, %s)\n", driver
, debugstr_w(path
->Buffer
) );
1117 attr
.Length
= sizeof(attr
);
1118 attr
.ObjectName
= path
;
1119 attr
.Attributes
= OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
;
1120 if ((ret
= NtOpenKey(&driver_key
, KEY_ALL_ACCESS
, &attr
)) != STATUS_SUCCESS
)
1121 ERR("Failed to open driver key, status %#x.\n", ret
);
1123 driver_obj
= driver
;
1125 driver
->MajorFunction
[IRP_MJ_PNP
] = common_pnp_dispatch
;
1126 driver
->MajorFunction
[IRP_MJ_INTERNAL_DEVICE_CONTROL
] = hid_internal_dispatch
;
1127 driver
->DriverExtension
->AddDevice
= driver_add_device
;
1128 driver
->DriverUnload
= driver_unload
;
1130 return STATUS_SUCCESS
;