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