Don't dereference device objects if they were not referenced
[reactos.git] / reactos / drivers / input / mouclass / mouclass.c
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 #define NDEBUG
11 #include <debug.h>
12
13 #define INITGUID
14 #include "mouclass.h"
15
16 static NTSTATUS
17 SearchForLegacyDrivers(
18 IN PDRIVER_OBJECT DriverObject,
19 IN PCLASS_DRIVER_EXTENSION DriverExtension);
20
21 static VOID NTAPI
22 DriverUnload(IN PDRIVER_OBJECT DriverObject)
23 {
24 // nothing to do here yet
25 }
26
27 static NTSTATUS NTAPI
28 ClassCreate(
29 IN PDEVICE_OBJECT DeviceObject,
30 IN PIRP Irp)
31 {
32 DPRINT("IRP_MJ_CREATE\n");
33
34 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
35 return ForwardIrpAndForget(DeviceObject, Irp);
36
37 /* FIXME: open all associated Port devices */
38 return STATUS_SUCCESS;
39 }
40
41 static NTSTATUS NTAPI
42 ClassClose(
43 IN PDEVICE_OBJECT DeviceObject,
44 IN PIRP Irp)
45 {
46 DPRINT("IRP_MJ_CLOSE\n");
47
48 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
49 return ForwardIrpAndForget(DeviceObject, Irp);
50
51 /* FIXME: close all associated Port devices */
52 return STATUS_SUCCESS;
53 }
54
55 static NTSTATUS NTAPI
56 ClassCleanup(
57 IN PDEVICE_OBJECT DeviceObject,
58 IN PIRP Irp)
59 {
60 DPRINT("IRP_MJ_CLEANUP\n");
61
62 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
63 return ForwardIrpAndForget(DeviceObject, Irp);
64
65 /* FIXME: cleanup all associated Port devices */
66 return STATUS_SUCCESS;
67 }
68
69 static NTSTATUS NTAPI
70 ClassRead(
71 IN PDEVICE_OBJECT DeviceObject,
72 IN PIRP Irp)
73 {
74 DPRINT("IRP_MJ_READ\n");
75
76 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
77 return ForwardIrpAndForget(DeviceObject, Irp);
78
79 if (IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length < sizeof(MOUSE_INPUT_DATA))
80 {
81 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
82 Irp->IoStatus.Information = 0;
83 IoCompleteRequest(Irp, IO_NO_INCREMENT);
84
85 return STATUS_BUFFER_TOO_SMALL;
86 }
87
88 IoMarkIrpPending(Irp);
89 IoStartPacket(DeviceObject, Irp, NULL, NULL);
90 return STATUS_PENDING;
91 }
92
93 static NTSTATUS NTAPI
94 ClassDeviceControl(
95 IN PDEVICE_OBJECT DeviceObject,
96 IN PIRP Irp)
97 {
98 PCLASS_DEVICE_EXTENSION DeviceExtension;
99 NTSTATUS Status = Irp->IoStatus.Status;
100
101 DPRINT("IRP_MJ_DEVICE_CONTROL\n");
102
103 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
104 return ForwardIrpAndForget(DeviceObject, Irp);
105
106 DeviceExtension = (PCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
107
108 switch (IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode)
109 {
110 case IOCTL_MOUSE_QUERY_ATTRIBUTES:
111 {
112 /* FIXME: We hope that all devices will return the same result.
113 * Ask only the first one */
114 PLIST_ENTRY Head = &((PCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->ListHead;
115 if (Head->Flink != Head)
116 {
117 /* We have at least one keyboard */
118 PPORT_DEVICE_EXTENSION DevExt = CONTAINING_RECORD(Head->Flink, PORT_DEVICE_EXTENSION, ListEntry);
119 IoGetCurrentIrpStackLocation(Irp)->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
120 IoSkipCurrentIrpStackLocation(Irp);
121 return IoCallDriver(DevExt->DeviceObject, Irp);
122 }
123 break;
124 }
125 default:
126 DPRINT1("IRP_MJ_DEVICE_CONTROL / unknown I/O control code 0x%lx\n",
127 IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode);
128 ASSERT(FALSE);
129 break;
130 }
131
132 Irp->IoStatus.Status = Status;
133 Irp->IoStatus.Information = 0;
134 IoCompleteRequest(Irp, IO_NO_INCREMENT);
135
136 return Status;
137 }
138
139 static NTSTATUS NTAPI
140 IrpStub(
141 IN PDEVICE_OBJECT DeviceObject,
142 IN PIRP Irp)
143 {
144 NTSTATUS Status = STATUS_NOT_SUPPORTED;
145
146 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
147 {
148 /* Forward some IRPs to lower device */
149 switch (IoGetCurrentIrpStackLocation(Irp)->MajorFunction)
150 {
151 case IRP_MJ_PNP:
152 case IRP_MJ_INTERNAL_DEVICE_CONTROL:
153 return ForwardIrpAndForget(DeviceObject, Irp);
154 default:
155 {
156 DPRINT1("Port DO stub for major function 0x%lx\n",
157 IoGetCurrentIrpStackLocation(Irp)->MajorFunction);
158 ASSERT(FALSE);
159 Status = Irp->IoStatus.Status;
160 }
161 }
162 }
163 else
164 {
165 DPRINT1("Class DO stub for major function 0x%lx\n",
166 IoGetCurrentIrpStackLocation(Irp)->MajorFunction);
167 ASSERT(FALSE);
168 Status = Irp->IoStatus.Status;
169 }
170
171 Irp->IoStatus.Status = Status;
172 IoCompleteRequest(Irp, IO_NO_INCREMENT);
173 return Status;
174 }
175
176 static NTSTATUS
177 ReadRegistryEntries(
178 IN PUNICODE_STRING RegistryPath,
179 IN PCLASS_DRIVER_EXTENSION DriverExtension)
180 {
181 UNICODE_STRING ParametersRegistryKey;
182 RTL_QUERY_REGISTRY_TABLE Parameters[4];
183 NTSTATUS Status;
184
185 ULONG DefaultConnectMultiplePorts = 1;
186 ULONG DefaultDataQueueSize = 0x64;
187 UNICODE_STRING DefaultDeviceBaseName = RTL_CONSTANT_STRING(L"PointerClass");
188
189 ParametersRegistryKey.Length = 0;
190 ParametersRegistryKey.MaximumLength = RegistryPath->Length + sizeof(L"\\Parameters") + sizeof(UNICODE_NULL);
191 ParametersRegistryKey.Buffer = ExAllocatePool(PagedPool, ParametersRegistryKey.MaximumLength);
192 if (!ParametersRegistryKey.Buffer)
193 {
194 DPRINT("ExAllocatePool() failed\n");
195 return STATUS_INSUFFICIENT_RESOURCES;
196 }
197 RtlCopyUnicodeString(&ParametersRegistryKey, RegistryPath);
198 RtlAppendUnicodeToString(&ParametersRegistryKey, L"\\Parameters");
199 ParametersRegistryKey.Buffer[ParametersRegistryKey.Length / sizeof(WCHAR)] = UNICODE_NULL;
200
201 RtlZeroMemory(Parameters, sizeof(Parameters));
202
203 Parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL;
204 Parameters[0].Name = L"ConnectMultiplePorts";
205 Parameters[0].EntryContext = &DriverExtension->ConnectMultiplePorts;
206 Parameters[0].DefaultType = REG_DWORD;
207 Parameters[0].DefaultData = &DefaultConnectMultiplePorts;
208 Parameters[0].DefaultLength = sizeof(ULONG);
209
210 Parameters[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL;
211 Parameters[1].Name = L"MouseDataQueueSize";
212 Parameters[1].EntryContext = &DriverExtension->DataQueueSize;
213 Parameters[1].DefaultType = REG_DWORD;
214 Parameters[1].DefaultData = &DefaultDataQueueSize;
215 Parameters[1].DefaultLength = sizeof(ULONG);
216
217 Parameters[2].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL;
218 Parameters[2].Name = L"PointerDeviceBaseName";
219 Parameters[2].EntryContext = &DriverExtension->DeviceBaseName;
220 Parameters[2].DefaultType = REG_SZ;
221 Parameters[2].DefaultData = &DefaultDeviceBaseName;
222 Parameters[2].DefaultLength = 0;
223
224 Status = RtlQueryRegistryValues(
225 RTL_REGISTRY_ABSOLUTE,
226 ParametersRegistryKey.Buffer,
227 Parameters,
228 NULL,
229 NULL);
230
231 if (NT_SUCCESS(Status))
232 {
233 /* Check values */
234 if (DriverExtension->ConnectMultiplePorts != 0
235 && DriverExtension->ConnectMultiplePorts != 1)
236 {
237 DriverExtension->ConnectMultiplePorts = DefaultConnectMultiplePorts;
238 }
239 if (DriverExtension->DataQueueSize == 0)
240 {
241 DriverExtension->DataQueueSize = DefaultDataQueueSize;
242 }
243 }
244 else if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
245 {
246 /* Registry path doesn't exist. Set defaults */
247 DriverExtension->ConnectMultiplePorts = DefaultConnectMultiplePorts;
248 DriverExtension->DataQueueSize = DefaultDataQueueSize;
249 Status = RtlDuplicateUnicodeString(
250 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
251 &DefaultDeviceBaseName,
252 &DriverExtension->DeviceBaseName);
253 }
254
255 return Status;
256 }
257
258 static NTSTATUS
259 CreateClassDeviceObject(
260 IN PDRIVER_OBJECT DriverObject,
261 OUT PDEVICE_OBJECT *ClassDO OPTIONAL)
262 {
263 PCLASS_DRIVER_EXTENSION DriverExtension;
264 ULONG DeviceId = 0;
265 ULONG PrefixLength;
266 UNICODE_STRING DeviceNameU;
267 PWSTR DeviceIdW = NULL; /* Pointer into DeviceNameU.Buffer */
268 PDEVICE_OBJECT Fdo;
269 PCLASS_DEVICE_EXTENSION DeviceExtension;
270 NTSTATUS Status;
271
272 DPRINT("CreateClassDeviceObject(0x%p)\n", DriverObject);
273
274 /* Create new device object */
275 DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject);
276 DeviceNameU.Length = 0;
277 DeviceNameU.MaximumLength =
278 wcslen(L"\\Device\\") * sizeof(WCHAR) /* "\Device\" */
279 + DriverExtension->DeviceBaseName.Length /* "PointerClass" */
280 + 4 * sizeof(WCHAR) /* Id between 0 and 9999 */
281 + sizeof(UNICODE_NULL); /* Final NULL char */
282 DeviceNameU.Buffer = ExAllocatePool(PagedPool, DeviceNameU.MaximumLength);
283 if (!DeviceNameU.Buffer)
284 {
285 DPRINT("ExAllocatePool() failed\n");
286 return STATUS_INSUFFICIENT_RESOURCES;
287 }
288 Status = RtlAppendUnicodeToString(&DeviceNameU, L"\\Device\\");
289 if (!NT_SUCCESS(Status))
290 {
291 DPRINT("RtlAppendUnicodeToString() failed with status 0x%08lx\n", Status);
292 goto cleanup;
293 }
294 Status = RtlAppendUnicodeStringToString(&DeviceNameU, &DriverExtension->DeviceBaseName);
295 if (!NT_SUCCESS(Status))
296 {
297 DPRINT("RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status);
298 goto cleanup;
299 }
300 PrefixLength = DeviceNameU.MaximumLength - 4 * sizeof(WCHAR) - sizeof(UNICODE_NULL);
301 DeviceIdW = &DeviceNameU.Buffer[PrefixLength / sizeof(WCHAR)];
302 while (DeviceId < 9999)
303 {
304 DeviceNameU.Length = PrefixLength + swprintf(DeviceIdW, L"%lu", DeviceId) * sizeof(WCHAR);
305 Status = IoCreateDevice(
306 DriverObject,
307 sizeof(CLASS_DEVICE_EXTENSION),
308 &DeviceNameU,
309 FILE_DEVICE_MOUSE,
310 FILE_DEVICE_SECURE_OPEN,
311 TRUE,
312 &Fdo);
313 if (NT_SUCCESS(Status))
314 goto cleanup;
315 else if (Status != STATUS_OBJECT_NAME_COLLISION)
316 {
317 DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status);
318 goto cleanup;
319 }
320 DeviceId++;
321 }
322 DPRINT("Too many devices starting with '\\Device\\%wZ'\n", &DriverExtension->DeviceBaseName);
323 Status = STATUS_TOO_MANY_NAMES;
324 cleanup:
325 if (!NT_SUCCESS(Status))
326 {
327 ExFreePool(DeviceNameU.Buffer);
328 return Status;
329 }
330
331 DeviceExtension = (PCLASS_DEVICE_EXTENSION)Fdo->DeviceExtension;
332 RtlZeroMemory(DeviceExtension, sizeof(CLASS_DEVICE_EXTENSION));
333 DeviceExtension->Common.IsClassDO = TRUE;
334 DeviceExtension->DriverExtension = DriverExtension;
335 InitializeListHead(&DeviceExtension->ListHead);
336 KeInitializeSpinLock(&DeviceExtension->ListSpinLock);
337 KeInitializeSpinLock(&DeviceExtension->SpinLock);
338 DeviceExtension->ReadIsPending = FALSE;
339 DeviceExtension->InputCount = 0;
340 DeviceExtension->PortData = ExAllocatePool(NonPagedPool, DeviceExtension->DriverExtension->DataQueueSize * sizeof(MOUSE_INPUT_DATA));
341 Fdo->Flags |= DO_POWER_PAGABLE;
342 Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
343
344 /* Add entry entry to HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\[DeviceBaseName] */
345 RtlWriteRegistryValue(
346 RTL_REGISTRY_DEVICEMAP,
347 DriverExtension->DeviceBaseName.Buffer,
348 DeviceNameU.Buffer,
349 REG_SZ,
350 DriverExtension->RegistryPath.Buffer,
351 DriverExtension->RegistryPath.MaximumLength);
352
353 ExFreePool(DeviceNameU.Buffer);
354
355 if (ClassDO)
356 *ClassDO = Fdo;
357
358 return STATUS_SUCCESS;
359 }
360
361 static BOOLEAN
362 ClassCallback(
363 IN PDEVICE_OBJECT ClassDeviceObject,
364 IN OUT PMOUSE_INPUT_DATA DataStart,
365 IN PMOUSE_INPUT_DATA DataEnd,
366 IN OUT PULONG ConsumedCount)
367 {
368 PCLASS_DEVICE_EXTENSION ClassDeviceExtension = ClassDeviceObject->DeviceExtension;
369 PIRP Irp = NULL;
370 KIRQL OldIrql;
371 PIO_STACK_LOCATION Stack;
372 ULONG InputCount = DataEnd - DataStart;
373 ULONG ReadSize;
374
375 ASSERT(ClassDeviceExtension->Common.IsClassDO);
376
377 KeAcquireSpinLock(&ClassDeviceExtension->SpinLock, &OldIrql);
378
379 DPRINT("ClassCallback()\n");
380 /* A filter driver might have consumed all the data already; I'm
381 * not sure if they are supposed to move the packets when they
382 * consume them though.
383 */
384 if (ClassDeviceExtension->ReadIsPending == TRUE && InputCount)
385 {
386 Irp = ClassDeviceObject->CurrentIrp;
387 ClassDeviceObject->CurrentIrp = NULL;
388 Stack = IoGetCurrentIrpStackLocation(Irp);
389
390 /* A read request is waiting for input, so go straight to it */
391 /* FIXME: use SEH */
392 RtlCopyMemory(
393 Irp->MdlAddress ? MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority) : Irp->UserBuffer,
394 DataStart,
395 sizeof(MOUSE_INPUT_DATA));
396
397 /* Go to next packet and complete this request with STATUS_SUCCESS */
398 Irp->IoStatus.Status = STATUS_SUCCESS;
399 Irp->IoStatus.Information = sizeof(MOUSE_INPUT_DATA);
400 Stack->Parameters.Read.Length = sizeof(MOUSE_INPUT_DATA);
401
402 ClassDeviceExtension->ReadIsPending = FALSE;
403
404 /* Skip the packet we just sent away */
405 DataStart++;
406 (*ConsumedCount)++;
407 InputCount--;
408 }
409
410 /* If we have data from the port driver and a higher service to send the data to */
411 if (InputCount != 0)
412 {
413 if (ClassDeviceExtension->InputCount + InputCount > ClassDeviceExtension->DriverExtension->DataQueueSize)
414 ReadSize = ClassDeviceExtension->DriverExtension->DataQueueSize - ClassDeviceExtension->InputCount;
415 else
416 ReadSize = InputCount;
417
418 /*
419 * FIXME: If we exceed the buffer, data gets thrown away.. better
420 * solution?
421 */
422
423 /*
424 * Move the input data from the port data queue to our class data
425 * queue.
426 */
427 RtlMoveMemory(
428 ClassDeviceExtension->PortData,
429 (PCHAR)DataStart,
430 sizeof(MOUSE_INPUT_DATA) * ReadSize);
431
432 /* Move the pointer and counter up */
433 ClassDeviceExtension->PortData += ReadSize;
434 ClassDeviceExtension->InputCount += ReadSize;
435
436 (*ConsumedCount) += ReadSize;
437 }
438 else
439 {
440 DPRINT("ClassCallBack() entered, InputCount = %lu - DOING NOTHING\n", InputCount);
441 }
442
443 KeReleaseSpinLock(&ClassDeviceExtension->SpinLock, OldIrql);
444
445 if (Irp != NULL)
446 {
447 IoStartNextPacket(ClassDeviceObject, FALSE);
448 IoCompleteRequest(Irp, IO_MOUSE_INCREMENT);
449 }
450
451 DPRINT("Leaving ClassCallback()\n");
452 return TRUE;
453 }
454
455 /* Send IOCTL_INTERNAL_*_CONNECT to port */
456 static NTSTATUS
457 ConnectPortDriver(
458 IN PDEVICE_OBJECT PortDO,
459 IN PDEVICE_OBJECT ClassDO)
460 {
461 KEVENT Event;
462 PIRP Irp;
463 IO_STATUS_BLOCK IoStatus;
464 CONNECT_DATA ConnectData;
465 NTSTATUS Status;
466
467 KeInitializeEvent(&Event, NotificationEvent, FALSE);
468
469 ConnectData.ClassDeviceObject = ClassDO;
470 ConnectData.ClassService = ClassCallback;
471
472 Irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_MOUSE_CONNECT,
473 PortDO,
474 &ConnectData, sizeof(CONNECT_DATA),
475 NULL, 0,
476 TRUE, &Event, &IoStatus);
477
478 Status = IoCallDriver(PortDO, Irp);
479
480 if (Status == STATUS_PENDING)
481 KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
482 else
483 IoStatus.Status = Status;
484
485 if (NT_SUCCESS(IoStatus.Status))
486 {
487 ObReferenceObject(PortDO);
488 ExInterlockedInsertTailList(
489 &((PCLASS_DEVICE_EXTENSION)ClassDO->DeviceExtension)->ListHead,
490 &((PPORT_DEVICE_EXTENSION)PortDO->DeviceExtension)->ListEntry,
491 &((PCLASS_DEVICE_EXTENSION)ClassDO->DeviceExtension)->ListSpinLock);
492 if (ClassDO->StackSize <= PortDO->StackSize)
493 {
494 /* Increase the stack size, in case we have to
495 * forward some IRPs to the port device object
496 */
497 ClassDO->StackSize = PortDO->StackSize + 1;
498 }
499 }
500
501 return IoStatus.Status;
502 }
503
504 static NTSTATUS NTAPI
505 ClassAddDevice(
506 IN PDRIVER_OBJECT DriverObject,
507 IN PDEVICE_OBJECT Pdo)
508 {
509 PCLASS_DRIVER_EXTENSION DriverExtension;
510 PDEVICE_OBJECT Fdo = NULL;
511 PPORT_DEVICE_EXTENSION DeviceExtension = NULL;
512 NTSTATUS Status;
513
514 DPRINT("ClassAddDevice called. Pdo = 0x%p\n", Pdo);
515
516 DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject);
517
518 if (Pdo == NULL)
519 /* We're getting a NULL Pdo at the first call as we're a legacy driver.
520 * Use it to search for legacy port drivers. */
521 return SearchForLegacyDrivers(DriverObject, DriverExtension);
522
523 /* Create new device object */
524 Status = IoCreateDevice(
525 DriverObject,
526 sizeof(PORT_DEVICE_EXTENSION),
527 NULL,
528 Pdo->DeviceType,
529 FILE_DEVICE_SECURE_OPEN,
530 TRUE,
531 &Fdo);
532 if (!NT_SUCCESS(Status))
533 {
534 DPRINT("IoCreateDevice() failed with status 0x%08lx\n", Status);
535 goto cleanup;
536 }
537
538 DeviceExtension = (PPORT_DEVICE_EXTENSION)Fdo->DeviceExtension;
539 RtlZeroMemory(DeviceExtension, sizeof(CLASS_DEVICE_EXTENSION));
540 DeviceExtension->Common.IsClassDO = FALSE;
541 DeviceExtension->DeviceObject = Fdo;
542 DeviceExtension->PnpState = dsStopped;
543 Status = IoAttachDeviceToDeviceStackSafe(Fdo, Pdo, &DeviceExtension->LowerDevice);
544 if (!NT_SUCCESS(Status))
545 {
546 DPRINT("IoAttachDeviceToDeviceStackSafe() failed with status 0x%08lx\n", Status);
547 goto cleanup;
548 }
549 if (DeviceExtension->LowerDevice->Flags & DO_POWER_PAGABLE)
550 Fdo->Flags |= DO_POWER_PAGABLE;
551 if (DeviceExtension->LowerDevice->Flags & DO_BUFFERED_IO)
552 Fdo->Flags |= DO_BUFFERED_IO;
553
554 if (DriverExtension->ConnectMultiplePorts)
555 DeviceExtension->ClassDO = DriverExtension->MainClassDeviceObject;
556 else
557 {
558 /* We need a new class device object for this Fdo */
559 Status = CreateClassDeviceObject(
560 DriverObject,
561 &DeviceExtension->ClassDO);
562 if (!NT_SUCCESS(Status))
563 {
564 DPRINT("CreateClassDeviceObject() failed with status 0x%08lx\n", Status);
565 goto cleanup;
566 }
567 }
568 Status = ConnectPortDriver(Fdo, DeviceExtension->ClassDO);
569 if (!NT_SUCCESS(Status))
570 {
571 DPRINT("ConnectPortDriver() failed with status 0x%08lx\n", Status);
572 goto cleanup;
573 }
574 Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
575
576 /* Register interface */
577 Status = IoRegisterDeviceInterface(
578 Pdo,
579 &GUID_DEVINTERFACE_MOUSE,
580 NULL,
581 &DeviceExtension->InterfaceName);
582 if (Status == STATUS_INVALID_PARAMETER_1)
583 {
584 /* The Pdo was a strange one ; maybe it is a legacy device.
585 * Ignore the error. */
586 return STATUS_SUCCESS;
587 }
588 else if (!NT_SUCCESS(Status))
589 {
590 DPRINT("IoRegisterDeviceInterface() failed with status 0x%08lx\n", Status);
591 goto cleanup;
592 }
593
594 return STATUS_SUCCESS;
595
596 cleanup:
597 if (DeviceExtension)
598 {
599 if (DeviceExtension->LowerDevice)
600 IoDetachDevice(DeviceExtension->LowerDevice);
601 if (DriverExtension->ConnectMultiplePorts && DeviceExtension->ClassDO)
602 {
603 PCLASS_DEVICE_EXTENSION ClassDeviceExtension;
604 ClassDeviceExtension = (PCLASS_DEVICE_EXTENSION)DeviceExtension->ClassDO->DeviceExtension;
605 ExFreePool(ClassDeviceExtension->PortData);
606 }
607 }
608 if (Fdo)
609 IoDeleteDevice(Fdo);
610 return Status;
611 }
612
613 static VOID NTAPI
614 ClassStartIo(
615 IN PDEVICE_OBJECT DeviceObject,
616 IN PIRP Irp)
617 {
618 PCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
619 PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
620
621 ASSERT(DeviceExtension->Common.IsClassDO);
622
623 if (DeviceExtension->InputCount > 0)
624 {
625 KIRQL oldIrql;
626
627 KeAcquireSpinLock(&DeviceExtension->SpinLock, &oldIrql);
628
629 DPRINT("Mdl: %p, UserBuffer: %p, InputCount: %lu\n",
630 Irp->MdlAddress,
631 Irp->UserBuffer,
632 DeviceExtension->InputCount);
633
634 /* FIXME: use SEH */
635 RtlCopyMemory(
636 Irp->MdlAddress ? MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority) : Irp->UserBuffer,
637 DeviceExtension->PortData - DeviceExtension->InputCount,
638 sizeof(MOUSE_INPUT_DATA));
639
640 if (DeviceExtension->InputCount > 1)
641 {
642 RtlMoveMemory(
643 DeviceExtension->PortData - DeviceExtension->InputCount,
644 DeviceExtension->PortData - DeviceExtension->InputCount + 1,
645 (DeviceExtension->InputCount - 1) * sizeof(MOUSE_INPUT_DATA));
646 }
647 DeviceExtension->PortData--;
648 DeviceExtension->InputCount--;
649 DeviceExtension->ReadIsPending = FALSE;
650
651 /* Go to next packet and complete this request with STATUS_SUCCESS */
652 Irp->IoStatus.Status = STATUS_SUCCESS;
653 Irp->IoStatus.Information = sizeof(MOUSE_INPUT_DATA);
654 Stack->Parameters.Read.Length = sizeof(MOUSE_INPUT_DATA);
655 IoCompleteRequest(Irp, IO_MOUSE_INCREMENT);
656
657 IoStartNextPacket(DeviceObject, FALSE);
658 KeReleaseSpinLock(&DeviceExtension->SpinLock, oldIrql);
659 }
660 else
661 {
662 DeviceExtension->ReadIsPending = TRUE;
663 }
664 }
665
666 static NTSTATUS
667 SearchForLegacyDrivers(
668 IN PDRIVER_OBJECT DriverObject,
669 IN PCLASS_DRIVER_EXTENSION DriverExtension)
670 {
671 UNICODE_STRING DeviceMapKeyU = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\HARDWARE\\DEVICEMAP");
672 UNICODE_STRING PortBaseName = {0, };
673 PKEY_VALUE_BASIC_INFORMATION KeyValueInformation = NULL;
674 OBJECT_ATTRIBUTES ObjectAttributes;
675 HANDLE hDeviceMapKey = (HANDLE)-1;
676 HANDLE hPortKey = (HANDLE)-1;
677 ULONG Index = 0;
678 ULONG Size, ResultLength;
679 NTSTATUS Status;
680
681 /* Create port base name, by replacing Class by Port at the end of the class base name */
682 Status = RtlDuplicateUnicodeString(
683 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
684 &DriverExtension->DeviceBaseName,
685 &PortBaseName);
686 if (!NT_SUCCESS(Status))
687 {
688 DPRINT("RtlDuplicateUnicodeString() failed with status 0x%08lx\n", Status);
689 goto cleanup;
690 }
691 PortBaseName.Length -= (sizeof(L"Class") - sizeof(UNICODE_NULL));
692 RtlAppendUnicodeToString(&PortBaseName, L"Port");
693
694 /* Allocate memory */
695 Size = sizeof(KEY_VALUE_BASIC_INFORMATION) + MAX_PATH;
696 KeyValueInformation = ExAllocatePool(PagedPool, Size);
697 if (!KeyValueInformation)
698 {
699 DPRINT("ExAllocatePool() failed\n");
700 Status = STATUS_INSUFFICIENT_RESOURCES;
701 goto cleanup;
702 }
703
704 /* Open HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP */
705 InitializeObjectAttributes(&ObjectAttributes, &DeviceMapKeyU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
706 Status = ZwOpenKey(&hDeviceMapKey, 0, &ObjectAttributes);
707 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
708 {
709 DPRINT("HKLM\\HARDWARE\\DEVICEMAP is non-existent\n");
710 Status = STATUS_SUCCESS;
711 goto cleanup;
712 }
713 else if (!NT_SUCCESS(Status))
714 {
715 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
716 goto cleanup;
717 }
718
719 /* Open sub key */
720 InitializeObjectAttributes(&ObjectAttributes, &PortBaseName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, hDeviceMapKey, NULL);
721 Status = ZwOpenKey(&hPortKey, KEY_QUERY_VALUE, &ObjectAttributes);
722 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
723 {
724 DPRINT("HKLM\\HARDWARE\\DEVICEMAP\\%wZ is non-existent\n", &PortBaseName);
725 Status = STATUS_SUCCESS;
726 goto cleanup;
727 }
728 else if (!NT_SUCCESS(Status))
729 {
730 DPRINT("ZwOpenKey() failed with status 0x%08lx\n", Status);
731 goto cleanup;
732 }
733
734 /* Read each value name */
735 while (ZwEnumerateValueKey(hPortKey, Index++, KeyValueBasicInformation, KeyValueInformation, Size, &ResultLength) == STATUS_SUCCESS)
736 {
737 UNICODE_STRING PortName;
738 PDEVICE_OBJECT PortDeviceObject = NULL;
739 PFILE_OBJECT FileObject = NULL;
740
741 PortName.Length = PortName.MaximumLength = KeyValueInformation->NameLength;
742 PortName.Buffer = KeyValueInformation->Name;
743
744 /* Open the device object pointer */
745 Status = IoGetDeviceObjectPointer(&PortName, FILE_READ_ATTRIBUTES, &FileObject, &PortDeviceObject);
746 if (!NT_SUCCESS(Status))
747 {
748 DPRINT("IoGetDeviceObjectPointer(%wZ) failed with status 0x%08lx\n", Status);
749 continue;
750 }
751 DPRINT("Legacy driver found: %wZ\n", &PortDeviceObject->DriverObject->DriverName);
752
753 Status = ClassAddDevice(DriverObject, PortDeviceObject);
754 if (!NT_SUCCESS(Status))
755 {
756 /* FIXME: Log the error */
757 DPRINT("ClassAddDevice() failed with status 0x%08lx\n", Status);
758 }
759 }
760 if (Status == STATUS_NO_MORE_ENTRIES)
761 Status = STATUS_SUCCESS;
762
763 cleanup:
764 if (KeyValueInformation != NULL)
765 ExFreePool(KeyValueInformation);
766 if (hDeviceMapKey != (HANDLE)-1)
767 ZwClose(hDeviceMapKey);
768 if (hPortKey != (HANDLE)-1)
769 ZwClose(hPortKey);
770 return Status;
771 }
772
773 /*
774 * Standard DriverEntry method.
775 */
776 NTSTATUS NTAPI
777 DriverEntry(
778 IN PDRIVER_OBJECT DriverObject,
779 IN PUNICODE_STRING RegistryPath)
780 {
781 PCLASS_DRIVER_EXTENSION DriverExtension;
782 ULONG i;
783 NTSTATUS Status;
784
785 Status = IoAllocateDriverObjectExtension(
786 DriverObject,
787 DriverObject,
788 sizeof(CLASS_DRIVER_EXTENSION),
789 (PVOID*)&DriverExtension);
790 if (!NT_SUCCESS(Status))
791 {
792 DPRINT("IoAllocateDriverObjectExtension() failed with status 0x%08lx\n", Status);
793 return Status;
794 }
795 RtlZeroMemory(DriverExtension, sizeof(CLASS_DRIVER_EXTENSION));
796
797 Status = RtlDuplicateUnicodeString(
798 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
799 RegistryPath,
800 &DriverExtension->RegistryPath);
801 if (!NT_SUCCESS(Status))
802 {
803 DPRINT("RtlDuplicateUnicodeString() failed with status 0x%08lx\n", Status);
804 return Status;
805 }
806
807 Status = ReadRegistryEntries(RegistryPath, DriverExtension);
808 if (!NT_SUCCESS(Status))
809 {
810 DPRINT("ReadRegistryEntries() failed with status 0x%08lx\n", Status);
811 return Status;
812 }
813
814 if (DriverExtension->ConnectMultiplePorts == 1)
815 {
816 Status = CreateClassDeviceObject(
817 DriverObject,
818 &DriverExtension->MainClassDeviceObject);
819 if (!NT_SUCCESS(Status))
820 {
821 DPRINT("CreateClassDeviceObject() failed with status 0x%08lx\n", Status);
822 return Status;
823 }
824 }
825
826 DriverObject->DriverExtension->AddDevice = ClassAddDevice;
827 DriverObject->DriverUnload = DriverUnload;
828
829 for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
830 DriverObject->MajorFunction[i] = IrpStub;
831
832 DriverObject->MajorFunction[IRP_MJ_CREATE] = ClassCreate;
833 DriverObject->MajorFunction[IRP_MJ_CLOSE] = ClassClose;
834 DriverObject->MajorFunction[IRP_MJ_CLEANUP] = ClassCleanup;
835 DriverObject->MajorFunction[IRP_MJ_READ] = ClassRead;
836 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ClassDeviceControl;
837 DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = ForwardIrpAndForget;
838 DriverObject->DriverStartIo = ClassStartIo;
839
840 return STATUS_SUCCESS;
841 }