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