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 ASSERT(FALSE); 469 return ForwardIrpAndForget(DeviceObject, Irp); 470 } 471 } 472 473 if (Status != STATUS_PENDING) 474 { 475 Irp->IoStatus.Status = Status; 476 IoCompleteRequest(Irp, IO_NO_INCREMENT); 477 } 478 479 return Status; 480 } 481 482 VOID 483 NTAPI 484 i8042InitializeKeyboardAttributes( 485 PI8042_KEYBOARD_EXTENSION DeviceExtension) 486 { 487 PPORT_DEVICE_EXTENSION PortDeviceExtension; 488 PI8042_SETTINGS Settings; 489 PKEYBOARD_ATTRIBUTES KeyboardAttributes; 490 491 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension; 492 Settings = &PortDeviceExtension->Settings; 493 494 KeyboardAttributes = &DeviceExtension->KeyboardAttributes; 495 496 KeyboardAttributes->KeyboardIdentifier.Type = (UCHAR)Settings->OverrideKeyboardType; 497 KeyboardAttributes->KeyboardIdentifier.Subtype = (UCHAR)Settings->OverrideKeyboardSubtype; 498 KeyboardAttributes->NumberOfFunctionKeys = 4; 499 KeyboardAttributes->NumberOfIndicators = 3; 500 KeyboardAttributes->NumberOfKeysTotal = 101; 501 KeyboardAttributes->InputDataQueueLength = Settings->KeyboardDataQueueSize; 502 KeyboardAttributes->KeyRepeatMinimum.UnitId = 0; 503 KeyboardAttributes->KeyRepeatMinimum.Rate = (USHORT)Settings->SampleRate; 504 KeyboardAttributes->KeyRepeatMinimum.Delay = 0; 505 KeyboardAttributes->KeyRepeatMinimum.UnitId = 0; 506 KeyboardAttributes->KeyRepeatMinimum.Rate = (USHORT)Settings->SampleRate; 507 KeyboardAttributes->KeyRepeatMinimum.Delay = 0; 508 } 509 510 /* 511 * Runs the keyboard IOCTL_INTERNAL dispatch. 512 */ 513 NTSTATUS NTAPI 514 i8042KbdInternalDeviceControl( 515 IN PDEVICE_OBJECT DeviceObject, 516 IN PIRP Irp) 517 { 518 PIO_STACK_LOCATION Stack; 519 PI8042_KEYBOARD_EXTENSION DeviceExtension; 520 NTSTATUS Status; 521 522 Stack = IoGetCurrentIrpStackLocation(Irp); 523 Irp->IoStatus.Information = 0; 524 DeviceExtension = (PI8042_KEYBOARD_EXTENSION)DeviceObject->DeviceExtension; 525 526 switch (Stack->Parameters.DeviceIoControl.IoControlCode) 527 { 528 case IOCTL_INTERNAL_KEYBOARD_CONNECT: 529 { 530 SIZE_T Size; 531 PIO_WORKITEM WorkItem = NULL; 532 PI8042_HOOK_WORKITEM WorkItemData = NULL; 533 534 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_KEYBOARD_CONNECT\n"); 535 if (Stack->Parameters.DeviceIoControl.InputBufferLength != sizeof(CONNECT_DATA)) 536 { 537 Status = STATUS_INVALID_PARAMETER; 538 goto cleanup; 539 } 540 541 DeviceExtension->KeyboardData = 542 *((PCONNECT_DATA)Stack->Parameters.DeviceIoControl.Type3InputBuffer); 543 544 /* Send IOCTL_INTERNAL_I8042_HOOK_KEYBOARD to device stack */ 545 WorkItem = IoAllocateWorkItem(DeviceObject); 546 if (!WorkItem) 547 { 548 WARN_(I8042PRT, "IoAllocateWorkItem() failed\n"); 549 Status = STATUS_INSUFFICIENT_RESOURCES; 550 goto cleanup; 551 } 552 WorkItemData = ExAllocatePoolWithTag( 553 NonPagedPool, 554 sizeof(I8042_HOOK_WORKITEM), 555 I8042PRT_TAG); 556 if (!WorkItemData) 557 { 558 WARN_(I8042PRT, "ExAllocatePoolWithTag() failed\n"); 559 Status = STATUS_NO_MEMORY; 560 goto cleanup; 561 } 562 WorkItemData->WorkItem = WorkItem; 563 WorkItemData->Irp = Irp; 564 565 /* Initialize extension */ 566 DeviceExtension->Common.Type = Keyboard; 567 Size = DeviceExtension->Common.PortDeviceExtension->Settings.KeyboardDataQueueSize * sizeof(KEYBOARD_INPUT_DATA); 568 DeviceExtension->KeyboardBuffer = ExAllocatePoolWithTag( 569 NonPagedPool, 570 Size, 571 I8042PRT_TAG); 572 if (!DeviceExtension->KeyboardBuffer) 573 { 574 WARN_(I8042PRT, "ExAllocatePoolWithTag() failed\n"); 575 Status = STATUS_NO_MEMORY; 576 goto cleanup; 577 } 578 RtlZeroMemory(DeviceExtension->KeyboardBuffer, Size); 579 KeInitializeDpc( 580 &DeviceExtension->DpcKeyboard, 581 i8042KbdDpcRoutine, 582 DeviceExtension); 583 DeviceExtension->PowerWorkItem = IoAllocateWorkItem(DeviceObject); 584 if (!DeviceExtension->PowerWorkItem) 585 { 586 WARN_(I8042PRT, "IoAllocateWorkItem() failed\n"); 587 Status = STATUS_INSUFFICIENT_RESOURCES; 588 goto cleanup; 589 } 590 DeviceExtension->DebugWorkItem = IoAllocateWorkItem(DeviceObject); 591 if (!DeviceExtension->DebugWorkItem) 592 { 593 WARN_(I8042PRT, "IoAllocateWorkItem() failed\n"); 594 Status = STATUS_INSUFFICIENT_RESOURCES; 595 goto cleanup; 596 } 597 DeviceExtension->Common.PortDeviceExtension->KeyboardExtension = DeviceExtension; 598 DeviceExtension->Common.PortDeviceExtension->Flags |= KEYBOARD_CONNECTED; 599 600 i8042InitializeKeyboardAttributes(DeviceExtension); 601 602 IoMarkIrpPending(Irp); 603 /* FIXME: DeviceExtension->KeyboardHook.IsrWritePort = ; */ 604 DeviceExtension->KeyboardHook.QueueKeyboardPacket = i8042KbdQueuePacket; 605 DeviceExtension->KeyboardHook.CallContext = DeviceExtension; 606 IoQueueWorkItem(WorkItem, 607 i8042SendHookWorkItem, 608 DelayedWorkQueue, 609 WorkItemData); 610 Status = STATUS_PENDING; 611 break; 612 613 cleanup: 614 if (DeviceExtension->KeyboardBuffer) 615 ExFreePoolWithTag(DeviceExtension->KeyboardBuffer, I8042PRT_TAG); 616 if (DeviceExtension->PowerWorkItem) 617 IoFreeWorkItem(DeviceExtension->PowerWorkItem); 618 if (DeviceExtension->DebugWorkItem) 619 IoFreeWorkItem(DeviceExtension->DebugWorkItem); 620 if (WorkItem) 621 IoFreeWorkItem(WorkItem); 622 if (WorkItemData) 623 ExFreePoolWithTag(WorkItemData, I8042PRT_TAG); 624 break; 625 } 626 case IOCTL_INTERNAL_KEYBOARD_DISCONNECT: 627 { 628 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_KEYBOARD_DISCONNECT\n"); 629 /* MSDN says that operation is to implemented. 630 * To implement it, we just have to do: 631 * DeviceExtension->KeyboardData.ClassService = NULL; 632 */ 633 Status = STATUS_NOT_IMPLEMENTED; 634 break; 635 } 636 case IOCTL_INTERNAL_I8042_HOOK_KEYBOARD: 637 { 638 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_INTERNAL_I8042_HOOK_KEYBOARD\n"); 639 /* Nothing to do here */ 640 Status = STATUS_SUCCESS; 641 break; 642 } 643 case IOCTL_KEYBOARD_QUERY_ATTRIBUTES: 644 { 645 PKEYBOARD_ATTRIBUTES KeyboardAttributes; 646 647 /* FIXME: KeyboardAttributes are not initialized anywhere */ 648 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_KEYBOARD_QUERY_ATTRIBUTES\n"); 649 if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KEYBOARD_ATTRIBUTES)) 650 { 651 Status = STATUS_BUFFER_TOO_SMALL; 652 break; 653 } 654 655 KeyboardAttributes = Irp->AssociatedIrp.SystemBuffer; 656 *KeyboardAttributes = DeviceExtension->KeyboardAttributes; 657 658 Irp->IoStatus.Information = sizeof(KEYBOARD_ATTRIBUTES); 659 Status = STATUS_SUCCESS; 660 break; 661 } 662 case IOCTL_KEYBOARD_QUERY_TYPEMATIC: 663 { 664 DPRINT1("IOCTL_KEYBOARD_QUERY_TYPEMATIC not implemented\n"); 665 Status = STATUS_NOT_IMPLEMENTED; 666 break; 667 } 668 case IOCTL_KEYBOARD_SET_TYPEMATIC: 669 { 670 DPRINT1("IOCTL_KEYBOARD_SET_TYPEMATIC not implemented\n"); 671 Status = STATUS_NOT_IMPLEMENTED; 672 break; 673 } 674 case IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION: 675 { 676 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_KEYBOARD_QUERY_INDICATOR_TRANSLATION\n"); 677 678 /* We should check the UnitID, but it's kind of pointless as 679 * all keyboards are supposed to have the same one 680 */ 681 if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION)) 682 { 683 Status = STATUS_BUFFER_TOO_SMALL; 684 } 685 else 686 { 687 RtlCopyMemory( 688 Irp->AssociatedIrp.SystemBuffer, 689 &IndicatorTranslation, 690 sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION)); 691 Irp->IoStatus.Information = sizeof(LOCAL_KEYBOARD_INDICATOR_TRANSLATION); 692 Status = STATUS_SUCCESS; 693 } 694 break; 695 } 696 case IOCTL_KEYBOARD_QUERY_INDICATORS: 697 { 698 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_KEYBOARD_QUERY_INDICATORS\n"); 699 700 if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KEYBOARD_INDICATOR_PARAMETERS)) 701 { 702 Status = STATUS_BUFFER_TOO_SMALL; 703 } 704 else 705 { 706 RtlCopyMemory( 707 Irp->AssociatedIrp.SystemBuffer, 708 &DeviceExtension->KeyboardIndicators, 709 sizeof(KEYBOARD_INDICATOR_PARAMETERS)); 710 Irp->IoStatus.Information = sizeof(KEYBOARD_INDICATOR_PARAMETERS); 711 Status = STATUS_SUCCESS; 712 } 713 break; 714 } 715 case IOCTL_KEYBOARD_SET_INDICATORS: 716 { 717 TRACE_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / IOCTL_KEYBOARD_SET_INDICATORS\n"); 718 719 if (Stack->Parameters.DeviceIoControl.InputBufferLength < sizeof(KEYBOARD_INDICATOR_PARAMETERS)) 720 { 721 Status = STATUS_BUFFER_TOO_SMALL; 722 } 723 else 724 { 725 RtlCopyMemory( 726 &DeviceExtension->KeyboardIndicators, 727 Irp->AssociatedIrp.SystemBuffer, 728 sizeof(KEYBOARD_INDICATOR_PARAMETERS)); 729 Status = STATUS_PENDING; 730 IoMarkIrpPending(Irp); 731 IoStartPacket(DeviceObject, Irp, NULL, NULL); 732 } 733 break; 734 } 735 default: 736 { 737 ERR_(I8042PRT, "IRP_MJ_INTERNAL_DEVICE_CONTROL / unknown ioctl code 0x%lx\n", 738 Stack->Parameters.DeviceIoControl.IoControlCode); 739 ASSERT(FALSE); 740 return ForwardIrpAndForget(DeviceObject, Irp); 741 } 742 } 743 744 if (Status != STATUS_PENDING) 745 { 746 Irp->IoStatus.Status = Status; 747 IoCompleteRequest(Irp, IO_NO_INCREMENT); 748 } 749 return Status; 750 } 751 752 /* 753 * Call the customization hook. The ToReturn parameter is about whether 754 * we should go on with the interrupt. The return value is what 755 * we should return (indicating to the system whether someone else 756 * should try to handle the interrupt) 757 */ 758 static BOOLEAN 759 i8042KbdCallIsrHook( 760 IN PI8042_KEYBOARD_EXTENSION DeviceExtension, 761 IN UCHAR Status, 762 IN UCHAR Input, 763 OUT PBOOLEAN ToReturn) 764 { 765 BOOLEAN HookReturn, HookContinue; 766 767 HookContinue = FALSE; 768 769 if (DeviceExtension->KeyboardHook.IsrRoutine) 770 { 771 HookReturn = DeviceExtension->KeyboardHook.IsrRoutine( 772 DeviceExtension->KeyboardHook.Context, 773 DeviceExtension->KeyboardBuffer + DeviceExtension->KeysInBuffer, 774 &DeviceExtension->Common.PortDeviceExtension->Packet, 775 Status, 776 &Input, 777 &HookContinue, 778 &DeviceExtension->KeyboardScanState); 779 780 if (!HookContinue) 781 { 782 *ToReturn = HookReturn; 783 return TRUE; 784 } 785 } 786 return FALSE; 787 } 788 789 BOOLEAN NTAPI 790 i8042KbdInterruptService( 791 IN PKINTERRUPT Interrupt, 792 PVOID Context) 793 { 794 PI8042_KEYBOARD_EXTENSION DeviceExtension; 795 PPORT_DEVICE_EXTENSION PortDeviceExtension; 796 PKEYBOARD_INPUT_DATA InputData; 797 ULONG Counter; 798 UCHAR PortStatus = 0, Output = 0; 799 BOOLEAN ToReturn = FALSE; 800 NTSTATUS Status; 801 802 UNREFERENCED_PARAMETER(Interrupt); 803 804 __analysis_assume(Context != NULL); 805 DeviceExtension = Context; 806 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension; 807 InputData = DeviceExtension->KeyboardBuffer + DeviceExtension->KeysInBuffer; 808 Counter = PortDeviceExtension->Settings.PollStatusIterations; 809 810 while (Counter) 811 { 812 Status = i8042ReadStatus(PortDeviceExtension, &PortStatus); 813 if (!NT_SUCCESS(Status)) 814 { 815 WARN_(I8042PRT, "i8042ReadStatus() failed with status 0x%08lx\n", Status); 816 return FALSE; 817 } 818 Status = i8042ReadKeyboardData(PortDeviceExtension, &Output); 819 if (NT_SUCCESS(Status)) 820 break; 821 KeStallExecutionProcessor(1); 822 Counter--; 823 } 824 if (Counter == 0) 825 { 826 WARN_(I8042PRT, "Spurious i8042 keyboard interrupt\n"); 827 return FALSE; 828 } 829 830 INFO_(I8042PRT, "Got: 0x%02x\n", Output); 831 832 if (PortDeviceExtension->Settings.CrashOnCtrlScroll) 833 { 834 /* Test for CTRL + SCROLL LOCK twice */ 835 static const UCHAR ScanCodes[] = { 0x1d, 0x46, 0xc6, 0x46, 0 }; 836 837 if (Output == ScanCodes[DeviceExtension->ComboPosition]) 838 { 839 DeviceExtension->ComboPosition++; 840 if (ScanCodes[DeviceExtension->ComboPosition] == 0) 841 KeBugCheck(MANUALLY_INITIATED_CRASH); 842 } 843 else if (Output == 0xfa) 844 { 845 /* Ignore ACK */ 846 } 847 else if (Output == ScanCodes[0]) 848 DeviceExtension->ComboPosition = 1; 849 else 850 DeviceExtension->ComboPosition = 0; 851 852 /* Test for TAB + key combination */ 853 if (InputData->MakeCode == 0x0F) 854 DeviceExtension->TabPressed = !(InputData->Flags & KEY_BREAK); 855 else if (DeviceExtension->TabPressed) 856 { 857 DeviceExtension->TabPressed = FALSE; 858 859 /* Check which action to do */ 860 if (InputData->MakeCode == 0x25) 861 { 862 /* k - Breakpoint */ 863 DbgBreakPointWithStatus(DBG_STATUS_SYSRQ); 864 } 865 else if (InputData->MakeCode == 0x30) 866 { 867 /* b - Bugcheck */ 868 KeBugCheck(MANUALLY_INITIATED_CRASH); 869 } 870 #if defined(KDBG) 871 else 872 { 873 /* Send request to the kernel debugger. 874 * Unknown requests will be ignored. */ 875 KdSystemDebugControl(' soR', 876 (PVOID)(ULONG_PTR)InputData->MakeCode, 877 0, 878 NULL, 879 0, 880 NULL, 881 KernelMode); 882 } 883 #endif 884 } 885 } 886 887 if (i8042KbdCallIsrHook(DeviceExtension, PortStatus, Output, &ToReturn)) 888 return ToReturn; 889 890 if (i8042PacketIsr(PortDeviceExtension, Output)) 891 { 892 if (PortDeviceExtension->PacketComplete) 893 { 894 TRACE_(I8042PRT, "Packet complete\n"); 895 KeInsertQueueDpc(&DeviceExtension->DpcKeyboard, NULL, NULL); 896 } 897 TRACE_(I8042PRT, "Irq eaten by packet\n"); 898 return TRUE; 899 } 900 901 TRACE_(I8042PRT, "Irq is keyboard input\n"); 902 903 if (DeviceExtension->KeyboardScanState == Normal) 904 { 905 switch (Output) 906 { 907 case 0xe0: 908 DeviceExtension->KeyboardScanState = GotE0; 909 return TRUE; 910 case 0xe1: 911 DeviceExtension->KeyboardScanState = GotE1; 912 return TRUE; 913 default: 914 break; 915 } 916 } 917 918 /* Update InputData */ 919 InputData->Flags = 0; 920 switch (DeviceExtension->KeyboardScanState) 921 { 922 case GotE0: 923 InputData->Flags |= KEY_E0; 924 break; 925 case GotE1: 926 InputData->Flags |= KEY_E1; 927 break; 928 default: 929 break; 930 } 931 DeviceExtension->KeyboardScanState = Normal; 932 if (Output & 0x80) 933 InputData->Flags |= KEY_BREAK; 934 else 935 InputData->Flags |= KEY_MAKE; 936 InputData->MakeCode = Output & 0x7f; 937 InputData->Reserved = 0; 938 939 DeviceExtension->KeyboardHook.QueueKeyboardPacket(DeviceExtension->KeyboardHook.CallContext); 940 941 return TRUE; 942 } 943