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