/** @file
  Install Serial IO Protocol that layers on top of a Debug Communication Library instance.

  Copyright (c) 2012 - 2018, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "DxeDebugAgentLib.h"

//
// Serial I/O Protocol Interface definitions.
//

/**
  Reset serial device.

  @param[in] This           Pointer to EFI_SERIAL_IO_PROTOCOL.

  @retval EFI_SUCCESS       Reset successfully.

**/
EFI_STATUS
EFIAPI
SerialReset (
  IN EFI_SERIAL_IO_PROTOCOL  *This
  );

/**
  Set new attributes to a serial device.

  @param[in]  This                Pointer to EFI_SERIAL_IO_PROTOCOL.
  @param[in]  BaudRate            The baudrate of the serial device.
  @param[in]  ReceiveFifoDepth    The depth of receive FIFO buffer.
  @param[in]  Timeout             The request timeout for a single char.
  @param[in]  Parity              The type of parity used in serial device.
  @param[in]  DataBits            Number of databits used in serial device.
  @param[in]  StopBits            Number of stopbits used in serial device.

  @retval EFI_SUCCESS             The new attributes were set.
  @retval EFI_INVALID_PARAMETER   One or more attributes have an unsupported value.
  @retval EFI_DEVICE_ERROR        The serial device is not functioning correctly (no return).

**/
EFI_STATUS
EFIAPI
SerialSetAttributes (
  IN EFI_SERIAL_IO_PROTOCOL  *This,
  IN UINT64                  BaudRate,
  IN UINT32                  ReceiveFifoDepth,
  IN UINT32                  Timeout,
  IN EFI_PARITY_TYPE         Parity,
  IN UINT8                   DataBits,
  IN EFI_STOP_BITS_TYPE      StopBits
  );

/**
  Set Control Bits.

  @param[in] This            Pointer to EFI_SERIAL_IO_PROTOCOL.
  @param[in] Control         Control bits that can be settable.

  @retval EFI_SUCCESS        New Control bits were set successfully.
  @retval EFI_UNSUPPORTED    The Control bits wanted to set are not supported.

**/
EFI_STATUS
EFIAPI
SerialSetControl (
  IN EFI_SERIAL_IO_PROTOCOL  *This,
  IN UINT32                  Control
  );

/**
  Get ControlBits.

  @param[in]  This         Pointer to EFI_SERIAL_IO_PROTOCOL.
  @param[out] Control      Control signals of the serial device.

  @retval EFI_SUCCESS  Get Control signals successfully.

**/
EFI_STATUS
EFIAPI
SerialGetControl (
  IN EFI_SERIAL_IO_PROTOCOL  *This,
  OUT UINT32                 *Control
  );

/**
  Write the specified number of bytes to serial device.

  @param[in]      This       Pointer to EFI_SERIAL_IO_PROTOCOL.
  @param[in, out] BufferSize On input the size of Buffer, on output the amount of
                             data actually written.
  @param[in]      Buffer     The buffer of data to write.

  @retval EFI_SUCCESS        The data were written successfully.
  @retval EFI_DEVICE_ERROR   The device reported an error.
  @retval EFI_TIMEOUT        The write operation was stopped due to timeout.

**/
EFI_STATUS
EFIAPI
SerialWrite (
  IN EFI_SERIAL_IO_PROTOCOL  *This,
  IN OUT UINTN               *BufferSize,
  IN VOID                    *Buffer
  );

/**
  Read the specified number of bytes from serial device.

  @param[in] This            Pointer to EFI_SERIAL_IO_PROTOCOL.
  @param[in, out] BufferSize On input the size of Buffer, on output the amount of
                             data returned in buffer.
  @param[out] Buffer         The buffer to return the data into.

  @retval EFI_SUCCESS        The data were read successfully.
  @retval EFI_DEVICE_ERROR   The device reported an error.
  @retval EFI_TIMEOUT        The read operation was stopped due to timeout.

**/
EFI_STATUS
EFIAPI
SerialRead (
  IN EFI_SERIAL_IO_PROTOCOL  *This,
  IN OUT UINTN               *BufferSize,
  OUT VOID                   *Buffer
  );

//
// Serial Driver Defaults
//
#define SERIAL_PORT_DEFAULT_RECEIVE_FIFO_DEPTH  1
#define SERIAL_PORT_DEFAULT_TIMEOUT             1000000
#define SERIAL_PORT_DEFAULT_CONTROL_MASK        0
#define SERIAL_PORT_LOOPBACK_BUFFER_FULL        BIT8

