Don't call IoRaiseHardError at DISPATCH_LEVEL
[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 {
499 /*
500 * We're exceeding the buffer, and data will be thrown away...
501 * FIXME: What could we do, as we are at DISPATCH_LEVEL?
502 */
503 ReadSize = ClassDeviceExtension->DriverExtension->DataQueueSize - ClassDeviceExtension->InputCount;
504 }
505 else
506 ReadSize = InputCount;
507
508 /*
509 * Move the input data from the port data queue to our class data
510 * queue.
511 */
512 RtlMoveMemory(
513 ClassDeviceExtension->PortData,
514 (PCHAR)DataStart,
515 sizeof(KEYBOARD_INPUT_DATA) * ReadSize);
516
517 /* Move the pointer and counter up */
518 ClassDeviceExtension->PortData += ReadSize;
519 ClassDeviceExtension->InputCount += ReadSize;
520
521 (*ConsumedCount) += ReadSize;
522 }
523 else
524 {
525 DPRINT("ClassCallBack() entered, InputCount = %lu - DOING NOTHING\n", InputCount);
526 }
527
528 KeReleaseSpinLock(&ClassDeviceExtension->SpinLock, OldIrql);
529
530 if (Irp != NULL)
531 {
532 IoStartNextPacket(ClassDeviceObject, FALSE);
533 IoCompleteRequest(Irp, IO_KEYBOARD_INCREMENT);
534 }
535
536 DPRINT("Leaving ClassCallback()\n");
537 return TRUE;
538 }
539
540 /* Send IOCTL_INTERNAL_*_CONNECT to port */
541 static NTSTATUS
542 ConnectPortDriver(
543 IN PDEVICE_OBJECT PortDO,
544 IN PDEVICE_OBJECT ClassDO)
545 {
546 KEVENT Event;
547 PIRP Irp;
548 IO_STATUS_BLOCK IoStatus;
549 CONNECT_DATA ConnectData;
550 NTSTATUS Status;
551
552 KeInitializeEvent(&Event, NotificationEvent, FALSE);
553
554 ConnectData.ClassDeviceObject = ClassDO;
555 ConnectData.ClassService = ClassCallback;
556
557 Irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_KEYBOARD_CONNECT,
558 PortDO,
559 &ConnectData, sizeof(CONNECT_DATA),
560 NULL, 0,
561 TRUE, &Event, &IoStatus);
562
563 Status = IoCallDriver(PortDO, Irp);
564
565 if (Status == STATUS_PENDING)
566 KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
567 else
568 IoStatus.Status = Status;
569
570 if (NT_SUCCESS(IoStatus.Status))
571 {
572 ObReferenceObject(PortDO);
573 ExInterlockedInsertTailList(
574 &((PCLASS_DEVICE_EXTENSION)ClassDO->DeviceExtension)->ListHead,
575 &((PPORT_DEVICE_EXTENSION)PortDO->DeviceExtension)->ListEntry,
576 &((PCLASS_DEVICE_EXTENSION)ClassDO->DeviceExtension)->ListSpinLock);
577 if (ClassDO->StackSize <= PortDO->StackSize)
578 {
579 /* Increase the stack size, in case we have to
580 * forward some IRPs to the port device object
581 */
582 ClassDO->StackSize = PortDO->StackSize + 1;
583 }
584 }
585
586 return IoStatus.Status;
587 }
588
589 static NTSTATUS NTAPI
590 ClassAddDevice(
591 IN PDRIVER_OBJECT DriverObject,
592 IN PDEVICE_OBJECT Pdo)
593 {
594 PCLASS_DRIVER_EXTENSION DriverExtension;
595 PDEVICE_OBJECT Fdo = NULL;
596 PPORT_DEVICE_EXTENSION DeviceExtension = NULL;
597 NTSTATUS Status;
598
599 DPRINT("ClassAddDevice called. Pdo = 0x%p\n", Pdo);
600
601 DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject);
602
603 if (Pdo == NULL)
604 /* We're getting a NULL Pdo at the first call as we're a legacy driver.
605 * Use it to search for legacy port drivers. */
606 return SearchForLegacyDrivers(DriverObject, DriverExtension);
607
608 /* Create new device object */
609 Status = IoCreateDevice(
610 DriverObject,
611 sizeof(PORT_DEVICE_EXTENSION),
612 NULL,
613 Pdo->DeviceType,
614 Pdo->Characteristics & FILE_DEVICE_SECURE_OPEN ? FILE_DEVICE_SECURE_OPEN : 0,
615 TRUE,
616 &Fdo);
617 if (!NT_SUCCESS(Status))
618 {
619 DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status);
620 goto cleanup;
621 }
622
623 DeviceExtension = (PPORT_DEVICE_EXTENSION)Fdo->DeviceExtension;
624 RtlZeroMemory(DeviceExtension, sizeof(CLASS_DEVICE_EXTENSION));
625 DeviceExtension->Common.IsClassDO = FALSE;
626 DeviceExtension->DeviceObject = Fdo;
627 DeviceExtension->PnpState = dsStopped;
628 Status = IoAttachDeviceToDeviceStackSafe(Fdo, Pdo, &DeviceExtension->LowerDevice);
629 if (!NT_SUCCESS(Status))
630 {
631 DPRINT("IoAttachDeviceToDeviceStackSafe() failed with status 0x%08lx\n", Status);
632 goto cleanup;
633 }
634 if (DeviceExtension->LowerDevice->Flags & DO_POWER_PAGABLE)
635 Fdo->Flags |= DO_POWER_PAGABLE;
636 if (DeviceExtension->LowerDevice->Flags & DO_BUFFERED_IO)
637 Fdo->Flags |= DO_BUFFERED_IO;
638 if (DeviceExtension->LowerDevice->Flags & DO_DIRECT_IO)
639 Fdo->Flags |= DO_DIRECT_IO;
640
641 if (DriverExtension->ConnectMultiplePorts)
642 DeviceExtension->ClassDO = DriverExtension->MainClassDeviceObject;
643 else
644 {
645 /* We need a new class device object for this Fdo */
646 Status = CreateClassDeviceObject(
647 DriverObject,
648 &DeviceExtension->ClassDO);
649 if (!NT_SUCCESS(Status))
650 {
651 DPRINT("CreateClassDeviceObject() failed with status 0x%08lx\n", Status);
652 goto cleanup;
653 }
654 }
655 Status = ConnectPortDriver(Fdo, DeviceExtension->ClassDO);
656 if (!NT_SUCCESS(Status))
657 {
658 DPRINT("ConnectPortDriver() failed with status 0x%08lx\n", Status);
659 goto cleanup;
660 }
661 Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
662
663 /* Register interface */
664 Status = IoRegisterDeviceInterface(
665 Pdo,
666 &GUID_DEVINTERFACE_KEYBOARD,
667 NULL,
668 &DeviceExtension->InterfaceName);
669 if (Status == STATUS_INVALID_PARAMETER_1)
670 {
671 /* The Pdo was a strange one ; maybe it is a legacy device.
672 * Ignore the error. */
673 return STATUS_SUCCESS;
674 }
675 else if (!NT_SUCCESS(Status))
676 {
677 DPRINT("IoRegisterDeviceInterface() failed with status 0x%08lx\n", Status);
678 goto cleanup;
679 }
680
681 return STATUS_SUCCESS;
682
683 cleanup:
684 if (DeviceExtension)
685 {
686 if (DeviceExtension->LowerDevice)
687 IoDetachDevice(DeviceExtension->LowerDevice);
688 if (DriverExtension->ConnectMultiplePorts && DeviceExtension->ClassDO)
689 {
690 PCLASS_DEVICE_EXTENSION ClassDeviceExtension;
691 ClassDeviceExtension = (PCLASS_DEVICE_EXTENSION)DeviceExtension->ClassDO->DeviceExtension;
692 ExFreePool(ClassDeviceExtension->PortData);
693 }
694 }
695 if (Fdo)
696 IoDeleteDevice(Fdo);
697 return Status;
698 }
699
700 static VOID NTAPI
701 ClassStartIo(
702 IN PDEVICE_OBJECT DeviceObject,
703 IN PIRP Irp)
704 {
705 PCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
706 PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
707
708 ASSERT(DeviceExtension->Common.IsClassDO);
709
710 if (DeviceExtension->InputCount > 0)
711 {
712 KIRQL oldIrql;
713 NTSTATUS Status;
714
715 KeAcquireSpinLock(&DeviceExtension->SpinLock, &oldIrql);
716
717 Status = FillOneEntry(
718 DeviceObject,
719 Irp,
720 DeviceExtension->PortData - DeviceExtension->InputCount);
721
722 if (NT_SUCCESS(Status))
723 {
724 if (DeviceExtension->InputCount > 1)
725 {
726 RtlMoveMemory(
727 DeviceExtension->PortData - DeviceExtension->InputCount,
728 DeviceExtension->PortData - DeviceExtension->InputCount + 1,
729 (DeviceExtension->InputCount - 1) * sizeof(KEYBOARD_INPUT_DATA));
730 }
731
732 DeviceExtension->PortData--;
733 DeviceExtension->InputCount--;
734 DeviceExtension->ReadIsPending = FALSE;
735
736 Irp->IoStatus.Information = sizeof(KEYBOARD_INPUT_DATA);
737 Stack->Parameters.Read.Length = sizeof(KEYBOARD_INPUT_DATA);
738 }
739
740 /* Go to next packet and complete this request */
741 Irp->IoStatus.Status = Status;
742 IoCompleteRequest(Irp, IO_KEYBOARD_INCREMENT);
743
744 IoStartNextPacket(DeviceObject, FALSE);
745 KeReleaseSpinLock(&DeviceExtension->SpinLock, oldIrql);
746 }
747 else
748 {
749 DeviceExtension->ReadIsPending = TRUE;
750 }
751 }
752
753 static NTSTATUS
754 SearchForLegacyDrivers(
755 IN PDRIVER_OBJECT DriverObject,
756 IN PCLASS_DRIVER_EXTENSION DriverExtension)
757 {
758 UNICODE_STRING DeviceMapKeyU = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\HARDWARE\\DEVICEMAP");
759 UNICODE_STRING PortBaseName = {0, };
760 PKEY_VALUE_BASIC_INFORMATION KeyValueInformation = NULL;
761 OBJECT_ATTRIBUTES ObjectAttributes;
762 HANDLE hDeviceMapKey = (HANDLE)-1;
763 HANDLE hPortKey = (HANDLE)-1;
764 ULONG Index = 0;
765 ULONG Size, ResultLength;
766 NTSTATUS Status;
767
768 /* Create port base name, by replacing Class by Port at the end of the class base name */
769 Status = RtlDuplicateUnicodeString(
770 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
771 &DriverExtension->DeviceBaseName,
772 &PortBaseName);
773 if (!NT_SUCCESS(Status))
774 {
775 DPRINT("RtlDuplicateUnicodeString() failed with status 0x%08lx\n", Status);
776 goto cleanup;
777 }
778 PortBaseName.Length -= (sizeof(L"Class") - sizeof(UNICODE_NULL));
779 RtlAppendUnicodeToString(&PortBaseName, L"Port");
780
781 /* Allocate memory */
782 Size = sizeof(KEY_VALUE_BASIC_INFORMATION) + MAX_PATH;
783 KeyValueInformation = ExAllocatePool(PagedPool, Size);
784 if (!KeyValueInformation)
785 {
786 DPRINT("ExAllocatePool() failed\n");
787 Status = STATUS_INSUFFICIENT_RESOURCES;
788 goto cleanup;
789 }
790
791 /* Open HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP */
792 InitializeObjectAttributes(&ObjectAttributes, &DeviceMapKeyU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
793 Status = ZwOpenKey(&hDeviceMapKey, 0, &ObjectAttributes);
794 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
795 {
796 DPRINT("HKLM\\HARDWARE\\DEVICEMAP is non-existent\n");
797 Status = STATUS_SUCCESS;
798 goto cleanup;
799 }
800 else if (!NT_SUCCESS(Status))
801 {
802 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
803 goto cleanup;
804 }
805
806 /* Open sub key */
807 InitializeObjectAttributes(&ObjectAttributes, &PortBaseName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, hDeviceMapKey, NULL);
808 Status = ZwOpenKey(&hPortKey, KEY_QUERY_VALUE, &ObjectAttributes);
809 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
810 {
811 DPRINT("HKLM\\HARDWARE\\DEVICEMAP\\%wZ is non-existent\n", &PortBaseName);
812 Status = STATUS_SUCCESS;
813 goto cleanup;
814 }
815 else if (!NT_SUCCESS(Status))
816 {
817 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
818 goto cleanup;
819 }
820
821 /* Read each value name */
822 while (ZwEnumerateValueKey(hPortKey, Index++, KeyValueBasicInformation, KeyValueInformation, Size, &ResultLength) == STATUS_SUCCESS)
823 {
824 UNICODE_STRING PortName;
825 PDEVICE_OBJECT PortDeviceObject = NULL;
826 PFILE_OBJECT FileObject = NULL;
827
828 PortName.Length = PortName.MaximumLength = KeyValueInformation->NameLength;
829 PortName.Buffer = KeyValueInformation->Name;
830
831 /* Open the device object pointer */
832 Status = IoGetDeviceObjectPointer(&PortName, FILE_READ_ATTRIBUTES, &FileObject, &PortDeviceObject);
833 if (!NT_SUCCESS(Status))
834 {
835 DPRINT("IoGetDeviceObjectPointer(%wZ) failed with status 0x%08lx\n", Status);
836 continue;
837 }
838 DPRINT("Legacy driver found: %wZ\n", &PortDeviceObject->DriverObject->DriverName);
839
840 Status = ClassAddDevice(DriverObject, PortDeviceObject);
841 if (!NT_SUCCESS(Status))
842 {
843 /* FIXME: Log the error */
844 DPRINT("ClassAddDevice() failed with status 0x%08lx\n", Status);
845 }
846 }
847 if (Status == STATUS_NO_MORE_ENTRIES)
848 Status = STATUS_SUCCESS;
849
850 cleanup:
851 if (KeyValueInformation != NULL)
852 ExFreePool(KeyValueInformation);
853 if (hDeviceMapKey != (HANDLE)-1)
854 ZwClose(hDeviceMapKey);
855 if (hPortKey != (HANDLE)-1)
856 ZwClose(hPortKey);
857 return Status;
858 }
859
860 /*
861 * Standard DriverEntry method.
862 */
863 NTSTATUS NTAPI
864 DriverEntry(
865 IN PDRIVER_OBJECT DriverObject,
866 IN PUNICODE_STRING RegistryPath)
867 {
868 PCLASS_DRIVER_EXTENSION DriverExtension;
869 ULONG i;
870 NTSTATUS Status;
871
872 Status = IoAllocateDriverObjectExtension(
873 DriverObject,
874 DriverObject,
875 sizeof(CLASS_DRIVER_EXTENSION),
876 (PVOID*)&DriverExtension);
877 if (!NT_SUCCESS(Status))
878 {
879 DPRINT("IoAllocateDriverObjectExtension() failed with status 0x%08lx\n", Status);
880 return Status;
881 }
882 RtlZeroMemory(DriverExtension, sizeof(CLASS_DRIVER_EXTENSION));
883
884 Status = RtlDuplicateUnicodeString(
885 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
886 RegistryPath,
887 &DriverExtension->RegistryPath);
888 if (!NT_SUCCESS(Status))
889 {
890 DPRINT("RtlDuplicateUnicodeString() failed with status 0x%08lx\n", Status);
891 return Status;
892 }
893
894 Status = ReadRegistryEntries(RegistryPath, DriverExtension);
895 if (!NT_SUCCESS(Status))
896 {
897 DPRINT("ReadRegistryEntries() failed with status 0x%08lx\n", Status);
898 return Status;
899 }
900
901 if (DriverExtension->ConnectMultiplePorts == 1)
902 {
903 Status = CreateClassDeviceObject(
904 DriverObject,
905 &DriverExtension->MainClassDeviceObject);
906 if (!NT_SUCCESS(Status))
907 {
908 DPRINT("CreateClassDeviceObject() failed with status 0x%08lx\n", Status);
909 return Status;
910 }
911 }
912
913 DriverObject->DriverExtension->AddDevice = ClassAddDevice;
914 DriverObject->DriverUnload = DriverUnload;
915
916 for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
917 DriverObject->MajorFunction[i] = IrpStub;
918
919 DriverObject->MajorFunction[IRP_MJ_CREATE] = ClassCreate;
920 DriverObject->MajorFunction[IRP_MJ_CLOSE] = ClassClose;
921 DriverObject->MajorFunction[IRP_MJ_CLEANUP] = ClassCleanup;
922 DriverObject->MajorFunction[IRP_MJ_READ] = ClassRead;
923 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ClassDeviceControl;
924 DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = ForwardIrpAndForget;
925 DriverObject->DriverStartIo = ClassStartIo;
926
927 return STATUS_SUCCESS;
928 }