/** @file
  Data source for network testing.

  Copyright (c) 2011 - 2024, Intel Corporation. All rights reserved.
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include <errno.h>
#include <Uefi.h>

#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/PcdLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>

#include <netinet/in.h>

#include <Protocol/ServiceBinding.h>
#include <Protocol/Tcp4.h>

#include <sys/EfiSysCall.h>
#include <sys/poll.h>
#include <sys/socket.h>

#include <stdio.h>
#include <string.h>


#define DATA_SAMPLE_SHIFT           5       ///<  Shift for number of samples
#define RANGE_SWITCH        ( 1024 * 1024 ) ///<  Switch display ranges
#define DATA_RATE_UPDATE_SHIFT      2       ///<  2n seconds between updates
#define AVERAGE_SHIFT_COUNT ( 6 - DATA_RATE_UPDATE_SHIFT )  ///<  2n samples in average
#define DATA_SAMPLES        ( 1 << DATA_SAMPLE_SHIFT )      ///<  Number of samples

#define TPL_DATASOURCE      TPL_CALLBACK  ///<  Synchronization TPL

#define PACKET_SIZE                 1448  ///<  Size of data packets
#define DATA_BUFFER_SIZE    (( 65536 / PACKET_SIZE ) * PACKET_SIZE )  ///<  Buffer size in bytes


//
//  Socket Data
//
int Socket = -1;

//
//  TCP V4 Data
//
BOOLEAN bTcp4;                      ///<  TRUE if TCP4 is being used
BOOLEAN bTcp4Connected;             ///<  TRUE if connected to remote system
BOOLEAN bTcp4Connecting;            ///<  TRUE while connection in progress
UINTN Tcp4Index;                    ///<  Index into handle array
EFI_HANDLE Tcp4Controller;          ///<  Network controller handle
EFI_HANDLE Tcp4Handle;              ///<  TCP4 port handle
EFI_TCP4_PROTOCOL * pTcp4Protocol;  ///<  TCP4 protocol pointer
EFI_SERVICE_BINDING_PROTOCOL * pTcp4Service;  ///<  TCP4 Service binding
EFI_TCP4_CONFIG_DATA Tcp4ConfigData;///<  TCP4 configuration data
EFI_TCP4_OPTION Tcp4Option;         ///<  TCP4 port options
EFI_TCP4_CLOSE_TOKEN Tcp4CloseToken;///<  Close control
EFI_TCP4_CONNECTION_TOKEN Tcp4ConnectToken; ///<  Connection control
EFI_TCP4_LISTEN_TOKEN Tcp4ListenToken;      ///<  Listen control
EFI_TCP4_IO_TOKEN Tcp4TxToken;      ///<  Normal data token

//
//  Timer Data
//
volatile BOOLEAN bTick;
BOOLEAN bTimerRunning;
EFI_EVENT pTimer;

//
//  Remote IP Address Data
//
struct sockaddr_in6 RemoteHostAddress;
CHAR8 * pRemoteHost;

//
//  Traffic Data
//
UINT64 TotalBytesSent;
UINT32 In;
UINT32 Samples;
UINT64 BytesSent[ DATA_SAMPLES ];
UINT8 Buffer[ DATA_BUFFER_SIZE ];


//
//  Forward routine declarations
//
EFI_STATUS TimerStart ( UINTN Milliseconds );


/**
  Check for control C entered at console

  @retval  EFI_SUCCESS  Control C not entered
  @retval  EFI_ABORTED  Control C entered
**/
EFI_STATUS
ControlCCheck (
  )
{
  EFI_STATUS Status;

  //
  //  Assume no user intervention
  //
  Status = EFI_SUCCESS;

  //
  //  Display user stop request
  //
  if ( EFI_ERROR ( Status )) {
    DEBUG (( DEBUG_INFO,
              "User stop request!\r\n" ));
  }

  //
  //  Return the check status
  //
  return Status;
}


/**
  Get a digit

  @param [in] pDigit    The address of the next digit
  @param [out] pValue   The address to receive the value

  @return   Returns the address of the separator

**/
CHAR8 *
GetDigit (
  CHAR8 * pDigit,
  UINT32 * pValue
  )
{
  UINT32 Value;

  //
  //  Walk the digits
  //
  Value = 0;
  while (( '0' <= *pDigit ) && ( '9' >= *pDigit )) {
    //
    //  Make room for the new least significant digit
    //
    Value *= 10;

    //
    //  Convert the digit from ASCII to binary
    //
    Value += *pDigit - '0';

    //
    //  Set the next digit
    //
    pDigit += 1;
  }

  //
  //  Return the value
  //
  *pValue = Value;

  //
  //  Return the next separator
  //
  return pDigit;
}


