Fix the PS2 keyboard driver to call hotkey callback even no one is calling ReadKeyStroke
[mirror_edk2.git] / IntelFrameworkModulePkg / Bus / Isa / Ps2KeyboardDxe / Ps2KbdTextIn.c
1 /** @file
2 Routines implements SIMPLE_TEXT_IN protocol's interfaces based on 8042 interfaces
3 provided by Ps2KbdCtrller.c.
4
5 Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16
17 #include "Ps2Keyboard.h"
18
19 /**
20 Check whether the EFI key buffer is empty.
21
22 @param Queue Pointer to instance of EFI_KEY_QUEUE.
23
24 @retval TRUE The EFI key buffer is empty.
25 @retval FALSE The EFI key buffer isn't empty.
26 **/
27 BOOLEAN
28 IsEfikeyBufEmpty (
29 IN EFI_KEY_QUEUE *Queue
30 )
31 {
32 return (BOOLEAN) (Queue->Head == Queue->Tail);
33 }
34
35
36
37 /**
38 Push one key data to the EFI key buffer.
39
40 @param Queue Pointer to instance of EFI_KEY_QUEUE.
41 @param KeyData The key data to push.
42 **/
43 VOID
44 PushEfikeyBufTail (
45 IN EFI_KEY_QUEUE *Queue,
46 IN EFI_KEY_DATA *KeyData
47 )
48 {
49 if ((Queue->Tail + 1) % KEYBOARD_EFI_KEY_MAX_COUNT == Queue->Head) {
50 return;
51 }
52
53 CopyMem (&Queue->Buffer[Queue->Tail], KeyData, sizeof (EFI_KEY_DATA));
54 Queue->Tail = (Queue->Tail + 1) % KEYBOARD_EFI_KEY_MAX_COUNT;
55 }
56
57 /**
58 Read & remove one key data from the EFI key buffer.
59
60 @param Queue Pointer to instance of EFI_KEY_QUEUE.
61 @param KeyData Receive the key data.
62
63 @retval EFI_SUCCESS The key data is popped successfully.
64 @retval EFI_NOT_READY There is no key data available.
65 **/
66 EFI_STATUS
67 PopEfikeyBufHead (
68 IN EFI_KEY_QUEUE *Queue,
69 OUT EFI_KEY_DATA *KeyData
70 )
71 {
72 if (IsEfikeyBufEmpty (Queue)) {
73 return EFI_NOT_READY;
74 }
75 //
76 // Retrieve and remove the values
77 //
78 CopyMem (KeyData, &Queue->Buffer[Queue->Head], sizeof (EFI_KEY_DATA));
79 Queue->Head = (Queue->Head + 1) % KEYBOARD_EFI_KEY_MAX_COUNT;
80 return EFI_SUCCESS;
81 }
82
83 /**
84 Judge whether is a registed key
85
86 @param RegsiteredData A pointer to a buffer that is filled in with the keystroke
87 state data for the key that was registered.
88 @param InputData A pointer to a buffer that is filled in with the keystroke
89 state data for the key that was pressed.
90
91 @retval TRUE Key be pressed matches a registered key.
92 @retval FLASE Match failed.
93
94 **/
95 BOOLEAN
96 IsKeyRegistered (
97 IN EFI_KEY_DATA *RegsiteredData,
98 IN EFI_KEY_DATA *InputData
99 )
100
101 {
102 ASSERT (RegsiteredData != NULL && InputData != NULL);
103
104 if ((RegsiteredData->Key.ScanCode != InputData->Key.ScanCode) ||
105 (RegsiteredData->Key.UnicodeChar != InputData->Key.UnicodeChar)) {
106 return FALSE;
107 }
108
109 //
110 // Assume KeyShiftState/KeyToggleState = 0 in Registered key data means these state could be ignored.
111 //
112 if (RegsiteredData->KeyState.KeyShiftState != 0 &&
113 RegsiteredData->KeyState.KeyShiftState != InputData->KeyState.KeyShiftState) {
114 return FALSE;
115 }
116 if (RegsiteredData->KeyState.KeyToggleState != 0 &&
117 RegsiteredData->KeyState.KeyToggleState != InputData->KeyState.KeyToggleState) {
118 return FALSE;
119 }
120
121 return TRUE;
122
123 }
124
125 /**
126 Reads the next keystroke from the input device. The WaitForKey Event can
127 be used to test for existance of a keystroke via WaitForEvent () call.
128
129 @param ConsoleInDev Ps2 Keyboard private structure
130 @param KeyData A pointer to a buffer that is filled in with the keystroke
131 state data for the key that was pressed.
132
133
134 @retval EFI_SUCCESS The keystroke information was returned.
135 @retval EFI_NOT_READY There was no keystroke data availiable.
136 @retval EFI_DEVICE_ERROR The keystroke information was not returned due to
137 hardware errors.
138 @retval EFI_INVALID_PARAMETER KeyData is NULL.
139
140 **/
141 EFI_STATUS
142 KeyboardReadKeyStrokeWorker (
143 IN KEYBOARD_CONSOLE_IN_DEV *ConsoleInDev,
144 OUT EFI_KEY_DATA *KeyData
145 )
146
147 {
148 EFI_STATUS Status;
149 EFI_TPL OldTpl;
150
151 if (KeyData == NULL) {
152 return EFI_INVALID_PARAMETER;
153 }
154
155 //
156 // Enter critical section
157 //
158 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
159
160 KeyboardTimerHandler (NULL, ConsoleInDev);
161
162 if (ConsoleInDev->KeyboardErr) {
163 Status = EFI_DEVICE_ERROR;
164 } else {
165 Status = PopEfikeyBufHead (&ConsoleInDev->EfiKeyQueue, KeyData);
166 }
167
168 gBS->RestoreTPL (OldTpl);
169 return Status;
170 }
171
172 /**
173 Perform 8042 controller and keyboard initialization which implement SIMPLE_TEXT_IN.Reset()
174
175 @param This Pointer to instance of EFI_SIMPLE_TEXT_INPUT_PROTOCOL
176 @param ExtendedVerification Indicate that the driver may perform a more
177 exhaustive verification operation of the device during
178 reset, now this par is ignored in this driver
179
180 **/
181 EFI_STATUS
182 EFIAPI
183 KeyboardEfiReset (
184 IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
185 IN BOOLEAN ExtendedVerification
186 )
187 {
188 EFI_STATUS Status;
189 KEYBOARD_CONSOLE_IN_DEV *ConsoleIn;
190 EFI_TPL OldTpl;
191
192 ConsoleIn = KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (This);
193 if (ConsoleIn->KeyboardErr) {
194 return EFI_DEVICE_ERROR;
195 }
196
197 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
198 EFI_PROGRESS_CODE,
199 EFI_PERIPHERAL_KEYBOARD | EFI_P_PC_RESET,
200 ConsoleIn->DevicePath
201 );
202
203 //
204 // Enter critical section
205 //
206 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
207
208 //
209 // Call InitKeyboard to initialize the keyboard
210 //
211 Status = InitKeyboard (ConsoleIn, ExtendedVerification);
212 if (EFI_ERROR (Status)) {
213 //
214 // Leave critical section and return
215 //
216 gBS->RestoreTPL (OldTpl);
217 return EFI_DEVICE_ERROR;
218 }
219
220 //
221 // Leave critical section and return
222 //
223 gBS->RestoreTPL (OldTpl);
224
225 //
226 // Report the status If a stuck key was detected
227 //
228 if (KeyReadStatusRegister (ConsoleIn) & 0x01) {
229 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
230 EFI_ERROR_CODE | EFI_ERROR_MINOR,
231 EFI_PERIPHERAL_KEYBOARD | EFI_P_KEYBOARD_EC_STUCK_KEY,
232 ConsoleIn->DevicePath
233 );
234 }
235 //
236 // Report the status If keyboard is locked
237 //
238 if ((KeyReadStatusRegister (ConsoleIn) & 0x10) == 0) {
239 REPORT_STATUS_CODE_WITH_DEVICE_PATH (
240 EFI_ERROR_CODE | EFI_ERROR_MINOR,
241 EFI_PERIPHERAL_KEYBOARD | EFI_P_KEYBOARD_EC_LOCKED,
242 ConsoleIn->DevicePath
243 );
244 }
245
246 return EFI_SUCCESS;
247 }
248
249 /**
250 Retrieve key values for driver user which implement SIMPLE_TEXT_IN.ReadKeyStroke().
251
252 @param This Pointer to instance of EFI_SIMPLE_TEXT_INPUT_PROTOCOL
253 @param Key The output buffer for key value
254
255 @retval EFI_SUCCESS success to read key stroke
256 **/
257 EFI_STATUS
258 EFIAPI
259 KeyboardReadKeyStroke (
260 IN EFI_SIMPLE_TEXT_INPUT_PROTOCOL *This,
261 OUT EFI_INPUT_KEY *Key
262 )
263 {
264 EFI_STATUS Status;
265 KEYBOARD_CONSOLE_IN_DEV *ConsoleIn;
266 EFI_KEY_DATA KeyData;
267
268 ConsoleIn = KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (This);
269 Status = KeyboardReadKeyStrokeWorker (ConsoleIn, &KeyData);
270 if (EFI_ERROR (Status)) {
271 return Status;
272 }
273
274 CopyMem (Key, &KeyData.Key, sizeof (EFI_INPUT_KEY));
275 return EFI_SUCCESS;
276
277 }
278
279 /**
280 Event notification function for SIMPLE_TEXT_IN.WaitForKey event
281 Signal the event if there is key available
282
283 @param Event the event object
284 @param Context waitting context
285
286 **/
287 VOID
288 EFIAPI
289 KeyboardWaitForKey (
290 IN EFI_EVENT Event,
291 IN VOID *Context
292 )
293 {
294 EFI_TPL OldTpl;
295 KEYBOARD_CONSOLE_IN_DEV *ConsoleIn;
296
297 ConsoleIn = (KEYBOARD_CONSOLE_IN_DEV *) Context;
298
299 //
300 // Enter critical section
301 //
302 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
303
304 KeyboardTimerHandler (NULL, ConsoleIn);
305
306 if (!ConsoleIn->KeyboardErr) {
307 //
308 // Someone is waiting on the keyboard event, if there's
309 // a key pending, signal the event
310 //
311 if (!IsEfikeyBufEmpty (&ConsoleIn->EfiKeyQueue)) {
312 gBS->SignalEvent (Event);
313 }
314 }
315 //
316 // Leave critical section and return
317 //
318 gBS->RestoreTPL (OldTpl);
319 }
320
321 /**
322 Event notification function for SIMPLE_TEXT_INPUT_EX_PROTOCOL.WaitForKeyEx event
323 Signal the event if there is key available
324
325 @param Event event object
326 @param Context waiting context
327
328 **/
329 VOID
330 EFIAPI
331 KeyboardWaitForKeyEx (
332 IN EFI_EVENT Event,
333 IN VOID *Context
334 )
335
336 {
337 KeyboardWaitForKey (Event, Context);
338 }
339
340 /**
341 Reset the input device and optionaly run diagnostics
342
343 @param This Protocol instance pointer.
344 @param ExtendedVerification Driver may perform diagnostics on reset.
345
346 @retval EFI_SUCCESS The device was reset.
347 @retval EFI_DEVICE_ERROR The device is not functioning properly and could
348 not be reset.
349
350 **/
351 EFI_STATUS
352 EFIAPI
353 KeyboardEfiResetEx (
354 IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
355 IN BOOLEAN ExtendedVerification
356 )
357
358 {
359 KEYBOARD_CONSOLE_IN_DEV *ConsoleInDev;
360
361 ConsoleInDev = TEXT_INPUT_EX_KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (This);
362
363 return ConsoleInDev->ConIn.Reset (
364 &ConsoleInDev->ConIn,
365 ExtendedVerification
366 );
367 }
368
369 /**
370 Reads the next keystroke from the input device. The WaitForKey Event can
371 be used to test for existance of a keystroke via WaitForEvent () call.
372
373
374 @param This Protocol instance pointer.
375 @param KeyData A pointer to a buffer that is filled in with the keystroke
376 state data for the key that was pressed.
377
378 @retval EFI_SUCCESS The keystroke information was returned.
379 @retval EFI_NOT_READY There was no keystroke data availiable.
380 @retval EFI_DEVICE_ERROR The keystroke information was not returned due to
381 hardware errors.
382 @retval EFI_INVALID_PARAMETER KeyData is NULL.
383
384 **/
385 EFI_STATUS
386 EFIAPI
387 KeyboardReadKeyStrokeEx (
388 IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
389 OUT EFI_KEY_DATA *KeyData
390 )
391
392 {
393 KEYBOARD_CONSOLE_IN_DEV *ConsoleInDev;
394
395 if (KeyData == NULL) {
396 return EFI_INVALID_PARAMETER;
397 }
398
399 ConsoleInDev = TEXT_INPUT_EX_KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (This);
400 return KeyboardReadKeyStrokeWorker (ConsoleInDev, KeyData);
401
402 }
403
404 /**
405 Set certain state for the input device.
406
407 @param This Protocol instance pointer.
408 @param KeyToggleState A pointer to the EFI_KEY_TOGGLE_STATE to set the
409 state for the input device.
410
411 @retval EFI_SUCCESS The device state was set successfully.
412 @retval EFI_DEVICE_ERROR The device is not functioning correctly and could
413 not have the setting adjusted.
414 @retval EFI_UNSUPPORTED The device does not have the ability to set its state.
415 @retval EFI_INVALID_PARAMETER KeyToggleState is NULL.
416
417 **/
418 EFI_STATUS
419 EFIAPI
420 KeyboardSetState (
421 IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
422 IN EFI_KEY_TOGGLE_STATE *KeyToggleState
423 )
424
425 {
426 EFI_STATUS Status;
427 KEYBOARD_CONSOLE_IN_DEV *ConsoleInDev;
428 EFI_TPL OldTpl;
429
430 if (KeyToggleState == NULL) {
431 return EFI_INVALID_PARAMETER;
432 }
433
434 ConsoleInDev = TEXT_INPUT_EX_KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (This);
435
436 //
437 // Enter critical section
438 //
439 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
440
441 if (ConsoleInDev->KeyboardErr) {
442 Status = EFI_DEVICE_ERROR;
443 goto Exit;
444 }
445
446 if ((*KeyToggleState & EFI_TOGGLE_STATE_VALID) != EFI_TOGGLE_STATE_VALID) {
447 Status = EFI_UNSUPPORTED;
448 goto Exit;
449 }
450
451 //
452 // Update the status light
453 //
454 ConsoleInDev->ScrollLock = FALSE;
455 ConsoleInDev->NumLock = FALSE;
456 ConsoleInDev->CapsLock = FALSE;
457
458 if ((*KeyToggleState & EFI_SCROLL_LOCK_ACTIVE) == EFI_SCROLL_LOCK_ACTIVE) {
459 ConsoleInDev->ScrollLock = TRUE;
460 }
461 if ((*KeyToggleState & EFI_NUM_LOCK_ACTIVE) == EFI_NUM_LOCK_ACTIVE) {
462 ConsoleInDev->NumLock = TRUE;
463 }
464 if ((*KeyToggleState & EFI_CAPS_LOCK_ACTIVE) == EFI_CAPS_LOCK_ACTIVE) {
465 ConsoleInDev->CapsLock = TRUE;
466 }
467
468 Status = UpdateStatusLights (ConsoleInDev);
469 if (EFI_ERROR (Status)) {
470 Status = EFI_DEVICE_ERROR;
471 }
472
473 Exit:
474 //
475 // Leave critical section and return
476 //
477 gBS->RestoreTPL (OldTpl);
478
479 return Status;
480
481 }
482
483 /**
484 Register a notification function for a particular keystroke for the input device.
485
486 @param This Protocol instance pointer.
487 @param KeyData A pointer to a buffer that is filled in with the keystroke
488 information data for the key that was pressed.
489 @param KeyNotificationFunction Points to the function to be called when the key
490 sequence is typed specified by KeyData.
491 @param NotifyHandle Points to the unique handle assigned to the registered notification.
492
493 @retval EFI_SUCCESS The notification function was registered successfully.
494 @retval EFI_OUT_OF_RESOURCES Unable to allocate resources for necesssary data structures.
495 @retval EFI_INVALID_PARAMETER KeyData or NotifyHandle or KeyNotificationFunction is NULL.
496
497 **/
498 EFI_STATUS
499 EFIAPI
500 KeyboardRegisterKeyNotify (
501 IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
502 IN EFI_KEY_DATA *KeyData,
503 IN EFI_KEY_NOTIFY_FUNCTION KeyNotificationFunction,
504 OUT EFI_HANDLE *NotifyHandle
505 )
506 {
507 EFI_STATUS Status;
508 KEYBOARD_CONSOLE_IN_DEV *ConsoleInDev;
509 EFI_TPL OldTpl;
510 LIST_ENTRY *Link;
511 KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify;
512 KEYBOARD_CONSOLE_IN_EX_NOTIFY *NewNotify;
513
514 if (KeyData == NULL || NotifyHandle == NULL || KeyNotificationFunction == NULL) {
515 return EFI_INVALID_PARAMETER;
516 }
517
518 ConsoleInDev = TEXT_INPUT_EX_KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (This);
519
520 //
521 // Enter critical section
522 //
523 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
524
525 //
526 // Return EFI_SUCCESS if the (KeyData, NotificationFunction) is already registered.
527 //
528 for (Link = ConsoleInDev->NotifyList.ForwardLink; Link != &ConsoleInDev->NotifyList; Link = Link->ForwardLink) {
529 CurrentNotify = CR (
530 Link,
531 KEYBOARD_CONSOLE_IN_EX_NOTIFY,
532 NotifyEntry,
533 KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE
534 );
535 if (IsKeyRegistered (&CurrentNotify->KeyData, KeyData)) {
536 if (CurrentNotify->KeyNotificationFn == KeyNotificationFunction) {
537 *NotifyHandle = CurrentNotify->NotifyHandle;
538 Status = EFI_SUCCESS;
539 goto Exit;
540 }
541 }
542 }
543
544 //
545 // Allocate resource to save the notification function
546 //
547 NewNotify = (KEYBOARD_CONSOLE_IN_EX_NOTIFY *) AllocateZeroPool (sizeof (KEYBOARD_CONSOLE_IN_EX_NOTIFY));
548 if (NewNotify == NULL) {
549 Status = EFI_OUT_OF_RESOURCES;
550 goto Exit;
551 }
552
553 NewNotify->Signature = KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE;
554 NewNotify->KeyNotificationFn = KeyNotificationFunction;
555 NewNotify->NotifyHandle = (EFI_HANDLE) NewNotify;
556 CopyMem (&NewNotify->KeyData, KeyData, sizeof (EFI_KEY_DATA));
557 InsertTailList (&ConsoleInDev->NotifyList, &NewNotify->NotifyEntry);
558
559 *NotifyHandle = NewNotify->NotifyHandle;
560 Status = EFI_SUCCESS;
561
562 Exit:
563 //
564 // Leave critical section and return
565 //
566 gBS->RestoreTPL (OldTpl);
567 return Status;
568
569 }
570
571 /**
572 Remove a registered notification function from a particular keystroke.
573
574 @param This Protocol instance pointer.
575 @param NotificationHandle The handle of the notification function being unregistered.
576
577
578 @retval EFI_SUCCESS The notification function was unregistered successfully.
579 @retval EFI_INVALID_PARAMETER The NotificationHandle is invalid.
580
581 **/
582 EFI_STATUS
583 EFIAPI
584 KeyboardUnregisterKeyNotify (
585 IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *This,
586 IN EFI_HANDLE NotificationHandle
587 )
588 {
589 EFI_STATUS Status;
590 KEYBOARD_CONSOLE_IN_DEV *ConsoleInDev;
591 EFI_TPL OldTpl;
592 LIST_ENTRY *Link;
593 KEYBOARD_CONSOLE_IN_EX_NOTIFY *CurrentNotify;
594
595 if (NotificationHandle == NULL) {
596 return EFI_INVALID_PARAMETER;
597 }
598
599 if (((KEYBOARD_CONSOLE_IN_EX_NOTIFY *) NotificationHandle)->Signature != KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE) {
600 return EFI_INVALID_PARAMETER;
601 }
602
603 ConsoleInDev = TEXT_INPUT_EX_KEYBOARD_CONSOLE_IN_DEV_FROM_THIS (This);
604
605 //
606 // Enter critical section
607 //
608 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
609
610 for (Link = ConsoleInDev->NotifyList.ForwardLink; Link != &ConsoleInDev->NotifyList; Link = Link->ForwardLink) {
611 CurrentNotify = CR (
612 Link,
613 KEYBOARD_CONSOLE_IN_EX_NOTIFY,
614 NotifyEntry,
615 KEYBOARD_CONSOLE_IN_EX_NOTIFY_SIGNATURE
616 );
617 if (CurrentNotify->NotifyHandle == NotificationHandle) {
618 //
619 // Remove the notification function from NotifyList and free resources
620 //
621 RemoveEntryList (&CurrentNotify->NotifyEntry);
622
623 gBS->FreePool (CurrentNotify);
624 Status = EFI_SUCCESS;
625 goto Exit;
626 }
627 }
628
629 //
630 // Can not find the specified Notification Handle
631 //
632 Status = EFI_INVALID_PARAMETER;
633 Exit:
634 //
635 // Leave critical section and return
636 //
637 gBS->RestoreTPL (OldTpl);
638 return Status;
639 }
640