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