/**
  Get the IP address

  @retval  EFI_SUCCESS  The IP address is valid
  @retval  Other        Failure to convert the IP address
**/
EFI_STATUS
IpAddress (
  )
{
  struct sockaddr_in * pRemoteAddress4;
  struct sockaddr_in6 * pRemoteAddress6;
  UINT32 RemoteAddress;
  EFI_STATUS Status;
  UINT32 Value1;
  UINT32 Value2;
  UINT32 Value3;
  UINT32 Value4;
  UINT32 Value5;
  UINT32 Value6;
  UINT32 Value7;
  UINT32 Value8;

  //
  //  Assume failure
  //
  Status = EFI_INVALID_PARAMETER;

  //
  //  Get the port number
  //
  ZeroMem ( &RemoteHostAddress, sizeof ( RemoteHostAddress ));
  RemoteHostAddress.sin6_port = htons ( PcdGet16 ( DataSource_Port ));
  pRemoteAddress4 = (struct sockaddr_in *)&RemoteHostAddress;
  pRemoteAddress6 = &RemoteHostAddress;
  
  //
  //  Convert the IP address from a string to a numeric value
  //
  if (( 4 == sscanf ( pRemoteHost,
                      "%d.%d.%d.%d",
                      &Value1,
                      &Value2,
                      &Value3,
                      &Value4 ))
      && ( 255 >= Value1 )
      && ( 255 >= Value2 )
      && ( 255 >= Value3 )
      && ( 255 >= Value4 )) {
    //
    //  Build the IPv4 address
    //
    pRemoteAddress4->sin_len = sizeof ( *pRemoteAddress4 );
    pRemoteAddress4->sin_family = AF_INET;
    RemoteAddress = Value1
                  | ( Value2 << 8 )
                  | ( Value3 << 16 )
                  | ( Value4 << 24 );
    pRemoteAddress4->sin_addr.s_addr = RemoteAddress;
    Status = EFI_SUCCESS;

    //
    //  Display the IP address
    //
    DEBUG (( DEBUG_INFO,
              "%d.%d.%d.%d: Remote host IP address\r\n",
              Value1,
              Value2,
              Value3,
              Value4 ));
  }
  else if (( 8 == sscanf ( pRemoteHost,
                           "%x:%x:%x:%x:%x:%x:%x:%x",
                           &Value1,
                           &Value2,
                           &Value3,
                           &Value4,
                           &Value5,
                           &Value6,
                           &Value7,
                           &Value8 ))
            && ( 0xffff >= Value1 )
            && ( 0xffff >= Value2 )
            && ( 0xffff >= Value3 )
            && ( 0xffff >= Value4 )
            && ( 0xffff >= Value5 )
            && ( 0xffff >= Value6 )
            && ( 0xffff >= Value7 )
            && ( 0xffff >= Value8 )) {
    //
    //  Build the IPv6 address
    //
    pRemoteAddress6->sin6_len = sizeof ( *pRemoteAddress6 );
    pRemoteAddress6->sin6_family = AF_INET6;
    pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 0 ] = (UINT8)( Value1 >> 8 );
    pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 1 ] = (UINT8)Value1;
    pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 2 ] = (UINT8)( Value2 >> 8 );
    pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 3 ] = (UINT8)Value2;
    pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 4 ] = (UINT8)( Value3 >> 8 );
    pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 5 ] = (UINT8)Value3;
    pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 6 ] = (UINT8)( Value4 >> 8 );
    pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 7 ] = (UINT8)Value4;
    pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 8 ] = (UINT8)( Value5 >> 8 );
    pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 9 ] = (UINT8)Value5;
    pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 10 ] = (UINT8)( Value6 >> 8 );
    pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 11 ] = (UINT8)Value6;
    pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 12 ] = (UINT8)( Value7 >> 8 );
    pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 13 ] = (UINT8)Value7;
    pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 14 ] = (UINT8)( Value8 >> 8 );
    pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 15 ] = (UINT8)Value8;
    Status = EFI_SUCCESS;

    //
    //  Display the IP address
    //
    DEBUG (( DEBUG_INFO,
              "[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]: Remote host IP address\r\n",
              Value1,
              Value2,
              Value3,
              Value4,
              Value5,
              Value6,
              Value7,
              Value8 ));
  }
  else {
    Print ( L"ERROR - Invalid IP address!\r\n" );
  }

  //
  //  Return the operation status
  //
  return Status;
}


/**
  Close the socket

  @retval  EFI_SUCCESS  The application is running normally
  @retval  Other        The user stopped the application
**/
EFI_STATUS
SocketClose (
  )
{
  int CloseStatus;
  EFI_STATUS Status;

  //
  //  Determine if the socket is open
  //
  Status = EFI_DEVICE_ERROR;
  if ( -1 != Socket ) {
    //
    //  Attempt to close the socket
    //
    CloseStatus = close ( Socket );
    if ( 0 == CloseStatus ) {
      DEBUG (( DEBUG_INFO,
                "0x%08x: Socket closed\r\n",
                Socket ));
      Socket = -1;
      Status = EFI_SUCCESS;
    }
    else {
      DEBUG (( DEBUG_ERROR,
                "ERROR: Failed to close socket, errno: %d\r\n",
                errno ));
    }
  }

  //
  //  Return the operation status
  //
  return Status;
}


/**
  Connect the socket

  @retval  EFI_SUCCESS  The application is running normally
  @retval  Other        The user stopped the application
**/
EFI_STATUS
SocketConnect (
  )
{
  int ConnectStatus;
  struct sockaddr_in * pRemoteAddress4;
  struct sockaddr_in6 * pRemoteAddress6;
  EFI_STATUS Status;

  //
  //  Display the connecting message
  //
  pRemoteAddress4 = (struct sockaddr_in *)&RemoteHostAddress;
  pRemoteAddress6 = &RemoteHostAddress;
  if ( AF_INET == pRemoteAddress6->sin6_family ) {
    Print ( L"Connecting to remote system %d.%d.%d.%d:%d\r\n",
            pRemoteAddress4->sin_addr.s_addr & 0xff,
            ( pRemoteAddress4->sin_addr.s_addr >> 8 ) & 0xff,
            ( pRemoteAddress4->sin_addr.s_addr >> 16 ) & 0xff,
            ( pRemoteAddress4->sin_addr.s_addr >> 24 ) & 0xff,
            ntohs ( pRemoteAddress4->sin_port ));
  }
  else {
    Print ( L"Connecting to remote system [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",
            pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 0 ],
            pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 1 ],
            pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 2 ],
            pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 3 ],
            pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 4 ],
            pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 5 ],
            pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 6 ],
            pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 7 ],
            pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 8 ],
            pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 9 ],
            pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 10 ],
            pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 11 ],
            pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 12 ],
            pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 13 ],
            pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 14 ],
            pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 15 ],
            ntohs ( pRemoteAddress6->sin6_port ));
  }

  //
  //  Connect to the remote system
  //
  Status = EFI_SUCCESS;
  do {
    //
    //  Check for user stop request
    //
    while ( !bTick ) {
      Status = ControlCCheck ( );
      if ( EFI_ERROR ( Status )) {
        break;
      }
    }
    bTick = FALSE;
    if ( EFI_ERROR ( Status )) {
      break;
    }

    //
    //  Connect to the remote system
    //
    ConnectStatus = connect ( Socket,
                              (struct sockaddr *)pRemoteAddress6,
                              pRemoteAddress6->sin6_len );
    if ( -1 != ConnectStatus ) {
      if ( AF_INET == pRemoteAddress6->sin6_family ) {
        Print ( L"Connected to remote system %d.%d.%d.%d:%d\r\n",
                pRemoteAddress4->sin_addr.s_addr & 0xff,
                ( pRemoteAddress4->sin_addr.s_addr >> 8 ) & 0xff,
                ( pRemoteAddress4->sin_addr.s_addr >> 16 ) & 0xff,
                ( pRemoteAddress4->sin_addr.s_addr >> 24 ) & 0xff,
                ntohs ( pRemoteAddress4->sin_port ));
      }
      else {
        Print ( L"Connected to remote system [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",
                pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 0 ],
                pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 1 ],
                pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 2 ],
                pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 3 ],
                pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 4 ],
                pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 5 ],
                pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 6 ],
                pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 7 ],
                pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 8 ],
                pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 9 ],
                pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 10 ],
                pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 11 ],
                pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 12 ],
                pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 13 ],
                pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 14 ],
                pRemoteAddress6->sin6_addr.__u6_addr.__u6_addr8[ 15 ],
                ntohs ( pRemoteAddress6->sin6_port ));
      }
