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