//
// EFI_SERIAL_IO_MODE instance
//
EFI_SERIAL_IO_MODE  mSerialIoMode = {
  SERIAL_PORT_DEFAULT_CONTROL_MASK,
  SERIAL_PORT_DEFAULT_TIMEOUT,
  0,  // default BaudRate
  SERIAL_PORT_DEFAULT_RECEIVE_FIFO_DEPTH,
  0,  // default DataBits
  0,  // default Parity
  0   // default StopBits
};

//
// EFI_SERIAL_IO_PROTOCOL instance
//
EFI_SERIAL_IO_PROTOCOL  mSerialIo = {
  SERIAL_IO_INTERFACE_REVISION,
  SerialReset,
  SerialSetAttributes,
  SerialSetControl,
  SerialGetControl,
  SerialWrite,
  SerialRead,
  &mSerialIoMode
};

//
// Serial IO Device Path definition
//
typedef struct {
  VENDOR_DEVICE_PATH          VendorDevicePath;
  UART_DEVICE_PATH            UartDevicePath;
  EFI_DEVICE_PATH_PROTOCOL    EndDevicePath;
} SERIAL_IO_DEVICE_PATH;

//
// Serial IO Device Patch instance
//
SERIAL_IO_DEVICE_PATH  mSerialIoDevicePath = {
  {
    {
      HARDWARE_DEVICE_PATH,
      HW_VENDOR_DP,
      {
        (UINT8)(sizeof (VENDOR_DEVICE_PATH)),
        (UINT8)((sizeof (VENDOR_DEVICE_PATH)) >> 8)
      }
    },
    EFI_DEBUG_AGENT_GUID,
  },
  {
    {
      MESSAGING_DEVICE_PATH,
      MSG_UART_DP,
      {
        (UINT8)(sizeof (UART_DEVICE_PATH)),
        (UINT8)((sizeof (UART_DEVICE_PATH)) >> 8)
      }
    },
    0,
    0,  // default BaudRate
    0,  // default DataBits
    0,  // default Parity
    0,  // default StopBits
  },
  {
    END_DEVICE_PATH_TYPE,
    END_ENTIRE_DEVICE_PATH_SUBTYPE,
    {
      END_DEVICE_PATH_LENGTH,
      0
    }
  }
};

#define DEBUG_SERIAL_IO_FIFO_DEPTH  10
//
//  Data buffer for Terminal input character and Debug Symbols.
//  The depth is DEBUG_SERIAL_IO_FIFO_DEPTH.
//  Fields:
//      First   UINT8: The index of the first data in array Data[].
//      Last    UINT8: The index, which you can put a new data into array Data[].
//      Surplus UINT8: Identify how many data you can put into array Data[].
//      Data[]  UINT8: An array, which used to store data.
//
typedef struct {
  UINT8    First;
  UINT8    Last;
  UINT8    Surplus;
  UINT8    Data[DEBUG_SERIAL_IO_FIFO_DEPTH];
} DEBUG_SERIAL_FIFO;

//
// Global Variables
//
EFI_HANDLE         mSerialIoHandle        = NULL;
UINTN              mLoopbackBuffer        = 0;
DEBUG_SERIAL_FIFO  mSerialFifoForTerminal = {
  0, 0, DEBUG_SERIAL_IO_FIFO_DEPTH, { 0 }
};
DEBUG_SERIAL_FIFO  mSerialFifoForDebug = {
  0, 0, DEBUG_SERIAL_IO_FIFO_DEPTH, { 0 }
};

/**
  Detect whether specific FIFO is empty or not.

  @param[in]  Fifo    A pointer to the Data Structure DEBUG_SERIAL_FIFO.

  @return whether specific FIFO is empty or not.

**/
BOOLEAN
IsDebugTerminalFifoEmpty (
  IN DEBUG_SERIAL_FIFO  *Fifo
  )
{
  if (Fifo->Surplus == DEBUG_SERIAL_IO_FIFO_DEPTH) {
    return TRUE;
  }

  return FALSE;
}

/**
  Detect whether specific FIFO is full or not.

  @param[in] Fifo    A pointer to the Data Structure DEBUG_SERIAL_FIFO.

  @return whether specific FIFO is full or not.

**/
BOOLEAN
IsDebugTerminalFifoFull (
  IN DEBUG_SERIAL_FIFO  *Fifo
  )