Print ( L"ConnectStatus: %d, Status: %r\r\n", ConnectStatus, Status );
    }
    else {
      //
      //  Close the socket and try again
      //
      if ( EAGAIN != errno ) {
        Status = EFI_NOT_STARTED;
        break;
      }
    }
  } while ( -1 == ConnectStatus );

  //
  //  Return the operation status
  //
Print ( L"SocketConnect returning Status: %r\r\n", Status );
  return Status;
}


/**
  Create the socket

  @param [in] Family    Network family, AF_INET or AF_INET6

  @retval  EFI_SUCCESS  The application is running normally
  @retval  Other        The user stopped the application
**/
EFI_STATUS
SocketNew (
  sa_family_t Family
  )
{
  EFI_STATUS Status;

  //
  //  Loop creating the socket
  //
  DEBUG (( DEBUG_INFO,
            "Creating the socket\r\n" ));
  do {
    //
    //  Check for user stop request
    //
    Status = ControlCCheck ( );
    if ( EFI_ERROR ( Status )) {
      break;
    }

    //
    //  Attempt to create the socket
    //
    Socket = socket ( Family,
                      SOCK_STREAM,
                      IPPROTO_TCP );
    if ( -1 != Socket ) {
      DEBUG (( DEBUG_INFO,
                "0x%08x: Socket created\r\n",
                Socket ));
      break;
    }
  } while ( -1 == Socket );

  //
  //  Return the operation status
  //
  return Status;
}


/**
  Send data over the socket

  @retval  EFI_SUCCESS  The application is running normally
  @retval  Other        The user stopped the application
**/
EFI_STATUS
SocketSend (
  )
{
  size_t BytesSentLocal;
  EFI_STATUS Status;
  EFI_TPL TplPrevious;

  //
  //  Restart the timer
  //
  TimerStart ( 1 * 1000 );

  //
  //  Loop until the connection breaks or the user stops
  //
  do {
    //
    //  Check for user stop request
    //
    Status = ControlCCheck ( );
    if ( EFI_ERROR ( Status )) {
      break;
    }

    //
    //  Send some bytes
    //
    BytesSentLocal = write ( Socket, &Buffer[0], sizeof ( Buffer ));
    if ( -1 == BytesSentLocal ) {
      DEBUG (( DEBUG_INFO,
                "ERROR: send failed, errno: %d\r\n",
                errno ));
Print ( L"ERROR: send failed, errno: %d\r\n", errno );

      //
      //  Try again
      //
      Status = EFI_SUCCESS;

//
//  Exit now
//
Status = EFI_NOT_STARTED;
      break;
    }

    //
    //  Synchronize with the TimerCallback routine
    //
    TplPrevious = gBS->RaiseTPL ( TPL_DATASOURCE );

    //
    //  Account for the data sent
    //
    TotalBytesSent += BytesSentLocal;

    //
    //  Release the TimerCallback routine synchronization
    //
    gBS->RestoreTPL ( TplPrevious );
  } while ( !EFI_ERROR ( Status ));

  //
  //  Return the operation status
  //
  return Status;
}


/**
  Open the network connection and send the data.

  @retval EFI_SUCCESS   Continue looping
  @retval other         Stopped by user's Control-C input

**/
EFI_STATUS
SocketOpen (
  )
{
  EFI_STATUS Status;

  //
  //  Use do/while and break instead of goto
  //
  do {
    //
    //  Wait for the network layer to initialize
    //
    Status = SocketNew ( RemoteHostAddress.sin6_family );
    if ( EFI_ERROR ( Status )) {
      break;
    }

    //
    //  Wait for the remote network application to start
    //
    Status = SocketConnect ( );
Print ( L"Status: %r\r\n", Status );
    if ( EFI_NOT_STARTED == Status ) {
      Status = SocketClose ( );
      continue;
    }
    else if ( EFI_SUCCESS != Status ) {
      //
      //  Control-C
      //
      break;
    }

    //
    //  Send data until the connection breaks
    //
    Status = SocketSend ( );
    if ( EFI_ERROR ( Status )) {
      break;
    }
  } while ( FALSE );

  //
  //  Return the operation status
  //
Print ( L"Returning Status: %r\r\n", Status );
  return Status;
}


