]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitter.c
MdeModulePkg/ConSplitterDxe: Optimize the ConSplitterTextOutSetMode
[mirror_edk2.git] / MdeModulePkg / Universal / Console / ConSplitterDxe / ConSplitter.c
index 01bec197bbc124db06ffc9d9ace86d1832387e3e..63c814ae1816755f3011fcb588037d3dddea1320 100644 (file)
   never removed. Such design ensures sytem function well during none console\r
   device situation.\r
 \r
-Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>\r
 (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>\r
-This program and the accompanying materials\r
-are licensed and made available under the terms and conditions of the BSD License\r
-which accompanies this distribution.  The full text of the license may be found at\r
-http://opensource.org/licenses/bsd-license.php\r
-\r
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 \r
 #include "ConSplitter.h"\r
 \r
 //\r
-// Identify if ConIn is connected in PcdConInConnectOnDemand enabled mode. \r
+// Identify if ConIn is connected in PcdConInConnectOnDemand enabled mode.\r
 // default not connect\r
 //\r
 BOOLEAN  mConInIsConnect = FALSE;\r
@@ -67,6 +61,8 @@ GLOBAL_REMOVE_IF_UNREFERENCED TEXT_IN_SPLITTER_PRIVATE_DATA  mConIn = {
     (LIST_ENTRY *) NULL,\r
     (LIST_ENTRY *) NULL\r
   },\r
+  (EFI_KEY_DATA *) NULL,\r
+  0,\r
   0,\r
   FALSE,\r
 \r
@@ -184,7 +180,8 @@ GLOBAL_REMOVE_IF_UNREFERENCED TEXT_OUT_SPLITTER_PRIVATE_DATA mConOut = {
   0,\r
   (TEXT_OUT_SPLITTER_QUERY_DATA *) NULL,\r
   0,\r
-  (INT32 *) NULL\r
+  (INT32 *) NULL,\r
+  FALSE\r
 };\r
 \r
 //\r
@@ -239,7 +236,8 @@ GLOBAL_REMOVE_IF_UNREFERENCED TEXT_OUT_SPLITTER_PRIVATE_DATA mStdErr = {
   0,\r
   (TEXT_OUT_SPLITTER_QUERY_DATA *) NULL,\r
   0,\r
-  (INT32 *) NULL\r
+  (INT32 *) NULL,\r
+  FALSE\r
 };\r
 \r
 //\r
@@ -565,7 +563,7 @@ ConSplitterDriverEntry(
                     &mStdErr.TextOut,\r
                     NULL\r
                     );\r
-    if (!EFI_ERROR (Status)) {  \r
+    if (!EFI_ERROR (Status)) {\r
       //\r
       // Update the EFI System Table with new virtual console\r
       // and update the pointer to Text Output protocol.\r
@@ -574,7 +572,7 @@ ConSplitterDriverEntry(
       gST->StdErr               = &mStdErr.TextOut;\r
     }\r
   }\r
-  \r
+\r
   //\r
   // Update the CRC32 in the EFI System Table header\r
   //\r
@@ -606,6 +604,7 @@ ConSplitterTextInConstructor (
   )\r
 {\r
   EFI_STATUS  Status;\r
+  UINTN       TextInExListCount;\r
 \r
   //\r
   // Allocate buffer for Simple Text Input device\r
@@ -631,6 +630,19 @@ ConSplitterTextInConstructor (
                   );\r
   ASSERT_EFI_ERROR (Status);\r
 \r
+  //\r
+  // Allocate buffer for KeyQueue\r
+  //\r
+  TextInExListCount = ConInPrivate->TextInExListCount;\r
+  Status = ConSplitterGrowBuffer (\r
+             sizeof (EFI_KEY_DATA),\r
+             &TextInExListCount,\r
+             (VOID **) &ConInPrivate->KeyQueue\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
   //\r
   // Allocate buffer for Simple Text Input Ex device\r
   //\r
@@ -711,7 +723,7 @@ ConSplitterTextInConstructor (
   Status = gBS->CreateEventEx (\r
                   EVT_NOTIFY_SIGNAL,\r
                   TPL_CALLBACK,\r
-                  ConSplitterEmptyCallbackFunction,\r
+                  EfiEventEmptyFunction,\r
                   NULL,\r
                   &gConnectConInEventGuid,\r
                   &ConInPrivate->ConnectConInEvent\r
@@ -1968,6 +1980,17 @@ ConSplitterTextInExAddDevice (
         return EFI_OUT_OF_RESOURCES;\r
       }\r
     }\r
+\r
+    TextInExListCount = Private->TextInExListCount;\r
+    Status = ConSplitterGrowBuffer (\r
+               sizeof (EFI_KEY_DATA),\r
+               &TextInExListCount,\r
+               (VOID **) &Private->KeyQueue\r
+               );\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
     Status = ConSplitterGrowBuffer (\r
               sizeof (EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *),\r
               &Private->TextInExListCount,\r
@@ -2271,7 +2294,7 @@ ConSplitterGrowMapTable (
     Size        = Private->CurrentNumberOfConsoles * sizeof (INT32);\r
     Index       = 0;\r
     SrcAddress  = OldTextOutModeMap;\r
-    NewStepSize = NewSize / sizeof(INT32);    \r
+    NewStepSize = NewSize / sizeof(INT32);\r
     // If Private->CurrentNumberOfConsoles is not zero and OldTextOutModeMap\r
     // is not NULL, it indicates that the original TextOutModeMap is not enough\r
     // for the new console devices and has been enlarged by CONSOLE_SPLITTER_ALLOC_UNIT columns.\r
@@ -2945,7 +2968,7 @@ Done:
   //\r
   // Force GraphicsOutput mode to be set,\r
   //\r
-  \r
+\r
   Mode = &Private->GraphicsOutputModeBuffer[CurrentIndex];\r
   if ((GraphicsOutput != NULL) &&\r
       (Mode->HorizontalResolution == CurrentGraphicsOutputMode->Info->HorizontalResolution) &&\r
@@ -3024,7 +3047,7 @@ ConsplitterSetConsoleOutMode (
   MaxMode      = (UINTN) (TextOut->Mode->MaxMode);\r
 \r
   MaxModeInfo.Column = 0;\r
-  MaxModeInfo.Row    = 0; \r
+  MaxModeInfo.Row    = 0;\r
   ModeInfo.Column    = PcdGet32 (PcdConOutColumn);\r
   ModeInfo.Row       = PcdGet32 (PcdConOutRow);\r
 \r
@@ -3111,8 +3134,9 @@ ConSplitterTextOutAddDevice (
   EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;\r
   EFI_STATUS                           DeviceStatus;\r
 \r
-  Status                = EFI_SUCCESS;\r
-  CurrentNumOfConsoles  = Private->CurrentNumberOfConsoles;\r
+  Status                      = EFI_SUCCESS;\r
+  CurrentNumOfConsoles        = Private->CurrentNumberOfConsoles;\r
+  Private->AddingConOutDevice = TRUE;\r
 \r
   //\r
   // If the Text Out List is full, enlarge it by calling ConSplitterGrowBuffer().\r
@@ -3164,7 +3188,7 @@ ConSplitterTextOutAddDevice (
 \r
   DeviceStatus = EFI_DEVICE_ERROR;\r
   Status       = EFI_DEVICE_ERROR;\r
-  \r
+\r
   //\r
   // This device display mode will be added into Graphics Ouput modes.\r
   //\r
@@ -3269,6 +3293,8 @@ ConSplitterTextOutAddDevice (
   //\r
   ConsplitterSetConsoleOutMode (Private);\r
 \r
+  Private->AddingConOutDevice = FALSE;\r
+\r
   return Status;\r
 }\r
 \r
@@ -3334,7 +3360,7 @@ ConSplitterTextOutDeleteDevice (
                       Private->VirtualHandle,\r
                       &gEfiUgaDrawProtocolGuid,\r
                       &Private->UgaDraw\r
-                      );      \r
+                      );\r
     } else if (!FeaturePcdGet (PcdConOutUgaSupport)) {\r
       Status = gBS->UninstallProtocolInterface (\r
                       Private->VirtualHandle,\r
@@ -3445,11 +3471,46 @@ ConSplitterTextInReset (
 \r
   if (!EFI_ERROR (ReturnStatus)) {\r
     ToggleStateSyncReInitialization (Private);\r
+    //\r
+    // Empty the key queue.\r
+    //\r
+    Private->CurrentNumberOfKeys = 0;\r
   }\r
 \r
   return ReturnStatus;\r
 }\r
 \r
+/**\r
+  Dequeue the saved key from internal key queue.\r
+\r
+  @param  Private                  Protocol instance pointer.\r
+  @param  KeyData                  A pointer to a buffer that is filled in with the\r
+                                   keystroke state data for the key that was\r
+                                   pressed.\r
+  @retval EFI_NOT_FOUND            Queue is empty.\r
+  @retval EFI_SUCCESS              First key is dequeued and returned.\r
+**/\r
+EFI_STATUS\r
+ConSplitterTextInExDequeueKey (\r
+  IN  TEXT_IN_SPLITTER_PRIVATE_DATA   *Private,\r
+  OUT EFI_KEY_DATA                    *KeyData\r
+  )\r
+{\r
+  if (Private->CurrentNumberOfKeys == 0) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+  //\r
+  // Return the first saved key.\r
+  //\r
+  CopyMem (KeyData, &Private->KeyQueue[0], sizeof (EFI_KEY_DATA));\r
+  Private->CurrentNumberOfKeys--;\r
+  CopyMem (\r
+    &Private->KeyQueue[0],\r
+    &Private->KeyQueue[1],\r
+    Private->CurrentNumberOfKeys * sizeof (EFI_KEY_DATA)\r
+    );\r
+  return EFI_SUCCESS;\r
+}\r
 \r
 /**\r
   Reads the next keystroke from the input device. The WaitForKey Event can\r
@@ -3473,7 +3534,21 @@ ConSplitterTextInPrivateReadKeyStroke (
 {\r
   EFI_STATUS    Status;\r
   UINTN         Index;\r
-  EFI_INPUT_KEY CurrentKey;\r
+  EFI_KEY_DATA  KeyData;\r
+\r
+  //\r
+  // Return the first saved non-NULL key.\r
+  //\r
+  while (TRUE) {\r
+    Status = ConSplitterTextInExDequeueKey (Private, &KeyData);\r
+    if (EFI_ERROR (Status)) {\r
+      break;\r
+    }\r
+    if ((KeyData.Key.ScanCode != CHAR_NULL) || (KeyData.Key.UnicodeChar != SCAN_NULL)) {\r
+      CopyMem (Key, &KeyData.Key, sizeof (EFI_INPUT_KEY));\r
+      return Status;\r
+    }\r
+  }\r
 \r
   Key->UnicodeChar  = 0;\r
   Key->ScanCode     = SCAN_NULL;\r
@@ -3486,15 +3561,15 @@ ConSplitterTextInPrivateReadKeyStroke (
   for (Index = 0; Index < Private->CurrentNumberOfConsoles;) {\r
     Status = Private->TextInList[Index]->ReadKeyStroke (\r
                                           Private->TextInList[Index],\r
-                                          &CurrentKey\r
+                                          &KeyData.Key\r
                                           );\r
     if (!EFI_ERROR (Status)) {\r
       //\r
       // If it is not partial keystorke, return the key. Otherwise, continue\r
       // to read key from THIS physical console input device.\r
       //\r
-      if ((CurrentKey.ScanCode != CHAR_NULL) || (CurrentKey.UnicodeChar != SCAN_NULL)) {\r
-        *Key = CurrentKey;\r
+      if ((KeyData.Key.ScanCode != CHAR_NULL) || (KeyData.Key.UnicodeChar != SCAN_NULL)) {\r
+        CopyMem (Key, &KeyData.Key, sizeof (EFI_INPUT_KEY));\r
         return Status;\r
       }\r
     } else {\r
@@ -3540,7 +3615,7 @@ ConSplitterTextInReadKeyStroke (
   // Signal ConnectConIn event on first call in Lazy ConIn mode\r
   //\r
   if (!mConInIsConnect && PcdGetBool (PcdConInConnectOnDemand)) {\r
-    DEBUG ((EFI_D_INFO, "Connect ConIn in first ReadKeyStoke in Lazy ConIn mode.\n"));    \r
+    DEBUG ((EFI_D_INFO, "Connect ConIn in first ReadKeyStoke in Lazy ConIn mode.\n"));\r
     gBS->SignalEvent (Private->ConnectConInEvent);\r
     mConInIsConnect = TRUE;\r
   }\r
@@ -3681,6 +3756,10 @@ ConSplitterTextInResetEx (
 \r
   if (!EFI_ERROR (ReturnStatus)) {\r
     ToggleStateSyncReInitialization (Private);\r
+    //\r
+    // Empty the key queue.\r
+    //\r
+    Private->CurrentNumberOfKeys = 0;\r
   }\r
 \r
   return ReturnStatus;\r
@@ -3714,6 +3793,7 @@ ConSplitterTextInReadKeyStrokeEx (
   TEXT_IN_SPLITTER_PRIVATE_DATA *Private;\r
   EFI_STATUS                    Status;\r
   UINTN                         Index;\r
+  EFI_KEY_STATE                 KeyState;\r
   EFI_KEY_DATA                  CurrentKeyData;\r
 \r
 \r
@@ -3725,48 +3805,91 @@ ConSplitterTextInReadKeyStrokeEx (
 \r
   Private->KeyEventSignalState = FALSE;\r
 \r
-  KeyData->Key.UnicodeChar  = 0;\r
-  KeyData->Key.ScanCode     = SCAN_NULL;\r
-\r
   //\r
   // Signal ConnectConIn event on first call in Lazy ConIn mode\r
   //\r
   if (!mConInIsConnect && PcdGetBool (PcdConInConnectOnDemand)) {\r
-    DEBUG ((EFI_D_INFO, "Connect ConIn in first ReadKeyStoke in Lazy ConIn mode.\n"));    \r
+    DEBUG ((EFI_D_INFO, "Connect ConIn in first ReadKeyStoke in Lazy ConIn mode.\n"));\r
     gBS->SignalEvent (Private->ConnectConInEvent);\r
     mConInIsConnect = TRUE;\r
   }\r
 \r
   //\r
-  // if no physical console input device exists, return EFI_NOT_READY;\r
-  // if any physical console input device has key input,\r
-  // return the key and EFI_SUCCESS.\r
+  // Return the first saved key.\r
   //\r
-  for (Index = 0; Index < Private->CurrentNumberOfExConsoles;) {\r
+  Status = ConSplitterTextInExDequeueKey (Private, KeyData);\r
+  if (!EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+  ASSERT (Private->CurrentNumberOfKeys == 0);\r
+\r
+  ZeroMem (&KeyState, sizeof (KeyState));\r
+\r
+  //\r
+  // Iterate through all physical consoles to get key state.\r
+  // Some physical consoles may return valid key.\r
+  // Queue the valid keys.\r
+  //\r
+  for (Index = 0; Index < Private->CurrentNumberOfExConsoles; Index++) {\r
+    ZeroMem (&CurrentKeyData, sizeof (EFI_KEY_DATA));\r
     Status = Private->TextInExList[Index]->ReadKeyStrokeEx (\r
-                                          Private->TextInExList[Index],\r
-                                          &CurrentKeyData\r
-                                          );\r
+                                             Private->TextInExList[Index],\r
+                                             &CurrentKeyData\r
+                                             );\r
+    if (EFI_ERROR (Status) && (Status != EFI_NOT_READY)) {\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // Consolidate the key state from all physical consoles.\r
+    //\r
+    if ((CurrentKeyData.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) != 0) {\r
+      KeyState.KeyShiftState |= CurrentKeyData.KeyState.KeyShiftState;\r
+    }\r
+    if ((CurrentKeyData.KeyState.KeyToggleState & EFI_TOGGLE_STATE_VALID) != 0) {\r
+      KeyState.KeyToggleState |= CurrentKeyData.KeyState.KeyToggleState;\r
+    }\r
+\r
     if (!EFI_ERROR (Status)) {\r
       //\r
       // If virtual KeyState has been required to be exposed, or it is not\r
-      // partial keystorke, return the key. Otherwise, continue to read key\r
-      // from THIS physical console input device.\r
+      // partial keystorke, queue the key.\r
+      // It's possible that user presses at multiple keyboards at the same moment,\r
+      // Private->KeyQueue[] are the storage to save all the keys.\r
       //\r
       if ((Private->VirtualKeyStateExported) ||\r
           (CurrentKeyData.Key.ScanCode != CHAR_NULL) ||\r
           (CurrentKeyData.Key.UnicodeChar != SCAN_NULL)) {\r
-        CopyMem (KeyData, &CurrentKeyData, sizeof (CurrentKeyData));\r
-        return Status;\r
+        CopyMem (\r
+          &Private->KeyQueue[Private->CurrentNumberOfKeys],\r
+          &CurrentKeyData,\r
+          sizeof (EFI_KEY_DATA)\r
+          );\r
+        Private->CurrentNumberOfKeys++;\r
       }\r
-    } else {\r
-      //\r
-      // Continue to read key from NEXT physical console input device.\r
-      //\r
-      Index++;\r
     }\r
   }\r
 \r
+  //\r
+  // Consolidate the key state for all keys in Private->KeyQueue[]\r
+  //\r
+  for (Index = 0; Index < Private->CurrentNumberOfKeys; Index++) {\r
+    CopyMem (&Private->KeyQueue[Index].KeyState, &KeyState, sizeof (EFI_KEY_STATE));\r
+  }\r
+\r
+  //\r
+  // Return the first saved key.\r
+  //\r
+  Status = ConSplitterTextInExDequeueKey (Private, KeyData);\r
+  if (!EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Always return the key state even there is no key pressed.\r
+  //\r
+  ZeroMem (&KeyData->Key, sizeof (KeyData->Key));\r
+  CopyMem (&KeyData->KeyState, &KeyState, sizeof (KeyData->KeyState));\r
   return EFI_NOT_READY;\r
 }\r
 \r
@@ -3842,11 +3965,14 @@ ConSplitterTextInSetState (
   Register a notification function for a particular keystroke for the input device.\r
 \r
   @param  This                     Protocol instance pointer.\r
-  @param  KeyData                  A pointer to a buffer that is filled in with the\r
-                                   keystroke information data for the key that was\r
-                                   pressed.\r
+  @param  KeyData                  A pointer to a buffer that is filled in with\r
+                                   the keystroke information for the key that was\r
+                                   pressed. If KeyData.Key, KeyData.KeyState.KeyToggleState\r
+                                   and KeyData.KeyState.KeyShiftState are 0, then any incomplete\r
+                                   keystroke will trigger a notification of the KeyNotificationFunction.\r
   @param  KeyNotificationFunction  Points to the function to be called when the key\r
-                                   sequence is typed specified by KeyData.\r
+                                   sequence is typed specified by KeyData. This notification function\r
+                                   should be called at <=TPL_CALLBACK.\r
   @param  NotifyHandle             Points to the unique handle assigned to the\r
                                    registered notification.\r
 \r
@@ -4515,7 +4641,7 @@ ConSplitterTextOutOutputString (
     Private->TextOutMode.CursorRow    = Private->TextOutList[0].TextOut->Mode->CursorRow;\r
   } else {\r
     //\r
-    // When there is no real console devices in system, \r
+    // When there is no real console devices in system,\r
     // update cursor position for the virtual device in consplitter.\r
     //\r
     Private->TextOut.QueryMode (\r
@@ -4523,28 +4649,28 @@ ConSplitterTextOutOutputString (
                        Private->TextOutMode.Mode,\r
                        &MaxColumn,\r
                        &MaxRow\r
-                       );    \r
+                       );\r
     for (; *WString != CHAR_NULL; WString++) {\r
       switch (*WString) {\r
       case CHAR_BACKSPACE:\r
         if (Private->TextOutMode.CursorColumn == 0 && Private->TextOutMode.CursorRow > 0) {\r
           Private->TextOutMode.CursorRow--;\r
-          Private->TextOutMode.CursorColumn = (INT32) (MaxColumn - 1);          \r
+          Private->TextOutMode.CursorColumn = (INT32) (MaxColumn - 1);\r
         } else if (Private->TextOutMode.CursorColumn > 0) {\r
           Private->TextOutMode.CursorColumn--;\r
         }\r
         break;\r
-      \r
+\r
       case CHAR_LINEFEED:\r
         if (Private->TextOutMode.CursorRow < (INT32) (MaxRow - 1)) {\r
           Private->TextOutMode.CursorRow++;\r
         }\r
         break;\r
-      \r
+\r
       case CHAR_CARRIAGE_RETURN:\r
         Private->TextOutMode.CursorColumn = 0;\r
         break;\r
-      \r
+\r
       default:\r
         if (Private->TextOutMode.CursorColumn < (INT32) (MaxColumn - 1)) {\r
           Private->TextOutMode.CursorColumn++;\r
@@ -4728,12 +4854,18 @@ ConSplitterTextOutSetMode (
   //\r
   TextOutModeMap = Private->TextOutModeMap + Private->TextOutListCount * ModeNumber;\r
   for (Index = 0, ReturnStatus = EFI_SUCCESS; Index < Private->CurrentNumberOfConsoles; Index++) {\r
-    Status = Private->TextOutList[Index].TextOut->SetMode (\r
-                                                    Private->TextOutList[Index].TextOut,\r
-                                                    TextOutModeMap[Index]\r
-                                                    );\r
-    if (EFI_ERROR (Status)) {\r
-      ReturnStatus = Status;\r
+    //\r
+    // While adding a console out device do not set same mode again for the same device.\r
+    //\r
+    if ((!Private->AddingConOutDevice) ||\r
+        (TextOutModeMap[Index] != Private->TextOutList[Index].TextOut->Mode->Mode)) {\r
+      Status = Private->TextOutList[Index].TextOut->SetMode (\r
+                                                      Private->TextOutList[Index].TextOut,\r
+                                                      TextOutModeMap[Index]\r
+                                                      );\r
+      if (EFI_ERROR (Status)) {\r
+        ReturnStatus = Status;\r
+      }\r
     }\r
   }\r
 \r
@@ -4979,21 +5111,3 @@ ConSplitterTextOutEnableCursor (
 \r
   return ReturnStatus;\r
 }\r
-\r
-\r
-/**\r
-  An empty function to pass error checking of CreateEventEx ().\r
-\r
-  @param  Event                 Event whose notification function is being invoked.\r
-  @param  Context               Pointer to the notification function's context,\r
-                                which is implementation-dependent.\r
-\r
-**/\r
-VOID\r
-EFIAPI\r
-ConSplitterEmptyCallbackFunction (\r
-  IN EFI_EVENT                Event,\r
-  IN VOID                     *Context\r
-  )\r
-{\r
-}\r