Sync with trunk r58687.
[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 #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 FALSE,
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 Fdo->Flags |= DO_BUFFERED_IO;
372
373 /* Add entry entry to HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\[DeviceBaseName] */
374 RtlWriteRegistryValue(
375 RTL_REGISTRY_DEVICEMAP,
376 DriverExtension->DeviceBaseName.Buffer,
377 DeviceExtension->DeviceName,
378 REG_SZ,
379 DriverExtension->RegistryPath.Buffer,
380 DriverExtension->RegistryPath.MaximumLength);
381
382 if (ClassDO)
383 *ClassDO = Fdo;
384
385 return STATUS_SUCCESS;
386 }
387
388 static NTSTATUS
389 FillEntries(
390 IN PDEVICE_OBJECT ClassDeviceObject,
391 IN PIRP Irp,
392 IN PMOUSE_INPUT_DATA DataStart,
393 IN SIZE_T NumberOfEntries)
394 {
395 NTSTATUS Status = STATUS_SUCCESS;
396
397 if (ClassDeviceObject->Flags & DO_BUFFERED_IO)
398 {
399 RtlCopyMemory(
400 Irp->AssociatedIrp.SystemBuffer,
401 DataStart,
402 NumberOfEntries * sizeof(MOUSE_INPUT_DATA));
403 }
404 else if (ClassDeviceObject->Flags & DO_DIRECT_IO)
405 {
406 PVOID DestAddress = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
407 if (DestAddress)
408 {
409 RtlCopyMemory(
410 DestAddress,
411 DataStart,
412 NumberOfEntries * sizeof(MOUSE_INPUT_DATA));
413 }
414 else
415 Status = STATUS_UNSUCCESSFUL;
416 }
417 else
418 {
419 _SEH2_TRY
420 {
421 RtlCopyMemory(
422 Irp->UserBuffer,
423 DataStart,
424 NumberOfEntries * sizeof(MOUSE_INPUT_DATA));
425 }
426 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
427 {
428 Status = _SEH2_GetExceptionCode();
429 }
430 _SEH2_END;
431 }
432
433 return Status;
434 }
435
436 static BOOLEAN NTAPI
437 ClassCallback(
438 IN PDEVICE_OBJECT ClassDeviceObject,
439 IN OUT PMOUSE_INPUT_DATA DataStart,
440 IN PMOUSE_INPUT_DATA DataEnd,
441 IN OUT PULONG ConsumedCount)
442 {
443 PCLASS_DEVICE_EXTENSION ClassDeviceExtension = ClassDeviceObject->DeviceExtension;
444 KIRQL OldIrql;
445 SIZE_T InputCount = DataEnd - DataStart;
446 SIZE_T ReadSize;
447
448 TRACE_(CLASS_NAME, "ClassCallback()\n");
449
450 ASSERT(ClassDeviceExtension->Common.IsClassDO);
451
452 KeAcquireSpinLock(&ClassDeviceExtension->SpinLock, &OldIrql);
453 if (InputCount > 0)
454 {
455 if (ClassDeviceExtension->InputCount + InputCount > ClassDeviceExtension->DriverExtension->DataQueueSize)
456 {
457 /*
458 * We're exceeding the buffer, and data will be thrown away...
459 * FIXME: What could we do, as we are at DISPATCH_LEVEL?
460 */
461 ReadSize = ClassDeviceExtension->DriverExtension->DataQueueSize - ClassDeviceExtension->InputCount;
462 }
463 else
464 ReadSize = InputCount;
465
466 /*
467 * Move the input data from the port data queue to our class data
468 * queue.
469 */
470 RtlCopyMemory(
471 &ClassDeviceExtension->PortData[ClassDeviceExtension->InputCount],
472 (PCHAR)DataStart,
473 sizeof(MOUSE_INPUT_DATA) * ReadSize);
474
475 /* Move the counter up */
476 ClassDeviceExtension->InputCount += ReadSize;
477
478 (*ConsumedCount) += (ULONG)ReadSize;
479
480 /* Complete pending IRP (if any) */
481 if (ClassDeviceExtension->PendingIrp)
482 HandleReadIrp(ClassDeviceObject, ClassDeviceExtension->PendingIrp, FALSE);
483 }
484 KeReleaseSpinLock(&ClassDeviceExtension->SpinLock, OldIrql);
485
486 TRACE_(CLASS_NAME, "Leaving ClassCallback()\n");
487 return TRUE;
488 }
489
490 /* Send IOCTL_INTERNAL_*_CONNECT to port */
491 static NTSTATUS
492 ConnectPortDriver(
493 IN PDEVICE_OBJECT PortDO,
494 IN PDEVICE_OBJECT ClassDO)
495 {
496 KEVENT Event;
497 PIRP Irp;
498 IO_STATUS_BLOCK IoStatus;
499 CONNECT_DATA ConnectData;
500 NTSTATUS Status;
501
502 TRACE_(CLASS_NAME, "Connecting PortDO %p to ClassDO %p\n", PortDO, ClassDO);
503
504 KeInitializeEvent(&Event, NotificationEvent, FALSE);
505
506 ConnectData.ClassDeviceObject = ClassDO;
507 ConnectData.ClassService = ClassCallback;
508
509 Irp = IoBuildDeviceIoControlRequest(
510 IOCTL_INTERNAL_MOUSE_CONNECT,
511 PortDO,
512 &ConnectData, sizeof(CONNECT_DATA),
513 NULL, 0,
514 TRUE, &Event, &IoStatus);
515 if (!Irp)
516 return STATUS_INSUFFICIENT_RESOURCES;
517
518 Status = IoCallDriver(PortDO, Irp);
519
520 if (Status == STATUS_PENDING)
521 KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
522 else
523 IoStatus.Status = Status;
524
525 if (NT_SUCCESS(IoStatus.Status))
526 {
527 ObReferenceObject(PortDO);
528 ExInterlockedInsertTailList(
529 &((PCLASS_DEVICE_EXTENSION)ClassDO->DeviceExtension)->ListHead,
530 &((PPORT_DEVICE_EXTENSION)PortDO->DeviceExtension)->ListEntry,
531 &((PCLASS_DEVICE_EXTENSION)ClassDO->DeviceExtension)->ListSpinLock);
532 if (ClassDO->StackSize <= PortDO->StackSize)
533 {
534 /* Increase the stack size, in case we have to
535 * forward some IRPs to the port device object
536 */
537 ClassDO->StackSize = PortDO->StackSize + 1;
538 }
539 }
540
541 return IoStatus.Status;
542 }
543
544 /* Send IOCTL_INTERNAL_*_DISCONNECT to port + destroy the Port DO */
545 static VOID
546 DestroyPortDriver(
547 IN PDEVICE_OBJECT PortDO)
548 {
549 PPORT_DEVICE_EXTENSION DeviceExtension;
550 PCLASS_DEVICE_EXTENSION ClassDeviceExtension;
551 PCLASS_DRIVER_EXTENSION DriverExtension;
552 KEVENT Event;
553 PIRP Irp;
554 IO_STATUS_BLOCK IoStatus;
555 KIRQL OldIrql;
556 NTSTATUS Status;
557
558 TRACE_(CLASS_NAME, "Destroying PortDO %p\n", PortDO);
559
560 DeviceExtension = (PPORT_DEVICE_EXTENSION)PortDO->DeviceExtension;
561 ClassDeviceExtension = DeviceExtension->ClassDO->DeviceExtension;
562 DriverExtension = IoGetDriverObjectExtension(PortDO->DriverObject, PortDO->DriverObject);
563
564 /* Send IOCTL_INTERNAL_*_DISCONNECT */
565 KeInitializeEvent(&Event, NotificationEvent, FALSE);
566 Irp = IoBuildDeviceIoControlRequest(
567 IOCTL_INTERNAL_MOUSE_DISCONNECT,
568 PortDO,
569 NULL, 0,
570 NULL, 0,
571 TRUE, &Event, &IoStatus);
572 if (Irp)
573 {
574 Status = IoCallDriver(PortDO, Irp);
575 if (Status == STATUS_PENDING)
576 KeWaitForSingleObject(&Event, Suspended, KernelMode, FALSE, NULL);
577 }
578
579 /* Remove from ClassDeviceExtension->ListHead list */
580 KeAcquireSpinLock(&ClassDeviceExtension->ListSpinLock, &OldIrql);
581 RemoveHeadList(DeviceExtension->ListEntry.Blink);
582 KeReleaseSpinLock(&ClassDeviceExtension->ListSpinLock, OldIrql);
583
584 /* Remove entry from HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\[DeviceBaseName] */
585 RtlDeleteRegistryValue(
586 RTL_REGISTRY_DEVICEMAP,
587 DriverExtension->DeviceBaseName.Buffer,
588 ClassDeviceExtension->DeviceName);
589
590 if (DeviceExtension->LowerDevice)
591 IoDetachDevice(DeviceExtension->LowerDevice);
592 ObDereferenceObject(PortDO);
593
594 if (!DriverExtension->ConnectMultiplePorts && DeviceExtension->ClassDO)
595 {
596 ExFreePoolWithTag(ClassDeviceExtension->PortData, CLASS_TAG);
597 ExFreePoolWithTag((PVOID)ClassDeviceExtension->DeviceName, CLASS_TAG);
598 IoDeleteDevice(DeviceExtension->ClassDO);
599 }
600
601 IoDeleteDevice(PortDO);
602 }
603
604 static NTSTATUS NTAPI
605 ClassAddDevice(
606 IN PDRIVER_OBJECT DriverObject,
607 IN PDEVICE_OBJECT Pdo)
608 {
609 PCLASS_DRIVER_EXTENSION DriverExtension;
610 PDEVICE_OBJECT Fdo = NULL;
611 PPORT_DEVICE_EXTENSION DeviceExtension = NULL;
612 NTSTATUS Status;
613
614 TRACE_(CLASS_NAME, "ClassAddDevice called. Pdo = 0x%p\n", Pdo);
615
616 DriverExtension = IoGetDriverObjectExtension(DriverObject, DriverObject);
617
618 if (Pdo == NULL)
619 /* We may get a NULL Pdo at the first call as we're a legacy driver. Ignore it */
620 return STATUS_SUCCESS;
621
622 /* Create new device object */
623 Status = IoCreateDevice(
624 DriverObject,
625 sizeof(PORT_DEVICE_EXTENSION),
626 NULL,
627 Pdo->DeviceType,
628 Pdo->Characteristics & FILE_DEVICE_SECURE_OPEN ? FILE_DEVICE_SECURE_OPEN : 0,
629 FALSE,
630 &Fdo);
631 if (!NT_SUCCESS(Status))
632 {
633 WARN_(CLASS_NAME, "IoCreateDevice() failed with status 0x%08lx\n", Status);
634 goto cleanup;
635 }
636 IoSetStartIoAttributes(Fdo, TRUE, TRUE);
637
638 DeviceExtension = (PPORT_DEVICE_EXTENSION)Fdo->DeviceExtension;
639 RtlZeroMemory(DeviceExtension, sizeof(PORT_DEVICE_EXTENSION));
640 DeviceExtension->Common.IsClassDO = FALSE;
641 DeviceExtension->DeviceObject = Fdo;
642 DeviceExtension->PnpState = dsStopped;
643 Status = IoAttachDeviceToDeviceStackSafe(Fdo, Pdo, &DeviceExtension->LowerDevice);
644 if (!NT_SUCCESS(Status))
645 {
646 WARN_(CLASS_NAME, "IoAttachDeviceToDeviceStackSafe() failed with status 0x%08lx\n", Status);
647 goto cleanup;
648 }
649 if (DeviceExtension->LowerDevice->Flags & DO_POWER_PAGABLE)
650 Fdo->Flags |= DO_POWER_PAGABLE;
651 if (DeviceExtension->LowerDevice->Flags & DO_BUFFERED_IO)
652 Fdo->Flags |= DO_BUFFERED_IO;
653 if (DeviceExtension->LowerDevice->Flags & DO_DIRECT_IO)
654 Fdo->Flags |= DO_DIRECT_IO;
655
656 if (DriverExtension->ConnectMultiplePorts)
657 DeviceExtension->ClassDO = DriverExtension->MainClassDeviceObject;
658 else
659 {
660 /* We need a new class device object for this Fdo */
661 Status = CreateClassDeviceObject(
662 DriverObject,
663 &DeviceExtension->ClassDO);
664 if (!NT_SUCCESS(Status))
665 {
666 WARN_(CLASS_NAME, "CreateClassDeviceObject() failed with status 0x%08lx\n", Status);
667 goto cleanup;
668 }
669 }
670 Status = ConnectPortDriver(Fdo, DeviceExtension->ClassDO);
671 if (!NT_SUCCESS(Status))
672 {
673 WARN_(CLASS_NAME, "ConnectPortDriver() failed with status 0x%08lx\n", Status);
674 goto cleanup;
675 }
676 Fdo->Flags &= ~DO_DEVICE_INITIALIZING;
677
678 /* Register interface ; ignore the error (if any) as having
679 * a registred interface is not so important... */
680 Status = IoRegisterDeviceInterface(
681 Pdo,
682 &GUID_DEVINTERFACE_MOUSE,
683 NULL,
684 &DeviceExtension->InterfaceName);
685 if (!NT_SUCCESS(Status))
686 DeviceExtension->InterfaceName.Length = 0;
687
688 return STATUS_SUCCESS;
689
690 cleanup:
691 if (Fdo)
692 DestroyPortDriver(Fdo);
693 return Status;
694 }
695
696 static VOID NTAPI
697 ClassCancelRoutine(
698 IN PDEVICE_OBJECT DeviceObject,
699 IN PIRP Irp)
700 {
701 PCLASS_DEVICE_EXTENSION ClassDeviceExtension = DeviceObject->DeviceExtension;
702 KIRQL OldIrql;
703 BOOLEAN wasQueued = FALSE;
704
705 TRACE_(CLASS_NAME, "ClassCancelRoutine(DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
706
707 ASSERT(ClassDeviceExtension->Common.IsClassDO);
708
709 IoReleaseCancelSpinLock(Irp->CancelIrql);
710
711 KeAcquireSpinLock(&ClassDeviceExtension->SpinLock, &OldIrql);
712
713 if (ClassDeviceExtension->PendingIrp == Irp)
714 {
715 ClassDeviceExtension->PendingIrp = NULL;
716 wasQueued = TRUE;
717 }
718 KeReleaseSpinLock(&ClassDeviceExtension->SpinLock, OldIrql);
719
720 if (wasQueued)
721 {
722 Irp->IoStatus.Status = STATUS_CANCELLED;
723 Irp->IoStatus.Information = 0;
724 IoCompleteRequest(Irp, IO_NO_INCREMENT);
725 }
726 else
727 {
728 DPRINT1("Cancelled IRP is not pending. Race condition?\n");
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 KIRQL OldIrql;
741
742 TRACE_(CLASS_NAME, "HandleReadIrp(DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
743
744 ASSERT(DeviceExtension->Common.IsClassDO);
745
746 if (DeviceExtension->InputCount > 0)
747 {
748 SIZE_T NumberOfEntries;
749
750 NumberOfEntries = MIN(
751 DeviceExtension->InputCount,
752 IoGetCurrentIrpStackLocation(Irp)->Parameters.Read.Length / sizeof(MOUSE_INPUT_DATA));
753
754 Status = FillEntries(
755 DeviceObject,
756 Irp,
757 DeviceExtension->PortData,
758 NumberOfEntries);
759
760 if (NT_SUCCESS(Status))
761 {
762 if (DeviceExtension->InputCount > NumberOfEntries)
763 {
764 RtlMoveMemory(
765 &DeviceExtension->PortData[0],
766 &DeviceExtension->PortData[NumberOfEntries],
767 (DeviceExtension->InputCount - NumberOfEntries) * sizeof(MOUSE_INPUT_DATA));
768 }
769
770 DeviceExtension->InputCount -= NumberOfEntries;
771
772 Irp->IoStatus.Information = NumberOfEntries * sizeof(MOUSE_INPUT_DATA);
773 }
774
775 /* Go to next packet and complete this request */
776 Irp->IoStatus.Status = Status;
777
778 (VOID)IoSetCancelRoutine(Irp, NULL);
779 IoCompleteRequest(Irp, IO_MOUSE_INCREMENT);
780 DeviceExtension->PendingIrp = NULL;
781 }
782 else
783 {
784 IoAcquireCancelSpinLock(&OldIrql);
785 if (Irp->Cancel)
786 {
787 DeviceExtension->PendingIrp = NULL;
788 Status = STATUS_CANCELLED;
789 }
790 else
791 {
792 IoMarkIrpPending(Irp);
793 DeviceExtension->PendingIrp = Irp;
794 (VOID)IoSetCancelRoutine(Irp, ClassCancelRoutine);
795 Status = STATUS_PENDING;
796 }
797 IoReleaseCancelSpinLock(OldIrql);
798 }
799 return Status;
800 }
801
802 static NTSTATUS NTAPI
803 ClassPnp(
804 IN PDEVICE_OBJECT DeviceObject,
805 IN PIRP Irp)
806 {
807 PPORT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
808 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
809 OBJECT_ATTRIBUTES ObjectAttributes;
810 IO_STATUS_BLOCK Iosb;
811 NTSTATUS Status;
812
813 switch (IrpSp->MinorFunction)
814 {
815 case IRP_MN_START_DEVICE:
816 Status = ForwardIrpAndWait(DeviceObject, Irp);
817 if (NT_SUCCESS(Status))
818 {
819 InitializeObjectAttributes(&ObjectAttributes,
820 &DeviceExtension->InterfaceName,
821 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
822 NULL,
823 NULL);
824
825 Status = ZwOpenFile(&DeviceExtension->FileHandle,
826 FILE_READ_DATA,
827 &ObjectAttributes,
828 &Iosb,
829 0,
830 0);
831 if (!NT_SUCCESS(Status))
832 DeviceExtension->FileHandle = NULL;
833 }
834 else
835 DeviceExtension->FileHandle = NULL;
836 Irp->IoStatus.Status = Status;
837 IoCompleteRequest(Irp, IO_NO_INCREMENT);
838 return Status;
839
840 case IRP_MN_REMOVE_DEVICE:
841 case IRP_MN_STOP_DEVICE:
842 if (DeviceExtension->FileHandle)
843 {
844 ZwClose(DeviceExtension->FileHandle);
845 DeviceExtension->FileHandle = NULL;
846 }
847 Status = STATUS_SUCCESS;
848 break;
849
850 default:
851 Status = Irp->IoStatus.Status;
852 break;
853 }
854
855 Irp->IoStatus.Status = Status;
856 if (NT_SUCCESS(Status) || Status == STATUS_NOT_SUPPORTED)
857 {
858 IoSkipCurrentIrpStackLocation(Irp);
859 return IoCallDriver(DeviceExtension->LowerDevice, Irp);
860 }
861 else
862 {
863 IoCompleteRequest(Irp, IO_NO_INCREMENT);
864 return Status;
865 }
866 }
867
868 static VOID NTAPI
869 ClassStartIo(
870 IN PDEVICE_OBJECT DeviceObject,
871 IN PIRP Irp)
872 {
873 PCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
874 KIRQL OldIrql;
875
876 TRACE_(CLASS_NAME, "ClassStartIo(DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
877
878 ASSERT(DeviceExtension->Common.IsClassDO);
879
880 KeAcquireSpinLock(&DeviceExtension->SpinLock, &OldIrql);
881 HandleReadIrp(DeviceObject, Irp, TRUE);
882 KeReleaseSpinLock(&DeviceExtension->SpinLock, OldIrql);
883 }
884
885 static VOID NTAPI
886 SearchForLegacyDrivers(
887 IN PDRIVER_OBJECT DriverObject,
888 IN PVOID Context, /* PCLASS_DRIVER_EXTENSION */
889 IN ULONG Count)
890 {
891 UNICODE_STRING DeviceMapKeyU = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\HARDWARE\\DEVICEMAP");
892 PCLASS_DRIVER_EXTENSION DriverExtension;
893 UNICODE_STRING PortBaseName = { 0, 0, NULL };
894 PKEY_VALUE_BASIC_INFORMATION KeyValueInformation = NULL;
895 OBJECT_ATTRIBUTES ObjectAttributes;
896 HANDLE hDeviceMapKey = (HANDLE)-1;
897 HANDLE hPortKey = (HANDLE)-1;
898 ULONG Index = 0;
899 ULONG Size, ResultLength;
900 NTSTATUS Status;
901
902 TRACE_(CLASS_NAME, "SearchForLegacyDrivers(%p %p %lu)\n",
903 DriverObject, Context, Count);
904
905 if (Count != 1)
906 return;
907 DriverExtension = (PCLASS_DRIVER_EXTENSION)Context;
908
909 /* Create port base name, by replacing Class by Port at the end of the class base name */
910 Status = DuplicateUnicodeString(
911 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
912 &DriverExtension->DeviceBaseName,
913 &PortBaseName);
914 if (!NT_SUCCESS(Status))
915 {
916 WARN_(CLASS_NAME, "DuplicateUnicodeString() failed with status 0x%08lx\n", Status);
917 goto cleanup;
918 }
919 PortBaseName.Length -= (sizeof(L"Class") - sizeof(UNICODE_NULL));
920 RtlAppendUnicodeToString(&PortBaseName, L"Port");
921
922 /* Allocate memory */
923 Size = sizeof(KEY_VALUE_BASIC_INFORMATION) + MAX_PATH;
924 KeyValueInformation = ExAllocatePoolWithTag(PagedPool, Size, CLASS_TAG);
925 if (!KeyValueInformation)
926 {
927 WARN_(CLASS_NAME, "ExAllocatePoolWithTag() failed\n");
928 Status = STATUS_NO_MEMORY;
929 goto cleanup;
930 }
931
932 /* Open HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP */
933 InitializeObjectAttributes(&ObjectAttributes, &DeviceMapKeyU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
934 Status = ZwOpenKey(&hDeviceMapKey, 0, &ObjectAttributes);
935 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
936 {
937 INFO_(CLASS_NAME, "HKLM\\HARDWARE\\DEVICEMAP is non-existent\n");
938 Status = STATUS_SUCCESS;
939 goto cleanup;
940 }
941 else if (!NT_SUCCESS(Status))
942 {
943 WARN_(CLASS_NAME, "ZwOpenKey() failed with status 0x%08lx\n", Status);
944 goto cleanup;
945 }
946
947 /* Open sub key */
948 InitializeObjectAttributes(&ObjectAttributes, &PortBaseName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, hDeviceMapKey, NULL);
949 Status = ZwOpenKey(&hPortKey, KEY_QUERY_VALUE, &ObjectAttributes);
950 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
951 {
952 INFO_(CLASS_NAME, "HKLM\\HARDWARE\\DEVICEMAP\\%wZ is non-existent\n", &PortBaseName);
953 Status = STATUS_SUCCESS;
954 goto cleanup;
955 }
956 else if (!NT_SUCCESS(Status))
957 {
958 WARN_(CLASS_NAME, "ZwOpenKey() failed with status 0x%08lx\n", Status);
959 goto cleanup;
960 }
961
962 /* Read each value name */
963 while (ZwEnumerateValueKey(hPortKey, Index++, KeyValueBasicInformation, KeyValueInformation, Size, &ResultLength) == STATUS_SUCCESS)
964 {
965 UNICODE_STRING PortName;
966 PDEVICE_OBJECT PortDeviceObject = NULL;
967 PFILE_OBJECT FileObject = NULL;
968
969 PortName.Length = PortName.MaximumLength = (USHORT)KeyValueInformation->NameLength;
970 PortName.Buffer = KeyValueInformation->Name;
971
972 /* Open the device object pointer */
973 Status = IoGetDeviceObjectPointer(&PortName, FILE_READ_ATTRIBUTES, &FileObject, &PortDeviceObject);
974 if (!NT_SUCCESS(Status))
975 {
976 WARN_(CLASS_NAME, "IoGetDeviceObjectPointer(%wZ) failed with status 0x%08lx\n", &PortName, Status);
977 continue;
978 }
979 INFO_(CLASS_NAME, "Legacy driver found\n");
980
981 Status = ClassAddDevice(DriverObject, PortDeviceObject);
982 if (!NT_SUCCESS(Status))
983 {
984 /* FIXME: Log the error */
985 WARN_(CLASS_NAME, "ClassAddDevice() failed with status 0x%08lx\n", Status);
986 }
987
988 ObDereferenceObject(FileObject);
989 }
990
991 cleanup:
992 if (KeyValueInformation != NULL)
993 ExFreePoolWithTag(KeyValueInformation, CLASS_TAG);
994 if (hDeviceMapKey != (HANDLE)-1)
995 ZwClose(hDeviceMapKey);
996 if (hPortKey != (HANDLE)-1)
997 ZwClose(hPortKey);
998 }
999
1000 /*
1001 * Standard DriverEntry method.
1002 */
1003 NTSTATUS NTAPI
1004 DriverEntry(
1005 IN PDRIVER_OBJECT DriverObject,
1006 IN PUNICODE_STRING RegistryPath)
1007 {
1008 PCLASS_DRIVER_EXTENSION DriverExtension;
1009 ULONG i;
1010 NTSTATUS Status;
1011
1012 Status = IoAllocateDriverObjectExtension(
1013 DriverObject,
1014 DriverObject,
1015 sizeof(CLASS_DRIVER_EXTENSION),
1016 (PVOID*)&DriverExtension);
1017 if (!NT_SUCCESS(Status))
1018 {
1019 WARN_(CLASS_NAME, "IoAllocateDriverObjectExtension() failed with status 0x%08lx\n", Status);
1020 return Status;
1021 }
1022 RtlZeroMemory(DriverExtension, sizeof(CLASS_DRIVER_EXTENSION));
1023
1024 Status = DuplicateUnicodeString(
1025 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
1026 RegistryPath,
1027 &DriverExtension->RegistryPath);
1028 if (!NT_SUCCESS(Status))
1029 {
1030 WARN_(CLASS_NAME, "DuplicateUnicodeString() failed with status 0x%08lx\n", Status);
1031 return Status;
1032 }
1033
1034 Status = ReadRegistryEntries(RegistryPath, DriverExtension);
1035 if (!NT_SUCCESS(Status))
1036 {
1037 WARN_(CLASS_NAME, "ReadRegistryEntries() failed with status 0x%08lx\n", Status);
1038 return Status;
1039 }
1040
1041 if (DriverExtension->ConnectMultiplePorts == 1)
1042 {
1043 Status = CreateClassDeviceObject(
1044 DriverObject,
1045 &DriverExtension->MainClassDeviceObject);
1046 if (!NT_SUCCESS(Status))
1047 {
1048 WARN_(CLASS_NAME, "CreateClassDeviceObject() failed with status 0x%08lx\n", Status);
1049 return Status;
1050 }
1051 }
1052
1053 DriverObject->DriverExtension->AddDevice = ClassAddDevice;
1054 DriverObject->DriverUnload = DriverUnload;
1055
1056 for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
1057 DriverObject->MajorFunction[i] = IrpStub;
1058
1059 DriverObject->MajorFunction[IRP_MJ_CREATE] = ClassCreate;
1060 DriverObject->MajorFunction[IRP_MJ_CLOSE] = ClassClose;
1061 DriverObject->MajorFunction[IRP_MJ_CLEANUP] = ClassCleanup;
1062 DriverObject->MajorFunction[IRP_MJ_READ] = ClassRead;
1063 DriverObject->MajorFunction[IRP_MJ_PNP] = ClassPnp;
1064 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ClassDeviceControl;
1065 DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = ForwardIrpAndForget;
1066 DriverObject->DriverStartIo = ClassStartIo;
1067
1068 /* We will detect the legacy devices later */
1069 IoRegisterDriverReinitialization(
1070 DriverObject,
1071 SearchForLegacyDrivers,
1072 DriverExtension);
1073
1074 return STATUS_SUCCESS;
1075 }