1 /* 2 * PROJECT: ReactOS i8042 (ps/2 keyboard-mouse controller) driver 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: drivers/input/i8042prt/i8042prt.c 5 * PURPOSE: Driver entry function 6 * PROGRAMMERS: Copyright Victor Kirhenshtein (sauros@iname.com) 7 Copyright Jason Filby (jasonfilby@yahoo.com) 8 Copyright Martijn Vernooij (o112w8r02@sneakemail.com) 9 Copyright 2006-2007 Herv� Poussineau (hpoussin@reactos.org) 10 */ 11 12 /* INCLUDES ******************************************************************/ 13 14 #include "i8042prt.h" 15 16 #include <debug.h> 17 18 /* FUNCTIONS *****************************************************************/ 19 20 static DRIVER_STARTIO i8042StartIo; 21 static DRIVER_DISPATCH IrpStub; 22 _Dispatch_type_(IRP_MJ_DEVICE_CONTROL) 23 static DRIVER_DISPATCH i8042DeviceControl; 24 _Dispatch_type_(IRP_MJ_INTERNAL_DEVICE_CONTROL) 25 static DRIVER_DISPATCH i8042InternalDeviceControl; 26 _Dispatch_type_(IRP_MJ_SYSTEM_CONTROL) 27 static DRIVER_DISPATCH i8042SystemControl; 28 _Dispatch_type_(IRP_MJ_POWER) 29 static DRIVER_DISPATCH i8042Power; 30 DRIVER_INITIALIZE DriverEntry; 31 32 NTSTATUS NTAPI 33 i8042AddDevice( 34 IN PDRIVER_OBJECT DriverObject, 35 IN PDEVICE_OBJECT Pdo) 36 { 37 PI8042_DRIVER_EXTENSION DriverExtension; 38 PFDO_DEVICE_EXTENSION DeviceExtension = NULL; 39 PDEVICE_OBJECT Fdo = NULL; 40 ULONG DeviceExtensionSize; 41 NTSTATUS Status; 42 43 TRACE_(I8042PRT, "i8042AddDevice(%p %p)\n", DriverObject, Pdo); 44 45 DriverExtension = (PI8042_DRIVER_EXTENSION)IoGetDriverObjectExtension(DriverObject, DriverObject); 46 47 if (Pdo == NULL) 48 { 49 /* We're getting a NULL Pdo at the first call as 50 * we are a legacy driver. Ignore it */ 51 return STATUS_SUCCESS; 52 } 53 54 /* Create new device object. As we don't know if the device would be a keyboard 55 * or a mouse, we have to allocate the biggest device extension. */ 56 DeviceExtensionSize = MAX(sizeof(I8042_KEYBOARD_EXTENSION), sizeof(I8042_MOUSE_EXTENSION)); 57 Status = IoCreateDevice( 58 DriverObject, 59 DeviceExtensionSize, 60 NULL, 61 Pdo->DeviceType, 62 FILE_DEVICE_SECURE_OPEN, 63 TRUE, 64 &Fdo); 65 if (!NT_SUCCESS(Status)) 66 { 67 WARN_(I8042PRT, "IoCreateDevice() failed with status 0x%08lx\n", Status); 68 goto cleanup; 69 } 70 71 DeviceExtension = (PFDO_DEVICE_EXTENSION)Fdo->DeviceExtension; 72 RtlZeroMemory(DeviceExtension, DeviceExtensionSize); 73 DeviceExtension->Type = Unknown; 74 DeviceExtension->Fdo = Fdo; 75 DeviceExtension->Pdo = Pdo; 76 DeviceExtension->PortDeviceExtension = &DriverExtension->Port; 77 Status = IoAttachDeviceToDeviceStackSafe(Fdo, Pdo, &DeviceExtension->LowerDevice); 78 if (!NT_SUCCESS(Status)) 79 { 80 WARN_(I8042PRT, "IoAttachDeviceToDeviceStackSafe() failed with status 0x%08lx\n", Status); 81 goto cleanup; 82 } 83 84 ExInterlockedInsertTailList( 85 &DriverExtension->DeviceListHead, 86 &DeviceExtension->ListEntry, 87 &DriverExtension->DeviceListLock); 88 89 Fdo->Flags &= ~DO_DEVICE_INITIALIZING; 90 return STATUS_SUCCESS; 91 92 cleanup: 93 if (DeviceExtension && DeviceExtension->LowerDevice) 94 IoDetachDevice(DeviceExtension->LowerDevice); 95 if (Fdo) 96 IoDeleteDevice(Fdo); 97 return Status; 98 } 99 100 VOID NTAPI 101 i8042SendHookWorkItem( 102 IN PDEVICE_OBJECT DeviceObject, 103 IN PVOID Context) 104 { 105 PI8042_HOOK_WORKITEM WorkItemData; 106 PFDO_DEVICE_EXTENSION FdoDeviceExtension; 107 PPORT_DEVICE_EXTENSION PortDeviceExtension; 108 PDEVICE_OBJECT TopOfStack = NULL; 109 ULONG IoControlCode; 110 PVOID InputBuffer; 111 ULONG InputBufferLength; 112 IO_STATUS_BLOCK IoStatus; 113 KEVENT Event; 114 PIRP NewIrp; 115 NTSTATUS Status; 116 117 TRACE_(I8042PRT, "i8042SendHookWorkItem(%p %p)\n", DeviceObject, Context); 118 119 WorkItemData = (PI8042_HOOK_WORKITEM)Context; 120 FdoDeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; 121 PortDeviceExtension = FdoDeviceExtension->PortDeviceExtension; 122 123 switch (FdoDeviceExtension->Type) 124 { 125 case Keyboard: 126 { 127 PI8042_KEYBOARD_EXTENSION DeviceExtension; 128 DeviceExtension = (PI8042_KEYBOARD_EXTENSION)FdoDeviceExtension; 129 IoControlCode = IOCTL_INTERNAL_I8042_HOOK_KEYBOARD; 130 InputBuffer = &DeviceExtension->KeyboardHook; 131 InputBufferLength = sizeof(INTERNAL_I8042_HOOK_KEYBOARD); 132 break; 133 } 134 case Mouse: 135 { 136 PI8042_MOUSE_EXTENSION DeviceExtension; 137 DeviceExtension = (PI8042_MOUSE_EXTENSION)FdoDeviceExtension; 138 IoControlCode = IOCTL_INTERNAL_I8042_HOOK_MOUSE; 139 InputBuffer = &DeviceExtension->MouseHook; 140 InputBufferLength = sizeof(INTERNAL_I8042_HOOK_MOUSE); 141 break; 142 } 143 default: 144 { 145 ERR_(I8042PRT, "Unknown FDO type %u\n", FdoDeviceExtension->Type); 146 ASSERT(FALSE); 147 WorkItemData->Irp->IoStatus.Status = STATUS_INTERNAL_ERROR; 148 goto cleanup; 149 } 150 } 151 152 KeInitializeEvent(&Event, NotificationEvent, FALSE); 153 TopOfStack = IoGetAttachedDeviceReference(DeviceObject); 154 155 NewIrp = IoBuildDeviceIoControlRequest( 156 IoControlCode, 157 TopOfStack, 158 InputBuffer, 159 InputBufferLength, 160 NULL, 161 0, 162 TRUE, 163 &Event, 164 &IoStatus); 165 166 if (!NewIrp) 167 { 168 WARN_(I8042PRT, "IoBuildDeviceIoControlRequest() failed\n"); 169 WorkItemData->Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES; 170 goto cleanup; 171 } 172 173 Status = IoCallDriver(TopOfStack, NewIrp); 174 if (Status == STATUS_PENDING) 175 { 176 KeWaitForSingleObject( 177 &Event, 178 Executive, 179 KernelMode, 180 FALSE, 181 NULL); 182 Status = IoStatus.Status; 183 } 184 if (!NT_SUCCESS(Status)) 185 { 186 WARN_(I8042PRT, "IoCallDriver() failed with status 0x%08lx\n", Status); 187 goto cleanup; 188 } 189 190 if (FdoDeviceExtension->Type == Keyboard) 191 { 192 PI8042_KEYBOARD_EXTENSION DeviceExtension; 193 194 DeviceExtension = (PI8042_KEYBOARD_EXTENSION)FdoDeviceExtension; 195 /* Call the hooked initialization if it exists */ 196 if (DeviceExtension->KeyboardHook.InitializationRoutine) 197 { 198 Status = DeviceExtension->KeyboardHook.InitializationRoutine( 199 DeviceExtension->KeyboardHook.Context, 200 PortDeviceExtension, 201 i8042SynchReadPort, 202 i8042SynchWritePortKbd, 203 FALSE); 204 if (!NT_SUCCESS(Status)) 205 { 206 WARN_(I8042PRT, "KeyboardHook.InitializationRoutine() failed with status 0x%08lx\n", Status); 207 WorkItemData->Irp->IoStatus.Status = Status; 208 goto cleanup; 209 } 210 } 211 } 212 213 WorkItemData->Irp->IoStatus.Status = STATUS_SUCCESS; 214 215 cleanup: 216 if (TopOfStack != NULL) 217 ObDereferenceObject(TopOfStack); 218 WorkItemData->Irp->IoStatus.Information = 0; 219 IoCompleteRequest(WorkItemData->Irp, IO_NO_INCREMENT); 220 221 IoFreeWorkItem(WorkItemData->WorkItem); 222 ExFreePoolWithTag(WorkItemData, I8042PRT_TAG); 223 } 224 225 static VOID NTAPI 226 i8042StartIo( 227 IN PDEVICE_OBJECT DeviceObject, 228 IN PIRP Irp) 229 { 230 PFDO_DEVICE_EXTENSION DeviceExtension; 231 232 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; 233 switch (DeviceExtension->Type) 234 { 235 case Keyboard: 236 i8042KbdStartIo(DeviceObject, Irp); 237 break; 238 default: 239 ERR_(I8042PRT, "Unknown FDO type %u\n", DeviceExtension->Type); 240 ASSERT(FALSE); 241 break; 242 } 243 } 244 245 /* Write the current byte of the packet. Returns FALSE in case 246 * of problems. 247 */ 248 static BOOLEAN 249 i8042PacketWrite( 250 IN PPORT_DEVICE_EXTENSION DeviceExtension) 251 { 252 UCHAR Port = DeviceExtension->PacketPort; 253 254 if (Port) 255 { 256 if (!i8042Write(DeviceExtension, 257 DeviceExtension->ControlPort, 258 Port)) 259 { 260 /* something is really wrong! */ 261 WARN_(I8042PRT, "Failed to send packet byte!\n"); 262 return FALSE; 263 } 264 } 265 266 return i8042Write(DeviceExtension, 267 DeviceExtension->DataPort, 268 DeviceExtension->Packet.Bytes[DeviceExtension->Packet.CurrentByte]); 269 } 270 271 BOOLEAN 272 i8042PacketIsr( 273 IN PPORT_DEVICE_EXTENSION DeviceExtension, 274 IN UCHAR Output) 275 { 276 if (DeviceExtension->Packet.State == Idle) 277 return FALSE; 278 279 switch (Output) 280 { 281 case KBD_RESEND: 282 DeviceExtension->PacketResends++; 283 if (DeviceExtension->PacketResends > DeviceExtension->Settings.ResendIterations) 284 { 285 DeviceExtension->Packet.State = Idle; 286 DeviceExtension->PacketComplete = TRUE; 287 DeviceExtension->PacketResult = STATUS_IO_TIMEOUT; 288 DeviceExtension->PacketResends = 0; 289 return TRUE; 290 } 291 DeviceExtension->Packet.CurrentByte--; 292 break; 293 294 case KBD_NACK: 295 DeviceExtension->Packet.State = Idle; 296 DeviceExtension->PacketComplete = TRUE; 297 DeviceExtension->PacketResult = STATUS_UNEXPECTED_IO_ERROR; 298 DeviceExtension->PacketResends = 0; 299 return TRUE; 300 301 default: 302 DeviceExtension->PacketResends = 0; 303 } 304 305 if (DeviceExtension->Packet.CurrentByte >= DeviceExtension->Packet.ByteCount) 306 { 307 DeviceExtension->Packet.State = Idle; 308 DeviceExtension->PacketComplete = TRUE; 309 DeviceExtension->PacketResult = STATUS_SUCCESS; 310 return TRUE; 311 } 312 313 if (!i8042PacketWrite(DeviceExtension)) 314 { 315 DeviceExtension->Packet.State = Idle; 316 DeviceExtension->PacketComplete = TRUE; 317 DeviceExtension->PacketResult = STATUS_IO_TIMEOUT; 318 return TRUE; 319 } 320 DeviceExtension->Packet.CurrentByte++; 321 322 return TRUE; 323 } 324 325 /* 326 * This function starts a packet. It must be called with the 327 * correct DIRQL. 328 */ 329 NTSTATUS 330 i8042StartPacket( 331 IN PPORT_DEVICE_EXTENSION DeviceExtension, 332 IN PFDO_DEVICE_EXTENSION FdoDeviceExtension, 333 IN PUCHAR Bytes, 334 IN ULONG ByteCount, 335 IN PIRP Irp) 336 { 337 KIRQL Irql; 338 NTSTATUS Status; 339 340 Irql = KeAcquireInterruptSpinLock(DeviceExtension->HighestDIRQLInterrupt); 341 342 if (DeviceExtension->Packet.State != Idle) 343 { 344 Status = STATUS_DEVICE_BUSY; 345 goto done; 346 } 347 348 switch (FdoDeviceExtension->Type) 349 { 350 case Keyboard: DeviceExtension->PacketPort = 0; break; 351 case Mouse: DeviceExtension->PacketPort = CTRL_WRITE_MOUSE; break; 352 default: 353 ERR_(I8042PRT, "Unknown FDO type %u\n", FdoDeviceExtension->Type); 354 ASSERT(FALSE); 355 Status = STATUS_INTERNAL_ERROR; 356 goto done; 357 } 358 359 DeviceExtension->Packet.Bytes = Bytes; 360 DeviceExtension->Packet.CurrentByte = 0; 361 DeviceExtension->Packet.ByteCount = ByteCount; 362 DeviceExtension->Packet.State = SendingBytes; 363 DeviceExtension->PacketResult = Status = STATUS_PENDING; 364 DeviceExtension->CurrentIrp = Irp; 365 DeviceExtension->CurrentIrpDevice = FdoDeviceExtension->Fdo; 366 367 if (!i8042PacketWrite(DeviceExtension)) 368 { 369 Status = STATUS_IO_TIMEOUT; 370 DeviceExtension->Packet.State = Idle; 371 DeviceExtension->PacketResult = STATUS_ABANDONED; 372 goto done; 373 } 374 375 DeviceExtension->Packet.CurrentByte++; 376 377 done: 378 KeReleaseInterruptSpinLock(DeviceExtension->HighestDIRQLInterrupt, Irql); 379 380 if (Status != STATUS_PENDING) 381 { 382 DeviceExtension->CurrentIrp = NULL; 383 DeviceExtension->CurrentIrpDevice = NULL; 384 Irp->IoStatus.Status = Status; 385 IoCompleteRequest(Irp, IO_NO_INCREMENT); 386 } 387 return Status; 388 } 389 390 static NTSTATUS NTAPI 391 IrpStub( 392 IN PDEVICE_OBJECT DeviceObject, 393 IN PIRP Irp) 394 { 395 NTSTATUS Status = Irp->IoStatus.Status; 396 397 UNREFERENCED_PARAMETER(DeviceObject); 398 399 /* Do nothing */ 400 ASSERT(FALSE); 401 IoCompleteRequest(Irp, IO_NO_INCREMENT); 402 return Status; 403 } 404 405 static NTSTATUS NTAPI 406 i8042DeviceControl( 407 IN PDEVICE_OBJECT DeviceObject, 408 IN PIRP Irp) 409 { 410 PFDO_DEVICE_EXTENSION DeviceExtension; 411 412 TRACE_(I8042PRT, "i8042DeviceControl(%p %p)\n", DeviceObject, Irp); 413 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; 414 415 switch (DeviceExtension->Type) 416 { 417 case Keyboard: 418 return i8042KbdDeviceControl(DeviceObject, Irp); 419 break; 420 default: 421 return IrpStub(DeviceObject, Irp); 422 } 423 } 424 425 static NTSTATUS NTAPI 426 i8042InternalDeviceControl( 427 IN PDEVICE_OBJECT DeviceObject, 428 IN PIRP Irp) 429 { 430 PFDO_DEVICE_EXTENSION DeviceExtension; 431 ULONG ControlCode; 432 NTSTATUS Status; 433 434 TRACE_(I8042PRT, "i8042InternalDeviceControl(%p %p)\n", DeviceObject, Irp); 435 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension; 436 437 switch (DeviceExtension->Type) 438 { 439 case Unknown: 440 { 441 ControlCode = IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode; 442 switch (ControlCode) 443 { 444 case IOCTL_INTERNAL_KEYBOARD_CONNECT: 445 Status = i8042KbdInternalDeviceControl(DeviceObject, Irp); 446 break; 447 case IOCTL_INTERNAL_MOUSE_CONNECT: 448 Status = i8042MouInternalDeviceControl(DeviceObject, Irp); 449 break; 450 default: 451 ERR_(I8042PRT, "Unknown IO control code 0x%lx\n", ControlCode); 452 ASSERT(FALSE); 453 Status = STATUS_INVALID_DEVICE_REQUEST; 454 break; 455 } 456 break; 457 } 458 case Keyboard: 459 Status = i8042KbdInternalDeviceControl(DeviceObject, Irp); 460 break; 461 case Mouse: 462 Status = i8042MouInternalDeviceControl(DeviceObject, Irp); 463 break; 464 default: 465 ERR_(I8042PRT, "Unknown FDO type %u\n", DeviceExtension->Type); 466 ASSERT(FALSE); 467 Status = STATUS_INTERNAL_ERROR; 468 IoCompleteRequest(Irp, IO_NO_INCREMENT); 469 break; 470 } 471 472 return Status; 473 } 474 475 static NTSTATUS NTAPI 476 i8042Power( 477 IN PDEVICE_OBJECT DeviceObject, 478 IN PIRP Irp) 479 { 480 PFDO_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; 481 PDEVICE_OBJECT LowerDevice = DeviceExtension->LowerDevice; 482 483 PoStartNextPowerIrp(Irp); 484 IoSkipCurrentIrpStackLocation(Irp); 485 return PoCallDriver(LowerDevice, Irp); 486 } 487 488 static NTSTATUS NTAPI 489 i8042SystemControl( 490 IN PDEVICE_OBJECT DeviceObject, 491 IN PIRP Irp) 492 { 493 return ForwardIrpAndForget(DeviceObject, Irp); 494 } 495 496 NTSTATUS NTAPI 497 DriverEntry( 498 IN PDRIVER_OBJECT DriverObject, 499 IN PUNICODE_STRING RegistryPath) 500 { 501 PI8042_DRIVER_EXTENSION DriverExtension; 502 ULONG i; 503 NTSTATUS Status; 504 505 /* ROS Hack: ideally, we shouldn't have to initialize debug level this way, 506 but since the only way is to change it via KDBG, it's better to leave 507 it here too. */ 508 #if 0 509 DbgSetDebugFilterState( 510 DPFLTR_I8042PRT_ID, 511 (1 << DPFLTR_ERROR_LEVEL) | (1 << DPFLTR_WARNING_LEVEL) | 512 (1 << DPFLTR_TRACE_LEVEL) /*| (1 << DPFLTR_INFO_LEVEL)*/ | DPFLTR_MASK, 513 TRUE); 514 #endif 515 516 Status = IoAllocateDriverObjectExtension( 517 DriverObject, 518 DriverObject, 519 sizeof(I8042_DRIVER_EXTENSION), 520 (PVOID*)&DriverExtension); 521 if (!NT_SUCCESS(Status)) 522 { 523 WARN_(I8042PRT, "IoAllocateDriverObjectExtension() failed with status 0x%08lx\n", Status); 524 return Status; 525 } 526 RtlZeroMemory(DriverExtension, sizeof(I8042_DRIVER_EXTENSION)); 527 KeInitializeSpinLock(&DriverExtension->Port.SpinLock); 528 InitializeListHead(&DriverExtension->DeviceListHead); 529 KeInitializeSpinLock(&DriverExtension->DeviceListLock); 530 531 Status = DuplicateUnicodeString( 532 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE, 533 RegistryPath, 534 &DriverExtension->RegistryPath); 535 if (!NT_SUCCESS(Status)) 536 { 537 WARN_(I8042PRT, "DuplicateUnicodeString() failed with status 0x%08lx\n", Status); 538 return Status; 539 } 540 541 Status = ReadRegistryEntries(RegistryPath, &DriverExtension->Port.Settings); 542 if (!NT_SUCCESS(Status)) 543 { 544 WARN_(I8042PRT, "ReadRegistryEntries() failed with status 0x%08lx\n", Status); 545 return Status; 546 } 547 548 DriverObject->DriverExtension->AddDevice = i8042AddDevice; 549 DriverObject->DriverStartIo = i8042StartIo; 550 551 for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) 552 DriverObject->MajorFunction[i] = IrpStub; 553 554 DriverObject->MajorFunction[IRP_MJ_CREATE] = i8042Create; 555 DriverObject->MajorFunction[IRP_MJ_CLEANUP] = i8042Cleanup; 556 DriverObject->MajorFunction[IRP_MJ_CLOSE] = i8042Close; 557 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = i8042DeviceControl; 558 DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = i8042InternalDeviceControl; 559 DriverObject->MajorFunction[IRP_MJ_POWER] = i8042Power; 560 DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = i8042SystemControl; 561 DriverObject->MajorFunction[IRP_MJ_PNP] = i8042Pnp; 562 563 i8042InitializeHwHacks(); 564 565 return STATUS_SUCCESS; 566 } 567