{
  if (Fifo->Surplus == 0) {
    return TRUE;
  }

  return FALSE;
}

/**
  Add data to specific FIFO.

  @param[in] Fifo               A pointer to the Data Structure DEBUG_SERIAL_FIFO.
  @param[in] Data               The data added to FIFO.

  @retval EFI_SUCCESS           Add data to specific FIFO successfully.
  @retval EFI_OUT_OF_RESOURCE   Failed to add data because FIFO is already full.

**/
EFI_STATUS
DebugTerminalFifoAdd (
  IN DEBUG_SERIAL_FIFO  *Fifo,
  IN UINT8              Data
  )

{
  //
  // if FIFO full can not add data
  //
  if (IsDebugTerminalFifoFull (Fifo)) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // FIFO is not full can add data
  //
  Fifo->Data[Fifo->Last] = Data;
  Fifo->Surplus--;
  Fifo->Last++;
  if (Fifo->Last == DEBUG_SERIAL_IO_FIFO_DEPTH) {
    Fifo->Last = 0;
  }

  return EFI_SUCCESS;
}

/**
  Remove data from specific FIFO.

  @param[in]  Fifo              A pointer to the Data Structure DEBUG_SERIAL_FIFO.
  @param[out] Data              The data removed from FIFO.

  @retval EFI_SUCCESS           Remove data from specific FIFO successfully.
  @retval EFI_OUT_OF_RESOURCE   Failed to remove data because FIFO is empty.

**/
EFI_STATUS
DebugTerminalFifoRemove (
  IN  DEBUG_SERIAL_FIFO  *Fifo,
  OUT UINT8              *Data
  )
{
  //
  // if FIFO is empty, no data can remove
  //
  if (IsDebugTerminalFifoEmpty (Fifo)) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // FIFO is not empty, can remove data
  //
  *Data = Fifo->Data[Fifo->First];
  Fifo->Surplus++;
  Fifo->First++;
  if (Fifo->First == DEBUG_SERIAL_IO_FIFO_DEPTH) {
    Fifo->First = 0;
  }

  return EFI_SUCCESS;
}

/**
  Install EFI Serial IO protocol based on Debug Communication Library.

**/
VOID
InstallSerialIo (
  VOID
  )
{
  EFI_STATUS  Status;

  Status = gBS->InstallMultipleProtocolInterfaces (
                  &mSerialIoHandle,
                  &gEfiDevicePathProtocolGuid,
                  &mSerialIoDevicePath,
                  &gEfiSerialIoProtocolGuid,
                  &mSerialIo,
                  NULL
                  );
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "Debug Agent: Failed to install EFI Serial IO Protocol on Debug Port!\n"));
  }
}

/**
  Reset serial device.

  @param[in] This           Pointer to EFI_SERIAL_IO_PROTOCOL.

  @retval EFI_SUCCESS       Reset successfully.

**/
EFI_STATUS
EFIAPI
SerialReset (
  IN EFI_SERIAL_IO_PROTOCOL  *This
  )
{
  mSerialIoMode.ControlMask = SERIAL_PORT_DEFAULT_CONTROL_MASK;
  mLoopbackBuffer           = 0;
  //
  // Not reset serial device hardware indeed.
  //
  return EFI_SUCCESS;
}

