sync with trunk head (34904)
[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 INITGUID
11 #include "mouclass.h"
12
13 static DRIVER_UNLOAD DriverUnload;
14 static DRIVER_DISPATCH ClassCreate;
15 static DRIVER_DISPATCH ClassClose;
16 static DRIVER_DISPATCH ClassCleanup;
17 static DRIVER_DISPATCH ClassRead;
18 static DRIVER_DISPATCH ClassDeviceControl;
19 static DRIVER_DISPATCH IrpStub;
20 static DRIVER_ADD_DEVICE ClassAddDevice;
21 static DRIVER_STARTIO ClassStartIo;
22 static DRIVER_CANCEL ClassCancelRoutine;
23 static NTSTATUS
24 HandleReadIrp(
25 IN PDEVICE_OBJECT DeviceObject,
26 IN PIRP Irp,
27 BOOLEAN IsInStartIo);
28
29 static VOID NTAPI
30 DriverUnload(IN PDRIVER_OBJECT DriverObject)
31 {
32 // nothing to do here yet
33 }
34
35 static NTSTATUS NTAPI
36 ClassCreate(
37 IN PDEVICE_OBJECT DeviceObject,
38 IN PIRP Irp)
39 {
40 TRACE_(CLASS_NAME, "IRP_MJ_CREATE\n");
41
42 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
43 return ForwardIrpAndForget(DeviceObject, Irp);
44
45 /* FIXME: open all associated Port devices */
46 Irp->IoStatus.Status = STATUS_SUCCESS;
47 Irp->IoStatus.Information = 0;
48 IoCompleteRequest(Irp, IO_NO_INCREMENT);
49 return STATUS_SUCCESS;
50 }
51
52 static NTSTATUS NTAPI
53 ClassClose(
54 IN PDEVICE_OBJECT DeviceObject,
55 IN PIRP Irp)
56 {
57 TRACE_(CLASS_NAME, "IRP_MJ_CLOSE\n");
58
59 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
60 return ForwardIrpAndForget(DeviceObject, Irp);
61
62 /* FIXME: close all associated Port devices */
63 Irp->IoStatus.Status = STATUS_SUCCESS;
64 Irp->IoStatus.Information = 0;
65 IoCompleteRequest(Irp, IO_NO_INCREMENT);
66 return STATUS_SUCCESS;
67 }
68
69 static NTSTATUS NTAPI
70 ClassCleanup(
71 IN PDEVICE_OBJECT DeviceObject,
72 IN PIRP Irp)
73 {
74 TRACE_(CLASS_NAME, "IRP_MJ_CLEANUP\n");
75
76 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
77 return ForwardIrpAndForget(DeviceObject, Irp);
78
79 /* FIXME: cleanup all associated Port devices */
80 Irp->IoStatus.Status = STATUS_SUCCESS;
81 Irp->IoStatus.Information = 0;
82 IoCompleteRequest(Irp, IO_NO_INCREMENT);
83 return STATUS_SUCCESS;
84 }
85
86 static NTSTATUS NTAPI
87 ClassRead(
88 IN PDEVICE_OBJECT DeviceObject,
89 IN PIRP Irp)
90 {
91 PCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
92 KIRQL OldIrql;
93 NTSTATUS Status;
94
95 TRACE_(CLASS_NAME, "IRP_MJ_READ\n");
96
97 ASSERT(DeviceExtension->Common.IsClassDO);
98
99 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
100 return ForwardIrpAndForget(DeviceObject, Irp);
101
102 if (IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length < sizeof(MOUSE_INPUT_DATA))
103 {
104 Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
105 Irp->IoStatus.Information = 0;
106 IoCompleteRequest(Irp, IO_NO_INCREMENT);
107
108 return STATUS_BUFFER_TOO_SMALL;
109 }
110
111 KeAcquireSpinLock(&DeviceExtension->SpinLock, &OldIrql);
112 Status = HandleReadIrp(DeviceObject, Irp, FALSE);
113 KeReleaseSpinLock(&DeviceExtension->SpinLock, OldIrql);
114 return Status;
115 }
116
117 static NTSTATUS NTAPI
118 ClassDeviceControl(
119 IN PDEVICE_OBJECT DeviceObject,
120 IN PIRP Irp)
121 {
122 PCLASS_DEVICE_EXTENSION DeviceExtension;
123 NTSTATUS Status = STATUS_NOT_SUPPORTED;
124
125 TRACE_(CLASS_NAME, "IRP_MJ_DEVICE_CONTROL\n");
126
127 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
128 return ForwardIrpAndForget(DeviceObject, Irp);
129
130 DeviceExtension = (PCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
131
132 switch (IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode)
133 {
134 case IOCTL_MOUSE_QUERY_ATTRIBUTES:
135 {
136 /* FIXME: We hope that all devices will return the same result.
137 * Ask only the first one */
138 PLIST_ENTRY Head = &((PCLASS_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->ListHead;
139 if (Head->Flink != Head)
140 {
141 /* We have at least one device */
142 PPORT_DEVICE_EXTENSION DevExt = CONTAINING_RECORD(Head->Flink, PORT_DEVICE_EXTENSION, ListEntry);
143 IoGetCurrentIrpStackLocation(Irp)->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
144 IoSkipCurrentIrpStackLocation(Irp);
145 return IoCallDriver(DevExt->DeviceObject, Irp);
146 }
147 break;
148 }
149 default:
150 WARN_(CLASS_NAME, "IRP_MJ_DEVICE_CONTROL / unknown I/O control code 0x%lx\n",
151 IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode);
152 ASSERT(FALSE);
153 break;
154 }
155
156 Irp->IoStatus.Status = Status;
157 Irp->IoStatus.Information = 0;
158 IoCompleteRequest(Irp, IO_NO_INCREMENT);
159
160 return Status;
161 }
162
163 static NTSTATUS NTAPI
164 IrpStub(
165 IN PDEVICE_OBJECT DeviceObject,
166 IN PIRP Irp)
167 {
168 NTSTATUS Status = STATUS_NOT_SUPPORTED;
169
170 if (!((PCOMMON_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->IsClassDO)
171 {
172 /* Forward some IRPs to lower device */
173 switch (IoGetCurrentIrpStackLocation(Irp)->MajorFunction)
174 {
175 case IRP_MJ_PNP:
176 case IRP_MJ_INTERNAL_DEVICE_CONTROL:
177 return ForwardIrpAndForget(DeviceObject, Irp);
178 default:
179 {
180 ERR_(CLASS_NAME, "Port DO stub for major function 0x%lx\n",
181 IoGetCurrentIrpStackLocation(Irp)->MajorFunction);
182 ASSERT(FALSE);
183 }
184 }
185 }
186 else
187 {
188 ERR_(CLASS_NAME, "Class DO stub for major function 0x%lx\n",
189 IoGetCurrentIrpStackLocation(Irp)->MajorFunction);
190 ASSERT(FALSE);
191 }
192
193 Irp->IoStatus.Status = Status;
194 IoCompleteRequest(Irp, IO_NO_INCREMENT);
195 return Status;
196 }
197
198 static NTSTATUS
199 ReadRegistryEntries(
200 IN PUNICODE_STRING RegistryPath,
201 IN PCLASS_DRIVER_EXTENSION DriverExtension)
202 {
203 UNICODE_STRING ParametersRegistryKey;
204 RTL_QUERY_REGISTRY_TABLE Parameters[4];
205 NTSTATUS Status;
206
207 ULONG DefaultConnectMultiplePorts = 1;
208 ULONG DefaultDataQueueSize = 0x64;
209 PCWSTR DefaultDeviceBaseName = L"PointerClass";
210
211 ParametersRegistryKey.Length = 0;
212 ParametersRegistryKey.MaximumLength = RegistryPath->Length + sizeof(L"\\Parameters") + sizeof(UNICODE_NULL);
213 ParametersRegistryKey.Buffer = ExAllocatePoolWithTag(PagedPool, ParametersRegistryKey.MaximumLength, CLASS_TAG);
214 if (!ParametersRegistryKey.Buffer)
215 {
216 WARN_(CLASS_NAME, "ExAllocatePoolWithTag() failed\n");
217 return STATUS_NO_MEMORY;
218 }
219 RtlCopyUnicodeString(&ParametersRegistryKey, RegistryPath);
220 RtlAppendUnicodeToString(&ParametersRegistryKey, L"\\Parameters");
221 ParametersRegistryKey.Buffer[ParametersRegistryKey.Length / sizeof(WCHAR)] = UNICODE_NULL;
222
223 RtlZeroMemory(Parameters, sizeof(Parameters));
224
225 Parameters[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL;
226 Parameters[0].Name = L"ConnectMultiplePorts";
227 Parameters[0].EntryContext = &DriverExtension->ConnectMultiplePorts;
228 Parameters[0].DefaultType = REG_DWORD;
229 Parameters[0].DefaultData = &DefaultConnectMultiplePorts;
230 Parameters[0].DefaultLength = sizeof(ULONG);
231
232 Parameters[1].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL;
233 Parameters[1].Name = L"MouseDataQueueSize";
234 Parameters[1].EntryContext = &DriverExtension->DataQueueSize;
235 Parameters[1].DefaultType = REG_DWORD;
236 Parameters[1].DefaultData = &DefaultDataQueueSize;
237 Parameters[1].DefaultLength = sizeof(ULONG);
238
239 Parameters[2].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_REGISTRY_OPTIONAL;
240 Parameters[2].Name = L"PointerDeviceBaseName";
241 Parameters[2].EntryContext = &DriverExtension->DeviceBaseName;
242 Parameters[2].DefaultType = REG_SZ;
243 Parameters[2].DefaultData = (PVOID)DefaultDeviceBaseName;
244 Parameters[2].DefaultLength = 0;
245
246 Status = RtlQueryRegistryValues(
247 RTL_REGISTRY_ABSOLUTE,
248 ParametersRegistryKey.Buffer,
249 Parameters,
250 NULL,
251 NULL);
252
253 if (NT_SUCCESS(Status))
254 {
255 /* Check values */
256 if (DriverExtension->ConnectMultiplePorts != 0
257 && DriverExtension->ConnectMultiplePorts != 1)
258 {
259 DriverExtension->ConnectMultiplePorts = DefaultConnectMultiplePorts;
260 }
261 if (DriverExtension->DataQueueSize == 0)
262 {
263 DriverExtension->DataQueueSize = DefaultDataQueueSize;
264 }
265 }
266 else if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
267 {
268 /* Registry path doesn't exist. Set defaults */
269 DriverExtension->ConnectMultiplePorts = DefaultConnectMultiplePorts;
270 DriverExtension->DataQueueSize = DefaultDataQueueSize;
271 if (RtlCreateUnicodeString(&DriverExtension->DeviceBaseName, DefaultDeviceBaseName))
272 Status = STATUS_SUCCESS;
273 else
274 Status = STATUS_NO_MEMORY;
275 }
276
277 ExFreePoolWithTag(ParametersRegistryKey.Buffer, CLASS_TAG);
278 return Status;
279 }
280
281 static NTSTATUS
282 CreateClassDeviceObject(
283 IN PDRIVER_OBJECT DriverObject,
284 OUT PDEVICE_OBJECT *ClassDO OPTIONAL)
285 {
286 PCLASS_DRIVER_EXTENSION DriverExtension;
287 ULONG DeviceId = 0;
288 ULONG PrefixLength;
289 UNICODE_STRING DeviceNameU;
290 PWSTR DeviceIdW = NULL; /* Pointer into DeviceNameU.Buffer */
291 PDEVICE_OBJECT Fdo;
292 PCLASS_DEVICE_EXTENSION DeviceExtension;
293 NTSTATUS Status;
294
295 TRACE_(CLASS_NAME, "CreateClassDeviceObject(0x%p)\n", DriverObject);
296
297 /* Create new device object */
298 DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject);
299 DeviceNameU.Length = 0;
300 DeviceNameU.MaximumLength =
301 wcslen(L"\\Device\\") * sizeof(WCHAR) /* "\Device\" */
302 + DriverExtension->DeviceBaseName.Length /* "PointerClass" */
303 + 4 * sizeof(WCHAR) /* Id between 0 and 9999 */
304 + sizeof(UNICODE_NULL); /* Final NULL char */
305 DeviceNameU.Buffer = ExAllocatePoolWithTag(PagedPool, DeviceNameU.MaximumLength, CLASS_TAG);
306 if (!DeviceNameU.Buffer)
307 {
308 WARN_(CLASS_NAME, "ExAllocatePoolWithTag() failed\n");
309 return STATUS_NO_MEMORY;
310 }
311 Status = RtlAppendUnicodeToString(&DeviceNameU, L"\\Device\\");
312 if (!NT_SUCCESS(Status))
313 {
314 WARN_(CLASS_NAME, "RtlAppendUnicodeToString() failed with status 0x%08lx\n", Status);
315 goto cleanup;
316 }
317 Status = RtlAppendUnicodeStringToString(&DeviceNameU, &DriverExtension->DeviceBaseName);
318 if (!NT_SUCCESS(Status))
319 {
320 WARN_(CLASS_NAME, "RtlAppendUnicodeStringToString() failed with status 0x%08lx\n", Status);
321 goto cleanup;
322 }
323 PrefixLength = DeviceNameU.MaximumLength - 4 * sizeof(WCHAR) - sizeof(UNICODE_NULL);
324 DeviceIdW = &DeviceNameU.Buffer[PrefixLength / sizeof(WCHAR)];
325 while (DeviceId < 9999)
326 {
327 DeviceNameU.Length = (USHORT)(PrefixLength + swprintf(DeviceIdW, L"%lu", DeviceId) * sizeof(WCHAR));
328 Status = IoCreateDevice(
329 DriverObject,
330 sizeof(CLASS_DEVICE_EXTENSION),
331 &DeviceNameU,
332 FILE_DEVICE_MOUSE,
333 FILE_DEVICE_SECURE_OPEN,
334 TRUE,
335 &Fdo);
336 if (NT_SUCCESS(Status))
337 goto cleanup;
338 else if (Status != STATUS_OBJECT_NAME_COLLISION)
339 {
340 WARN_(CLASS_NAME, "IoCreateDevice() failed with status 0x%08lx\n", Status);
341 goto cleanup;
342 }
343 DeviceId++;
344 }
345 WARN_(CLASS_NAME, "Too many devices starting with '\\Device\\%wZ'\n", &DriverExtension->DeviceBaseName);
346 Status = STATUS_TOO_MANY_NAMES;
347 cleanup:
348 if (!NT_SUCCESS(Status))
349 {
350 ExFreePoolWithTag(DeviceNameU.Buffer, CLASS_TAG);
351 return Status;
352 }
353
354 DeviceExtension = (PCLASS_DEVICE_EXTENSION)Fdo->DeviceExtension;
355 RtlZeroMemory(DeviceExtension, sizeof(CLASS_DEVICE_EXTENSION));
356 DeviceExtension->Common.IsClassDO = TRUE;
357 DeviceExtension->DriverExtension = DriverExtension;
358 InitializeListHead(&DeviceExtension->ListHead);
359 KeInitializeSpinLock(&DeviceExtension->ListSpinLock);
360 KeInitializeSpinLock(&DeviceExtension->SpinLock);
361 DeviceExtension->InputCount = 0;
362 DeviceExtension->PortData = ExAllocatePoolWithTag(NonPagedPool, DeviceExtension->DriverExtension->DataQueueSize * sizeof(MOUSE_INPUT_DATA), CLASS_TAG);
363 if (!DeviceExtension->PortData)
364 {
365 ExFreePoolWithTag(DeviceNameU.Buffer, CLASS_TAG);
366 return STATUS_NO_MEMORY;
367 }
368 DeviceExtension->DeviceName = DeviceNameU.Buffer;
369 Fdo->Flags |= DO_POWER_PAGABLE;
370 Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
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 _SEH_TRY
419 {
420 RtlCopyMemory(
421 Irp->UserBuffer,
422 DataStart,
423 NumberOfEntries * sizeof(MOUSE_INPUT_DATA));
424 }
425 _SEH_HANDLE
426 {
427 Status = _SEH_GetExceptionCode();
428 }
429 _SEH_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 RemoveHeadList(DeviceExtension->ListEntry.Blink);
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 TRUE,
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 registred 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 /* Hm, this shouldn't happen */
728 ASSERT(FALSE);
729 }
730 }
731
732 static NTSTATUS
733 HandleReadIrp(
734 IN PDEVICE_OBJECT DeviceObject,
735 IN PIRP Irp,
736 BOOLEAN IsInStartIo)
737 {
738 PCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
739 NTSTATUS Status;
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 (VOID)IoSetCancelRoutine(Irp, ClassCancelRoutine);
784 if (Irp->Cancel && IoSetCancelRoutine(Irp, NULL))
785 {
786 DeviceExtension->PendingIrp = NULL;
787 Status = STATUS_CANCELLED;
788 }
789 else
790 {
791 IoMarkIrpPending(Irp);
792 DeviceExtension->PendingIrp = Irp;
793 Status = STATUS_PENDING;
794 }
795 }
796 return Status;
797 }
798
799 static VOID NTAPI
800 ClassStartIo(
801 IN PDEVICE_OBJECT DeviceObject,
802 IN PIRP Irp)
803 {
804 PCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
805 KIRQL OldIrql;
806
807 TRACE_(CLASS_NAME, "ClassStartIo(DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
808
809 ASSERT(DeviceExtension->Common.IsClassDO);
810
811 KeAcquireSpinLock(&DeviceExtension->SpinLock, &OldIrql);
812 HandleReadIrp(DeviceObject, Irp, TRUE);
813 KeReleaseSpinLock(&DeviceExtension->SpinLock, OldIrql);
814 }
815
816 static VOID NTAPI
817 SearchForLegacyDrivers(
818 IN PDRIVER_OBJECT DriverObject,
819 IN PVOID Context, /* PCLASS_DRIVER_EXTENSION */
820 IN ULONG Count)
821 {
822 UNICODE_STRING DeviceMapKeyU = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\HARDWARE\\DEVICEMAP");
823 PCLASS_DRIVER_EXTENSION DriverExtension;
824 UNICODE_STRING PortBaseName = {0, };
825 PKEY_VALUE_BASIC_INFORMATION KeyValueInformation = NULL;
826 OBJECT_ATTRIBUTES ObjectAttributes;
827 HANDLE hDeviceMapKey = (HANDLE)-1;
828 HANDLE hPortKey = (HANDLE)-1;
829 ULONG Index = 0;
830 ULONG Size, ResultLength;
831 NTSTATUS Status;
832
833 TRACE_(CLASS_NAME, "SearchForLegacyDrivers(%p %p %lu)\n",
834 DriverObject, Context, Count);
835
836 if (Count != 1)
837 return;
838 DriverExtension = (PCLASS_DRIVER_EXTENSION)Context;
839
840 /* Create port base name, by replacing Class by Port at the end of the class base name */
841 Status = DuplicateUnicodeString(
842 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
843 &DriverExtension->DeviceBaseName,
844 &PortBaseName);
845 if (!NT_SUCCESS(Status))
846 {
847 WARN_(CLASS_NAME, "DuplicateUnicodeString() failed with status 0x%08lx\n", Status);
848 goto cleanup;
849 }
850 PortBaseName.Length -= (sizeof(L"Class") - sizeof(UNICODE_NULL));
851 RtlAppendUnicodeToString(&PortBaseName, L"Port");
852
853 /* Allocate memory */
854 Size = sizeof(KEY_VALUE_BASIC_INFORMATION) + MAX_PATH;
855 KeyValueInformation = ExAllocatePoolWithTag(PagedPool, Size, CLASS_TAG);
856 if (!KeyValueInformation)
857 {
858 WARN_(CLASS_NAME, "ExAllocatePoolWithTag() failed\n");
859 Status = STATUS_NO_MEMORY;
860 goto cleanup;
861 }
862
863 /* Open HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP */
864 InitializeObjectAttributes(&ObjectAttributes, &DeviceMapKeyU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
865 Status = ZwOpenKey(&hDeviceMapKey, 0, &ObjectAttributes);
866 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
867 {
868 INFO_(CLASS_NAME, "HKLM\\HARDWARE\\DEVICEMAP is non-existent\n");
869 Status = STATUS_SUCCESS;
870 goto cleanup;
871 }
872 else if (!NT_SUCCESS(Status))
873 {
874 WARN_(CLASS_NAME, "ZwOpenKey() failed with status 0x%08lx\n", Status);
875 goto cleanup;
876 }
877
878 /* Open sub key */
879 InitializeObjectAttributes(&ObjectAttributes, &PortBaseName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, hDeviceMapKey, NULL);
880 Status = ZwOpenKey(&hPortKey, KEY_QUERY_VALUE, &ObjectAttributes);
881 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
882 {
883 INFO_(CLASS_NAME, "HKLM\\HARDWARE\\DEVICEMAP\\%wZ is non-existent\n", &PortBaseName);
884 Status = STATUS_SUCCESS;
885 goto cleanup;
886 }
887 else if (!NT_SUCCESS(Status))
888 {
889 WARN_(CLASS_NAME, "ZwOpenKey() failed with status 0x%08lx\n", Status);
890 goto cleanup;
891 }
892
893 /* Read each value name */
894 while (ZwEnumerateValueKey(hPortKey, Index++, KeyValueBasicInformation, KeyValueInformation, Size, &ResultLength) == STATUS_SUCCESS)
895 {
896 UNICODE_STRING PortName;
897 PDEVICE_OBJECT PortDeviceObject = NULL;
898 PFILE_OBJECT FileObject = NULL;
899
900 PortName.Length = PortName.MaximumLength = (USHORT)KeyValueInformation->NameLength;
901 PortName.Buffer = KeyValueInformation->Name;
902
903 /* Open the device object pointer */
904 Status = IoGetDeviceObjectPointer(&PortName, FILE_READ_ATTRIBUTES, &FileObject, &PortDeviceObject);
905 if (!NT_SUCCESS(Status))
906 {
907 WARN_(CLASS_NAME, "IoGetDeviceObjectPointer(%wZ) failed with status 0x%08lx\n", &PortName, Status);
908 continue;
909 }
910 INFO_(CLASS_NAME, "Legacy driver found\n");
911
912 Status = ClassAddDevice(DriverObject, PortDeviceObject);
913 if (!NT_SUCCESS(Status))
914 {
915 /* FIXME: Log the error */
916 WARN_(CLASS_NAME, "ClassAddDevice() failed with status 0x%08lx\n", Status);
917 }
918 }
919
920 cleanup:
921 if (KeyValueInformation != NULL)
922 ExFreePoolWithTag(KeyValueInformation, CLASS_TAG);
923 if (hDeviceMapKey != (HANDLE)-1)
924 ZwClose(hDeviceMapKey);
925 if (hPortKey != (HANDLE)-1)
926 ZwClose(hPortKey);
927 }
928
929 /*
930 * Standard DriverEntry method.
931 */
932 NTSTATUS NTAPI
933 DriverEntry(
934 IN PDRIVER_OBJECT DriverObject,
935 IN PUNICODE_STRING RegistryPath)
936 {
937 PCLASS_DRIVER_EXTENSION DriverExtension;
938 ULONG i;
939 NTSTATUS Status;
940
941 Status = IoAllocateDriverObjectExtension(
942 DriverObject,
943 DriverObject,
944 sizeof(CLASS_DRIVER_EXTENSION),
945 (PVOID*)&DriverExtension);
946 if (!NT_SUCCESS(Status))
947 {
948 WARN_(CLASS_NAME, "IoAllocateDriverObjectExtension() failed with status 0x%08lx\n", Status);
949 return Status;
950 }
951 RtlZeroMemory(DriverExtension, sizeof(CLASS_DRIVER_EXTENSION));
952
953 Status = DuplicateUnicodeString(
954 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
955 RegistryPath,
956 &DriverExtension->RegistryPath);
957 if (!NT_SUCCESS(Status))
958 {
959 WARN_(CLASS_NAME, "DuplicateUnicodeString() failed with status 0x%08lx\n", Status);
960 return Status;
961 }
962
963 Status = ReadRegistryEntries(RegistryPath, DriverExtension);
964 if (!NT_SUCCESS(Status))
965 {
966 WARN_(CLASS_NAME, "ReadRegistryEntries() failed with status 0x%08lx\n", Status);
967 return Status;
968 }
969
970 if (DriverExtension->ConnectMultiplePorts == 1)
971 {
972 Status = CreateClassDeviceObject(
973 DriverObject,
974 &DriverExtension->MainClassDeviceObject);
975 if (!NT_SUCCESS(Status))
976 {
977 WARN_(CLASS_NAME, "CreateClassDeviceObject() failed with status 0x%08lx\n", Status);
978 return Status;
979 }
980 }
981
982 DriverObject->DriverExtension->AddDevice = ClassAddDevice;
983 DriverObject->DriverUnload = DriverUnload;
984
985 for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
986 DriverObject->MajorFunction[i] = IrpStub;
987
988 DriverObject->MajorFunction[IRP_MJ_CREATE] = ClassCreate;
989 DriverObject->MajorFunction[IRP_MJ_CLOSE] = ClassClose;
990 DriverObject->MajorFunction[IRP_MJ_CLEANUP] = ClassCleanup;
991 DriverObject->MajorFunction[IRP_MJ_READ] = ClassRead;
992 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ClassDeviceControl;
993 DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = ForwardIrpAndForget;
994 DriverObject->DriverStartIo = ClassStartIo;
995
996 /* We will detect the legacy devices later */
997 IoRegisterDriverReinitialization(
998 DriverObject,
999 SearchForLegacyDrivers,
1000 DriverExtension);
1001
1002 return STATUS_SUCCESS;
1003 }