/**
  Close the TCP connection

  @retval  EFI_SUCCESS  The application is running normally
  @retval  Other        The user stopped the application
**/
EFI_STATUS
Tcp4Close (
  )
{
  UINTN Index;
  UINT8 * pIpAddress;
  EFI_STATUS Status;

  //
  //  Close the port
  //
  if ( bTcp4Connected ) {
    Tcp4CloseToken.AbortOnClose = TRUE;
    Status = pTcp4Protocol->Close ( pTcp4Protocol,
                                    &Tcp4CloseToken );
    if ( EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_ERROR,
                "ERROR - Failed to start the TCP port close, Status: %r\r\n",
                Status ));
    }
    else {
      Status = gBS->WaitForEvent ( 1,
                                   &Tcp4CloseToken.CompletionToken.Event,
                                    &Index );
      if ( EFI_ERROR ( Status )) {
        DEBUG (( DEBUG_ERROR,
                  "ERROR - Failed to wait for close event, Status: %r\r\n",
                  Status ));
      }
      else {
        Status = Tcp4CloseToken.CompletionToken.Status;
        if ( EFI_ERROR ( Status )) {
          DEBUG (( DEBUG_ERROR,
                    "ERROR - Failed to close the TCP port, Status: %r\r\n",
                    Status ));
        }
        else {
          DEBUG (( DEBUG_INFO,
                    "0x%08x: TCP port closed\r\n",
                    pTcp4Protocol ));
          bTcp4Connected = FALSE;

          //
          //  Display the port closed message
          //
          pIpAddress = (UINT8 *)&((struct sockaddr_in *)&RemoteHostAddress)->sin_addr.s_addr;
          Print ( L"Closed connection to %d.%d.%d.%d:%d\r\n",
                  pIpAddress[0],
                  pIpAddress[1],
                  pIpAddress[2],
                  pIpAddress[3],
                  ntohs ( ((struct sockaddr_in *)&RemoteHostAddress)->sin_port ));
        }
      }
    }
  }

  //
  //  Release the events
  //
  if ( NULL != Tcp4TxToken.CompletionToken.Event ) {
    Status = gBS->CloseEvent ( Tcp4TxToken.CompletionToken.Event );
    if ( !EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_INFO,
                "0x%08x: TX event closed\r\n",
                Tcp4TxToken.CompletionToken.Event ));
      Tcp4TxToken.CompletionToken.Event = NULL;
    }
    else {
      DEBUG (( DEBUG_ERROR,
                "ERROR - Failed to close the Tcp4TxToken event, Status: %r\r\n",
                Status ));
    }
  }

  if ( NULL != Tcp4ListenToken.CompletionToken.Event ) {
    Status = gBS->CloseEvent ( Tcp4ListenToken.CompletionToken.Event );
    if ( !EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_INFO,
                "0x%08x: Listen event closed\r\n",
                Tcp4ListenToken.CompletionToken.Event ));
      Tcp4ListenToken.CompletionToken.Event = NULL;
    }
    else {
      DEBUG (( DEBUG_ERROR,
                "ERROR - Failed to close the Tcp4ListenToken event, Status: %r\r\n",
                Status ));
    }
  }

  if ( NULL != Tcp4ConnectToken.CompletionToken.Event ) {
    Status = gBS->CloseEvent ( Tcp4ConnectToken.CompletionToken.Event );
    if ( !EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_INFO,
                "0x%08x: Connect event closed\r\n",
                Tcp4ConnectToken.CompletionToken.Event ));
      Tcp4ConnectToken.CompletionToken.Event = NULL;
    }
    else {
      DEBUG (( DEBUG_ERROR,
                "ERROR - Failed to close the Tcp4ConnectToken event, Status: %r\r\n",
                Status ));
    }
  }

  if ( NULL != Tcp4CloseToken.CompletionToken.Event ) {
    Status = gBS->CloseEvent ( Tcp4CloseToken.CompletionToken.Event );
    if ( !EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_INFO,
                "0x%08x: Close event closed\r\n",
                Tcp4CloseToken.CompletionToken.Event ));
      Tcp4CloseToken.CompletionToken.Event = NULL;
    }
    else {
      DEBUG (( DEBUG_ERROR,
                "ERROR - Failed to close the Tcp4CloseToken event, Status: %r\r\n",
                Status ));
    }
  }

  //
  //  Close the TCP protocol
  //
  if ( NULL != pTcp4Protocol ) {
    Status = gBS->CloseProtocol ( Tcp4Handle,
                                  &gEfiTcp4ProtocolGuid,
                                  gImageHandle,
                                  NULL );
    if ( EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_ERROR,
                "ERROR - Failed to close the TCP protocol, Status: %r\r\n",
                Status ));
    }
    else {
      DEBUG (( DEBUG_INFO,
                "0x%08x: TCP4 protocol closed\r\n",
                pTcp4Protocol ));
      pTcp4Protocol = NULL;
    }
  }

  //
  //  Done with the TCP service
  //
  if ( NULL != Tcp4Handle ) {
    Status = pTcp4Service->DestroyChild ( pTcp4Service,
                                          Tcp4Handle );
    if ( EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_ERROR,
                "ERROR - Failed to release TCP service handle, Status: %r\r\n",
                Status ));
    }
    else {
      DEBUG (( DEBUG_INFO,
                "Ox%08x: TCP service closed\r\n",
                Tcp4Handle ));
      Tcp4Handle = NULL;
    }
  }

  //
  //  Close the service protocol
  //
  if ( NULL != pTcp4Service ) {
    Status = gBS->CloseProtocol ( Tcp4Controller,
                                  &gEfiTcp4ServiceBindingProtocolGuid,
                                  gImageHandle,
                                  NULL );
    if ( !EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_INFO,
                "0x%08x: Controller closed gEfiTcp4ServiceBindingProtocolGuid protocol\r\n",
                Tcp4Controller ));
      pTcp4Service = NULL;
    }
    else {
      DEBUG (( DEBUG_ERROR,
                "ERROR - Failed to close the gEfiTcp4ServiceBindingProtocolGuid protocol, Status: %r\r\n",
                Status ));
    }
  }
  Tcp4Controller = NULL;
  bTcp4Connecting = TRUE;

  //
  //  Mark the connection as closed
  //
  Status = EFI_SUCCESS;

  //
  //  Return the operation status
  //
  return Status;
}