/**
  Set new attributes to a serial device.

  @param[in]  This                Pointer to EFI_SERIAL_IO_PROTOCOL.
  @param[in]  BaudRate            The baudrate of the serial device.
  @param[in]  ReceiveFifoDepth    The depth of receive FIFO buffer.
  @param[in]  Timeout             The request timeout for a single char.
  @param[in]  Parity              The type of parity used in serial device.
  @param[in]  DataBits            Number of databits used in serial device.
  @param[in]  StopBits            Number of stopbits used in serial device.

  @retval EFI_SUCCESS             The new attributes were set.
  @retval EFI_INVALID_PARAMETER   One or more attributes have an unsupported value.
  @retval EFI_DEVICE_ERROR        The serial device is not functioning correctly (no return).

**/
EFI_STATUS
EFIAPI
SerialSetAttributes (
  IN EFI_SERIAL_IO_PROTOCOL  *This,
  IN UINT64                  BaudRate,
  IN UINT32                  ReceiveFifoDepth,
  IN UINT32                  Timeout,
  IN EFI_PARITY_TYPE         Parity,
  IN UINT8                   DataBits,
  IN EFI_STOP_BITS_TYPE      StopBits
  )
{
  //
  // The Debug Communication Library CAN NOT change communications parameters (if it has)
  // actually. Because it also has no any idea on what parameters are based on, we cannot
  // check the input parameters (like BaudRate, Parity, DataBits and StopBits).
  //

  //
  // Update the Timeout value in the mode structure based on the request.
  // The Debug Communication Library can not support a timeout on writes, but the timeout on
  // reads can be provided by this module.
  //
  if (Timeout == 0) {
    mSerialIoMode.Timeout = SERIAL_PORT_DEFAULT_TIMEOUT;
  } else {
    mSerialIoMode.Timeout = Timeout;
  }

  //
  // Update the ReceiveFifoDepth value in the mode structure based on the request.
  // This module assumes that the Debug Communication Library uses a FIFO depth of
  // SERIAL_PORT_DEFAULT_RECEIVE_FIFO_DEPTH.  The Debug Communication Library may actually be
  // using a larger FIFO, but there is no way to tell.
  //
  if ((ReceiveFifoDepth == 0) || (ReceiveFifoDepth >= SERIAL_PORT_DEFAULT_RECEIVE_FIFO_DEPTH)) {
    mSerialIoMode.ReceiveFifoDepth = SERIAL_PORT_DEFAULT_RECEIVE_FIFO_DEPTH;
  } else {
    return EFI_INVALID_PARAMETER;
  }

  return EFI_SUCCESS;
}

/**
  Set Control Bits.

  @param[in] This            Pointer to EFI_SERIAL_IO_PROTOCOL.
  @param[in] Control         Control bits that can be settable.

  @retval EFI_SUCCESS        New Control bits were set successfully.
  @retval EFI_UNSUPPORTED    The Control bits wanted to set are not supported.

**/
EFI_STATUS
EFIAPI
SerialSetControl (
  IN EFI_SERIAL_IO_PROTOCOL  *This,
  IN UINT32                  Control
  )
{
  //
  // The only control bit supported by this module is software loopback.
  // If any other bit is set, then return an error
  //
  if ((Control & (~EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE)) != 0) {
    return EFI_UNSUPPORTED;
  }

  mSerialIoMode.ControlMask = Control;
  return EFI_SUCCESS;
}

/**
  Get ControlBits.

  @param[in]  This         Pointer to EFI_SERIAL_IO_PROTOCOL.
  @param[out] Control      Control signals of the serial device.

  @retval EFI_SUCCESS  Get Control signals successfully.

**/
EFI_STATUS
EFIAPI
SerialGetControl (
  IN EFI_SERIAL_IO_PROTOCOL  *This,
  OUT UINT32                 *Control
  )
{
  DEBUG_PORT_HANDLE  Handle;
  BOOLEAN            DebugTimerInterruptState;
  EFI_TPL            Tpl;

  //
  // Raise TPL to prevent recursion from EFI timer interrupts
  //
  Tpl = gBS->RaiseTPL (TPL_NOTIFY);

  //
  // Save and disable Debug Timer interrupt to avoid it to access Debug Port
  //
  DebugTimerInterruptState = SaveAndSetDebugTimerInterrupt (FALSE);
  Handle                   = GetDebugPortHandle ();

  //
  // Always assume the output buffer is empty and the Debug Communication Library can process
  // more write requests.
  //
  *Control = mSerialIoMode.ControlMask | EFI_SERIAL_OUTPUT_BUFFER_EMPTY;

  //
  // Check to see if the Terminal FIFO is empty and
  // check to see if the input buffer in the Debug Communication Library is empty
  //
  if (!IsDebugTerminalFifoEmpty (&mSerialFifoForTerminal) || DebugPortPollBuffer (Handle)) {
    *Control &= ~EFI_SERIAL_INPUT_BUFFER_EMPTY;
  }

  //
  // Restore Debug Timer interrupt
  //
  SaveAndSetDebugTimerInterrupt (DebugTimerInterruptState);

  //
  // Restore to original TPL
  //
  gBS->RestoreTPL (Tpl);

  return EFI_SUCCESS;
}

