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