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