/** @file
  Data source for network testing.

  Copyright (c) 2011-2012, Intel Corporation
  All rights reserved. This program and the accompanying materials
  are licensed and made available under the terms and conditions of the BSD License
  which accompanies this distribution.  The full text of the license may be found at
  http://opensource.org/licenses/bsd-license.php

  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

**/

#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 BytesSent;
  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
    //
    BytesSent = write ( Socket, &Buffer[0], sizeof ( Buffer ));
    if ( -1 == BytesSent ) {
      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 += BytesSent;

    //
    //  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;
}
