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