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/keyboard.c 5 * PURPOSE: Keyboard specific functions 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 <poclass.h> 17 #include <ndk/kdfuncs.h> 18 19 #include <debug.h> 20 21 /* GLOBALS *******************************************************************/ 22 23 static IO_WORKITEM_ROUTINE i8042PowerWorkItem; 24 static KDEFERRED_ROUTINE i8042KbdDpcRoutine; 25 26 /* This structure starts with the same layout as KEYBOARD_INDICATOR_TRANSLATION */ 27 typedef struct _LOCAL_KEYBOARD_INDICATOR_TRANSLATION { 28 USHORT NumberOfIndicatorKeys; 29 INDICATOR_LIST IndicatorList[3]; 30 } LOCAL_KEYBOARD_INDICATOR_TRANSLATION, *PLOCAL_KEYBOARD_INDICATOR_TRANSLATION; 31 32 static LOCAL_KEYBOARD_INDICATOR_TRANSLATION IndicatorTranslation = { 3, { 33 {0x3A, KEYBOARD_CAPS_LOCK_ON}, 34 {0x45, KEYBOARD_NUM_LOCK_ON}, 35 {0x46, KEYBOARD_SCROLL_LOCK_ON}}}; 36 37 /* FUNCTIONS *****************************************************************/ 38 39 /* 40 * These functions are callbacks for filter driver custom interrupt 41 * service routines. 42 */ 43 /*static VOID NTAPI 44 i8042KbdIsrWritePort( 45 IN PVOID Context, 46 IN UCHAR Value) 47 { 48 PI8042_KEYBOARD_EXTENSION DeviceExtension; 49 50 DeviceExtension = (PI8042_KEYBOARD_EXTENSION)Context; 51 52 if (DeviceExtension->KeyboardHook.IsrWritePort) 53 { 54 DeviceExtension->KeyboardHook.IsrWritePort( 55 DeviceExtension->KeyboardHook.CallContext, 56 Value); 57 } 58 else 59 i8042IsrWritePort(Context, Value, 0); 60 }*/ 61 62 static VOID NTAPI 63 i8042KbdQueuePacket( 64 IN PVOID Context) 65 { 66 PI8042_KEYBOARD_EXTENSION DeviceExtension; 67 68 DeviceExtension = (PI8042_KEYBOARD_EXTENSION)Context; 69 70 DeviceExtension->KeyComplete = TRUE; 71 DeviceExtension->KeysInBuffer++; 72 if (DeviceExtension->KeysInBuffer > DeviceExtension->Common.PortDeviceExtension->Settings.KeyboardDataQueueSize) 73 { 74 WARN_(I8042PRT, "Keyboard buffer overflow\n"); 75 DeviceExtension->KeysInBuffer--; 76 } 77 78 TRACE_(I8042PRT, "Irq completes key\n"); 79 KeInsertQueueDpc(&DeviceExtension->DpcKeyboard, NULL, NULL); 80 } 81 82 /* 83 * These functions are callbacks for filter driver custom 84 * initialization routines. 85 */ 86 NTSTATUS NTAPI 87 i8042SynchWritePortKbd( 88 IN PVOID Context, 89 IN UCHAR Value, 90 IN BOOLEAN WaitForAck) 91 { 92 return i8042SynchWritePort( 93 (PPORT_DEVICE_EXTENSION)Context, 94 0, 95 Value, 96 WaitForAck); 97 } 98 99 /* 100 * Process the keyboard internal device requests 101 */ 102 VOID NTAPI 103 i8042KbdStartIo( 104 IN PDEVICE_OBJECT DeviceObject, 105 IN PIRP Irp) 106 { 107 PIO_STACK_LOCATION Stack; 108 PI8042_KEYBOARD_EXTENSION DeviceExtension; 109 PPORT_DEVICE_EXTENSION PortDeviceExtension; 110 111 Stack = IoGetCurrentIrpStackLocation(Irp); 112 DeviceExtension = (PI8042_KEYBOARD_EXTENSION)DeviceObject->DeviceExtension; 113 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension; 114 115 switch (Stack->Parameters.DeviceIoControl.IoControlCode) 116 { 117 case IOCTL_KEYBOARD_SET_INDICATORS: 118 { 119 TRACE_(I8042PRT, "IOCTL_KEYBOARD_SET_INDICATORS\n"); 120 INFO_(I8042PRT, "Leds: {%s%s%s }\n", 121 DeviceExtension->KeyboardIndicators.LedFlags & KEYBOARD_CAPS_LOCK_ON ? " CAPSLOCK" : "", 122 DeviceExtension->KeyboardIndicators.LedFlags & KEYBOARD_NUM_LOCK_ON ? " NUMLOCK" : "", 123 DeviceExtension->KeyboardIndicators.LedFlags & KEYBOARD_SCROLL_LOCK_ON ? " SCROLLLOCK" : ""); 124 125 PortDeviceExtension->PacketBuffer[0] = KBD_CMD_SET_LEDS; 126 PortDeviceExtension->PacketBuffer[1] = 0; 127 if (DeviceExtension->KeyboardIndicators.LedFlags & KEYBOARD_CAPS_LOCK_ON) 128 PortDeviceExtension->PacketBuffer[1] |= KBD_LED_CAPS; 129 130 if (DeviceExtension->KeyboardIndicators.LedFlags & KEYBOARD_NUM_LOCK_ON) 131 PortDeviceExtension->PacketBuffer[1] |= KBD_LED_NUM; 132 133 if (DeviceExtension->KeyboardIndicators.LedFlags & KEYBOARD_SCROLL_LOCK_ON) 134 PortDeviceExtension->PacketBuffer[1] |= KBD_LED_SCROLL; 135 136 i8042StartPacket( 137 PortDeviceExtension, 138 &DeviceExtension->Common, 139 PortDeviceExtension->PacketBuffer, 140 2, 141 Irp); 142 break; 143 } 144 default: 145 { 146 ERR_(I8042PRT, "Unknown ioctl code 0x%lx\n", 147 Stack->Parameters.DeviceIoControl.IoControlCode); 148 ASSERT(FALSE); 149 } 150 } 151 } 152 153 static VOID 154 i8042PacketDpc( 155 IN PPORT_DEVICE_EXTENSION DeviceExtension) 156 { 157 BOOLEAN FinishIrp = FALSE; 158 KIRQL Irql; 159 NTSTATUS Result = STATUS_INTERNAL_ERROR; /* Shouldn't happen */ 160 161 /* If the interrupt happens before this is setup, the key 162 * was already in the buffer. Too bad! */ 163 if (!DeviceExtension->HighestDIRQLInterrupt) 164 return; 165 166 Irql = KeAcquireInterruptSpinLock(DeviceExtension->HighestDIRQLInterrupt); 167 168 if (DeviceExtension->Packet.State == Idle 169 && DeviceExtension->PacketComplete) 170 { 171 FinishIrp = TRUE; 172 Result = DeviceExtension->PacketResult; 173 DeviceExtension->PacketComplete = FALSE; 174 } 175 176 KeReleaseInterruptSpinLock(DeviceExtension->HighestDIRQLInterrupt, Irql); 177 178 if (!FinishIrp) 179 return; 180 181 if (DeviceExtension->CurrentIrp) 182 { 183 DeviceExtension->CurrentIrp->IoStatus.Status = Result; 184 IoCompleteRequest(DeviceExtension->CurrentIrp, IO_NO_INCREMENT); 185 IoStartNextPacket(DeviceExtension->CurrentIrpDevice, FALSE); 186 DeviceExtension->CurrentIrp = NULL; 187 DeviceExtension->CurrentIrpDevice = NULL; 188 } 189 } 190 191 static VOID NTAPI 192 i8042PowerWorkItem( 193 IN PDEVICE_OBJECT DeviceObject, 194 IN PVOID Context) 195 { 196 PI8042_KEYBOARD_EXTENSION DeviceExtension; 197 PIRP WaitingIrp; 198 NTSTATUS Status; 199 200 UNREFERENCED_PARAMETER(DeviceObject); 201 202 __analysis_assume(Context != NULL); 203 DeviceExtension = Context; 204 205 /* See http://blogs.msdn.com/doronh/archive/2006/09/08/746961.aspx */ 206 207 /* Register GUID_DEVICE_SYS_BUTTON interface and report capability */ 208 if (DeviceExtension->NewCaps != DeviceExtension->ReportedCaps) 209 { 210 WaitingIrp = InterlockedExchangePointer((PVOID)&DeviceExtension->PowerIrp, NULL); 211 if (WaitingIrp) 212 { 213 /* Cancel the current power irp, as capability changed */ 214 WaitingIrp->IoStatus.Status = STATUS_UNSUCCESSFUL; 215 WaitingIrp->IoStatus.Information = sizeof(ULONG); 216 IoCompleteRequest(WaitingIrp, IO_NO_INCREMENT); 217 } 218 219 if (DeviceExtension->PowerInterfaceName.MaximumLength == 0) 220 { 221 /* We have never registered this interface ; do it */ 222 Status = IoRegisterDeviceInterface( 223 DeviceExtension->Common.Pdo, 224 &GUID_DEVICE_SYS_BUTTON, 225 NULL, 226 &DeviceExtension->PowerInterfaceName); 227 if (!NT_SUCCESS(Status)) 228 { 229 /* We can't do more yet, ignore the keypress... */ 230 WARN_(I8042PRT, "IoRegisterDeviceInterface(GUID_DEVICE_SYS_BUTTON) failed with status 0x%08lx\n", 231 Status); 232 DeviceExtension->PowerInterfaceName.MaximumLength = 0; 233 return; 234 } 235 } 236 else 237 { 238 /* Disable the interface. Once activated again, capabilities would be asked again */ 239 Status = IoSetDeviceInterfaceState( 240 &DeviceExtension->PowerInterfaceName, 241 FALSE); 242 if (!NT_SUCCESS(Status)) 243 { 244 /* Ignore the key press... */ 245 WARN_(I8042PRT, "Disabling interface %wZ failed with status 0x%08lx\n", 246 &DeviceExtension->PowerInterfaceName, Status); 247 return; 248 } 249 } 250 /* Enable the interface. This leads to receiving a IOCTL_GET_SYS_BUTTON_CAPS, 251 * so we can report new capability */ 252 Status = IoSetDeviceInterfaceState( 253 &DeviceExtension->PowerInterfaceName, 254 TRUE); 255 if (!NT_SUCCESS(Status)) 256 { 257 /* Ignore the key press... */ 258 WARN_(I8042PRT, "Enabling interface %wZ failed with status 0x%08lx\n", 259 &DeviceExtension->PowerInterfaceName, Status); 260 return; 261 } 262 } 263 264 /* Directly complete the IOCTL_GET_SYS_BUTTON_EVENT Irp (if any) */ 265 WaitingIrp = InterlockedExchangePointer((PVOID)&DeviceExtension->PowerIrp, NULL); 266 if (WaitingIrp) 267 { 268 PULONG pEvent = (PULONG)WaitingIrp->AssociatedIrp.SystemBuffer; 269 270 WaitingIrp->IoStatus.Status = STATUS_SUCCESS; 271 WaitingIrp->IoStatus.Information = sizeof(ULONG); 272 *pEvent = InterlockedExchange((PLONG)&DeviceExtension->LastPowerKey, 0); 273 IoCompleteRequest(WaitingIrp, IO_NO_INCREMENT); 274 } 275 } 276 277 /* Return TRUE if it was a power key */ 278 static BOOLEAN 279 HandlePowerKeys( 280 IN PI8042_KEYBOARD_EXTENSION DeviceExtension) 281 { 282 PKEYBOARD_INPUT_DATA InputData; 283 ULONG KeyPress; 284 285 InputData = DeviceExtension->KeyboardBuffer + DeviceExtension->KeysInBuffer - 1; 286 if (!(InputData->Flags & KEY_E0)) 287 return FALSE; 288 289 switch (InputData->MakeCode) 290 { 291 case KEYBOARD_POWER_CODE: 292 KeyPress = SYS_BUTTON_POWER; 293 break; 294 case KEYBOARD_SLEEP_CODE: 295 KeyPress = SYS_BUTTON_SLEEP; 296 break; 297 case KEYBOARD_WAKE_CODE: 298 KeyPress = SYS_BUTTON_WAKE; 299 break; 300 default: 301 return FALSE; 302 } 303 304 if (InputData->Flags & KEY_BREAK) 305 /* We already took care of the key press */ 306 return TRUE; 307 308 /* Our work can only be done at passive level, so use a workitem */ 309 DeviceExtension->NewCaps |= KeyPress; 310 InterlockedExchange((PLONG)&DeviceExtension->LastPowerKey, KeyPress); 311 IoQueueWorkItem( 312 DeviceExtension->PowerWorkItem, 313 &i8042PowerWorkItem, 314 DelayedWorkQueue, 315 DeviceExtension); 316 return TRUE; 317 } 318 319 static VOID NTAPI 320 i8042KbdDpcRoutine( 321 IN PKDPC Dpc, 322 IN PVOID DeferredContext, 323 IN PVOID SystemArgument1, 324 IN PVOID SystemArgument2) 325 { 326 PI8042_KEYBOARD_EXTENSION DeviceExtension; 327 PPORT_DEVICE_EXTENSION PortDeviceExtension; 328 ULONG KeysTransferred = 0; 329 ULONG KeysInBufferCopy; 330 KIRQL Irql; 331 332 UNREFERENCED_PARAMETER(Dpc); 333 UNREFERENCED_PARAMETER(SystemArgument1); 334 UNREFERENCED_PARAMETER(SystemArgument2); 335 336 __analysis_assume(DeferredContext != NULL); 337 DeviceExtension = DeferredContext; 338 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension; 339 340 if (HandlePowerKeys(DeviceExtension)) 341 { 342 DeviceExtension->KeyComplete = FALSE; 343 return; 344 } 345 346 i8042PacketDpc(PortDeviceExtension); 347 if (!DeviceExtension->KeyComplete) 348 return; 349 /* We got the interrupt as it was being enabled, too bad */ 350 if (!PortDeviceExtension->HighestDIRQLInterrupt) 351 return; 352 353 Irql = KeAcquireInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt); 354 355 DeviceExtension->KeyComplete = FALSE; 356 KeysInBufferCopy = DeviceExtension->KeysInBuffer; 357 358 KeReleaseInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt, Irql); 359 360 TRACE_(I8042PRT, "Send a key\n"); 361 362 if (!DeviceExtension->KeyboardData.ClassService) 363 return; 364 365 INFO_(I8042PRT, "Sending %lu key(s)\n", KeysInBufferCopy); 366 (*(PSERVICE_CALLBACK_ROUTINE)DeviceExtension->KeyboardData.ClassService)( 367 DeviceExtension->KeyboardData.ClassDeviceObject, 368 DeviceExtension->KeyboardBuffer, 369 DeviceExtension->KeyboardBuffer + KeysInBufferCopy, 370 &KeysTransferred); 371 372 /* Validate that the callback didn't change the Irql. */ 373 ASSERT(KeGetCurrentIrql() == Irql); 374 375 Irql = KeAcquireInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt); 376 DeviceExtension->KeysInBuffer -= KeysTransferred; 377 KeReleaseInterruptSpinLock(PortDeviceExtension->HighestDIRQLInterrupt, Irql); 378 } 379 380 /* 381 * Runs the keyboard IOCTL dispatch. 382 */ 383 NTSTATUS NTAPI 384 i8042KbdDeviceControl( 385 IN PDEVICE_OBJECT DeviceObject, 386 IN PIRP Irp) 387 { 388 PIO_STACK_LOCATION Stack; 389 PI8042_KEYBOARD_EXTENSION DeviceExtension; 390 NTSTATUS Status; 391 392 Stack = IoGetCurrentIrpStackLocation(Irp); 393 Irp->IoStatus.Information = 0; 394 DeviceExtension = (PI8042_KEYBOARD_EXTENSION)DeviceObject->DeviceExtension; 395 396 switch (Stack->Parameters.DeviceIoControl.IoControlCode) 397 { 398 case IOCTL_GET_SYS_BUTTON_CAPS: 399 { 400 /* Part of GUID_DEVICE_SYS_BUTTON interface */ 401 PULONG pCaps; 402 TRACE_(I8042PRT, "IOCTL_GET_SYS_BUTTON_CAPS\n"); 403 404 if (Stack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(ULONG)) 405 Status = STATUS_INVALID_PARAMETER; 406 else 407 { 408 pCaps = (PULONG)Irp->AssociatedIrp.SystemBuffer; 409 *pCaps = DeviceExtension->NewCaps; 410 DeviceExtension->ReportedCaps = DeviceExtension->NewCaps; 411 Irp->IoStatus.Information = sizeof(ULONG); 412 Status = STATUS_SUCCESS; 413 } 414 break; 415 } 416 case IOCTL_GET_SYS_BUTTON_EVENT: 417 { 418 /* Part of GUID_DEVICE_SYS_BUTTON interface */ 419 PIRP WaitingIrp; 420 TRACE_(I8042PRT, "IOCTL_GET_SYS_BUTTON_EVENT\n"); 421 422 if (Stack->Parameters.DeviceIoControl.OutputBufferLength != sizeof(ULONG)) 423 Status = STATUS_INVALID_PARAMETER; 424 else 425 { 426 WaitingIrp = InterlockedCompareExchangePointer( 427 (PVOID)&DeviceExtension->PowerIrp, 428 Irp, 429 NULL); 430 /* Check if an Irp is already pending */ 431 if (WaitingIrp) 432 { 433 /* Unable to have a 2nd pending IRP for this IOCTL */ 434 WARN_(I8042PRT, "Unable to pend a second IRP for IOCTL_GET_SYS_BUTTON_EVENT\n"); 435 Status = STATUS_INVALID_PARAMETER; 436 Irp->IoStatus.Status = Status; 437 IoCompleteRequest(Irp, IO_NO_INCREMENT); 438 } 439 else 440 { 441 ULONG PowerKey; 442 PowerKey = InterlockedExchange((PLONG)&DeviceExtension->LastPowerKey, 0); 443 if (PowerKey != 0) 444 { 445 (VOID)InterlockedCompareExchangePointer((PVOID)&DeviceExtension->PowerIrp, NULL, Irp); 446 *(PULONG)Irp->AssociatedIrp.SystemBuffer = PowerKey; 447 Status = STATUS_SUCCESS; 448 Irp->IoStatus.Status = Status; 449 Irp->IoStatus.Information = sizeof(ULONG); 450 IoCompleteRequest(Irp, IO_NO_INCREMENT); 451 } 452 else 453 { 454 TRACE_(I8042PRT, "Pending IOCTL_GET_SYS_BUTTON_EVENT\n"); 455 Status = STATUS_PENDING; 456 Irp->IoStatus.Status = Status; 457 IoMarkIrpPending(Irp); 458 } 459 } 460 return Status; 461 } 462 break; 463 } 464 default: 465 { 466 ERR_(I8042PRT, "IRP_MJ_DEVICE_CONTROL / unknown ioctl code 0x%lx\n", 467 Stack->Parameters.DeviceIoControl.IoControlCode); 468 return ForwardIrpAndForget(DeviceObject, Irp); 469 } 470 } 471 472 if (Status != STATUS_PENDING) 473 { 474 Irp->IoStatus.Status = Status; 475 IoCompleteRequest(Irp, IO_NO_INCREMENT); 476 } 477 478 return Status; 479 } 480 481 VOID 482 NTAPI 483 i8042InitializeKeyboardAttributes( 484 PI8042_KEYBOARD_EXTENSION DeviceExtension) 485 { 486 PPORT_DEVICE_EXTENSION PortDeviceExtension; 487 PI8042_SETTINGS Settings; 488 PKEYBOARD_ATTRIBUTES KeyboardAttributes; 489 490 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension; 491 Settings = &PortDeviceExtension->Settings; 492 493 KeyboardAttributes = &DeviceExtension->KeyboardAttributes; 494 495 KeyboardAttributes->KeyboardIdentifier.Type = (UCHAR)Settings->OverrideKeyboardType; 496 KeyboardAttributes->KeyboardIdentifier.Subtype = (UCHAR)Settings->OverrideKeyboardSubtype; 497 KeyboardAttributes->NumberOfFunctionKeys = 4; 498 KeyboardAttributes->NumberOfIndicators = 3; 499 KeyboardAttributes->NumberOfKeysTotal = 101; 500 KeyboardAttributes->InputDataQueueLength = Settings->KeyboardDataQueueSize; 501 KeyboardAttributes->KeyRepeatMinimum.UnitId = 0; 502 KeyboardAttributes->KeyRepeatMinimum.Rate = (USHORT)Settings->SampleRate; 503 KeyboardAttributes->KeyRepeatMinimum.Delay = 0; 504 KeyboardAttributes->KeyRepeatMinimum.UnitId = 0; 505 KeyboardAttributes->KeyRepeatMinimum.Rate = (USHORT)Settings->SampleRate; 506 KeyboardAttributes->KeyRepeatMinimum.Delay = 0; 507 } 508 509 /* 510 * Runs the keyboard IOCTL_INTERNAL dispatch. 511 */ 512 NTSTATUS NTAPI 513 i8042KbdInternalDeviceControl( 514 IN PDEVICE_OBJECT DeviceObject, 515 IN PIRP Irp) 516 { 517 PIO_STACK_LOCATION Stack; 518 PI8042_KEYBOARD_EXTENSION DeviceExtension; 519 NTSTATUS Status; 520 521 Stack = IoGetCurrentIrpStackLocation(Irp); 522 Irp->IoStatus.Information = 0; 523 DeviceExtension = (PI8042_KEYBOARD_EXTENSION)DeviceObject->DeviceExtension; 524 525 switch (Stack->Parameters.DeviceIoControl.IoControlCode) 526 { 527 case IOCTL_INTERNAL_KEYBOARD_CONNECT: 528 { 529 SIZE_T Size; 530 PIO_WORKITEM WorkItem = NULL; 531 PI8042_HOOK_WORKITEM WorkItemData = NULL; 532 533 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_KEYBOARD_CONNECT\n"); 534 if (Stack->Parameters.DeviceIoControl.InputBufferLength != sizeof(CONNECT_DATA)) 535 { 536 Status = STATUS_INVALID_PARAMETER; 537 goto cleanup; 538 } 539 540 DeviceExtension->KeyboardData = 541 *((PCONNECT_DATA)Stack->Parameters.DeviceIoControl.Type3InputBuffer); 542 543 /* Send IOCTL_INTERNAL_I8042_HOOK_KEYBOARD to device stack */ 544 WorkItem = IoAllocateWorkItem(DeviceObject); 545 if (!WorkItem) 546 { 547 WARN_(I8042PRT, "IoAllocateWorkItem() failed\n"); 548 Status = STATUS_INSUFFICIENT_RESOURCES; 549 goto cleanup; 550 } 551 WorkItemData = ExAllocatePoolWithTag( 552 NonPagedPool, 553 sizeof(I8042_HOOK_WORKITEM), 554 I8042PRT_TAG); 555 if (!WorkItemData) 556 { 557 WARN_(I8042PRT, "ExAllocatePoolWithTag() failed\n"); 558 Status = STATUS_NO_MEMORY; 559 goto cleanup; 560 } 561 WorkItemData->WorkItem = WorkItem; 562 WorkItemData->Irp = Irp; 563 564 /* Initialize extension */ 565 DeviceExtension->Common.Type = Keyboard; 566 Size = DeviceExtension->Common.PortDeviceExtension->Settings.KeyboardDataQueueSize * sizeof(KEYBOARD_INPUT_DATA); 567 DeviceExtension->KeyboardBuffer = ExAllocatePoolWithTag( 568 NonPagedPool, 569 Size, 570 I8042PRT_TAG); 571 if (!DeviceExtension->KeyboardBuffer) 572 { 573 WARN_(I8042PRT, "ExAllocatePoolWithTag() failed\n"); 574 Status = STATUS_NO_MEMORY; 575 goto cleanup; 576 } 577 RtlZeroMemory(DeviceExtension->KeyboardBuffer, Size); 578 KeInitializeDpc( 579 &DeviceExtension->DpcKeyboard, 580 i8042KbdDpcRoutine, 581 DeviceExtension); 582 DeviceExtension->PowerWorkItem = IoAllocateWorkItem(DeviceObject); 583 if (!DeviceExtension->PowerWorkItem) 584 { 585 WARN_(I8042PRT, "IoAllocateWorkItem() failed\n"); 586 Status = STATUS_INSUFFICIENT_RESOURCES; 587 goto cleanup; 588 } 589 DeviceExtension->DebugWorkItem = IoAllocateWorkItem(DeviceObject); 590 if (!DeviceExtension->DebugWorkItem) 591 { 592 WARN_(I8042PRT, "IoAllocateWorkItem() failed\n"); 593 Status = STATUS_INSUFFICIENT_RESOURCES; 594 goto cleanup; 595 } 596 DeviceExtension->Common.PortDeviceExtension->KeyboardExtension = DeviceExtension; 597 DeviceExtension->Common.PortDeviceExtension->Flags |= KEYBOARD_CONNECTED; 598 599 i8042InitializeKeyboardAttributes(DeviceExtension); 600 601 IoMarkIrpPending(Irp); 602 /* FIXME: DeviceExtension->KeyboardHook.IsrWritePort = ; */ 603 DeviceExtension->KeyboardHook.QueueKeyboardPacket = i8042KbdQueuePacket; 604 DeviceExtension->KeyboardHook.CallContext = DeviceExtension; 605 IoQueueWorkItem(WorkItem, 606 i8042SendHookWorkItem, 607 DelayedWorkQueue, 608 WorkItemData); 609 Status = STATUS_PENDING; 610 break; 611 612 cleanup: 613 if (DeviceExtension->KeyboardBuffer) 614 ExFreePoolWithTag(DeviceExtension->KeyboardBuffer, I8042PRT_TAG); 615 if (DeviceExtension->PowerWorkItem) 616 IoFreeWorkItem(DeviceExtension->PowerWorkItem); 617 if (DeviceExtension->DebugWorkItem) 618 IoFreeWorkItem(DeviceExtension->DebugWorkItem); 619 if (WorkItem) 620 IoFreeWorkItem(WorkItem); 621 if (WorkItemData) 622 ExFreePoolWithTag(WorkItemData, I8042PRT_TAG); 623 break; 624 } 625 case IOCTL_INTERNAL_KEYBOARD_DISCONNECT: 626 { 627 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_KEYBOARD_DISCONNECT\n"); 628 /* MSDN says that operation is to implemented. 629 * To implement it, we just have to do: 630 * DeviceExtension->KeyboardData.ClassService = NULL; 631 */ 632 Status = STATUS_NOT_IMPLEMENTED; 633 break; 634 } 635 case IOCTL_INTERNAL_I8042_HOOK_KEYBOARD: 636 { 637 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_I8042_HOOK_KEYBOARD\n"); 638 /* Nothing to do here */ 639 Status = STATUS_SUCCESS; 640 break; 641 } 642 case IOCTL_KEYBOARD_QUERY_ATTRIBUTES: 643 { 644 PKEYBOARD_ATTRIBUTES KeyboardAttributes; 645 646 /* FIXME: KeyboardAttributes are not initialized anywhere */ 647 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_KEYBOARD_QUERY_ATTRIBUTES\n"); 648 if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KEYBOARD_ATTRIBUTES)) 649 { 650 Status = STATUS_BUFFER_TOO_SMALL; 651 break; 652 } 653 654 KeyboardAttributes = Irp->AssociatedIrp.SystemBuffer; 655 *KeyboardAttributes = DeviceExtension->KeyboardAttributes; 656 657 Irp->IoStatus.Information = sizeof(KEYBOARD_ATTRIBUTES); 658 Status = STATUS_SUCCESS; 659 break; 660 } 661 case IOCTL_KEYBOARD_QUERY_TYPEMATIC: 662 { 663 DPRINT1("IOCTL_KEYBOARD_QUERY_TYPEMATIC not implemented\n"); 664 Status = STATUS_NOT_IMPLEMENTED; 665 break; 666 } 667 case IOCTL_KEYBOARD_SET_TYPEMATIC: 668 { 669 DPRINT1("IOCTL_KEYBOARD_SET_TYPEMATIC not implemented\n"); 670 Status = STATUS_NOT_IMPLEMENTED; 671 break; 672 } 673 case IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION: 674 { 675 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION\n"); 676 677 /* We should check the UnitID, but it's kind of pointless as 678 * all keyboards are supposed to have the same one 679 */ 680 if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION)) 681 { 682 Status = STATUS_BUFFER_TOO_SMALL; 683 } 684 else 685 { 686 RtlCopyMemory( 687 Irp->AssociatedIrp.SystemBuffer, 688 &IndicatorTranslation, 689 sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION)); 690 Irp->IoStatus.Information = sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION); 691 Status = STATUS_SUCCESS; 692 } 693 break; 694 } 695 case IOCTL_KEYBOARD_QUERY_INDICATORS: 696 { 697 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_KEYBOARD_QUERY_INDICATORS\n"); 698 699 if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KEYBOARD_INDICATOR_PARAMETERS)) 700 { 701 Status = STATUS_BUFFER_TOO_SMALL; 702 } 703 else 704 { 705 RtlCopyMemory( 706 Irp->AssociatedIrp.SystemBuffer, 707 &DeviceExtension->KeyboardIndicators, 708 sizeof(KEYBOARD_INDICATOR_PARAMETERS)); 709 Irp->IoStatus.Information = sizeof(KEYBOARD_INDICATOR_PARAMETERS); 710 Status = STATUS_SUCCESS; 711 } 712 break; 713 } 714 case IOCTL_KEYBOARD_SET_INDICATORS: 715 { 716 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_KEYBOARD_SET_INDICATORS\n"); 717 718 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(KEYBOARD_INDICATOR_PARAMETERS)) 719 { 720 Status = STATUS_BUFFER_TOO_SMALL; 721 } 722 else 723 { 724 RtlCopyMemory( 725 &DeviceExtension->KeyboardIndicators, 726 Irp->AssociatedIrp.SystemBuffer, 727 sizeof(KEYBOARD_INDICATOR_PARAMETERS)); 728 Status = STATUS_PENDING; 729 IoMarkIrpPending(Irp); 730 IoStartPacket(DeviceObject, Irp, NULL, NULL); 731 } 732 break; 733 } 734 default: 735 { 736 ERR_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / unknown ioctl code 0x%lx\n", 737 Stack->Parameters.DeviceIoControl.IoControlCode); 738 ASSERT(FALSE); 739 return ForwardIrpAndForget(DeviceObject, Irp); 740 } 741 } 742 743 if (Status != STATUS_PENDING) 744 { 745 Irp->IoStatus.Status = Status; 746 IoCompleteRequest(Irp, IO_NO_INCREMENT); 747 } 748 return Status; 749 } 750 751 /* 752 * Call the customization hook. The ToReturn parameter is about whether 753 * we should go on with the interrupt. The return value is what 754 * we should return (indicating to the system whether someone else 755 * should try to handle the interrupt) 756 */ 757 static BOOLEAN 758 i8042KbdCallIsrHook( 759 IN PI8042_KEYBOARD_EXTENSION DeviceExtension, 760 IN UCHAR Status, 761 IN UCHAR Input, 762 OUT PBOOLEAN ToReturn) 763 { 764 BOOLEAN HookReturn, HookContinue; 765 766 HookContinue = FALSE; 767 768 if (DeviceExtension->KeyboardHook.IsrRoutine) 769 { 770 HookReturn = DeviceExtension->KeyboardHook.IsrRoutine( 771 DeviceExtension->KeyboardHook.Context, 772 DeviceExtension->KeyboardBuffer + DeviceExtension->KeysInBuffer, 773 &DeviceExtension->Common.PortDeviceExtension->Packet, 774 Status, 775 &Input, 776 &HookContinue, 777 &DeviceExtension->KeyboardScanState); 778 779 if (!HookContinue) 780 { 781 *ToReturn = HookReturn; 782 return TRUE; 783 } 784 } 785 return FALSE; 786 } 787 788 BOOLEAN NTAPI 789 i8042KbdInterruptService( 790 IN PKINTERRUPT Interrupt, 791 PVOID Context) 792 { 793 PI8042_KEYBOARD_EXTENSION DeviceExtension; 794 PPORT_DEVICE_EXTENSION PortDeviceExtension; 795 PKEYBOARD_INPUT_DATA InputData; 796 ULONG Counter; 797 UCHAR PortStatus = 0, Output = 0; 798 BOOLEAN ToReturn = FALSE; 799 NTSTATUS Status; 800 801 UNREFERENCED_PARAMETER(Interrupt); 802 803 __analysis_assume(Context != NULL); 804 DeviceExtension = Context; 805 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension; 806 InputData = DeviceExtension->KeyboardBuffer + DeviceExtension->KeysInBuffer; 807 Counter = PortDeviceExtension->Settings.PollStatusIterations; 808 809 while (Counter) 810 { 811 Status = i8042ReadStatus(PortDeviceExtension, &PortStatus); 812 if (!NT_SUCCESS(Status)) 813 { 814 WARN_(I8042PRT, "i8042ReadStatus() failed with status 0x%08lx\n", Status); 815 return FALSE; 816 } 817 Status = i8042ReadKeyboardData(PortDeviceExtension, &Output); 818 if (NT_SUCCESS(Status)) 819 break; 820 KeStallExecutionProcessor(1); 821 Counter--; 822 } 823 if (Counter == 0) 824 { 825 WARN_(I8042PRT, "Spurious i8042 keyboard interrupt\n"); 826 return FALSE; 827 } 828 829 INFO_(I8042PRT, "Got: 0x%02x\n", Output); 830 831 if (PortDeviceExtension->Settings.CrashOnCtrlScroll) 832 { 833 /* Test for CTRL + SCROLL LOCK twice */ 834 static const UCHAR ScanCodes[] = { 0x1d, 0x46, 0xc6, 0x46, 0 }; 835 836 if (Output == ScanCodes[DeviceExtension->ComboPosition]) 837 { 838 DeviceExtension->ComboPosition++; 839 if (ScanCodes[DeviceExtension->ComboPosition] == 0) 840 KeBugCheck(MANUALLY_INITIATED_CRASH); 841 } 842 else if (Output == 0xfa) 843 { 844 /* Ignore ACK */ 845 } 846 else if (Output == ScanCodes[0]) 847 DeviceExtension->ComboPosition = 1; 848 else 849 DeviceExtension->ComboPosition = 0; 850 851 /* Test for TAB + key combination */ 852 if (InputData->MakeCode == 0x0F) 853 DeviceExtension->TabPressed = !(InputData->Flags & KEY_BREAK); 854 else if (DeviceExtension->TabPressed) 855 { 856 DeviceExtension->TabPressed = FALSE; 857 858 /* Check which action to do */ 859 if (InputData->MakeCode == 0x25) 860 { 861 /* k - Breakpoint */ 862 DbgBreakPointWithStatus(DBG_STATUS_SYSRQ); 863 } 864 else if (InputData->MakeCode == 0x30) 865 { 866 /* b - Bugcheck */ 867 KeBugCheck(MANUALLY_INITIATED_CRASH); 868 } 869 #if defined(KDBG) 870 else 871 { 872 /* Send request to the kernel debugger. 873 * Unknown requests will be ignored. */ 874 KdSystemDebugControl(' soR', 875 (PVOID)(ULONG_PTR)InputData->MakeCode, 876 0, 877 NULL, 878 0, 879 NULL, 880 KernelMode); 881 } 882 #endif 883 } 884 } 885 886 if (i8042KbdCallIsrHook(DeviceExtension, PortStatus, Output, &ToReturn)) 887 return ToReturn; 888 889 if (i8042PacketIsr(PortDeviceExtension, Output)) 890 { 891 if (PortDeviceExtension->PacketComplete) 892 { 893 TRACE_(I8042PRT, "Packet complete\n"); 894 KeInsertQueueDpc(&DeviceExtension->DpcKeyboard, NULL, NULL); 895 } 896 TRACE_(I8042PRT, "Irq eaten by packet\n"); 897 return TRUE; 898 } 899 900 TRACE_(I8042PRT, "Irq is keyboard input\n"); 901 902 if (DeviceExtension->KeyboardScanState == Normal) 903 { 904 switch (Output) 905 { 906 case 0xe0: 907 DeviceExtension->KeyboardScanState = GotE0; 908 return TRUE; 909 case 0xe1: 910 DeviceExtension->KeyboardScanState = GotE1; 911 return TRUE; 912 default: 913 break; 914 } 915 } 916 917 /* Update InputData */ 918 InputData->Flags = 0; 919 switch (DeviceExtension->KeyboardScanState) 920 { 921 case GotE0: 922 InputData->Flags |= KEY_E0; 923 break; 924 case GotE1: 925 InputData->Flags |= KEY_E1; 926 break; 927 default: 928 break; 929 } 930 DeviceExtension->KeyboardScanState = Normal; 931 if (Output & 0x80) 932 InputData->Flags |= KEY_BREAK; 933 else 934 InputData->Flags |= KEY_MAKE; 935 InputData->MakeCode = Output & 0x7f; 936 InputData->Reserved = 0; 937 938 DeviceExtension->KeyboardHook.QueueKeyboardPacket(DeviceExtension->KeyboardHook.CallContext); 939 940 return TRUE; 941 } 942