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