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