2 * Human Interface Device class driver
4 * Copyright 2015 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
21 #define NONAMELESSUNION
26 #include "ddk/hidtypes.h"
29 #include "wine/debug.h"
31 #include "wine/list.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(hid
);
35 #if defined(__i386__) && !defined(_WIN32)
37 extern void * WINAPI
wrap_fastcall_func1( void *func
, const void *a
);
38 __ASM_STDCALL_FUNC( wrap_fastcall_func1
, 8,
41 "xchgl (%esp),%ecx\n\t"
44 #define call_fastcall_func1(func,a) wrap_fastcall_func1(func,a)
48 #define call_fastcall_func1(func,a) func(a)
52 static struct list minidriver_list
= LIST_INIT(minidriver_list
);
54 static minidriver
*find_minidriver(DRIVER_OBJECT
*driver
)
57 LIST_FOR_EACH_ENTRY(md
, &minidriver_list
, minidriver
, entry
)
59 if (md
->minidriver
.DriverObject
== driver
)
65 static NTSTATUS
get_device_id(DEVICE_OBJECT
*device
, BUS_QUERY_ID_TYPE type
, WCHAR
*id
)
67 IO_STACK_LOCATION
*irpsp
;
68 IO_STATUS_BLOCK irp_status
;
72 KeInitializeEvent(&event
, NotificationEvent
, FALSE
);
73 irp
= IoBuildSynchronousFsdRequest(IRP_MJ_PNP
, device
, NULL
, 0, NULL
, &event
, &irp_status
);
75 return STATUS_NO_MEMORY
;
77 irpsp
= IoGetNextIrpStackLocation(irp
);
78 irpsp
->MinorFunction
= IRP_MN_QUERY_ID
;
79 irpsp
->Parameters
.QueryId
.IdType
= type
;
81 if (IoCallDriver(device
, irp
) == STATUS_PENDING
)
82 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);
84 wcscpy(id
, (WCHAR
*)irp_status
.Information
);
85 ExFreePool((WCHAR
*)irp_status
.Information
);
86 return irp_status
.u
.Status
;
89 /* user32 reserves 1 & 2 for winemouse and winekeyboard,
90 * keep this in sync with user_private.h */
91 #define WINE_MOUSE_HANDLE 1
92 #define WINE_KEYBOARD_HANDLE 2
94 static UINT32
alloc_rawinput_handle(void)
96 static LONG counter
= WINE_KEYBOARD_HANDLE
+ 1;
97 return InterlockedIncrement(&counter
);
100 static NTSTATUS WINAPI
driver_add_device(DRIVER_OBJECT
*driver
, DEVICE_OBJECT
*bus_pdo
)
102 WCHAR device_id
[MAX_DEVICE_ID_LEN
], instance_id
[MAX_DEVICE_ID_LEN
];
103 BASE_DEVICE_EXTENSION
*ext
;
106 minidriver
*minidriver
;
108 if ((status
= get_device_id(bus_pdo
, BusQueryDeviceID
, device_id
)))
110 ERR("Failed to get PDO device id, status %#x.\n", status
);
114 if ((status
= get_device_id(bus_pdo
, BusQueryInstanceID
, instance_id
)))
116 ERR("Failed to get PDO instance id, status %#x.\n", status
);
120 TRACE("Adding device to PDO %p, id %s\\%s.\n", bus_pdo
, debugstr_w(device_id
), debugstr_w(instance_id
));
121 minidriver
= find_minidriver(driver
);
123 if ((status
= IoCreateDevice(driver
, sizeof(*ext
) + minidriver
->minidriver
.DeviceExtensionSize
,
124 NULL
, FILE_DEVICE_BUS_EXTENDER
, 0, FALSE
, &fdo
)))
126 ERR("Failed to create bus FDO, status %#x.\n", status
);
129 ext
= fdo
->DeviceExtension
;
131 ext
->u
.fdo
.hid_ext
.MiniDeviceExtension
= ext
+ 1;
132 ext
->u
.fdo
.hid_ext
.PhysicalDeviceObject
= bus_pdo
;
133 ext
->u
.fdo
.hid_ext
.NextDeviceObject
= bus_pdo
;
134 swprintf(ext
->device_id
, ARRAY_SIZE(ext
->device_id
), L
"HID\\%s", wcsrchr(device_id
, '\\') + 1);
135 wcscpy(ext
->instance_id
, instance_id
);
137 status
= minidriver
->AddDevice(minidriver
->minidriver
.DriverObject
, fdo
);
138 if (status
!= STATUS_SUCCESS
)
140 ERR("Minidriver AddDevice failed (%x)\n",status
);
145 IoAttachDeviceToDeviceStack(fdo
, bus_pdo
);
146 fdo
->Flags
&= ~DO_DEVICE_INITIALIZING
;
148 return STATUS_SUCCESS
;
151 static void create_child(minidriver
*minidriver
, DEVICE_OBJECT
*fdo
)
153 BASE_DEVICE_EXTENSION
*fdo_ext
= fdo
->DeviceExtension
, *pdo_ext
;
154 HID_DEVICE_ATTRIBUTES attr
= {0};
155 HID_DESCRIPTOR descriptor
;
156 DEVICE_OBJECT
*child_pdo
;
157 BYTE
*reportDescriptor
;
158 UNICODE_STRING string
;
164 status
= call_minidriver(IOCTL_HID_GET_DEVICE_ATTRIBUTES
, fdo
, NULL
, 0, &attr
, sizeof(attr
));
165 if (status
!= STATUS_SUCCESS
)
167 ERR("Minidriver failed to get Attributes(%x)\n",status
);
171 swprintf(pdo_name
, ARRAY_SIZE(pdo_name
), L
"\\Device\\HID#%p&%p", fdo
->DriverObject
,
172 fdo_ext
->u
.fdo
.hid_ext
.PhysicalDeviceObject
);
173 RtlInitUnicodeString(&string
, pdo_name
);
174 if ((status
= IoCreateDevice(fdo
->DriverObject
, sizeof(*pdo_ext
), &string
, 0, 0, FALSE
, &child_pdo
)))
176 ERR("Failed to create child PDO, status %#x.\n", status
);
179 fdo_ext
->u
.fdo
.child_pdo
= child_pdo
;
181 pdo_ext
= child_pdo
->DeviceExtension
;
182 pdo_ext
->u
.pdo
.parent_fdo
= fdo
;
183 InitializeListHead(&pdo_ext
->u
.pdo
.irp_queue
);
184 KeInitializeSpinLock(&pdo_ext
->u
.pdo
.irp_queue_lock
);
185 wcscpy(pdo_ext
->device_id
, fdo_ext
->device_id
);
186 wcscpy(pdo_ext
->instance_id
, fdo_ext
->instance_id
);
188 pdo_ext
->u
.pdo
.information
.VendorID
= attr
.VendorID
;
189 pdo_ext
->u
.pdo
.information
.ProductID
= attr
.ProductID
;
190 pdo_ext
->u
.pdo
.information
.VersionNumber
= attr
.VersionNumber
;
191 pdo_ext
->u
.pdo
.information
.Polled
= minidriver
->minidriver
.DevicesArePolled
;
193 status
= call_minidriver(IOCTL_HID_GET_DEVICE_DESCRIPTOR
, fdo
, NULL
, 0, &descriptor
, sizeof(descriptor
));
194 if (status
!= STATUS_SUCCESS
)
196 ERR("Cannot get Device Descriptor(%x)\n",status
);
197 IoDeleteDevice(child_pdo
);
200 for (i
= 0; i
< descriptor
.bNumDescriptors
; i
++)
201 if (descriptor
.DescriptorList
[i
].bReportType
== HID_REPORT_DESCRIPTOR_TYPE
)
204 if (i
>= descriptor
.bNumDescriptors
)
206 ERR("No Report Descriptor found in reply\n");
207 IoDeleteDevice(child_pdo
);
211 reportDescriptor
= HeapAlloc(GetProcessHeap(), 0, descriptor
.DescriptorList
[i
].wReportLength
);
212 status
= call_minidriver(IOCTL_HID_GET_REPORT_DESCRIPTOR
, fdo
, NULL
, 0,
213 reportDescriptor
, descriptor
.DescriptorList
[i
].wReportLength
);
214 if (status
!= STATUS_SUCCESS
)
216 ERR("Cannot get Report Descriptor(%x)\n",status
);
217 HeapFree(GetProcessHeap(), 0, reportDescriptor
);
218 IoDeleteDevice(child_pdo
);
222 pdo_ext
->u
.pdo
.preparsed_data
= ParseDescriptor(reportDescriptor
, descriptor
.DescriptorList
[i
].wReportLength
);
223 HeapFree(GetProcessHeap(), 0, reportDescriptor
);
224 if (!pdo_ext
->u
.pdo
.preparsed_data
)
226 ERR("Cannot parse Report Descriptor\n");
227 IoDeleteDevice(child_pdo
);
231 pdo_ext
->u
.pdo
.information
.DescriptorSize
= pdo_ext
->u
.pdo
.preparsed_data
->dwSize
;
233 IoInvalidateDeviceRelations(fdo_ext
->u
.fdo
.hid_ext
.PhysicalDeviceObject
, BusRelations
);
235 page
= pdo_ext
->u
.pdo
.preparsed_data
->caps
.UsagePage
;
236 usage
= pdo_ext
->u
.pdo
.preparsed_data
->caps
.Usage
;
237 if (page
== HID_USAGE_PAGE_GENERIC
&& usage
== HID_USAGE_GENERIC_MOUSE
)
238 pdo_ext
->u
.pdo
.rawinput_handle
= WINE_MOUSE_HANDLE
;
239 else if (page
== HID_USAGE_PAGE_GENERIC
&& usage
== HID_USAGE_GENERIC_KEYBOARD
)
240 pdo_ext
->u
.pdo
.rawinput_handle
= WINE_KEYBOARD_HANDLE
;
242 pdo_ext
->u
.pdo
.rawinput_handle
= alloc_rawinput_handle();
244 pdo_ext
->u
.pdo
.poll_interval
= DEFAULT_POLL_INTERVAL
;
246 pdo_ext
->u
.pdo
.ring_buffer
= RingBuffer_Create(
247 sizeof(HID_XFER_PACKET
) + pdo_ext
->u
.pdo
.preparsed_data
->caps
.InputReportByteLength
);
249 HID_StartDeviceThread(child_pdo
);
252 static NTSTATUS
fdo_pnp(DEVICE_OBJECT
*device
, IRP
*irp
)
254 minidriver
*minidriver
= find_minidriver(device
->DriverObject
);
255 IO_STACK_LOCATION
*stack
= IoGetCurrentIrpStackLocation(irp
);
256 BASE_DEVICE_EXTENSION
*ext
= device
->DeviceExtension
;
258 TRACE("irp %p, minor function %#x.\n", irp
, stack
->MinorFunction
);
260 switch (stack
->MinorFunction
)
262 case IRP_MN_QUERY_DEVICE_RELATIONS
:
264 DEVICE_RELATIONS
*devices
;
265 DEVICE_OBJECT
*child
;
267 if (stack
->Parameters
.QueryDeviceRelations
.Type
!= BusRelations
)
268 return minidriver
->PNPDispatch(device
, irp
);
270 if (!(devices
= ExAllocatePool(PagedPool
, offsetof(DEVICE_RELATIONS
, Objects
[1]))))
272 irp
->IoStatus
.u
.Status
= STATUS_NO_MEMORY
;
273 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
274 return STATUS_NO_MEMORY
;
277 if ((child
= ext
->u
.fdo
.child_pdo
))
279 devices
->Objects
[0] = ext
->u
.fdo
.child_pdo
;
280 call_fastcall_func1(ObfReferenceObject
, ext
->u
.fdo
.child_pdo
);
288 irp
->IoStatus
.Information
= (ULONG_PTR
)devices
;
289 irp
->IoStatus
.u
.Status
= STATUS_SUCCESS
;
290 IoSkipCurrentIrpStackLocation(irp
);
291 return IoCallDriver(ext
->u
.fdo
.hid_ext
.NextDeviceObject
, irp
);
294 case IRP_MN_START_DEVICE
:
298 if ((ret
= minidriver
->PNPDispatch(device
, irp
)))
300 create_child(minidriver
, device
);
301 return STATUS_SUCCESS
;
304 case IRP_MN_REMOVE_DEVICE
:
308 ret
= minidriver
->PNPDispatch(device
, irp
);
310 IoDetachDevice(ext
->u
.fdo
.hid_ext
.NextDeviceObject
);
311 IoDeleteDevice(device
);
316 return minidriver
->PNPDispatch(device
, irp
);
320 static NTSTATUS
pdo_pnp(DEVICE_OBJECT
*device
, IRP
*irp
)
322 IO_STACK_LOCATION
*irpsp
= IoGetCurrentIrpStackLocation(irp
);
323 BASE_DEVICE_EXTENSION
*ext
= device
->DeviceExtension
;
324 NTSTATUS status
= irp
->IoStatus
.u
.Status
;
326 TRACE("irp %p, minor function %#x.\n", irp
, irpsp
->MinorFunction
);
328 switch(irpsp
->MinorFunction
)
330 case IRP_MN_QUERY_ID
:
332 WCHAR
*id
= ExAllocatePool(PagedPool
, sizeof(WCHAR
) * REGSTR_VAL_MAX_HCID_LEN
);
333 TRACE("IRP_MN_QUERY_ID[%i]\n", irpsp
->Parameters
.QueryId
.IdType
);
334 switch (irpsp
->Parameters
.QueryId
.IdType
)
336 case BusQueryHardwareIDs
:
337 case BusQueryCompatibleIDs
:
341 /* Device instance ID */
342 lstrcpyW(ptr
, ext
->device_id
);
343 ptr
+= lstrlenW(ext
->device_id
);
344 lstrcpyW(ptr
, L
"\\");
346 lstrcpyW(ptr
, ext
->instance_id
);
347 ptr
+= lstrlenW(ext
->instance_id
) + 1;
349 lstrcpyW(ptr
, ext
->device_id
);
350 ptr
+= lstrlenW(ext
->device_id
) + 1;
352 lstrcpyW(ptr
, L
"HID");
353 ptr
+= lstrlenW(L
"HID") + 1;
355 irp
->IoStatus
.Information
= (ULONG_PTR
)id
;
356 status
= STATUS_SUCCESS
;
359 case BusQueryDeviceID
:
360 lstrcpyW(id
, ext
->device_id
);
361 irp
->IoStatus
.Information
= (ULONG_PTR
)id
;
362 status
= STATUS_SUCCESS
;
364 case BusQueryInstanceID
:
365 lstrcpyW(id
, ext
->instance_id
);
366 irp
->IoStatus
.Information
= (ULONG_PTR
)id
;
367 status
= STATUS_SUCCESS
;
370 case BusQueryContainerID
:
371 case BusQueryDeviceSerialNumber
:
372 FIXME("unimplemented id type %#x\n", irpsp
->Parameters
.QueryId
.IdType
);
379 case IRP_MN_QUERY_CAPABILITIES
:
381 DEVICE_CAPABILITIES
*caps
= irpsp
->Parameters
.DeviceCapabilities
.Capabilities
;
383 caps
->RawDeviceOK
= 1;
384 status
= STATUS_SUCCESS
;
388 case IRP_MN_START_DEVICE
:
389 if ((status
= IoRegisterDeviceInterface(device
, &GUID_DEVINTERFACE_HID
, NULL
, &ext
->u
.pdo
.link_name
)))
391 ERR("Failed to register interface, status %#x.\n", status
);
395 /* FIXME: This should probably be done in mouhid.sys. */
396 if (ext
->u
.pdo
.preparsed_data
->caps
.UsagePage
== HID_USAGE_PAGE_GENERIC
397 && ext
->u
.pdo
.preparsed_data
->caps
.Usage
== HID_USAGE_GENERIC_MOUSE
)
399 if (!IoRegisterDeviceInterface(device
, &GUID_DEVINTERFACE_MOUSE
, NULL
, &ext
->u
.pdo
.mouse_link_name
))
400 ext
->u
.pdo
.is_mouse
= TRUE
;
403 IoSetDeviceInterfaceState(&ext
->u
.pdo
.link_name
, TRUE
);
404 if (ext
->u
.pdo
.is_mouse
)
405 IoSetDeviceInterfaceState(&ext
->u
.pdo
.mouse_link_name
, TRUE
);
406 status
= STATUS_SUCCESS
;
409 case IRP_MN_REMOVE_DEVICE
:
413 IoSetDeviceInterfaceState(&ext
->u
.pdo
.link_name
, FALSE
);
414 if (ext
->u
.pdo
.is_mouse
)
415 IoSetDeviceInterfaceState(&ext
->u
.pdo
.mouse_link_name
, FALSE
);
417 if (ext
->u
.pdo
.thread
)
419 SetEvent(ext
->u
.pdo
.halt_event
);
420 WaitForSingleObject(ext
->u
.pdo
.thread
, INFINITE
);
422 CloseHandle(ext
->u
.pdo
.halt_event
);
424 HeapFree(GetProcessHeap(), 0, ext
->u
.pdo
.preparsed_data
);
425 if (ext
->u
.pdo
.ring_buffer
)
426 RingBuffer_Destroy(ext
->u
.pdo
.ring_buffer
);
428 while ((queued_irp
= pop_irp_from_queue(ext
)))
430 queued_irp
->IoStatus
.u
.Status
= STATUS_DEVICE_REMOVED
;
431 IoCompleteRequest(queued_irp
, IO_NO_INCREMENT
);
434 RtlFreeUnicodeString(&ext
->u
.pdo
.link_name
);
436 irp
->IoStatus
.u
.Status
= STATUS_SUCCESS
;
437 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
438 IoDeleteDevice(device
);
439 return STATUS_SUCCESS
;
442 case IRP_MN_SURPRISE_REMOVAL
:
443 status
= STATUS_SUCCESS
;
447 FIXME("Unhandled minor function %#x.\n", irpsp
->MinorFunction
);
450 irp
->IoStatus
.u
.Status
= status
;
451 IoCompleteRequest( irp
, IO_NO_INCREMENT
);
455 static NTSTATUS WINAPI
driver_pnp(DEVICE_OBJECT
*device
, IRP
*irp
)
457 BASE_DEVICE_EXTENSION
*ext
= device
->DeviceExtension
;
460 return fdo_pnp(device
, irp
);
462 return pdo_pnp(device
, irp
);
465 static NTSTATUS WINAPI
driver_create(DEVICE_OBJECT
*device
, IRP
*irp
)
467 BASE_DEVICE_EXTENSION
*ext
= device
->DeviceExtension
;
471 irp
->IoStatus
.u
.Status
= STATUS_UNSUCCESSFUL
;
472 IoCompleteRequest(irp
, IO_NO_INCREMENT
);
473 return STATUS_UNSUCCESSFUL
;
476 return pdo_create(device
, irp
);
479 static NTSTATUS WINAPI
driver_close(DEVICE_OBJECT
*device
, IRP
*irp
)
481 return pdo_close(device
, irp
);
484 static NTSTATUS WINAPI
driver_ioctl(DEVICE_OBJECT
*device
, IRP
*irp
)
486 return pdo_ioctl(device
, irp
);
489 static NTSTATUS WINAPI
driver_read(DEVICE_OBJECT
*device
, IRP
*irp
)
491 return pdo_read(device
, irp
);
494 static NTSTATUS WINAPI
driver_write(DEVICE_OBJECT
*device
, IRP
*irp
)
496 return pdo_write(device
, irp
);
499 static void WINAPI
driver_unload(DRIVER_OBJECT
*driver
)
505 if ((md
= find_minidriver(driver
)))
507 if (md
->DriverUnload
)
508 md
->DriverUnload(md
->minidriver
.DriverObject
);
509 list_remove(&md
->entry
);
510 HeapFree(GetProcessHeap(), 0, md
);
514 NTSTATUS WINAPI
HidRegisterMinidriver(HID_MINIDRIVER_REGISTRATION
*registration
)
518 if (!(driver
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*driver
))))
519 return STATUS_NO_MEMORY
;
521 driver
->DriverUnload
= registration
->DriverObject
->DriverUnload
;
522 registration
->DriverObject
->DriverUnload
= driver_unload
;
524 registration
->DriverObject
->MajorFunction
[IRP_MJ_DEVICE_CONTROL
] = driver_ioctl
;
525 registration
->DriverObject
->MajorFunction
[IRP_MJ_READ
] = driver_read
;
526 registration
->DriverObject
->MajorFunction
[IRP_MJ_WRITE
] = driver_write
;
527 registration
->DriverObject
->MajorFunction
[IRP_MJ_CREATE
] = driver_create
;
528 registration
->DriverObject
->MajorFunction
[IRP_MJ_CLOSE
] = driver_close
;
530 driver
->PNPDispatch
= registration
->DriverObject
->MajorFunction
[IRP_MJ_PNP
];
531 registration
->DriverObject
->MajorFunction
[IRP_MJ_PNP
] = driver_pnp
;
533 driver
->AddDevice
= registration
->DriverObject
->DriverExtension
->AddDevice
;
534 registration
->DriverObject
->DriverExtension
->AddDevice
= driver_add_device
;
536 driver
->minidriver
= *registration
;
537 list_add_tail(&minidriver_list
, &driver
->entry
);
539 return STATUS_SUCCESS
;
542 NTSTATUS
call_minidriver(ULONG code
, DEVICE_OBJECT
*device
, void *in_buff
, ULONG in_size
, void *out_buff
, ULONG out_size
)
548 KeInitializeEvent(&event
, NotificationEvent
, FALSE
);
550 irp
= IoBuildDeviceIoControlRequest(code
, device
, in_buff
, in_size
,
551 out_buff
, out_size
, TRUE
, &event
, &io
);
553 if (IoCallDriver(device
, irp
) == STATUS_PENDING
)
554 KeWaitForSingleObject(&event
, Executive
, KernelMode
, FALSE
, NULL
);