/**
  Write the specified number of bytes to serial device.

  @param[in]      This       Pointer to EFI_SERIAL_IO_PROTOCOL.
  @param[in, out] BufferSize On input the size of Buffer, on output the amount of
                             data actually written.
  @param[in]      Buffer     The buffer of data to write.

  @retval EFI_SUCCESS        The data were written successfully.
  @retval EFI_DEVICE_ERROR   The device reported an error.
  @retval EFI_TIMEOUT        The write operation was stopped due to timeout.

**/
EFI_STATUS
EFIAPI
SerialWrite (
  IN EFI_SERIAL_IO_PROTOCOL  *This,
  IN OUT UINTN               *BufferSize,
  IN VOID                    *Buffer
  )
{
  DEBUG_PORT_HANDLE  Handle;
  BOOLEAN            DebugTimerInterruptState;
  EFI_TPL            Tpl;

  //
  // Raise TPL to prevent recursion from EFI timer interrupts
  //
  Tpl = gBS->RaiseTPL (TPL_NOTIFY);

  //
  // Save and disable Debug Timer interrupt to avoid it to access Debug Port
  //
  DebugTimerInterruptState = SaveAndSetDebugTimerInterrupt (FALSE);
  Handle                   = GetDebugPortHandle ();

  if ((mSerialIoMode.ControlMask & EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE) != 0) {
    if (*BufferSize == 0) {
      return EFI_SUCCESS;
    }

    if ((mLoopbackBuffer & SERIAL_PORT_LOOPBACK_BUFFER_FULL) != 0) {
      *BufferSize = 0;
      return EFI_TIMEOUT;
    }

    mLoopbackBuffer = SERIAL_PORT_LOOPBACK_BUFFER_FULL | *(UINT8 *)Buffer;
    *BufferSize     = 1;
  } else {
    *BufferSize = DebugPortWriteBuffer (Handle, Buffer, *BufferSize);
  }

  //
  // Restore Debug Timer interrupt
  //
  SaveAndSetDebugTimerInterrupt (DebugTimerInterruptState);

  //
  // Restore to original TPL
  //
  gBS->RestoreTPL (Tpl);

  return EFI_SUCCESS;
}

/**
  Read the specified number of bytes from serial device.

  @param[in] This            Pointer to EFI_SERIAL_IO_PROTOCOL.
  @param[in, out] BufferSize On input the size of Buffer, on output the amount of
                             data returned in buffer.
  @param[out] Buffer         The buffer to return the data into.

  @retval EFI_SUCCESS        The data were read successfully.
  @retval EFI_DEVICE_ERROR   The device reported an error.
  @retval EFI_TIMEOUT        The read operation was stopped due to timeout.

**/
EFI_STATUS
EFIAPI
SerialRead (
  IN EFI_SERIAL_IO_PROTOCOL  *This,
  IN OUT UINTN               *BufferSize,
  OUT VOID                   *Buffer
  )
{
  EFI_STATUS           Status;
  UINTN                Index;
  UINT8                *Uint8Buffer;
  BOOLEAN              DebugTimerInterruptState;
  EFI_TPL              Tpl;
  DEBUG_PORT_HANDLE    Handle;
  DEBUG_PACKET_HEADER  DebugHeader;
  UINT8                *Data8;

  //
  // Raise TPL to prevent recursion from EFI timer interrupts
  //
  Tpl = gBS->RaiseTPL (TPL_NOTIFY);

  //
  // Save and disable Debug Timer interrupt to avoid it to access Debug Port
  //
  DebugTimerInterruptState = SaveAndSetDebugTimerInterrupt (FALSE);
  Handle                   = GetDebugPortHandle ();

  Data8       = (UINT8 *)&DebugHeader;
  Uint8Buffer = (UINT8 *)Buffer;
  if ((mSerialIoMode.ControlMask & EFI_SERIAL_SOFTWARE_LOOPBACK_ENABLE) != 0) {
    if ((mLoopbackBuffer & SERIAL_PORT_LOOPBACK_BUFFER_FULL) == 0) {
      return EFI_TIMEOUT;
    }

    *Uint8Buffer    = (UINT8)(mLoopbackBuffer & 0xff);
    mLoopbackBuffer = 0;
    *BufferSize     = 1;
  } else {
    for (Index = 0; Index < *BufferSize; Index++) {
      //
      // Read input character from terminal FIFO firstly
      //
      Status = DebugTerminalFifoRemove (&mSerialFifoForTerminal, Data8);
      if (Status == EFI_SUCCESS) {
        *Uint8Buffer = *Data8;
        Uint8Buffer++;
        continue;
      }

      //
      // Read the input character from Debug Port
      //
      if (!DebugPortPollBuffer (Handle)) {
        break;
      }

      DebugAgentReadBuffer (Handle, Data8, 1, 0);

      if (*Data8 == DEBUG_STARTING_SYMBOL_ATTACH) {
        //
        // Add the debug symbol into Debug FIFO
        //
        DebugAgentMsgPrint (DEBUG_AGENT_INFO, "Terminal Timer attach symbol received %x", *Data8);
        DebugTerminalFifoAdd (&mSerialFifoForDebug, *Data8);
      } else if (*Data8 == DEBUG_STARTING_SYMBOL_NORMAL) {
        Status = ReadRemainingBreakPacket (Handle, &DebugHeader);
        if (Status == EFI_SUCCESS) {
          DebugAgentMsgPrint (DEBUG_AGENT_INFO, "Terminal Timer break symbol received %x", DebugHeader.Command);
          DebugTerminalFifoAdd (&mSerialFifoForDebug, DebugHeader.Command);
        }

        if (Status == EFI_TIMEOUT) {
          continue;
        }
      } else {
        *Uint8Buffer = *Data8;
        Uint8Buffer++;
      }
    }

    *BufferSize = (UINTN)Uint8Buffer - (UINTN)Buffer;
  }

  //
  // Restore Debug Timer interrupt
  //
  SaveAndSetDebugTimerInterrupt (DebugTimerInterruptState);

  //
  // Restore to original TPL
  //
  gBS->RestoreTPL (Tpl);

  return EFI_SUCCESS;
}

