428758fd99f8e4f7b533ac3de0fdd5342c3a74e7
[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 - 2010, 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->ScancodeBufStartPos = 0;
247 ConsoleIn->ScancodeBufEndPos = KEYBOARD_BUFFER_MAX_COUNT - 1;
248 ConsoleIn->ScancodeBufCount = 0;
249 ConsoleIn->Ctrled = FALSE;
250 ConsoleIn->Alted = FALSE;
251 ConsoleIn->DevicePath = ParentDevicePath;
252
253 ConsoleIn->ConInEx.Reset = KeyboardEfiResetEx;
254 ConsoleIn->ConInEx.ReadKeyStrokeEx = KeyboardReadKeyStrokeEx;
255 ConsoleIn->ConInEx.SetState = KeyboardSetState;
256 ConsoleIn->ConInEx.RegisterKeyNotify = KeyboardRegisterKeyNotify;
257 ConsoleIn->ConInEx.UnregisterKeyNotify = KeyboardUnregisterKeyNotify;
258
259 InitializeListHead (&ConsoleIn->NotifyList);
260
261 //
262 // Fix for random hangs in System waiting for the Key if no KBC is present in BIOS.
263 //
264 KeyboardRead (ConsoleIn, &Data);
265 if ((KeyReadStatusRegister (ConsoleIn) & (KBC_PARE | KBC_TIM)) == (KBC_PARE | KBC_TIM)) {
266 //
267 // If nobody decodes KBC I/O port, it will read back as 0xFF.
268 // Check the Time-Out and Parity bit to see if it has an active KBC in system
269 //
270 Status = EFI_DEVICE_ERROR;
271 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_NOT_DETECTED;
272 goto ErrorExit;
273 }
274
275 //
276 // Setup the WaitForKey event
277 //
278 Status = gBS->CreateEvent (
279 EVT_NOTIFY_WAIT,
280 TPL_NOTIFY,
281 KeyboardWaitForKey,
282 &(ConsoleIn->ConIn),
283 &((ConsoleIn->ConIn).WaitForKey)
284 );
285 if (EFI_ERROR (Status)) {
286 Status = EFI_OUT_OF_RESOURCES;
287 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR;
288 goto ErrorExit;
289 }
290 //
291 // Setup the WaitForKeyEx event
292 //
293 Status = gBS->CreateEvent (
294 EVT_NOTIFY_WAIT,
295 TPL_NOTIFY,
296 KeyboardWaitForKeyEx,
297 &(ConsoleIn->ConInEx),
298 &(ConsoleIn->ConInEx.WaitForKeyEx)
299 );
300 if (EFI_ERROR (Status)) {
301 Status = EFI_OUT_OF_RESOURCES;
302 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR;
303 goto ErrorExit;
304 }
305 // Setup a periodic timer, used for reading keystrokes at a fixed interval
306 //
307 Status = gBS->CreateEvent (
308 EVT_TIMER | EVT_NOTIFY_SIGNAL,
309 TPL_NOTIFY,
310 KeyboardTimerHandler,
311 ConsoleIn,
312 &ConsoleIn->TimerEvent
313 );
314 if (EFI_ERROR (Status)) {
315 Status = EFI_OUT_OF_RESOURCES;
316 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR;
317 goto ErrorExit;
318 }
319
320 Status = gBS->SetTimer (
321 ConsoleIn->TimerEvent,
322 TimerPeriodic,
323 KEYBOARD_TIMER_INTERVAL
324 );
325 if (EFI_ERROR (Status)) {
326 Status = EFI_OUT_OF_RESOURCES;
327 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR;
328 goto ErrorExit;
329 }
330
331 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
332 EFI_PROGRESS_CODE,
333 EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_PRESENCE_DETECT,
334 ParentDevicePath
335 );
336
337 //
338 // Reset the keyboard device
339 //
340 Status = ConsoleIn->ConInEx.Reset (&ConsoleIn->ConInEx, TRUE);
341 if (EFI_ERROR (Status)) {
342 Status = EFI_DEVICE_ERROR;
343 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_NOT_DETECTED;
344 goto ErrorExit;
345 }
346
347 ConsoleIn->ControllerNameTable = NULL;
348 AddUnicodeString2 (
349 "eng",
350 gPs2KeyboardComponentName.SupportedLanguages,
351 &ConsoleIn->ControllerNameTable,
352 L"PS/2 Keyboard Device",
353 TRUE
354 );
355 AddUnicodeString2 (
356 "en",
357 gPs2KeyboardComponentName2.SupportedLanguages,
358 &ConsoleIn->ControllerNameTable,
359 L"PS/2 Keyboard Device",
360 FALSE
361 );
362
363
364 //
365 // Install protocol interfaces for the keyboard device.
366 //
367 Status = gBS->InstallMultipleProtocolInterfaces (
368 &Controller,
369 &gEfiSimpleTextInProtocolGuid,
370 &ConsoleIn->ConIn,
371 &gEfiSimpleTextInputExProtocolGuid,
372 &ConsoleIn->ConInEx,
373 NULL
374 );
375 if (EFI_ERROR (Status)) {
376 StatusCode = EFI_PERIPHERAL_KEYBOARD | EFI_P_EC_CONTROLLER_ERROR;
377 goto ErrorExit;
378 }
379
380 return Status;
381
382 ErrorExit:
383 //
384 // Report error code
385 //
386 if (StatusCode != 0) {
387 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
388 EFI_ERROR_CODE | EFI_ERROR_MINOR,
389 StatusCode,
390 ParentDevicePath
391 );
392 }
393
394 if ((ConsoleIn != NULL) && (ConsoleIn->ConIn.WaitForKey != NULL)) {
395 gBS->CloseEvent (ConsoleIn->ConIn.WaitForKey);
396 }
397
398 if ((ConsoleIn != NULL) && (ConsoleIn->TimerEvent != NULL)) {
399 gBS->CloseEvent (ConsoleIn->TimerEvent);
400 }
401 if ((ConsoleIn != NULL) && (ConsoleIn->ConInEx.WaitForKeyEx != NULL)) {
402 gBS->CloseEvent (ConsoleIn->ConInEx.WaitForKeyEx);
403 }
404 KbdFreeNotifyList (&ConsoleIn->NotifyList);
405 if ((ConsoleIn != NULL) && (ConsoleIn->ControllerNameTable != NULL)) {
406 FreeUnicodeStringTable (ConsoleIn->ControllerNameTable);
407 }
408 //
409 // Since there will be no timer handler for keyboard input any more,
410 // exhaust input data just in case there is still keyboard data left
411 //
412 if (ConsoleIn != NULL) {
413 Status1 = EFI_SUCCESS;
414 while (!EFI_ERROR (Status1) && (Status != EFI_DEVICE_ERROR)) {
415 Status1 = KeyboardRead (ConsoleIn, &Data);;
416 }
417 }
418
419 if (ConsoleIn != NULL) {
420 gBS->FreePool (ConsoleIn);
421 }
422
423 gBS->CloseProtocol (
424 Controller,
425 &gEfiDevicePathProtocolGuid,
426 This->DriverBindingHandle,
427 Controller
428 );
429
430 gBS->CloseProtocol (
431 Controller,
432 &gEfiIsaIoProtocolGuid,
433 This->DriverBindingHandle,
434 Controller
435 );
436
437 return Status;
438 }
439
440 /**
441 Stop this driver on ControllerHandle. Support stoping any child handles
442 created by this driver.
443
444 @param This Protocol instance pointer.
445 @param ControllerHandle Handle of device to stop driver on
446 @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of
447 children is zero stop the entire bus driver.
448 @param ChildHandleBuffer List of Child Handles to Stop.
449
450 @retval EFI_SUCCESS This driver is removed ControllerHandle
451 @retval other This driver was not removed from this device
452
453 **/
454 EFI_STATUS
455 EFIAPI
456 KbdControllerDriverStop (
457 IN EFI_DRIVER_BINDING_PROTOCOL *This,
458 IN EFI_HANDLE Controller,
459 IN UINTN NumberOfChildren,
460 IN EFI_HANDLE *ChildHandleBuffer
461 )
462 {
463 EFI_STATUS Status;
464 EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn;
465 KEYBOARD_CONSOLE_IN_DEV *ConsoleIn;
466 UINT8 Data;
467
468 //
469 // Disable Keyboard
470 //
471 Status = gBS->OpenProtocol (
472 Controller,
473 &gEfiSimpleTextInProtocolGuid,
474 (VOID **) &ConIn,
475 This->DriverBindingHandle,
476 Controller,
477 EFI_OPEN_PROTOCOL_GET_PROTOCOL
478 );
479 if (EFI_ERROR (Status)) {
480 return Status;
481 }
482 Status = gBS->OpenProtocol (
483 Controller,
484 &gEfiSimpleTextInputExProtocolGuid,
485 NULL,
486 This->DriverBindingHandle,
487 Controller,
488 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
489 );
490 if (EFI_ERROR (Status)) {
491 return Status;
492 }
493
494 ConsoleIn = KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (ConIn);
495
496 //
497 // Report that the keyboard is being disabled
498 //
499 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
500 EFI_PROGRESS_CODE,
501 EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_DISABLE,
502 ConsoleIn->DevicePath
503 );
504
505 if (ConsoleIn->TimerEvent != NULL) {
506 gBS->CloseEvent (ConsoleIn->TimerEvent);
507 ConsoleIn->TimerEvent = NULL;
508 }
509 //
510 // Disable the keyboard interface
511 //
512 Status = DisableKeyboard (ConsoleIn);
513
514 //
515 // Since there will be no timer handler for keyboard input any more,
516 // exhaust input data just in case there is still keyboard data left
517 //
518 Status = EFI_SUCCESS;
519 while (!EFI_ERROR (Status)) {
520 Status = KeyboardRead (ConsoleIn, &Data);;
521 }
522 //
523 // Uninstall the SimpleTextIn and SimpleTextInEx protocols
524 //
525 Status = gBS->UninstallMultipleProtocolInterfaces (
526 Controller,
527 &gEfiSimpleTextInProtocolGuid,
528 &ConsoleIn->ConIn,
529 &gEfiSimpleTextInputExProtocolGuid,
530 &ConsoleIn->ConInEx,
531 NULL
532 );
533 if (EFI_ERROR (Status)) {
534 return Status;
535 }
536
537 gBS->CloseProtocol (
538 Controller,
539 &gEfiDevicePathProtocolGuid,
540 This->DriverBindingHandle,
541 Controller
542 );
543
544 gBS->CloseProtocol (
545 Controller,
546 &gEfiIsaIoProtocolGuid,
547 This->DriverBindingHandle,
548 Controller
549 );
550
551 //
552 // Free other resources
553 //
554 if ((ConsoleIn->ConIn).WaitForKey != NULL) {
555 gBS->CloseEvent ((ConsoleIn->ConIn).WaitForKey);
556 (ConsoleIn->ConIn).WaitForKey = NULL;
557 }
558 if (ConsoleIn->ConInEx.WaitForKeyEx != NULL) {
559 gBS->CloseEvent (ConsoleIn->ConInEx.WaitForKeyEx);
560 ConsoleIn->ConInEx.WaitForKeyEx = NULL;
561 }
562 KbdFreeNotifyList (&ConsoleIn->NotifyList);
563 FreeUnicodeStringTable (ConsoleIn->ControllerNameTable);
564 gBS->FreePool (ConsoleIn);
565
566 return EFI_SUCCESS;
567 }
568
569 /**
570 Free the waiting key notify list.
571
572 @param ListHead Pointer to list head
573
574 @retval EFI_INVALID_PARAMETER ListHead is NULL
575 @retval EFI_SUCCESS Sucess to free NotifyList
576 **/
577 EFI_STATUS
578 KbdFreeNotifyList (
579 IN OUT LIST_ENTRY *ListHead
580 )
581 {
582 KEYBOARD_CONSOLE_IN_EX_NOTIFY *NotifyNode;
583
584 if (ListHead == NULL) {
585 return EFI_INVALID_PARAMETER;
586 }
587 while (!IsListEmpty (ListHead)) {
588 NotifyNode = CR (
589 ListHead->ForwardLink,
590 KEYBOARD_CONSOLE_IN_EX_NOTIFY,
591 NotifyEntry,
592 KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE
593 );
594 RemoveEntryList (ListHead->ForwardLink);
595 gBS->FreePool (NotifyNode);
596 }
597
598 return EFI_SUCCESS;
599 }
600
601 /**
602 The module Entry Point for module Ps2Keyboard.
603
604 @param[in] ImageHandle The firmware allocated handle for the EFI image.
605 @param[in] SystemTable A pointer to the EFI System Table.
606
607 @retval EFI_SUCCESS The entry point is executed successfully.
608 @retval other Some error occurs when executing this entry point.
609
610 **/
611 EFI_STATUS
612 EFIAPI
613 InitializePs2Keyboard(
614 IN EFI_HANDLE ImageHandle,
615 IN EFI_SYSTEM_TABLE *SystemTable
616 )
617 {
618 EFI_STATUS Status;
619
620 //
621 // Install driver model protocol(s).
622 //
623 Status = EfiLibInstallDriverBindingComponentName2 (
624 ImageHandle,
625 SystemTable,
626 &gKeyboardControllerDriver,
627 ImageHandle,
628 &gPs2KeyboardComponentName,
629 &gPs2KeyboardComponentName2
630 );
631 ASSERT_EFI_ERROR (Status);
632
633
634 return Status;
635 }
636