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