083e1451c273480bbc51eff03cce875480265dc3
[reactos.git] / reactos / 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 NDEBUG
11 #include <debug.h>
12
13 #define INITGUID
14 #include "kbdclass.h"
15
16 static VOID NTAPI
17 DriverUnload(IN PDRIVER_OBJECT DriverObject)
18 {
19 // nothing to do here yet
20 }
21
22 static NTSTATUS NTAPI
23 KbdclassCreate(
24 IN PDEVICE_OBJECT DeviceObject,
25 IN PIRP Irp)
26 {
27 DPRINT("IRP_MJ_CREATE\n");
28
29 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
30 return ForwardIrpAndForget(DeviceObject, Irp);
31
32 /* FIXME: open all associated Port devices */
33 return STATUS_SUCCESS;
34 }
35
36 static NTSTATUS NTAPI
37 KbdclassClose(
38 IN PDEVICE_OBJECT DeviceObject,
39 IN PIRP Irp)
40 {
41 DPRINT("IRP_MJ_CLOSE\n");
42
43 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
44 return ForwardIrpAndForget(DeviceObject, Irp);
45
46 /* FIXME: close all associated Port devices */
47 return STATUS_SUCCESS;
48 }
49
50 static NTSTATUS NTAPI
51 KbdclassCleanup(
52 IN PDEVICE_OBJECT DeviceObject,
53 IN PIRP Irp)
54 {
55 DPRINT("IRP_MJ_CLEANUP\n");
56
57 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
58 return ForwardIrpAndForget(DeviceObject, Irp);
59
60 /* FIXME: close all associated Port devices */
61 return STATUS_SUCCESS;
62 }
63
64 static NTSTATUS NTAPI
65 KbdclassRead(
66 IN PDEVICE_OBJECT DeviceObject,
67 IN PIRP Irp)
68 {
69 DPRINT("IRP_MJ_READ\n");
70
71 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
72 return ForwardIrpAndForget(DeviceObject, Irp);
73
74 if (IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length < sizeof(KEYBOARD_INPUT_DATA))
75 {
76 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
77 Irp->IoStatus.Information = 0;
78 IoCompleteRequest(Irp, IO_NO_INCREMENT);
79
80 return STATUS_BUFFER_TOO_SMALL;
81 }
82
83 IoMarkIrpPending(Irp);
84 IoStartPacket(DeviceObject, Irp, NULL, NULL);
85 return STATUS_PENDING;
86 }
87
88 static NTSTATUS NTAPI
89 KbdclassDeviceControl(
90 IN PDEVICE_OBJECT DeviceObject,
91 IN PIRP Irp)
92 {
93 NTSTATUS Status;
94
95 DPRINT("IRP_MJ_DEVICE_CONTROL\n");
96
97 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
98 return ForwardIrpAndForget(DeviceObject, Irp);
99
100 switch (IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode)
101 {
102 case IOCTL_KEYBOARD_QUERY_ATTRIBUTES:
103 case IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION:
104 case IOCTL_KEYBOARD_QUERY_INDICATORS:
105 case IOCTL_KEYBOARD_QUERY_TYPEMATIC:
106 case IOCTL_KEYBOARD_SET_INDICATORS:
107 case IOCTL_KEYBOARD_SET_TYPEMATIC: /* not in MSDN, would seem logical */
108 /* FIXME: send it to all associated Port devices */
109 Status = STATUS_NOT_SUPPORTED;
110 break;
111 default:
112 DPRINT1("IRP_MJ_DEVICE_CONTROL / unknown I/O control code 0x%lx\n",
113 IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode);
114 Status = STATUS_NOT_SUPPORTED;
115 }
116
117 Irp->IoStatus.Status = Status;
118 Irp->IoStatus.Information = 0;
119 IoCompleteRequest(Irp, IO_NO_INCREMENT);
120
121 return Status;
122 }
123
124 static NTSTATUS NTAPI
125 IrpStub(
126 IN PDEVICE_OBJECT DeviceObject,
127 IN PIRP Irp)
128 {
129 NTSTATUS Status = STATUS_NOT_SUPPORTED;
130
131 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
132 {
133 /* Forward some IRPs to lower device */
134 switch (IoGetCurrentIrpStackLocation(Irp)->MajorFunction)
135 {
136 case IRP_MJ_INTERNAL_DEVICE_CONTROL:
137 return ForwardIrpAndForget(DeviceObject, Irp);
138 default:
139 {
140 DPRINT1("Port DO stub for major function 0x%lx\n",
141 IoGetCurrentIrpStackLocation(Irp)->MajorFunction);
142 ASSERT(FALSE);
143 Status = Irp->IoStatus.Status;
144 }
145 }
146 }
147 else
148 {
149 DPRINT1("Class DO stub for major function 0x%lx\n",
150 IoGetCurrentIrpStackLocation(Irp)->MajorFunction);
151 ASSERT(FALSE);
152 Status = Irp->IoStatus.Status;
153 }
154
155 Irp->IoStatus.Status = Status;
156 IoCompleteRequest(Irp, IO_NO_INCREMENT);
157 return Status;
158 }
159
160 static NTSTATUS
161 ReadRegistryEntries(
162 IN PUNICODE_STRING RegistryPath,
163 IN PKBDCLASS_DRIVER_EXTENSION DriverExtension)
164 {
165 UNICODE_STRING ParametersRegistryKey;
166 RTL_QUERY_REGISTRY_TABLE Parameters[4];
167 NTSTATUS Status;
168
169 ULONG DefaultConnectMultiplePorts = 1;
170 ULONG DefaultKeyboardDataQueueSize = 0x64;
171 UNICODE_STRING DefaultKeyboardDeviceBaseName = RTL_CONSTANT_STRING(L"KeyboardClass");
172
173 ParametersRegistryKey.Length = 0;
174 ParametersRegistryKey.MaximumLength = RegistryPath->Length + sizeof(L"\\Parameters") + sizeof(UNICODE_NULL);
175 ParametersRegistryKey.Buffer = ExAllocatePool(PagedPool, ParametersRegistryKey.MaximumLength);
176 if (!ParametersRegistryKey.Buffer)
177 {
178 DPRINT("ExAllocatePool() failed\n");
179 return STATUS_INSUFFICIENT_RESOURCES;
180 }
181 RtlCopyUnicodeString(&ParametersRegistryKey, RegistryPath);
182 RtlAppendUnicodeToString(&ParametersRegistryKey, L"\\Parameters");
183 ParametersRegistryKey.Buffer[ParametersRegistryKey.Length / sizeof(WCHAR)] = UNICODE_NULL;
184
185 RtlZeroMemory(Parameters, sizeof(Parameters));
186
187 Parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL;
188 Parameters[0].Name = L"ConnectMultiplePorts";
189 Parameters[0].EntryContext = &DriverExtension->ConnectMultiplePorts;
190 Parameters[0].DefaultType = REG_DWORD;
191 Parameters[0].DefaultData = &DefaultConnectMultiplePorts;
192 Parameters[0].DefaultLength = sizeof(ULONG);
193
194 Parameters[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL;
195 Parameters[1].Name = L"KeyboardDataQueueSize";
196 Parameters[1].EntryContext = &DriverExtension->KeyboardDataQueueSize;
197 Parameters[1].DefaultType = REG_DWORD;
198 Parameters[1].DefaultData = &DefaultKeyboardDataQueueSize;
199 Parameters[1].DefaultLength = sizeof(ULONG);
200
201 Parameters[2].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL;
202 Parameters[2].Name = L"KeyboardDeviceBaseName";
203 Parameters[2].EntryContext = &DriverExtension->KeyboardDeviceBaseName;
204 Parameters[2].DefaultType = REG_SZ;
205 Parameters[2].DefaultData = &DefaultKeyboardDeviceBaseName;
206 Parameters[2].DefaultLength = sizeof(ULONG);
207
208 Status = RtlQueryRegistryValues(
209 RTL_REGISTRY_ABSOLUTE,
210 ParametersRegistryKey.Buffer,
211 Parameters,
212 NULL,
213 NULL);
214
215 if (NT_SUCCESS(Status))
216 {
217 /* Check values */
218 if (DriverExtension->ConnectMultiplePorts != 0
219 && DriverExtension->ConnectMultiplePorts != 1)
220 {
221 DriverExtension->ConnectMultiplePorts = DefaultConnectMultiplePorts;
222 }
223 if (DriverExtension->KeyboardDataQueueSize == 0)
224 {
225 DriverExtension->KeyboardDataQueueSize = DefaultKeyboardDataQueueSize;
226 }
227 }
228 else if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
229 {
230 /* Registry path doesn't exist. Set defaults */
231 DriverExtension->ConnectMultiplePorts = DefaultConnectMultiplePorts;
232 DriverExtension->KeyboardDataQueueSize = DefaultKeyboardDataQueueSize;
233 Status = RtlDuplicateUnicodeString(
234 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
235 &DefaultKeyboardDeviceBaseName,
236 &DriverExtension->KeyboardDeviceBaseName);
237 }
238
239 return Status;
240 }
241
242 static NTSTATUS
243 CreateKeyboardClassDeviceObject(
244 IN PDRIVER_OBJECT DriverObject,
245 OUT PDEVICE_OBJECT *ClassDO OPTIONAL)
246 {
247 PKBDCLASS_DRIVER_EXTENSION DriverExtension;
248 UNICODE_STRING SymbolicLinkName = RTL_CONSTANT_STRING(L"\\??\\Keyboard");
249 ULONG DeviceId = 0;
250 ULONG PrefixLength;
251 UNICODE_STRING DeviceNameU;
252 PWSTR DeviceIdW = NULL; /* Pointer into DeviceNameU.Buffer */
253 PDEVICE_OBJECT Fdo;
254 PKBDCLASS_DEVICE_EXTENSION DeviceExtension;
255 NTSTATUS Status;
256
257 DPRINT("CreateKeyboardClassDeviceObject(0x%p)\n", DriverObject);
258
259 /* Create new device object */
260 DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject);
261 DeviceNameU.Length = 0;
262 DeviceNameU.MaximumLength =
263 wcslen(L"\\Device\\") * sizeof(WCHAR) /* "\Device\" */
264 + DriverExtension->KeyboardDeviceBaseName.Length/* "KeyboardClass" */
265 + 4 * sizeof(WCHAR) /* Id between 0 and 9999 */
266 + sizeof(UNICODE_NULL); /* Final NULL char */
267 DeviceNameU.Buffer = ExAllocatePool(PagedPool, DeviceNameU.MaximumLength);
268 if (!DeviceNameU.Buffer)
269 {
270 DPRINT("ExAllocatePool() failed\n");
271 return STATUS_INSUFFICIENT_RESOURCES;
272 }
273 Status = RtlAppendUnicodeToString(&DeviceNameU, L"\\Device\\");
274 if (!NT_SUCCESS(Status))
275 {
276 DPRINT("RtlAppendUnicodeToString() failed with status 0x%08lx\n", Status);
277 goto cleanup;
278 }
279 Status = RtlAppendUnicodeStringToString(&DeviceNameU, &DriverExtension->KeyboardDeviceBaseName);
280 if (!NT_SUCCESS(Status))
281 {
282 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status);
283 goto cleanup;
284 }
285 PrefixLength = DeviceNameU.MaximumLength - 4 * sizeof(WCHAR) - sizeof(UNICODE_NULL);
286 DeviceIdW = &DeviceNameU.Buffer[PrefixLength / sizeof(WCHAR)];
287 while (DeviceId < 9999)
288 {
289 DeviceNameU.Length = PrefixLength + swprintf(DeviceIdW, L"%lu", DeviceId) * sizeof(WCHAR);
290 Status = IoCreateDevice(
291 DriverObject,
292 sizeof(KBDCLASS_DEVICE_EXTENSION),
293 &DeviceNameU,
294 FILE_DEVICE_KEYBOARD,
295 FILE_DEVICE_SECURE_OPEN,
296 TRUE,
297 &Fdo);
298 if (NT_SUCCESS(Status))
299 goto cleanup;
300 else if (Status != STATUS_OBJECT_NAME_COLLISION)
301 {
302 DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status);
303 goto cleanup;
304 }
305 DeviceId++;
306 }
307 DPRINT("Too much devices starting with '\\Device\\%wZ'\n", &DriverExtension->KeyboardDeviceBaseName);
308 Status = STATUS_UNSUCCESSFUL;
309 cleanup:
310 if (!NT_SUCCESS(Status))
311 {
312 ExFreePool(DeviceNameU.Buffer);
313 return Status;
314 }
315
316 DeviceExtension = (PKBDCLASS_DEVICE_EXTENSION)Fdo->DeviceExtension;
317 RtlZeroMemory(DeviceExtension, sizeof(KBDCLASS_DEVICE_EXTENSION));
318 DeviceExtension->Common.IsClassDO = TRUE;
319 DeviceExtension->DriverExtension = DriverExtension;
320 DeviceExtension->PnpState = dsStopped;
321 KeInitializeSpinLock(&(DeviceExtension->SpinLock));
322 DeviceExtension->ReadIsPending = FALSE;
323 DeviceExtension->InputCount = 0;
324 DeviceExtension->PortData = ExAllocatePool(NonPagedPool, DeviceExtension->DriverExtension->KeyboardDataQueueSize * sizeof(KEYBOARD_INPUT_DATA));
325 Fdo->Flags |= DO_POWER_PAGABLE | DO_BUFFERED_IO;
326 Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
327
328 /* FIXME: create registry entry in HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP */
329
330 /* HACK: 1st stage setup needs a keyboard to open it in user-mode
331 * Create a link to user space... */
332 IoCreateSymbolicLink(&SymbolicLinkName, &DeviceNameU);
333
334 ExFreePool(DeviceNameU.Buffer);
335
336 if (ClassDO)
337 *ClassDO = Fdo;
338
339 return STATUS_SUCCESS;
340 }
341
342 static BOOLEAN
343 KbdclassCallback(
344 IN PDEVICE_OBJECT ClassDeviceObject,
345 IN OUT PKEYBOARD_INPUT_DATA KeyboardDataStart,
346 IN PKEYBOARD_INPUT_DATA KeyboardDataEnd,
347 IN OUT PULONG ConsumedCount)
348 {
349 PKBDCLASS_DEVICE_EXTENSION ClassDeviceExtension = ClassDeviceObject->DeviceExtension;
350 PIRP Irp = NULL;
351 KIRQL OldIrql;
352 PIO_STACK_LOCATION Stack;
353 ULONG InputCount = KeyboardDataEnd - KeyboardDataStart;
354 ULONG ReadSize;
355
356 ASSERT(ClassDeviceExtension->Common.IsClassDO);
357
358 DPRINT("KbdclassCallback()\n");
359 /* A filter driver might have consumed all the data already; I'm
360 * not sure if they are supposed to move the packets when they
361 * consume them though.
362 */
363 if (ClassDeviceExtension->ReadIsPending == TRUE && InputCount)
364 {
365 Irp = ClassDeviceObject->CurrentIrp;
366 ClassDeviceObject->CurrentIrp = NULL;
367 Stack = IoGetCurrentIrpStackLocation(Irp);
368
369 /* A read request is waiting for input, so go straight to it */
370 /* FIXME: use SEH */
371 RtlCopyMemory(
372 Irp->MdlAddress ? MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority) : Irp->AssociatedIrp.SystemBuffer,
373 KeyboardDataStart,
374 sizeof(KEYBOARD_INPUT_DATA));
375
376 /* Go to next packet and complete this request with STATUS_SUCCESS */
377 Irp->IoStatus.Status = STATUS_SUCCESS;
378 Irp->IoStatus.Information = sizeof(KEYBOARD_INPUT_DATA);
379 Stack->Parameters.Read.Length = sizeof(KEYBOARD_INPUT_DATA);
380
381 ClassDeviceExtension->ReadIsPending = FALSE;
382
383 /* Skip the packet we just sent away */
384 KeyboardDataStart++;
385 (*ConsumedCount)++;
386 InputCount--;
387 }
388
389 /* If we have data from the port driver and a higher service to send the data to */
390 if (InputCount != 0)
391 {
392 KeAcquireSpinLock(&ClassDeviceExtension->SpinLock, &OldIrql);
393
394 if (ClassDeviceExtension->InputCount + InputCount > ClassDeviceExtension->DriverExtension->KeyboardDataQueueSize)
395 ReadSize = ClassDeviceExtension->DriverExtension->KeyboardDataQueueSize - ClassDeviceExtension->InputCount;
396 else
397 ReadSize = InputCount;
398
399 /*
400 * FIXME: If we exceed the buffer, keyboard data gets thrown away.. better
401 * solution?
402 */
403
404 /*
405 * Move the keyboard input data from the port data queue to our class data
406 * queue.
407 */
408 RtlMoveMemory(
409 ClassDeviceExtension->PortData,
410 (PCHAR)KeyboardDataStart,
411 sizeof(KEYBOARD_INPUT_DATA) * ReadSize);
412
413 /* Move the pointer and counter up */
414 ClassDeviceExtension->PortData += ReadSize;
415 ClassDeviceExtension->InputCount += ReadSize;
416
417 KeReleaseSpinLock(&ClassDeviceExtension->SpinLock, OldIrql);
418 (*ConsumedCount) += ReadSize;
419 }
420 else
421 {
422 DPRINT("KbdclassCallBack() entered, InputCount = %lu - DOING NOTHING\n", InputCount);
423 }
424
425 if (Irp != NULL)
426 {
427 IoStartNextPacket(ClassDeviceObject, FALSE);
428 IoCompleteRequest(Irp, IO_KEYBOARD_INCREMENT);
429 }
430
431 DPRINT("Leaving KbdclassCallback()\n");
432 return TRUE;
433 }
434
435 /* Send IOCTL_INTERNAL_KEYBOARD_CONNECT to keyboard port */
436 static NTSTATUS
437 ConnectKeyboardPortDriver(
438 IN PDEVICE_OBJECT KeyboardPortDO,
439 IN PDEVICE_OBJECT KeyboardClassDO)
440 {
441 KEVENT Event;
442 PIRP Irp;
443 IO_STATUS_BLOCK IoStatus;
444 CONNECT_DATA ConnectData;
445 NTSTATUS Status;
446
447 KeInitializeEvent(&Event, NotificationEvent, FALSE);
448
449 ConnectData.ClassDeviceObject = KeyboardClassDO;
450 ConnectData.ClassService = KbdclassCallback;
451
452 Irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_KEYBOARD_CONNECT,
453 KeyboardPortDO,
454 &ConnectData, sizeof(CONNECT_DATA),
455 NULL, 0,
456 TRUE, &Event, &IoStatus);
457
458 Status = IoCallDriver(KeyboardPortDO, Irp);
459
460 if (Status == STATUS_PENDING)
461 KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
462 else
463 IoStatus.Status = Status;
464
465 if (NT_SUCCESS(Status))
466 ObReferenceObject(KeyboardPortDO);
467
468 return IoStatus.Status;
469 }
470
471 static NTSTATUS NTAPI
472 KbdclassAddDevice(
473 IN PDRIVER_OBJECT DriverObject,
474 IN PDEVICE_OBJECT Pdo)
475 {
476 PKBDCLASS_DRIVER_EXTENSION DriverExtension;
477 PDEVICE_OBJECT Fdo;
478 PKBDCLASS_DEVICE_EXTENSION DeviceExtension;
479 NTSTATUS Status;
480
481 DPRINT("KbdclassAddDevice called. Pdo = 0x%p\n", Pdo);
482
483 DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject);
484
485 /* Create new device object */
486 Status = IoCreateDevice(
487 DriverObject,
488 sizeof(KBDCLASS_DEVICE_EXTENSION),
489 NULL,
490 Pdo->DeviceType,
491 FILE_DEVICE_SECURE_OPEN,
492 TRUE,
493 &Fdo);
494 if (!NT_SUCCESS(Status))
495 {
496 DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status);
497 return Status;
498 }
499
500 DeviceExtension = (PKBDCLASS_DEVICE_EXTENSION)Fdo->DeviceExtension;
501 RtlZeroMemory(DeviceExtension, sizeof(KBDCLASS_DEVICE_EXTENSION));
502 DeviceExtension->Common.IsClassDO = FALSE;
503 DeviceExtension->PnpState = dsStopped;
504 Fdo->Flags |= DO_POWER_PAGABLE;
505 Status = IoAttachDeviceToDeviceStackSafe(Fdo, Pdo, &DeviceExtension->LowerDevice);
506 if (!NT_SUCCESS(Status))
507 {
508 DPRINT("IoAttachDeviceToDeviceStackSafe() failed with status 0x%08lx\n", Status);
509 IoDeleteDevice(Fdo);
510 return Status;
511 }
512 Fdo->Flags |= DO_BUFFERED_IO;
513
514 if (DriverExtension->ConnectMultiplePorts)
515 Status = ConnectKeyboardPortDriver(Fdo, DriverExtension->MainKbdclassDeviceObject);
516 else
517 Status = ConnectKeyboardPortDriver(Fdo, Fdo);
518 if (!NT_SUCCESS(Status))
519 {
520 DPRINT("ConnectKeyboardPortDriver() failed with status 0x%08lx\n", Status);
521 IoDetachDevice(DeviceExtension->LowerDevice);
522 /* FIXME: why can't I cleanup without error? */
523 //IoDeleteDevice(Fdo);
524 return Status;
525 }
526 Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
527
528 /* Register GUID_DEVINTERFACE_KEYBOARD interface */
529 Status = IoRegisterDeviceInterface(
530 Pdo,
531 &GUID_DEVINTERFACE_KEYBOARD,
532 NULL,
533 &DeviceExtension->KeyboardInterfaceName);
534 if (!NT_SUCCESS(Status))
535 {
536 DPRINT("IoRegisterDeviceInterface() failed with status 0x%08lx\n", Status);
537 return Status;
538 }
539
540 return STATUS_SUCCESS;
541 }
542
543 static VOID NTAPI
544 KbdclassStartIo(
545 IN PDEVICE_OBJECT DeviceObject,
546 IN PIRP Irp)
547 {
548 PKBDCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
549 PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
550
551 ASSERT(DeviceExtension->Common.IsClassDO);
552
553 if (DeviceExtension->InputCount > 0)
554 {
555 KIRQL oldIrql;
556
557 KeAcquireSpinLock(&DeviceExtension->SpinLock, &oldIrql);
558
559 /* FIXME: use SEH */
560 RtlCopyMemory(
561 Irp->MdlAddress ? MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority) : Irp->UserBuffer,
562 DeviceExtension->PortData - DeviceExtension->InputCount,
563 sizeof(KEYBOARD_INPUT_DATA));
564
565 if (DeviceExtension->InputCount > 1)
566 {
567 RtlMoveMemory(
568 DeviceExtension->PortData - DeviceExtension->InputCount,
569 DeviceExtension->PortData - DeviceExtension->InputCount + 1,
570 (DeviceExtension->InputCount - 1) * sizeof(KEYBOARD_INPUT_DATA));
571 }
572 DeviceExtension->PortData--;
573 DeviceExtension->InputCount--;
574 DeviceExtension->ReadIsPending = FALSE;
575
576 /* Go to next packet and complete this request with STATUS_SUCCESS */
577 Irp->IoStatus.Status = STATUS_SUCCESS;
578 Irp->IoStatus.Information = sizeof(KEYBOARD_INPUT_DATA);
579 Stack->Parameters.Read.Length = sizeof(KEYBOARD_INPUT_DATA);
580 IoCompleteRequest(Irp, IO_KEYBOARD_INCREMENT);
581
582 IoStartNextPacket(DeviceObject, FALSE);
583 KeReleaseSpinLock(&DeviceExtension->SpinLock, oldIrql);
584 }
585 else
586 {
587 DeviceExtension->ReadIsPending = TRUE;
588 }
589 }
590
591 static NTSTATUS
592 SearchForLegacyDrivers(
593 IN PDRIVER_OBJECT DriverObject,
594 IN PKBDCLASS_DRIVER_EXTENSION DriverExtension)
595 {
596 UNICODE_STRING DeviceMapKeyU = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\HARDWARE\\DEVICEMAP");
597 UNICODE_STRING PortBaseName = {0, };
598 PKEY_VALUE_BASIC_INFORMATION KeyValueInformation = NULL;
599 OBJECT_ATTRIBUTES ObjectAttributes;
600 HANDLE hDeviceMapKey = (HANDLE)-1;
601 HANDLE hPortKey = (HANDLE)-1;
602 ULONG Index = 0;
603 ULONG Size, ResultLength;
604 NTSTATUS Status;
605
606 /* Create port base name, by replacing Class by Port at the end of the class base name */
607 Status = RtlDuplicateUnicodeString(
608 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
609 &DriverExtension->KeyboardDeviceBaseName,
610 &PortBaseName);
611 if (!NT_SUCCESS(Status))
612 {
613 DPRINT("RtlDuplicateUnicodeString() failed with status 0x%08lx\n", Status);
614 goto cleanup;
615 }
616 PortBaseName.Length -= (sizeof(L"Class") - sizeof(UNICODE_NULL));
617 RtlAppendUnicodeToString(&PortBaseName, L"Port");
618
619 /* Allocate memory */
620 Size = sizeof(KEY_VALUE_BASIC_INFORMATION) + MAX_PATH;
621 KeyValueInformation = ExAllocatePool(PagedPool, Size);
622 if (!KeyValueInformation)
623 {
624 DPRINT("ExAllocatePool() failed\n");
625 Status = STATUS_INSUFFICIENT_RESOURCES;
626 goto cleanup;
627 }
628
629 /* Open HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP */
630 InitializeObjectAttributes(&ObjectAttributes, &DeviceMapKeyU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
631 Status = ZwOpenKey(&hDeviceMapKey, 0, &ObjectAttributes);
632 if (!NT_SUCCESS(Status))
633 {
634 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
635 goto cleanup;
636 }
637
638 /* Open sub key */
639 InitializeObjectAttributes(&ObjectAttributes, &PortBaseName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, hDeviceMapKey, NULL);
640 Status = ZwOpenKey(&hPortKey, KEY_QUERY_VALUE, &ObjectAttributes);
641 if (!NT_SUCCESS(Status))
642 {
643 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
644 DPRINT1("ZwOpenKey() failed with status 0x%08lx\n", Status);
645 goto cleanup;
646 }
647
648 /* Read each value name */
649 while (ZwEnumerateValueKey(hPortKey, Index++, KeyValueBasicInformation, KeyValueInformation, Size, &ResultLength) == STATUS_SUCCESS)
650 {
651 UNICODE_STRING PortName;
652 PDEVICE_OBJECT PortDeviceObject = NULL;
653 PFILE_OBJECT FileObject = NULL;
654
655 PortName.Length = PortName.MaximumLength = KeyValueInformation->NameLength;
656 PortName.Buffer = KeyValueInformation->Name;
657
658 /* Open the device object pointer */
659 Status = IoGetDeviceObjectPointer(&PortName, FILE_READ_ATTRIBUTES, &FileObject, &PortDeviceObject);
660 if (!NT_SUCCESS(Status))
661 {
662 DPRINT("IoGetDeviceObjectPointer(%wZ) failed with status 0x%08lx\n", Status);
663 }
664
665 /* Connect the port device object */
666 if (DriverExtension->ConnectMultiplePorts)
667 {
668 Status = ConnectKeyboardPortDriver(PortDeviceObject, DriverExtension->MainKbdclassDeviceObject);
669 if (!NT_SUCCESS(Status))
670 {
671 /* FIXME: Log the error */
672 DPRINT("ConnectKeyboardPortDriver() failed with status 0x%08lx\n", Status);
673 ObReferenceObject(PortDeviceObject);
674 }
675 }
676 else
677 {
678 PDEVICE_OBJECT ClassDO;
679 Status = CreateKeyboardClassDeviceObject(DriverObject, &ClassDO);
680 if (!NT_SUCCESS(Status))
681 {
682 /* FIXME: Log the error */
683 DPRINT("CreatePointerClassDeviceObject() failed with status 0x%08lx\n", Status);
684 ObReferenceObject(PortDeviceObject);
685 continue;
686 }
687 Status = ConnectKeyboardPortDriver(PortDeviceObject, ClassDO);
688 if (!NT_SUCCESS(Status))
689 {
690 /* FIXME: Log the error */
691 DPRINT("ConnectKeyboardPortDriver() failed with status 0x%08lx\n", Status);
692 ObReferenceObject(PortDeviceObject);
693 IoDeleteDevice(ClassDO);
694 }
695 }
696 }
697 if (Status == STATUS_NO_MORE_ENTRIES)
698 Status = STATUS_SUCCESS;
699
700 cleanup:
701 if (KeyValueInformation != NULL)
702 ExFreePool(KeyValueInformation);
703 if (hDeviceMapKey != (HANDLE)-1)
704 ZwClose(hDeviceMapKey);
705 if (hPortKey != (HANDLE)-1)
706 ZwClose(hPortKey);
707 return Status;
708 }
709
710 /*
711 * Standard DriverEntry method.
712 */
713 NTSTATUS NTAPI
714 DriverEntry(
715 IN PDRIVER_OBJECT DriverObject,
716 IN PUNICODE_STRING RegistryPath)
717 {
718 PKBDCLASS_DRIVER_EXTENSION DriverExtension;
719 ULONG i;
720 NTSTATUS Status;
721
722 Status = IoAllocateDriverObjectExtension(
723 DriverObject,
724 DriverObject,
725 sizeof(KBDCLASS_DRIVER_EXTENSION),
726 (PVOID*)&DriverExtension);
727 if (!NT_SUCCESS(Status))
728 {
729 DPRINT("IoAllocateDriverObjectExtension() failed with status 0x%08lx\n", Status);
730 return Status;
731 }
732 RtlZeroMemory(DriverExtension, sizeof(KBDCLASS_DRIVER_EXTENSION));
733
734 Status = ReadRegistryEntries(RegistryPath, DriverExtension);
735 if (!NT_SUCCESS(Status))
736 {
737 DPRINT("ReadRegistryEntries() failed with status 0x%08lx\n", Status);
738 return Status;
739 }
740
741 if (DriverExtension->ConnectMultiplePorts == 1)
742 {
743 Status = CreateKeyboardClassDeviceObject(
744 DriverObject,
745 &DriverExtension->MainKbdclassDeviceObject);
746 if (!NT_SUCCESS(Status))
747 {
748 DPRINT("CreateKeyboardClassDeviceObject() failed with status 0x%08lx\n", Status);
749 return Status;
750 }
751 }
752
753 DriverObject->DriverExtension->AddDevice = KbdclassAddDevice;
754 DriverObject->DriverUnload = DriverUnload;
755
756 for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
757 DriverObject->MajorFunction[i] = IrpStub;
758
759 DriverObject->MajorFunction[IRP_MJ_CREATE] = KbdclassCreate;
760 DriverObject->MajorFunction[IRP_MJ_CLOSE] = KbdclassClose;
761 DriverObject->MajorFunction[IRP_MJ_CLEANUP] = KbdclassCleanup;
762 DriverObject->MajorFunction[IRP_MJ_READ] = KbdclassRead;
763 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KbdclassDeviceControl;
764 DriverObject->DriverStartIo = KbdclassStartIo;
765
766 Status = SearchForLegacyDrivers(DriverObject, DriverExtension);
767
768 return Status;
769 }