2 PS2 Mouse Communication Interface.
4 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
9 #include "Ps2MouseAbsolutePointer.h"
12 UINT8 SampleRateTbl
[MaxSampleRate
] = { 0xa, 0x14, 0x28, 0x3c, 0x50, 0x64, 0xc8 };
14 UINT8 ResolutionTbl
[MaxResolution
] = { 0, 1, 2, 3 };
17 Issue self test command via IsaIo interface.
19 @param IsaIo Pointer to instance of EFI_ISA_IO_PROTOCOL
21 @return EFI_SUCCESS Success to do keyboard self testing.
22 @return others Fail to do keyboard self testing.
26 IN EFI_ISA_IO_PROTOCOL
*IsaIo
33 // Keyboard controller self test
35 Status
= Out8042Command (IsaIo
, SELF_TEST
);
36 if (EFI_ERROR (Status
)) {
42 Status
= In8042Data (IsaIo
, &Data
);
43 if (EFI_ERROR (Status
)) {
48 return EFI_DEVICE_ERROR
;
53 Status
= Out8042Command (IsaIo
, READ_CMD_BYTE
);
54 if (EFI_ERROR (Status
)) {
58 Status
= In8042Data (IsaIo
, &Data
);
59 if (EFI_ERROR (Status
)) {
63 Status
= Out8042Command (IsaIo
, WRITE_CMD_BYTE
);
64 if (EFI_ERROR (Status
)) {
69 Status
= Out8042Data (IsaIo
, Data
);
70 if (EFI_ERROR (Status
)) {
78 Issue command to enable keyboard AUX functionality.
80 @param IsaIo Pointer to instance of EFI_ISA_IO_PROTOCOL
82 @return Status of command issuing.
86 IN EFI_ISA_IO_PROTOCOL
*IsaIo
90 // Send 8042 enable mouse command
92 return Out8042Command (IsaIo
, ENABLE_AUX
);
96 Issue command to disable keyboard AUX functionality.
98 @param IsaIo Pointer to instance of EFI_ISA_IO_PROTOCOL
100 @return Status of command issuing.
104 IN EFI_ISA_IO_PROTOCOL
*IsaIo
108 // Send 8042 disable mouse command
110 return Out8042Command (IsaIo
, DISABLE_AUX
);
114 Issue command to enable keyboard.
116 @param IsaIo Pointer to instance of EFI_ISA_IO_PROTOCOL
118 @return Status of command issuing.
122 IN EFI_ISA_IO_PROTOCOL
*IsaIo
126 // Send 8042 enable keyboard command
128 return Out8042Command (IsaIo
, ENABLE_KB
);
132 Issue command to disable keyboard.
134 @param IsaIo Pointer to instance of EFI_ISA_IO_PROTOCOL
136 @return Status of command issuing.
140 IN EFI_ISA_IO_PROTOCOL
*IsaIo
144 // Send 8042 disable keyboard command
146 return Out8042Command (IsaIo
, DISABLE_KB
);
150 Issue command to check keyboard status.
152 @param IsaIo Pointer to instance of EFI_ISA_IO_PROTOCOL
153 @param KeyboardEnable return whether keyboard is enable.
155 @return Status of command issuing.
159 IN EFI_ISA_IO_PROTOCOL
*IsaIo
,
160 OUT BOOLEAN
*KeyboardEnable
167 // Send command to read KBC command byte
169 Status
= Out8042Command (IsaIo
, READ_CMD_BYTE
);
170 if (EFI_ERROR (Status
)) {
174 Status
= In8042Data (IsaIo
, &Data
);
175 if (EFI_ERROR (Status
)) {
179 // Check keyboard enable or not
181 if ((Data
& CMD_KB_STS
) == CMD_KB_DIS
) {
182 *KeyboardEnable
= FALSE
;
184 *KeyboardEnable
= TRUE
;
191 Issue command to reset keyboard.
193 @param IsaIo Pointer to instance of EFI_ISA_IO_PROTOCOL
195 @return Status of command issuing.
199 IN EFI_ISA_IO_PROTOCOL
*IsaIo
205 Status
= Out8042AuxCommand (IsaIo
, RESET_CMD
, FALSE
);
206 if (EFI_ERROR (Status
)) {
210 Status
= In8042AuxData (IsaIo
, &Data
);
211 if (EFI_ERROR (Status
)) {
215 // Check BAT Complete Code
217 if (Data
!= PS2MOUSE_BAT1
) {
218 return EFI_DEVICE_ERROR
;
221 Status
= In8042AuxData (IsaIo
, &Data
);
222 if (EFI_ERROR (Status
)) {
226 // Check BAT Complete Code
228 if (Data
!= PS2MOUSE_BAT2
) {
229 return EFI_DEVICE_ERROR
;
236 Issue command to set mouse's sample rate
238 @param IsaIo Pointer to instance of EFI_ISA_IO_PROTOCOL
239 @param SampleRate value of sample rate
241 @return Status of command issuing.
244 PS2MouseSetSampleRate (
245 IN EFI_ISA_IO_PROTOCOL
*IsaIo
,
246 IN MOUSE_SR SampleRate
252 // Send auxiliary command to set mouse sample rate
254 Status
= Out8042AuxCommand (IsaIo
, SETSR_CMD
, FALSE
);
255 if (EFI_ERROR (Status
)) {
259 Status
= Out8042AuxData (IsaIo
, SampleRateTbl
[SampleRate
]);
265 Issue command to set mouse's resolution.
267 @param IsaIo Pointer to instance of EFI_ISA_IO_PROTOCOL
268 @param Resolution value of resolution
270 @return Status of command issuing.
273 PS2MouseSetResolution (
274 IN EFI_ISA_IO_PROTOCOL
*IsaIo
,
275 IN MOUSE_RE Resolution
281 // Send auxiliary command to set mouse resolution
283 Status
= Out8042AuxCommand (IsaIo
, SETRE_CMD
, FALSE
);
284 if (EFI_ERROR (Status
)) {
288 Status
= Out8042AuxData (IsaIo
, ResolutionTbl
[Resolution
]);
294 Issue command to set mouse's scaling.
296 @param IsaIo Pointer to instance of EFI_ISA_IO_PROTOCOL
297 @param Scaling value of scaling
299 @return Status of command issuing.
303 IN EFI_ISA_IO_PROTOCOL
*IsaIo
,
309 Command
= (UINT8
) (Scaling
== Scaling1
? SETSF1_CMD
: SETSF2_CMD
);
312 // Send auxiliary command to set mouse scaling data
314 return Out8042AuxCommand (IsaIo
, Command
, FALSE
);
318 Issue command to enable Ps2 mouse.
320 @param IsaIo Pointer to instance of EFI_ISA_IO_PROTOCOL
322 @return Status of command issuing.
326 IN EFI_ISA_IO_PROTOCOL
*IsaIo
330 // Send auxiliary command to enable mouse
332 return Out8042AuxCommand (IsaIo
, ENABLE_CMD
, FALSE
);
336 Get mouse packet . Only care first 3 bytes
338 @param MouseAbsolutePointerDev Pointer to PS2 Absolute Pointer Simulation Device Private Data Structure
340 @retval EFI_NOT_READY Mouse Device not ready to input data packet, or some error happened during getting the packet
341 @retval EFI_SUCCESS The data packet is gotten successfully.
346 PS2_MOUSE_ABSOLUTE_POINTER_DEV
*MouseAbsolutePointerDev
351 BOOLEAN KeyboardEnable
;
352 UINT8 Packet
[PS2_PACKET_LENGTH
];
356 INT16 RelativeMovementX
;
357 INT16 RelativeMovementY
;
361 KeyboardEnable
= FALSE
;
363 State
= PS2_READ_BYTE_ONE
;
366 // State machine to get mouse packet
371 case PS2_READ_BYTE_ONE
:
373 // Read mouse first byte data, if failed, immediately return
375 KbcDisableAux (MouseAbsolutePointerDev
->IsaIo
);
376 Status
= PS2MouseRead (MouseAbsolutePointerDev
->IsaIo
, &Data
, &Count
, State
);
377 if (EFI_ERROR (Status
)) {
378 KbcEnableAux (MouseAbsolutePointerDev
->IsaIo
);
379 return EFI_NOT_READY
;
383 KbcEnableAux (MouseAbsolutePointerDev
->IsaIo
);
384 return EFI_NOT_READY
;
387 if (IS_PS2_SYNC_BYTE (Data
)) {
389 State
= PS2_READ_DATA_BYTE
;
391 CheckKbStatus (MouseAbsolutePointerDev
->IsaIo
, &KeyboardEnable
);
392 KbcDisableKb (MouseAbsolutePointerDev
->IsaIo
);
393 KbcEnableAux (MouseAbsolutePointerDev
->IsaIo
);
397 case PS2_READ_DATA_BYTE
:
399 Status
= PS2MouseRead (MouseAbsolutePointerDev
->IsaIo
, (Packet
+ 1), &Count
, State
);
400 if (EFI_ERROR (Status
)) {
401 if (KeyboardEnable
) {
402 KbcEnableKb (MouseAbsolutePointerDev
->IsaIo
);
405 return EFI_NOT_READY
;
409 if (KeyboardEnable
) {
410 KbcEnableKb (MouseAbsolutePointerDev
->IsaIo
);
413 return EFI_NOT_READY
;
416 State
= PS2_PROCESS_PACKET
;
419 case PS2_PROCESS_PACKET
:
420 if (KeyboardEnable
) {
421 KbcEnableKb (MouseAbsolutePointerDev
->IsaIo
);
426 RelativeMovementX
= Packet
[1];
427 RelativeMovementY
= Packet
[2];
429 // Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0
430 // Byte 0 | Y overflow | X overflow | Y sign bit | X sign bit | Always 1 | Middle Btn | Right Btn | Left Btn
431 // Byte 1 | 8 bit X Movement
432 // Byte 2 | 8 bit Y Movement
434 // 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.
435 // Y sign bit + 8 bit Y Movement : Same as X sign bit + 8 bit X Movement.
438 // First, Clear X and Y high 8 bits
440 RelativeMovementX
= (INT16
) (RelativeMovementX
& 0xFF);
441 RelativeMovementY
= (INT16
) (RelativeMovementY
& 0xFF);
443 // Second, if the 9-bit signed twos complement integer is negative, set the high 8 bit 0xff
445 if ((Packet
[0] & 0x10) != 0) {
446 RelativeMovementX
= (INT16
) (RelativeMovementX
| 0xFF00);
448 if ((Packet
[0] & 0x20) != 0) {
449 RelativeMovementY
= (INT16
) (RelativeMovementY
| 0xFF00);
453 RButton
= (UINT8
) (Packet
[0] & 0x2);
454 LButton
= (UINT8
) (Packet
[0] & 0x1);
457 // Update mouse state
459 MouseAbsolutePointerDev
->State
.CurrentX
+= RelativeMovementX
;
460 MouseAbsolutePointerDev
->State
.CurrentY
-= RelativeMovementY
;
461 MouseAbsolutePointerDev
->State
.CurrentZ
= 0;
462 MouseAbsolutePointerDev
->State
.ActiveButtons
= (UINT8
) (LButton
|| RButton
) & 0x3;
463 MouseAbsolutePointerDev
->StateChanged
= TRUE
;
471 Read data via IsaIo protocol with given number.
473 @param IsaIo Pointer to instance of EFI_ISA_IO_PROTOCOL
474 @param Buffer Buffer receive data of mouse
475 @param BufSize The size of buffer
476 @param State Check input or read data
478 @return status of reading mouse data.
482 IN EFI_ISA_IO_PROTOCOL
*IsaIo
,
484 IN OUT UINTN
*BufSize
,
491 Status
= EFI_SUCCESS
;
494 if (State
== PS2_READ_BYTE_ONE
) {
496 // Check input for mouse
498 Status
= CheckForInput (IsaIo
);
500 if (EFI_ERROR (Status
)) {
505 while (BytesRead
< *BufSize
) {
507 Status
= WaitOutputFull (IsaIo
, TIMEOUT
);
508 if (EFI_ERROR (Status
)) {
512 IsaIo
->Io
.Read (IsaIo
, EfiIsaIoWidthUint8
, KBC_DATA_PORT
, 1, Buffer
);
515 Buffer
= (UINT8
*) Buffer
+ 1;
518 // Verify the correct number of bytes read
520 if (BytesRead
== 0 || BytesRead
!= *BufSize
) {
521 Status
= EFI_NOT_FOUND
;
524 *BufSize
= BytesRead
;
532 I/O work flow of outing 8042 command.
534 @param IsaIo Pointer to instance of EFI_ISA_IO_PROTOCOL
535 @param Command I/O command.
537 @retval EFI_SUCCESS Success to execute I/O work flow
538 @retval EFI_TIMEOUT Keyboard controller time out.
542 IN EFI_ISA_IO_PROTOCOL
*IsaIo
,
550 // Wait keyboard controller input buffer empty
552 Status
= WaitInputEmpty (IsaIo
, TIMEOUT
);
553 if (EFI_ERROR (Status
)) {
560 IsaIo
->Io
.Write (IsaIo
, EfiIsaIoWidthUint8
, KBC_CMD_STS_PORT
, 1, &Data
);
562 Status
= WaitInputEmpty (IsaIo
, TIMEOUT
);
563 if (EFI_ERROR (Status
)) {
571 I/O work flow of outing 8042 data.
573 @param IsaIo Pointer to instance of EFI_ISA_IO_PROTOCOL
574 @param Data Data value
576 @retval EFI_SUCCESS Success to execute I/O work flow
577 @retval EFI_TIMEOUT Keyboard controller time out.
581 IN EFI_ISA_IO_PROTOCOL
*IsaIo
,
588 // Wait keyboard controller input buffer empty
590 Status
= WaitInputEmpty (IsaIo
, TIMEOUT
);
591 if (EFI_ERROR (Status
)) {
596 IsaIo
->Io
.Write (IsaIo
, EfiIsaIoWidthUint8
, KBC_DATA_PORT
, 1, &Temp
);
598 Status
= WaitInputEmpty (IsaIo
, TIMEOUT
);
599 if (EFI_ERROR (Status
)) {
607 I/O work flow of in 8042 data.
609 @param IsaIo Pointer to instance of EFI_ISA_IO_PROTOCOL
610 @param Data Data value
612 @retval EFI_SUCCESS Success to execute I/O work flow
613 @retval EFI_TIMEOUT Keyboard controller time out.
617 IN EFI_ISA_IO_PROTOCOL
*IsaIo
,
624 Delay
= TIMEOUT
/ 50;
627 IsaIo
->Io
.Read (IsaIo
, EfiIsaIoWidthUint8
, KBC_CMD_STS_PORT
, 1, &Temp
);
630 // Check keyboard controller status bit 0(output buffer status)
632 if ((Temp
& KBC_OUTB
) == KBC_OUTB
) {
638 } while (Delay
!= 0);
644 IsaIo
->Io
.Read (IsaIo
, EfiIsaIoWidthUint8
, KBC_DATA_PORT
, 1, Data
);
650 I/O work flow of outing 8042 Aux command.
652 @param IsaIo Pointer to instance of EFI_ISA_IO_PROTOCOL
653 @param Command Aux I/O command
654 @param Resend Whether need resend the Aux command.
656 @retval EFI_SUCCESS Success to execute I/O work flow
657 @retval EFI_TIMEOUT Keyboard controller time out.
661 IN EFI_ISA_IO_PROTOCOL
*IsaIo
,
670 // Wait keyboard controller input buffer empty
672 Status
= WaitInputEmpty (IsaIo
, TIMEOUT
);
673 if (EFI_ERROR (Status
)) {
677 // Send write to auxiliary device command
679 Data
= WRITE_AUX_DEV
;
680 IsaIo
->Io
.Write (IsaIo
, EfiIsaIoWidthUint8
, KBC_CMD_STS_PORT
, 1, &Data
);
682 Status
= WaitInputEmpty (IsaIo
, TIMEOUT
);
683 if (EFI_ERROR (Status
)) {
687 // Send auxiliary device command
689 IsaIo
->Io
.Write (IsaIo
, EfiIsaIoWidthUint8
, KBC_DATA_PORT
, 1, &Command
);
694 Status
= In8042AuxData (IsaIo
, &Data
);
695 if (EFI_ERROR (Status
)) {
699 if (Data
== PS2_ACK
) {
701 // Receive mouse acknowledge, command send success
709 return EFI_DEVICE_ERROR
;
711 } else if (Data
== PS2_RESEND
) {
715 Status
= Out8042AuxCommand (IsaIo
, Command
, TRUE
);
716 if (EFI_ERROR (Status
)) {
722 // Invalid return code
724 return EFI_DEVICE_ERROR
;
732 I/O work flow of outing 8042 Aux data.
734 @param IsaIo Pointer to instance of EFI_ISA_IO_PROTOCOL
735 @param Data Buffer holding return value
737 @retval EFI_SUCCESS Success to execute I/O work flow.
738 @retval EFI_TIMEOUT Keyboard controller time out.
742 IN EFI_ISA_IO_PROTOCOL
*IsaIo
,
749 // Wait keyboard controller input buffer empty
751 Status
= WaitInputEmpty (IsaIo
, TIMEOUT
);
752 if (EFI_ERROR (Status
)) {
756 // Send write to auxiliary device command
758 Temp
= WRITE_AUX_DEV
;
759 IsaIo
->Io
.Write (IsaIo
, EfiIsaIoWidthUint8
, KBC_CMD_STS_PORT
, 1, &Temp
);
761 Status
= WaitInputEmpty (IsaIo
, TIMEOUT
);
762 if (EFI_ERROR (Status
)) {
767 IsaIo
->Io
.Write (IsaIo
, EfiIsaIoWidthUint8
, KBC_DATA_PORT
, 1, &Temp
);
769 Status
= WaitInputEmpty (IsaIo
, TIMEOUT
);
770 if (EFI_ERROR (Status
)) {
778 I/O work flow of in 8042 Aux data.
780 @param IsaIo Pointer to instance of EFI_ISA_IO_PROTOCOL
781 @param Data Buffer holding return value.
783 @retval EFI_SUCCESS Success to execute I/O work flow
784 @retval EFI_TIMEOUT Keyboard controller time out.
788 IN EFI_ISA_IO_PROTOCOL
*IsaIo
,
795 // wait for output data
797 Status
= WaitOutputFull (IsaIo
, BAT_TIMEOUT
);
798 if (EFI_ERROR (Status
)) {
802 IsaIo
->Io
.Read (IsaIo
, EfiIsaIoWidthUint8
, KBC_DATA_PORT
, 1, Data
);
809 Check keyboard controller status, if it is output buffer full and for auxiliary device.
811 @param IsaIo Pointer to instance of EFI_ISA_IO_PROTOCOL
813 @retval EFI_SUCCESS Keyboard controller is ready
814 @retval EFI_NOT_READY Keyboard controller is not ready
818 IN EFI_ISA_IO_PROTOCOL
*IsaIo
823 IsaIo
->Io
.Read (IsaIo
, EfiIsaIoWidthUint8
, KBC_CMD_STS_PORT
, 1, &Data
);
826 // Check keyboard controller status, if it is output buffer full and for auxiliary device
828 if ((Data
& (KBC_OUTB
| KBC_AUXB
)) != (KBC_OUTB
| KBC_AUXB
)) {
829 return EFI_NOT_READY
;
836 I/O work flow to wait input buffer empty in given time.
838 @param IsaIo Pointer to instance of EFI_ISA_IO_PROTOCOL
839 @param Timeout Wating time.
841 @retval EFI_TIMEOUT if input is still not empty in given time.
842 @retval EFI_SUCCESS input is empty.
846 IN EFI_ISA_IO_PROTOCOL
*IsaIo
,
853 Delay
= Timeout
/ 50;
856 IsaIo
->Io
.Read (IsaIo
, EfiIsaIoWidthUint8
, KBC_CMD_STS_PORT
, 1, &Data
);
859 // Check keyboard controller status bit 1(input buffer status)
861 if ((Data
& KBC_INPB
) == 0) {
867 } while (Delay
!= 0);
877 I/O work flow to wait output buffer full in given time.
879 @param IsaIo Pointer to instance of EFI_ISA_IO_PROTOCOL
880 @param Timeout given time
882 @retval EFI_TIMEOUT output is not full in given time
883 @retval EFI_SUCCESS output is full in given time.
887 IN EFI_ISA_IO_PROTOCOL
*IsaIo
,
894 Delay
= Timeout
/ 50;
897 IsaIo
->Io
.Read (IsaIo
, EfiIsaIoWidthUint8
, KBC_CMD_STS_PORT
, 1, &Data
);
900 // Check keyboard controller status bit 0(output buffer status)
901 // & bit5(output buffer for auxiliary device)
903 if ((Data
& (KBC_OUTB
| KBC_AUXB
)) == (KBC_OUTB
| KBC_AUXB
)) {
909 } while (Delay
!= 0);