/**
  Read the Attach/Break-in symbols from the debug port.

  @param[in]  Handle         Pointer to Debug Port handle.
  @param[out] BreakSymbol    Returned break symbol.

  @retval EFI_SUCCESS        Read the symbol in BreakSymbol.
  @retval EFI_NOT_FOUND      No read the break symbol.

**/
EFI_STATUS
DebugReadBreakFromDebugPort (
  IN  DEBUG_PORT_HANDLE  Handle,
  OUT UINT8              *BreakSymbol
  )
{
  EFI_STATUS           Status;
  DEBUG_PACKET_HEADER  DebugHeader;
  UINT8                *Data8;

  *BreakSymbol = 0;
  //
  // If Debug Port buffer has data, read it till it was break symbol or Debug Port buffer empty.
  //
  Data8 = (UINT8 *)&DebugHeader;
  while (TRUE) {
    //
    // If start symbol is not received
    //
    if (!DebugPortPollBuffer (Handle)) {
      //
      // If no data in Debug Port, exit
      //
      break;
    }

    //
    // Try to read the start symbol
    //
    DebugAgentReadBuffer (Handle, Data8, 1, 0);
    if (*Data8 == DEBUG_STARTING_SYMBOL_ATTACH) {
      DebugAgentMsgPrint (DEBUG_AGENT_INFO, "Debug Timer attach symbol received %x", *Data8);
      *BreakSymbol = *Data8;
      return EFI_SUCCESS;
    }

    if (*Data8 == DEBUG_STARTING_SYMBOL_NORMAL) {
      Status = ReadRemainingBreakPacket (Handle, &DebugHeader);
      if (Status == EFI_SUCCESS) {
        DebugAgentMsgPrint (DEBUG_AGENT_INFO, "Debug Timer break symbol received %x", DebugHeader.Command);
        *BreakSymbol = DebugHeader.Command;
        return EFI_SUCCESS;
      }

      if (Status == EFI_TIMEOUT) {
        break;
      }
    } else {
      //
      // Add to Terminal FIFO
      //
      DebugTerminalFifoAdd (&mSerialFifoForTerminal, *Data8);
    }
  }

  return EFI_NOT_FOUND;
}

/**
  Read the Attach/Break-in symbols.

  @param[in]  Handle         Pointer to Debug Port handle.
  @param[out] BreakSymbol    Returned break symbol.

  @retval EFI_SUCCESS        Read the symbol in BreakSymbol.
  @retval EFI_NOT_FOUND      No read the break symbol.

**/
EFI_STATUS
DebugReadBreakSymbol (
  IN  DEBUG_PORT_HANDLE  Handle,
  OUT UINT8              *BreakSymbol
  )
{
  EFI_STATUS  Status;
  UINT8       Data8;

  //
  // Read break symbol from debug FIFO firstly
  //
  Status = DebugTerminalFifoRemove (&mSerialFifoForDebug, &Data8);
  if (Status == EFI_SUCCESS) {
    *BreakSymbol = Data8;
    return EFI_SUCCESS;
  } else {
    //
    // Read Break symbol from debug port
    //
    return DebugReadBreakFromDebugPort (Handle, BreakSymbol);
  }
}
