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
36 #include "ddk/hidport.h"
37 #include "wine/debug.h"
38 #include "wine/unicode.h"
39 #include "wine/list.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(plugplay
);
44 WINE_DECLARE_DEBUG_CHANNEL(hid_report
);
46 #define VID_MICROSOFT 0x045e
48 static const WORD PID_XBOX_CONTROLLERS
[] = {
49 0x0202, /* Xbox Controller */
50 0x0285, /* Xbox Controller S */
51 0x0289, /* Xbox Controller S */
52 0x028e, /* Xbox360 Controller */
53 0x028f, /* Xbox360 Wireless Controller */
54 0x02d1, /* Xbox One Controller */
55 0x02dd, /* Xbox One Controller (Covert Forces/Firmware 2015) */
56 0x02e0, /* Xbox One X Controller */
57 0x02e3, /* Xbox One Elite Controller */
58 0x02e6, /* Wireless XBox Controller Dongle */
59 0x02ea, /* Xbox One S Controller */
60 0x02fd, /* Xbox One S Controller (Firmware 2017) */
61 0x0719, /* Xbox 360 Wireless Adapter */
67 DEVICE_OBJECT
*device
;
70 struct device_extension
72 struct pnp_device
*pnp_device
;
75 DWORD uid
, version
, index
;
78 const WCHAR
*busid
; /* Expected to be a static constant */
80 const platform_vtbl
*vtbl
;
83 DWORD last_report_size
;
84 BOOL last_report_read
;
87 CRITICAL_SECTION report_cs
;
89 BYTE platform_private
[1];
92 static CRITICAL_SECTION device_list_cs
;
93 static CRITICAL_SECTION_DEBUG critsect_debug
=
95 0, 0, &device_list_cs
,
96 { &critsect_debug
.ProcessLocksList
, &critsect_debug
.ProcessLocksList
},
97 0, 0, { (DWORD_PTR
)(__FILE__
": device_list_cs") }
99 static CRITICAL_SECTION device_list_cs
= { &critsect_debug
, -1, 0, 0, 0, 0 };
101 static struct list pnp_devset
= LIST_INIT(pnp_devset
);
103 static const WCHAR zero_serialW
[]= {'0','0','0','0',0};
104 static const WCHAR miW
[] = {'M','I',0};
105 static const WCHAR igW
[] = {'I','G',0};
107 static inline WCHAR
*strdupW(const WCHAR
*src
)
110 if (!src
) return NULL
;
111 dst
= HeapAlloc(GetProcessHeap(), 0, (strlenW(src
) + 1)*sizeof(WCHAR
));
112 if (dst
) strcpyW(dst
, src
);
116 void *get_platform_private(DEVICE_OBJECT
*device
)
118 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
119 return ext
->platform_private
;
122 static DWORD
get_device_index(WORD vid
, WORD pid
, WORD input
)
124 struct pnp_device
*ptr
;
127 LIST_FOR_EACH_ENTRY(ptr
, &pnp_devset
, struct pnp_device
, entry
)
129 struct device_extension
*ext
= (struct device_extension
*)ptr
->device
->DeviceExtension
;
130 if (ext
->vid
== vid
&& ext
->pid
== pid
&& ext
->input
== input
)
131 index
= max(ext
->index
+ 1, index
);
137 static WCHAR
*get_instance_id(DEVICE_OBJECT
*device
)
139 static const WCHAR formatW
[] = {'%','s','\\','v','i','d','_','%','0','4','x','&','p','i','d','_','%','0','4','x',
140 '\\','%','i','&','%','s','&','%','x','&','%','i',0};
141 static const WCHAR format_inputW
[] = {'%','s','\\','v','i','d','_','%','0','4','x','&','p','i','d','_','%','0','4','x','&',
142 '%','s','_','%','i','\\','%','i','&','%','s','&','%','x','&','%','i',0};
143 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
144 const WCHAR
*serial
= ext
->serial
? ext
->serial
: zero_serialW
;
145 DWORD len
= strlenW(ext
->busid
) + strlenW(serial
) + 64;
148 if ((dst
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
))))
150 if (ext
->input
== (WORD
)-1)
152 sprintfW(dst
, formatW
, ext
->busid
, ext
->vid
, ext
->pid
,
153 ext
->version
, serial
, ext
->uid
, ext
->index
);
157 sprintfW(dst
, format_inputW
, ext
->busid
, ext
->vid
, ext
->pid
, ext
->is_gamepad
? igW
: miW
,
158 ext
->input
, ext
->version
, serial
, ext
->uid
, ext
->index
);
165 static WCHAR
*get_device_id(DEVICE_OBJECT
*device
)
167 static const WCHAR formatW
[] = {'%','s','\\','V','i','d','_','%','0','4','x','&','P','i','d','_','%','0','4','x',0};
168 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
169 DWORD len
= strlenW(ext
->busid
) + 19;
172 if ((dst
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
))))
173 sprintfW(dst
, formatW
, ext
->busid
, ext
->vid
, ext
->pid
);
178 static WCHAR
*get_compatible_ids(DEVICE_OBJECT
*device
)
180 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
181 WCHAR
*iid
, *did
, *dst
, *ptr
;
184 if (!(iid
= get_instance_id(device
)))
187 if (!(did
= get_device_id(device
)))
189 HeapFree(GetProcessHeap(), 0, iid
);
193 len
= strlenW(iid
) + strlenW(did
) + strlenW(ext
->busid
) + 4;
194 if ((dst
= HeapAlloc(GetProcessHeap(), 0, len
* sizeof(WCHAR
))))
198 ptr
+= strlenW(iid
) + 1;
200 ptr
+= strlenW(did
) + 1;
201 strcpyW(ptr
, ext
->busid
);
202 ptr
+= strlenW(ext
->busid
) + 1;
206 HeapFree(GetProcessHeap(), 0, iid
);
207 HeapFree(GetProcessHeap(), 0, did
);
211 DEVICE_OBJECT
*bus_create_hid_device(DRIVER_OBJECT
*driver
, const WCHAR
*busidW
, WORD vid
, WORD pid
,
212 WORD input
, DWORD version
, DWORD uid
, const WCHAR
*serialW
, BOOL is_gamepad
,
213 const GUID
*class, const platform_vtbl
*vtbl
, DWORD platform_data_size
)
215 static const WCHAR device_name_fmtW
[] = {'\\','D','e','v','i','c','e','\\','%','s','#','%','p',0};
216 struct device_extension
*ext
;
217 struct pnp_device
*pnp_dev
;
218 DEVICE_OBJECT
*device
;
219 UNICODE_STRING nameW
;
222 SP_DEVINFO_DATA data
= {sizeof(data
)};
223 WCHAR
*instance
= NULL
;
227 TRACE("(%p, %s, %04x, %04x, %04x, %u, %u, %s, %u, %s, %p, %u)\n", driver
,
228 debugstr_w(busidW
), vid
, pid
, input
, version
, uid
, debugstr_w(serialW
),
229 is_gamepad
, debugstr_guid(class), vtbl
, platform_data_size
);
231 if (!(pnp_dev
= HeapAlloc(GetProcessHeap(), 0, sizeof(*pnp_dev
))))
234 sprintfW(dev_name
, device_name_fmtW
, busidW
, pnp_dev
);
235 RtlInitUnicodeString(&nameW
, dev_name
);
236 length
= FIELD_OFFSET(struct device_extension
, platform_private
[platform_data_size
]);
237 status
= IoCreateDevice(driver
, length
, &nameW
, 0, 0, FALSE
, &device
);
240 FIXME("failed to create device error %x\n", status
);
241 HeapFree(GetProcessHeap(), 0, pnp_dev
);
245 EnterCriticalSection(&device_list_cs
);
247 /* fill out device_extension struct */
248 ext
= (struct device_extension
*)device
->DeviceExtension
;
249 ext
->pnp_device
= pnp_dev
;
254 ext
->version
= version
;
255 ext
->index
= get_device_index(vid
, pid
, input
);
256 ext
->is_gamepad
= is_gamepad
;
257 ext
->serial
= strdupW(serialW
);
260 ext
->last_report
= NULL
;
261 ext
->last_report_size
= 0;
262 ext
->last_report_read
= TRUE
;
263 ext
->buffer_size
= 0;
265 memset(ext
->platform_private
, 0, platform_data_size
);
267 InitializeListHead(&ext
->irp_queue
);
268 InitializeCriticalSection(&ext
->report_cs
);
269 ext
->report_cs
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": report_cs");
271 /* add to list of pnp devices */
272 pnp_dev
->device
= device
;
273 list_add_tail(&pnp_devset
, &pnp_dev
->entry
);
275 LeaveCriticalSection(&device_list_cs
);
277 devinfo
= SetupDiCreateDeviceInfoList(class, NULL
);
278 if (devinfo
== INVALID_HANDLE_VALUE
)
280 ERR("failed to create device info list, error %#x\n", GetLastError());
284 instance
= get_instance_id(device
);
287 ERR("failed to generate instance id\n");
291 if (SetupDiCreateDeviceInfoW(devinfo
, instance
, class, NULL
, NULL
, DICD_INHERIT_CLASSDRVS
, &data
))
293 if (!SetupDiRegisterDeviceInfo(devinfo
, &data
, 0, NULL
, NULL
, NULL
))
294 ERR("failed to register device info, error %#x\n", GetLastError());
296 else if (GetLastError() != ERROR_DEVINST_ALREADY_EXISTS
)
297 ERR("failed to create device info, error %#x\n", GetLastError());
300 HeapFree(GetProcessHeap(), 0, instance
);
301 SetupDiDestroyDeviceInfoList(devinfo
);
305 DEVICE_OBJECT
*bus_find_hid_device(const platform_vtbl
*vtbl
, void *platform_dev
)
307 struct pnp_device
*dev
;
308 DEVICE_OBJECT
*ret
= NULL
;
310 TRACE("(%p, %p)\n", vtbl
, platform_dev
);
312 EnterCriticalSection(&device_list_cs
);
313 LIST_FOR_EACH_ENTRY(dev
, &pnp_devset
, struct pnp_device
, entry
)
315 struct device_extension
*ext
= (struct device_extension
*)dev
->device
->DeviceExtension
;
316 if (ext
->vtbl
!= vtbl
) continue;
317 if (ext
->vtbl
->compare_platform_device(dev
->device
, platform_dev
) == 0)
323 LeaveCriticalSection(&device_list_cs
);
325 TRACE("returning %p\n", ret
);
329 DEVICE_OBJECT
* bus_enumerate_hid_devices(const platform_vtbl
*vtbl
, enum_func function
, void* context
)
331 struct pnp_device
*dev
;
332 DEVICE_OBJECT
*ret
= NULL
;
334 TRACE("(%p)\n", vtbl
);
336 EnterCriticalSection(&device_list_cs
);
337 LIST_FOR_EACH_ENTRY(dev
, &pnp_devset
, struct pnp_device
, entry
)
339 struct device_extension
*ext
= (struct device_extension
*)dev
->device
->DeviceExtension
;
340 if (ext
->vtbl
!= vtbl
) continue;
341 if (function(dev
->device
, context
) == 0)
347 LeaveCriticalSection(&device_list_cs
);
351 void bus_remove_hid_device(DEVICE_OBJECT
*device
)
353 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
354 struct pnp_device
*pnp_device
= ext
->pnp_device
;
358 TRACE("(%p)\n", device
);
360 EnterCriticalSection(&device_list_cs
);
361 list_remove(&pnp_device
->entry
);
362 LeaveCriticalSection(&device_list_cs
);
364 /* Cancel pending IRPs */
365 EnterCriticalSection(&ext
->report_cs
);
366 while ((entry
= RemoveHeadList(&ext
->irp_queue
)) != &ext
->irp_queue
)
368 irp
= CONTAINING_RECORD(entry
, IRP
, Tail
.Overlay
.s
.ListEntry
);
369 irp
->IoStatus
.u
.Status
= STATUS_CANCELLED
;
370 irp
->IoStatus
.Information
= 0;
371 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
373 LeaveCriticalSection(&ext
->report_cs
);
375 ext
->report_cs
.DebugInfo
->Spare
[0] = 0;
376 DeleteCriticalSection(&ext
->report_cs
);
378 HeapFree(GetProcessHeap(), 0, ext
->serial
);
379 HeapFree(GetProcessHeap(), 0, ext
->last_report
);
380 IoDeleteDevice(device
);
382 /* pnp_device must be released after the device is gone */
383 HeapFree(GetProcessHeap(), 0, pnp_device
);
386 static NTSTATUS
build_device_relations(DEVICE_RELATIONS
**devices
)
389 struct pnp_device
*ptr
;
391 EnterCriticalSection(&device_list_cs
);
392 *devices
= HeapAlloc(GetProcessHeap(), 0, sizeof(DEVICE_RELATIONS
) +
393 list_count(&pnp_devset
) * sizeof (void *));
397 LeaveCriticalSection(&device_list_cs
);
398 return STATUS_INSUFFICIENT_RESOURCES
;
402 LIST_FOR_EACH_ENTRY(ptr
, &pnp_devset
, struct pnp_device
, entry
)
404 (*devices
)->Objects
[i
] = ptr
->device
;
407 LeaveCriticalSection(&device_list_cs
);
408 (*devices
)->Count
= i
;
409 return STATUS_SUCCESS
;
412 static NTSTATUS
handle_IRP_MN_QUERY_DEVICE_RELATIONS(IRP
*irp
)
414 NTSTATUS status
= irp
->IoStatus
.u
.Status
;
415 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation( irp
);
417 TRACE("IRP_MN_QUERY_DEVICE_RELATIONS\n");
418 switch (irpsp
->Parameters
.QueryDeviceRelations
.Type
)
420 case EjectionRelations
:
421 case RemovalRelations
:
422 case TargetDeviceRelation
:
424 FIXME("Unhandled Device Relation %x\n",irpsp
->Parameters
.QueryDeviceRelations
.Type
);
427 status
= build_device_relations((DEVICE_RELATIONS
**)&irp
->IoStatus
.Information
);
430 FIXME("Unknown Device Relation %x\n",irpsp
->Parameters
.QueryDeviceRelations
.Type
);
437 static NTSTATUS
handle_IRP_MN_QUERY_ID(DEVICE_OBJECT
*device
, IRP
*irp
)
439 NTSTATUS status
= irp
->IoStatus
.u
.Status
;
440 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation( irp
);
441 BUS_QUERY_ID_TYPE type
= irpsp
->Parameters
.QueryId
.IdType
;
443 TRACE("(%p, %p)\n", device
, irp
);
447 case BusQueryHardwareIDs
:
448 TRACE("BusQueryHardwareIDs\n");
449 irp
->IoStatus
.Information
= (ULONG_PTR
)get_compatible_ids(device
);
451 case BusQueryCompatibleIDs
:
452 TRACE("BusQueryCompatibleIDs\n");
453 irp
->IoStatus
.Information
= (ULONG_PTR
)get_compatible_ids(device
);
455 case BusQueryDeviceID
:
456 TRACE("BusQueryDeviceID\n");
457 irp
->IoStatus
.Information
= (ULONG_PTR
)get_device_id(device
);
459 case BusQueryInstanceID
:
460 TRACE("BusQueryInstanceID\n");
461 irp
->IoStatus
.Information
= (ULONG_PTR
)get_instance_id(device
);
464 FIXME("Unhandled type %08x\n", type
);
468 status
= irp
->IoStatus
.Information
? STATUS_SUCCESS
: STATUS_NO_MEMORY
;
472 NTSTATUS WINAPI
common_pnp_dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
474 NTSTATUS status
= irp
->IoStatus
.u
.Status
;
475 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation(irp
);
477 switch (irpsp
->MinorFunction
)
479 case IRP_MN_QUERY_DEVICE_RELATIONS
:
480 TRACE("IRP_MN_QUERY_DEVICE_RELATIONS\n");
481 status
= handle_IRP_MN_QUERY_DEVICE_RELATIONS(irp
);
482 irp
->IoStatus
.u
.Status
= status
;
484 case IRP_MN_QUERY_ID
:
485 TRACE("IRP_MN_QUERY_ID\n");
486 status
= handle_IRP_MN_QUERY_ID(device
, irp
);
487 irp
->IoStatus
.u
.Status
= status
;
489 case IRP_MN_QUERY_CAPABILITIES
:
490 TRACE("IRP_MN_QUERY_CAPABILITIES\n");
493 FIXME("Unhandled function %08x\n", irpsp
->MinorFunction
);
497 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
501 static NTSTATUS
deliver_last_report(struct device_extension
*ext
, DWORD buffer_length
, BYTE
* buffer
, ULONG_PTR
*out_length
)
503 if (buffer_length
< ext
->last_report_size
)
506 return STATUS_BUFFER_TOO_SMALL
;
510 if (ext
->last_report
)
511 memcpy(buffer
, ext
->last_report
, ext
->last_report_size
);
512 *out_length
= ext
->last_report_size
;
513 return STATUS_SUCCESS
;
517 NTSTATUS WINAPI
hid_internal_dispatch(DEVICE_OBJECT
*device
, IRP
*irp
)
519 NTSTATUS status
= irp
->IoStatus
.u
.Status
;
520 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation(irp
);
521 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
523 TRACE("(%p, %p)\n", device
, irp
);
525 switch (irpsp
->Parameters
.DeviceIoControl
.IoControlCode
)
527 case IOCTL_HID_GET_DEVICE_ATTRIBUTES
:
529 HID_DEVICE_ATTRIBUTES
*attr
= (HID_DEVICE_ATTRIBUTES
*)irp
->UserBuffer
;
530 TRACE("IOCTL_HID_GET_DEVICE_ATTRIBUTES\n");
532 if (irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(*attr
))
534 irp
->IoStatus
.u
.Status
= status
= STATUS_BUFFER_TOO_SMALL
;
538 memset(attr
, 0, sizeof(*attr
));
539 attr
->Size
= sizeof(*attr
);
540 attr
->VendorID
= ext
->vid
;
541 attr
->ProductID
= ext
->pid
;
542 attr
->VersionNumber
= ext
->version
;
544 irp
->IoStatus
.u
.Status
= status
= STATUS_SUCCESS
;
545 irp
->IoStatus
.Information
= sizeof(*attr
);
548 case IOCTL_HID_GET_DEVICE_DESCRIPTOR
:
550 HID_DESCRIPTOR
*descriptor
= (HID_DESCRIPTOR
*)irp
->UserBuffer
;
552 TRACE("IOCTL_HID_GET_DEVICE_DESCRIPTOR\n");
554 if (irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
< sizeof(*descriptor
))
556 irp
->IoStatus
.u
.Status
= status
= STATUS_BUFFER_TOO_SMALL
;
560 status
= ext
->vtbl
->get_reportdescriptor(device
, NULL
, 0, &length
);
561 if (status
!= STATUS_SUCCESS
&& status
!= STATUS_BUFFER_TOO_SMALL
)
563 WARN("Failed to get platform report descriptor length\n");
564 irp
->IoStatus
.u
.Status
= status
;
568 memset(descriptor
, 0, sizeof(*descriptor
));
569 descriptor
->bLength
= sizeof(*descriptor
);
570 descriptor
->bDescriptorType
= HID_HID_DESCRIPTOR_TYPE
;
571 descriptor
->bcdHID
= HID_REVISION
;
572 descriptor
->bCountry
= 0;
573 descriptor
->bNumDescriptors
= 1;
574 descriptor
->DescriptorList
[0].bReportType
= HID_REPORT_DESCRIPTOR_TYPE
;
575 descriptor
->DescriptorList
[0].wReportLength
= length
;
577 irp
->IoStatus
.u
.Status
= status
= STATUS_SUCCESS
;
578 irp
->IoStatus
.Information
= sizeof(*descriptor
);
581 case IOCTL_HID_GET_REPORT_DESCRIPTOR
:
583 DWORD length
= irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
;
584 TRACE("IOCTL_HID_GET_REPORT_DESCRIPTOR\n");
586 irp
->IoStatus
.u
.Status
= status
= ext
->vtbl
->get_reportdescriptor(device
, irp
->UserBuffer
, length
, &length
);
587 irp
->IoStatus
.Information
= length
;
590 case IOCTL_HID_GET_STRING
:
592 DWORD length
= irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
/ sizeof(WCHAR
);
593 DWORD index
= (ULONG_PTR
)irpsp
->Parameters
.DeviceIoControl
.Type3InputBuffer
;
594 TRACE("IOCTL_HID_GET_STRING[%08x]\n", index
);
596 irp
->IoStatus
.u
.Status
= status
= ext
->vtbl
->get_string(device
, index
, (WCHAR
*)irp
->UserBuffer
, length
);
597 if (status
== STATUS_SUCCESS
)
598 irp
->IoStatus
.Information
= (strlenW((WCHAR
*)irp
->UserBuffer
) + 1) * sizeof(WCHAR
);
601 case IOCTL_HID_GET_INPUT_REPORT
:
603 HID_XFER_PACKET
*packet
= (HID_XFER_PACKET
*)(irp
->UserBuffer
);
604 TRACE_(hid_report
)("IOCTL_HID_GET_INPUT_REPORT\n");
605 EnterCriticalSection(&ext
->report_cs
);
606 status
= ext
->vtbl
->begin_report_processing(device
);
607 if (status
!= STATUS_SUCCESS
)
609 irp
->IoStatus
.u
.Status
= status
;
610 LeaveCriticalSection(&ext
->report_cs
);
614 irp
->IoStatus
.u
.Status
= status
= deliver_last_report(ext
,
615 packet
->reportBufferLen
, packet
->reportBuffer
,
616 &irp
->IoStatus
.Information
);
618 if (status
== STATUS_SUCCESS
)
619 packet
->reportBufferLen
= irp
->IoStatus
.Information
;
620 LeaveCriticalSection(&ext
->report_cs
);
623 case IOCTL_HID_READ_REPORT
:
625 TRACE_(hid_report
)("IOCTL_HID_READ_REPORT\n");
626 EnterCriticalSection(&ext
->report_cs
);
627 status
= ext
->vtbl
->begin_report_processing(device
);
628 if (status
!= STATUS_SUCCESS
)
630 irp
->IoStatus
.u
.Status
= status
;
631 LeaveCriticalSection(&ext
->report_cs
);
634 if (!ext
->last_report_read
)
636 irp
->IoStatus
.u
.Status
= status
= deliver_last_report(ext
,
637 irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
,
638 irp
->UserBuffer
, &irp
->IoStatus
.Information
);
639 ext
->last_report_read
= TRUE
;
643 InsertTailList(&ext
->irp_queue
, &irp
->Tail
.Overlay
.s
.ListEntry
);
644 status
= STATUS_PENDING
;
646 LeaveCriticalSection(&ext
->report_cs
);
649 case IOCTL_HID_SET_OUTPUT_REPORT
:
650 case IOCTL_HID_WRITE_REPORT
:
652 HID_XFER_PACKET
*packet
= (HID_XFER_PACKET
*)(irp
->UserBuffer
);
653 TRACE_(hid_report
)("IOCTL_HID_WRITE_REPORT / IOCTL_HID_SET_OUTPUT_REPORT\n");
654 irp
->IoStatus
.u
.Status
= status
= ext
->vtbl
->set_output_report(
655 device
, packet
->reportId
, packet
->reportBuffer
,
656 packet
->reportBufferLen
, &irp
->IoStatus
.Information
);
659 case IOCTL_HID_GET_FEATURE
:
661 HID_XFER_PACKET
*packet
= (HID_XFER_PACKET
*)(irp
->UserBuffer
);
662 TRACE_(hid_report
)("IOCTL_HID_GET_FEATURE\n");
663 irp
->IoStatus
.u
.Status
= status
= ext
->vtbl
->get_feature_report(
664 device
, packet
->reportId
, packet
->reportBuffer
,
665 packet
->reportBufferLen
, &irp
->IoStatus
.Information
);
666 packet
->reportBufferLen
= irp
->IoStatus
.Information
;
669 case IOCTL_HID_SET_FEATURE
:
671 HID_XFER_PACKET
*packet
= (HID_XFER_PACKET
*)(irp
->UserBuffer
);
672 TRACE_(hid_report
)("IOCTL_HID_SET_FEATURE\n");
673 irp
->IoStatus
.u
.Status
= status
= ext
->vtbl
->set_feature_report(
674 device
, packet
->reportId
, packet
->reportBuffer
,
675 packet
->reportBufferLen
, &irp
->IoStatus
.Information
);
680 ULONG code
= irpsp
->Parameters
.DeviceIoControl
.IoControlCode
;
681 FIXME("Unsupported ioctl %x (device=%x access=%x func=%x method=%x)\n",
682 code
, code
>> 16, (code
>> 14) & 3, (code
>> 2) & 0xfff, code
& 3);
687 if (status
!= STATUS_PENDING
)
688 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
693 void process_hid_report(DEVICE_OBJECT
*device
, BYTE
*report
, DWORD length
)
695 struct device_extension
*ext
= (struct device_extension
*)device
->DeviceExtension
;
699 if (!length
|| !report
)
702 EnterCriticalSection(&ext
->report_cs
);
703 if (length
> ext
->buffer_size
)
705 HeapFree(GetProcessHeap(), 0, ext
->last_report
);
706 ext
->last_report
= HeapAlloc(GetProcessHeap(), 0, length
);
707 if (!ext
->last_report
)
709 ERR_(hid_report
)("Failed to alloc last report\n");
710 ext
->buffer_size
= 0;
711 ext
->last_report_size
= 0;
712 ext
->last_report_read
= TRUE
;
713 LeaveCriticalSection(&ext
->report_cs
);
717 ext
->buffer_size
= length
;
720 memcpy(ext
->last_report
, report
, length
);
721 ext
->last_report_size
= length
;
722 ext
->last_report_read
= FALSE
;
724 while ((entry
= RemoveHeadList(&ext
->irp_queue
)) != &ext
->irp_queue
)
726 IO_STACK_LOCATION
*irpsp
;
727 TRACE_(hid_report
)("Processing Request\n");
728 irp
= CONTAINING_RECORD(entry
, IRP
, Tail
.Overlay
.s
.ListEntry
);
729 irpsp
= IoGetCurrentIrpStackLocation(irp
);
730 irp
->IoStatus
.u
.Status
= deliver_last_report(ext
,
731 irpsp
->Parameters
.DeviceIoControl
.OutputBufferLength
,
732 irp
->UserBuffer
, &irp
->IoStatus
.Information
);
733 ext
->last_report_read
= TRUE
;
734 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
736 LeaveCriticalSection(&ext
->report_cs
);
739 DWORD
check_bus_option(UNICODE_STRING
*registry_path
, const UNICODE_STRING
*option
, DWORD default_value
)
741 OBJECT_ATTRIBUTES attr
;
743 DWORD output
= default_value
;
745 InitializeObjectAttributes(&attr
, registry_path
, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE
, NULL
, NULL
);
746 if (NtOpenKey(&key
, KEY_ALL_ACCESS
, &attr
) == STATUS_SUCCESS
)
749 char buffer
[FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
[sizeof(DWORD
)])];
751 KEY_VALUE_PARTIAL_INFORMATION
*info
= (KEY_VALUE_PARTIAL_INFORMATION
*)buffer
;
753 if (NtQueryValueKey(key
, option
, KeyValuePartialInformation
, info
, sizeof(buffer
), &size
) == STATUS_SUCCESS
)
755 if (info
->Type
== REG_DWORD
)
756 output
= *(DWORD
*)info
->Data
;
765 BOOL
is_xbox_gamepad(WORD vid
, WORD pid
)
769 if (vid
!= VID_MICROSOFT
)
772 for (i
= 0; i
< ARRAY_SIZE(PID_XBOX_CONTROLLERS
); i
++)
773 if (pid
== PID_XBOX_CONTROLLERS
[i
]) return TRUE
;
778 static void WINAPI
driver_unload(DRIVER_OBJECT
*driver
)
780 udev_driver_unload();
781 iohid_driver_unload();
785 NTSTATUS WINAPI
DriverEntry( DRIVER_OBJECT
*driver
, UNICODE_STRING
*path
)
787 static const WCHAR udevW
[] = {'\\','D','r','i','v','e','r','\\','U','D','E','V',0};
788 static UNICODE_STRING udev
= {sizeof(udevW
) - sizeof(WCHAR
), sizeof(udevW
), (WCHAR
*)udevW
};
789 static const WCHAR iohidW
[] = {'\\','D','r','i','v','e','r','\\','I','O','H','I','D',0};
790 static UNICODE_STRING iohid
= {sizeof(iohidW
) - sizeof(WCHAR
), sizeof(iohidW
), (WCHAR
*)iohidW
};
791 static const WCHAR sdlW
[] = {'\\','D','r','i','v','e','r','\\','S','D','L','J','O','Y',0};
792 static UNICODE_STRING sdl
= {sizeof(sdlW
) - sizeof(WCHAR
), sizeof(sdlW
), (WCHAR
*)sdlW
};
793 static const WCHAR SDL_enabledW
[] = {'E','n','a','b','l','e',' ','S','D','L',0};
794 static const UNICODE_STRING SDL_enabled
= {sizeof(SDL_enabledW
) - sizeof(WCHAR
), sizeof(SDL_enabledW
), (WCHAR
*)SDL_enabledW
};
796 TRACE( "(%p, %s)\n", driver
, debugstr_w(path
->Buffer
) );
798 if (check_bus_option(path
, &SDL_enabled
, 1))
800 if (IoCreateDriver(&sdl
, sdl_driver_init
) == STATUS_SUCCESS
)
801 return STATUS_SUCCESS
;
803 IoCreateDriver(&udev
, udev_driver_init
);
804 IoCreateDriver(&iohid
, iohid_driver_init
);
806 driver
->DriverUnload
= driver_unload
;
808 return STATUS_SUCCESS
;