6aff63713b0d3de3326911f201a26d0241acc0ce
[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 NTSTATUS
17 SearchForLegacyDrivers(
18 IN PDRIVER_OBJECT DriverObject,
19 IN PCLASS_DRIVER_EXTENSION DriverExtension);
20
21 static VOID NTAPI
22 DriverUnload(IN PDRIVER_OBJECT DriverObject)
23 {
24 // nothing to do here yet
25 }
26
27 static NTSTATUS NTAPI
28 ClassCreate(
29 IN PDEVICE_OBJECT DeviceObject,
30 IN PIRP Irp)
31 {
32 DPRINT("IRP_MJ_CREATE\n");
33
34 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
35 return ForwardIrpAndForget(DeviceObject, Irp);
36
37 /* FIXME: open all associated Port devices */
38 Irp->IoStatus.Status = STATUS_SUCCESS;
39 Irp->IoStatus.Information = 0;
40 IoCompleteRequest(Irp, IO_NO_INCREMENT);
41 return STATUS_SUCCESS;
42 }
43
44 static NTSTATUS NTAPI
45 ClassClose(
46 IN PDEVICE_OBJECT DeviceObject,
47 IN PIRP Irp)
48 {
49 DPRINT("IRP_MJ_CLOSE\n");
50
51 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
52 return ForwardIrpAndForget(DeviceObject, Irp);
53
54 /* FIXME: close all associated Port devices */
55 Irp->IoStatus.Status = STATUS_SUCCESS;
56 Irp->IoStatus.Information = 0;
57 IoCompleteRequest(Irp, IO_NO_INCREMENT);
58 return STATUS_SUCCESS;
59 }
60
61 static NTSTATUS NTAPI
62 ClassCleanup(
63 IN PDEVICE_OBJECT DeviceObject,
64 IN PIRP Irp)
65 {
66 DPRINT("IRP_MJ_CLEANUP\n");
67
68 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
69 return ForwardIrpAndForget(DeviceObject, Irp);
70
71 /* FIXME: cleanup all associated Port devices */
72 Irp->IoStatus.Status = STATUS_SUCCESS;
73 Irp->IoStatus.Information = 0;
74 IoCompleteRequest(Irp, IO_NO_INCREMENT);
75 return STATUS_SUCCESS;
76 }
77
78 static NTSTATUS NTAPI
79 ClassRead(
80 IN PDEVICE_OBJECT DeviceObject,
81 IN PIRP Irp)
82 {
83 DPRINT("IRP_MJ_READ\n");
84
85 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
86 return ForwardIrpAndForget(DeviceObject, Irp);
87
88 if (IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length < sizeof(KEYBOARD_INPUT_DATA))
89 {
90 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
91 Irp->IoStatus.Information = 0;
92 IoCompleteRequest(Irp, IO_NO_INCREMENT);
93
94 return STATUS_BUFFER_TOO_SMALL;
95 }
96
97 IoMarkIrpPending(Irp);
98 IoStartPacket(DeviceObject, Irp, NULL, NULL);
99 return STATUS_PENDING;
100 }
101
102 static NTSTATUS NTAPI
103 ClassDeviceControl(
104 IN PDEVICE_OBJECT DeviceObject,
105 IN PIRP Irp)
106 {
107 PCLASS_DEVICE_EXTENSION DeviceExtension;
108 NTSTATUS Status = Irp->IoStatus.Status;
109
110 DPRINT("IRP_MJ_DEVICE_CONTROL\n");
111
112 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
113 return ForwardIrpAndForget(DeviceObject, Irp);
114
115 DeviceExtension = (PCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
116
117 switch (IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode)
118 {
119 case IOCTL_KEYBOARD_QUERY_ATTRIBUTES:
120 case IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION:
121 case IOCTL_KEYBOARD_QUERY_INDICATORS:
122 case IOCTL_KEYBOARD_QUERY_TYPEMATIC:
123 {
124 /* FIXME: We hope that all devices will return the same result.
125 * Ask only the first one */
126 PLIST_ENTRY Head = &((PCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->ListHead;
127 if (Head->Flink != Head)
128 {
129 /* We have at least one keyboard */
130 PPORT_DEVICE_EXTENSION DevExt = CONTAINING_RECORD(Head->Flink, PORT_DEVICE_EXTENSION, ListEntry);
131 IoGetCurrentIrpStackLocation(Irp)->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
132 IoSkipCurrentIrpStackLocation(Irp);
133 return IoCallDriver(DevExt->DeviceObject, Irp);
134 }
135 break;
136 }
137 case IOCTL_KEYBOARD_SET_INDICATORS:
138 case IOCTL_KEYBOARD_SET_TYPEMATIC: /* not in MSDN, would seem logical */
139 {
140 /* Send it to all associated Port devices */
141 PLIST_ENTRY Head = &((PCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->ListHead;
142 PLIST_ENTRY Entry = Head->Flink;
143 Status = STATUS_SUCCESS;
144 while (Entry != Head)
145 {
146 PPORT_DEVICE_EXTENSION DevExt = CONTAINING_RECORD(Entry, PORT_DEVICE_EXTENSION, ListEntry);
147 NTSTATUS IntermediateStatus;
148
149 IoGetCurrentIrpStackLocation(Irp)->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
150 IntermediateStatus = ForwardIrpAndWait(DevExt->DeviceObject, Irp);
151 if (!NT_SUCCESS(IntermediateStatus))
152 Status = IntermediateStatus;
153 Entry = Entry->Flink;
154 }
155 break;
156 }
157 default:
158 DPRINT1("IRP_MJ_DEVICE_CONTROL / unknown I/O control code 0x%lx\n",
159 IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode);
160 ASSERT(FALSE);
161 break;
162 }
163
164 Irp->IoStatus.Status = Status;
165 Irp->IoStatus.Information = 0;
166 IoCompleteRequest(Irp, IO_NO_INCREMENT);
167
168 return Status;
169 }
170
171 static NTSTATUS NTAPI
172 IrpStub(
173 IN PDEVICE_OBJECT DeviceObject,
174 IN PIRP Irp)
175 {
176 NTSTATUS Status = STATUS_NOT_SUPPORTED;
177
178 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
179 {
180 /* Forward some IRPs to lower device */
181 switch (IoGetCurrentIrpStackLocation(Irp)->MajorFunction)
182 {
183 case IRP_MJ_PNP:
184 case IRP_MJ_INTERNAL_DEVICE_CONTROL:
185 return ForwardIrpAndForget(DeviceObject, Irp);
186 default:
187 {
188 DPRINT1("Port DO stub for major function 0x%lx\n",
189 IoGetCurrentIrpStackLocation(Irp)->MajorFunction);
190 ASSERT(FALSE);
191 Status = Irp->IoStatus.Status;
192 }
193 }
194 }
195 else
196 {
197 DPRINT1("Class DO stub for major function 0x%lx\n",
198 IoGetCurrentIrpStackLocation(Irp)->MajorFunction);
199 ASSERT(FALSE);
200 Status = Irp->IoStatus.Status;
201 }
202
203 Irp->IoStatus.Status = Status;
204 IoCompleteRequest(Irp, IO_NO_INCREMENT);
205 return Status;
206 }
207
208 static NTSTATUS
209 ReadRegistryEntries(
210 IN PUNICODE_STRING RegistryPath,
211 IN PCLASS_DRIVER_EXTENSION DriverExtension)
212 {
213 UNICODE_STRING ParametersRegistryKey;
214 RTL_QUERY_REGISTRY_TABLE Parameters[4];
215 NTSTATUS Status;
216
217 ULONG DefaultConnectMultiplePorts = 0;
218 ULONG DefaultDataQueueSize = 0x64;
219 UNICODE_STRING DefaultDeviceBaseName = RTL_CONSTANT_STRING(L"KeyboardClass");
220
221 ParametersRegistryKey.Length = 0;
222 ParametersRegistryKey.MaximumLength = RegistryPath->Length + sizeof(L"\\Parameters") + sizeof(UNICODE_NULL);
223 ParametersRegistryKey.Buffer = ExAllocatePool(PagedPool, ParametersRegistryKey.MaximumLength);
224 if (!ParametersRegistryKey.Buffer)
225 {
226 DPRINT("ExAllocatePool() failed\n");
227 return STATUS_INSUFFICIENT_RESOURCES;
228 }
229 RtlCopyUnicodeString(&ParametersRegistryKey, RegistryPath);
230 RtlAppendUnicodeToString(&ParametersRegistryKey, L"\\Parameters");
231 ParametersRegistryKey.Buffer[ParametersRegistryKey.Length / sizeof(WCHAR)] = UNICODE_NULL;
232
233 RtlZeroMemory(Parameters, sizeof(Parameters));
234
235 Parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL;
236 Parameters[0].Name = L"ConnectMultiplePorts";
237 Parameters[0].EntryContext = &DriverExtension->ConnectMultiplePorts;
238 Parameters[0].DefaultType = REG_DWORD;
239 Parameters[0].DefaultData = &DefaultConnectMultiplePorts;
240 Parameters[0].DefaultLength = sizeof(ULONG);
241
242 Parameters[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL;
243 Parameters[1].Name = L"KeyboardDataQueueSize";
244 Parameters[1].EntryContext = &DriverExtension->DataQueueSize;
245 Parameters[1].DefaultType = REG_DWORD;
246 Parameters[1].DefaultData = &DefaultDataQueueSize;
247 Parameters[1].DefaultLength = sizeof(ULONG);
248
249 Parameters[2].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL;
250 Parameters[2].Name = L"KeyboardDeviceBaseName";
251 Parameters[2].EntryContext = &DriverExtension->DeviceBaseName;
252 Parameters[2].DefaultType = REG_SZ;
253 Parameters[2].DefaultData = &DefaultDeviceBaseName;
254 Parameters[2].DefaultLength = 0;
255
256 Status = RtlQueryRegistryValues(
257 RTL_REGISTRY_ABSOLUTE,
258 ParametersRegistryKey.Buffer,
259 Parameters,
260 NULL,
261 NULL);
262
263 if (NT_SUCCESS(Status))
264 {
265 /* Check values */
266 if (DriverExtension->ConnectMultiplePorts != 0
267 && DriverExtension->ConnectMultiplePorts != 1)
268 {
269 DriverExtension->ConnectMultiplePorts = DefaultConnectMultiplePorts;
270 }
271 if (DriverExtension->DataQueueSize == 0)
272 {
273 DriverExtension->DataQueueSize = DefaultDataQueueSize;
274 }
275 }
276 else if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
277 {
278 /* Registry path doesn't exist. Set defaults */
279 DriverExtension->ConnectMultiplePorts = DefaultConnectMultiplePorts;
280 DriverExtension->DataQueueSize = DefaultDataQueueSize;
281 Status = RtlDuplicateUnicodeString(
282 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
283 &DefaultDeviceBaseName,
284 &DriverExtension->DeviceBaseName);
285 }
286
287 return Status;
288 }
289
290 static NTSTATUS
291 CreateClassDeviceObject(
292 IN PDRIVER_OBJECT DriverObject,
293 OUT PDEVICE_OBJECT *ClassDO OPTIONAL)
294 {
295 PCLASS_DRIVER_EXTENSION DriverExtension;
296 ULONG DeviceId = 0;
297 ULONG PrefixLength;
298 UNICODE_STRING DeviceNameU;
299 PWSTR DeviceIdW = NULL; /* Pointer into DeviceNameU.Buffer */
300 PDEVICE_OBJECT Fdo;
301 PCLASS_DEVICE_EXTENSION DeviceExtension;
302 NTSTATUS Status;
303
304 DPRINT("CreateClassDeviceObject(0x%p)\n", DriverObject);
305
306 /* Create new device object */
307 DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject);
308 DeviceNameU.Length = 0;
309 DeviceNameU.MaximumLength =
310 wcslen(L"\\Device\\") * sizeof(WCHAR) /* "\Device\" */
311 + DriverExtension->DeviceBaseName.Length /* "KeyboardClass" */
312 + 4 * sizeof(WCHAR) /* Id between 0 and 9999 */
313 + sizeof(UNICODE_NULL); /* Final NULL char */
314 DeviceNameU.Buffer = ExAllocatePool(PagedPool, DeviceNameU.MaximumLength);
315 if (!DeviceNameU.Buffer)
316 {
317 DPRINT("ExAllocatePool() failed\n");
318 return STATUS_INSUFFICIENT_RESOURCES;
319 }
320 Status = RtlAppendUnicodeToString(&DeviceNameU, L"\\Device\\");
321 if (!NT_SUCCESS(Status))
322 {
323 DPRINT("RtlAppendUnicodeToString() failed with status 0x%08lx\n", Status);
324 goto cleanup;
325 }
326 Status = RtlAppendUnicodeStringToString(&DeviceNameU, &DriverExtension->DeviceBaseName);
327 if (!NT_SUCCESS(Status))
328 {
329 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status);
330 goto cleanup;
331 }
332 PrefixLength = DeviceNameU.MaximumLength - 4 * sizeof(WCHAR) - sizeof(UNICODE_NULL);
333 DeviceIdW = &DeviceNameU.Buffer[PrefixLength / sizeof(WCHAR)];
334 while (DeviceId < 9999)
335 {
336 DeviceNameU.Length = PrefixLength + swprintf(DeviceIdW, L"%lu", DeviceId) * sizeof(WCHAR);
337 Status = IoCreateDevice(
338 DriverObject,
339 sizeof(CLASS_DEVICE_EXTENSION),
340 &DeviceNameU,
341 FILE_DEVICE_KEYBOARD,
342 FILE_DEVICE_SECURE_OPEN,
343 TRUE,
344 &Fdo);
345 if (NT_SUCCESS(Status))
346 goto cleanup;
347 else if (Status != STATUS_OBJECT_NAME_COLLISION)
348 {
349 DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status);
350 goto cleanup;
351 }
352 DeviceId++;
353 }
354 DPRINT("Too many devices starting with '\\Device\\%wZ'\n", &DriverExtension->DeviceBaseName);
355 Status = STATUS_TOO_MANY_NAMES;
356 cleanup:
357 if (!NT_SUCCESS(Status))
358 {
359 ExFreePool(DeviceNameU.Buffer);
360 return Status;
361 }
362
363 DeviceExtension = (PCLASS_DEVICE_EXTENSION)Fdo->DeviceExtension;
364 RtlZeroMemory(DeviceExtension, sizeof(CLASS_DEVICE_EXTENSION));
365 DeviceExtension->Common.IsClassDO = TRUE;
366 DeviceExtension->DriverExtension = DriverExtension;
367 InitializeListHead(&DeviceExtension->ListHead);
368 KeInitializeSpinLock(&DeviceExtension->ListSpinLock);
369 KeInitializeSpinLock(&DeviceExtension->SpinLock);
370 DeviceExtension->ReadIsPending = FALSE;
371 DeviceExtension->InputCount = 0;
372 DeviceExtension->PortData = ExAllocatePool(NonPagedPool, DeviceExtension->DriverExtension->DataQueueSize * sizeof(KEYBOARD_INPUT_DATA));
373 Fdo->Flags |= DO_POWER_PAGABLE;
374 Fdo->Flags |= DO_BUFFERED_IO; /* FIXME: Why is it needed for 1st stage setup? */
375 Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
376
377 /* Add entry entry to HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\[DeviceBaseName] */
378 RtlWriteRegistryValue(
379 RTL_REGISTRY_DEVICEMAP,
380 DriverExtension->DeviceBaseName.Buffer,
381 DeviceNameU.Buffer,
382 REG_SZ,
383 DriverExtension->RegistryPath.Buffer,
384 DriverExtension->RegistryPath.MaximumLength);
385
386 ExFreePool(DeviceNameU.Buffer);
387
388 if (ClassDO)
389 *ClassDO = Fdo;
390
391 return STATUS_SUCCESS;
392 }
393
394 static NTSTATUS
395 FillOneEntry(
396 IN PDEVICE_OBJECT ClassDeviceObject,
397 IN PIRP Irp,
398 IN PKEYBOARD_INPUT_DATA DataStart)
399 {
400 NTSTATUS Status = STATUS_SUCCESS;
401
402 if (ClassDeviceObject->Flags & DO_BUFFERED_IO)
403 {
404 RtlCopyMemory(
405 Irp->AssociatedIrp.SystemBuffer,
406 DataStart,
407 sizeof(KEYBOARD_INPUT_DATA));
408 }
409 else if (ClassDeviceObject->Flags & DO_DIRECT_IO)
410 {
411 PVOID DestAddress = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
412 if (DestAddress)
413 {
414 RtlCopyMemory(
415 DestAddress,
416 DataStart,
417 sizeof(KEYBOARD_INPUT_DATA));
418 }
419 else
420 Status = STATUS_UNSUCCESSFUL;
421 }
422 else
423 {
424 _SEH_TRY
425 {
426 RtlCopyMemory(
427 Irp->UserBuffer,
428 DataStart,
429 sizeof(KEYBOARD_INPUT_DATA));
430 }
431 _SEH_HANDLE
432 {
433 Status = _SEH_GetExceptionCode();
434 }
435 _SEH_END;
436 }
437
438 return Status;
439 }
440
441 static BOOLEAN
442 ClassCallback(
443 IN PDEVICE_OBJECT ClassDeviceObject,
444 IN OUT PKEYBOARD_INPUT_DATA DataStart,
445 IN PKEYBOARD_INPUT_DATA DataEnd,
446 IN OUT PULONG ConsumedCount)
447 {
448 PCLASS_DEVICE_EXTENSION ClassDeviceExtension = ClassDeviceObject->DeviceExtension;
449 PIRP Irp = NULL;
450 KIRQL OldIrql;
451 PIO_STACK_LOCATION Stack;
452 ULONG InputCount = DataEnd - DataStart;
453 ULONG ReadSize;
454
455 ASSERT(ClassDeviceExtension->Common.IsClassDO);
456
457 KeAcquireSpinLock(&ClassDeviceExtension->SpinLock, &OldIrql);
458
459 DPRINT("ClassCallback()\n");
460 /* A filter driver might have consumed all the data already; I'm
461 * not sure if they are supposed to move the packets when they
462 * consume them though.
463 */
464 if (ClassDeviceExtension->ReadIsPending == TRUE && InputCount)
465 {
466 NTSTATUS Status;
467
468 Irp = ClassDeviceObject->CurrentIrp;
469 ClassDeviceObject->CurrentIrp = NULL;
470 Stack = IoGetCurrentIrpStackLocation(Irp);
471
472 /* A read request is waiting for input, so go straight to it */
473 Status = FillOneEntry(
474 ClassDeviceObject,
475 Irp,
476 DataStart);
477
478 if (NT_SUCCESS(Status))
479 {
480 /* Go to next packet and complete this request with STATUS_SUCCESS */
481 Irp->IoStatus.Status = STATUS_SUCCESS;
482 Irp->IoStatus.Information = sizeof(KEYBOARD_INPUT_DATA);
483 Stack->Parameters.Read.Length = sizeof(KEYBOARD_INPUT_DATA);
484
485 ClassDeviceExtension->ReadIsPending = FALSE;
486
487 /* Skip the packet we just sent away */
488 DataStart++;
489 (*ConsumedCount)++;
490 InputCount--;
491 }
492 }
493
494 /* If we have data from the port driver and a higher service to send the data to */
495 if (InputCount != 0)
496 {
497 if (ClassDeviceExtension->InputCount + InputCount > ClassDeviceExtension->DriverExtension->DataQueueSize)
498 ReadSize = ClassDeviceExtension->DriverExtension->DataQueueSize - ClassDeviceExtension->InputCount;
499 else
500 ReadSize = InputCount;
501
502 /*
503 * If we exceed the buffer, data gets thrown away...
504 * Try at least to display a dialog
505 */
506 if (Irp != NULL)
507 IoRaiseHardError(Irp, NULL, ClassDeviceObject);
508
509 /*
510 * Move the input data from the port data queue to our class data
511 * queue.
512 */
513 RtlMoveMemory(
514 ClassDeviceExtension->PortData,
515 (PCHAR)DataStart,
516 sizeof(KEYBOARD_INPUT_DATA) * ReadSize);
517
518 /* Move the pointer and counter up */
519 ClassDeviceExtension->PortData += ReadSize;
520 ClassDeviceExtension->InputCount += ReadSize;
521
522 (*ConsumedCount) += ReadSize;
523 }
524 else
525 {
526 DPRINT("ClassCallBack() entered, InputCount = %lu - DOING NOTHING\n", InputCount);
527 }
528
529 KeReleaseSpinLock(&ClassDeviceExtension->SpinLock, OldIrql);
530
531 if (Irp != NULL)
532 {
533 IoStartNextPacket(ClassDeviceObject, FALSE);
534 IoCompleteRequest(Irp, IO_KEYBOARD_INCREMENT);
535 }
536
537 DPRINT("Leaving ClassCallback()\n");
538 return TRUE;
539 }
540
541 /* Send IOCTL_INTERNAL_*_CONNECT to port */
542 static NTSTATUS
543 ConnectPortDriver(
544 IN PDEVICE_OBJECT PortDO,
545 IN PDEVICE_OBJECT ClassDO)
546 {
547 KEVENT Event;
548 PIRP Irp;
549 IO_STATUS_BLOCK IoStatus;
550 CONNECT_DATA ConnectData;
551 NTSTATUS Status;
552
553 KeInitializeEvent(&Event, NotificationEvent, FALSE);
554
555 ConnectData.ClassDeviceObject = ClassDO;
556 ConnectData.ClassService = ClassCallback;
557
558 Irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_KEYBOARD_CONNECT,
559 PortDO,
560 &ConnectData, sizeof(CONNECT_DATA),
561 NULL, 0,
562 TRUE, &Event, &IoStatus);
563
564 Status = IoCallDriver(PortDO, Irp);
565
566 if (Status == STATUS_PENDING)
567 KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
568 else
569 IoStatus.Status = Status;
570
571 if (NT_SUCCESS(IoStatus.Status))
572 {
573 ObReferenceObject(PortDO);
574 ExInterlockedInsertTailList(
575 &((PCLASS_DEVICE_EXTENSION)ClassDO->DeviceExtension)->ListHead,
576 &((PPORT_DEVICE_EXTENSION)PortDO->DeviceExtension)->ListEntry,
577 &((PCLASS_DEVICE_EXTENSION)ClassDO->DeviceExtension)->ListSpinLock);
578 if (ClassDO->StackSize <= PortDO->StackSize)
579 {
580 /* Increase the stack size, in case we have to
581 * forward some IRPs to the port device object
582 */
583 ClassDO->StackSize = PortDO->StackSize + 1;
584 }
585 }
586
587 return IoStatus.Status;
588 }
589
590 static NTSTATUS NTAPI
591 ClassAddDevice(
592 IN PDRIVER_OBJECT DriverObject,
593 IN PDEVICE_OBJECT Pdo)
594 {
595 PCLASS_DRIVER_EXTENSION DriverExtension;
596 PDEVICE_OBJECT Fdo = NULL;
597 PPORT_DEVICE_EXTENSION DeviceExtension = NULL;
598 NTSTATUS Status;
599
600 DPRINT("ClassAddDevice called. Pdo = 0x%p\n", Pdo);
601
602 DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject);
603
604 if (Pdo == NULL)
605 /* We're getting a NULL Pdo at the first call as we're a legacy driver.
606 * Use it to search for legacy port drivers. */
607 return SearchForLegacyDrivers(DriverObject, DriverExtension);
608
609 /* Create new device object */
610 Status = IoCreateDevice(
611 DriverObject,
612 sizeof(PORT_DEVICE_EXTENSION),
613 NULL,
614 Pdo->DeviceType,
615 Pdo->Characteristics & FILE_DEVICE_SECURE_OPEN ? FILE_DEVICE_SECURE_OPEN : 0,
616 TRUE,
617 &Fdo);
618 if (!NT_SUCCESS(Status))
619 {
620 DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status);
621 goto cleanup;
622 }
623
624 DeviceExtension = (PPORT_DEVICE_EXTENSION)Fdo->DeviceExtension;
625 RtlZeroMemory(DeviceExtension, sizeof(CLASS_DEVICE_EXTENSION));
626 DeviceExtension->Common.IsClassDO = FALSE;
627 DeviceExtension->DeviceObject = Fdo;
628 DeviceExtension->PnpState = dsStopped;
629 Status = IoAttachDeviceToDeviceStackSafe(Fdo, Pdo, &DeviceExtension->LowerDevice);
630 if (!NT_SUCCESS(Status))
631 {
632 DPRINT("IoAttachDeviceToDeviceStackSafe() failed with status 0x%08lx\n", Status);
633 goto cleanup;
634 }
635 if (DeviceExtension->LowerDevice->Flags & DO_POWER_PAGABLE)
636 Fdo->Flags |= DO_POWER_PAGABLE;
637 if (DeviceExtension->LowerDevice->Flags & DO_BUFFERED_IO)
638 Fdo->Flags |= DO_BUFFERED_IO;
639 if (DeviceExtension->LowerDevice->Flags & DO_DIRECT_IO)
640 Fdo->Flags |= DO_DIRECT_IO;
641
642 if (DriverExtension->ConnectMultiplePorts)
643 DeviceExtension->ClassDO = DriverExtension->MainClassDeviceObject;
644 else
645 {
646 /* We need a new class device object for this Fdo */
647 Status = CreateClassDeviceObject(
648 DriverObject,
649 &DeviceExtension->ClassDO);
650 if (!NT_SUCCESS(Status))
651 {
652 DPRINT("CreateClassDeviceObject() failed with status 0x%08lx\n", Status);
653 goto cleanup;
654 }
655 }
656 Status = ConnectPortDriver(Fdo, DeviceExtension->ClassDO);
657 if (!NT_SUCCESS(Status))
658 {
659 DPRINT("ConnectPortDriver() failed with status 0x%08lx\n", Status);
660 goto cleanup;
661 }
662 Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
663
664 /* Register interface */
665 Status = IoRegisterDeviceInterface(
666 Pdo,
667 &GUID_DEVINTERFACE_KEYBOARD,
668 NULL,
669 &DeviceExtension->InterfaceName);
670 if (Status == STATUS_INVALID_PARAMETER_1)
671 {
672 /* The Pdo was a strange one ; maybe it is a legacy device.
673 * Ignore the error. */
674 return STATUS_SUCCESS;
675 }
676 else if (!NT_SUCCESS(Status))
677 {
678 DPRINT("IoRegisterDeviceInterface() failed with status 0x%08lx\n", Status);
679 goto cleanup;
680 }
681
682 return STATUS_SUCCESS;
683
684 cleanup:
685 if (DeviceExtension)
686 {
687 if (DeviceExtension->LowerDevice)
688 IoDetachDevice(DeviceExtension->LowerDevice);
689 if (DriverExtension->ConnectMultiplePorts && DeviceExtension->ClassDO)
690 {
691 PCLASS_DEVICE_EXTENSION ClassDeviceExtension;
692 ClassDeviceExtension = (PCLASS_DEVICE_EXTENSION)DeviceExtension->ClassDO->DeviceExtension;
693 ExFreePool(ClassDeviceExtension->PortData);
694 }
695 }
696 if (Fdo)
697 IoDeleteDevice(Fdo);
698 return Status;
699 }
700
701 static VOID NTAPI
702 ClassStartIo(
703 IN PDEVICE_OBJECT DeviceObject,
704 IN PIRP Irp)
705 {
706 PCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
707 PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
708
709 ASSERT(DeviceExtension->Common.IsClassDO);
710
711 if (DeviceExtension->InputCount > 0)
712 {
713 KIRQL oldIrql;
714 NTSTATUS Status;
715
716 KeAcquireSpinLock(&DeviceExtension->SpinLock, &oldIrql);
717
718 Status = FillOneEntry(
719 DeviceObject,
720 Irp,
721 DeviceExtension->PortData - DeviceExtension->InputCount);
722
723 if (NT_SUCCESS(Status))
724 {
725 if (DeviceExtension->InputCount > 1)
726 {
727 RtlMoveMemory(
728 DeviceExtension->PortData - DeviceExtension->InputCount,
729 DeviceExtension->PortData - DeviceExtension->InputCount + 1,
730 (DeviceExtension->InputCount - 1) * sizeof(KEYBOARD_INPUT_DATA));
731 }
732
733 DeviceExtension->PortData--;
734 DeviceExtension->InputCount--;
735 DeviceExtension->ReadIsPending = FALSE;
736
737 Irp->IoStatus.Information = sizeof(KEYBOARD_INPUT_DATA);
738 Stack->Parameters.Read.Length = sizeof(KEYBOARD_INPUT_DATA);
739 }
740
741 /* Go to next packet and complete this request */
742 Irp->IoStatus.Status = Status;
743 IoCompleteRequest(Irp, IO_KEYBOARD_INCREMENT);
744
745 IoStartNextPacket(DeviceObject, FALSE);
746 KeReleaseSpinLock(&DeviceExtension->SpinLock, oldIrql);
747 }
748 else
749 {
750 DeviceExtension->ReadIsPending = TRUE;
751 }
752 }
753
754 static NTSTATUS
755 SearchForLegacyDrivers(
756 IN PDRIVER_OBJECT DriverObject,
757 IN PCLASS_DRIVER_EXTENSION DriverExtension)
758 {
759 UNICODE_STRING DeviceMapKeyU = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\HARDWARE\\DEVICEMAP");
760 UNICODE_STRING PortBaseName = {0, };
761 PKEY_VALUE_BASIC_INFORMATION KeyValueInformation = NULL;
762 OBJECT_ATTRIBUTES ObjectAttributes;
763 HANDLE hDeviceMapKey = (HANDLE)-1;
764 HANDLE hPortKey = (HANDLE)-1;
765 ULONG Index = 0;
766 ULONG Size, ResultLength;
767 NTSTATUS Status;
768
769 /* Create port base name, by replacing Class by Port at the end of the class base name */
770 Status = RtlDuplicateUnicodeString(
771 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
772 &DriverExtension->DeviceBaseName,
773 &PortBaseName);
774 if (!NT_SUCCESS(Status))
775 {
776 DPRINT("RtlDuplicateUnicodeString() failed with status 0x%08lx\n", Status);
777 goto cleanup;
778 }
779 PortBaseName.Length -= (sizeof(L"Class") - sizeof(UNICODE_NULL));
780 RtlAppendUnicodeToString(&PortBaseName, L"Port");
781
782 /* Allocate memory */
783 Size = sizeof(KEY_VALUE_BASIC_INFORMATION) + MAX_PATH;
784 KeyValueInformation = ExAllocatePool(PagedPool, Size);
785 if (!KeyValueInformation)
786 {
787 DPRINT("ExAllocatePool() failed\n");
788 Status = STATUS_INSUFFICIENT_RESOURCES;
789 goto cleanup;
790 }
791
792 /* Open HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP */
793 InitializeObjectAttributes(&ObjectAttributes, &DeviceMapKeyU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
794 Status = ZwOpenKey(&hDeviceMapKey, 0, &ObjectAttributes);
795 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
796 {
797 DPRINT("HKLM\\HARDWARE\\DEVICEMAP is non-existent\n");
798 Status = STATUS_SUCCESS;
799 goto cleanup;
800 }
801 else if (!NT_SUCCESS(Status))
802 {
803 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
804 goto cleanup;
805 }
806
807 /* Open sub key */
808 InitializeObjectAttributes(&ObjectAttributes, &PortBaseName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, hDeviceMapKey, NULL);
809 Status = ZwOpenKey(&hPortKey, KEY_QUERY_VALUE, &ObjectAttributes);
810 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
811 {
812 DPRINT("HKLM\\HARDWARE\\DEVICEMAP\\%wZ is non-existent\n", &PortBaseName);
813 Status = STATUS_SUCCESS;
814 goto cleanup;
815 }
816 else if (!NT_SUCCESS(Status))
817 {
818 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
819 goto cleanup;
820 }
821
822 /* Read each value name */
823 while (ZwEnumerateValueKey(hPortKey, Index++, KeyValueBasicInformation, KeyValueInformation, Size, &ResultLength) == STATUS_SUCCESS)
824 {
825 UNICODE_STRING PortName;
826 PDEVICE_OBJECT PortDeviceObject = NULL;
827 PFILE_OBJECT FileObject = NULL;
828
829 PortName.Length = PortName.MaximumLength = KeyValueInformation->NameLength;
830 PortName.Buffer = KeyValueInformation->Name;
831
832 /* Open the device object pointer */
833 Status = IoGetDeviceObjectPointer(&PortName, FILE_READ_ATTRIBUTES, &FileObject, &PortDeviceObject);
834 if (!NT_SUCCESS(Status))
835 {
836 DPRINT("IoGetDeviceObjectPointer(%wZ) failed with status 0x%08lx\n", Status);
837 continue;
838 }
839 DPRINT("Legacy driver found: %wZ\n", &PortDeviceObject->DriverObject->DriverName);
840
841 Status = ClassAddDevice(DriverObject, PortDeviceObject);
842 if (!NT_SUCCESS(Status))
843 {
844 /* FIXME: Log the error */
845 DPRINT("ClassAddDevice() failed with status 0x%08lx\n", Status);
846 }
847 }
848 if (Status == STATUS_NO_MORE_ENTRIES)
849 Status = STATUS_SUCCESS;
850
851 cleanup:
852 if (KeyValueInformation != NULL)
853 ExFreePool(KeyValueInformation);
854 if (hDeviceMapKey != (HANDLE)-1)
855 ZwClose(hDeviceMapKey);
856 if (hPortKey != (HANDLE)-1)
857 ZwClose(hPortKey);
858 return Status;
859 }
860
861 /*
862 * Standard DriverEntry method.
863 */
864 NTSTATUS NTAPI
865 DriverEntry(
866 IN PDRIVER_OBJECT DriverObject,
867 IN PUNICODE_STRING RegistryPath)
868 {
869 PCLASS_DRIVER_EXTENSION DriverExtension;
870 ULONG i;
871 NTSTATUS Status;
872
873 Status = IoAllocateDriverObjectExtension(
874 DriverObject,
875 DriverObject,
876 sizeof(CLASS_DRIVER_EXTENSION),
877 (PVOID*)&DriverExtension);
878 if (!NT_SUCCESS(Status))
879 {
880 DPRINT("IoAllocateDriverObjectExtension() failed with status 0x%08lx\n", Status);
881 return Status;
882 }
883 RtlZeroMemory(DriverExtension, sizeof(CLASS_DRIVER_EXTENSION));
884
885 Status = RtlDuplicateUnicodeString(
886 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
887 RegistryPath,
888 &DriverExtension->RegistryPath);
889 if (!NT_SUCCESS(Status))
890 {
891 DPRINT("RtlDuplicateUnicodeString() failed with status 0x%08lx\n", Status);
892 return Status;
893 }
894
895 Status = ReadRegistryEntries(RegistryPath, DriverExtension);
896 if (!NT_SUCCESS(Status))
897 {
898 DPRINT("ReadRegistryEntries() failed with status 0x%08lx\n", Status);
899 return Status;
900 }
901
902 if (DriverExtension->ConnectMultiplePorts == 1)
903 {
904 Status = CreateClassDeviceObject(
905 DriverObject,
906 &DriverExtension->MainClassDeviceObject);
907 if (!NT_SUCCESS(Status))
908 {
909 DPRINT("CreateClassDeviceObject() failed with status 0x%08lx\n", Status);
910 return Status;
911 }
912 }
913
914 DriverObject->DriverExtension->AddDevice = ClassAddDevice;
915 DriverObject->DriverUnload = DriverUnload;
916
917 for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
918 DriverObject->MajorFunction[i] = IrpStub;
919
920 DriverObject->MajorFunction[IRP_MJ_CREATE] = ClassCreate;
921 DriverObject->MajorFunction[IRP_MJ_CLOSE] = ClassClose;
922 DriverObject->MajorFunction[IRP_MJ_CLEANUP] = ClassCleanup;
923 DriverObject->MajorFunction[IRP_MJ_READ] = ClassRead;
924 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ClassDeviceControl;
925 DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = ForwardIrpAndForget;
926 DriverObject->DriverStartIo = ClassStartIo;
927
928 return STATUS_SUCCESS;
929 }