- Update to r53061
[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 /* Hm, this shouldn't happen */
729 ASSERT(FALSE);
730 }
731 }
732
733 static NTSTATUS
734 HandleReadIrp(
735 IN PDEVICE_OBJECT DeviceObject,
736 IN PIRP Irp,
737 BOOLEAN IsInStartIo)
738 {
739 PCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
740 NTSTATUS Status;
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 (VOID)IoSetCancelRoutine(Irp, ClassCancelRoutine);
785 if (Irp->Cancel && IoSetCancelRoutine(Irp, NULL))
786 {
787 DeviceExtension->PendingIrp = NULL;
788 Status = STATUS_CANCELLED;
789 }
790 else
791 {
792 IoMarkIrpPending(Irp);
793 DeviceExtension->PendingIrp = Irp;
794 Status = STATUS_PENDING;
795 }
796 }
797 return Status;
798 }
799
800 static VOID NTAPI
801 ClassStartIo(
802 IN PDEVICE_OBJECT DeviceObject,
803 IN PIRP Irp)
804 {
805 PCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
806 KIRQL OldIrql;
807
808 TRACE_(CLASS_NAME, "ClassStartIo(DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
809
810 ASSERT(DeviceExtension->Common.IsClassDO);
811
812 KeAcquireSpinLock(&DeviceExtension->SpinLock, &OldIrql);
813 HandleReadIrp(DeviceObject, Irp, TRUE);
814 KeReleaseSpinLock(&DeviceExtension->SpinLock, OldIrql);
815 }
816
817 static VOID NTAPI
818 SearchForLegacyDrivers(
819 IN PDRIVER_OBJECT DriverObject,
820 IN PVOID Context, /* PCLASS_DRIVER_EXTENSION */
821 IN ULONG Count)
822 {
823 UNICODE_STRING DeviceMapKeyU = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\HARDWARE\\DEVICEMAP");
824 PCLASS_DRIVER_EXTENSION DriverExtension;
825 UNICODE_STRING PortBaseName = { 0, 0, NULL };
826 PKEY_VALUE_BASIC_INFORMATION KeyValueInformation = NULL;
827 OBJECT_ATTRIBUTES ObjectAttributes;
828 HANDLE hDeviceMapKey = (HANDLE)-1;
829 HANDLE hPortKey = (HANDLE)-1;
830 ULONG Index = 0;
831 ULONG Size, ResultLength;
832 NTSTATUS Status;
833
834 TRACE_(CLASS_NAME, "SearchForLegacyDrivers(%p %p %lu)\n",
835 DriverObject, Context, Count);
836
837 if (Count != 1)
838 return;
839 DriverExtension = (PCLASS_DRIVER_EXTENSION)Context;
840
841 /* Create port base name, by replacing Class by Port at the end of the class base name */
842 Status = DuplicateUnicodeString(
843 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
844 &DriverExtension->DeviceBaseName,
845 &PortBaseName);
846 if (!NT_SUCCESS(Status))
847 {
848 WARN_(CLASS_NAME, "DuplicateUnicodeString() failed with status 0x%08lx\n", Status);
849 goto cleanup;
850 }
851 PortBaseName.Length -= (sizeof(L"Class") - sizeof(UNICODE_NULL));
852 RtlAppendUnicodeToString(&PortBaseName, L"Port");
853
854 /* Allocate memory */
855 Size = sizeof(KEY_VALUE_BASIC_INFORMATION) + MAX_PATH;
856 KeyValueInformation = ExAllocatePoolWithTag(PagedPool, Size, CLASS_TAG);
857 if (!KeyValueInformation)
858 {
859 WARN_(CLASS_NAME, "ExAllocatePoolWithTag() failed\n");
860 Status = STATUS_NO_MEMORY;
861 goto cleanup;
862 }
863
864 /* Open HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP */
865 InitializeObjectAttributes(&ObjectAttributes, &DeviceMapKeyU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
866 Status = ZwOpenKey(&hDeviceMapKey, 0, &ObjectAttributes);
867 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
868 {
869 INFO_(CLASS_NAME, "HKLM\\HARDWARE\\DEVICEMAP is non-existent\n");
870 Status = STATUS_SUCCESS;
871 goto cleanup;
872 }
873 else if (!NT_SUCCESS(Status))
874 {
875 WARN_(CLASS_NAME, "ZwOpenKey() failed with status 0x%08lx\n", Status);
876 goto cleanup;
877 }
878
879 /* Open sub key */
880 InitializeObjectAttributes(&ObjectAttributes, &PortBaseName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, hDeviceMapKey, NULL);
881 Status = ZwOpenKey(&hPortKey, KEY_QUERY_VALUE, &ObjectAttributes);
882 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
883 {
884 INFO_(CLASS_NAME, "HKLM\\HARDWARE\\DEVICEMAP\\%wZ is non-existent\n", &PortBaseName);
885 Status = STATUS_SUCCESS;
886 goto cleanup;
887 }
888 else if (!NT_SUCCESS(Status))
889 {
890 WARN_(CLASS_NAME, "ZwOpenKey() failed with status 0x%08lx\n", Status);
891 goto cleanup;
892 }
893
894 /* Read each value name */
895 while (ZwEnumerateValueKey(hPortKey, Index++, KeyValueBasicInformation, KeyValueInformation, Size, &ResultLength) == STATUS_SUCCESS)
896 {
897 UNICODE_STRING PortName;
898 PDEVICE_OBJECT PortDeviceObject = NULL;
899 PFILE_OBJECT FileObject = NULL;
900
901 PortName.Length = PortName.MaximumLength = (USHORT)KeyValueInformation->NameLength;
902 PortName.Buffer = KeyValueInformation->Name;
903
904 /* Open the device object pointer */
905 Status = IoGetDeviceObjectPointer(&PortName, FILE_READ_ATTRIBUTES, &FileObject, &PortDeviceObject);
906 if (!NT_SUCCESS(Status))
907 {
908 WARN_(CLASS_NAME, "IoGetDeviceObjectPointer(%wZ) failed with status 0x%08lx\n", &PortName, Status);
909 continue;
910 }
911 INFO_(CLASS_NAME, "Legacy driver found\n");
912
913 Status = ClassAddDevice(DriverObject, PortDeviceObject);
914 if (!NT_SUCCESS(Status))
915 {
916 /* FIXME: Log the error */
917 WARN_(CLASS_NAME, "ClassAddDevice() failed with status 0x%08lx\n", Status);
918 }
919 }
920
921 cleanup:
922 if (KeyValueInformation != NULL)
923 ExFreePoolWithTag(KeyValueInformation, CLASS_TAG);
924 if (hDeviceMapKey != (HANDLE)-1)
925 ZwClose(hDeviceMapKey);
926 if (hPortKey != (HANDLE)-1)
927 ZwClose(hPortKey);
928 }
929
930 /*
931 * Standard DriverEntry method.
932 */
933 NTSTATUS NTAPI
934 DriverEntry(
935 IN PDRIVER_OBJECT DriverObject,
936 IN PUNICODE_STRING RegistryPath)
937 {
938 PCLASS_DRIVER_EXTENSION DriverExtension;
939 ULONG i;
940 NTSTATUS Status;
941
942 Status = IoAllocateDriverObjectExtension(
943 DriverObject,
944 DriverObject,
945 sizeof(CLASS_DRIVER_EXTENSION),
946 (PVOID*)&DriverExtension);
947 if (!NT_SUCCESS(Status))
948 {
949 WARN_(CLASS_NAME, "IoAllocateDriverObjectExtension() failed with status 0x%08lx\n", Status);
950 return Status;
951 }
952 RtlZeroMemory(DriverExtension, sizeof(CLASS_DRIVER_EXTENSION));
953
954 Status = DuplicateUnicodeString(
955 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
956 RegistryPath,
957 &DriverExtension->RegistryPath);
958 if (!NT_SUCCESS(Status))
959 {
960 WARN_(CLASS_NAME, "DuplicateUnicodeString() failed with status 0x%08lx\n", Status);
961 return Status;
962 }
963
964 Status = ReadRegistryEntries(RegistryPath, DriverExtension);
965 if (!NT_SUCCESS(Status))
966 {
967 WARN_(CLASS_NAME, "ReadRegistryEntries() failed with status 0x%08lx\n", Status);
968 return Status;
969 }
970
971 if (DriverExtension->ConnectMultiplePorts == 1)
972 {
973 Status = CreateClassDeviceObject(
974 DriverObject,
975 &DriverExtension->MainClassDeviceObject);
976 if (!NT_SUCCESS(Status))
977 {
978 WARN_(CLASS_NAME, "CreateClassDeviceObject() failed with status 0x%08lx\n", Status);
979 return Status;
980 }
981 }
982
983 DriverObject->DriverExtension->AddDevice = ClassAddDevice;
984 DriverObject->DriverUnload = DriverUnload;
985
986 for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
987 DriverObject->MajorFunction[i] = IrpStub;
988
989 DriverObject->MajorFunction[IRP_MJ_CREATE] = ClassCreate;
990 DriverObject->MajorFunction[IRP_MJ_CLOSE] = ClassClose;
991 DriverObject->MajorFunction[IRP_MJ_CLEANUP] = ClassCleanup;
992 DriverObject->MajorFunction[IRP_MJ_READ] = ClassRead;
993 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ClassDeviceControl;
994 DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = ForwardIrpAndForget;
995 DriverObject->DriverStartIo = ClassStartIo;
996
997 /* We will detect the legacy devices later */
998 IoRegisterDriverReinitialization(
999 DriverObject,
1000 SearchForLegacyDrivers,
1001 DriverExtension);
1002
1003 return STATUS_SUCCESS;
1004 }