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