]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2Keyboard.c
MdeModulePkg/Bus/Isa: Fix various typos
[mirror_edk2.git] / MdeModulePkg / Bus / Isa / Ps2KeyboardDxe / Ps2Keyboard.c
1 /** @file
2
3 PS/2 Keyboard driver. Routines that interacts with callers,
4 conforming to EFI driver model
5
6 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
8
9 **/
10
11 #include "Ps2Keyboard.h"
12
13 //
14 // Function prototypes
15 //
16 /**
17 Test controller is a keyboard Controller.
18
19 @param This Pointer of EFI_DRIVER_BINDING_PROTOCOL
20 @param Controller driver's controller
21 @param RemainingDevicePath children device path
22
23 @retval EFI_UNSUPPORTED controller is not floppy disk
24 @retval EFI_SUCCESS controller is floppy disk
25 **/
26 EFI_STATUS
27 EFIAPI
28 KbdControllerDriverSupported (
29 IN EFI_DRIVER_BINDING_PROTOCOL *This,
30 IN EFI_HANDLE Controller,
31 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
32 );
33
34 /**
35 Create KEYBOARD_CONSOLE_IN_DEV instance on controller.
36
37 @param This Pointer of EFI_DRIVER_BINDING_PROTOCOL
38 @param Controller driver controller handle
39 @param RemainingDevicePath Children's device path
40
41 @retval whether success to create floppy control instance.
42 **/
43 EFI_STATUS
44 EFIAPI
45 KbdControllerDriverStart (
46 IN EFI_DRIVER_BINDING_PROTOCOL *This,
47 IN EFI_HANDLE Controller,
48 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
49 );
50
51 /**
52 Stop this driver on ControllerHandle. Support stopping any child handles
53 created by this driver.
54
55 @param This Protocol instance pointer.
56 @param ControllerHandle Handle of device to stop driver on
57 @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of
58 children is zero stop the entire bus driver.
59 @param ChildHandleBuffer List of Child Handles to Stop.
60
61 @retval EFI_SUCCESS This driver is removed ControllerHandle
62 @retval other This driver was not removed from this device
63
64 **/
65 EFI_STATUS
66 EFIAPI
67 KbdControllerDriverStop (
68 IN EFI_DRIVER_BINDING_PROTOCOL *This,
69 IN EFI_HANDLE Controller,
70 IN UINTN NumberOfChildren,
71 IN EFI_HANDLE *ChildHandleBuffer
72 );
73
74 /**
75 Free the waiting key notify list.
76
77 @param ListHead Pointer to list head
78
79 @retval EFI_INVALID_PARAMETER ListHead is NULL
80 @retval EFI_SUCCESS Success to free NotifyList
81 **/
82 EFI_STATUS
83 KbdFreeNotifyList (
84 IN OUT LIST_ENTRY *ListHead
85 );
86
87 //
88 // DriverBinding Protocol Instance
89 //
90 EFI_DRIVER_BINDING_PROTOCOL gKeyboardControllerDriver = {
91 KbdControllerDriverSupported,
92 KbdControllerDriverStart,
93 KbdControllerDriverStop,
94 0xa,
95 NULL,
96 NULL
97 };
98
99 /**
100 Test controller is a keyboard Controller.
101
102 @param This Pointer of EFI_DRIVER_BINDING_PROTOCOL
103 @param Controller driver's controller
104 @param RemainingDevicePath children device path
105
106 @retval EFI_UNSUPPORTED controller is not floppy disk
107 @retval EFI_SUCCESS controller is floppy disk
108 **/
109 EFI_STATUS
110 EFIAPI
111 KbdControllerDriverSupported (
112 IN EFI_DRIVER_BINDING_PROTOCOL *This,
113 IN EFI_HANDLE Controller,
114 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
115 )
116 {
117 EFI_STATUS Status;
118 EFI_SIO_PROTOCOL *Sio;
119 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
120 ACPI_HID_DEVICE_PATH *Acpi;
121
122 //
123 // Check whether the controller is keyboard.
124 //
125 Status = gBS->OpenProtocol (
126 Controller,
127 &gEfiDevicePathProtocolGuid,
128 (VOID **) &DevicePath,
129 This->DriverBindingHandle,
130 Controller,
131 EFI_OPEN_PROTOCOL_GET_PROTOCOL
132 );
133 if (EFI_ERROR (Status)) {
134 return Status;
135 }
136
137 do {
138 Acpi = (ACPI_HID_DEVICE_PATH *) DevicePath;
139 DevicePath = NextDevicePathNode (DevicePath);
140 } while (!IsDevicePathEnd (DevicePath));
141
142 if (DevicePathType (Acpi) != ACPI_DEVICE_PATH ||
143 (DevicePathSubType (Acpi) != ACPI_DP && DevicePathSubType (Acpi) != ACPI_EXTENDED_DP)) {
144 return EFI_UNSUPPORTED;
145 }
146
147 if (Acpi->HID != EISA_PNP_ID (0x303) || Acpi->UID != 0) {
148 return EFI_UNSUPPORTED;
149 }
150
151 //
152 // Open the IO Abstraction(s) needed to perform the supported test
153 //
154 Status = gBS->OpenProtocol (
155 Controller,
156 &gEfiSioProtocolGuid,
157 (VOID **) &Sio,
158 This->DriverBindingHandle,
159 Controller,
160 EFI_OPEN_PROTOCOL_BY_DRIVER
161 );
162 if (EFI_ERROR (Status)) {
163 return Status;
164 }
165
166 //
167 // Close the I/O Abstraction(s) used to perform the supported test
168 //
169 gBS->CloseProtocol (
170 Controller,
171 &gEfiSioProtocolGuid,
172 This->DriverBindingHandle,
173 Controller
174 );
175
176 return Status;
177 }
178
179 /**
180 Create KEYBOARD_CONSOLE_IN_DEV instance on controller.
181
182 @param This Pointer of EFI_DRIVER_BINDING_PROTOCOL
183 @param Controller driver controller handle
184 @param RemainingDevicePath Children's device path
185
186 @retval whether success to create floppy control instance.
187 **/
188 EFI_STATUS
189 EFIAPI
190 KbdControllerDriverStart (
191 IN EFI_DRIVER_BINDING_PROTOCOL *This,
192 IN EFI_HANDLE Controller,
193 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
194 )
195 {
196 EFI_STATUS Status;
197 EFI_STATUS Status1;
198 EFI_SIO_PROTOCOL *Sio;
199 KEYBOARD_CONSOLE_IN_DEV *ConsoleIn;
200 UINT8 Data;
201 EFI_STATUS_CODE_VALUE StatusCode;
202 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
203
204 StatusCode = 0;
205
206 Status = gBS->OpenProtocol (
207 Controller,
208 &gEfiDevicePathProtocolGuid,
209 (VOID **) &DevicePath,
210 This->DriverBindingHandle,
211 Controller,
212 EFI_OPEN_PROTOCOL_GET_PROTOCOL
213 );
214 if (EFI_ERROR (Status)) {
215 return Status;
216 }
217 //
218 // Report that the keyboard is being enabled
219 //
220 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
221 EFI_PROGRESS_CODE,
222 EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_ENABLE,
223 DevicePath
224 );
225
226 //
227 // Get the ISA I/O Protocol on Controller's handle
228 //
229 Status = gBS->OpenProtocol (
230 Controller,
231 &gEfiSioProtocolGuid,
232 (VOID **) &Sio,
233 This->DriverBindingHandle,
234 Controller,
235 EFI_OPEN_PROTOCOL_BY_DRIVER
236 );
237 if (EFI_ERROR (Status)) {
238 return Status;
239 }
240 //
241 // Allocate private data
242 //
243 ConsoleIn = AllocateZeroPool (sizeof (KEYBOARD_CONSOLE_IN_DEV));
244 if (ConsoleIn == NULL) {
245 Status = EFI_OUT_OF_RESOURCES;
246 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR;
247 goto ErrorExit;
248 }
249 //
250 // Setup the device instance
251 //
252 ConsoleIn->Signature = KEYBOARD_CONSOLE_IN_DEV_SIGNATURE;
253 ConsoleIn->Handle = Controller;
254 (ConsoleIn->ConIn).Reset = KeyboardEfiReset;
255 (ConsoleIn->ConIn).ReadKeyStroke = KeyboardReadKeyStroke;
256 ConsoleIn->DataRegisterAddress = KEYBOARD_8042_DATA_REGISTER;
257 ConsoleIn->StatusRegisterAddress = KEYBOARD_8042_STATUS_REGISTER;
258 ConsoleIn->CommandRegisterAddress = KEYBOARD_8042_COMMAND_REGISTER;
259 ConsoleIn->DevicePath = DevicePath;
260
261 ConsoleIn->ConInEx.Reset = KeyboardEfiResetEx;
262 ConsoleIn->ConInEx.ReadKeyStrokeEx = KeyboardReadKeyStrokeEx;
263 ConsoleIn->ConInEx.SetState = KeyboardSetState;
264 ConsoleIn->ConInEx.RegisterKeyNotify = KeyboardRegisterKeyNotify;
265 ConsoleIn->ConInEx.UnregisterKeyNotify = KeyboardUnregisterKeyNotify;
266
267 InitializeListHead (&ConsoleIn->NotifyList);
268
269 //
270 // Fix for random hangs in System waiting for the Key if no KBC is present in BIOS.
271 // When KBC decode (IO port 0x60/0x64 decode) is not enabled,
272 // KeyboardRead will read back as 0xFF and return status is EFI_SUCCESS.
273 // So instead we read status register to detect after read if KBC decode is enabled.
274 //
275
276 //
277 // Return code is ignored on purpose.
278 //
279 if (!PcdGetBool (PcdFastPS2Detection)) {
280 KeyboardRead (ConsoleIn, &Data);
281 if ((KeyReadStatusRegister (ConsoleIn) & (KBC_PARE | KBC_TIM)) == (KBC_PARE | KBC_TIM)) {
282 //
283 // If nobody decodes KBC I/O port, it will read back as 0xFF.
284 // Check the Time-Out and Parity bit to see if it has an active KBC in system
285 //
286 Status = EFI_DEVICE_ERROR;
287 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_NOT_DETECTED;
288 goto ErrorExit;
289 }
290 }
291
292 //
293 // Setup the WaitForKey event
294 //
295 Status = gBS->CreateEvent (
296 EVT_NOTIFY_WAIT,
297 TPL_NOTIFY,
298 KeyboardWaitForKey,
299 ConsoleIn,
300 &((ConsoleIn->ConIn).WaitForKey)
301 );
302 if (EFI_ERROR (Status)) {
303 Status = EFI_OUT_OF_RESOURCES;
304 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR;
305 goto ErrorExit;
306 }
307 //
308 // Setup the WaitForKeyEx event
309 //
310 Status = gBS->CreateEvent (
311 EVT_NOTIFY_WAIT,
312 TPL_NOTIFY,
313 KeyboardWaitForKeyEx,
314 ConsoleIn,
315 &(ConsoleIn->ConInEx.WaitForKeyEx)
316 );
317 if (EFI_ERROR (Status)) {
318 Status = EFI_OUT_OF_RESOURCES;
319 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR;
320 goto ErrorExit;
321 }
322 // Setup a periodic timer, used for reading keystrokes at a fixed interval
323 //
324 Status = gBS->CreateEvent (
325 EVT_TIMER | EVT_NOTIFY_SIGNAL,
326 TPL_NOTIFY,
327 KeyboardTimerHandler,
328 ConsoleIn,
329 &ConsoleIn->TimerEvent
330 );
331 if (EFI_ERROR (Status)) {
332 Status = EFI_OUT_OF_RESOURCES;
333 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR;
334 goto ErrorExit;
335 }
336
337 Status = gBS->SetTimer (
338 ConsoleIn->TimerEvent,
339 TimerPeriodic,
340 KEYBOARD_TIMER_INTERVAL
341 );
342 if (EFI_ERROR (Status)) {
343 Status = EFI_OUT_OF_RESOURCES;
344 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR;
345 goto ErrorExit;
346 }
347
348 Status = gBS->CreateEvent (
349 EVT_NOTIFY_SIGNAL,
350 TPL_CALLBACK,
351 KeyNotifyProcessHandler,
352 ConsoleIn,
353 &ConsoleIn->KeyNotifyProcessEvent
354 );
355 if (EFI_ERROR (Status)) {
356 Status = EFI_OUT_OF_RESOURCES;
357 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR;
358 goto ErrorExit;
359 }
360
361 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
362 EFI_PROGRESS_CODE,
363 EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_PRESENCE_DETECT,
364 DevicePath
365 );
366
367 //
368 // Reset the keyboard device
369 //
370 Status = ConsoleIn->ConInEx.Reset (&ConsoleIn->ConInEx, FeaturePcdGet (PcdPs2KbdExtendedVerification));
371 if (EFI_ERROR (Status)) {
372 Status = EFI_DEVICE_ERROR;
373 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_NOT_DETECTED;
374 goto ErrorExit;
375 }
376
377 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
378 EFI_PROGRESS_CODE,
379 EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_DETECTED,
380 DevicePath
381 );
382
383 ConsoleIn->ControllerNameTable = NULL;
384 AddUnicodeString2 (
385 "eng",
386 gPs2KeyboardComponentName.SupportedLanguages,
387 &ConsoleIn->ControllerNameTable,
388 L"PS/2 Keyboard Device",
389 TRUE
390 );
391 AddUnicodeString2 (
392 "en",
393 gPs2KeyboardComponentName2.SupportedLanguages,
394 &ConsoleIn->ControllerNameTable,
395 L"PS/2 Keyboard Device",
396 FALSE
397 );
398
399
400 //
401 // Install protocol interfaces for the keyboard device.
402 //
403 Status = gBS->InstallMultipleProtocolInterfaces (
404 &Controller,
405 &gEfiSimpleTextInProtocolGuid,
406 &ConsoleIn->ConIn,
407 &gEfiSimpleTextInputExProtocolGuid,
408 &ConsoleIn->ConInEx,
409 NULL
410 );
411 if (EFI_ERROR (Status)) {
412 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR;
413 goto ErrorExit;
414 }
415
416 return Status;
417
418 ErrorExit:
419 //
420 // Report error code
421 //
422 if (StatusCode != 0) {
423 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
424 EFI_ERROR_CODE | EFI_ERROR_MINOR,
425 StatusCode,
426 DevicePath
427 );
428 }
429
430 if ((ConsoleIn != NULL) && (ConsoleIn->ConIn.WaitForKey != NULL)) {
431 gBS->CloseEvent (ConsoleIn->ConIn.WaitForKey);
432 }
433
434 if ((ConsoleIn != NULL) && (ConsoleIn->TimerEvent != NULL)) {
435 gBS->CloseEvent (ConsoleIn->TimerEvent);
436 }
437 if ((ConsoleIn != NULL) && (ConsoleIn->ConInEx.WaitForKeyEx != NULL)) {
438 gBS->CloseEvent (ConsoleIn->ConInEx.WaitForKeyEx);
439 }
440 if ((ConsoleIn != NULL) && (ConsoleIn->KeyNotifyProcessEvent != NULL)) {
441 gBS->CloseEvent (ConsoleIn->KeyNotifyProcessEvent);
442 }
443 KbdFreeNotifyList (&ConsoleIn->NotifyList);
444 if ((ConsoleIn != NULL) && (ConsoleIn->ControllerNameTable != NULL)) {
445 FreeUnicodeStringTable (ConsoleIn->ControllerNameTable);
446 }
447 //
448 // Since there will be no timer handler for keyboard input any more,
449 // exhaust input data just in case there is still keyboard data left
450 //
451 if (ConsoleIn != NULL) {
452 Status1 = EFI_SUCCESS;
453 while (!EFI_ERROR (Status1) && (Status != EFI_DEVICE_ERROR)) {
454 Status1 = KeyboardRead (ConsoleIn, &Data);;
455 }
456 }
457
458 if (ConsoleIn != NULL) {
459 gBS->FreePool (ConsoleIn);
460 }
461
462 gBS->CloseProtocol (
463 Controller,
464 &gEfiSioProtocolGuid,
465 This->DriverBindingHandle,
466 Controller
467 );
468
469 return Status;
470 }
471
472 /**
473 Stop this driver on ControllerHandle. Support stopping any child handles
474 created by this driver.
475
476 @param This Protocol instance pointer.
477 @param ControllerHandle Handle of device to stop driver on
478 @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of
479 children is zero stop the entire bus driver.
480 @param ChildHandleBuffer List of Child Handles to Stop.
481
482 @retval EFI_SUCCESS This driver is removed ControllerHandle
483 @retval other This driver was not removed from this device
484
485 **/
486 EFI_STATUS
487 EFIAPI
488 KbdControllerDriverStop (
489 IN EFI_DRIVER_BINDING_PROTOCOL *This,
490 IN EFI_HANDLE Controller,
491 IN UINTN NumberOfChildren,
492 IN EFI_HANDLE *ChildHandleBuffer
493 )
494 {
495 EFI_STATUS Status;
496 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn;
497 KEYBOARD_CONSOLE_IN_DEV *ConsoleIn;
498 UINT8 Data;
499
500 //
501 // Disable Keyboard
502 //
503 Status = gBS->OpenProtocol (
504 Controller,
505 &gEfiSimpleTextInProtocolGuid,
506 (VOID **) &ConIn,
507 This->DriverBindingHandle,
508 Controller,
509 EFI_OPEN_PROTOCOL_GET_PROTOCOL
510 );
511 if (EFI_ERROR (Status)) {
512 return Status;
513 }
514 Status = gBS->OpenProtocol (
515 Controller,
516 &gEfiSimpleTextInputExProtocolGuid,
517 NULL,
518 This->DriverBindingHandle,
519 Controller,
520 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
521 );
522 if (EFI_ERROR (Status)) {
523 return Status;
524 }
525
526 ConsoleIn = KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (ConIn);
527
528 //
529 // Report that the keyboard is being disabled
530 //
531 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
532 EFI_PROGRESS_CODE,
533 EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_DISABLE,
534 ConsoleIn->DevicePath
535 );
536
537 if (ConsoleIn->TimerEvent != NULL) {
538 gBS->CloseEvent (ConsoleIn->TimerEvent);
539 ConsoleIn->TimerEvent = NULL;
540 }
541
542 //
543 // Since there will be no timer handler for keyboard input any more,
544 // exhaust input data just in case there is still keyboard data left
545 //
546 Status = EFI_SUCCESS;
547 while (!EFI_ERROR (Status)) {
548 Status = KeyboardRead (ConsoleIn, &Data);;
549 }
550 //
551 // Uninstall the SimpleTextIn and SimpleTextInEx protocols
552 //
553 Status = gBS->UninstallMultipleProtocolInterfaces (
554 Controller,
555 &gEfiSimpleTextInProtocolGuid,
556 &ConsoleIn->ConIn,
557 &gEfiSimpleTextInputExProtocolGuid,
558 &ConsoleIn->ConInEx,
559 NULL
560 );
561 if (EFI_ERROR (Status)) {
562 return Status;
563 }
564
565 gBS->CloseProtocol (
566 Controller,
567 &gEfiSioProtocolGuid,
568 This->DriverBindingHandle,
569 Controller
570 );
571
572 //
573 // Free other resources
574 //
575 if ((ConsoleIn->ConIn).WaitForKey != NULL) {
576 gBS->CloseEvent ((ConsoleIn->ConIn).WaitForKey);
577 (ConsoleIn->ConIn).WaitForKey = NULL;
578 }
579 if (ConsoleIn->ConInEx.WaitForKeyEx != NULL) {
580 gBS->CloseEvent (ConsoleIn->ConInEx.WaitForKeyEx);
581 ConsoleIn->ConInEx.WaitForKeyEx = NULL;
582 }
583 if (ConsoleIn->KeyNotifyProcessEvent != NULL) {
584 gBS->CloseEvent (ConsoleIn->KeyNotifyProcessEvent);
585 ConsoleIn->KeyNotifyProcessEvent = NULL;
586 }
587 KbdFreeNotifyList (&ConsoleIn->NotifyList);
588 FreeUnicodeStringTable (ConsoleIn->ControllerNameTable);
589 gBS->FreePool (ConsoleIn);
590
591 return EFI_SUCCESS;
592 }
593
594 /**
595 Free the waiting key notify list.
596
597 @param ListHead Pointer to list head
598
599 @retval EFI_INVALID_PARAMETER ListHead is NULL
600 @retval EFI_SUCCESS Success to free NotifyList
601 **/
602 EFI_STATUS
603 KbdFreeNotifyList (
604 IN OUT LIST_ENTRY *ListHead
605 )
606 {
607 KEYBOARD_CONSOLE_IN_EX_NOTIFY *NotifyNode;
608
609 if (ListHead == NULL) {
610 return EFI_INVALID_PARAMETER;
611 }
612 while (!IsListEmpty (ListHead)) {
613 NotifyNode = CR (
614 ListHead->ForwardLink,
615 KEYBOARD_CONSOLE_IN_EX_NOTIFY,
616 NotifyEntry,
617 KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE
618 );
619 RemoveEntryList (ListHead->ForwardLink);
620 gBS->FreePool (NotifyNode);
621 }
622
623 return EFI_SUCCESS;
624 }
625
626 /**
627 The module Entry Point for module Ps2Keyboard.
628
629 @param[in] ImageHandle The firmware allocated handle for the EFI image.
630 @param[in] SystemTable A pointer to the EFI System Table.
631
632 @retval EFI_SUCCESS The entry point is executed successfully.
633 @retval other Some error occurs when executing this entry point.
634
635 **/
636 EFI_STATUS
637 EFIAPI
638 InitializePs2Keyboard(
639 IN EFI_HANDLE ImageHandle,
640 IN EFI_SYSTEM_TABLE *SystemTable
641 )
642 {
643 EFI_STATUS Status;
644
645 //
646 // Install driver model protocol(s).
647 //
648 Status = EfiLibInstallDriverBindingComponentName2 (
649 ImageHandle,
650 SystemTable,
651 &gKeyboardControllerDriver,
652 ImageHandle,
653 &gPs2KeyboardComponentName,
654 &gPs2KeyboardComponentName2
655 );
656 ASSERT_EFI_ERROR (Status);
657
658
659 return Status;
660 }
661