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
;
101 /* The root-enumerated device stack. */
102 DEVICE_OBJECT
*bus_pdo
;
103 static DEVICE_OBJECT
*bus_fdo
;
110 DEVICE_OBJECT
*device
;
113 struct device_extension
115 struct pnp_device
*pnp_device
;
117 WORD vid
, pid
, input
;
118 DWORD uid
, version
, index
;
121 const WCHAR
*busid
; /* Expected to be a static constant */
123 const platform_vtbl
*vtbl
;
126 DWORD last_report_size
;
127 BOOL last_report_read
;
129 LIST_ENTRY irp_queue
;
130 CRITICAL_SECTION report_cs
;
132 BYTE platform_private
[1];
135 static CRITICAL_SECTION device_list_cs
;
136 static CRITICAL_SECTION_DEBUG critsect_debug
=
138 0, 0, &device_list_cs
,
139 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
140 0, 0, { (DWORD_PTR
)(__FILE__
": device_list_cs") }
142 static CRITICAL_SECTION device_list_cs
= { &critsect_debug
, -1, 0, 0, 0, 0 };
144 static struct list pnp_devset
= LIST_INIT(pnp_devset
);
146 static const WCHAR zero_serialW
[]= {'0','0','0','0',0};
147 static const WCHAR miW
[] = {'M','I',0};
148 static const WCHAR igW
[] = {'I','G',0};
150 static inline WCHAR
*strdupW(const WCHAR
*src
)
153 if (!src
) return NULL
;
154 dst
= HeapAlloc(GetProcessHeap(), 0, (strlenW(src
) + 1)*sizeof(WCHAR
));
155 if (dst
) strcpyW(dst
, src
);
159 void *get_platform_private(DEVICE_OBJECT
*device
)
161 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
162 return ext
->platform_private
;
165 static DWORD
get_device_index(WORD vid
, WORD pid
, WORD input
)
167 struct pnp_device
*ptr
;
170 LIST_FOR_EACH_ENTRY(ptr
, &pnp_devset
, struct pnp_device
, entry
)
172 struct device_extension
*ext
= (struct device_extension
*)ptr
->device
->DeviceExtension
;
173 if (ext
->vid
== vid
&& ext
->pid
== pid
&& ext
->input
== input
)
174 index
= max(ext
->index
+ 1, index
);
180 static WCHAR
*get_instance_id(DEVICE_OBJECT
*device
)
182 static const WCHAR formatW
[] = {'%','i','&','%','s','&','%','x','&','%','i',0};
183 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
184 const WCHAR
*serial
= ext
->serial
? ext
->serial
: zero_serialW
;
185 DWORD len
= strlenW(serial
) + 33;
188 if ((dst
= ExAllocatePool(PagedPool
, len
* sizeof(WCHAR
))))
189 sprintfW(dst
, formatW
, ext
->version
, serial
, ext
->uid
, ext
->index
);
194 static WCHAR
*get_device_id(DEVICE_OBJECT
*device
)
196 static const WCHAR formatW
[] = {'%','s','\\','v','i','d','_','%','0','4','x',
197 '&','p','i','d','_','%','0','4','x',0};
198 static const WCHAR format_inputW
[] = {'%','s','\\','v','i','d','_','%','0','4','x',
199 '&','p','i','d','_','%','0','4','x','&','%','s','_','%','0','2','i',0};
200 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
201 DWORD len
= strlenW(ext
->busid
) + 34;
204 if ((dst
= ExAllocatePool(PagedPool
, len
* sizeof(WCHAR
))))
206 if (ext
->input
== (WORD
)-1)
208 sprintfW(dst
, formatW
, ext
->busid
, ext
->vid
, ext
->pid
);
212 sprintfW(dst
, format_inputW
, ext
->busid
, ext
->vid
, ext
->pid
,
213 ext
->is_gamepad
? igW
: miW
, ext
->input
);
220 static WCHAR
*get_compatible_ids(DEVICE_OBJECT
*device
)
222 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
225 if ((dst
= ExAllocatePool(PagedPool
, (strlenW(ext
->busid
) + 2) * sizeof(WCHAR
))))
227 strcpyW(dst
, ext
->busid
);
228 dst
[strlenW(dst
) + 1] = 0;
234 DEVICE_OBJECT
*bus_create_hid_device(const WCHAR
*busidW
, WORD vid
, WORD pid
,
235 WORD input
, DWORD version
, DWORD uid
, const WCHAR
*serialW
, BOOL is_gamepad
,
236 const platform_vtbl
*vtbl
, DWORD platform_data_size
)
238 static const WCHAR device_name_fmtW
[] = {'\\','D','e','v','i','c','e','\\','%','s','#','%','p',0};
239 struct device_extension
*ext
;
240 struct pnp_device
*pnp_dev
;
241 DEVICE_OBJECT
*device
;
242 UNICODE_STRING nameW
;
247 TRACE("(%s, %04x, %04x, %04x, %u, %u, %s, %u, %p, %u)\n",
248 debugstr_w(busidW
), vid
, pid
, input
, version
, uid
, debugstr_w(serialW
),
249 is_gamepad
, vtbl
, platform_data_size
);
251 if (!(pnp_dev
= HeapAlloc(GetProcessHeap(), 0, sizeof(*pnp_dev
))))
254 sprintfW(dev_name
, device_name_fmtW
, busidW
, pnp_dev
);
255 RtlInitUnicodeString(&nameW
, dev_name
);
256 length
= FIELD_OFFSET(struct device_extension
, platform_private
[platform_data_size
]);
257 status
= IoCreateDevice(driver_obj
, length
, &nameW
, 0, 0, FALSE
, &device
);
260 FIXME("failed to create device error %x\n", status
);
261 HeapFree(GetProcessHeap(), 0, pnp_dev
);
265 EnterCriticalSection(&device_list_cs
);
267 /* fill out device_extension struct */
268 ext
= (struct device_extension
*)device
->DeviceExtension
;
269 ext
->pnp_device
= pnp_dev
;
274 ext
->version
= version
;
275 ext
->index
= get_device_index(vid
, pid
, input
);
276 ext
->is_gamepad
= is_gamepad
;
277 ext
->serial
= strdupW(serialW
);
280 ext
->last_report
= NULL
;
281 ext
->last_report_size
= 0;
282 ext
->last_report_read
= TRUE
;
283 ext
->buffer_size
= 0;
285 memset(ext
->platform_private
, 0, platform_data_size
);
287 InitializeListHead(&ext
->irp_queue
);
288 InitializeCriticalSection(&ext
->report_cs
);
289 ext
->report_cs
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": report_cs");
291 /* add to list of pnp devices */
292 pnp_dev
->device
= device
;
293 list_add_tail(&pnp_devset
, &pnp_dev
->entry
);
295 LeaveCriticalSection(&device_list_cs
);
299 DEVICE_OBJECT
*bus_find_hid_device(const platform_vtbl
*vtbl
, void *platform_dev
)
301 struct pnp_device
*dev
;
302 DEVICE_OBJECT
*ret
= NULL
;
304 TRACE("(%p, %p)\n", vtbl
, platform_dev
);
306 EnterCriticalSection(&device_list_cs
);
307 LIST_FOR_EACH_ENTRY(dev
, &pnp_devset
, struct pnp_device
, entry
)
309 struct device_extension
*ext
= (struct device_extension
*)dev
->device
->DeviceExtension
;
310 if (ext
->vtbl
!= vtbl
) continue;
311 if (ext
->vtbl
->compare_platform_device(dev
->device
, platform_dev
) == 0)
317 LeaveCriticalSection(&device_list_cs
);
319 TRACE("returning %p\n", ret
);
323 DEVICE_OBJECT
* bus_enumerate_hid_devices(const platform_vtbl
*vtbl
, enum_func function
, void* context
)
325 struct pnp_device
*dev
, *dev_next
;
326 DEVICE_OBJECT
*ret
= NULL
;
329 TRACE("(%p)\n", vtbl
);
331 EnterCriticalSection(&device_list_cs
);
332 LIST_FOR_EACH_ENTRY_SAFE(dev
, dev_next
, &pnp_devset
, struct pnp_device
, entry
)
334 struct device_extension
*ext
= (struct device_extension
*)dev
->device
->DeviceExtension
;
335 if (ext
->vtbl
!= vtbl
) continue;
336 LeaveCriticalSection(&device_list_cs
);
337 cont
= function(dev
->device
, context
);
338 EnterCriticalSection(&device_list_cs
);
345 LeaveCriticalSection(&device_list_cs
);
349 void bus_unlink_hid_device(DEVICE_OBJECT
*device
)
351 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
352 struct pnp_device
*pnp_device
= ext
->pnp_device
;
354 EnterCriticalSection(&device_list_cs
);
355 list_remove(&pnp_device
->entry
);
356 LeaveCriticalSection(&device_list_cs
);
359 void bus_remove_hid_device(DEVICE_OBJECT
*device
)
361 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
362 struct pnp_device
*pnp_device
= ext
->pnp_device
;
366 TRACE("(%p)\n", device
);
368 /* Cancel pending IRPs */
369 EnterCriticalSection(&ext
->report_cs
);
370 while ((entry
= RemoveHeadList(&ext
->irp_queue
)) != &ext
->irp_queue
)
372 irp
= CONTAINING_RECORD(entry
, IRP
, Tail
.Overlay
.s
.ListEntry
);
373 irp
->IoStatus
.u
.Status
= STATUS_DELETE_PENDING
;
374 irp
->IoStatus
.Information
= 0;
375 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
377 LeaveCriticalSection(&ext
->report_cs
);
379 ext
->report_cs
.DebugInfo
->Spare
[0] = 0;
380 DeleteCriticalSection(&ext
->report_cs
);
382 HeapFree(GetProcessHeap(), 0, ext
->serial
);
383 HeapFree(GetProcessHeap(), 0, ext
->last_report
);
384 IoDeleteDevice(device
);
386 /* pnp_device must be released after the device is gone */
387 HeapFree(GetProcessHeap(), 0, pnp_device
);
390 static NTSTATUS
build_device_relations(DEVICE_RELATIONS
**devices
)
393 struct pnp_device
*ptr
;
395 EnterCriticalSection(&device_list_cs
);
396 *devices
= ExAllocatePool(PagedPool
, offsetof(DEVICE_RELATIONS
, Objects
[list_count(&pnp_devset
)]));
400 LeaveCriticalSection(&device_list_cs
);
401 return STATUS_INSUFFICIENT_RESOURCES
;
405 LIST_FOR_EACH_ENTRY(ptr
, &pnp_devset
, struct pnp_device
, entry
)
407 (*devices
)->Objects
[i
] = ptr
->device
;
408 call_fastcall_func1(ObfReferenceObject
, ptr
->device
);
411 LeaveCriticalSection(&device_list_cs
);
412 (*devices
)->Count
= i
;
413 return STATUS_SUCCESS
;
416 static NTSTATUS
handle_IRP_MN_QUERY_DEVICE_RELATIONS(IRP
*irp
)
418 NTSTATUS status
= irp
->IoStatus
.u
.Status
;
419 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation( irp
);
421 TRACE("IRP_MN_QUERY_DEVICE_RELATIONS\n");
422 switch (irpsp
->Parameters
.QueryDeviceRelations
.Type
)
424 case EjectionRelations
:
425 case RemovalRelations
:
426 case TargetDeviceRelation
:
428 FIXME("Unhandled Device Relation %x\n",irpsp
->Parameters
.QueryDeviceRelations
.Type
);
431 status
= build_device_relations((DEVICE_RELATIONS
**)&irp
->IoStatus
.Information
);
434 FIXME("Unknown Device Relation %x\n",irpsp
->Parameters
.QueryDeviceRelations
.Type
);
441 static NTSTATUS
handle_IRP_MN_QUERY_ID(DEVICE_OBJECT
*device
, IRP
*irp
)
443 NTSTATUS status
= irp
->IoStatus
.u
.Status
;
444 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation( irp
);
445 BUS_QUERY_ID_TYPE type
= irpsp
->Parameters
.QueryId
.IdType
;
447 TRACE("(%p, %p)\n", device
, irp
);
451 case BusQueryHardwareIDs
:
452 TRACE("BusQueryHardwareIDs\n");
453 irp
->IoStatus
.Information
= (ULONG_PTR
)get_compatible_ids(device
);
455 case BusQueryCompatibleIDs
:
456 TRACE("BusQueryCompatibleIDs\n");
457 irp
->IoStatus
.Information
= (ULONG_PTR
)get_compatible_ids(device
);
459 case BusQueryDeviceID
:
460 TRACE("BusQueryDeviceID\n");
461 irp
->IoStatus
.Information
= (ULONG_PTR
)get_device_id(device
);
463 case BusQueryInstanceID
:
464 TRACE("BusQueryInstanceID\n");
465 irp
->IoStatus
.Information
= (ULONG_PTR
)get_instance_id(device
);
468 FIXME("Unhandled type %08x\n", type
);
472 status
= irp
->IoStatus
.Information
? STATUS_SUCCESS
: STATUS_NO_MEMORY
;
476 static NTSTATUS
mouse_get_reportdescriptor(DEVICE_OBJECT
*device
, BYTE
*buffer
, DWORD length
, DWORD
*ret_length
)
478 TRACE("buffer %p, length %u.\n", buffer
, length
);
480 *ret_length
= sizeof(REPORT_HEADER
) + sizeof(REPORT_TAIL
);
481 if (length
< sizeof(REPORT_HEADER
) + sizeof(REPORT_TAIL
))
482 return STATUS_BUFFER_TOO_SMALL
;
484 memcpy(buffer
, REPORT_HEADER
, sizeof(REPORT_HEADER
));
485 memcpy(buffer
+ sizeof(REPORT_HEADER
), REPORT_TAIL
, sizeof(REPORT_TAIL
));
486 buffer
[IDX_HEADER_PAGE
] = HID_USAGE_PAGE_GENERIC
;
487 buffer
[IDX_HEADER_USAGE
] = HID_USAGE_GENERIC_MOUSE
;
489 return STATUS_SUCCESS
;
492 static NTSTATUS
mouse_get_string(DEVICE_OBJECT
*device
, DWORD index
, WCHAR
*buffer
, DWORD length
)
494 static const WCHAR nameW
[] = {'W','i','n','e',' ','H','I','D',' ','m','o','u','s','e',0};
495 if (index
!= HID_STRING_ID_IPRODUCT
)
496 return STATUS_NOT_IMPLEMENTED
;
497 if (length
< ARRAY_SIZE(nameW
))
498 return STATUS_BUFFER_TOO_SMALL
;
499 strcpyW(buffer
, nameW
);
500 return STATUS_SUCCESS
;
503 static NTSTATUS
mouse_begin_report_processing(DEVICE_OBJECT
*device
)
505 return STATUS_SUCCESS
;
508 static NTSTATUS
mouse_set_output_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*ret_length
)
510 FIXME("id %u, stub!\n", id
);
511 return STATUS_NOT_IMPLEMENTED
;
514 static NTSTATUS
mouse_get_feature_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*ret_length
)
516 FIXME("id %u, stub!\n", id
);
517 return STATUS_NOT_IMPLEMENTED
;
520 static NTSTATUS
mouse_set_feature_report(DEVICE_OBJECT
*device
, UCHAR id
, BYTE
*report
, DWORD length
, ULONG_PTR
*ret_length
)
522 FIXME("id %u, stub!\n", id
);
523 return STATUS_NOT_IMPLEMENTED
;
526 static const platform_vtbl mouse_vtbl
=
528 .get_reportdescriptor
= mouse_get_reportdescriptor
,
529 .get_string
= mouse_get_string
,
530 .begin_report_processing
= mouse_begin_report_processing
,
531 .set_output_report
= mouse_set_output_report
,
532 .get_feature_report
= mouse_get_feature_report
,
533 .set_feature_report
= mouse_set_feature_report
,
536 static void mouse_device_create(void)
538 static const WCHAR busidW
[] = {'W','I','N','E','M','O','U','S','E',0};
540 mouse_obj
= bus_create_hid_device(busidW
, 0, 0, -1, 0, 0, busidW
, FALSE
, &mouse_vtbl
, 0);
541 IoInvalidateDeviceRelations(bus_pdo
, BusRelations
);
544 static NTSTATUS
fdo_pnp_dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
546 static const WCHAR SDL_enabledW
[] = {'E','n','a','b','l','e',' ','S','D','L',0};
547 static const UNICODE_STRING SDL_enabled
= {sizeof(SDL_enabledW
) - sizeof(WCHAR
), sizeof(SDL_enabledW
), (WCHAR
*)SDL_enabledW
};
548 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation(irp
);
551 switch (irpsp
->MinorFunction
)
553 case IRP_MN_QUERY_DEVICE_RELATIONS
:
554 irp
->IoStatus
.u
.Status
= handle_IRP_MN_QUERY_DEVICE_RELATIONS(irp
);
556 case IRP_MN_START_DEVICE
:
557 mouse_device_create();
559 if (check_bus_option(&SDL_enabled
, 1))
561 if (sdl_driver_init() == STATUS_SUCCESS
)
563 irp
->IoStatus
.u
.Status
= STATUS_SUCCESS
;
569 irp
->IoStatus
.u
.Status
= STATUS_SUCCESS
;
571 case IRP_MN_SURPRISE_REMOVAL
:
572 irp
->IoStatus
.u
.Status
= STATUS_SUCCESS
;
574 case IRP_MN_REMOVE_DEVICE
:
575 udev_driver_unload();
576 iohid_driver_unload();
579 irp
->IoStatus
.u
.Status
= STATUS_SUCCESS
;
580 IoSkipCurrentIrpStackLocation(irp
);
581 ret
= IoCallDriver(bus_pdo
, irp
);
582 IoDetachDevice(bus_pdo
);
583 IoDeleteDevice(device
);
586 FIXME("Unhandled minor function %#x.\n", irpsp
->MinorFunction
);
589 IoSkipCurrentIrpStackLocation(irp
);
590 return IoCallDriver(bus_pdo
, irp
);
593 static NTSTATUS
pdo_pnp_dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
595 NTSTATUS status
= irp
->IoStatus
.u
.Status
;
596 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation(irp
);
598 switch (irpsp
->MinorFunction
)
600 case IRP_MN_QUERY_ID
:
601 TRACE("IRP_MN_QUERY_ID\n");
602 status
= handle_IRP_MN_QUERY_ID(device
, irp
);
604 case IRP_MN_QUERY_CAPABILITIES
:
605 TRACE("IRP_MN_QUERY_CAPABILITIES\n");
606 status
= STATUS_SUCCESS
;
609 FIXME("Unhandled function %08x\n", irpsp
->MinorFunction
);
613 irp
->IoStatus
.u
.Status
= status
;
614 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
618 static NTSTATUS WINAPI
common_pnp_dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
620 if (device
== bus_fdo
)
621 return fdo_pnp_dispatch(device
, irp
);
622 return pdo_pnp_dispatch(device
, irp
);
625 static NTSTATUS
deliver_last_report(struct device_extension
*ext
, DWORD buffer_length
, BYTE
* buffer
, ULONG_PTR
*out_length
)
627 if (buffer_length
< ext
->last_report_size
)
630 return STATUS_BUFFER_TOO_SMALL
;
634 if (ext
->last_report
)
635 memcpy(buffer
, ext
->last_report
, ext
->last_report_size
);
636 *out_length
= ext
->last_report_size
;
637 return STATUS_SUCCESS
;
641 static NTSTATUS
hid_get_native_string(DEVICE_OBJECT
*device
, DWORD index
, WCHAR
*buffer
, DWORD length
)
643 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
644 const struct product_desc
*vendor_products
;
645 unsigned int i
, vendor_products_size
= 0;
647 if (ext
->vid
== VID_MICROSOFT
)
649 vendor_products
= XBOX_CONTROLLERS
;
650 vendor_products_size
= ARRAY_SIZE(XBOX_CONTROLLERS
);
653 for (i
= 0; i
< vendor_products_size
; i
++)
655 if (ext
->pid
== vendor_products
[i
].pid
)
659 if (i
>= vendor_products_size
)
660 return STATUS_UNSUCCESSFUL
;
664 case HID_STRING_ID_IPRODUCT
:
665 if (vendor_products
[i
].product
)
667 strcpyW(buffer
, vendor_products
[i
].product
);
668 return STATUS_SUCCESS
;
671 case HID_STRING_ID_IMANUFACTURER
:
672 if (vendor_products
[i
].manufacturer
)
674 strcpyW(buffer
, vendor_products
[i
].manufacturer
);
675 return STATUS_SUCCESS
;
678 case HID_STRING_ID_ISERIALNUMBER
:
679 if (vendor_products
[i
].serialnumber
)
681 strcpyW(buffer
, vendor_products
[i
].serialnumber
);
682 return STATUS_SUCCESS
;
687 return STATUS_UNSUCCESSFUL
;
690 static NTSTATUS WINAPI
hid_internal_dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
692 NTSTATUS status
= irp
->IoStatus
.u
.Status
;
693 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation(irp
);
694 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
696 TRACE("(%p, %p)\n", device
, irp
);
698 if (device
== bus_fdo
)
700 IoSkipCurrentIrpStackLocation(irp
);
701 return IoCallDriver(bus_pdo
, irp
);
704 switch (irpsp
->Parameters
.DeviceIoControl
.IoControlCode
)
706 case IOCTL_HID_GET_DEVICE_ATTRIBUTES
:
708 HID_DEVICE_ATTRIBUTES
*attr
= (HID_DEVICE_ATTRIBUTES
*)irp
->UserBuffer
;
709 TRACE("IOCTL_HID_GET_DEVICE_ATTRIBUTES\n");
711 if (irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(*attr
))
713 irp
->IoStatus
.u
.Status
= status
= STATUS_BUFFER_TOO_SMALL
;
717 memset(attr
, 0, sizeof(*attr
));
718 attr
->Size
= sizeof(*attr
);
719 attr
->VendorID
= ext
->vid
;
720 attr
->ProductID
= ext
->pid
;
721 attr
->VersionNumber
= ext
->version
;
723 irp
->IoStatus
.u
.Status
= status
= STATUS_SUCCESS
;
724 irp
->IoStatus
.Information
= sizeof(*attr
);
727 case IOCTL_HID_GET_DEVICE_DESCRIPTOR
:
729 HID_DESCRIPTOR
*descriptor
= (HID_DESCRIPTOR
*)irp
->UserBuffer
;
731 TRACE("IOCTL_HID_GET_DEVICE_DESCRIPTOR\n");
733 if (irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(*descriptor
))
735 irp
->IoStatus
.u
.Status
= status
= STATUS_BUFFER_TOO_SMALL
;
739 status
= ext
->vtbl
->get_reportdescriptor(device
, NULL
, 0, &length
);
740 if (status
!= STATUS_SUCCESS
&& status
!= STATUS_BUFFER_TOO_SMALL
)
742 WARN("Failed to get platform report descriptor length\n");
743 irp
->IoStatus
.u
.Status
= status
;
747 memset(descriptor
, 0, sizeof(*descriptor
));
748 descriptor
->bLength
= sizeof(*descriptor
);
749 descriptor
->bDescriptorType
= HID_HID_DESCRIPTOR_TYPE
;
750 descriptor
->bcdHID
= HID_REVISION
;
751 descriptor
->bCountry
= 0;
752 descriptor
->bNumDescriptors
= 1;
753 descriptor
->DescriptorList
[0].bReportType
= HID_REPORT_DESCRIPTOR_TYPE
;
754 descriptor
->DescriptorList
[0].wReportLength
= length
;
756 irp
->IoStatus
.u
.Status
= status
= STATUS_SUCCESS
;
757 irp
->IoStatus
.Information
= sizeof(*descriptor
);
760 case IOCTL_HID_GET_REPORT_DESCRIPTOR
:
762 DWORD length
= irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
;
763 TRACE("IOCTL_HID_GET_REPORT_DESCRIPTOR\n");
765 irp
->IoStatus
.u
.Status
= status
= ext
->vtbl
->get_reportdescriptor(device
, irp
->UserBuffer
, length
, &length
);
766 irp
->IoStatus
.Information
= length
;
769 case IOCTL_HID_GET_STRING
:
771 DWORD length
= irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
/ sizeof(WCHAR
);
772 DWORD index
= (ULONG_PTR
)irpsp
->Parameters
.DeviceIoControl
.Type3InputBuffer
;
773 TRACE("IOCTL_HID_GET_STRING[%08x]\n", index
);
775 irp
->IoStatus
.u
.Status
= status
= hid_get_native_string(device
, index
, (WCHAR
*)irp
->UserBuffer
, length
);
776 if (status
!= STATUS_SUCCESS
)
777 irp
->IoStatus
.u
.Status
= status
= ext
->vtbl
->get_string(device
, index
, (WCHAR
*)irp
->UserBuffer
, length
);
778 if (status
== STATUS_SUCCESS
)
779 irp
->IoStatus
.Information
= (strlenW((WCHAR
*)irp
->UserBuffer
) + 1) * sizeof(WCHAR
);
782 case IOCTL_HID_GET_INPUT_REPORT
:
784 HID_XFER_PACKET
*packet
= (HID_XFER_PACKET
*)(irp
->UserBuffer
);
785 TRACE_(hid_report
)("IOCTL_HID_GET_INPUT_REPORT\n");
786 EnterCriticalSection(&ext
->report_cs
);
787 status
= ext
->vtbl
->begin_report_processing(device
);
788 if (status
!= STATUS_SUCCESS
)
790 irp
->IoStatus
.u
.Status
= status
;
791 LeaveCriticalSection(&ext
->report_cs
);
795 irp
->IoStatus
.u
.Status
= status
= deliver_last_report(ext
,
796 packet
->reportBufferLen
, packet
->reportBuffer
,
797 &irp
->IoStatus
.Information
);
799 if (status
== STATUS_SUCCESS
)
800 packet
->reportBufferLen
= irp
->IoStatus
.Information
;
801 LeaveCriticalSection(&ext
->report_cs
);
804 case IOCTL_HID_READ_REPORT
:
806 TRACE_(hid_report
)("IOCTL_HID_READ_REPORT\n");
807 EnterCriticalSection(&ext
->report_cs
);
808 status
= ext
->vtbl
->begin_report_processing(device
);
809 if (status
!= STATUS_SUCCESS
)
811 irp
->IoStatus
.u
.Status
= status
;
812 LeaveCriticalSection(&ext
->report_cs
);
815 if (!ext
->last_report_read
)
817 irp
->IoStatus
.u
.Status
= status
= deliver_last_report(ext
,
818 irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
,
819 irp
->UserBuffer
, &irp
->IoStatus
.Information
);
820 ext
->last_report_read
= TRUE
;
824 InsertTailList(&ext
->irp_queue
, &irp
->Tail
.Overlay
.s
.ListEntry
);
825 status
= STATUS_PENDING
;
827 LeaveCriticalSection(&ext
->report_cs
);
830 case IOCTL_HID_SET_OUTPUT_REPORT
:
831 case IOCTL_HID_WRITE_REPORT
:
833 HID_XFER_PACKET
*packet
= (HID_XFER_PACKET
*)(irp
->UserBuffer
);
834 TRACE_(hid_report
)("IOCTL_HID_WRITE_REPORT / IOCTL_HID_SET_OUTPUT_REPORT\n");
835 irp
->IoStatus
.u
.Status
= status
= ext
->vtbl
->set_output_report(
836 device
, packet
->reportId
, packet
->reportBuffer
,
837 packet
->reportBufferLen
, &irp
->IoStatus
.Information
);
840 case IOCTL_HID_GET_FEATURE
:
842 HID_XFER_PACKET
*packet
= (HID_XFER_PACKET
*)(irp
->UserBuffer
);
843 TRACE_(hid_report
)("IOCTL_HID_GET_FEATURE\n");
844 irp
->IoStatus
.u
.Status
= status
= ext
->vtbl
->get_feature_report(
845 device
, packet
->reportId
, packet
->reportBuffer
,
846 packet
->reportBufferLen
, &irp
->IoStatus
.Information
);
847 packet
->reportBufferLen
= irp
->IoStatus
.Information
;
850 case IOCTL_HID_SET_FEATURE
:
852 HID_XFER_PACKET
*packet
= (HID_XFER_PACKET
*)(irp
->UserBuffer
);
853 TRACE_(hid_report
)("IOCTL_HID_SET_FEATURE\n");
854 irp
->IoStatus
.u
.Status
= status
= ext
->vtbl
->set_feature_report(
855 device
, packet
->reportId
, packet
->reportBuffer
,
856 packet
->reportBufferLen
, &irp
->IoStatus
.Information
);
861 ULONG code
= irpsp
->Parameters
.DeviceIoControl
.IoControlCode
;
862 FIXME("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
863 code
, code
>> 16, (code
>> 14) & 3, (code
>> 2) & 0xfff, code
& 3);
868 if (status
!= STATUS_PENDING
)
869 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
874 void process_hid_report(DEVICE_OBJECT
*device
, BYTE
*report
, DWORD length
)
876 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
880 if (!length
|| !report
)
883 EnterCriticalSection(&ext
->report_cs
);
884 if (length
> ext
->buffer_size
)
886 HeapFree(GetProcessHeap(), 0, ext
->last_report
);
887 ext
->last_report
= HeapAlloc(GetProcessHeap(), 0, length
);
888 if (!ext
->last_report
)
890 ERR_(hid_report
)("Failed to alloc last report\n");
891 ext
->buffer_size
= 0;
892 ext
->last_report_size
= 0;
893 ext
->last_report_read
= TRUE
;
894 LeaveCriticalSection(&ext
->report_cs
);
898 ext
->buffer_size
= length
;
901 memcpy(ext
->last_report
, report
, length
);
902 ext
->last_report_size
= length
;
903 ext
->last_report_read
= FALSE
;
905 while ((entry
= RemoveHeadList(&ext
->irp_queue
)) != &ext
->irp_queue
)
907 IO_STACK_LOCATION
*irpsp
;
908 TRACE_(hid_report
)("Processing Request\n");
909 irp
= CONTAINING_RECORD(entry
, IRP
, Tail
.Overlay
.s
.ListEntry
);
910 irpsp
= IoGetCurrentIrpStackLocation(irp
);
911 irp
->IoStatus
.u
.Status
= deliver_last_report(ext
,
912 irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
,
913 irp
->UserBuffer
, &irp
->IoStatus
.Information
);
914 ext
->last_report_read
= TRUE
;
915 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
917 LeaveCriticalSection(&ext
->report_cs
);
920 DWORD
check_bus_option(const UNICODE_STRING
*option
, DWORD default_value
)
922 char buffer
[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
[sizeof(DWORD
)])];
923 KEY_VALUE_PARTIAL_INFORMATION
*info
= (KEY_VALUE_PARTIAL_INFORMATION
*)buffer
;
926 if (NtQueryValueKey(driver_key
, option
, KeyValuePartialInformation
, info
, sizeof(buffer
), &size
) == STATUS_SUCCESS
)
928 if (info
->Type
== REG_DWORD
)
929 return *(DWORD
*)info
->Data
;
932 return default_value
;
935 BOOL
is_xbox_gamepad(WORD vid
, WORD pid
)
939 if (vid
!= VID_MICROSOFT
)
942 for (i
= 0; i
< ARRAY_SIZE(XBOX_CONTROLLERS
); i
++)
943 if (pid
== XBOX_CONTROLLERS
[i
].pid
) return TRUE
;
948 static NTSTATUS WINAPI
driver_add_device(DRIVER_OBJECT
*driver
, DEVICE_OBJECT
*pdo
)
952 TRACE("driver %p, pdo %p.\n", driver
, pdo
);
954 if ((ret
= IoCreateDevice(driver
, 0, NULL
, FILE_DEVICE_BUS_EXTENDER
, 0, FALSE
, &bus_fdo
)))
956 ERR("Failed to create FDO, status %#x.\n", ret
);
960 IoAttachDeviceToDeviceStack(bus_fdo
, pdo
);
963 bus_fdo
->Flags
&= ~DO_DEVICE_INITIALIZING
;
965 return STATUS_SUCCESS
;
968 static void WINAPI
driver_unload(DRIVER_OBJECT
*driver
)
973 NTSTATUS WINAPI
DriverEntry( DRIVER_OBJECT
*driver
, UNICODE_STRING
*path
)
975 OBJECT_ATTRIBUTES attr
= {0};
978 TRACE( "(%p, %s)\n", driver
, debugstr_w(path
->Buffer
) );
980 attr
.Length
= sizeof(attr
);
981 attr
.ObjectName
= path
;
982 attr
.Attributes
= OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
;
983 if ((ret
= NtOpenKey(&driver_key
, KEY_ALL_ACCESS
, &attr
)) != STATUS_SUCCESS
)
984 ERR("Failed to open driver key, status %#x.\n", ret
);
988 driver
->MajorFunction
[IRP_MJ_PNP
] = common_pnp_dispatch
;
989 driver
->MajorFunction
[IRP_MJ_INTERNAL_DEVICE_CONTROL
] = hid_internal_dispatch
;
990 driver
->DriverExtension
->AddDevice
= driver_add_device
;
991 driver
->DriverUnload
= driver_unload
;
993 return STATUS_SUCCESS
;