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