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