xref: /reactos/drivers/input/kbdclass/kbdclass.c (revision 9393fc32)
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 }
1106