e1bbe5830a954fb061fcd806d910825ffe40944d
[reactos.git] / drivers / input / kbdclass / kbdclass.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Keyboard class driver
4 * FILE: drivers/kbdclass/kbdclass.c
5 * PURPOSE: Keyboard class driver
6 *
7 * PROGRAMMERS: Hervé Poussineau (hpoussin@reactos.org)
8 */
9
10 #define INITGUID
11 #include "kbdclass.h"
12
13 static DRIVER_UNLOAD DriverUnload;
14 static DRIVER_DISPATCH ClassCreate;
15 static DRIVER_DISPATCH ClassClose;
16 static DRIVER_DISPATCH ClassCleanup;
17 static DRIVER_DISPATCH ClassRead;
18 static DRIVER_DISPATCH ClassDeviceControl;
19 static DRIVER_DISPATCH IrpStub;
20 static DRIVER_ADD_DEVICE ClassAddDevice;
21 static DRIVER_STARTIO ClassStartIo;
22 static DRIVER_CANCEL ClassCancelRoutine;
23 static NTSTATUS
24 HandleReadIrp(
25 IN PDEVICE_OBJECT DeviceObject,
26 IN PIRP Irp,
27 BOOLEAN IsInStartIo);
28
29 static VOID NTAPI
30 DriverUnload(IN PDRIVER_OBJECT DriverObject)
31 {
32 // nothing to do here yet
33 }
34
35 static NTSTATUS NTAPI
36 ClassCreate(
37 IN PDEVICE_OBJECT DeviceObject,
38 IN PIRP Irp)
39 {
40 TRACE_(CLASS_NAME, "IRP_MJ_CREATE\n");
41
42 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
43 return ForwardIrpAndForget(DeviceObject, Irp);
44
45 /* FIXME: open all associated Port devices */
46 Irp->IoStatus.Status = STATUS_SUCCESS;
47 Irp->IoStatus.Information = 0;
48 IoCompleteRequest(Irp, IO_NO_INCREMENT);
49 return STATUS_SUCCESS;
50 }
51
52 static NTSTATUS NTAPI
53 ClassClose(
54 IN PDEVICE_OBJECT DeviceObject,
55 IN PIRP Irp)
56 {
57 TRACE_(CLASS_NAME, "IRP_MJ_CLOSE\n");
58
59 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
60 return ForwardIrpAndForget(DeviceObject, Irp);
61
62 /* FIXME: close all associated Port devices */
63 Irp->IoStatus.Status = STATUS_SUCCESS;
64 Irp->IoStatus.Information = 0;
65 IoCompleteRequest(Irp, IO_NO_INCREMENT);
66 return STATUS_SUCCESS;
67 }
68
69 static NTSTATUS NTAPI
70 ClassCleanup(
71 IN PDEVICE_OBJECT DeviceObject,
72 IN PIRP Irp)
73 {
74 TRACE_(CLASS_NAME, "IRP_MJ_CLEANUP\n");
75
76 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
77 return ForwardIrpAndForget(DeviceObject, Irp);
78
79 /* FIXME: cleanup all associated Port devices */
80 Irp->IoStatus.Status = STATUS_SUCCESS;
81 Irp->IoStatus.Information = 0;
82 IoCompleteRequest(Irp, IO_NO_INCREMENT);
83 return STATUS_SUCCESS;
84 }
85
86 static NTSTATUS NTAPI
87 ClassRead(
88 IN PDEVICE_OBJECT DeviceObject,
89 IN PIRP Irp)
90 {
91 PCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
92 KIRQL OldIrql;
93 NTSTATUS Status;
94
95 TRACE_(CLASS_NAME, "IRP_MJ_READ\n");
96
97 ASSERT(DeviceExtension->Common.IsClassDO);
98
99 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
100 return ForwardIrpAndForget(DeviceObject, Irp);
101
102 if (IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length < sizeof(KEYBOARD_INPUT_DATA))
103 {
104 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
105 Irp->IoStatus.Information = 0;
106 IoCompleteRequest(Irp, IO_NO_INCREMENT);
107
108 return STATUS_BUFFER_TOO_SMALL;
109 }
110
111 KeAcquireSpinLock(&DeviceExtension->SpinLock, &OldIrql);
112 Status = HandleReadIrp(DeviceObject, Irp, FALSE);
113 KeReleaseSpinLock(&DeviceExtension->SpinLock, OldIrql);
114 return Status;
115 }
116
117 static NTSTATUS NTAPI
118 ClassDeviceControl(
119 IN PDEVICE_OBJECT DeviceObject,
120 IN PIRP Irp)
121 {
122 //PCLASS_DEVICE_EXTENSION DeviceExtension;
123 NTSTATUS Status = STATUS_NOT_SUPPORTED;
124
125 TRACE_(CLASS_NAME, "IRP_MJ_DEVICE_CONTROL\n");
126
127 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
128 return ForwardIrpAndForget(DeviceObject, Irp);
129
130 //DeviceExtension = (PCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
131
132 switch (IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode)
133 {
134 case IOCTL_KEYBOARD_QUERY_ATTRIBUTES:
135 case IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION:
136 case IOCTL_KEYBOARD_QUERY_INDICATORS:
137 case IOCTL_KEYBOARD_QUERY_TYPEMATIC:
138 {
139 /* FIXME: We hope that all devices will return the same result.
140 * Ask only the first one */
141 PLIST_ENTRY Head = &((PCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->ListHead;
142 if (Head->Flink != Head)
143 {
144 /* We have at least one device */
145 PPORT_DEVICE_EXTENSION DevExt = CONTAINING_RECORD(Head->Flink, PORT_DEVICE_EXTENSION, ListEntry);
146 IoGetCurrentIrpStackLocation(Irp)->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
147 IoSkipCurrentIrpStackLocation(Irp);
148 return IoCallDriver(DevExt->DeviceObject, Irp);
149 }
150 break;
151 }
152 case IOCTL_KEYBOARD_SET_INDICATORS:
153 case IOCTL_KEYBOARD_SET_TYPEMATIC: /* not in MSDN, would seem logical */
154 {
155 /* Send it to all associated Port devices */
156 PLIST_ENTRY Head = &((PCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->ListHead;
157 PLIST_ENTRY Entry = Head->Flink;
158 Status = STATUS_SUCCESS;
159 while (Entry != Head)
160 {
161 PPORT_DEVICE_EXTENSION DevExt = CONTAINING_RECORD(Entry, PORT_DEVICE_EXTENSION, ListEntry);
162 NTSTATUS IntermediateStatus;
163
164 IoGetCurrentIrpStackLocation(Irp)->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
165 IntermediateStatus = ForwardIrpAndWait(DevExt->DeviceObject, Irp);
166 if (!NT_SUCCESS(IntermediateStatus))
167 Status = IntermediateStatus;
168 Entry = Entry->Flink;
169 }
170 break;
171 }
172 default:
173 WARN_(CLASS_NAME, "IRP_MJ_DEVICE_CONTROL / unknown I/O control code 0x%lx\n",
174 IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode);
175 ASSERT(FALSE);
176 break;
177 }
178
179 Irp->IoStatus.Status = Status;
180 Irp->IoStatus.Information = 0;
181 IoCompleteRequest(Irp, IO_NO_INCREMENT);
182
183 return Status;
184 }
185
186 static NTSTATUS NTAPI
187 IrpStub(
188 IN PDEVICE_OBJECT DeviceObject,
189 IN PIRP Irp)
190 {
191 NTSTATUS Status = STATUS_NOT_SUPPORTED;
192
193 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
194 {
195 /* Forward some IRPs to lower device */
196 switch (IoGetCurrentIrpStackLocation(Irp)->MajorFunction)
197 {
198 case IRP_MJ_PNP:
199 case IRP_MJ_INTERNAL_DEVICE_CONTROL:
200 return ForwardIrpAndForget(DeviceObject, Irp);
201 default:
202 {
203 ERR_(CLASS_NAME, "Port DO stub for major function 0x%lx\n",
204 IoGetCurrentIrpStackLocation(Irp)->MajorFunction);
205 ASSERT(FALSE);
206 }
207 }
208 }
209 else
210 {
211 ERR_(CLASS_NAME, "Class DO stub for major function 0x%lx\n",
212 IoGetCurrentIrpStackLocation(Irp)->MajorFunction);
213 ASSERT(FALSE);
214 }
215
216 Irp->IoStatus.Status = Status;
217 IoCompleteRequest(Irp, IO_NO_INCREMENT);
218 return Status;
219 }
220
221 static NTSTATUS
222 ReadRegistryEntries(
223 IN PUNICODE_STRING RegistryPath,
224 IN PCLASS_DRIVER_EXTENSION DriverExtension)
225 {
226 UNICODE_STRING ParametersRegistryKey;
227 RTL_QUERY_REGISTRY_TABLE Parameters[4];
228 NTSTATUS Status;
229
230 /* HACK: We don't support multiple devices with this disabled */
231 ULONG DefaultConnectMultiplePorts = 1;
232 ULONG DefaultDataQueueSize = 0x64;
233 PCWSTR DefaultDeviceBaseName = L"KeyboardClass";
234
235 ParametersRegistryKey.Length = 0;
236 ParametersRegistryKey.MaximumLength = RegistryPath->Length + sizeof(L"\\Parameters") + sizeof(UNICODE_NULL);
237 ParametersRegistryKey.Buffer = ExAllocatePoolWithTag(PagedPool, ParametersRegistryKey.MaximumLength, CLASS_TAG);
238 if (!ParametersRegistryKey.Buffer)
239 {
240 WARN_(CLASS_NAME, "ExAllocatePoolWithTag() failed\n");
241 return STATUS_NO_MEMORY;
242 }
243 RtlCopyUnicodeString(&ParametersRegistryKey, RegistryPath);
244 RtlAppendUnicodeToString(&ParametersRegistryKey, L"\\Parameters");
245 ParametersRegistryKey.Buffer[ParametersRegistryKey.Length / sizeof(WCHAR)] = UNICODE_NULL;
246
247 RtlZeroMemory(Parameters, sizeof(Parameters));
248
249 Parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL;
250 Parameters[0].Name = L"ConnectMultiplePorts";
251 Parameters[0].EntryContext = &DriverExtension->ConnectMultiplePorts;
252 Parameters[0].DefaultType = REG_DWORD;
253 Parameters[0].DefaultData = &DefaultConnectMultiplePorts;
254 Parameters[0].DefaultLength = sizeof(ULONG);
255
256 Parameters[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL;
257 Parameters[1].Name = L"KeyboardDataQueueSize";
258 Parameters[1].EntryContext = &DriverExtension->DataQueueSize;
259 Parameters[1].DefaultType = REG_DWORD;
260 Parameters[1].DefaultData = &DefaultDataQueueSize;
261 Parameters[1].DefaultLength = sizeof(ULONG);
262
263 Parameters[2].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL;
264 Parameters[2].Name = L"KeyboardDeviceBaseName";
265 Parameters[2].EntryContext = &DriverExtension->DeviceBaseName;
266 Parameters[2].DefaultType = REG_SZ;
267 Parameters[2].DefaultData = (PVOID)DefaultDeviceBaseName;
268 Parameters[2].DefaultLength = 0;
269
270 Status = RtlQueryRegistryValues(
271 RTL_REGISTRY_ABSOLUTE,
272 ParametersRegistryKey.Buffer,
273 Parameters,
274 NULL,
275 NULL);
276
277 if (NT_SUCCESS(Status))
278 {
279 /* Check values */
280 if (DriverExtension->ConnectMultiplePorts != 0
281 && DriverExtension->ConnectMultiplePorts != 1)
282 {
283 DriverExtension->ConnectMultiplePorts = DefaultConnectMultiplePorts;
284 }
285 if (DriverExtension->DataQueueSize == 0)
286 {
287 DriverExtension->DataQueueSize = DefaultDataQueueSize;
288 }
289 }
290 else if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
291 {
292 /* Registry path doesn't exist. Set defaults */
293 DriverExtension->ConnectMultiplePorts = DefaultConnectMultiplePorts;
294 DriverExtension->DataQueueSize = DefaultDataQueueSize;
295 if (RtlCreateUnicodeString(&DriverExtension->DeviceBaseName, DefaultDeviceBaseName))
296 Status = STATUS_SUCCESS;
297 else
298 Status = STATUS_NO_MEMORY;
299 }
300
301 ExFreePoolWithTag(ParametersRegistryKey.Buffer, CLASS_TAG);
302 return Status;
303 }
304
305 static NTSTATUS
306 CreateClassDeviceObject(
307 IN PDRIVER_OBJECT DriverObject,
308 OUT PDEVICE_OBJECT *ClassDO OPTIONAL)
309 {
310 PCLASS_DRIVER_EXTENSION DriverExtension;
311 ULONG DeviceId = 0;
312 ULONG PrefixLength;
313 UNICODE_STRING DeviceNameU;
314 PWSTR DeviceIdW = NULL; /* Pointer into DeviceNameU.Buffer */
315 PDEVICE_OBJECT Fdo;
316 PCLASS_DEVICE_EXTENSION DeviceExtension;
317 NTSTATUS Status;
318
319 TRACE_(CLASS_NAME, "CreateClassDeviceObject(0x%p)\n", DriverObject);
320
321 /* Create new device object */
322 DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject);
323 DeviceNameU.Length = 0;
324 DeviceNameU.MaximumLength =
325 wcslen(L"\\Device\\") * sizeof(WCHAR) /* "\Device\" */
326 + DriverExtension->DeviceBaseName.Length /* "KeyboardClass" */
327 + 4 * sizeof(WCHAR) /* Id between 0 and 9999 */
328 + sizeof(UNICODE_NULL); /* Final NULL char */
329 DeviceNameU.Buffer = ExAllocatePoolWithTag(PagedPool, DeviceNameU.MaximumLength, CLASS_TAG);
330 if (!DeviceNameU.Buffer)
331 {
332 WARN_(CLASS_NAME, "ExAllocatePoolWithTag() failed\n");
333 return STATUS_NO_MEMORY;
334 }
335 Status = RtlAppendUnicodeToString(&DeviceNameU, L"\\Device\\");
336 if (!NT_SUCCESS(Status))
337 {
338 WARN_(CLASS_NAME, "RtlAppendUnicodeToString() failed with status 0x%08lx\n", Status);
339 goto cleanup;
340 }
341 Status = RtlAppendUnicodeStringToString(&DeviceNameU, &DriverExtension->DeviceBaseName);
342 if (!NT_SUCCESS(Status))
343 {
344 WARN_(CLASS_NAME, "RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status);
345 goto cleanup;
346 }
347 PrefixLength = DeviceNameU.MaximumLength - 4 * sizeof(WCHAR) - sizeof(UNICODE_NULL);
348 DeviceIdW = &DeviceNameU.Buffer[PrefixLength / sizeof(WCHAR)];
349 while (DeviceId < 9999)
350 {
351 DeviceNameU.Length = (USHORT)(PrefixLength + swprintf(DeviceIdW, L"%lu", DeviceId) * sizeof(WCHAR));
352 Status = IoCreateDevice(
353 DriverObject,
354 sizeof(CLASS_DEVICE_EXTENSION),
355 &DeviceNameU,
356 FILE_DEVICE_KEYBOARD,
357 FILE_DEVICE_SECURE_OPEN,
358 FALSE,
359 &Fdo);
360 if (NT_SUCCESS(Status))
361 goto cleanup;
362 else if (Status != STATUS_OBJECT_NAME_COLLISION)
363 {
364 WARN_(CLASS_NAME, "IoCreateDevice() failed with status 0x%08lx\n", Status);
365 goto cleanup;
366 }
367 DeviceId++;
368 }
369 WARN_(CLASS_NAME, "Too many devices starting with '\\Device\\%wZ'\n", &DriverExtension->DeviceBaseName);
370 Status = STATUS_TOO_MANY_NAMES;
371 cleanup:
372 if (!NT_SUCCESS(Status))
373 {
374 ExFreePoolWithTag(DeviceNameU.Buffer, CLASS_TAG);
375 return Status;
376 }
377
378 DeviceExtension = (PCLASS_DEVICE_EXTENSION)Fdo->DeviceExtension;
379 RtlZeroMemory(DeviceExtension, sizeof(CLASS_DEVICE_EXTENSION));
380 DeviceExtension->Common.IsClassDO = TRUE;
381 DeviceExtension->DriverExtension = DriverExtension;
382 InitializeListHead(&DeviceExtension->ListHead);
383 KeInitializeSpinLock(&DeviceExtension->ListSpinLock);
384 KeInitializeSpinLock(&DeviceExtension->SpinLock);
385 DeviceExtension->InputCount = 0;
386 DeviceExtension->PortData = ExAllocatePoolWithTag(NonPagedPool, DeviceExtension->DriverExtension->DataQueueSize * sizeof(KEYBOARD_INPUT_DATA), CLASS_TAG);
387 if (!DeviceExtension->PortData)
388 {
389 ExFreePoolWithTag(DeviceNameU.Buffer, CLASS_TAG);
390 return STATUS_NO_MEMORY;
391 }
392 DeviceExtension->DeviceName = DeviceNameU.Buffer;
393 Fdo->Flags |= DO_POWER_PAGABLE;
394 Fdo->Flags |= DO_BUFFERED_IO; /* FIXME: Why is it needed for 1st stage setup? */
395 Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
396
397 /* Add entry entry to HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\[DeviceBaseName] */
398 RtlWriteRegistryValue(
399 RTL_REGISTRY_DEVICEMAP,
400 DriverExtension->DeviceBaseName.Buffer,
401 DeviceExtension->DeviceName,
402 REG_SZ,
403 DriverExtension->RegistryPath.Buffer,
404 DriverExtension->RegistryPath.MaximumLength);
405
406 if (ClassDO)
407 *ClassDO = Fdo;
408
409 return STATUS_SUCCESS;
410 }
411
412 static NTSTATUS
413 FillEntries(
414 IN PDEVICE_OBJECT ClassDeviceObject,
415 IN PIRP Irp,
416 IN PKEYBOARD_INPUT_DATA DataStart,
417 IN SIZE_T NumberOfEntries)
418 {
419 NTSTATUS Status = STATUS_SUCCESS;
420
421 if (ClassDeviceObject->Flags & DO_BUFFERED_IO)
422 {
423 RtlCopyMemory(
424 Irp->AssociatedIrp.SystemBuffer,
425 DataStart,
426 NumberOfEntries * sizeof(KEYBOARD_INPUT_DATA));
427 }
428 else if (ClassDeviceObject->Flags & DO_DIRECT_IO)
429 {
430 PVOID DestAddress = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
431 if (DestAddress)
432 {
433 RtlCopyMemory(
434 DestAddress,
435 DataStart,
436 NumberOfEntries * sizeof(KEYBOARD_INPUT_DATA));
437 }
438 else
439 Status = STATUS_UNSUCCESSFUL;
440 }
441 else
442 {
443 _SEH2_TRY
444 {
445 RtlCopyMemory(
446 Irp->UserBuffer,
447 DataStart,
448 NumberOfEntries * sizeof(KEYBOARD_INPUT_DATA));
449 }
450 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
451 {
452 Status = _SEH2_GetExceptionCode();
453 }
454 _SEH2_END;
455 }
456
457 return Status;
458 }
459
460 static BOOLEAN NTAPI
461 ClassCallback(
462 IN PDEVICE_OBJECT ClassDeviceObject,
463 IN OUT PKEYBOARD_INPUT_DATA DataStart,
464 IN PKEYBOARD_INPUT_DATA DataEnd,
465 IN OUT PULONG ConsumedCount)
466 {
467 PCLASS_DEVICE_EXTENSION ClassDeviceExtension = ClassDeviceObject->DeviceExtension;
468 KIRQL OldIrql;
469 SIZE_T InputCount = DataEnd - DataStart;
470 SIZE_T ReadSize;
471
472 TRACE_(CLASS_NAME, "ClassCallback()\n");
473
474 ASSERT(ClassDeviceExtension->Common.IsClassDO);
475
476 KeAcquireSpinLock(&ClassDeviceExtension->SpinLock, &OldIrql);
477 if (InputCount > 0)
478 {
479 if (ClassDeviceExtension->InputCount + InputCount > ClassDeviceExtension->DriverExtension->DataQueueSize)
480 {
481 /*
482 * We're exceeding the buffer, and data will be thrown away...
483 * FIXME: What could we do, as we are at DISPATCH_LEVEL?
484 */
485 ReadSize = ClassDeviceExtension->DriverExtension->DataQueueSize - ClassDeviceExtension->InputCount;
486 }
487 else
488 ReadSize = InputCount;
489
490 /*
491 * Move the input data from the port data queue to our class data
492 * queue.
493 */
494 RtlCopyMemory(
495 &ClassDeviceExtension->PortData[ClassDeviceExtension->InputCount],
496 (PCHAR)DataStart,
497 sizeof(KEYBOARD_INPUT_DATA) * ReadSize);
498
499 /* Move the counter up */
500 ClassDeviceExtension->InputCount += ReadSize;
501
502 (*ConsumedCount) += (ULONG)ReadSize;
503
504 /* Complete pending IRP (if any) */
505 if (ClassDeviceExtension->PendingIrp)
506 HandleReadIrp(ClassDeviceObject, ClassDeviceExtension->PendingIrp, FALSE);
507 }
508 KeReleaseSpinLock(&ClassDeviceExtension->SpinLock, OldIrql);
509
510 TRACE_(CLASS_NAME, "Leaving ClassCallback()\n");
511 return TRUE;
512 }
513
514 /* Send IOCTL_INTERNAL_*_CONNECT to port */
515 static NTSTATUS
516 ConnectPortDriver(
517 IN PDEVICE_OBJECT PortDO,
518 IN PDEVICE_OBJECT ClassDO)
519 {
520 KEVENT Event;
521 PIRP Irp;
522 IO_STATUS_BLOCK IoStatus;
523 CONNECT_DATA ConnectData;
524 NTSTATUS Status;
525
526 TRACE_(CLASS_NAME, "Connecting PortDO %p to ClassDO %p\n", PortDO, ClassDO);
527
528 KeInitializeEvent(&Event, NotificationEvent, FALSE);
529
530 ConnectData.ClassDeviceObject = ClassDO;
531 ConnectData.ClassService = ClassCallback;
532
533 Irp = IoBuildDeviceIoControlRequest(
534 IOCTL_INTERNAL_KEYBOARD_CONNECT,
535 PortDO,
536 &ConnectData, sizeof(CONNECT_DATA),
537 NULL, 0,
538 TRUE, &Event, &IoStatus);
539 if (!Irp)
540 return STATUS_INSUFFICIENT_RESOURCES;
541
542 Status = IoCallDriver(PortDO, Irp);
543
544 if (Status == STATUS_PENDING)
545 KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
546 else
547 IoStatus.Status = Status;
548
549 if (NT_SUCCESS(IoStatus.Status))
550 {
551 ObReferenceObject(PortDO);
552 ExInterlockedInsertTailList(
553 &((PCLASS_DEVICE_EXTENSION)ClassDO->DeviceExtension)->ListHead,
554 &((PPORT_DEVICE_EXTENSION)PortDO->DeviceExtension)->ListEntry,
555 &((PCLASS_DEVICE_EXTENSION)ClassDO->DeviceExtension)->ListSpinLock);
556 if (ClassDO->StackSize <= PortDO->StackSize)
557 {
558 /* Increase the stack size, in case we have to
559 * forward some IRPs to the port device object
560 */
561 ClassDO->StackSize = PortDO->StackSize + 1;
562 }
563 }
564
565 return IoStatus.Status;
566 }
567
568 /* Send IOCTL_INTERNAL_*_DISCONNECT to port + destroy the Port DO */
569 static VOID
570 DestroyPortDriver(
571 IN PDEVICE_OBJECT PortDO)
572 {
573 PPORT_DEVICE_EXTENSION DeviceExtension;
574 PCLASS_DEVICE_EXTENSION ClassDeviceExtension;
575 PCLASS_DRIVER_EXTENSION DriverExtension;
576 KEVENT Event;
577 PIRP Irp;
578 IO_STATUS_BLOCK IoStatus;
579 KIRQL OldIrql;
580 NTSTATUS Status;
581
582 TRACE_(CLASS_NAME, "Destroying PortDO %p\n", PortDO);
583
584 DeviceExtension = (PPORT_DEVICE_EXTENSION)PortDO->DeviceExtension;
585 ClassDeviceExtension = DeviceExtension->ClassDO->DeviceExtension;
586 DriverExtension = IoGetDriverObjectExtension(PortDO->DriverObject, PortDO->DriverObject);
587
588 /* Send IOCTL_INTERNAL_*_DISCONNECT */
589 KeInitializeEvent(&Event, NotificationEvent, FALSE);
590 Irp = IoBuildDeviceIoControlRequest(
591 IOCTL_INTERNAL_KEYBOARD_DISCONNECT,
592 PortDO,
593 NULL, 0,
594 NULL, 0,
595 TRUE, &Event, &IoStatus);
596 if (Irp)
597 {
598 Status = IoCallDriver(PortDO, Irp);
599 if (Status == STATUS_PENDING)
600 KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
601 }
602
603 /* Remove from ClassDeviceExtension->ListHead list */
604 KeAcquireSpinLock(&ClassDeviceExtension->ListSpinLock, &OldIrql);
605 RemoveHeadList(DeviceExtension->ListEntry.Blink);
606 KeReleaseSpinLock(&ClassDeviceExtension->ListSpinLock, OldIrql);
607
608 /* Remove entry from HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\[DeviceBaseName] */
609 RtlDeleteRegistryValue(
610 RTL_REGISTRY_DEVICEMAP,
611 DriverExtension->DeviceBaseName.Buffer,
612 ClassDeviceExtension->DeviceName);
613
614 if (DeviceExtension->LowerDevice)
615 IoDetachDevice(DeviceExtension->LowerDevice);
616 ObDereferenceObject(PortDO);
617
618 if (!DriverExtension->ConnectMultiplePorts && DeviceExtension->ClassDO)
619 {
620 ExFreePoolWithTag(ClassDeviceExtension->PortData, CLASS_TAG);
621 ExFreePoolWithTag((PVOID)ClassDeviceExtension->DeviceName, CLASS_TAG);
622 IoDeleteDevice(DeviceExtension->ClassDO);
623 }
624
625 IoDeleteDevice(PortDO);
626 }
627
628 static NTSTATUS NTAPI
629 ClassAddDevice(
630 IN PDRIVER_OBJECT DriverObject,
631 IN PDEVICE_OBJECT Pdo)
632 {
633 PCLASS_DRIVER_EXTENSION DriverExtension;
634 PDEVICE_OBJECT Fdo = NULL;
635 PPORT_DEVICE_EXTENSION DeviceExtension = NULL;
636 NTSTATUS Status;
637
638 TRACE_(CLASS_NAME, "ClassAddDevice called. Pdo = 0x%p\n", Pdo);
639
640 DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject);
641
642 if (Pdo == NULL)
643 /* We may get a NULL Pdo at the first call as we're a legacy driver. Ignore it */
644 return STATUS_SUCCESS;
645
646 /* Create new device object */
647 Status = IoCreateDevice(
648 DriverObject,
649 sizeof(PORT_DEVICE_EXTENSION),
650 NULL,
651 Pdo->DeviceType,
652 Pdo->Characteristics & FILE_DEVICE_SECURE_OPEN ? FILE_DEVICE_SECURE_OPEN : 0,
653 FALSE,
654 &Fdo);
655 if (!NT_SUCCESS(Status))
656 {
657 WARN_(CLASS_NAME, "IoCreateDevice() failed with status 0x%08lx\n", Status);
658 goto cleanup;
659 }
660 IoSetStartIoAttributes(Fdo, TRUE, TRUE);
661
662 DeviceExtension = (PPORT_DEVICE_EXTENSION)Fdo->DeviceExtension;
663 RtlZeroMemory(DeviceExtension, sizeof(PORT_DEVICE_EXTENSION));
664 DeviceExtension->Common.IsClassDO = FALSE;
665 DeviceExtension->DeviceObject = Fdo;
666 DeviceExtension->PnpState = dsStopped;
667 Status = IoAttachDeviceToDeviceStackSafe(Fdo, Pdo, &DeviceExtension->LowerDevice);
668 if (!NT_SUCCESS(Status))
669 {
670 WARN_(CLASS_NAME, "IoAttachDeviceToDeviceStackSafe() failed with status 0x%08lx\n", Status);
671 goto cleanup;
672 }
673 if (DeviceExtension->LowerDevice->Flags & DO_POWER_PAGABLE)
674 Fdo->Flags |= DO_POWER_PAGABLE;
675 if (DeviceExtension->LowerDevice->Flags & DO_BUFFERED_IO)
676 Fdo->Flags |= DO_BUFFERED_IO;
677 if (DeviceExtension->LowerDevice->Flags & DO_DIRECT_IO)
678 Fdo->Flags |= DO_DIRECT_IO;
679
680 if (DriverExtension->ConnectMultiplePorts)
681 DeviceExtension->ClassDO = DriverExtension->MainClassDeviceObject;
682 else
683 {
684 /* We need a new class device object for this Fdo */
685 Status = CreateClassDeviceObject(
686 DriverObject,
687 &DeviceExtension->ClassDO);
688 if (!NT_SUCCESS(Status))
689 {
690 WARN_(CLASS_NAME, "CreateClassDeviceObject() failed with status 0x%08lx\n", Status);
691 goto cleanup;
692 }
693 }
694 Status = ConnectPortDriver(Fdo, DeviceExtension->ClassDO);
695 if (!NT_SUCCESS(Status))
696 {
697 WARN_(CLASS_NAME, "ConnectPortDriver() failed with status 0x%08lx\n", Status);
698 goto cleanup;
699 }
700 Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
701
702 /* Register interface ; ignore the error (if any) as having
703 * a registred interface is not so important... */
704 Status = IoRegisterDeviceInterface(
705 Pdo,
706 &GUID_DEVINTERFACE_KEYBOARD,
707 NULL,
708 &DeviceExtension->InterfaceName);
709 if (!NT_SUCCESS(Status))
710 DeviceExtension->InterfaceName.Length = 0;
711
712 return STATUS_SUCCESS;
713
714 cleanup:
715 if (Fdo)
716 DestroyPortDriver(Fdo);
717 return Status;
718 }
719
720 static VOID NTAPI
721 ClassCancelRoutine(
722 IN PDEVICE_OBJECT DeviceObject,
723 IN PIRP Irp)
724 {
725 PCLASS_DEVICE_EXTENSION ClassDeviceExtension = DeviceObject->DeviceExtension;
726 KIRQL OldIrql;
727 BOOLEAN wasQueued = FALSE;
728
729 TRACE_(CLASS_NAME, "ClassCancelRoutine(DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
730
731 ASSERT(ClassDeviceExtension->Common.IsClassDO);
732
733 IoReleaseCancelSpinLock(Irp->CancelIrql);
734
735 KeAcquireSpinLock(&ClassDeviceExtension->SpinLock, &OldIrql);
736
737 if (ClassDeviceExtension->PendingIrp == Irp)
738 {
739 ClassDeviceExtension->PendingIrp = NULL;
740 wasQueued = TRUE;
741 }
742 KeReleaseSpinLock(&ClassDeviceExtension->SpinLock, OldIrql);
743
744 if (wasQueued)
745 {
746 Irp->IoStatus.Status = STATUS_CANCELLED;
747 Irp->IoStatus.Information = 0;
748 IoCompleteRequest(Irp, IO_NO_INCREMENT);
749 }
750 else
751 {
752 /* Hm, this shouldn't happen */
753 ASSERT(FALSE);
754 }
755 }
756
757 static NTSTATUS
758 HandleReadIrp(
759 IN PDEVICE_OBJECT DeviceObject,
760 IN PIRP Irp,
761 BOOLEAN IsInStartIo)
762 {
763 PCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
764 NTSTATUS Status;
765 KIRQL OldIrql;
766
767 TRACE_(CLASS_NAME, "HandleReadIrp(DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
768
769 ASSERT(DeviceExtension->Common.IsClassDO);
770
771 if (DeviceExtension->InputCount > 0)
772 {
773 SIZE_T NumberOfEntries;
774
775 NumberOfEntries = MIN(
776 DeviceExtension->InputCount,
777 IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length / sizeof(KEYBOARD_INPUT_DATA));
778
779 Status = FillEntries(
780 DeviceObject,
781 Irp,
782 DeviceExtension->PortData,
783 NumberOfEntries);
784
785 if (NT_SUCCESS(Status))
786 {
787 if (DeviceExtension->InputCount > NumberOfEntries)
788 {
789 RtlMoveMemory(
790 &DeviceExtension->PortData[0],
791 &DeviceExtension->PortData[NumberOfEntries],
792 (DeviceExtension->InputCount - NumberOfEntries) * sizeof(KEYBOARD_INPUT_DATA));
793 }
794
795 DeviceExtension->InputCount -= NumberOfEntries;
796
797 Irp->IoStatus.Information = NumberOfEntries * sizeof(KEYBOARD_INPUT_DATA);
798 }
799
800 /* Go to next packet and complete this request */
801 Irp->IoStatus.Status = Status;
802
803 (VOID)IoSetCancelRoutine(Irp, NULL);
804 IoCompleteRequest(Irp, IO_KEYBOARD_INCREMENT);
805 DeviceExtension->PendingIrp = NULL;
806 }
807 else
808 {
809 IoAcquireCancelSpinLock(&OldIrql);
810 if (Irp->Cancel)
811 {
812 DeviceExtension->PendingIrp = NULL;
813 Status = STATUS_CANCELLED;
814 }
815 else
816 {
817 IoMarkIrpPending(Irp);
818 DeviceExtension->PendingIrp = Irp;
819 (VOID)IoSetCancelRoutine(Irp, ClassCancelRoutine);
820 Status = STATUS_PENDING;
821 }
822 IoReleaseCancelSpinLock(OldIrql);
823 }
824 return Status;
825 }
826
827 static NTSTATUS NTAPI
828 ClassPnp(
829 IN PDEVICE_OBJECT DeviceObject,
830 IN PIRP Irp)
831 {
832 PPORT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
833 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
834 OBJECT_ATTRIBUTES ObjectAttributes;
835 IO_STATUS_BLOCK Iosb;
836 NTSTATUS Status;
837
838 switch (IrpSp->MinorFunction)
839 {
840 case IRP_MN_START_DEVICE:
841 Status = ForwardIrpAndWait(DeviceObject, Irp);
842 if (NT_SUCCESS(Status))
843 {
844 InitializeObjectAttributes(&ObjectAttributes,
845 &DeviceExtension->InterfaceName,
846 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
847 NULL,
848 NULL);
849
850 Status = ZwOpenFile(&DeviceExtension->FileHandle,
851 FILE_READ_DATA,
852 &ObjectAttributes,
853 &Iosb,
854 0,
855 0);
856 if (!NT_SUCCESS(Status))
857 DeviceExtension->FileHandle = NULL;
858 }
859 else
860 DeviceExtension->FileHandle = NULL;
861 Irp->IoStatus.Status = Status;
862 IoCompleteRequest(Irp, IO_NO_INCREMENT);
863 return Status;
864
865 case IRP_MN_REMOVE_DEVICE:
866 case IRP_MN_STOP_DEVICE:
867 if (DeviceExtension->FileHandle)
868 {
869 ZwClose(DeviceExtension->FileHandle);
870 DeviceExtension->FileHandle = NULL;
871 }
872 Status = STATUS_SUCCESS;
873 break;
874
875 default:
876 Status = Irp->IoStatus.Status;
877 break;
878 }
879
880 Irp->IoStatus.Status = Status;
881 if (NT_SUCCESS(Status) || Status == STATUS_NOT_SUPPORTED)
882 {
883 IoSkipCurrentIrpStackLocation(Irp);
884 return IoCallDriver(DeviceExtension->LowerDevice, Irp);
885 }
886 else
887 {
888 IoCompleteRequest(Irp, IO_NO_INCREMENT);
889 return Status;
890 }
891 }
892
893 static VOID NTAPI
894 ClassStartIo(
895 IN PDEVICE_OBJECT DeviceObject,
896 IN PIRP Irp)
897 {
898 PCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
899 KIRQL OldIrql;
900
901 TRACE_(CLASS_NAME, "ClassStartIo(DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
902
903 ASSERT(DeviceExtension->Common.IsClassDO);
904
905 KeAcquireSpinLock(&DeviceExtension->SpinLock, &OldIrql);
906 HandleReadIrp(DeviceObject, Irp, TRUE);
907 KeReleaseSpinLock(&DeviceExtension->SpinLock, OldIrql);
908 }
909
910 static VOID NTAPI
911 SearchForLegacyDrivers(
912 IN PDRIVER_OBJECT DriverObject,
913 IN PVOID Context, /* PCLASS_DRIVER_EXTENSION */
914 IN ULONG Count)
915 {
916 UNICODE_STRING DeviceMapKeyU = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\HARDWARE\\DEVICEMAP");
917 PCLASS_DRIVER_EXTENSION DriverExtension;
918 UNICODE_STRING PortBaseName = { 0, 0, NULL };
919 PKEY_VALUE_BASIC_INFORMATION KeyValueInformation = NULL;
920 OBJECT_ATTRIBUTES ObjectAttributes;
921 HANDLE hDeviceMapKey = (HANDLE)-1;
922 HANDLE hPortKey = (HANDLE)-1;
923 ULONG Index = 0;
924 ULONG Size, ResultLength;
925 NTSTATUS Status;
926
927 TRACE_(CLASS_NAME, "SearchForLegacyDrivers(%p %p %lu)\n",
928 DriverObject, Context, Count);
929
930 if (Count != 1)
931 return;
932 DriverExtension = (PCLASS_DRIVER_EXTENSION)Context;
933
934 /* Create port base name, by replacing Class by Port at the end of the class base name */
935 Status = DuplicateUnicodeString(
936 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
937 &DriverExtension->DeviceBaseName,
938 &PortBaseName);
939 if (!NT_SUCCESS(Status))
940 {
941 WARN_(CLASS_NAME, "DuplicateUnicodeString() failed with status 0x%08lx\n", Status);
942 goto cleanup;
943 }
944 PortBaseName.Length -= (sizeof(L"Class") - sizeof(UNICODE_NULL));
945 RtlAppendUnicodeToString(&PortBaseName, L"Port");
946
947 /* Allocate memory */
948 Size = sizeof(KEY_VALUE_BASIC_INFORMATION) + MAX_PATH;
949 KeyValueInformation = ExAllocatePoolWithTag(PagedPool, Size, CLASS_TAG);
950 if (!KeyValueInformation)
951 {
952 WARN_(CLASS_NAME, "ExAllocatePoolWithTag() failed\n");
953 Status = STATUS_NO_MEMORY;
954 goto cleanup;
955 }
956
957 /* Open HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP */
958 InitializeObjectAttributes(&ObjectAttributes, &DeviceMapKeyU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
959 Status = ZwOpenKey(&hDeviceMapKey, 0, &ObjectAttributes);
960 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
961 {
962 INFO_(CLASS_NAME, "HKLM\\HARDWARE\\DEVICEMAP is non-existent\n");
963 Status = STATUS_SUCCESS;
964 goto cleanup;
965 }
966 else if (!NT_SUCCESS(Status))
967 {
968 WARN_(CLASS_NAME, "ZwOpenKey() failed with status 0x%08lx\n", Status);
969 goto cleanup;
970 }
971
972 /* Open sub key */
973 InitializeObjectAttributes(&ObjectAttributes, &PortBaseName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, hDeviceMapKey, NULL);
974 Status = ZwOpenKey(&hPortKey, KEY_QUERY_VALUE, &ObjectAttributes);
975 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
976 {
977 INFO_(CLASS_NAME, "HKLM\\HARDWARE\\DEVICEMAP\\%wZ is non-existent\n", &PortBaseName);
978 Status = STATUS_SUCCESS;
979 goto cleanup;
980 }
981 else if (!NT_SUCCESS(Status))
982 {
983 WARN_(CLASS_NAME, "ZwOpenKey() failed with status 0x%08lx\n", Status);
984 goto cleanup;
985 }
986
987 /* Read each value name */
988 while (ZwEnumerateValueKey(hPortKey, Index++, KeyValueBasicInformation, KeyValueInformation, Size, &ResultLength) == STATUS_SUCCESS)
989 {
990 UNICODE_STRING PortName;
991 PDEVICE_OBJECT PortDeviceObject = NULL;
992 PFILE_OBJECT FileObject = NULL;
993
994 PortName.Length = PortName.MaximumLength = (USHORT)KeyValueInformation->NameLength;
995 PortName.Buffer = KeyValueInformation->Name;
996
997 /* Open the device object pointer */
998 Status = IoGetDeviceObjectPointer(&PortName, FILE_READ_ATTRIBUTES, &FileObject, &PortDeviceObject);
999 if (!NT_SUCCESS(Status))
1000 {
1001 WARN_(CLASS_NAME, "IoGetDeviceObjectPointer(%wZ) failed with status 0x%08lx\n", &PortName, Status);
1002 continue;
1003 }
1004 INFO_(CLASS_NAME, "Legacy driver found\n");
1005
1006 Status = ClassAddDevice(DriverObject, PortDeviceObject);
1007 if (!NT_SUCCESS(Status))
1008 {
1009 /* FIXME: Log the error */
1010 WARN_(CLASS_NAME, "ClassAddDevice() failed with status 0x%08lx\n", Status);
1011 }
1012
1013 /* A special hack for 1st stage setup: manually send start device to i8042prt */
1014 if (IsFirstStageSetup())
1015 Send8042StartDevice(DriverObject, PortDeviceObject);
1016 }
1017
1018 cleanup:
1019 if (KeyValueInformation != NULL)
1020 ExFreePoolWithTag(KeyValueInformation, CLASS_TAG);
1021 if (hDeviceMapKey != (HANDLE)-1)
1022 ZwClose(hDeviceMapKey);
1023 if (hPortKey != (HANDLE)-1)
1024 ZwClose(hPortKey);
1025 }
1026
1027 /*
1028 * Standard DriverEntry method.
1029 */
1030 NTSTATUS NTAPI
1031 DriverEntry(
1032 IN PDRIVER_OBJECT DriverObject,
1033 IN PUNICODE_STRING RegistryPath)
1034 {
1035 PCLASS_DRIVER_EXTENSION DriverExtension;
1036 ULONG i;
1037 NTSTATUS Status;
1038
1039 Status = IoAllocateDriverObjectExtension(
1040 DriverObject,
1041 DriverObject,
1042 sizeof(CLASS_DRIVER_EXTENSION),
1043 (PVOID*)&DriverExtension);
1044 if (!NT_SUCCESS(Status))
1045 {
1046 WARN_(CLASS_NAME, "IoAllocateDriverObjectExtension() failed with status 0x%08lx\n", Status);
1047 return Status;
1048 }
1049 RtlZeroMemory(DriverExtension, sizeof(CLASS_DRIVER_EXTENSION));
1050
1051 Status = DuplicateUnicodeString(
1052 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
1053 RegistryPath,
1054 &DriverExtension->RegistryPath);
1055 if (!NT_SUCCESS(Status))
1056 {
1057 WARN_(CLASS_NAME, "DuplicateUnicodeString() failed with status 0x%08lx\n", Status);
1058 return Status;
1059 }
1060
1061 Status = ReadRegistryEntries(RegistryPath, DriverExtension);
1062 if (!NT_SUCCESS(Status))
1063 {
1064 WARN_(CLASS_NAME, "ReadRegistryEntries() failed with status 0x%08lx\n", Status);
1065 return Status;
1066 }
1067
1068 if (DriverExtension->ConnectMultiplePorts == 1)
1069 {
1070 Status = CreateClassDeviceObject(
1071 DriverObject,
1072 &DriverExtension->MainClassDeviceObject);
1073 if (!NT_SUCCESS(Status))
1074 {
1075 WARN_(CLASS_NAME, "CreateClassDeviceObject() failed with status 0x%08lx\n", Status);
1076 return Status;
1077 }
1078 }
1079
1080 DriverObject->DriverExtension->AddDevice = ClassAddDevice;
1081 DriverObject->DriverUnload = DriverUnload;
1082
1083 for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
1084 DriverObject->MajorFunction[i] = IrpStub;
1085
1086 DriverObject->MajorFunction[IRP_MJ_CREATE] = ClassCreate;
1087 DriverObject->MajorFunction[IRP_MJ_CLOSE] = ClassClose;
1088 DriverObject->MajorFunction[IRP_MJ_CLEANUP] = ClassCleanup;
1089 DriverObject->MajorFunction[IRP_MJ_READ] = ClassRead;
1090 DriverObject->MajorFunction[IRP_MJ_PNP] = ClassPnp;
1091 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ClassDeviceControl;
1092 DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = ForwardIrpAndForget;
1093 DriverObject->DriverStartIo = ClassStartIo;
1094
1095 /* We will detect the legacy devices later */
1096 IoRegisterDriverReinitialization(
1097 DriverObject,
1098 SearchForLegacyDrivers,
1099 DriverExtension);
1100
1101 return STATUS_SUCCESS;
1102 }