kerberos: Move support for SpMakeSignature to the Unix library.
[wine.git] / dlls / hidclass.sys / pnp.c
blobde6f409a16b5c38d323620dcefd40439f7b162c1
1 /*
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
22 #include <unistd.h>
23 #include <stdarg.h>
24 #include "hid.h"
25 #include "ntddmou.h"
26 #include "ddk/hidtypes.h"
27 #include "ddk/wdm.h"
28 #include "regstr.h"
29 #include "wine/debug.h"
30 #include "wine/asm.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,
39 "popl %ecx\n\t"
40 "popl %eax\n\t"
41 "xchgl (%esp),%ecx\n\t"
42 "jmp *%eax" );
44 #define call_fastcall_func1(func,a) wrap_fastcall_func1(func,a)
46 #else
48 #define call_fastcall_func1(func,a) func(a)
50 #endif
52 static struct list minidriver_list = LIST_INIT(minidriver_list);
54 static minidriver *find_minidriver(DRIVER_OBJECT *driver)
56 minidriver *md;
57 LIST_FOR_EACH_ENTRY(md, &minidriver_list, minidriver, entry)
59 if (md->minidriver.DriverObject == driver)
60 return md;
62 return NULL;
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;
69 KEVENT event;
70 IRP *irp;
72 KeInitializeEvent(&event, NotificationEvent, FALSE);
73 irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, device, NULL, 0, NULL, &event, &irp_status);
74 if (irp == NULL)
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;
104 DEVICE_OBJECT *fdo;
105 NTSTATUS status;
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);
111 return status;
114 if ((status = get_device_id(bus_pdo, BusQueryInstanceID, instance_id)))
116 ERR("Failed to get PDO instance id, status %#x.\n", status);
117 return 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);
127 return status;
129 ext = fdo->DeviceExtension;
130 ext->is_fdo = TRUE;
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);
141 IoDeleteDevice(fdo);
142 return 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;
159 WCHAR pdo_name[255];
160 USAGE page, usage;
161 NTSTATUS status;
162 INT i;
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);
168 return;
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);
177 return;
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);
198 return;
200 for (i = 0; i < descriptor.bNumDescriptors; i++)
201 if (descriptor.DescriptorList[i].bReportType == HID_REPORT_DESCRIPTOR_TYPE)
202 break;
204 if (i >= descriptor.bNumDescriptors)
206 ERR("No Report Descriptor found in reply\n");
207 IoDeleteDevice(child_pdo);
208 return;
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);
219 return;
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);
228 return;
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;
241 else
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);
281 devices->Count = 1;
283 else
285 devices->Count = 0;
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:
296 NTSTATUS ret;
298 if ((ret = minidriver->PNPDispatch(device, irp)))
299 return ret;
300 create_child(minidriver, device);
301 return STATUS_SUCCESS;
304 case IRP_MN_REMOVE_DEVICE:
306 NTSTATUS ret;
308 ret = minidriver->PNPDispatch(device, irp);
310 IoDetachDevice(ext->u.fdo.hid_ext.NextDeviceObject);
311 IoDeleteDevice(device);
312 return ret;
315 default:
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:
339 WCHAR *ptr;
340 ptr = id;
341 /* Device instance ID */
342 lstrcpyW(ptr, ext->device_id);
343 ptr += lstrlenW(ext->device_id);
344 lstrcpyW(ptr, L"\\");
345 ptr += 1;
346 lstrcpyW(ptr, ext->instance_id);
347 ptr += lstrlenW(ext->instance_id) + 1;
348 /* Device ID */
349 lstrcpyW(ptr, ext->device_id);
350 ptr += lstrlenW(ext->device_id) + 1;
351 /* Bus ID */
352 lstrcpyW(ptr, L"HID");
353 ptr += lstrlenW(L"HID") + 1;
354 *ptr = 0;
355 irp->IoStatus.Information = (ULONG_PTR)id;
356 status = STATUS_SUCCESS;
357 break;
359 case BusQueryDeviceID:
360 lstrcpyW(id, ext->device_id);
361 irp->IoStatus.Information = (ULONG_PTR)id;
362 status = STATUS_SUCCESS;
363 break;
364 case BusQueryInstanceID:
365 lstrcpyW(id, ext->instance_id);
366 irp->IoStatus.Information = (ULONG_PTR)id;
367 status = STATUS_SUCCESS;
368 break;
370 case BusQueryContainerID:
371 case BusQueryDeviceSerialNumber:
372 FIXME("unimplemented id type %#x\n", irpsp->Parameters.QueryId.IdType);
373 ExFreePool(id);
374 break;
376 break;
379 case IRP_MN_QUERY_CAPABILITIES:
381 DEVICE_CAPABILITIES *caps = irpsp->Parameters.DeviceCapabilities.Capabilities;
383 caps->RawDeviceOK = 1;
384 status = STATUS_SUCCESS;
385 break;
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);
392 break;
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;
407 break;
409 case IRP_MN_REMOVE_DEVICE:
411 IRP *queued_irp;
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;
444 break;
446 default:
447 FIXME("Unhandled minor function %#x.\n", irpsp->MinorFunction);
450 irp->IoStatus.u.Status = status;
451 IoCompleteRequest( irp, IO_NO_INCREMENT );
452 return status;
455 static NTSTATUS WINAPI driver_pnp(DEVICE_OBJECT *device, IRP *irp)
457 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
459 if (ext->is_fdo)
460 return fdo_pnp(device, irp);
461 else
462 return pdo_pnp(device, irp);
465 static NTSTATUS WINAPI driver_create(DEVICE_OBJECT *device, IRP *irp)
467 BASE_DEVICE_EXTENSION *ext = device->DeviceExtension;
469 if (ext->is_fdo)
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)
501 minidriver *md;
503 TRACE("\n");
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)
516 minidriver *driver;
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)
544 IRP *irp;
545 IO_STATUS_BLOCK io;
546 KEVENT event;
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);
556 return io.u.Status;