c12ecd5dd6510d251198fd6842f391a4d1c52def
[reactos.git] / 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 #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 }