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