]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/Console/TerminalDxe/TerminalConIn.c
MdeModulePkg TerminalDxe: Execute key notify func at TPL_CALLBACK
[mirror_edk2.git] / MdeModulePkg / Universal / Console / TerminalDxe / TerminalConIn.c
index 5c3ea86fe104f1c6aeb68483aba97721f76681b4..016241017a42693ec05ab57fa4d84953256cdd84 100644 (file)
@@ -2,7 +2,7 @@
   Implementation for EFI_SIMPLE_TEXT_INPUT_PROTOCOL protocol.\r
 \r
 (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>\r
-Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>\r
 Copyright (C) 2016 Silicon Graphics, Inc. All rights reserved.<BR>\r
 This program and the accompanying materials\r
 are licensed and made available under the terms and conditions of the BSD License\r
@@ -94,6 +94,7 @@ TerminalConInReset (
   TerminalDevice->RawFiFo->Head     = TerminalDevice->RawFiFo->Tail;\r
   TerminalDevice->UnicodeFiFo->Head = TerminalDevice->UnicodeFiFo->Tail;\r
   TerminalDevice->EfiKeyFiFo->Head  = TerminalDevice->EfiKeyFiFo->Tail;\r
+  TerminalDevice->EfiKeyFiFoForNotify->Head = TerminalDevice->EfiKeyFiFoForNotify->Tail;\r
 \r
   if (EFI_ERROR (Status)) {\r
     REPORT_STATUS_CODE_WITH_DEVICE_PATH (\r
@@ -598,6 +599,59 @@ TerminalConInTimerHandler (
   TranslateRawDataToEfiKey (TerminalDevice);\r
 }\r
 \r
+/**\r
+  Process key notify.\r
+\r
+  @param  Event                 Indicates the event that invoke this function.\r
+  @param  Context               Indicates the calling context.\r
+**/\r
+VOID\r
+EFIAPI\r
+KeyNotifyProcessHandler (\r
+  IN  EFI_EVENT                 Event,\r
+  IN  VOID                      *Context\r
+  )\r
+{\r
+  BOOLEAN                       HasKey;\r
+  TERMINAL_DEV                  *TerminalDevice;\r
+  EFI_INPUT_KEY                 Key;\r
+  EFI_KEY_DATA                  KeyData;\r
+  LIST_ENTRY                    *Link;\r
+  LIST_ENTRY                    *NotifyList;\r
+  TERMINAL_CONSOLE_IN_EX_NOTIFY *CurrentNotify;\r
+  EFI_TPL                       OldTpl;\r
+\r
+  TerminalDevice = (TERMINAL_DEV *) Context;\r
+\r
+  //\r
+  // Invoke notification functions.\r
+  //\r
+  NotifyList = &TerminalDevice->NotifyList;\r
+  while (TRUE) {\r
+    //\r
+    // Enter critical section\r
+    //  \r
+    OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
+    HasKey = EfiKeyFiFoForNotifyRemoveOneKey (TerminalDevice->EfiKeyFiFoForNotify, &Key);\r
+    CopyMem (&KeyData.Key, &Key, sizeof (EFI_INPUT_KEY));\r
+    KeyData.KeyState.KeyShiftState  = 0;\r
+    KeyData.KeyState.KeyToggleState = 0;\r
+    //\r
+    // Leave critical section\r
+    //\r
+    gBS->RestoreTPL (OldTpl);\r
+    if (!HasKey) {\r
+      break;\r
+    }\r
+    for (Link = GetFirstNode (NotifyList); !IsNull (NotifyList, Link); Link = GetNextNode (NotifyList, Link)) {\r
+      CurrentNotify = CR (Link, TERMINAL_CONSOLE_IN_EX_NOTIFY, NotifyEntry, TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE);\r
+      if (IsKeyRegistered (&CurrentNotify->KeyData, &KeyData)) {\r
+        CurrentNotify->KeyNotificationFn (&KeyData);\r
+      }\r
+    }\r
+  }\r
+}\r
+\r
 /**\r
   Get one key out of serial buffer.\r
 \r
@@ -763,6 +817,126 @@ IsRawFiFoFull (
   return FALSE;\r
 }\r
 \r
+/**\r
+  Insert one pre-fetched key into the FIFO buffer.\r
+\r
+  @param  EfiKeyFiFo            Pointer to instance of EFI_KEY_FIFO.\r
+  @param  Input                 The key will be input.\r
+\r
+  @retval TRUE                  If insert successfully.\r
+  @retval FALSE                 If FIFO buffer is full before key insertion,\r
+                                and the key is lost.\r
+\r
+**/\r
+BOOLEAN\r
+EfiKeyFiFoForNotifyInsertOneKey (\r
+  EFI_KEY_FIFO                  *EfiKeyFiFo,\r
+  EFI_INPUT_KEY                 *Input\r
+  )\r
+{\r
+  UINT8                         Tail;\r
+\r
+  Tail = EfiKeyFiFo->Tail;\r
+\r
+  if (IsEfiKeyFiFoForNotifyFull (EfiKeyFiFo)) {\r
+    //\r
+    // FIFO is full\r
+    //\r
+    return FALSE;\r
+  }\r
+\r
+  CopyMem (&EfiKeyFiFo->Data[Tail], Input, sizeof (EFI_INPUT_KEY));\r
+\r
+  EfiKeyFiFo->Tail = (UINT8) ((Tail + 1) % (FIFO_MAX_NUMBER + 1));\r
+\r
+  return TRUE;\r
+}\r
+\r
+/**\r
+  Remove one pre-fetched key out of the FIFO buffer.\r
+\r
+  @param  EfiKeyFiFo            Pointer to instance of EFI_KEY_FIFO.\r
+  @param  Output                The key will be removed.\r
+\r
+  @retval TRUE                  If remove successfully.\r
+  @retval FALSE                 If FIFO buffer is empty before remove operation.\r
+\r
+**/\r
+BOOLEAN\r
+EfiKeyFiFoForNotifyRemoveOneKey (\r
+  EFI_KEY_FIFO                  *EfiKeyFiFo,\r
+  EFI_INPUT_KEY                 *Output\r
+  )\r
+{\r
+  UINT8                         Head;\r
+\r
+  Head = EfiKeyFiFo->Head;\r
+  ASSERT (Head < FIFO_MAX_NUMBER + 1);\r
+\r
+  if (IsEfiKeyFiFoForNotifyEmpty (EfiKeyFiFo)) {\r
+    //\r
+    // FIFO is empty\r
+    //\r
+    Output->ScanCode    = SCAN_NULL;\r
+    Output->UnicodeChar = 0;\r
+    return FALSE;\r
+  }\r
+\r
+  CopyMem (Output, &EfiKeyFiFo->Data[Head], sizeof (EFI_INPUT_KEY));\r
+\r
+  EfiKeyFiFo->Head = (UINT8) ((Head + 1) % (FIFO_MAX_NUMBER + 1));\r
+\r
+  return TRUE;\r
+}\r
+\r
+/**\r
+  Clarify whether FIFO buffer is empty.\r
+\r
+  @param  EfiKeyFiFo            Pointer to instance of EFI_KEY_FIFO.\r
+\r
+  @retval TRUE                  If FIFO buffer is empty.\r
+  @retval FALSE                 If FIFO buffer is not empty.\r
+\r
+**/\r
+BOOLEAN\r
+IsEfiKeyFiFoForNotifyEmpty (\r
+  EFI_KEY_FIFO                  *EfiKeyFiFo\r
+  )\r
+{\r
+  if (EfiKeyFiFo->Head == EfiKeyFiFo->Tail) {\r
+    return TRUE;\r
+  } else {\r
+    return FALSE;\r
+  }\r
+}\r
+\r
+/**\r
+  Clarify whether FIFO buffer is full.\r
+\r
+  @param  EfiKeyFiFo            Pointer to instance of EFI_KEY_FIFO.\r
+\r
+  @retval TRUE                  If FIFO buffer is full.\r
+  @retval FALSE                 If FIFO buffer is not full.\r
+\r
+**/\r
+BOOLEAN\r
+IsEfiKeyFiFoForNotifyFull (\r
+  EFI_KEY_FIFO                  *EfiKeyFiFo\r
+  )\r
+{\r
+  UINT8                         Tail;\r
+  UINT8                         Head;\r
+\r
+  Tail = EfiKeyFiFo->Tail;\r
+  Head = EfiKeyFiFo->Head;\r
+\r
+  if (((Tail + 1) % (FIFO_MAX_NUMBER + 1)) == Head) {\r
+    return TRUE;\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
 /**\r
   Insert one pre-fetched key into the FIFO buffer.\r
 \r
@@ -793,7 +967,7 @@ EfiKeyFiFoInsertOneKey (
   KeyData.KeyState.KeyToggleState = 0;\r
 \r
   //\r
-  // Invoke notification functions if exist\r
+  // Signal KeyNotify process event if this key pressed matches any key registered.\r
   //\r
   NotifyList = &TerminalDevice->NotifyList;\r
   for (Link = GetFirstNode (NotifyList); !IsNull (NotifyList,Link); Link = GetNextNode (NotifyList,Link)) {\r
@@ -804,7 +978,13 @@ EfiKeyFiFoInsertOneKey (
                       TERMINAL_CONSOLE_IN_EX_NOTIFY_SIGNATURE\r
                       );\r
     if (IsKeyRegistered (&CurrentNotify->KeyData, &KeyData)) {\r
-      CurrentNotify->KeyNotificationFn (&KeyData);\r
+      //\r
+      // The key notification function needs to run at TPL_CALLBACK\r
+      // while current TPL is TPL_NOTIFY. It will be invoked in\r
+      // KeyNotifyProcessHandler() which runs at TPL_CALLBACK.\r
+      //\r
+      EfiKeyFiFoForNotifyInsertOneKey (TerminalDevice->EfiKeyFiFoForNotify, Key);\r
+      gBS->SignalEvent (TerminalDevice->KeyNotifyProcessEvent);\r
     }\r
   }\r
   if (IsEfiKeyFiFoFull (TerminalDevice)) {\r