1 /** @file 2 3 PS/2 Keyboard driver. Routines that interacts with callers, 4 conforming to EFI driver model 5 6 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR> 7 This program and the accompanying materials 8 are licensed and made available under the terms and conditions of the BSD License 9 which accompanies this distribution. The full text of the license may be found at 10 http://opensource.org/licenses/bsd-license.php 11 12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 14 15 **/ 16 17 #include "Ps2Keyboard.h" 18 19 // 20 // Function prototypes 21 // 22 /** 23 Test controller is a keyboard Controller. 24 25 @param This Pointer of EFI_DRIVER_BINDING_PROTOCOL 26 @param Controller driver's controller 27 @param RemainingDevicePath children device path 28 29 @retval EFI_UNSUPPORTED controller is not floppy disk 30 @retval EFI_SUCCESS controller is floppy disk 31 **/ 32 EFI_STATUS 33 EFIAPI 34 KbdControllerDriverSupported ( 35 IN EFI_DRIVER_BINDING_PROTOCOL *This, 36 IN EFI_HANDLE Controller, 37 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath 38 ); 39 40 /** 41 Create KEYBOARD_CONSOLE_IN_DEV instance on controller. 42 43 @param This Pointer of EFI_DRIVER_BINDING_PROTOCOL 44 @param Controller driver controller handle 45 @param RemainingDevicePath Children's device path 46 47 @retval whether success to create floppy control instance. 48 **/ 49 EFI_STATUS 50 EFIAPI 51 KbdControllerDriverStart ( 52 IN EFI_DRIVER_BINDING_PROTOCOL *This, 53 IN EFI_HANDLE Controller, 54 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath 55 ); 56 57 /** 58 Stop this driver on ControllerHandle. Support stopping any child handles 59 created by this driver. 60 61 @param This Protocol instance pointer. 62 @param ControllerHandle Handle of device to stop driver on 63 @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of 64 children is zero stop the entire bus driver. 65 @param ChildHandleBuffer List of Child Handles to Stop. 66 67 @retval EFI_SUCCESS This driver is removed ControllerHandle 68 @retval other This driver was not removed from this device 69 70 **/ 71 EFI_STATUS 72 EFIAPI 73 KbdControllerDriverStop ( 74 IN EFI_DRIVER_BINDING_PROTOCOL *This, 75 IN EFI_HANDLE Controller, 76 IN UINTN NumberOfChildren, 77 IN EFI_HANDLE *ChildHandleBuffer 78 ); 79 80 /** 81 Free the waiting key notify list. 82 83 @param ListHead Pointer to list head 84 85 @retval EFI_INVALID_PARAMETER ListHead is NULL 86 @retval EFI_SUCCESS Sucess to free NotifyList 87 **/ 88 EFI_STATUS 89 KbdFreeNotifyList ( 90 IN OUT LIST_ENTRY *ListHead 91 ); 92 93 // 94 // DriverBinding Protocol Instance 95 // 96 EFI_DRIVER_BINDING_PROTOCOL gKeyboardControllerDriver = { 97 KbdControllerDriverSupported, 98 KbdControllerDriverStart, 99 KbdControllerDriverStop, 100 0xa, 101 NULL, 102 NULL 103 }; 104 105 /** 106 Test controller is a keyboard Controller. 107 108 @param This Pointer of EFI_DRIVER_BINDING_PROTOCOL 109 @param Controller driver's controller 110 @param RemainingDevicePath children device path 111 112 @retval EFI_UNSUPPORTED controller is not floppy disk 113 @retval EFI_SUCCESS controller is floppy disk 114 **/ 115 EFI_STATUS 116 EFIAPI 117 KbdControllerDriverSupported ( 118 IN EFI_DRIVER_BINDING_PROTOCOL *This, 119 IN EFI_HANDLE Controller, 120 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath 121 ) 122 { 123 EFI_STATUS Status; 124 EFI_ISA_IO_PROTOCOL *IsaIo; 125 126 // 127 // Open the IO Abstraction(s) needed to perform the supported test 128 // 129 Status = gBS->OpenProtocol ( 130 Controller, 131 &gEfiIsaIoProtocolGuid, 132 (VOID **) &IsaIo, 133 This->DriverBindingHandle, 134 Controller, 135 EFI_OPEN_PROTOCOL_BY_DRIVER 136 ); 137 if (EFI_ERROR (Status)) { 138 return Status; 139 } 140 // 141 // Use the ISA I/O Protocol to see if Controller is the Keyboard controller 142 // 143 if (IsaIo->ResourceList->Device.HID != EISA_PNP_ID (0x303) || IsaIo->ResourceList->Device.UID != 0) { 144 Status = EFI_UNSUPPORTED; 145 } 146 // 147 // Close the I/O Abstraction(s) used to perform the supported test 148 // 149 gBS->CloseProtocol ( 150 Controller, 151 &gEfiIsaIoProtocolGuid, 152 This->DriverBindingHandle, 153 Controller 154 ); 155 156 return Status; 157 } 158 159 /** 160 Create KEYBOARD_CONSOLE_IN_DEV instance on controller. 161 162 @param This Pointer of EFI_DRIVER_BINDING_PROTOCOL 163 @param Controller driver controller handle 164 @param RemainingDevicePath Children's device path 165 166 @retval whether success to create floppy control instance. 167 **/ 168 EFI_STATUS 169 EFIAPI 170 KbdControllerDriverStart ( 171 IN EFI_DRIVER_BINDING_PROTOCOL *This, 172 IN EFI_HANDLE Controller, 173 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath 174 ) 175 { 176 EFI_STATUS Status; 177 EFI_STATUS Status1; 178 EFI_ISA_IO_PROTOCOL *IsaIo; 179 KEYBOARD_CONSOLE_IN_DEV *ConsoleIn; 180 UINT8 Data; 181 EFI_STATUS_CODE_VALUE StatusCode; 182 EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; 183 184 StatusCode = 0; 185 186 Status = gBS->OpenProtocol ( 187 Controller, 188 &gEfiDevicePathProtocolGuid, 189 (VOID **) &ParentDevicePath, 190 This->DriverBindingHandle, 191 Controller, 192 EFI_OPEN_PROTOCOL_BY_DRIVER 193 ); 194 if (EFI_ERROR (Status)) { 195 return Status; 196 } 197 // 198 // Report that the keyboard is being enabled 199 // 200 REPORT_STATUS_CODE_WITH_DEVICE_PATH ( 201 EFI_PROGRESS_CODE, 202 EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_ENABLE, 203 ParentDevicePath 204 ); 205 206 // 207 // Get the ISA I/O Protocol on Controller's handle 208 // 209 Status = gBS->OpenProtocol ( 210 Controller, 211 &gEfiIsaIoProtocolGuid, 212 (VOID **) &IsaIo, 213 This->DriverBindingHandle, 214 Controller, 215 EFI_OPEN_PROTOCOL_BY_DRIVER 216 ); 217 if (EFI_ERROR (Status)) { 218 gBS->CloseProtocol ( 219 Controller, 220 &gEfiDevicePathProtocolGuid, 221 This->DriverBindingHandle, 222 Controller 223 ); 224 return EFI_INVALID_PARAMETER; 225 } 226 // 227 // Allocate private data 228 // 229 ConsoleIn = AllocateZeroPool (sizeof (KEYBOARD_CONSOLE_IN_DEV)); 230 if (ConsoleIn == NULL) { 231 Status = EFI_OUT_OF_RESOURCES; 232 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR; 233 goto ErrorExit; 234 } 235 // 236 // Setup the device instance 237 // 238 ConsoleIn->Signature = KEYBOARD_CONSOLE_IN_DEV_SIGNATURE; 239 ConsoleIn->Handle = Controller; 240 (ConsoleIn->ConIn).Reset = KeyboardEfiReset; 241 (ConsoleIn->ConIn).ReadKeyStroke = KeyboardReadKeyStroke; 242 ConsoleIn->DataRegisterAddress = KEYBOARD_8042_DATA_REGISTER; 243 ConsoleIn->StatusRegisterAddress = KEYBOARD_8042_STATUS_REGISTER; 244 ConsoleIn->CommandRegisterAddress = KEYBOARD_8042_COMMAND_REGISTER; 245 ConsoleIn->IsaIo = IsaIo; 246 ConsoleIn->DevicePath = ParentDevicePath; 247 248 ConsoleIn->ConInEx.Reset = KeyboardEfiResetEx; 249 ConsoleIn->ConInEx.ReadKeyStrokeEx = KeyboardReadKeyStrokeEx; 250 ConsoleIn->ConInEx.SetState = KeyboardSetState; 251 ConsoleIn->ConInEx.RegisterKeyNotify = KeyboardRegisterKeyNotify; 252 ConsoleIn->ConInEx.UnregisterKeyNotify = KeyboardUnregisterKeyNotify; 253 254 InitializeListHead (&ConsoleIn->NotifyList); 255 256 // 257 // Fix for random hangs in System waiting for the Key if no KBC is present in BIOS. 258 // When KBC decode (IO port 0x60/0x64 decode) is not enabled, 259 // KeyboardRead will read back as 0xFF and return status is EFI_SUCCESS. 260 // So instead we read status register to detect after read if KBC decode is enabled. 261 // 262 263 // 264 // Return code is ignored on purpose. 265 // 266 if (!PcdGetBool (PcdFastPS2Detection)) { 267 KeyboardRead (ConsoleIn, &Data); 268 if ((KeyReadStatusRegister (ConsoleIn) & (KBC_PARE | KBC_TIM)) == (KBC_PARE | KBC_TIM)) { 269 // 270 // If nobody decodes KBC I/O port, it will read back as 0xFF. 271 // Check the Time-Out and Parity bit to see if it has an active KBC in system 272 // 273 Status = EFI_DEVICE_ERROR; 274 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_NOT_DETECTED; 275 goto ErrorExit; 276 } 277 } 278 279 // 280 // Setup the WaitForKey event 281 // 282 Status = gBS->CreateEvent ( 283 EVT_NOTIFY_WAIT, 284 TPL_NOTIFY, 285 KeyboardWaitForKey, 286 ConsoleIn, 287 &((ConsoleIn->ConIn).WaitForKey) 288 ); 289 if (EFI_ERROR (Status)) { 290 Status = EFI_OUT_OF_RESOURCES; 291 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR; 292 goto ErrorExit; 293 } 294 // 295 // Setup the WaitForKeyEx event 296 // 297 Status = gBS->CreateEvent ( 298 EVT_NOTIFY_WAIT, 299 TPL_NOTIFY, 300 KeyboardWaitForKeyEx, 301 ConsoleIn, 302 &(ConsoleIn->ConInEx.WaitForKeyEx) 303 ); 304 if (EFI_ERROR (Status)) { 305 Status = EFI_OUT_OF_RESOURCES; 306 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR; 307 goto ErrorExit; 308 } 309 310 // Setup a periodic timer, used for reading keystrokes at a fixed interval 311 // 312 Status = gBS->CreateEvent ( 313 EVT_TIMER | EVT_NOTIFY_SIGNAL, 314 TPL_NOTIFY, 315 KeyboardTimerHandler, 316 ConsoleIn, 317 &ConsoleIn->TimerEvent 318 ); 319 if (EFI_ERROR (Status)) { 320 Status = EFI_OUT_OF_RESOURCES; 321 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR; 322 goto ErrorExit; 323 } 324 325 Status = gBS->SetTimer ( 326 ConsoleIn->TimerEvent, 327 TimerPeriodic, 328 KEYBOARD_TIMER_INTERVAL 329 ); 330 if (EFI_ERROR (Status)) { 331 Status = EFI_OUT_OF_RESOURCES; 332 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR; 333 goto ErrorExit; 334 } 335 336 Status = gBS->CreateEvent ( 337 EVT_NOTIFY_SIGNAL, 338 TPL_CALLBACK, 339 KeyNotifyProcessHandler, 340 ConsoleIn, 341 &ConsoleIn->KeyNotifyProcessEvent 342 ); 343 if (EFI_ERROR (Status)) { 344 Status = EFI_OUT_OF_RESOURCES; 345 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR; 346 goto ErrorExit; 347 } 348 349 REPORT_STATUS_CODE_WITH_DEVICE_PATH ( 350 EFI_PROGRESS_CODE, 351 EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_PRESENCE_DETECT, 352 ParentDevicePath 353 ); 354 355 // 356 // Reset the keyboard device 357 // 358 Status = ConsoleIn->ConInEx.Reset (&ConsoleIn->ConInEx, FeaturePcdGet (PcdPs2KbdExtendedVerification)); 359 if (EFI_ERROR (Status)) { 360 Status = EFI_DEVICE_ERROR; 361 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_NOT_DETECTED; 362 goto ErrorExit; 363 } 364 365 REPORT_STATUS_CODE_WITH_DEVICE_PATH ( 366 EFI_PROGRESS_CODE, 367 EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_DETECTED, 368 ParentDevicePath 369 ); 370 371 ConsoleIn->ControllerNameTable = NULL; 372 AddUnicodeString2 ( 373 "eng", 374 gPs2KeyboardComponentName.SupportedLanguages, 375 &ConsoleIn->ControllerNameTable, 376 L"PS/2 Keyboard Device", 377 TRUE 378 ); 379 AddUnicodeString2 ( 380 "en", 381 gPs2KeyboardComponentName2.SupportedLanguages, 382 &ConsoleIn->ControllerNameTable, 383 L"PS/2 Keyboard Device", 384 FALSE 385 ); 386 387 388 // 389 // Install protocol interfaces for the keyboard device. 390 // 391 Status = gBS->InstallMultipleProtocolInterfaces ( 392 &Controller, 393 &gEfiSimpleTextInProtocolGuid, 394 &ConsoleIn->ConIn, 395 &gEfiSimpleTextInputExProtocolGuid, 396 &ConsoleIn->ConInEx, 397 NULL 398 ); 399 if (EFI_ERROR (Status)) { 400 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR; 401 goto ErrorExit; 402 } 403 404 return Status; 405 406 ErrorExit: 407 // 408 // Report error code 409 // 410 if (StatusCode != 0) { 411 REPORT_STATUS_CODE_WITH_DEVICE_PATH ( 412 EFI_ERROR_CODE | EFI_ERROR_MINOR, 413 StatusCode, 414 ParentDevicePath 415 ); 416 } 417 418 if ((ConsoleIn != NULL) && (ConsoleIn->ConIn.WaitForKey != NULL)) { 419 gBS->CloseEvent (ConsoleIn->ConIn.WaitForKey); 420 } 421 422 if ((ConsoleIn != NULL) && (ConsoleIn->TimerEvent != NULL)) { 423 gBS->CloseEvent (ConsoleIn->TimerEvent); 424 } 425 if ((ConsoleIn != NULL) && (ConsoleIn->ConInEx.WaitForKeyEx != NULL)) { 426 gBS->CloseEvent (ConsoleIn->ConInEx.WaitForKeyEx); 427 } 428 if ((ConsoleIn != NULL) && (ConsoleIn->KeyNotifyProcessEvent != NULL)) { 429 gBS->CloseEvent (ConsoleIn->KeyNotifyProcessEvent); 430 } 431 KbdFreeNotifyList (&ConsoleIn->NotifyList); 432 if ((ConsoleIn != NULL) && (ConsoleIn->ControllerNameTable != NULL)) { 433 FreeUnicodeStringTable (ConsoleIn->ControllerNameTable); 434 } 435 // 436 // Since there will be no timer handler for keyboard input any more, 437 // exhaust input data just in case there is still keyboard data left 438 // 439 if (ConsoleIn != NULL) { 440 Status1 = EFI_SUCCESS; 441 while (!EFI_ERROR (Status1) && (Status != EFI_DEVICE_ERROR)) { 442 Status1 = KeyboardRead (ConsoleIn, &Data);; 443 } 444 } 445 446 if (ConsoleIn != NULL) { 447 gBS->FreePool (ConsoleIn); 448 } 449 450 gBS->CloseProtocol ( 451 Controller, 452 &gEfiDevicePathProtocolGuid, 453 This->DriverBindingHandle, 454 Controller 455 ); 456 457 gBS->CloseProtocol ( 458 Controller, 459 &gEfiIsaIoProtocolGuid, 460 This->DriverBindingHandle, 461 Controller 462 ); 463 464 return Status; 465 } 466 467 /** 468 Stop this driver on ControllerHandle. Support stopping any child handles 469 created by this driver. 470 471 @param This Protocol instance pointer. 472 @param ControllerHandle Handle of device to stop driver on 473 @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of 474 children is zero stop the entire bus driver. 475 @param ChildHandleBuffer List of Child Handles to Stop. 476 477 @retval EFI_SUCCESS This driver is removed ControllerHandle 478 @retval other This driver was not removed from this device 479 480 **/ 481 EFI_STATUS 482 EFIAPI 483 KbdControllerDriverStop ( 484 IN EFI_DRIVER_BINDING_PROTOCOL *This, 485 IN EFI_HANDLE Controller, 486 IN UINTN NumberOfChildren, 487 IN EFI_HANDLE *ChildHandleBuffer 488 ) 489 { 490 EFI_STATUS Status; 491 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn; 492 KEYBOARD_CONSOLE_IN_DEV *ConsoleIn; 493 UINT8 Data; 494 495 // 496 // Disable Keyboard 497 // 498 Status = gBS->OpenProtocol ( 499 Controller, 500 &gEfiSimpleTextInProtocolGuid, 501 (VOID **) &ConIn, 502 This->DriverBindingHandle, 503 Controller, 504 EFI_OPEN_PROTOCOL_GET_PROTOCOL 505 ); 506 if (EFI_ERROR (Status)) { 507 return Status; 508 } 509 Status = gBS->OpenProtocol ( 510 Controller, 511 &gEfiSimpleTextInputExProtocolGuid, 512 NULL, 513 This->DriverBindingHandle, 514 Controller, 515 EFI_OPEN_PROTOCOL_TEST_PROTOCOL 516 ); 517 if (EFI_ERROR (Status)) { 518 return Status; 519 } 520 521 ConsoleIn = KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (ConIn); 522 523 // 524 // Report that the keyboard is being disabled 525 // 526 REPORT_STATUS_CODE_WITH_DEVICE_PATH ( 527 EFI_PROGRESS_CODE, 528 EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_DISABLE, 529 ConsoleIn->DevicePath 530 ); 531 532 if (ConsoleIn->TimerEvent != NULL) { 533 gBS->CloseEvent (ConsoleIn->TimerEvent); 534 ConsoleIn->TimerEvent = NULL; 535 } 536 537 // 538 // Since there will be no timer handler for keyboard input any more, 539 // exhaust input data just in case there is still keyboard data left 540 // 541 Status = EFI_SUCCESS; 542 while (!EFI_ERROR (Status)) { 543 Status = KeyboardRead (ConsoleIn, &Data);; 544 } 545 // 546 // Uninstall the SimpleTextIn and SimpleTextInEx protocols 547 // 548 Status = gBS->UninstallMultipleProtocolInterfaces ( 549 Controller, 550 &gEfiSimpleTextInProtocolGuid, 551 &ConsoleIn->ConIn, 552 &gEfiSimpleTextInputExProtocolGuid, 553 &ConsoleIn->ConInEx, 554 NULL 555 ); 556 if (EFI_ERROR (Status)) { 557 return Status; 558 } 559 560 gBS->CloseProtocol ( 561 Controller, 562 &gEfiDevicePathProtocolGuid, 563 This->DriverBindingHandle, 564 Controller 565 ); 566 567 gBS->CloseProtocol ( 568 Controller, 569 &gEfiIsaIoProtocolGuid, 570 This->DriverBindingHandle, 571 Controller 572 ); 573 574 // 575 // Free other resources 576 // 577 if ((ConsoleIn->ConIn).WaitForKey != NULL) { 578 gBS->CloseEvent ((ConsoleIn->ConIn).WaitForKey); 579 (ConsoleIn->ConIn).WaitForKey = NULL; 580 } 581 if (ConsoleIn->ConInEx.WaitForKeyEx != NULL) { 582 gBS->CloseEvent (ConsoleIn->ConInEx.WaitForKeyEx); 583 ConsoleIn->ConInEx.WaitForKeyEx = NULL; 584 } 585 if (ConsoleIn->KeyNotifyProcessEvent != NULL) { 586 gBS->CloseEvent (ConsoleIn->KeyNotifyProcessEvent); 587 ConsoleIn->KeyNotifyProcessEvent = NULL; 588 } 589 KbdFreeNotifyList (&ConsoleIn->NotifyList); 590 FreeUnicodeStringTable (ConsoleIn->ControllerNameTable); 591 gBS->FreePool (ConsoleIn); 592 593 return EFI_SUCCESS; 594 } 595 596 /** 597 Free the waiting key notify list. 598 599 @param ListHead Pointer to list head 600 601 @retval EFI_INVALID_PARAMETER ListHead is NULL 602 @retval EFI_SUCCESS Sucess to free NotifyList 603 **/ 604 EFI_STATUS 605 KbdFreeNotifyList ( 606 IN OUT LIST_ENTRY *ListHead 607 ) 608 { 609 KEYBOARD_CONSOLE_IN_EX_NOTIFY *NotifyNode; 610 611 if (ListHead == NULL) { 612 return EFI_INVALID_PARAMETER; 613 } 614 while (!IsListEmpty (ListHead)) { 615 NotifyNode = CR ( 616 ListHead->ForwardLink, 617 KEYBOARD_CONSOLE_IN_EX_NOTIFY, 618 NotifyEntry, 619 KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE 620 ); 621 RemoveEntryList (ListHead->ForwardLink); 622 gBS->FreePool (NotifyNode); 623 } 624 625 return EFI_SUCCESS; 626 } 627 628 /** 629 The module Entry Point for module Ps2Keyboard. 630 631 @param[in] ImageHandle The firmware allocated handle for the EFI image. 632 @param[in] SystemTable A pointer to the EFI System Table. 633 634 @retval EFI_SUCCESS The entry point is executed successfully. 635 @retval other Some error occurs when executing this entry point. 636 637 **/ 638 EFI_STATUS 639 EFIAPI 640 InitializePs2Keyboard( 641 IN EFI_HANDLE ImageHandle, 642 IN EFI_SYSTEM_TABLE *SystemTable 643 ) 644 { 645 EFI_STATUS Status; 646 647 // 648 // Install driver model protocol(s). 649 // 650 Status = EfiLibInstallDriverBindingComponentName2 ( 651 ImageHandle, 652 SystemTable, 653 &gKeyboardControllerDriver, 654 ImageHandle, 655 &gPs2KeyboardComponentName, 656 &gPs2KeyboardComponentName2 657 ); 658 ASSERT_EFI_ERROR (Status); 659 660 661 return Status; 662 } 663 664