/**
  Locate TCP protocol

  @retval EFI_SUCCESS   Protocol found
  @retval other         Protocl not found
**/
EFI_STATUS
Tcp4Locate (
  )
{
  UINTN HandleCount;
  EFI_HANDLE * pHandles;
  UINT8 * pIpAddress;
  EFI_STATUS Status;

  //
  //  Use do/while and break instead of goto
  //
  do {
    //
    //  Attempt to locate the next TCP adapter in the system
    //
    Status = gBS->LocateHandleBuffer ( ByProtocol,
                                       &gEfiTcp4ServiceBindingProtocolGuid,
                                       NULL,
                                       &HandleCount,
                                       &pHandles );
    if ( EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_WARN,
                "WARNING - No network controllers or TCP4 available, Status: %r\r\n",
                Status ));
      break;
    }

    //
    //  Wrap the index if necessary
    //
    if ( HandleCount <= Tcp4Index ) {
      Tcp4Index = 0;

      //
      //  Wait for the next timer tick
      //
      do {
      } while ( !bTick );
      bTick = FALSE;
    }

    //
    //  Display the connecting message
    //
    if ( bTcp4Connecting ) {
      pIpAddress = (UINT8 *)&((struct sockaddr_in *)&RemoteHostAddress)->sin_addr.s_addr;
      Print ( L"Connecting to %d.%d.%d.%d:%d\r\n",
              pIpAddress[0],
              pIpAddress[1],
              pIpAddress[2],
              pIpAddress[3],
              ntohs ( ((struct sockaddr_in *)&RemoteHostAddress)->sin_port ));
      bTcp4Connecting = FALSE;
    }

    //
    //  Open the network controller's service protocol
    //
    Tcp4Controller = pHandles[ Tcp4Index++ ];
    Status = gBS->OpenProtocol (
                    Tcp4Controller,
                    &gEfiTcp4ServiceBindingProtocolGuid,
                    (VOID **) &pTcp4Service,
                    gImageHandle,
                    NULL,
                    EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL );
    if ( EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_ERROR,
                "ERROR - Failed to open gEfiTcp4ServiceBindingProtocolGuid on controller 0x%08x\r\n",
                Tcp4Controller ));
      Tcp4Controller = NULL;
      break;
    }
    DEBUG (( DEBUG_INFO,
              "0x%08x: Controller opened gEfiTcp4ServiceBindingProtocolGuid protocol\r\n",
              Tcp4Controller ));

    //
    //  Connect to the TCP service
    //
    Status = pTcp4Service->CreateChild ( pTcp4Service,
                                         &Tcp4Handle );
    if ( EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_ERROR,
                "ERROR - Failed to open TCP service, Status: %r\r\n",
                Status ));
      Tcp4Handle = NULL;
      break;
    }
    DEBUG (( DEBUG_INFO,
              "Ox%08x: TCP service opened\r\n",
              Tcp4Handle ));

    //
    //  Locate the TCP protcol
    //
    Status = gBS->OpenProtocol ( Tcp4Handle,
                                 &gEfiTcp4ProtocolGuid,
                                 (VOID **)&pTcp4Protocol,
                                 gImageHandle,
                                 NULL,
                                 EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL );
    if ( EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_ERROR,
                "ERROR - Failed to open the TCP protocol, Status: %r\r\n",
                Status ));
      pTcp4Protocol = NULL;
      break;
    }
    DEBUG (( DEBUG_INFO,
              "0x%08x: TCP4 protocol opened\r\n",
              pTcp4Protocol ));
  }while ( FALSE );

  //
  //  Release the handle buffer
  //
  gBS->FreePool ( pHandles );

  //
  //  Return the operation status
  //
  return Status;
}


/**
  Send data over the TCP4 connection

  @retval  EFI_SUCCESS  The application is running normally
  @retval  Other        The user stopped the application
**/
EFI_STATUS
Tcp4Send (
  )
{
  UINTN Index;
  EFI_TCP4_TRANSMIT_DATA Packet;
  EFI_STATUS Status;
  EFI_TPL TplPrevious;

  //
  //  Restart the timer
  //
  TimerStart ( 1 * 1000 );

  //
  //  Initialize the packet
  //
  Packet.DataLength = sizeof ( Buffer );
  Packet.FragmentCount = 1;
  Packet.Push = FALSE;
  Packet.Urgent = FALSE;
  Packet.FragmentTable[0].FragmentBuffer = &Buffer[0];
  Packet.FragmentTable[0].FragmentLength = sizeof ( Buffer );
  Tcp4TxToken.Packet.TxData = &Packet;

  //
  //  Loop until the connection breaks or the user stops
  //
  do {
    //
    //  Check for user stop request
    //
    Status = ControlCCheck ( );
    if ( EFI_ERROR ( Status )) {
      break;
    }

    //
    //  Send some bytes
    //
    Status = pTcp4Protocol->Transmit ( pTcp4Protocol,
                                       &Tcp4TxToken );
    if ( EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_ERROR,
                "ERROR - Failed to start the transmit, Status: %r\r\n",
                Status ));

      //
      //  Try again
      //
      Status = EFI_SUCCESS;
      break;
    }

    //
    //  Wait for the transmit to complete
    //
    Status = gBS->WaitForEvent ( 1,
                                 &Tcp4TxToken.CompletionToken.Event,
                                 &Index );
    if ( EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_ERROR,
                "ERROR - Failed to wait for transmit completion, Status: %r\r\n",
                Status ));

      //
      //  Try again
      //
      Status = EFI_SUCCESS;
      break;
    }

    //
    //  Get the transmit status
    //
    Status = Tcp4TxToken.CompletionToken.Status;
    if ( EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_WARN,
                "WARNING - Failed the transmission, Status: %r\r\n",
                Status ));

      //
      //  Try again
      //
      Status = EFI_SUCCESS;

