[USB-BRINGUP-TRUNK]
[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 NTSTATUS NTAPI
804 ClassPnp(
805 IN PDEVICE_OBJECT DeviceObject,
806 IN PIRP Irp)
807 {
808 PPORT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
809 PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
810 OBJECT_ATTRIBUTES ObjectAttributes;
811 IO_STATUS_BLOCK Iosb;
812 NTSTATUS Status;
813
814 switch (IrpSp->MinorFunction)
815 {
816 case IRP_MN_START_DEVICE:
817 Status = ForwardIrpAndWait(DeviceObject, Irp);
818 if (NT_SUCCESS(Status))
819 {
820 InitializeObjectAttributes(&ObjectAttributes,
821 &DeviceExtension->InterfaceName,
822 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
823 NULL,
824 NULL);
825
826 Status = ZwOpenFile(&DeviceExtension->FileHandle,
827 FILE_READ_DATA,
828 &ObjectAttributes,
829 &Iosb,
830 0,
831 0);
832 if (!NT_SUCCESS(Status))
833 DeviceExtension->FileHandle = NULL;
834 }
835 else
836 DeviceExtension->FileHandle = NULL;
837 Irp->IoStatus.Status = Status;
838 IoCompleteRequest(Irp, IO_NO_INCREMENT);
839 return Status;
840
841 case IRP_MN_REMOVE_DEVICE:
842 case IRP_MN_STOP_DEVICE:
843 if (DeviceExtension->FileHandle)
844 {
845 ZwClose(DeviceExtension->FileHandle);
846 DeviceExtension->FileHandle = NULL;
847 }
848 Status = STATUS_SUCCESS;
849 break;
850
851 default:
852 Status = Irp->IoStatus.Status;
853 break;
854 }
855
856 Irp->IoStatus.Status = Status;
857 if (NT_SUCCESS(Status))
858 {
859 IoSkipCurrentIrpStackLocation(Irp);
860 return IoCallDriver(DeviceExtension->LowerDevice, Irp);
861 }
862 else
863 {
864 IoCompleteRequest(Irp, IO_NO_INCREMENT);
865 return Status;
866 }
867 }
868
869 static VOID NTAPI
870 ClassStartIo(
871 IN PDEVICE_OBJECT DeviceObject,
872 IN PIRP Irp)
873 {
874 PCLASS_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
875 KIRQL OldIrql;
876
877 TRACE_(CLASS_NAME, "ClassStartIo(DeviceObject %p, Irp %p)\n", DeviceObject, Irp);
878
879 ASSERT(DeviceExtension->Common.IsClassDO);
880
881 KeAcquireSpinLock(&DeviceExtension->SpinLock, &OldIrql);
882 HandleReadIrp(DeviceObject, Irp, TRUE);
883 KeReleaseSpinLock(&DeviceExtension->SpinLock, OldIrql);
884 }
885
886 static VOID NTAPI
887 SearchForLegacyDrivers(
888 IN PDRIVER_OBJECT DriverObject,
889 IN PVOID Context, /* PCLASS_DRIVER_EXTENSION */
890 IN ULONG Count)
891 {
892 UNICODE_STRING DeviceMapKeyU = RTL_CONSTANT_STRING(L"\\REGISTRY\\MACHINE\\HARDWARE\\DEVICEMAP");
893 PCLASS_DRIVER_EXTENSION DriverExtension;
894 UNICODE_STRING PortBaseName = { 0, 0, NULL };
895 PKEY_VALUE_BASIC_INFORMATION KeyValueInformation = NULL;
896 OBJECT_ATTRIBUTES ObjectAttributes;
897 HANDLE hDeviceMapKey = (HANDLE)-1;
898 HANDLE hPortKey = (HANDLE)-1;
899 ULONG Index = 0;
900 ULONG Size, ResultLength;
901 NTSTATUS Status;
902
903 TRACE_(CLASS_NAME, "SearchForLegacyDrivers(%p %p %lu)\n",
904 DriverObject, Context, Count);
905
906 if (Count != 1)
907 return;
908 DriverExtension = (PCLASS_DRIVER_EXTENSION)Context;
909
910 /* Create port base name, by replacing Class by Port at the end of the class base name */
911 Status = DuplicateUnicodeString(
912 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
913 &DriverExtension->DeviceBaseName,
914 &PortBaseName);
915 if (!NT_SUCCESS(Status))
916 {
917 WARN_(CLASS_NAME, "DuplicateUnicodeString() failed with status 0x%08lx\n", Status);
918 goto cleanup;
919 }
920 PortBaseName.Length -= (sizeof(L"Class") - sizeof(UNICODE_NULL));
921 RtlAppendUnicodeToString(&PortBaseName, L"Port");
922
923 /* Allocate memory */
924 Size = sizeof(KEY_VALUE_BASIC_INFORMATION) + MAX_PATH;
925 KeyValueInformation = ExAllocatePoolWithTag(PagedPool, Size, CLASS_TAG);
926 if (!KeyValueInformation)
927 {
928 WARN_(CLASS_NAME, "ExAllocatePoolWithTag() failed\n");
929 Status = STATUS_NO_MEMORY;
930 goto cleanup;
931 }
932
933 /* Open HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP */
934 InitializeObjectAttributes(&ObjectAttributes, &DeviceMapKeyU, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
935 Status = ZwOpenKey(&hDeviceMapKey, 0, &ObjectAttributes);
936 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
937 {
938 INFO_(CLASS_NAME, "HKLM\\HARDWARE\\DEVICEMAP is non-existent\n");
939 Status = STATUS_SUCCESS;
940 goto cleanup;
941 }
942 else if (!NT_SUCCESS(Status))
943 {
944 WARN_(CLASS_NAME, "ZwOpenKey() failed with status 0x%08lx\n", Status);
945 goto cleanup;
946 }
947
948 /* Open sub key */
949 InitializeObjectAttributes(&ObjectAttributes, &PortBaseName, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, hDeviceMapKey, NULL);
950 Status = ZwOpenKey(&hPortKey, KEY_QUERY_VALUE, &ObjectAttributes);
951 if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
952 {
953 INFO_(CLASS_NAME, "HKLM\\HARDWARE\\DEVICEMAP\\%wZ is non-existent\n", &PortBaseName);
954 Status = STATUS_SUCCESS;
955 goto cleanup;
956 }
957 else if (!NT_SUCCESS(Status))
958 {
959 WARN_(CLASS_NAME, "ZwOpenKey() failed with status 0x%08lx\n", Status);
960 goto cleanup;
961 }
962
963 /* Read each value name */
964 while (ZwEnumerateValueKey(hPortKey, Index++, KeyValueBasicInformation, KeyValueInformation, Size, &ResultLength) == STATUS_SUCCESS)
965 {
966 UNICODE_STRING PortName;
967 PDEVICE_OBJECT PortDeviceObject = NULL;
968 PFILE_OBJECT FileObject = NULL;
969
970 PortName.Length = PortName.MaximumLength = (USHORT)KeyValueInformation->NameLength;
971 PortName.Buffer = KeyValueInformation->Name;
972
973 /* Open the device object pointer */
974 Status = IoGetDeviceObjectPointer(&PortName, FILE_READ_ATTRIBUTES, &FileObject, &PortDeviceObject);
975 if (!NT_SUCCESS(Status))
976 {
977 WARN_(CLASS_NAME, "IoGetDeviceObjectPointer(%wZ) failed with status 0x%08lx\n", &PortName, Status);
978 continue;
979 }
980 INFO_(CLASS_NAME, "Legacy driver found\n");
981
982 Status = ClassAddDevice(DriverObject, PortDeviceObject);
983 if (!NT_SUCCESS(Status))
984 {
985 /* FIXME: Log the error */
986 WARN_(CLASS_NAME, "ClassAddDevice() failed with status 0x%08lx\n", Status);
987 }
988 }
989
990 cleanup:
991 if (KeyValueInformation != NULL)
992 ExFreePoolWithTag(KeyValueInformation, CLASS_TAG);
993 if (hDeviceMapKey != (HANDLE)-1)
994 ZwClose(hDeviceMapKey);
995 if (hPortKey != (HANDLE)-1)
996 ZwClose(hPortKey);
997 }
998
999 /*
1000 * Standard DriverEntry method.
1001 */
1002 NTSTATUS NTAPI
1003 DriverEntry(
1004 IN PDRIVER_OBJECT DriverObject,
1005 IN PUNICODE_STRING RegistryPath)
1006 {
1007 PCLASS_DRIVER_EXTENSION DriverExtension;
1008 ULONG i;
1009 NTSTATUS Status;
1010
1011 Status = IoAllocateDriverObjectExtension(
1012 DriverObject,
1013 DriverObject,
1014 sizeof(CLASS_DRIVER_EXTENSION),
1015 (PVOID*)&DriverExtension);
1016 if (!NT_SUCCESS(Status))
1017 {
1018 WARN_(CLASS_NAME, "IoAllocateDriverObjectExtension() failed with status 0x%08lx\n", Status);
1019 return Status;
1020 }
1021 RtlZeroMemory(DriverExtension, sizeof(CLASS_DRIVER_EXTENSION));
1022
1023 Status = DuplicateUnicodeString(
1024 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
1025 RegistryPath,
1026 &DriverExtension->RegistryPath);
1027 if (!NT_SUCCESS(Status))
1028 {
1029 WARN_(CLASS_NAME, "DuplicateUnicodeString() failed with status 0x%08lx\n", Status);
1030 return Status;
1031 }
1032
1033 Status = ReadRegistryEntries(RegistryPath, DriverExtension);
1034 if (!NT_SUCCESS(Status))
1035 {
1036 WARN_(CLASS_NAME, "ReadRegistryEntries() failed with status 0x%08lx\n", Status);
1037 return Status;
1038 }
1039
1040 if (DriverExtension->ConnectMultiplePorts == 1)
1041 {
1042 Status = CreateClassDeviceObject(
1043 DriverObject,
1044 &DriverExtension->MainClassDeviceObject);
1045 if (!NT_SUCCESS(Status))
1046 {
1047 WARN_(CLASS_NAME, "CreateClassDeviceObject() failed with status 0x%08lx\n", Status);
1048 return Status;
1049 }
1050 }
1051
1052 DriverObject->DriverExtension->AddDevice = ClassAddDevice;
1053 DriverObject->DriverUnload = DriverUnload;
1054
1055 for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
1056 DriverObject->MajorFunction[i] = IrpStub;
1057
1058 DriverObject->MajorFunction[IRP_MJ_CREATE] = ClassCreate;
1059 DriverObject->MajorFunction[IRP_MJ_CLOSE] = ClassClose;
1060 DriverObject->MajorFunction[IRP_MJ_CLEANUP] = ClassCleanup;
1061 DriverObject->MajorFunction[IRP_MJ_READ] = ClassRead;
1062 DriverObject->MajorFunction[IRP_MJ_PNP] = ClassPnp;
1063 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ClassDeviceControl;
1064 DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = ForwardIrpAndForget;
1065 DriverObject->DriverStartIo = ClassStartIo;
1066
1067 /* We will detect the legacy devices later */
1068 IoRegisterDriverReinitialization(
1069 DriverObject,
1070 SearchForLegacyDrivers,
1071 DriverExtension);
1072
1073 return STATUS_SUCCESS;
1074 }