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