//
//  Exit now
//
Status = EFI_NOT_STARTED;
      break;
    }

    //
    //  Synchronize with the TimerCallback routine
    //
    TplPrevious = gBS->RaiseTPL ( TPL_DATASOURCE );

    //
    //  Account for the data sent
    //
    TotalBytesSent += Packet.DataLength;

    //
    //  Release the TimerCallback routine synchronization
    //
    gBS->RestoreTPL ( TplPrevious );
  } while ( !EFI_ERROR ( Status ));

  //
  //  Return the operation status
  //
  return Status;
}


/**
  Open the network connection and send the data.

  @retval EFI_SUCCESS   Continue looping
  @retval other         Stopped by user's Control-C input

**/
EFI_STATUS
Tcp4Open (
  )
{
  UINTN Index;
  UINT8 * pIpAddress;
  EFI_STATUS Status;

  //
  //  Use do/while and break instead of goto
  //
  do {
    //
    //  Locate the TCP protocol
    //
    Status = Tcp4Locate ( );
    if ( EFI_ERROR ( Status )) {
      break;
    }

    //
    //  Create the necessary events
    //
    Status = gBS->CreateEvent ( 0,
                                TPL_CALLBACK,
                                NULL,
                                NULL,
                                &Tcp4CloseToken.CompletionToken.Event );
    if ( EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_ERROR,
                "ERROR - Failed to create the close event, Status: %r\r\n",
                Status ));
      Tcp4CloseToken.CompletionToken.Event = NULL;
      break;
    }
    DEBUG (( DEBUG_INFO,
              "0x%08x: Close event open\r\n",
              Tcp4CloseToken.CompletionToken.Event ));

    Status = gBS->CreateEvent ( 0,
                                TPL_CALLBACK,
                                NULL,
                                NULL,
                                &Tcp4ConnectToken.CompletionToken.Event );
    if ( EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_ERROR,
                "ERROR - Failed to create the connect event, Status: %r\r\n",
                Status ));
      Tcp4ConnectToken.CompletionToken.Event = NULL;
      break;
    }
    DEBUG (( DEBUG_INFO,
              "0x%08x: Connect event open\r\n",
              Tcp4ConnectToken.CompletionToken.Event ));

    Status = gBS->CreateEvent ( 0,
                                TPL_CALLBACK,
                                NULL,
                                NULL,
                                &Tcp4ListenToken.CompletionToken.Event );
    if ( EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_ERROR,
                "ERROR - Failed to create the listen event, Status: %r\r\n",
                Status ));
      Tcp4ListenToken.CompletionToken.Event = NULL;
      break;
    }
    DEBUG (( DEBUG_INFO,
              "0x%08x: Listen event open\r\n",
              Tcp4ListenToken.CompletionToken.Event ));

    Status = gBS->CreateEvent ( 0,
                                TPL_CALLBACK,
                                NULL,
                                NULL,
                                &Tcp4TxToken.CompletionToken.Event );
    if ( EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_ERROR,
                "ERROR - Failed to create the TX event, Status: %r\r\n",
                Status ));
      Tcp4TxToken.CompletionToken.Event = NULL;
      break;
    }
    DEBUG (( DEBUG_INFO,
              "0x%08x: TX event open\r\n",
              Tcp4TxToken.CompletionToken.Event ));

    //
    //  Configure the local TCP port
    //
    Tcp4ConfigData.TimeToLive = 255;
    Tcp4ConfigData.TypeOfService = 0;
    Tcp4ConfigData.ControlOption = NULL;
    Tcp4ConfigData.AccessPoint.ActiveFlag = TRUE;
    Tcp4ConfigData.AccessPoint.StationAddress.Addr[0] = 0;
    Tcp4ConfigData.AccessPoint.StationAddress.Addr[1] = 0;
    Tcp4ConfigData.AccessPoint.StationAddress.Addr[2] = 0;
    Tcp4ConfigData.AccessPoint.StationAddress.Addr[3] = 0;
    Tcp4ConfigData.AccessPoint.StationPort = 0;
    Tcp4ConfigData.AccessPoint.RemoteAddress.Addr[0] = (UINT8)  ((struct sockaddr_in *)&RemoteHostAddress)->sin_addr.s_addr;
    Tcp4ConfigData.AccessPoint.RemoteAddress.Addr[1] = (UINT8)( ((struct sockaddr_in *)&RemoteHostAddress)->sin_addr.s_addr >> 8 );
    Tcp4ConfigData.AccessPoint.RemoteAddress.Addr[2] = (UINT8)( ((struct sockaddr_in *)&RemoteHostAddress)->sin_addr.s_addr >> 16 );
    Tcp4ConfigData.AccessPoint.RemoteAddress.Addr[3] = (UINT8)( ((struct sockaddr_in *)&RemoteHostAddress)->sin_addr.s_addr >> 24 );
    Tcp4ConfigData.AccessPoint.RemotePort = ntohs (((struct sockaddr_in *)&RemoteHostAddress)->sin_port);
    Tcp4ConfigData.AccessPoint.UseDefaultAddress = TRUE;
    Tcp4ConfigData.AccessPoint.SubnetMask.Addr[0] = 0;
    Tcp4ConfigData.AccessPoint.SubnetMask.Addr[1] = 0;
    Tcp4ConfigData.AccessPoint.SubnetMask.Addr[2] = 0;
    Tcp4ConfigData.AccessPoint.SubnetMask.Addr[3] = 0;
    Status = pTcp4Protocol->Configure ( pTcp4Protocol,
                                        &Tcp4ConfigData );
    if ( EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_ERROR,
                "ERROR - Failed to configure TCP port, Status: %r\r\n",
                Status ));
      break;
    }
    DEBUG (( DEBUG_INFO,
              "0x%08x: TCP4 port configured\r\n",
              pTcp4Protocol ));

    //
    //  Connect to the remote TCP port
    //
    Status = pTcp4Protocol->Connect ( pTcp4Protocol,
                                      &Tcp4ConnectToken );
    if ( EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_ERROR,
                "ERROR - Failed to start the connection to the remote system, Status: %r\r\n",
                Status ));
      break;
    }
    Status = gBS->WaitForEvent ( 1,
                                 &Tcp4ConnectToken.CompletionToken.Event,
                                 &Index );
    if ( EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_ERROR,
                "ERROR - Failed to wait for the connection, Status: %r\r\n",
                Status ));
      break;
    }
    Status = Tcp4ConnectToken.CompletionToken.Status;
    if ( EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_WARN,
                "WARNING - Failed to connect to the remote system, Status: %r\r\n",
                Status ));
      break;
    }
    DEBUG (( DEBUG_INFO,
              "0x%08x: TCP4 port connected\r\n",
              pTcp4Protocol ));
    bTcp4Connected = TRUE;

    //
    //  Display the connection
    //
    pIpAddress = (UINT8 *)&((struct sockaddr_in *)&RemoteHostAddress)->sin_addr.s_addr;
    Print ( L"Connected to %d.%d.%d.%d:%d\r\n",
            pIpAddress[0],
            pIpAddress[1],
            pIpAddress[2],
            pIpAddress[3],
            ntohs ( ((struct sockaddr_in *)&RemoteHostAddress)->sin_port ));
  } while ( 0 );

  if ( EFI_ERROR ( Status )) {
    //
    //  Try again
    //
    Status = EFI_SUCCESS;
  }
  else {
    //
    //  Semd data until the connection breaks
    //
    Status = Tcp4Send ( );
  }

  //
  //  Return the operation status
  //
  return Status;
}


