1 /** @file 2 PS2 Mouse Communication Interface. 3 4 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR> 5 This program and the accompanying materials 6 are licensed and made available under the terms and conditions of the BSD License 7 which accompanies this distribution. The full text of the license may be found at 8 http://opensource.org/licenses/bsd-license.php 9 10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 12 13 **/ 14 15 #include "Ps2Mouse.h" 16 #include "CommPs2.h" 17 18 UINT8 SampleRateTbl[MaxSampleRate] = { 0xa, 0x14, 0x28, 0x3c, 0x50, 0x64, 0xc8 }; 19 20 UINT8 ResolutionTbl[MaxResolution] = { 0, 1, 2, 3 }; 21 22 /** 23 Issue self test command via IsaIo interface. 24 25 @return EFI_SUCCESS Success to do keyboard self testing. 26 @return others Fail to do keyboard self testing. 27 **/ 28 EFI_STATUS 29 KbcSelfTest ( 30 VOID 31 ) 32 { 33 EFI_STATUS Status; 34 UINT8 Data; 35 36 // 37 // Keyboard controller self test 38 // 39 Status = Out8042Command (SELF_TEST); 40 if (EFI_ERROR (Status)) { 41 return Status; 42 } 43 // 44 // Read return code 45 // 46 Status = In8042Data (&Data); 47 if (EFI_ERROR (Status)) { 48 return Status; 49 } 50 51 if (Data != 0x55) { 52 return EFI_DEVICE_ERROR; 53 } 54 // 55 // Set system flag 56 // 57 Status = Out8042Command (READ_CMD_BYTE); 58 if (EFI_ERROR (Status)) { 59 return Status; 60 } 61 62 Status = In8042Data (&Data); 63 if (EFI_ERROR (Status)) { 64 return Status; 65 } 66 67 Status = Out8042Command (WRITE_CMD_BYTE); 68 if (EFI_ERROR (Status)) { 69 return Status; 70 } 71 72 Data |= CMD_SYS_FLAG; 73 Status = Out8042Data (Data); 74 if (EFI_ERROR (Status)) { 75 return Status; 76 } 77 78 return EFI_SUCCESS; 79 } 80 81 /** 82 Issue command to enable keyboard AUX functionality. 83 84 @return Status of command issuing. 85 **/ 86 EFI_STATUS 87 KbcEnableAux ( 88 VOID 89 ) 90 { 91 // 92 // Send 8042 enable mouse command 93 // 94 return Out8042Command (ENABLE_AUX); 95 } 96 97 /** 98 Issue command to disable keyboard AUX functionality. 99 100 @param IsaIo Pointer to instance of EFI_ISA_IO_PROTOCOL 101 102 @return Status of command issuing. 103 **/ 104 EFI_STATUS 105 KbcDisableAux ( 106 VOID 107 ) 108 { 109 // 110 // Send 8042 disable mouse command 111 // 112 return Out8042Command (DISABLE_AUX); 113 } 114 115 /** 116 Issue command to enable keyboard. 117 118 @param IsaIo Pointer to instance of EFI_ISA_IO_PROTOCOL 119 120 @return Status of command issuing. 121 **/ 122 EFI_STATUS 123 KbcEnableKb ( 124 VOID 125 ) 126 { 127 // 128 // Send 8042 enable keyboard command 129 // 130 return Out8042Command (ENABLE_KB); 131 } 132 133 /** 134 Issue command to disable keyboard. 135 136 @return Status of command issuing. 137 **/ 138 EFI_STATUS 139 KbcDisableKb ( 140 VOID 141 ) 142 { 143 // 144 // Send 8042 disable keyboard command 145 // 146 return Out8042Command (DISABLE_KB); 147 } 148 149 /** 150 Issue command to check keyboard status. 151 152 @param KeyboardEnable return whether keyboard is enable. 153 154 @return Status of command issuing. 155 **/ 156 EFI_STATUS 157 CheckKbStatus ( 158 OUT BOOLEAN *KeyboardEnable 159 ) 160 { 161 EFI_STATUS Status; 162 UINT8 Data; 163 164 // 165 // Send command to read KBC command byte 166 // 167 Status = Out8042Command (READ_CMD_BYTE); 168 if (EFI_ERROR (Status)) { 169 return Status; 170 } 171 172 Status = In8042Data (&Data); 173 if (EFI_ERROR (Status)) { 174 return Status; 175 } 176 // 177 // Check keyboard enable or not 178 // 179 if ((Data & CMD_KB_STS) == CMD_KB_DIS) { 180 *KeyboardEnable = FALSE; 181 } else { 182 *KeyboardEnable = TRUE; 183 } 184 185 return EFI_SUCCESS; 186 } 187 188 /** 189 Issue command to reset keyboard. 190 191 @return Status of command issuing. 192 **/ 193 EFI_STATUS 194 PS2MouseReset ( 195 VOID 196 ) 197 { 198 EFI_STATUS Status; 199 UINT8 Data; 200 201 Status = Out8042AuxCommand (RESET_CMD, FALSE); 202 if (EFI_ERROR (Status)) { 203 return Status; 204 } 205 206 Status = In8042AuxData (&Data); 207 if (EFI_ERROR (Status)) { 208 return Status; 209 } 210 // 211 // Check BAT Complete Code 212 // 213 if (Data != PS2MOUSE_BAT1) { 214 return EFI_DEVICE_ERROR; 215 } 216 217 Status = In8042AuxData (&Data); 218 if (EFI_ERROR (Status)) { 219 return Status; 220 } 221 // 222 // Check BAT Complete Code 223 // 224 if (Data != PS2MOUSE_BAT2) { 225 return EFI_DEVICE_ERROR; 226 } 227 228 return EFI_SUCCESS; 229 } 230 231 /** 232 Issue command to set mouse's sample rate 233 234 @param SampleRate value of sample rate 235 236 @return Status of command issuing. 237 **/ 238 EFI_STATUS 239 PS2MouseSetSampleRate ( 240 IN MOUSE_SR SampleRate 241 ) 242 { 243 EFI_STATUS Status; 244 245 // 246 // Send auxiliary command to set mouse sample rate 247 // 248 Status = Out8042AuxCommand (SETSR_CMD, FALSE); 249 if (EFI_ERROR (Status)) { 250 return Status; 251 } 252 253 Status = Out8042AuxData (SampleRateTbl[SampleRate]); 254 255 return Status; 256 } 257 258 /** 259 Issue command to set mouse's resolution. 260 261 @param Resolution value of resolution 262 263 @return Status of command issuing. 264 **/ 265 EFI_STATUS 266 PS2MouseSetResolution ( 267 IN MOUSE_RE Resolution 268 ) 269 { 270 EFI_STATUS Status; 271 272 // 273 // Send auxiliary command to set mouse resolution 274 // 275 Status = Out8042AuxCommand (SETRE_CMD, FALSE); 276 if (EFI_ERROR (Status)) { 277 return Status; 278 } 279 280 Status = Out8042AuxData (ResolutionTbl[Resolution]); 281 282 return Status; 283 } 284 285 /** 286 Issue command to set mouse's scaling. 287 288 @param Scaling value of scaling 289 290 @return Status of command issuing. 291 **/ 292 EFI_STATUS 293 PS2MouseSetScaling ( 294 IN MOUSE_SF Scaling 295 ) 296 { 297 // 298 // Send auxiliary command to set mouse scaling data 299 // 300 return Out8042AuxCommand (Scaling == Scaling1 ? SETSF1_CMD : SETSF2_CMD, FALSE); 301 } 302 303 /** 304 Issue command to enable Ps2 mouse. 305 306 @return Status of command issuing. 307 **/ 308 EFI_STATUS 309 PS2MouseEnable ( 310 VOID 311 ) 312 { 313 // 314 // Send auxiliary command to enable mouse 315 // 316 return Out8042AuxCommand (ENABLE_CMD, FALSE); 317 } 318 319 /** 320 Get mouse packet . Only care first 3 bytes 321 322 @param MouseDev Pointer of PS2 Mouse Private Data Structure 323 324 @retval EFI_NOT_READY Mouse Device not ready to input data packet, or some error happened during getting the packet 325 @retval EFI_SUCCESS The data packet is gotten successfully. 326 327 **/ 328 EFI_STATUS 329 PS2MouseGetPacket ( 330 PS2_MOUSE_DEV *MouseDev 331 ) 332 333 { 334 EFI_STATUS Status; 335 BOOLEAN KeyboardEnable; 336 UINT8 Packet[PS2_PACKET_LENGTH]; 337 UINT8 Data; 338 UINTN Count; 339 UINTN State; 340 INT16 RelativeMovementX; 341 INT16 RelativeMovementY; 342 BOOLEAN LButton; 343 BOOLEAN RButton; 344 345 KeyboardEnable = FALSE; 346 State = PS2_READ_BYTE_ONE; 347 348 // 349 // State machine to get mouse packet 350 // 351 while (1) { 352 353 switch (State) { 354 case PS2_READ_BYTE_ONE: 355 // 356 // Read mouse first byte data, if failed, immediately return 357 // 358 KbcDisableAux (); 359 Count = 1; 360 Status = PS2MouseRead (&Data, &Count, State); 361 if (EFI_ERROR (Status)) { 362 KbcEnableAux (); 363 return EFI_NOT_READY; 364 } 365 366 if (Count != 1) { 367 KbcEnableAux (); 368 return EFI_NOT_READY; 369 } 370 371 if (IS_PS2_SYNC_BYTE (Data)) { 372 Packet[0] = Data; 373 State = PS2_READ_DATA_BYTE; 374 375 CheckKbStatus (&KeyboardEnable); 376 KbcDisableKb (); 377 KbcEnableAux (); 378 } 379 break; 380 381 case PS2_READ_DATA_BYTE: 382 Count = 2; 383 Status = PS2MouseRead ((Packet + 1), &Count, State); 384 if (EFI_ERROR (Status)) { 385 if (KeyboardEnable) { 386 KbcEnableKb (); 387 } 388 389 return EFI_NOT_READY; 390 } 391 392 if (Count != 2) { 393 if (KeyboardEnable) { 394 KbcEnableKb (); 395 } 396 397 return EFI_NOT_READY; 398 } 399 400 State = PS2_PROCESS_PACKET; 401 break; 402 403 case PS2_PROCESS_PACKET: 404 if (KeyboardEnable) { 405 KbcEnableKb (); 406 } 407 // 408 // Decode the packet 409 // 410 RelativeMovementX = Packet[1]; 411 RelativeMovementY = Packet[2]; 412 // 413 // Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 414 // Byte 0 | Y overflow | X overflow | Y sign bit | X sign bit | Always 1 | Middle Btn | Right Btn | Left Btn 415 // Byte 1 | 8 bit X Movement 416 // Byte 2 | 8 bit Y Movement 417 // 418 // X sign bit + 8 bit X Movement : 9-bit signed twos complement integer that presents the relative displacement of the device in the X direction since the last data transmission. 419 // Y sign bit + 8 bit Y Movement : Same as X sign bit + 8 bit X Movement. 420 // 421 // 422 // First, Clear X and Y high 8 bits 423 // 424 RelativeMovementX = (INT16) (RelativeMovementX & 0xFF); 425 RelativeMovementY = (INT16) (RelativeMovementY & 0xFF); 426 // 427 // Second, if the 9-bit signed twos complement integer is negative, set the high 8 bit 0xff 428 // 429 if ((Packet[0] & 0x10) != 0) { 430 RelativeMovementX = (INT16) (RelativeMovementX | 0xFF00); 431 } 432 if ((Packet[0] & 0x20) != 0) { 433 RelativeMovementY = (INT16) (RelativeMovementY | 0xFF00); 434 } 435 436 437 RButton = (UINT8) (Packet[0] & 0x2); 438 LButton = (UINT8) (Packet[0] & 0x1); 439 440 // 441 // Update mouse state 442 // 443 MouseDev->State.RelativeMovementX += RelativeMovementX; 444 MouseDev->State.RelativeMovementY -= RelativeMovementY; 445 MouseDev->State.RightButton = (UINT8) (RButton ? TRUE : FALSE); 446 MouseDev->State.LeftButton = (UINT8) (LButton ? TRUE : FALSE); 447 MouseDev->StateChanged = TRUE; 448 449 return EFI_SUCCESS; 450 } 451 } 452 } 453 454 /** 455 Read data via IsaIo protocol with given number. 456 457 @param Buffer Buffer receive data of mouse 458 @param BufSize The size of buffer 459 @param State Check input or read data 460 461 @return status of reading mouse data. 462 **/ 463 EFI_STATUS 464 PS2MouseRead ( 465 OUT UINT8 *Buffer, 466 IN OUT UINTN *BufSize, 467 IN UINTN State 468 ) 469 { 470 EFI_STATUS Status; 471 UINTN BytesRead; 472 473 Status = EFI_SUCCESS; 474 475 if (State == PS2_READ_BYTE_ONE) { 476 // 477 // Check input for mouse 478 // 479 Status = CheckForInput (); 480 481 if (EFI_ERROR (Status)) { 482 return Status; 483 } 484 } 485 486 for (BytesRead = 0; BytesRead < *BufSize; BytesRead++) { 487 488 Status = WaitOutputFull (TIMEOUT); 489 if (EFI_ERROR (Status)) { 490 break; 491 } 492 Buffer[BytesRead] = IoRead8 (KBC_DATA_PORT); 493 } 494 // 495 // Verify the correct number of bytes read 496 // 497 if (BytesRead == 0 || BytesRead != *BufSize) { 498 Status = EFI_NOT_FOUND; 499 } 500 501 *BufSize = BytesRead; 502 return Status; 503 } 504 505 // 506 // 8042 I/O function 507 // 508 /** 509 I/O work flow of outing 8042 command. 510 511 @param Command I/O command. 512 513 @retval EFI_SUCCESS Success to execute I/O work flow 514 @retval EFI_TIMEOUT Keyboard controller time out. 515 **/ 516 EFI_STATUS 517 Out8042Command ( 518 IN UINT8 Command 519 ) 520 { 521 EFI_STATUS Status; 522 523 // 524 // Wait keyboard controller input buffer empty 525 // 526 Status = WaitInputEmpty (TIMEOUT); 527 if (EFI_ERROR (Status)) { 528 return Status; 529 } 530 // 531 // Send command 532 // 533 IoWrite8 (KBC_CMD_STS_PORT, Command); 534 535 Status = WaitInputEmpty (TIMEOUT); 536 if (EFI_ERROR (Status)) { 537 return Status; 538 } 539 540 return EFI_SUCCESS; 541 } 542 543 /** 544 I/O work flow of outing 8042 data. 545 546 @param Data Data value 547 548 @retval EFI_SUCCESS Success to execute I/O work flow 549 @retval EFI_TIMEOUT Keyboard controller time out. 550 **/ 551 EFI_STATUS 552 Out8042Data ( 553 IN UINT8 Data 554 ) 555 { 556 EFI_STATUS Status; 557 // 558 // Wait keyboard controller input buffer empty 559 // 560 Status = WaitInputEmpty (TIMEOUT); 561 if (EFI_ERROR (Status)) { 562 return Status; 563 } 564 565 IoWrite8 (KBC_DATA_PORT, Data); 566 return WaitInputEmpty (TIMEOUT); 567 } 568 569 /** 570 I/O work flow of in 8042 data. 571 572 @param Data Data value 573 574 @retval EFI_SUCCESS Success to execute I/O work flow 575 @retval EFI_TIMEOUT Keyboard controller time out. 576 **/ 577 EFI_STATUS 578 In8042Data ( 579 IN OUT UINT8 *Data 580 ) 581 { 582 UINTN Delay; 583 584 Delay = TIMEOUT / 50; 585 586 do { 587 // 588 // Check keyboard controller status bit 0(output buffer status) 589 // 590 if ((IoRead8 (KBC_CMD_STS_PORT) & KBC_OUTB) == KBC_OUTB) { 591 break; 592 } 593 594 gBS->Stall (50); 595 Delay--; 596 } while (Delay != 0); 597 598 if (Delay == 0) { 599 return EFI_TIMEOUT; 600 } 601 602 *Data = IoRead8 (KBC_DATA_PORT); 603 604 return EFI_SUCCESS; 605 } 606 607 /** 608 I/O work flow of outing 8042 Aux command. 609 610 @param Command Aux I/O command 611 @param Resend Whether need resend the Aux command. 612 613 @retval EFI_SUCCESS Success to execute I/O work flow 614 @retval EFI_TIMEOUT Keyboard controller time out. 615 **/ 616 EFI_STATUS 617 Out8042AuxCommand ( 618 IN UINT8 Command, 619 IN BOOLEAN Resend 620 ) 621 { 622 EFI_STATUS Status; 623 UINT8 Data; 624 625 // 626 // Wait keyboard controller input buffer empty 627 // 628 Status = WaitInputEmpty (TIMEOUT); 629 if (EFI_ERROR (Status)) { 630 return Status; 631 } 632 // 633 // Send write to auxiliary device command 634 // 635 IoWrite8 (KBC_CMD_STS_PORT, WRITE_AUX_DEV); 636 637 Status = WaitInputEmpty (TIMEOUT); 638 if (EFI_ERROR (Status)) { 639 return Status; 640 } 641 // 642 // Send auxiliary device command 643 // 644 IoWrite8 (KBC_DATA_PORT, Command); 645 646 // 647 // Read return code 648 // 649 Status = In8042AuxData (&Data); 650 if (EFI_ERROR (Status)) { 651 return Status; 652 } 653 654 if (Data == PS2_ACK) { 655 // 656 // Receive mouse acknowledge, command send success 657 // 658 return EFI_SUCCESS; 659 660 } else if (Resend) { 661 // 662 // Resend fail 663 // 664 return EFI_DEVICE_ERROR; 665 666 } else if (Data == PS2_RESEND) { 667 // 668 // Resend command 669 // 670 Status = Out8042AuxCommand (Command, TRUE); 671 if (EFI_ERROR (Status)) { 672 return Status; 673 } 674 675 } else { 676 // 677 // Invalid return code 678 // 679 return EFI_DEVICE_ERROR; 680 681 } 682 683 return EFI_SUCCESS; 684 } 685 686 /** 687 I/O work flow of outing 8042 Aux data. 688 689 @param Data Buffer holding return value 690 691 @retval EFI_SUCCESS Success to execute I/O work flow. 692 @retval EFI_TIMEOUT Keyboard controller time out. 693 **/ 694 EFI_STATUS 695 Out8042AuxData ( 696 IN UINT8 Data 697 ) 698 { 699 EFI_STATUS Status; 700 // 701 // Wait keyboard controller input buffer empty 702 // 703 Status = WaitInputEmpty (TIMEOUT); 704 if (EFI_ERROR (Status)) { 705 return Status; 706 } 707 // 708 // Send write to auxiliary device command 709 // 710 IoWrite8 (KBC_CMD_STS_PORT, WRITE_AUX_DEV); 711 712 Status = WaitInputEmpty (TIMEOUT); 713 if (EFI_ERROR (Status)) { 714 return Status; 715 } 716 717 IoWrite8 (KBC_DATA_PORT, Data); 718 719 Status = WaitInputEmpty (TIMEOUT); 720 if (EFI_ERROR (Status)) { 721 return Status; 722 } 723 724 return EFI_SUCCESS; 725 } 726 727 /** 728 I/O work flow of in 8042 Aux data. 729 730 @param Data Buffer holding return value. 731 732 @retval EFI_SUCCESS Success to execute I/O work flow 733 @retval EFI_TIMEOUT Keyboard controller time out. 734 **/ 735 EFI_STATUS 736 In8042AuxData ( 737 IN OUT UINT8 *Data 738 ) 739 { 740 EFI_STATUS Status; 741 742 // 743 // wait for output data 744 // 745 Status = WaitOutputFull (BAT_TIMEOUT); 746 if (EFI_ERROR (Status)) { 747 return Status; 748 } 749 750 *Data = IoRead8 (KBC_DATA_PORT); 751 752 return EFI_SUCCESS; 753 } 754 755 756 /** 757 Check keyboard controller status, if it is output buffer full and for auxiliary device. 758 759 @retval EFI_SUCCESS Keyboard controller is ready 760 @retval EFI_NOT_READY Keyboard controller is not ready 761 **/ 762 EFI_STATUS 763 CheckForInput ( 764 VOID 765 ) 766 { 767 UINT8 Data; 768 769 Data = IoRead8 (KBC_CMD_STS_PORT); 770 771 // 772 // Check keyboard controller status, if it is output buffer full and for auxiliary device 773 // 774 if ((Data & (KBC_OUTB | KBC_AUXB)) != (KBC_OUTB | KBC_AUXB)) { 775 return EFI_NOT_READY; 776 } 777 778 return EFI_SUCCESS; 779 } 780 781 /** 782 I/O work flow to wait input buffer empty in given time. 783 784 @param Timeout Wating time. 785 786 @retval EFI_TIMEOUT if input is still not empty in given time. 787 @retval EFI_SUCCESS input is empty. 788 **/ 789 EFI_STATUS 790 WaitInputEmpty ( 791 IN UINTN Timeout 792 ) 793 { 794 UINTN Delay; 795 UINT8 Data; 796 797 Delay = Timeout / 50; 798 799 do { 800 Data = IoRead8 (KBC_CMD_STS_PORT); 801 802 // 803 // Check keyboard controller status bit 1(input buffer status) 804 // 805 if ((Data & KBC_INPB) == 0) { 806 break; 807 } 808 809 gBS->Stall (50); 810 Delay--; 811 } while (Delay != 0); 812 813 if (Delay == 0) { 814 return EFI_TIMEOUT; 815 } 816 817 return EFI_SUCCESS; 818 } 819 820 /** 821 I/O work flow to wait output buffer full in given time. 822 823 @param Timeout given time 824 825 @retval EFI_TIMEOUT output is not full in given time 826 @retval EFI_SUCCESS output is full in given time. 827 **/ 828 EFI_STATUS 829 WaitOutputFull ( 830 IN UINTN Timeout 831 ) 832 { 833 UINTN Delay; 834 UINT8 Data; 835 836 Delay = Timeout / 50; 837 838 do { 839 Data = IoRead8 (KBC_CMD_STS_PORT); 840 841 // 842 // Check keyboard controller status bit 0(output buffer status) 843 // & bit5(output buffer for auxiliary device) 844 // 845 if ((Data & (KBC_OUTB | KBC_AUXB)) == (KBC_OUTB | KBC_AUXB)) { 846 break; 847 } 848 849 gBS->Stall (50); 850 Delay--; 851 } while (Delay != 0); 852 853 if (Delay == 0) { 854 return EFI_TIMEOUT; 855 } 856 857 return EFI_SUCCESS; 858 } 859