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