/**
  Handle the timer callback

  @param [in] Event     Event that caused this callback
  @param [in] pContext  Context for this routine
**/
VOID
EFIAPI
TimerCallback (
  IN EFI_EVENT Event,
  IN VOID * pContext
  )
{
  UINT32 Average;
  UINT64 BitsPerSecond;
  UINT32 Index;
  UINT64 TotalBytes;

  //
  //  Notify the other code of the timer tick
  //
  bTick = TRUE;

  //
  //  Update the average bytes per second
  //
  if ( 0 != TotalBytesSent ) {
    BytesSent[ In ] = TotalBytesSent;
    TotalBytesSent = 0;
    In += 1;
    if ( DATA_SAMPLES <= In ) {
      In = 0;
    }

    //
    //  Separate the samples
    //
    if ( DATA_SAMPLES == Samples ) {
      Print ( L"---------- Stable average ----------\r\n" );
    }
    Samples += 1;

    //
    //  Compute the data rate
    //
    TotalBytes = 0;
    for ( Index = 0; DATA_SAMPLES > Index; Index++ ) {
      TotalBytes += BytesSent[ Index ];
    }
    Average = (UINT32)RShiftU64 ( TotalBytes, DATA_SAMPLE_SHIFT );
    BitsPerSecond = Average * 8;

    //
    //  Display the data rate
    //
    if (( RANGE_SWITCH >> 10 ) > Average ) {
      Print ( L"Ave: %d Bytes/Sec, %Ld Bits/sec\r\n",
              Average,
              BitsPerSecond );
    }
    else {
      BitsPerSecond /= 1000;
      if ( RANGE_SWITCH > Average ) {
        Print ( L"Ave: %d.%03d KiBytes/Sec, %Ld KBits/sec\r\n",
                Average >> 10,
                (( Average & 0x3ff ) * 1000 ) >> 10,
                BitsPerSecond );
      }
      else {
        BitsPerSecond /= 1000;
        Average >>= 10;
        if ( RANGE_SWITCH > Average ) {
          Print ( L"Ave: %d.%03d MiBytes/Sec, %Ld MBits/sec\r\n",
                  Average >> 10,
                  (( Average & 0x3ff ) * 1000 ) >> 10,
                  BitsPerSecond );
        }
        else {
          BitsPerSecond /= 1000;
          Average >>= 10;
          if ( RANGE_SWITCH > Average ) {
            Print ( L"Ave: %d.%03d GiBytes/Sec, %Ld GBits/sec\r\n",
                    Average >> 10,
                    (( Average & 0x3ff ) * 1000 ) >> 10,
                    BitsPerSecond );
          }
          else {
            BitsPerSecond /= 1000;
            Average >>= 10;
            if ( RANGE_SWITCH > Average ) {
              Print ( L"Ave: %d.%03d TiBytes/Sec, %Ld TBits/sec\r\n",
                      Average >> 10,
                      (( Average & 0x3ff ) * 1000 ) >> 10,
                      BitsPerSecond );
            }
            else {
              BitsPerSecond /= 1000;
              Average >>= 10;
              Print ( L"Ave: %d.%03d PiBytes/Sec, %Ld PBits/sec\r\n",
                      Average >> 10,
                      (( Average & 0x3ff ) * 1000 ) >> 10,
                      BitsPerSecond );
            }
          }
        }
      }
    }
  }
}


/**
  Create the timer

  @retval  EFI_SUCCESS  The timer was successfully created
  @retval  Other        Timer initialization failed
**/
EFI_STATUS
TimerCreate (
  )
{
  EFI_STATUS Status;

  //
  //  Create the timer
  //
  Status = gBS->CreateEvent ( EVT_TIMER | EVT_NOTIFY_SIGNAL,
                              TPL_DATASOURCE,
                              TimerCallback,
                              NULL,
                              &pTimer );
  if ( EFI_ERROR ( Status )) {
    DEBUG (( DEBUG_ERROR,
              "ERROR - Failed to allocate the timer event, Status: %r\r\n",
              Status ));
  }
  else {
    DEBUG (( DEBUG_INFO,
              "0x%08x: Timer created\r\n",
              pTimer ));
  }

  //
  //  Return the operation status
  //
  return Status;
}


