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