1 /** @file
2   PS2 Mouse Communication Interface.
3 
4 Copyright (c) 2006 - 2007, 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 "Ps2MouseAbsolutePointer.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 MouseAbsolutePointerDev  Pointer to PS2 Absolute Pointer Simulation Device 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_ABSOLUTE_POINTER_DEV * MouseAbsolutePointerDev)351 PS2MouseGetPacket (
352   PS2_MOUSE_ABSOLUTE_POINTER_DEV     *MouseAbsolutePointerDev
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 (MouseAbsolutePointerDev->IsaIo);
382       Status = PS2MouseRead (MouseAbsolutePointerDev->IsaIo, &Data, &Count, State);
383       if (EFI_ERROR (Status)) {
384         KbcEnableAux (MouseAbsolutePointerDev->IsaIo);
385         return EFI_NOT_READY;
386       }
387 
388       if (Count != 1) {
389         KbcEnableAux (MouseAbsolutePointerDev->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 (MouseAbsolutePointerDev->IsaIo, &KeyboardEnable);
398         KbcDisableKb (MouseAbsolutePointerDev->IsaIo);
399         KbcEnableAux (MouseAbsolutePointerDev->IsaIo);
400       }
401       break;
402 
403     case PS2_READ_DATA_BYTE:
404       Count   = 2;
405       Status  = PS2MouseRead (MouseAbsolutePointerDev->IsaIo, (Packet + 1), &Count, State);
406       if (EFI_ERROR (Status)) {
407         if (KeyboardEnable) {
408           KbcEnableKb (MouseAbsolutePointerDev->IsaIo);
409         }
410 
411         return EFI_NOT_READY;
412       }
413 
414       if (Count != 2) {
415         if (KeyboardEnable) {
416           KbcEnableKb (MouseAbsolutePointerDev->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 (MouseAbsolutePointerDev->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       MouseAbsolutePointerDev->State.CurrentX += RelativeMovementX;
466       MouseAbsolutePointerDev->State.CurrentY -= RelativeMovementY;
467       MouseAbsolutePointerDev->State.CurrentZ = 0;
468       MouseAbsolutePointerDev->State.ActiveButtons = (UINT8) (LButton || RButton) & 0x3;
469       MouseAbsolutePointerDev->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 //
535 // 8042 I/O function
536 //
537 /**
538   I/O work flow of outing 8042 command.
539 
540   @param IsaIo   Pointer to instance of EFI_ISA_IO_PROTOCOL
541   @param Command I/O command.
542 
543   @retval EFI_SUCCESS Success to excute I/O work flow
544   @retval EFI_TIMEOUT Keyboard controller time out.
545 **/
546 EFI_STATUS
Out8042Command(IN EFI_ISA_IO_PROTOCOL * IsaIo,IN UINT8 Command)547 Out8042Command (
548   IN EFI_ISA_IO_PROTOCOL                  *IsaIo,
549   IN UINT8                                Command
550   )
551 {
552   EFI_STATUS  Status;
553   UINT8       Data;
554 
555   //
556   // Wait keyboard controller input buffer empty
557   //
558   Status = WaitInputEmpty (IsaIo, TIMEOUT);
559   if (EFI_ERROR (Status)) {
560     return Status;
561   }
562   //
563   // Send command
564   //
565   Data = Command;
566   IsaIo->Io.Write (IsaIo, EfiIsaIoWidthUint8, KBC_CMD_STS_PORT, 1, &Data);
567 
568   Status = WaitInputEmpty (IsaIo, TIMEOUT);
569   if (EFI_ERROR (Status)) {
570     return Status;
571   }
572 
573   return EFI_SUCCESS;
574 }
575 
576 /**
577   I/O work flow of outing 8042 data.
578 
579   @param IsaIo   Pointer to instance of EFI_ISA_IO_PROTOCOL
580   @param Data    Data value
581 
582   @retval EFI_SUCCESS Success to excute I/O work flow
583   @retval EFI_TIMEOUT Keyboard controller time out.
584 **/
585 EFI_STATUS
Out8042Data(IN EFI_ISA_IO_PROTOCOL * IsaIo,IN UINT8 Data)586 Out8042Data (
587   IN EFI_ISA_IO_PROTOCOL                  *IsaIo,
588   IN UINT8                                Data
589   )
590 {
591   EFI_STATUS  Status;
592   UINT8       Temp;
593   //
594   // Wait keyboard controller input buffer empty
595   //
596   Status = WaitInputEmpty (IsaIo, TIMEOUT);
597   if (EFI_ERROR (Status)) {
598     return Status;
599   }
600 
601   Temp = Data;
602   IsaIo->Io.Write (IsaIo, EfiIsaIoWidthUint8, KBC_DATA_PORT, 1, &Temp);
603 
604   Status = WaitInputEmpty (IsaIo, TIMEOUT);
605   if (EFI_ERROR (Status)) {
606     return Status;
607   }
608 
609   return EFI_SUCCESS;
610 }
611 
612 /**
613   I/O work flow of in 8042 data.
614 
615   @param IsaIo   Pointer to instance of EFI_ISA_IO_PROTOCOL
616   @param Data    Data value
617 
618   @retval EFI_SUCCESS Success to excute I/O work flow
619   @retval EFI_TIMEOUT Keyboard controller time out.
620 **/
621 EFI_STATUS
In8042Data(IN EFI_ISA_IO_PROTOCOL * IsaIo,IN OUT UINT8 * Data)622 In8042Data (
623   IN EFI_ISA_IO_PROTOCOL                  *IsaIo,
624   IN OUT UINT8                            *Data
625   )
626 {
627   UINTN Delay;
628   UINT8 Temp;
629 
630   Delay = TIMEOUT / 50;
631 
632   do {
633     IsaIo->Io.Read (IsaIo, EfiIsaIoWidthUint8, KBC_CMD_STS_PORT, 1, &Temp);
634 
635     //
636     // Check keyboard controller status bit 0(output buffer status)
637     //
638     if ((Temp & KBC_OUTB) == KBC_OUTB) {
639       break;
640     }
641 
642     gBS->Stall (50);
643     Delay--;
644   } while (Delay != 0);
645 
646   if (Delay == 0) {
647     return EFI_TIMEOUT;
648   }
649 
650   IsaIo->Io.Read (IsaIo, EfiIsaIoWidthUint8, KBC_DATA_PORT, 1, Data);
651 
652   return EFI_SUCCESS;
653 }
654 
655 /**
656   I/O work flow of outing 8042 Aux command.
657 
658   @param IsaIo   Pointer to instance of EFI_ISA_IO_PROTOCOL
659   @param Command Aux I/O command
660   @param Resend  Whether need resend the Aux command.
661 
662   @retval EFI_SUCCESS Success to excute I/O work flow
663   @retval EFI_TIMEOUT Keyboard controller time out.
664 **/
665 EFI_STATUS
Out8042AuxCommand(IN EFI_ISA_IO_PROTOCOL * IsaIo,IN UINT8 Command,IN BOOLEAN Resend)666 Out8042AuxCommand (
667   IN EFI_ISA_IO_PROTOCOL                  *IsaIo,
668   IN UINT8                                Command,
669   IN BOOLEAN                              Resend
670   )
671 {
672   EFI_STATUS  Status;
673   UINT8       Data;
674 
675   //
676   // Wait keyboard controller input buffer empty
677   //
678   Status = WaitInputEmpty (IsaIo, TIMEOUT);
679   if (EFI_ERROR (Status)) {
680     return Status;
681   }
682   //
683   // Send write to auxiliary device command
684   //
685   Data = WRITE_AUX_DEV;
686   IsaIo->Io.Write (IsaIo, EfiIsaIoWidthUint8, KBC_CMD_STS_PORT, 1, &Data);
687 
688   Status = WaitInputEmpty (IsaIo, TIMEOUT);
689   if (EFI_ERROR (Status)) {
690     return Status;
691   }
692   //
693   // Send auxiliary device command
694   //
695   IsaIo->Io.Write (IsaIo, EfiIsaIoWidthUint8, KBC_DATA_PORT, 1, &Command);
696 
697   //
698   // Read return code
699   //
700   Status = In8042AuxData (IsaIo, &Data);
701   if (EFI_ERROR (Status)) {
702     return Status;
703   }
704 
705   if (Data == PS2_ACK) {
706     //
707     // Receive mouse acknowledge, command send success
708     //
709     return EFI_SUCCESS;
710 
711   } else if (Resend) {
712     //
713     // Resend fail
714     //
715     return EFI_DEVICE_ERROR;
716 
717   } else if (Data == PS2_RESEND) {
718     //
719     // Resend command
720     //
721     Status = Out8042AuxCommand (IsaIo, Command, TRUE);
722     if (EFI_ERROR (Status)) {
723       return Status;
724     }
725 
726   } else {
727     //
728     // Invalid return code
729     //
730     return EFI_DEVICE_ERROR;
731 
732   }
733 
734   return EFI_SUCCESS;
735 }
736 
737 /**
738   I/O work flow of outing 8042 Aux data.
739 
740   @param IsaIo   Pointer to instance of EFI_ISA_IO_PROTOCOL
741   @param Data    Buffer holding return value
742 
743   @retval EFI_SUCCESS Success to excute I/O work flow.
744   @retval EFI_TIMEOUT Keyboard controller time out.
745 **/
746 EFI_STATUS
Out8042AuxData(IN EFI_ISA_IO_PROTOCOL * IsaIo,IN UINT8 Data)747 Out8042AuxData (
748   IN EFI_ISA_IO_PROTOCOL                  *IsaIo,
749   IN UINT8                                Data
750   )
751 {
752   EFI_STATUS  Status;
753   UINT8       Temp;
754   //
755   // Wait keyboard controller input buffer empty
756   //
757   Status = WaitInputEmpty (IsaIo, TIMEOUT);
758   if (EFI_ERROR (Status)) {
759     return Status;
760   }
761   //
762   // Send write to auxiliary device command
763   //
764   Temp = WRITE_AUX_DEV;
765   IsaIo->Io.Write (IsaIo, EfiIsaIoWidthUint8, KBC_CMD_STS_PORT, 1, &Temp);
766 
767   Status = WaitInputEmpty (IsaIo, TIMEOUT);
768   if (EFI_ERROR (Status)) {
769     return Status;
770   }
771 
772   Temp = Data;
773   IsaIo->Io.Write (IsaIo, EfiIsaIoWidthUint8, KBC_DATA_PORT, 1, &Temp);
774 
775   Status = WaitInputEmpty (IsaIo, TIMEOUT);
776   if (EFI_ERROR (Status)) {
777     return Status;
778   }
779 
780   return EFI_SUCCESS;
781 }
782 
783 /**
784   I/O work flow of in 8042 Aux data.
785 
786   @param IsaIo   Pointer to instance of EFI_ISA_IO_PROTOCOL
787   @param Data    Buffer holding return value.
788 
789   @retval EFI_SUCCESS Success to excute I/O work flow
790   @retval EFI_TIMEOUT Keyboard controller time out.
791 **/
792 EFI_STATUS
In8042AuxData(IN EFI_ISA_IO_PROTOCOL * IsaIo,IN OUT UINT8 * Data)793 In8042AuxData (
794   IN EFI_ISA_IO_PROTOCOL                  *IsaIo,
795   IN OUT UINT8                            *Data
796   )
797 {
798   EFI_STATUS  Status;
799 
800   //
801   // wait for output data
802   //
803   Status = WaitOutputFull (IsaIo, BAT_TIMEOUT);
804   if (EFI_ERROR (Status)) {
805     return Status;
806   }
807 
808   IsaIo->Io.Read (IsaIo, EfiIsaIoWidthUint8, KBC_DATA_PORT, 1, Data);
809 
810   return EFI_SUCCESS;
811 }
812 
813 
814 /**
815   Check keyboard controller status, if it is output buffer full and for auxiliary device.
816 
817   @param IsaIo   Pointer to instance of EFI_ISA_IO_PROTOCOL
818 
819   @retval EFI_SUCCESS   Keyboard controller is ready
820   @retval EFI_NOT_READY Keyboard controller is not ready
821 **/
822 EFI_STATUS
CheckForInput(IN EFI_ISA_IO_PROTOCOL * IsaIo)823 CheckForInput (
824   IN EFI_ISA_IO_PROTOCOL                  *IsaIo
825   )
826 {
827   UINT8 Data;
828 
829   IsaIo->Io.Read (IsaIo, EfiIsaIoWidthUint8, KBC_CMD_STS_PORT, 1, &Data);
830 
831   //
832   // Check keyboard controller status, if it is output buffer full and for auxiliary device
833   //
834   if ((Data & (KBC_OUTB | KBC_AUXB)) != (KBC_OUTB | KBC_AUXB)) {
835     return EFI_NOT_READY;
836   }
837 
838   return EFI_SUCCESS;
839 }
840 
841 /**
842   I/O work flow to wait input buffer empty in given time.
843 
844   @param IsaIo   Pointer to instance of EFI_ISA_IO_PROTOCOL
845   @param Timeout Wating time.
846 
847   @retval EFI_TIMEOUT if input is still not empty in given time.
848   @retval EFI_SUCCESS input is empty.
849 **/
850 EFI_STATUS
WaitInputEmpty(IN EFI_ISA_IO_PROTOCOL * IsaIo,IN UINTN Timeout)851 WaitInputEmpty (
852   IN EFI_ISA_IO_PROTOCOL                  *IsaIo,
853   IN UINTN                                Timeout
854   )
855 {
856   UINTN Delay;
857   UINT8 Data;
858 
859   Delay = Timeout / 50;
860 
861   do {
862     IsaIo->Io.Read (IsaIo, EfiIsaIoWidthUint8, KBC_CMD_STS_PORT, 1, &Data);
863 
864     //
865     // Check keyboard controller status bit 1(input buffer status)
866     //
867     if ((Data & KBC_INPB) == 0) {
868       break;
869     }
870 
871     gBS->Stall (50);
872     Delay--;
873   } while (Delay != 0);
874 
875   if (Delay == 0) {
876     return EFI_TIMEOUT;
877   }
878 
879   return EFI_SUCCESS;
880 }
881 
882 /**
883   I/O work flow to wait output buffer full in given time.
884 
885   @param IsaIo   Pointer to instance of EFI_ISA_IO_PROTOCOL
886   @param Timeout given time
887 
888   @retval EFI_TIMEOUT  output is not full in given time
889   @retval EFI_SUCCESS  output is full in given time.
890 **/
891 EFI_STATUS
WaitOutputFull(IN EFI_ISA_IO_PROTOCOL * IsaIo,IN UINTN Timeout)892 WaitOutputFull (
893   IN EFI_ISA_IO_PROTOCOL                  *IsaIo,
894   IN UINTN                                Timeout
895   )
896 {
897   UINTN Delay;
898   UINT8 Data;
899 
900   Delay = Timeout / 50;
901 
902   do {
903     IsaIo->Io.Read (IsaIo, EfiIsaIoWidthUint8, KBC_CMD_STS_PORT, 1, &Data);
904 
905     //
906     // Check keyboard controller status bit 0(output buffer status)
907     //  & bit5(output buffer for auxiliary device)
908     //
909     if ((Data & (KBC_OUTB | KBC_AUXB)) == (KBC_OUTB | KBC_AUXB)) {
910       break;
911     }
912 
913     gBS->Stall (50);
914     Delay--;
915   } while (Delay != 0);
916 
917   if (Delay == 0) {
918     return EFI_TIMEOUT;
919   }
920 
921   return EFI_SUCCESS;
922 }
923 
924