/**
  Stop the timer

  @retval  EFI_SUCCESS  The timer was stopped successfully
  @retval  Other        The timer failed to stop
**/
EFI_STATUS
TimerStop (
  )
{
  EFI_STATUS Status;

  //
  //  Assume success
  //
  Status = EFI_SUCCESS;

  //
  //  Determine if the timer is running
  //
  if ( bTimerRunning ) {
    //
    //  Stop the timer
    //
    Status = gBS->SetTimer ( pTimer,
                             TimerCancel,
                             0 );
    if ( EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_ERROR,
                "ERROR - Failed to stop the timer, Status: %r\r\n",
                Status ));
    }
    else {
      //
      //  Timer timer is now stopped
      //
      bTimerRunning = FALSE;
      DEBUG (( DEBUG_INFO,
                "0x%08x: Timer stopped\r\n",
                pTimer ));
    }
  }

  //
  //  Return the operation status
  //
  return Status;
}


/**
  Start the timer

  @param [in] Milliseconds  The number of milliseconds between timer callbacks

  @retval  EFI_SUCCESS  The timer was successfully created
  @retval  Other        Timer initialization failed
**/
EFI_STATUS
TimerStart (
  UINTN Milliseconds
  )
{
  EFI_STATUS Status;
  UINT64 TimeDelay;

  //
  //  Stop the timer if necessary
  //
  Status = EFI_SUCCESS;
  if ( bTimerRunning ) {
    Status = TimerStop ( );
  }
  if ( !EFI_ERROR ( Status )) {
    //
    //  Compute the new delay
    //
    TimeDelay = Milliseconds;
    TimeDelay *= 1000 * 10;

    //
    //  Start the timer
    //
    Status = gBS->SetTimer ( pTimer,
                             TimerPeriodic,
                             TimeDelay );
    if ( EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_ERROR,
                "ERROR - Failed to start the timer, Status: %r\r\n",
                Status ));
    }
    else {
      //
      //  The timer is now running
      //
      bTimerRunning = TRUE;
      DEBUG (( DEBUG_INFO,
        "0x%08x: Timer running\r\n",
        pTimer ));
    }
  }

  //
  //  Return the operation status
  //
  return Status;
}


/**
  Destroy the timer

  @retval  EFI_SUCCESS  The timer was destroyed successfully
  @retval  Other        Failed to destroy the timer
**/
EFI_STATUS
TimerDestroy (
  )
{
  EFI_STATUS Status;

  //
  //  Assume success
  //
  Status = EFI_SUCCESS;

  //
  //  Determine if the timer is running
  //
  if ( bTimerRunning ) {
    //
    //  Stop the timer
    //
    Status = TimerStop ( );
  }
  if (( !EFI_ERROR ( Status )) && ( NULL != pTimer )) {
    //
    //  Done with this timer
    //
    Status = gBS->CloseEvent ( pTimer );
    if ( EFI_ERROR ( Status )) {
      DEBUG (( DEBUG_ERROR,
                "ERROR - Failed to free the timer event, Status: %r\r\n",
                Status ));
    }
    else {
      DEBUG (( DEBUG_INFO,
                "0x%08x: Timer Destroyed\r\n",
                pTimer ));
      pTimer = NULL;
    }
  }

  //
  //  Return the operation status
  //
  return Status;
}


/**
  Send data to the DataSink program to test a network's bandwidth.

  @param [in] Argc  The number of arguments
  @param [in] Argv  The argument value array

  @retval  0        The application exited normally.
  @retval  Other    An error occurred.
**/
int
main (
  IN int Argc,
  IN char **Argv
  )
{
  EFI_STATUS (* pClose) ();
  EFI_STATUS (* pOpen) ();
  EFI_STATUS Status;

  DEBUG (( DEBUG_INFO,
            "DataSource starting\r\n" ));

  //
  //  Validate the command line
  //
  if ( 2 > Argc ) {
    Print ( L"%s  <remote IP address> [Use TCP]\r\n", Argv[0] );
    return -1;
  }

  //
  //  Determine if TCP should be used
  //
  bTcp4 = (BOOLEAN)( 2 < Argc );

  //
  //  Determine the support routines
  //
  if ( bTcp4 ) {
    pOpen = Tcp4Open;
    pClose = Tcp4Close;
    bTcp4Connecting = TRUE;
  }
  else {
    pOpen = SocketOpen;
    pClose = SocketClose;
  }

  //
  //  Use for/break instead of goto
  //
  for ( ; ; ) {
    //
    //  No bytes sent so far
    //
    TotalBytesSent = 0;
    Samples = 0;
    memset ( &BytesSent, 0, sizeof ( BytesSent ));

    //
    //  Get the IP address
    //
    pRemoteHost = Argv[1];
    Status = IpAddress ( );
    if ( EFI_ERROR ( Status )) {
      break;
    }

    //
    //  Create the timer
    //
    bTick = TRUE;
    Status = TimerCreate ( );
    if ( EFI_ERROR ( Status )) {
      break;
    }

    //
    //  Loop forever abusing the specified system
    //
    do {
      //
      //  Start a timer to perform connection polling and display updates
      //
      Status = TimerStart ( 2 * 1000 );
      if ( EFI_ERROR ( Status )) {
        break;
      }

      //
      //  Open the network connection and send the data
      //
      Status = pOpen ( );
      if ( EFI_ERROR ( Status )) {
        break;
      }

      //
      //  Done with the network connection
      //
      Status = pClose ( );
    } while ( !EFI_ERROR ( Status ));

    //
    //  Close the network connection if necessary
    //
    pClose ( );

    //
    //  All done
    //
    break;
  }

  //
  //  Stop the timer if necessary
  //
  TimerStop ( );
  TimerDestroy ( );

  //
  //  Return the operation status
  //
  DEBUG (( DEBUG_INFO,
            "DataSource exiting, Status: %r\r\n",
            Status ));
  return Status;
}
