]> git.proxmox.com Git - mirror_edk2.git/blobdiff - AppPkg/Applications/Sockets/TftpServer/TftpServer.c
Merged socket development branch:
[mirror_edk2.git] / AppPkg / Applications / Sockets / TftpServer / TftpServer.c
index 9b522d1b5570bdbe5c84342b4afa5275f99edf5e..e9ab0dee5f059537d37dc1257a27273ed66a9dbb 100644 (file)
-/*++\r
-  This file contains an 'Intel UEFI Application' and is\r
-  licensed for Intel CPUs and chipsets under the terms of your\r
-  license agreement with Intel or your vendor.  This file may\r
-  be modified by the user, subject to additional terms of the\r
-  license agreement\r
---*/\r
-/*++\r
-\r
-Copyright (c)  2011 Intel Corporation. All rights reserved\r
-This software and associated documentation (if any) is furnished\r
-under a license and may only be used or copied in accordance\r
-with the terms of the license. Except as permitted by such\r
-license, no part of this software or documentation may be\r
-reproduced, stored in a retrieval system, or transmitted in any\r
-form or by any means without the express written consent of\r
-Intel Corporation.\r
-\r
---*/\r
-\r
 /** @file\r
   This is a simple TFTP server application\r
 \r
+  Copyright (c) 2011, 2012, Intel Corporation\r
+  All rights reserved. This program and the accompanying materials\r
+  are licensed and made available under the terms and conditions of the BSD License\r
+  which accompanies this distribution.  The full text of the license may be found at\r
+  http://opensource.org/licenses/bsd-license.php\r
+\r
+  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
 **/\r
 \r
 #include <TftpServer.h>\r
 \r
-TSDT_TFTP_SERVER mTftpServer; ///<  TFTP server's control structure\r
+TSDT_TFTP_SERVER mTftpServer;       ///<  TFTP server's control structure\r
+volatile BOOLEAN mbTftpServerExit;  ///<  Set TRUE to cause TFTP server to exit\r
 \r
 \r
 /**\r
-  Add a connection context to the list of connection contexts.\r
+  Read file data into a buffer\r
 \r
-  @param [in] pTftpServer   The TFTP server control structure address.\r
+  @param [in] pContext    Connection context structure address\r
 \r
-  @retval Context structure address, NULL if allocation fails\r
+  @retval TRUE if a read error occurred\r
 \r
 **/\r
-TSDT_CONNECTION_CONTEXT *\r
-ContextAdd (\r
-  IN TSDT_TFTP_SERVER * pTftpServer\r
+BOOLEAN\r
+BufferFill (\r
+  IN TSDT_CONNECTION_CONTEXT * pContext\r
   )\r
 {\r
-  size_t LengthInBytes;\r
-  TSDT_CONNECTION_CONTEXT * pContext;\r
-  EFI_STATUS Status;\r
+  BOOLEAN bReadError;\r
+  size_t BytesRead;\r
+  UINT64 LengthInBytes;\r
 \r
   DBG_ENTER ( );\r
 \r
   //\r
-  //  Use for/break instead of goto\r
+  //  Use break instead of goto\r
   //\r
+  bReadError = FALSE;\r
   for ( ; ; ) {\r
     //\r
-    //  Allocate a new context\r
-    //\r
-    LengthInBytes = sizeof ( *pContext );\r
-    Status = gBS->AllocatePool ( EfiRuntimeServicesData,\r
-                                 LengthInBytes,\r
-                                 (VOID **)&pContext );\r
-    if ( EFI_ERROR ( Status )) {\r
-      DEBUG (( DEBUG_ERROR | DEBUG_POOL,\r
-                "ERROR - Failed to allocate the context, Status: %r\r\n",\r
-                Status ));\r
-      pContext = NULL;\r
+    //  Determine if there is any work to do\r
+    //\r
+    LengthInBytes = DIM ( pContext->FileData ) >> 1;\r
+    if (( pContext->ValidBytes > LengthInBytes )\r
+      || ( 0 == pContext->BytesRemaining )) {\r
+      break;\r
+    }\r
+\r
+    //\r
+    //  Determine the number of bytes to read\r
+    //\r
+    if ( LengthInBytes > pContext->BytesRemaining ) {\r
+      LengthInBytes = pContext->BytesRemaining;\r
+    }\r
+\r
+    //\r
+    //  Read in the next portion of the file\r
+    //\r
+    BytesRead = fread ( pContext->pFill,\r
+                        1,\r
+                        (size_t)LengthInBytes,\r
+                        pContext->File );\r
+    if ( -1 == BytesRead ) {\r
+      bReadError = TRUE;\r
       break;\r
     }\r
 \r
+    //\r
+    //  Account for the file data read\r
+    //\r
+    pContext->BytesRemaining -= BytesRead;\r
+    pContext->ValidBytes += BytesRead;\r
+    DEBUG (( DEBUG_FILE_BUFFER,\r
+              "0x%08x: Buffer filled with %Ld bytes, %Ld bytes ramaining\r\n",\r
+              pContext->pFill,\r
+              BytesRead,\r
+              pContext->BytesRemaining ));\r
+\r
+    //\r
+    //  Set the next buffer location\r
+    //\r
+    pContext->pFill += BytesRead;\r
+    if ( pContext->pEnd <= pContext->pFill ) {\r
+      pContext->pFill = &pContext->FileData[ 0 ];\r
+    }\r
+\r
+    //\r
+    //  Verify that the end of the buffer is reached\r
+    //\r
+    ASSERT ( 0 == ( DIM ( pContext->FileData ) & 1 ));\r
+    break;\r
+  }\r
+\r
+  //\r
+  //  Return the read status\r
+  //\r
+  DBG_EXIT ( );\r
+  return bReadError;\r
+}\r
+\r
+\r
+/**\r
+  Add a connection context to the list of connection contexts.\r
+\r
+  @param [in] pTftpServer   Address of the ::TSDT_TFTP_SERVER structure\r
+  @param [in] SocketFd      Socket file descriptor\r
+\r
+  @retval Context structure address, NULL if allocation fails\r
+\r
+**/\r
+TSDT_CONNECTION_CONTEXT *\r
+ContextAdd (\r
+  IN TSDT_TFTP_SERVER * pTftpServer,\r
+  IN int SocketFd\r
+  )\r
+{\r
+  TSDT_CONNECTION_CONTEXT * pContext;\r
+  TFTP_PACKET * pEnd;\r
+  TFTP_PACKET * pPacket;\r
+\r
+  DBG_ENTER ( );\r
+\r
+  //\r
+  //  Allocate a new context\r
+  //\r
+  pContext = (TSDT_CONNECTION_CONTEXT *)AllocateZeroPool ( sizeof ( *pContext ));\r
+  if ( NULL != pContext ) {\r
     //\r
     //  Initialize the context\r
     //\r
-    ZeroMem ( pContext, LengthInBytes );\r
+    pContext->SocketFd = SocketFd;\r
     CopyMem ( &pContext->RemoteAddress,\r
               &pTftpServer->RemoteAddress,\r
               sizeof ( pContext->RemoteAddress ));\r
-    pContext->BlockSize = TFTP_MAX_BLOCK_SIZE;\r
-    pContext->pBuffer = &pContext->FileData[0];\r
-    pContext->pEnd = &pContext->pBuffer[sizeof ( pContext->pBuffer )];\r
-    pContext->MaxTransferSize = 0;\r
-    pContext->MaxTransferSize -= 1;\r
+    pContext->BlockSize = 512;\r
+\r
+    //\r
+    //  Buffer management\r
+    //\r
+    pContext->pFill = &pContext->FileData[ 0 ];\r
+    pContext->pEnd = &pContext->FileData[ sizeof ( pContext->FileData )];\r
+    pContext->pBuffer = pContext->pFill;\r
+\r
+    //\r
+    //  Window management\r
+    //\r
+    pContext->MaxTimeout = MultU64x32 ( PcdGet32 ( Tftp_MaxTimeoutInSec ),\r
+                                        2 * 1000 * 1000 * 1000 );\r
+    pContext->Rtt2x = pContext->MaxTimeout;\r
+    pContext->WindowSize = MAX_PACKETS;\r
+    WindowTimeout ( pContext );\r
+\r
+    //\r
+    //  Place the packets on the free list\r
+    //\r
+    pPacket = &pContext->Tx[ 0 ];\r
+    pEnd = &pPacket[ DIM ( pContext->Tx )];\r
+    while ( pEnd > pPacket ) {\r
+      PacketFree ( pContext, pPacket );\r
+      pPacket += 1;\r
+    }\r
 \r
     //\r
     //  Display the new context\r
     //\r
-    DEBUG (( DEBUG_PORT_WORK | DEBUG_INFO,\r
-              "0x%08x: Context for %d.%d.%d.%d:%d\r\n",\r
-              pContext,\r
-              (UINT8)pContext->RemoteAddress.sin_addr.s_addr,\r
-              (UINT8)( pContext->RemoteAddress.sin_addr.s_addr >> 8 ),\r
-              (UINT8)( pContext->RemoteAddress.sin_addr.s_addr >> 16 ),\r
-              (UINT8)( pContext->RemoteAddress.sin_addr.s_addr >> 24 ),\r
-              htons ( pContext->RemoteAddress.sin_port )));\r
+    if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {\r
+      DEBUG (( DEBUG_PORT_WORK,\r
+                "0x%08x: Context for %d.%d.%d.%d:%d\r\n",\r
+                pContext,\r
+                (UINT8)pTftpServer->RemoteAddress.v4.sin_addr.s_addr,\r
+                (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ),\r
+                (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ),\r
+                (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ),\r
+                htons ( pTftpServer->RemoteAddress.v4.sin_port )));\r
+    }\r
+    else {\r
+      DEBUG (( DEBUG_PORT_WORK,\r
+                "0x%08x: Context for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",\r
+                pContext,\r
+                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],\r
+                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],\r
+                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],\r
+                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],\r
+                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],\r
+                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],\r
+                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],\r
+                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],\r
+                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],\r
+                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],\r
+                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],\r
+                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],\r
+                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],\r
+                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],\r
+                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],\r
+                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],\r
+                htons ( pTftpServer->RemoteAddress.v6.sin6_port )));\r
+    }\r
 \r
     //\r
     //  Add the context to the context list\r
     //\r
     pContext->pNext = pTftpServer->pContextList;\r
     pTftpServer->pContextList = pContext;\r
-\r
-    //\r
-    //  All done\r
-    //\r
-    break;\r
   }\r
 \r
   //\r
@@ -114,10 +219,8 @@ ContextAdd (
 /**\r
   Locate a remote connection context.\r
 \r
-  @param [in] pTftpServer   The TFTP server control structure address.\r
-\r
+  @param [in] pTftpServer   Address of the ::TSDT_TFTP_SERVER structure\r
   @param [in] pIpAddress    The start of the remote IP address in network order\r
-\r
   @param [in] Port          The remote port number\r
 \r
   @retval Context structure address, NULL if not found\r
@@ -140,8 +243,9 @@ ContextFind (
     //\r
     //  Attempt to locate the remote network connection\r
     //\r
-    if (( pTftpServer->RemoteAddress.sin_addr.s_addr == pContext->RemoteAddress.sin_addr.s_addr )\r
-      && ( pTftpServer->RemoteAddress.sin_port == pContext->RemoteAddress.sin_port )) {\r
+    if ( 0 == memcmp ( &pTftpServer->RemoteAddress,\r
+                       &pContext->RemoteAddress,\r
+                       pTftpServer->RemoteAddress.v6.sin6_len )) {\r
       //\r
       //  The connection was found\r
       //\r
@@ -168,9 +272,8 @@ ContextFind (
 /**\r
   Remove a context from the list.\r
 \r
-  @param [in] pTftpServer    The TFTP server control structure address.\r
-\r
-  @param [in] pContext       The context structure address.\r
+  @param [in] pTftpServer   Address of the ::TSDT_TFTP_SERVER structure\r
+  @param [in] pContext      Address of a ::TSDT_CONNECTION_CONTEXT structure\r
 \r
 **/\r
 VOID\r
@@ -228,297 +331,900 @@ ContextRemove (
 \r
 \r
 /**\r
-  Process the work for the sockets.\r
+  Queue data packets for transmission\r
+\r
+  @param [in] pContext    Connection context structure address\r
 \r
-  @param [in] pTftpServer   The TFTP server control structure address.\r
+  @retval TRUE if a read error occurred\r
 \r
 **/\r
-VOID\r
-PortWork (\r
-  IN TSDT_TFTP_SERVER * pTftpServer\r
+BOOLEAN\r
+PacketFill (\r
+  IN TSDT_CONNECTION_CONTEXT * pContext\r
   )\r
 {\r
-  TSDT_CONNECTION_CONTEXT * pContext;\r
-  socklen_t RemoteAddressLength;\r
+  BOOLEAN bReadError;\r
+  UINT64 LengthInBytes;\r
+  UINT8 * pBuffer;\r
+  TFTP_PACKET * pPacket;\r
 \r
   DBG_ENTER ( );\r
 \r
   //\r
-  //  Handle input events\r
+  //  Use break instead of goto\r
   //\r
-  if ( 0 != ( pTftpServer->TftpPort.revents & POLLRDNORM )) {\r
+  bReadError = FALSE;\r
+  for ( ; ; ) {\r
     //\r
-    //  Receive the message from the remote system\r
+    //  Fill the buffer if necessary\r
     //\r
-    RemoteAddressLength = sizeof ( pTftpServer->RemoteAddress );\r
-    pTftpServer->RxBytes = recvfrom ( pTftpServer->TftpPort.fd,\r
-                                      &pTftpServer->RxBuffer[0],\r
-                                      sizeof ( pTftpServer->RxBuffer ),\r
-                                      0,\r
-                                      (struct sockaddr *) &pTftpServer->RemoteAddress,\r
-                                      &RemoteAddressLength );\r
-    if ( -1 != pTftpServer->RxBytes ) {\r
-      pTftpServer->RemoteAddress.sin_len = (UINT8) RemoteAddressLength;\r
-      DEBUG (( DEBUG_TFTP_PORT,\r
-                 "Received %d bytes from %d.%d.%d.%d:%d\r\n",\r
-                 pTftpServer->RxBytes,\r
-                 pTftpServer->RemoteAddress.sin_addr.s_addr & 0xff,\r
-                 ( pTftpServer->RemoteAddress.sin_addr.s_addr >> 8 ) & 0xff,\r
-                 ( pTftpServer->RemoteAddress.sin_addr.s_addr >> 16 ) & 0xff,\r
-                 ( pTftpServer->RemoteAddress.sin_addr.s_addr >> 24 ) & 0xff,\r
-                 htons ( pTftpServer->RemoteAddress.sin_port )));\r
-\r
+    bReadError = BufferFill ( pContext );\r
+    if ( bReadError ) {\r
       //\r
-      //  Lookup connection context using the remote system address and port\r
-      //  to determine if an existing connection to this remote\r
-      //  system exists\r
+      //  File access mode not supported\r
       //\r
-      pContext = ContextFind ( pTftpServer );\r
+      DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,\r
+                "ERROR - File read failure!\r\n" ));\r
 \r
       //\r
-      //  Process the received message\r
+      //  Tell the client of the error\r
       //\r
-      TftpProcessRequest ( pTftpServer, pContext );\r
+      SendError ( pContext,\r
+                  TFTP_ERROR_SEE_MSG,\r
+                  (UINT8 *)"Read failure" );\r
+      break;\r
     }\r
-    else {\r
+\r
+    //\r
+    //  Determine if any packets can be filled\r
+    //\r
+    if ( pContext->bEofSent\r
+      || ( NULL == pContext->pFreeList )) {\r
       //\r
-      //  Receive error on the TFTP server port\r
-      //  Close the server socket\r
+      //  All of the packets are filled\r
       //\r
-      DEBUG (( DEBUG_ERROR,\r
-                "ERROR - Failed receive on TFTP server port, errno: 0x%08x\r\n",\r
-                errno ));\r
-      pTftpServer->TftpPort.revents |= POLLHUP;\r
+      break;\r
     }\r
-  }\r
 \r
-  //\r
-  //  Handle the close event\r
-  //\r
-  if ( 0 != ( pTftpServer->TftpPort.revents & POLLHUP )) {\r
     //\r
-    //  Close the port\r
+    //  Set the TFTP opcode and block number\r
+    //\r
+    pPacket = PacketGet ( pContext );\r
+    pBuffer = &pPacket->TxBuffer[ 0 ];\r
+    *pBuffer++ = 0;\r
+    *pBuffer++ = TFTP_OP_DATA;\r
+    *pBuffer++ = (UINT8)( pContext->BlockNumber >> 8 );\r
+    *pBuffer++ = (UINT8)pContext->BlockNumber;\r
+\r
+    //\r
+    //  Determine how much data needs to be sent\r
+    //\r
+    LengthInBytes = pContext->BlockSize;\r
+    if (( pContext->BytesToSend < TFTP_MAX_BLOCK_SIZE )\r
+      && ( LengthInBytes > pContext->BytesToSend )) {\r
+      LengthInBytes = pContext->BytesToSend;\r
+      pContext->bEofSent = TRUE;\r
+    }\r
+    DEBUG (( DEBUG_TX_PACKET,\r
+              "0x%08x: Packet, Block %d filled with %d bytes\r\n",\r
+              pPacket,\r
+              pContext->BlockNumber,\r
+              (UINT32)LengthInBytes ));\r
+    \r
+    //\r
+    //  Copy the file data into the packet\r
+    //\r
+    pPacket->TxBytes = (ssize_t)( 2 + 2 + LengthInBytes );\r
+    if ( 0 < LengthInBytes ) {\r
+      CopyMem ( pBuffer,\r
+                pContext->pBuffer,\r
+                (UINTN)LengthInBytes );\r
+      DEBUG (( DEBUG_FILE_BUFFER,\r
+                "0x%08x: Buffer consumed %d bytes of file data\r\n",\r
+                pContext->pBuffer,\r
+                LengthInBytes ));\r
+\r
+      //\r
+      //  Account for the file data consumed\r
+      //\r
+      pContext->ValidBytes -= LengthInBytes;\r
+      pContext->BytesToSend -= LengthInBytes;\r
+      pContext->pBuffer += LengthInBytes;\r
+      if ( pContext->pEnd <= pContext->pBuffer ) {\r
+        pContext->pBuffer = &pContext->FileData[ 0 ];\r
+      }\r
+    }\r
+    \r
+    //\r
+    //  Queue the packet for transmission\r
     //\r
-    close ( pTftpServer->TftpPort.fd );\r
-    pTftpServer->TftpPort.fd = -1;\r
+    PacketQueue ( pContext, pPacket );\r
   }\r
 \r
+  //\r
+  //  Return the read status\r
+  //\r
   DBG_EXIT ( );\r
+  return bReadError;\r
 }\r
 \r
 \r
 /**\r
-  Scan the list of sockets and process any pending work\r
+  Free the packet\r
 \r
-  @param [in] pTftpServer   The TFTP server control structure address.\r
+  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure\r
+  @param [in] pPacket     Address of a ::TFTP_PACKET structure\r
 \r
 **/\r
 VOID\r
-SocketPoll (\r
-  IN TSDT_TFTP_SERVER * pTftpServer\r
+PacketFree(\r
+  IN TSDT_CONNECTION_CONTEXT * pContext,\r
+  IN TFTP_PACKET * pPacket\r
   )\r
 {\r
-  int FDCount;\r
-\r
-  DEBUG (( DEBUG_SOCKET_POLL, "Entering SocketPoll\r\n" ));\r
+  DBG_ENTER ( );\r
 \r
   //\r
-  //  Determine if any ports are active\r
+  //  Don't free the error packet\r
   //\r
-  FDCount = poll ( &pTftpServer->TftpPort,\r
-                   1,\r
-                   CLIENT_POLL_DELAY );\r
-  if ( -1 == FDCount ) {\r
-    DEBUG (( DEBUG_ERROR | DEBUG_SOCKET_POLL,\r
-              "ERROR - errno: %d\r\n",\r
-              errno ));\r
-  }\r
-\r
-  if ( 0 < FDCount ) {\r
+  if ( pPacket != &pContext->ErrorPacket ) {\r
     //\r
-    //  Process this port\r
+    //  Place the packet on the free list\r
     //\r
-    PortWork ( pTftpServer );\r
-    pTftpServer->TftpPort.revents = 0;\r
+    pPacket->pNext = pContext->pFreeList;\r
+    pContext->pFreeList = pPacket;\r
+    DEBUG (( DEBUG_TX_PACKET,\r
+              "0x%08x: Packet queued to free list\r\n",\r
+              pPacket ));\r
   }\r
 \r
-  DEBUG (( DEBUG_SOCKET_POLL, "Exiting SocketPoll\r\n" ));\r
+  DBG_EXIT ( );\r
 }\r
 \r
 \r
 /**\r
-  Convert a character to lower case\r
+  Get a packet from the free list for transmission\r
 \r
-  @param [in] Character The character to convert\r
+  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure\r
 \r
-  @return   The lower case equivalent of the character\r
+  @retval Address of a ::TFTP_PACKET structure\r
 \r
 **/\r
-int\r
-tolower (\r
-  int Character\r
+TFTP_PACKET *\r
+PacketGet (\r
+  IN TSDT_CONNECTION_CONTEXT * pContext\r
   )\r
 {\r
+  TFTP_PACKET * pPacket;\r
+\r
+  DBG_ENTER ( );\r
+\r
   //\r
-  //  Determine if the character is upper case\r
+  //  Get the next packet from the free list\r
   //\r
-  if (( 'A' <= Character ) && ( 'Z' >= Character )) {\r
-    //\r
-    //  Convert the character to lower caes\r
-    //\r
-    Character += 'a' - 'A';\r
+  pPacket = pContext->pFreeList;\r
+  if ( NULL != pPacket ) {\r
+    pContext->pFreeList = pPacket->pNext;\r
+    pPacket->RetryCount = 0;\r
+    DEBUG (( DEBUG_TX_PACKET,\r
+              "0x%08x: Packet removed from free list\r\n",\r
+              pPacket ));\r
   }\r
 \r
   //\r
-  //  Return the converted character\r
+  //  Return the packet\r
   //\r
-  return Character;\r
+  DBG_EXIT_HEX ( pPacket );\r
+  return pPacket;\r
 }\r
 \r
 \r
 /**\r
-  Case independent string comparison\r
+  Queue the packet for transmission\r
 \r
-  @param [in] pString1  Zero terminated string address\r
-  @param [in] pString2  Zero terminated string address\r
+  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure\r
+  @param [in] pPacket     Address of a ::TFTP_PACKET structure\r
 \r
-  @return     Returns the first character difference between string 1\r
-              and string 2.\r
+  @retval TRUE if a transmission error has occurred\r
 \r
 **/\r
-int\r
-stricmp (\r
-  char * pString1,\r
-  char * pString2\r
+BOOLEAN\r
+PacketQueue (\r
+  IN TSDT_CONNECTION_CONTEXT * pContext,\r
+  IN TFTP_PACKET * pPacket\r
   )\r
 {\r
-  int Char1;\r
-  int Char2;\r
-  int Difference;\r
+  BOOLEAN bTransmitError;\r
+  TFTP_PACKET * pTail;\r
+  EFI_STATUS Status;\r
+\r
+  DBG_ENTER ( );\r
 \r
   //\r
-  //  Walk the length of the strings\r
+  //  Account for this data block\r
   //\r
-  do {\r
-    //\r
-    //  Get the next characters\r
-    //\r
-    Char1 = (UINT8)*pString1++;\r
-    Char2 = (UINT8)*pString2++;\r
+  pPacket->BlockNumber = pContext->BlockNumber;\r
+  pContext->BlockNumber += 1;\r
 \r
-    //\r
-    //  Convert them to lower case\r
-    //\r
-    Char1 = tolower ( Char1 );\r
-    Char2 = tolower ( Char2 );\r
+  //\r
+  //  Queue the packet for transmission\r
+  //\r
+  pTail = pContext->pTxTail;\r
+  if ( NULL == pTail ) {\r
+    pContext->pTxHead = pPacket;\r
+  }\r
+  else {\r
+    pTail->pNext = pPacket;\r
+  }\r
+  pContext->pTxTail = pPacket;\r
+  pPacket->pNext = NULL;\r
+  DEBUG (( DEBUG_TX_PACKET,\r
+            "0x%08x: Packet queued to TX list\r\n",\r
+            pPacket ));\r
 \r
-    //\r
-    //  Done when the characters differ\r
-    //\r
-    Difference = Char1 - Char2;\r
-    if ( 0 != Difference ) {\r
-      break;\r
+  //\r
+  //  Start the transmission if necessary\r
+  //\r
+  bTransmitError = FALSE;\r
+  if ( pContext->PacketsInWindow < pContext->WindowSize ) {\r
+    Status = PacketTx ( pContext, pPacket );\r
+    bTransmitError = (BOOLEAN)( EFI_ERROR ( Status ));\r
+  }\r
+\r
+  //\r
+  //  Return the transmit status\r
+  //\r
+  DBG_EXIT_TF ( bTransmitError );\r
+  return bTransmitError;\r
+}\r
+\r
+\r
+/**\r
+  Remove a packet from the transmit queue\r
+\r
+  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure\r
+\r
+**/\r
+TFTP_PACKET *\r
+PacketRemove(\r
+  IN TSDT_CONNECTION_CONTEXT * pContext\r
+  )\r
+{\r
+  TFTP_PACKET * pNext;\r
+  TFTP_PACKET * pPacket;\r
+\r
+  DBG_ENTER ( );\r
+\r
+  //\r
+  //  Remove a packet from the transmit queue\r
+  //\r
+  //\r
+  pPacket = pContext->pTxHead;\r
+  if ( NULL != pPacket ) {\r
+    pNext = pPacket->pNext;\r
+    pContext->pTxHead = pNext;\r
+    if ( NULL == pNext ) {\r
+      pContext->pTxTail = NULL;\r
     }\r
+    DEBUG (( DEBUG_TX_PACKET,\r
+              "0x%08x: Packet removed from TX list\r\n",\r
+              pPacket ));\r
 \r
     //\r
-    //  Done at the end of the string\r
+    //  Remove this packet from the window\r
     //\r
-  } while ( 0 != Char1 );\r
+    pContext->PacketsInWindow -= 1;\r
+  }\r
 \r
   //\r
-  //  Return the difference\r
+  //  Return the packet\r
   //\r
-  return Difference;\r
+  DBG_EXIT_HEX ( pPacket );\r
+  return pPacket;\r
 }\r
 \r
 \r
 /**\r
-  Get the next TFTP option\r
+  Transmit the packet\r
 \r
-  @param [in] pOption       Address of a zero terminated option string\r
-  @param [in] pEnd          End of buffer address\r
-  @param [in] ppNextOption  Address to receive the address of the next\r
-                            zero terminated option string\r
+  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure\r
+  @param [in] pPacket     Address of a ::TFTP_PACKET structure\r
 \r
   @retval EFI_SUCCESS   Message processed successfully\r
 \r
 **/\r
 EFI_STATUS\r
-TftpOptionGet (\r
-  IN UINT8 * pOption,\r
-  IN UINT8 * pEnd,\r
-  IN UINT8 ** ppNextOption\r
+PacketTx (\r
+  IN TSDT_CONNECTION_CONTEXT * pContext,\r
+  IN TFTP_PACKET * pPacket\r
   )\r
 {\r
-  UINT8 * pNextOption;\r
+  ssize_t LengthInBytes;\r
   EFI_STATUS Status;\r
 \r
+  DBG_ENTER ( );\r
+\r
   //\r
-  //  Locate the end of the option\r
+  //  Assume success\r
   //\r
-  pNextOption = pOption;\r
-  while (( pEnd > pNextOption ) && ( 0 != *pNextOption )) {\r
-    pNextOption += 1;\r
-  }\r
-  if ( pEnd <= pNextOption ) {\r
+  Status = EFI_SUCCESS;\r
+\r
+  //\r
+  //  Determine if this packet should be transmitted\r
+  //\r
+  if ( PcdGet32 ( Tftp_MaxRetry ) >= pPacket->RetryCount ) {\r
+    pPacket->RetryCount += 1;\r
+\r
     //\r
-    //  Error - end of buffer reached\r
+    //  Display the operation\r
     //\r
-    DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,\r
-              "ERROR - Option without zero termination received!\r\n" ));\r
-    Status = EFI_INVALID_PARAMETER;\r
-  }\r
-  else {\r
+    DEBUG (( DEBUG_TX_PACKET,\r
+              "0x%08x: Packet transmiting\r\n",\r
+              pPacket ));\r
+    DEBUG (( DEBUG_TX,\r
+              "0x%08x: pContext sending 0x%08x bytes\r\n",\r
+              pContext,\r
+              pPacket->TxBytes ));\r
+\r
     //\r
-    //  Zero terminated option found\r
+    //  Keep track of when the packet was transmitted\r
     //\r
-    pNextOption += 1;\r
+    if ( PcdGetBool ( Tftp_HighSpeed )) {\r
+      pPacket->TxTime = GetPerformanceCounter ( );\r
+    }\r
 \r
     //\r
-    //  Display the zero terminated ASCII option string\r
+    //  Send the TFTP packet\r
     //\r
-    DEBUG (( DEBUG_TFTP_REQUEST,\r
-              "Option: %a\r\n",\r
-              pOption ));\r
-    Status = EFI_SUCCESS;\r
+    pContext->PacketsInWindow += 1;\r
+    LengthInBytes = sendto ( pContext->SocketFd,\r
+                             &pPacket->TxBuffer[ 0 ],\r
+                             pPacket->TxBytes,\r
+                             0,\r
+                             (struct sockaddr *)&pContext->RemoteAddress,\r
+                             pContext->RemoteAddress.sin6_len );\r
+    if ( -1 == LengthInBytes ) {\r
+      DEBUG (( DEBUG_ERROR | DEBUG_TX,\r
+                "ERROR - Transmit failure, errno: 0x%08x\r\n",\r
+                errno ));\r
+      pContext->PacketsInWindow -= 1;\r
+      Status = EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+  else {\r
+    //\r
+    //  Too many retries\r
+    //\r
+    Status = EFI_NO_RESPONSE;\r
+    DEBUG (( DEBUG_WARN | DEBUG_WINDOW,\r
+              "WARNING - No response from TFTP client\r\n" ));\r
   }\r
-\r
-  //\r
-  //  Return the next option address\r
-  //\r
-  *ppNextOption = pNextOption;\r
 \r
   //\r
   //  Return the operation status\r
   //\r
+  DBG_EXIT_STATUS ( Status );\r
   return Status;\r
 }\r
 \r
 \r
 /**\r
-  Place an option value into the option acknowledgement\r
-\r
-  @param [in] pOack     Option acknowledgement address\r
-  @param [in] Value     Value to translate into ASCII decimal\r
+  Process the work for the sockets.\r
 \r
-  @return               Option acknowledgement address\r
+  @param [in] pTftpServer   Address of the ::TSDT_TFTP_SERVER structure\r
+  @param [in] pIndex        Address of an index into the pollfd array\r
 \r
 **/\r
-UINT8 *\r
-TftpOptionSet (\r
-  IN UINT8 * pOack,\r
-  IN UINT64 Value\r
+VOID\r
+PortWork (\r
+  IN TSDT_TFTP_SERVER * pTftpServer,\r
+  IN int * pIndex\r
   )\r
 {\r
-  UINT64 NextValue;\r
+  int Index;\r
+  TSDT_CONNECTION_CONTEXT * pContext;\r
+  struct pollfd * pTftpPort;\r
+  socklen_t RemoteAddressLength;\r
+  int revents;\r
 \r
-  //\r
-  //  Determine the next value\r
-  //\r
-  NextValue = Value / 10;\r
+  DBG_ENTER ( );\r
 \r
   //\r
-  //  Supress leading zeros\r
+  //  Locate the port\r
+  //\r
+  Index = *pIndex;\r
+  if ( -1 != Index ) {\r
+    pTftpPort = &pTftpServer->TftpPort[ *pIndex ];\r
+\r
+    //\r
+    //  Handle input events\r
+    //\r
+    revents = pTftpPort->revents;\r
+    pTftpPort->revents = 0;\r
+    if ( 0 != ( revents & POLLRDNORM )) {\r
+      //\r
+      //  Receive the message from the remote system\r
+      //\r
+      RemoteAddressLength = sizeof ( pTftpServer->RemoteAddress );\r
+      pTftpServer->RxBytes = recvfrom ( pTftpPort->fd,\r
+                                        &pTftpServer->RxBuffer[ 0 ],\r
+                                        sizeof ( pTftpServer->RxBuffer ),\r
+                                        0,\r
+                                        (struct sockaddr *) &pTftpServer->RemoteAddress,\r
+                                        &RemoteAddressLength );\r
+      if ( -1 != pTftpServer->RxBytes ) {\r
+        if ( PcdGetBool ( Tftp_HighSpeed )) {\r
+          pTftpServer->RxTime = GetPerformanceCounter ( );\r
+        }\r
+        if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {\r
+          DEBUG (( DEBUG_TFTP_PORT,\r
+                   "Received %d bytes from %d.%d.%d.%d:%d\r\n",\r
+                   pTftpServer->RxBytes,\r
+                   pTftpServer->RemoteAddress.v4.sin_addr.s_addr & 0xff,\r
+                   ( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ) & 0xff,\r
+                   ( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ) & 0xff,\r
+                   ( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ) & 0xff,\r
+                   htons ( pTftpServer->RemoteAddress.v4.sin_port )));\r
+        }\r
+        else {\r
+          DEBUG (( DEBUG_TFTP_PORT,\r
+                   "Received %d bytes from [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",\r
+                   pTftpServer->RxBytes,\r
+                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],\r
+                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],\r
+                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],\r
+                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],\r
+                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],\r
+                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],\r
+                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],\r
+                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],\r
+                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],\r
+                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],\r
+                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],\r
+                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],\r
+                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],\r
+                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],\r
+                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],\r
+                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],\r
+                   htons ( pTftpServer->RemoteAddress.v6.sin6_port )));\r
+        }\r
+\r
+        //\r
+        //  Lookup connection context using the remote system address and port\r
+        //  to determine if an existing connection to this remote\r
+        //  system exists\r
+        //\r
+        pContext = ContextFind ( pTftpServer );\r
+\r
+        //\r
+        //  Process the received message\r
+        //\r
+        TftpProcessRequest ( pTftpServer, pContext, pTftpPort->fd );\r
+      }\r
+      else {\r
+        //\r
+        //  Receive error on the TFTP server port\r
+        //  Close the server socket\r
+        //\r
+        DEBUG (( DEBUG_ERROR,\r
+                  "ERROR - Failed receive on TFTP server port, errno: 0x%08x\r\n",\r
+                  errno ));\r
+        revents |= POLLHUP;\r
+      }\r
+    }\r
+\r
+    //\r
+    //  Handle the close event\r
+    //\r
+    if ( 0 != ( revents & POLLHUP )) {\r
+      //\r
+      //  Close the port\r
+      //\r
+      close ( pTftpPort->fd );\r
+      pTftpPort->fd = -1;\r
+      *pIndex = -1;\r
+      pTftpServer->Entries -= 1;\r
+      ASSERT ( 0 <= pTftpServer->Entries );\r
+    }\r
+  }\r
+\r
+  DBG_EXIT ( );\r
+}\r
+\r
+\r
+/**\r
+  Build and send an error packet\r
+\r
+  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure\r
+  @param [in] Error       Error number for the packet\r
+  @param [in] pError      Zero terminated error string address\r
+\r
+  @retval EFI_SUCCESS     Message processed successfully\r
+\r
+**/\r
+EFI_STATUS\r
+SendError (\r
+  IN TSDT_CONNECTION_CONTEXT * pContext,\r
+  IN UINT16 Error,\r
+  IN UINT8 * pError\r
+  )\r
+{\r
+  UINT8 Character;\r
+  UINT8 * pBuffer;\r
+  TFTP_PACKET * pPacket;\r
+  EFI_STATUS Status;\r
+\r
+  DBG_ENTER ( );\r
+\r
+  //\r
+  //  Build the error packet\r
+  //\r
+  pPacket = &pContext->ErrorPacket;\r
+  pBuffer = &pPacket->TxBuffer[ 0 ];\r
+  pBuffer[ 0 ] = 0;\r
+  pBuffer[ 1 ] = TFTP_OP_ERROR;\r
+  pBuffer[ 2 ] = (UINT8)( Error >> 8 );\r
+  pBuffer[ 3 ] = (UINT8)Error;\r
+\r
+  //\r
+  //  Copy the zero terminated string into the buffer\r
+  //\r
+  pBuffer += 4;\r
+  do {\r
+    Character = *pError++;\r
+    *pBuffer++ = Character;\r
+  } while ( 0 != Character );\r
+\r
+  //\r
+  //  Send the error message\r
+  //\r
+  pPacket->TxBytes = pBuffer - &pPacket->TxBuffer[ 0 ];\r
+  Status = PacketTx ( pContext, pPacket );\r
+\r
+  //\r
+  //  Return the operation status\r
+  //\r
+  DBG_EXIT_STATUS ( Status );\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Scan the list of sockets and process any pending work\r
+\r
+  @param [in] pTftpServer   Address of the ::TSDT_TFTP_SERVER structure\r
+\r
+**/\r
+VOID\r
+SocketPoll (\r
+  IN TSDT_TFTP_SERVER * pTftpServer\r
+  )\r
+{\r
+  int FDCount;\r
+\r
+  DEBUG (( DEBUG_SOCKET_POLL, "Entering SocketPoll\r\n" ));\r
+\r
+  //\r
+  //  Determine if any ports are active\r
+  //\r
+  if ( 0 != pTftpServer->Entries ) {\r
+    FDCount = poll ( &pTftpServer->TftpPort[ 0 ],\r
+                     pTftpServer->Entries,\r
+                     CLIENT_POLL_DELAY );\r
+    if ( 0 < FDCount ) {\r
+      //\r
+      //  Process this port\r
+      //\r
+      PortWork ( pTftpServer, &pTftpServer->Udpv4Index );\r
+      PortWork ( pTftpServer, &pTftpServer->Udpv6Index );\r
+    }\r
+  }\r
+\r
+  DEBUG (( DEBUG_SOCKET_POLL, "Exiting SocketPoll\r\n" ));\r
+}\r
+\r
+\r
+/**\r
+  Process the ACK\r
+\r
+  @param [in] pTftpServer   Address of the ::TSDT_TFTP_SERVER structure\r
+  @param [in] pContext    Connection context structure address\r
+\r
+  @retval TRUE if the context should be closed\r
+\r
+**/\r
+BOOLEAN\r
+TftpAck (\r
+  IN TSDT_TFTP_SERVER * pTftpServer,\r
+  IN TSDT_CONNECTION_CONTEXT * pContext\r
+  )\r
+{\r
+  INTN AckNumber;\r
+  BOOLEAN bCloseContext;\r
+  UINT16 BlockNumber;\r
+  UINT8 * pBuffer;\r
+  TFTP_PACKET * pPacket;\r
+  EFI_STATUS Status;\r
+\r
+  DBG_ENTER ( );\r
+\r
+  //\r
+  //  Use break instead of goto\r
+  //\r
+  bCloseContext = FALSE;\r
+  for ( ; ; ) {\r
+    //\r
+    //  Validate the parameters\r
+    //\r
+    if ( NULL == pContext ) {\r
+      if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {\r
+        DEBUG (( DEBUG_ERROR,\r
+                  "ERROR - File not open for %d.%d.%d.%d:%d\r\n",\r
+                  (UINT8)pTftpServer->RemoteAddress.v4.sin_addr.s_addr,\r
+                  (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ),\r
+                  (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ),\r
+                  (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ),\r
+                  htons ( pTftpServer->RemoteAddress.v4.sin_port )));\r
+      }\r
+      else {\r
+        DEBUG (( DEBUG_ERROR,\r
+                  "ERROR - File not open for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],\r
+                  htons ( pTftpServer->RemoteAddress.v6.sin6_port )));\r
+      }\r
+      break;\r
+    }\r
+\r
+    //\r
+    //  Verify that the ACK was expected\r
+    //\r
+    pPacket = pContext->pTxHead;\r
+    if ( NULL == pPacket ) {\r
+      //\r
+      //  ACK not expected!\r
+      //\r
+      DEBUG (( DEBUG_ERROR,\r
+                "ERROR - Expecting data not ACKs for pContext 0x%08x\r\n",\r
+                pContext ));\r
+      break;\r
+    }\r
+\r
+    //\r
+    //  Get the ACKed block number\r
+    //\r
+    pBuffer = &pTftpServer->RxBuffer[ 0 ];\r
+    BlockNumber = HTONS ( *(UINT16 *)&pBuffer[ 2 ]);\r
+\r
+    //\r
+    //  Determine if this is the correct ACK\r
+    //\r
+    DEBUG (( DEBUG_TFTP_ACK,\r
+              "ACK for block 0x%04x received\r\n",\r
+              BlockNumber ));\r
+    AckNumber = BlockNumber - pPacket->BlockNumber;\r
+    if (( 0 > AckNumber ) || ( AckNumber >= (INTN)pContext->PacketsInWindow )){\r
+      DEBUG (( DEBUG_WARN | DEBUG_TFTP_ACK,\r
+                "WARNING - Expecting ACK 0x%0x4 not received ACK 0x%08x\r\n",\r
+                pPacket->BlockNumber,\r
+                BlockNumber ));\r
+      break;\r
+    }\r
+\r
+    //\r
+    //  Release the ACKed packets\r
+    //\r
+    do {\r
+      //\r
+      //  Remove the packet from the transmit list and window\r
+      //\r
+      pPacket = PacketRemove ( pContext );\r
+\r
+      //\r
+      //  Get the block number of this packet\r
+      //\r
+      AckNumber = pPacket->BlockNumber;\r
+\r
+      //\r
+      //  Increase the size of the transmit window\r
+      //\r
+      if ( PcdGetBool ( Tftp_HighSpeed )\r
+        && ( AckNumber == BlockNumber )) {\r
+        WindowAck ( pTftpServer, pContext, pPacket );\r
+      }\r
+\r
+      //\r
+      //  Free this packet\r
+      //\r
+      PacketFree ( pContext, pPacket );\r
+    } while (( NULL != pContext->pTxHead ) && ( AckNumber != BlockNumber ));\r
+\r
+    //\r
+    //  Fill the window with packets\r
+    //\r
+    pPacket = pContext->pTxHead;\r
+    while (( NULL != pPacket )\r
+      && ( pContext->PacketsInWindow < pContext->WindowSize )\r
+      && ( !bCloseContext )) {\r
+      Status = PacketTx ( pContext, pPacket );\r
+      bCloseContext = (BOOLEAN)( EFI_ERROR ( Status ));\r
+      pPacket = pPacket->pNext;\r
+    }\r
+    \r
+    //\r
+    //  Get more packets ready for transmission\r
+    //\r
+    PacketFill ( pContext );\r
+\r
+    //\r
+    //  Close the context when the last packet is ACKed\r
+    //\r
+    if ( 0 == pContext->PacketsInWindow ) {\r
+      bCloseContext = TRUE;\r
+\r
+      //\r
+      //  Display the bandwidth\r
+      //\r
+      if ( PcdGetBool ( Tftp_Bandwidth )) {\r
+        UINT64 Bandwidth;\r
+        UINT64 DeltaTime;\r
+        UINT64 NanoSeconds;\r
+        UINT32 Value;\r
+\r
+        //\r
+        //  Compute the download time\r
+        //\r
+        DeltaTime = GetPerformanceCounter ( );\r
+        if ( pTftpServer->Time2 > pTftpServer->Time1 ) {\r
+          DeltaTime = DeltaTime - pContext->TimeStart;\r
+        }\r
+        else {\r
+          DeltaTime = pContext->TimeStart - DeltaTime;\r
+        }\r
+        NanoSeconds = GetTimeInNanoSecond ( DeltaTime );\r
+        Bandwidth = pContext->LengthInBytes;\r
+        DEBUG (( DEBUG_WINDOW,\r
+                  "File Length %Ld, Transfer Time: %d.%03d Sec\r\n",\r
+                  Bandwidth,\r
+                  DivU64x32 ( NanoSeconds, 1000 * 1000 * 1000 ),\r
+                  ((UINT32)DivU64x32 ( NanoSeconds, 1000 * 1000 )) % 1000 ));\r
+\r
+        //\r
+        //  Display the round trip time\r
+        //\r
+        Bandwidth = MultU64x32 ( Bandwidth, 8 * 1000 * 1000 );\r
+        Bandwidth /= NanoSeconds;\r
+        if ( 1000 > Bandwidth ) {\r
+          Value = (UINT32)Bandwidth;\r
+          Print ( L"Bandwidth: %d Kbits/Sec\r\n",\r
+                  Value );\r
+        }\r
+        else if (( 1000 * 1000 ) > Bandwidth ) {\r
+          Value = (UINT32)Bandwidth;\r
+          Print ( L"Bandwidth: %d.%03d Mbits/Sec\r\n",\r
+                  Value / 1000,\r
+                  Value % 1000 );\r
+        }\r
+        else {\r
+          Value = (UINT32)DivU64x32 ( Bandwidth, 1000 );\r
+          Print ( L"Bandwidth: %d.%03d Gbits/Sec\r\n",\r
+                  Value / 1000,\r
+                  Value % 1000 );\r
+        }\r
+      }\r
+    }\r
+    break;\r
+  }\r
+\r
+  //\r
+  //  Return the operation status\r
+  //\r
+  DBG_EXIT ( );\r
+  return bCloseContext;\r
+}\r
+\r
+\r
+/**\r
+  Get the next TFTP option\r
+\r
+  @param [in] pOption       Address of a zero terminated option string\r
+  @param [in] pEnd          End of buffer address\r
+  @param [in] ppNextOption  Address to receive the address of the next\r
+                            zero terminated option string\r
+\r
+  @retval EFI_SUCCESS   Message processed successfully\r
+\r
+**/\r
+EFI_STATUS\r
+TftpOptionGet (\r
+  IN UINT8 * pOption,\r
+  IN UINT8 * pEnd,\r
+  IN UINT8 ** ppNextOption\r
+  )\r
+{\r
+  UINT8 * pNextOption;\r
+  EFI_STATUS Status;\r
+\r
+  //\r
+  //  Locate the end of the option\r
+  //\r
+  pNextOption = pOption;\r
+  while (( pEnd > pNextOption ) && ( 0 != *pNextOption )) {\r
+    pNextOption += 1;\r
+  }\r
+  if ( pEnd <= pNextOption ) {\r
+    //\r
+    //  Error - end of buffer reached\r
+    //\r
+    DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,\r
+              "ERROR - Option without zero termination received!\r\n" ));\r
+    Status = EFI_INVALID_PARAMETER;\r
+  }\r
+  else {\r
+    //\r
+    //  Zero terminated option found\r
+    //\r
+    pNextOption += 1;\r
+\r
+    //\r
+    //  Display the zero terminated ASCII option string\r
+    //\r
+    DEBUG (( DEBUG_TFTP_REQUEST,\r
+              "Option: %a\r\n",\r
+              pOption ));\r
+    Status = EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  //  Return the next option address\r
+  //\r
+  *ppNextOption = pNextOption;\r
+\r
+  //\r
+  //  Return the operation status\r
+  //\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Place an option value into the option acknowledgement\r
+\r
+  @param [in] pOack     Option acknowledgement address\r
+  @param [in] Value     Value to translate into ASCII decimal\r
+\r
+  @return               Option acknowledgement address\r
+\r
+**/\r
+UINT8 *\r
+TftpOptionSet (\r
+  IN UINT8 * pOack,\r
+  IN UINT64 Value\r
+  )\r
+{\r
+  UINT64 NextValue;\r
+\r
+  //\r
+  //  Determine the next value\r
+  //\r
+  NextValue = Value / 10;\r
+\r
+  //\r
+  //  Supress leading zeros\r
   //\r
   if ( 0 != NextValue ) {\r
     pOack = TftpOptionSet ( pOack, NextValue );\r
@@ -539,7 +1245,7 @@ TftpOptionSet (
 /**\r
   Process the TFTP request\r
 \r
-  @param [in] pContext  The context structure address.\r
+  @param [in] pContext  Address of a ::TSDT_CONNECTION_CONTEXT structure\r
   @param [in] pOption   Address of the first zero terminated option string\r
   @param [in] pEnd      End of buffer address\r
 \r
@@ -553,20 +1259,27 @@ TftpOptions (
 {\r
   UINT8 * pNextOption;\r
   UINT8 * pOack;\r
+  TFTP_PACKET * pPacket;\r
   UINT8 * pTemp;\r
   UINT8 * pValue;\r
   EFI_STATUS Status;\r
   INT32 Value;\r
 \r
+  //\r
+  //  Get a packet\r
+  //\r
+  pPacket = PacketGet ( pContext );\r
+\r
   //\r
   //  Start the OACK packet\r
   //  Let the OACK handle the parsing errors\r
   //  See http://tools.ietf.org/html/rfc2347\r
   //\r
-  pOack = &pContext->TxBuffer[0];\r
+  pOack = &pPacket->TxBuffer[ 0 ];\r
   *pOack++ = 0;\r
   *pOack++ = TFTP_OP_OACK;\r
-  pContext->TxBytes = 2;\r
+  pPacket->TxBytes = 2;\r
+  pPacket->BlockNumber = 0;\r
 \r
   //\r
   //  Walk the list of options\r
@@ -585,7 +1298,7 @@ TftpOptions (
       //  blksize - See http://tools.ietf.org/html/rfc2348\r
       //\r
       pValue = pNextOption;\r
-      if ( 0 == stricmp ((char *)pOption, "blksize" )) {\r
+      if ( 0 == strcasecmp ((char *)pOption, "blksize" )) {\r
         //\r
         //  Get the value\r
         //\r
@@ -625,7 +1338,7 @@ TftpOptions (
             *pOack++ = 0;\r
             pOack = TftpOptionSet ( pOack, pContext->BlockSize );\r
             *pOack++ = 0;\r
-            pContext->TxBytes += pOack - pTemp;\r
+            pPacket->TxBytes += pOack - pTemp;\r
           }\r
         }\r
       }\r
@@ -633,7 +1346,7 @@ TftpOptions (
       //\r
       //  timeout - See http://tools.ietf.org/html/rfc2349\r
       //\r
-      else if ( 0 == stricmp ((char *)pOption, "timeout" )) {\r
+      else if ( 0 == strcasecmp ((char *)pOption, "timeout" )) {\r
         //\r
         //  Get the value\r
         //\r
@@ -644,10 +1357,10 @@ TftpOptions (
             //\r
             //  Set the timeout value\r
             //\r
-            pContext->Timeout = Value;\r
+            pContext->MaxTimeout = Value;\r
             DEBUG (( DEBUG_TFTP_REQUEST,\r
                       "Using timeout of %d seconds\r\n",\r
-                      pContext->Timeout ));\r
+                      pContext->MaxTimeout ));\r
 \r
             //\r
             //  Update the OACK\r
@@ -661,9 +1374,9 @@ TftpOptions (
             *pOack++ = 'u';\r
             *pOack++ = 't';\r
             *pOack++ = 0;\r
-            pOack = TftpOptionSet ( pOack, pContext->Timeout );\r
+            pOack = TftpOptionSet ( pOack, pContext->MaxTimeout );\r
             *pOack++ = 0;\r
-            pContext->TxBytes += pOack - pTemp;\r
+            pPacket->TxBytes += pOack - pTemp;\r
           }\r
         }\r
       }\r
@@ -671,7 +1384,7 @@ TftpOptions (
       //\r
       //  tsize - See http://tools.ietf.org/html/rfc2349\r
       //\r
-      else if ( 0 == stricmp ((char *)pOption, "tsize" )) {\r
+      else if ( 0 == strcasecmp ((char *)pOption, "tsize" )) {\r
         //\r
         //  Get the value\r
         //\r
@@ -698,7 +1411,7 @@ TftpOptions (
             *pOack++ = 0;\r
             pOack = TftpOptionSet ( pOack, pContext->LengthInBytes );\r
             *pOack++ = 0;\r
-            pContext->TxBytes += pOack - pTemp;\r
+            pPacket->TxBytes += pOack - pTemp;\r
           }\r
         }\r
       }\r
@@ -717,6 +1430,16 @@ TftpOptions (
     //\r
     pOption = pNextOption;\r
   } while ( pEnd > pOption );\r
+\r
+  //\r
+  //  Transmit the OACK if necessary\r
+  //\r
+  if ( 2 < pPacket->TxBytes ) {\r
+    PacketQueue ( pContext, pPacket );\r
+  }\r
+  else {\r
+    PacketFree ( pContext, pPacket );\r
+  }\r
 }\r
 \r
 \r
@@ -781,302 +1504,137 @@ TftpOptionValue (
 /**\r
   Process the TFTP request\r
 \r
-  @param [in] pTftpServer The TFTP server control structure address.\r
-  @param [in] pContext    Connection context structure address\r
+  @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure\r
+  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure\r
+  @param [in] SocketFd    Socket file descriptor\r
 \r
 **/\r
 VOID\r
 TftpProcessRequest (\r
   IN TSDT_TFTP_SERVER * pTftpServer,\r
-  IN TSDT_CONNECTION_CONTEXT * pContext\r
+  IN TSDT_CONNECTION_CONTEXT * pContext,\r
+  IN int SocketFd\r
   )\r
 {\r
   BOOLEAN bCloseContext;\r
-  BOOLEAN bIgnorePacket;\r
-  UINT16 BlockNumber;\r
   UINT16 Opcode;\r
-  UINT8 * pBuffer;\r
-  UINT8 * pEnd;\r
-  UINT8 * pFileName;\r
-  UINT8 * pMode;\r
-  UINT8 * pOption;\r
-  EFI_STATUS Status;\r
 \r
   DBG_ENTER ( );\r
 \r
   //\r
   //  Get the opcode\r
   //\r
-  pBuffer = &pTftpServer->RxBuffer[0];\r
-  Opcode = HTONS ( *(UINT16 *)&pBuffer[0]);\r
-Print ( L"TFTP Opcode: 0x%08x\r\n", Opcode );\r
+  Opcode = HTONS ( *(UINT16 *)&pTftpServer->RxBuffer[ 0 ]);\r
+  DEBUG (( DEBUG_TFTP_REQUEST,\r
+            "TFTP Opcode: 0x%08x\r\n",\r
+            Opcode ));\r
 \r
   //\r
   //  Validate the parameters\r
   //\r
   bCloseContext = FALSE;\r
-  bIgnorePacket = FALSE;\r
   switch ( Opcode ) {\r
   default:\r
     DEBUG (( DEBUG_TFTP_REQUEST,\r
               "ERROR - Unknown TFTP opcode: %d\r\n",\r
               Opcode ));\r
-    bIgnorePacket = TRUE;\r
+    break;\r
+\r
+  case TFTP_OP_ACK:\r
+    bCloseContext = TftpAck ( pTftpServer, pContext );\r
     break;\r
 \r
   case TFTP_OP_READ_REQUEST:\r
+    bCloseContext = TftpRead ( pTftpServer, pContext, SocketFd );\r
     break;\r
 \r
+\r
+\r
+  \r
   case TFTP_OP_DATA:\r
     if ( NULL == pContext ) {\r
-      DEBUG (( DEBUG_ERROR,\r
-                "ERROR - File not open for %d.%d.%d.%d:%d\r\n",\r
-                (UINT8)pTftpServer->RemoteAddress.sin_addr.s_addr,\r
-                (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 8 ),\r
-                (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 16 ),\r
-                (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 24 ),\r
-                htons ( pTftpServer->RemoteAddress.sin_port )));\r
-      bIgnorePacket = TRUE;\r
+      if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {\r
+        DEBUG (( DEBUG_ERROR,\r
+                  "ERROR - File not open for %d.%d.%d.%d:%d\r\n",\r
+                  (UINT8)pTftpServer->RemoteAddress.v4.sin_addr.s_addr,\r
+                  (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ),\r
+                  (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ),\r
+                  (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ),\r
+                  htons ( pTftpServer->RemoteAddress.v4.sin_port )));\r
+      }\r
+      else {\r
+        DEBUG (( DEBUG_ERROR,\r
+                  "ERROR - File not open for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],\r
+                  htons ( pTftpServer->RemoteAddress.v6.sin6_port )));\r
+      }\r
       break;\r
     }\r
-    if ( pContext->bExpectAck ) {\r
+    if ( 0 != pContext->PacketsInWindow ) {\r
       DEBUG (( DEBUG_ERROR,\r
                 "ERROR - Expecting ACKs not data for pContext 0x%08x\r\n",\r
                 pContext ));\r
-      bIgnorePacket = TRUE;\r
       break;\r
     }\r
-    if ( pTftpServer->RxBytes > (ssize_t)( pContext->BlockSize + 2 + 2 ))\r
-    {\r
+    if ( pTftpServer->RxBytes > (ssize_t)( pContext->BlockSize + 2 + 2 )) {\r
       DEBUG (( DEBUG_ERROR,\r
                 "ERROR - Receive data length of %d > %d bytes (maximum block size) for pContext 0x%08x\r\n",\r
                 pTftpServer->RxBytes - 2 - 2,\r
                 pContext->BlockSize,\r
                 pContext ));\r
-      bIgnorePacket = TRUE;\r
-      break;\r
-    }\r
-    break;\r
-\r
-  case TFTP_OP_ACK:\r
-    if ( NULL == pContext ) {\r
-      DEBUG (( DEBUG_ERROR,\r
-                "ERROR - File not open for %d.%d.%d.%d:%d\r\n",\r
-                (UINT8)pTftpServer->RemoteAddress.sin_addr.s_addr,\r
-                (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 8 ),\r
-                (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 16 ),\r
-                (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 24 ),\r
-                htons ( pTftpServer->RemoteAddress.sin_port )));\r
-      bIgnorePacket = TRUE;\r
-    }\r
-    if ( !pContext->bExpectAck ) {\r
-      DEBUG (( DEBUG_ERROR,\r
-                "ERROR - Expecting data not ACKs for pContext 0x%08x\r\n",\r
-                pContext ));\r
-      bIgnorePacket = TRUE;\r
       break;\r
     }\r
     break;\r
 \r
   case TFTP_OP_ERROR:\r
     if ( NULL == pContext ) {\r
-      DEBUG (( DEBUG_ERROR,\r
-                "ERROR - File not open for %d.%d.%d.%d:%d\r\n",\r
-                (UINT8)pTftpServer->RemoteAddress.sin_addr.s_addr,\r
-                (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 8 ),\r
-                (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 16 ),\r
-                (UINT8)( pTftpServer->RemoteAddress.sin_addr.s_addr >> 24 ),\r
-                htons ( pTftpServer->RemoteAddress.sin_port )));\r
-      bIgnorePacket = TRUE;\r
-    }\r
-    break;\r
-  }\r
-  if ( !bIgnorePacket ) {\r
-    //\r
-    //  Process the request\r
-    //\r
-    switch ( Opcode ) {\r
-    default:\r
-      DEBUG (( DEBUG_TFTP_REQUEST,\r
-                "ERROR - Unable to process TFTP opcode: %d\r\n",\r
-                Opcode ));\r
-      break;\r
-\r
-    case TFTP_OP_READ_REQUEST:\r
-\r
-      //\r
-      //  Close the context if necessary\r
-      //\r
-      if ( NULL != pContext ) {\r
-        ContextRemove ( pTftpServer, pContext );\r
-      }\r
-\r
-      //\r
-      //  Create the connection context\r
-      //\r
-      pContext = ContextAdd ( pTftpServer );\r
-      if ( NULL == pContext ) {\r
-        break;\r
-      }\r
-\r
-      //\r
-      //  Locate the mode\r
-      //\r
-      pFileName = &pBuffer[2];\r
-      pEnd = &pBuffer[pTftpServer->RxBytes];\r
-      pMode = pFileName;\r
-      while (( pEnd > pMode ) && ( 0 != *pMode )) {\r
-        pMode += 1;\r
-      }\r
-      if ( pEnd <= pMode ) {\r
-        //\r
-        //  Mode not found\r
-        //\r
-        DEBUG (( DEBUG_ERROR | DEBUG_RX,\r
-                  "ERROR - File mode not found\r\n" ));\r
-        //\r
-        //  Tell the client of the error\r
-        //\r
-        TftpSendError ( pTftpServer,\r
-                        pContext,\r
-                        0,\r
-                        (UINT8 *)"File open mode not found" );\r
-        break;\r
-      }\r
-      pMode += 1;\r
-      DEBUG (( DEBUG_TFTP_REQUEST,\r
-                "TFTP - FileName: %a\n",\r
-                pFileName ));\r
-\r
-      //\r
-      //  Locate the options\r
-      //\r
-      pOption = pMode;\r
-      while (( pEnd > pOption ) && ( 0 != *pOption )) {\r
-        pOption += 1;\r
-      }\r
-      if ( pEnd <= pOption ) {\r
-        //\r
-        //  End of mode not found\r
-        //\r
-        DEBUG (( DEBUG_ERROR | DEBUG_RX,\r
-                  "ERROR - File mode not valid\r\n" ));\r
-        //\r
-        //  Tell the client of the error\r
-        //\r
-        TftpSendError ( pTftpServer,\r
-                        pContext,\r
-                        0,\r
-                        (UINT8 *)"File open mode not valid" );\r
-        break;\r
-      }\r
-      pOption += 1;\r
-      DEBUG (( DEBUG_TFTP_REQUEST,\r
-                "TFTP - Mode: %a\r\n",\r
-                pMode ));\r
-\r
-      //\r
-      //  Verify the mode is supported\r
-      //\r
-      if ( 0 != stricmp ((char *)pMode, "octet" )) {\r
-        //\r
-        //  File access mode not supported\r
-        //\r
-        DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,\r
-                  "ERROR - File mode %a not supported\r\n",\r
-                  pMode ));\r
-\r
-        //\r
-        //  Tell the client of the error\r
-        //\r
-        TftpSendError ( pTftpServer,\r
-                        pContext,\r
-                        0,\r
-                        (UINT8 *)"File open mode not supported" );\r
-        break;\r
-      }\r
-\r
-      //\r
-      //  Open the file, close the context on error\r
-      //\r
-// TODO: Remove the following line\r
-pContext->File = (EFI_HANDLE)1;\r
-\r
-      //\r
-      //  Determine the file length\r
-      //\r
-//fstat\r
-\r
-      //\r
-      //  Process the options\r
-      //\r
-      TftpOptions ( pContext, pOption, pEnd );\r
-\r
-      //\r
-      //  Read in the first portion of the file\r
-      //\r
-\r
-      //\r
-      //  Send the first block\r
-      //\r
-      pContext->bExpectAck = TRUE;\r
-      if ( 2 < pContext->TxBytes ) {\r
-        //\r
-        //  Send the OACK\r
-        //\r
-        Status = TftpTxPacket ( pTftpServer, pContext );\r
+      if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {\r
+        DEBUG (( DEBUG_ERROR,\r
+                  "ERROR - File not open for %d.%d.%d.%d:%d\r\n",\r
+                  (UINT8)pTftpServer->RemoteAddress.v4.sin_addr.s_addr,\r
+                  (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ),\r
+                  (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ),\r
+                  (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ),\r
+                  htons ( pTftpServer->RemoteAddress.v4.sin_port )));\r
       }\r
       else {\r
-        //\r
-        //  Send the first block of data\r
-        //\r
-        Status = TftpSendNextBlock ( pTftpServer, pContext );\r
-      }\r
-      break;\r
-\r
-    case TFTP_OP_ACK:\r
-      //\r
-      //  Get the block number that is being ACKed\r
-      //\r
-      BlockNumber = pTftpServer->RxBuffer[2];\r
-      BlockNumber <<= 8;\r
-      BlockNumber |= pTftpServer->RxBuffer[3];\r
-\r
-      //\r
-      //  Determine if this is the correct ACK\r
-      //\r
-      DEBUG (( DEBUG_TFTP_ACK,\r
-                "ACK for block 0x%04x received\r\n",\r
-                BlockNumber ));\r
-      if (( !pContext->bExpectAck )\r
-        || ( BlockNumber != pContext->AckNext ))\r
-      {\r
-        DEBUG (( DEBUG_WARN | DEBUG_TFTP_ACK,\r
-                  "WARNING - Expecting ACK 0x%0x4 not received ACK 0x%08x\r\n",\r
-                  pContext->AckNext,\r
-                  BlockNumber ));\r
+        DEBUG (( DEBUG_ERROR,\r
+                  "ERROR - File not open for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],\r
+                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],\r
+                  htons ( pTftpServer->RemoteAddress.v6.sin6_port )));\r
       }\r
-      else {\r
-        //\r
-        //  Process the expected ACK\r
-        //\r
-        if ( pContext->bEofSent ) {\r
-          bCloseContext = TRUE;\r
-        }\r
-        else {\r
-          //\r
-          //  Set the next expected ACK\r
-          //\r
-          pContext->AckNext += 1;\r
-\r
-          //\r
-          //  Send the next packet of data\r
-          //\r
-          Status = TftpSendNextBlock ( pTftpServer, pContext );\r
-        }\r
-      }\r
-      break;\r
     }\r
+    break;\r
   }\r
 \r
   //\r
@@ -1091,119 +1649,229 @@ pContext->File = (EFI_HANDLE)1;
 \r
 \r
 /**\r
-  Build and send an error packet\r
+  Process the read request\r
 \r
-  @param [in] pTftpServer The TFTP server control structure address.\r
-  @param [in] pContext    The context structure address.\r
-  @param [in] Error       Error number for the packet\r
-  @param [in] pError      Zero terminated error string address\r
+  @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure\r
+  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure\r
+  @param [in] SocketFd    Socket file descriptor\r
 \r
-  @retval EFI_SUCCESS     Message processed successfully\r
+  @retval TRUE if the context should be closed\r
 \r
 **/\r
-EFI_STATUS\r
-TftpSendError (\r
+BOOLEAN\r
+TftpRead (\r
   IN TSDT_TFTP_SERVER * pTftpServer,\r
   IN TSDT_CONNECTION_CONTEXT * pContext,\r
-  IN UINT16 Error,\r
-  IN UINT8 * pError\r
+  IN int SocketFd\r
   )\r
 {\r
-  UINT8 Character;\r
+  BOOLEAN bCloseContext;\r
+  struct stat FileStatus;\r
   UINT8 * pBuffer;\r
-  EFI_STATUS Status;\r
+  UINT8 * pEnd;\r
+  UINT8 * pFileName;\r
+  UINT8 * pMode;\r
+  UINT8 * pOption;\r
+  CHAR8 * pReadMode;\r
+  UINT64 TimeStart;\r
 \r
   DBG_ENTER ( );\r
 \r
   //\r
-  //  Build the error packet\r
+  //  Log the receive time\r
   //\r
-  pBuffer = &pContext->TxBuffer[0];\r
-  pBuffer[0] = 0;\r
-  pBuffer[1] = TFTP_OP_ERROR;\r
-  pBuffer[2] = (UINT8)( Error >> 8 );\r
-  pBuffer[3] = (UINT8)Error;\r
+  TimeStart = 0;\r
+  if ( PcdGetBool ( Tftp_Bandwidth )) {\r
+    TimeStart = GetPerformanceCounter ( );\r
+  }\r
 \r
   //\r
-  //  Copy the zero terminated string into the buffer\r
+  //  Close the context if necessary\r
   //\r
-  pBuffer += 4;\r
-  do {\r
-    Character = *pError++;\r
-    *pBuffer++ = Character;\r
-  } while ( 0 != Character );\r
+  bCloseContext = FALSE;\r
+  if ( NULL != pContext ) {\r
+    ContextRemove ( pTftpServer, pContext );\r
+  }\r
 \r
   //\r
-  //  Send the error message\r
+  //  Use break instead of goto\r
   //\r
-  pContext->TxBytes = pBuffer - &pContext->TxBuffer[0];\r
-  Status = TftpTxPacket ( pTftpServer, pContext );\r
+  for ( ; ; ) {\r
+    //\r
+    //  Create the connection context\r
+    //\r
+    pContext = ContextAdd ( pTftpServer, SocketFd );\r
+    if ( NULL == pContext ) {\r
+      break;\r
+    }\r
 \r
-  //\r
-  //  Return the operation status\r
-  //\r
-  DBG_EXIT_STATUS ( Status );\r
-  return Status;\r
-}\r
+    //\r
+    //  Set the start time\r
+    //\r
+    if ( PcdGetBool ( Tftp_Bandwidth )) {\r
+      pContext->TimeStart = TimeStart;\r
+    }\r
 \r
+    //\r
+    //  Locate the mode\r
+    //\r
+    pBuffer = &pTftpServer->RxBuffer[ 0 ];\r
+    pEnd = &pBuffer[ pTftpServer->RxBytes ];\r
+    pFileName = &pBuffer[ 2 ];\r
+    pMode = pFileName;\r
+    while (( pEnd > pMode ) && ( 0 != *pMode )) {\r
+      pMode += 1;\r
+    }\r
+    if ( pEnd <= pMode ) {\r
+      //\r
+      //  Mode not found\r
+      //\r
+      DEBUG (( DEBUG_ERROR | DEBUG_RX,\r
+                "ERROR - File mode not found\r\n" ));\r
+      //\r
+      //  Tell the client of the error\r
+      //\r
+      SendError ( pContext,\r
+                  TFTP_ERROR_SEE_MSG,\r
+                  (UINT8 *)"File open mode not found" );\r
+      break;\r
+    }\r
+    pMode += 1;\r
+    DEBUG (( DEBUG_TFTP_REQUEST,\r
+              "TFTP - FileName: %a\r\n",\r
+              pFileName ));\r
 \r
-/**\r
-  Send the next block of file system data\r
+    //\r
+    //  Locate the options\r
+    //\r
+    pOption = pMode;\r
+    while (( pEnd > pOption ) && ( 0 != *pOption )) {\r
+      pOption += 1;\r
+    }\r
+    if ( pEnd <= pOption ) {\r
+      //\r
+      //  End of mode not found\r
+      //\r
+      DEBUG (( DEBUG_ERROR | DEBUG_RX,\r
+                "ERROR - File mode not valid\r\n" ));\r
+      //\r
+      //  Tell the client of the error\r
+      //\r
+      SendError ( pContext,\r
+                  TFTP_ERROR_SEE_MSG,\r
+                  (UINT8 *)"File open mode not valid" );\r
+      break;\r
+    }\r
+    pOption += 1;\r
+    DEBUG (( DEBUG_TFTP_REQUEST,\r
+              "TFTP - Mode: %a\r\n",\r
+              pMode ));\r
 \r
-  @param [in] pTftpServer The TFTP server control structure address.\r
-  @param [in] pContext    The context structure address.\r
+    //\r
+    //  Verify the mode is supported\r
+    //\r
+    pReadMode = "r";\r
+    if ( 0 == strcasecmp ((char *)pMode, "octet" )) {\r
+      //\r
+      //  Read the file as binary input\r
+      //\r
+      pReadMode = "rb";\r
+    }\r
 \r
-  @retval EFI_SUCCESS   Message processed successfully\r
+    //\r
+    //  Determine the file length\r
+    //\r
+    pContext->File = fopen ( pFileName, pReadMode );\r
+    if (( NULL == pContext->File )\r
+        || ( -1 == stat ( pFileName, &FileStatus ))) {\r
+      //\r
+      //  File not found\r
+      //\r
+      DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,\r
+                ( NULL == pContext->File )\r
+                ? "ERROR - File not found!\r\n"\r
+                : "ERROR - Unable to determine file %a size!\r\n",\r
+                pFileName ));\r
 \r
-**/\r
-EFI_STATUS\r
-TftpSendNextBlock (\r
-  IN TSDT_TFTP_SERVER * pTftpServer,\r
-  IN TSDT_CONNECTION_CONTEXT * pContext\r
-  )\r
-{\r
-  ssize_t LengthInBytes;\r
-  UINT8 * pBuffer;\r
-  EFI_STATUS Status;\r
+      //\r
+      //  Tell the client of the error\r
+      //\r
+      SendError ( pContext,\r
+                  TFTP_ERROR_NOT_FOUND,\r
+                  (UINT8 *)"File not found" );\r
+      break;\r
+    }\r
+    pContext->LengthInBytes = FileStatus.st_size;\r
+    pContext->BytesRemaining = pContext->LengthInBytes;\r
+    pContext->BytesToSend = pContext->LengthInBytes;\r
 \r
-  //\r
-  //  Determine how much data needs to be sent\r
-  //\r
-  LengthInBytes = pContext->BlockSize;\r
-  if (( pContext->LengthInBytes < TFTP_MAX_BLOCK_SIZE )\r
-    || ( LengthInBytes > (ssize_t)pContext->LengthInBytes )) {\r
-    LengthInBytes = (ssize_t)pContext->LengthInBytes;\r
-    pContext->bEofSent = TRUE;\r
-  }\r
+    //\r
+    //  Display the file size\r
+    //\r
+    DEBUG_CODE_BEGIN ( );\r
+    UINT32 Value;\r
+\r
+    if ( 1024 > pContext->LengthInBytes ) {\r
+      Value = (UINT32)pContext->LengthInBytes;\r
+      DEBUG (( DEBUG_FILE_BUFFER,\r
+                "%a size: %d Bytes\r\n",\r
+                pFileName,\r
+                Value ));\r
+    }\r
+    else if (( 1024 * 1024 ) > pContext->LengthInBytes ) {\r
+      Value = (UINT32)pContext->LengthInBytes;\r
+      DEBUG (( DEBUG_FILE_BUFFER,\r
+                "%a size: %d.%03d KiBytes (%Ld Bytes)\r\n",\r
+                pFileName,\r
+                Value / 1024,\r
+                (( Value % 1024 ) * 1000 ) / 1024,\r
+                pContext->LengthInBytes ));\r
+    }\r
+    else if (( 1024 * 1024 * 1024 ) > pContext->LengthInBytes ) {\r
+      Value = (UINT32)DivU64x32 ( pContext->LengthInBytes, 1024 );\r
+      DEBUG (( DEBUG_FILE_BUFFER,\r
+                "%a size: %d.%03d MiBytes (%Ld Bytes)\r\n",\r
+                pFileName,\r
+                Value / 1024,\r
+                (( Value % 1024 ) * 1000 ) / 1024,\r
+                pContext->LengthInBytes ));\r
+    }\r
+    else {\r
+      Value = (UINT32)DivU64x32 ( pContext->LengthInBytes, 1024 * 1024 );\r
+      DEBUG (( DEBUG_FILE_BUFFER,\r
+                "%a size: %d.%03d GiBytes (%Ld Bytes)\r\n",\r
+                pFileName,\r
+                Value / 1024,\r
+                (( Value % 1024 ) * 1000 ) / 1024,\r
+                pContext->LengthInBytes ));\r
+    }\r
+    DEBUG_CODE_END ( );\r
 \r
-  //\r
-  //  Set the TFTP opcode and block number\r
-  //\r
-  pBuffer = &pContext->TxBuffer[0];\r
-  *pBuffer++ = 0;\r
-  *pBuffer++ = TFTP_OP_DATA;\r
-  *pBuffer++ = (UINT8)( pContext->AckNext >> 8 );\r
-  *pBuffer++ = (UINT8)pContext->AckNext;\r
+    //\r
+    //  Process the options\r
+    //\r
+    if ( pEnd > pOption ) {\r
+      TftpOptions ( pContext, pOption, pEnd );\r
+    }\r
+    else {\r
+      //\r
+      //  Skip the open ACK\r
+      //\r
+      pContext->BlockNumber = 1;\r
+    }\r
 \r
-  //\r
-  //  Copy the file data into the transmit buffer\r
-  //\r
-  pContext->TxBytes = 2 + 2 + LengthInBytes;\r
-  if ( 0 < LengthInBytes ) {\r
-    CopyMem ( &pBuffer,\r
-              pContext->pBuffer,\r
-              LengthInBytes );\r
+    //\r
+    //  Send the first packet (OACK or data block)\r
+    //\r
+    bCloseContext = PacketFill ( pContext );\r
+    break;\r
   }\r
 \r
   //\r
-  //  Send the next block\r
-  //\r
-  Status = TftpTxPacket ( pTftpServer, pContext );\r
-\r
-  //\r
-  //  Return the operation status\r
+  //  Return the close status\r
   //\r
-  return Status;\r
+  DBG_EXIT ( );\r
+  return bCloseContext;\r
 }\r
 \r
 \r
@@ -1215,242 +1883,314 @@ TftpSendNextBlock (
   some time to get the IP address and initialize the upper layers of\r
   the network stack.\r
 \r
-  @param [in] pTftpServer  The TFTP server control structure address.\r
+  @param [in] pTftpServer   Address of the ::TSDT_TFTP_SERVER structure\r
+  @param [in] AddressFamily The address family to use for the conection.\r
+  @param [in] pIndex        Address of the index into the port array\r
 \r
 **/\r
 VOID\r
-TftpServerTimer (\r
-  IN TSDT_TFTP_SERVER * pTftpServer\r
+TftpServerSocket (\r
+  IN TSDT_TFTP_SERVER * pTftpServer,\r
+  IN sa_family_t AddressFamily,\r
+  IN int * pIndex\r
   )\r
 {\r
-  UINT16 TftpPort;\r
   int SocketStatus;\r
-  EFI_STATUS Status;\r
+  struct pollfd * pTftpPort;\r
+  UINT16 TftpPort;\r
+  union {\r
+    struct sockaddr_in v4;\r
+    struct sockaddr_in6 v6;\r
+  } TftpServerAddress;\r
 \r
-  DEBUG (( DEBUG_SERVER_TIMER, "Entering TftpServerTimer\r\n" ));\r
+  DEBUG (( DEBUG_SERVER_TIMER, "Entering TftpServerListen\r\n" ));\r
 \r
   //\r
-  //  Open the TFTP port on the server\r
+  //  Determine if the socket is already initialized\r
   //\r
-  do {\r
-    do {\r
-      //\r
-      //  Wait for a while\r
-      //\r
-      Status = gBS->CheckEvent ( pTftpServer->TimerEvent );\r
-    } while ( EFI_SUCCESS != Status );\r
-\r
+  if ( -1 == *pIndex ) {\r
     //\r
     //  Attempt to create the socket for the TFTP server\r
     //\r
-    pTftpServer->TftpPort.events = POLLRDNORM | POLLHUP;\r
-    pTftpServer->TftpPort.revents = 0;\r
-    pTftpServer->TftpPort.fd = socket ( AF_INET,\r
-                                        SOCK_DGRAM,\r
-                                        IPPROTO_UDP );\r
-    if ( -1 != pTftpServer->TftpPort.fd )\r
-    {\r
+    pTftpPort = &pTftpServer->TftpPort[ pTftpServer->Entries ];\r
+    pTftpPort->fd = socket ( AddressFamily,\r
+                             SOCK_DGRAM,\r
+                             IPPROTO_UDP );\r
+    if ( -1 != pTftpPort->fd ) {\r
+      //\r
+      //  Initialize the poll structure\r
+      //\r
+      pTftpPort->events = POLLRDNORM | POLLHUP;\r
+      pTftpPort->revents = 0;\r
+\r
       //\r
       //  Set the socket address\r
       //\r
-      ZeroMem ( &pTftpServer->TftpServerAddress,\r
-                sizeof ( pTftpServer->TftpServerAddress ));\r
       TftpPort = 69;\r
-      DEBUG (( DEBUG_TFTP_PORT,\r
-                "TFTP Port: %d\r\n",\r
-                TftpPort ));\r
-      pTftpServer->TftpServerAddress.sin_len = sizeof ( pTftpServer->TftpServerAddress );\r
-      pTftpServer->TftpServerAddress.sin_family = AF_INET;\r
-      pTftpServer->TftpServerAddress.sin_addr.s_addr = INADDR_ANY;\r
-      pTftpServer->TftpServerAddress.sin_port = htons ( TftpPort );\r
+      ZeroMem ( &TftpServerAddress, sizeof ( TftpServerAddress ));\r
+      TftpServerAddress.v4.sin_port = htons ( TftpPort );\r
+      if ( AF_INET == AddressFamily ) {\r
+        TftpServerAddress.v4.sin_len = sizeof ( TftpServerAddress.v4 );\r
+        TftpServerAddress.v4.sin_family = AF_INET;\r
+      }\r
+      else {\r
+        TftpServerAddress.v6.sin6_len = sizeof ( TftpServerAddress.v6 );\r
+        TftpServerAddress.v6.sin6_family = AF_INET6;\r
+      }\r
 \r
       //\r
       //  Bind the socket to the TFTP port\r
       //\r
-      SocketStatus = bind ( pTftpServer->TftpPort.fd,\r
-                            (struct sockaddr *) &pTftpServer->TftpServerAddress,\r
-                            pTftpServer->TftpServerAddress.sin_len );\r
+      SocketStatus = bind ( pTftpPort->fd,\r
+                            (struct sockaddr *) &TftpServerAddress,\r
+                            TftpServerAddress.v6.sin6_len );\r
       if ( -1 != SocketStatus ) {\r
         DEBUG (( DEBUG_TFTP_PORT,\r
                   "0x%08x: Socket bound to port %d\r\n",\r
-                  pTftpServer->TftpPort.fd,\r
+                  pTftpPort->fd,\r
                   TftpPort ));\r
+\r
+        //\r
+        //  Account for this connection\r
+        //\r
+        *pIndex = pTftpServer->Entries;\r
+        pTftpServer->Entries += 1;\r
+        ASSERT ( DIM ( pTftpServer->TftpPort ) >= pTftpServer->Entries );\r
       }\r
 \r
       //\r
       //  Release the socket if necessary\r
       //\r
       if ( -1 == SocketStatus ) {\r
-        close ( pTftpServer->TftpPort.fd );\r
-        pTftpServer->TftpPort.fd = -1;\r
+        close ( pTftpPort->fd );\r
+        pTftpPort->fd = -1;\r
       }\r
     }\r
+  }\r
 \r
-    //\r
-    //  Wait until the socket is open\r
-    //\r
-  }while ( -1 == pTftpServer->TftpPort.fd );\r
-\r
-  DEBUG (( DEBUG_SERVER_TIMER, "Exiting TftpServerTimer\r\n" ));\r
+  DEBUG (( DEBUG_SERVER_TIMER, "Exiting TftpServerListen\r\n" ));\r
 }\r
 \r
 \r
 /**\r
-  Start the TFTP server port creation timer\r
+  Update the window due to the ACK\r
 \r
-  @param [in] pTftpServer The TFTP server control structure address.\r
-\r
-  @retval EFI_SUCCESS         The timer was successfully started.\r
-  @retval EFI_ALREADY_STARTED The timer is already running.\r
-  @retval Other               The timer failed to start.\r
+  @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure\r
+  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure\r
+  @param [in] pPacket     Address of a ::TFTP_PACKET structure\r
 \r
 **/\r
-EFI_STATUS\r
-TftpServerTimerStart (\r
-  IN TSDT_TFTP_SERVER * pTftpServer\r
+VOID\r
+WindowAck (\r
+  IN TSDT_TFTP_SERVER * pTftpServer,\r
+  IN TSDT_CONNECTION_CONTEXT * pContext,\r
+  IN TFTP_PACKET * pPacket\r
   )\r
 {\r
-  EFI_STATUS Status;\r
-  UINT64 TriggerTime;\r
+  if ( PcdGetBool ( Tftp_HighSpeed )) {\r
+    UINT64 DeltaTime;\r
+    UINT64 NanoSeconds;\r
 \r
-  DBG_ENTER ( );\r
+    DBG_ENTER ( );\r
 \r
-  //\r
-  //  Assume the timer is already running\r
-  //\r
-  Status = EFI_ALREADY_STARTED;\r
-  if ( !pTftpServer->bTimerRunning ) {\r
     //\r
-    //  Compute the poll interval\r
+    //  Compute the round trip time\r
     //\r
-    TriggerTime = TFTP_PORT_POLL_DELAY * ( 1000 * 10 );\r
-    Status = gBS->SetTimer ( pTftpServer->TimerEvent,\r
-                             TimerPeriodic,\r
-                             TriggerTime );\r
-    if ( !EFI_ERROR ( Status )) {\r
-      DEBUG (( DEBUG_TFTP_PORT, "TFTP port timer started\r\n" ));\r
-\r
-      //\r
-      //  Mark the timer running\r
-      //\r
-      pTftpServer->bTimerRunning = TRUE;\r
+    if ( pTftpServer->Time2 > pTftpServer->Time1 ) {\r
+      DeltaTime = pTftpServer->RxTime - pPacket->TxTime;\r
     }\r
     else {\r
-      DEBUG (( DEBUG_ERROR | DEBUG_TFTP_PORT,\r
-                "ERROR - Failed to start TFTP port timer, Status: %r\r\n",\r
-                Status ));\r
+      DeltaTime = pPacket->TxTime - pTftpServer->RxTime;\r
     }\r
-  }\r
 \r
-  //\r
-  //  Return the operation status\r
-  //\r
-  DBG_EXIT_STATUS ( Status );\r
-  return Status;\r
+    //\r
+    //  Adjust the round trip time\r
+    //\r
+    NanoSeconds = GetTimeInNanoSecond ( DeltaTime );\r
+    DeltaTime = RShiftU64 ( pContext->Rtt2x, ACK_SHIFT );\r
+    pContext->Rtt2x += NanoSeconds + NanoSeconds - DeltaTime;\r
+    if ( pContext->Rtt2x > pContext->MaxTimeout ) {\r
+      pContext->Rtt2x = pContext->MaxTimeout;\r
+    }\r
+\r
+    //\r
+    //  Account for the ACK\r
+    //\r
+    if ( pContext->WindowSize < MAX_PACKETS ) {\r
+      pContext->AckCount -= 1;\r
+      if ( 0 == pContext->AckCount ) {\r
+        //\r
+        //  Increase the window\r
+        //\r
+        pContext->WindowSize += 1;\r
+\r
+        //\r
+        //  Set the ACK count\r
+        //\r
+        if ( pContext->WindowSize < pContext->Threshold ) {\r
+          pContext->AckCount = pContext->WindowSize * PcdGet32 ( Tftp_AckMultiplier );\r
+        }\r
+        else {\r
+          pContext->AckCount = PcdGet32 ( Tftp_AckLogBase ) << pContext->WindowSize;\r
+        }\r
+\r
+        //\r
+        //  Display the round trip time\r
+        //\r
+        DEBUG_CODE_BEGIN ( );\r
+        UINT32 Value;\r
+        \r
+        DeltaTime = RShiftU64 ( pContext->Rtt2x, 1 );\r
+        if ( 1000 > DeltaTime ) {\r
+          DEBUG (( DEBUG_WINDOW,\r
+                    "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %Ld nSec\r\n",\r
+                    pContext->WindowSize,\r
+                    pContext->Threshold,\r
+                    pContext->AckCount,\r
+                    DeltaTime ));\r
+        }\r
+        else if (( 1000 * 1000 ) > DeltaTime ) {\r
+          Value = (UINT32)DeltaTime;\r
+          DEBUG (( DEBUG_WINDOW,\r
+                    "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d uSec\r\n",\r
+                    pContext->WindowSize,\r
+                    pContext->Threshold,\r
+                    pContext->AckCount,\r
+                    Value / 1000,\r
+                    Value % 1000 ));\r
+        }\r
+        else if (( 1000 * 1000 * 1000 ) > DeltaTime ) {\r
+          Value = (UINT32)DivU64x32 ( DeltaTime, 1000 );\r
+          DEBUG (( DEBUG_WINDOW,\r
+                    "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d mSec\r\n",\r
+                    pContext->WindowSize,\r
+                    pContext->Threshold,\r
+                    pContext->AckCount,\r
+                    Value / 1000,\r
+                    Value % 1000 ));\r
+        }\r
+        else {\r
+          Value = (UINT32)DivU64x32 ( DeltaTime, 1000 * 1000 );\r
+          DEBUG (( DEBUG_WINDOW,\r
+                    "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d Sec\r\n",\r
+                    pContext->WindowSize,\r
+                    pContext->Threshold,\r
+                    pContext->AckCount,\r
+                    Value / 1000,\r
+                    Value % 1000 ));\r
+        }\r
+        DEBUG_CODE_END ( );\r
+      }\r
+    }\r
+\r
+    DBG_EXIT ( );\r
+  }\r
 }\r
 \r
 \r
 /**\r
-  Stop the TFTP server port creation timer\r
-\r
-  @param [in] pTftpServer The TFTP server control structure address.\r
+  A timeout has occurred, close the window\r
 \r
-  @retval EFI_SUCCESS   The TFTP port timer is stopped\r
-  @retval Other         Failed to stop the TFTP port timer\r
+  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure\r
 \r
 **/\r
-EFI_STATUS\r
-TftpServerTimerStop (\r
-  IN TSDT_TFTP_SERVER * pTftpServer\r
+VOID\r
+WindowTimeout (\r
+  IN TSDT_CONNECTION_CONTEXT * pContext\r
   )\r
 {\r
-  EFI_STATUS Status;\r
+  if ( PcdGetBool ( Tftp_HighSpeed )) {\r
+    TFTP_PACKET * pPacket;\r
 \r
-  DBG_ENTER ( );\r
+    DBG_ENTER ( );\r
 \r
-  //\r
-  //  Assume the timer is stopped\r
-  //\r
-  Status = EFI_SUCCESS;\r
-  if ( pTftpServer->bTimerRunning ) {\r
     //\r
-    //  Stop the port creation polling\r
+    //  Set the threshold at half the previous window size\r
     //\r
-    Status = gBS->SetTimer ( pTftpServer->TimerEvent,\r
-                             TimerCancel,\r
-                             0 );\r
-    if ( !EFI_ERROR ( Status )) {\r
-      DEBUG (( DEBUG_TFTP_PORT, "TFT[ port timer stopped\r\n" ));\r
+    pContext->Threshold = ( pContext->WindowSize + 1 ) >> 1;\r
 \r
-      //\r
-      //  Mark the timer stopped\r
-      //\r
-      pTftpServer->bTimerRunning = FALSE;\r
+    //\r
+    //  Close the transmit window\r
+    //\r
+    pContext->WindowSize = 1;\r
+    pContext->PacketsInWindow = 0;\r
+\r
+    //\r
+    //  Double the round trip time\r
+    //\r
+    pContext->Rtt2x = LShiftU64 ( pContext->Rtt2x, 1 );\r
+    if ( pContext->Rtt2x > pContext->MaxTimeout ) {\r
+      pContext->Rtt2x = pContext->MaxTimeout;\r
+    }\r
+\r
+    //\r
+    //  Set the ACK count\r
+    //\r
+    if ( pContext->WindowSize < pContext->Threshold ) {\r
+      pContext->AckCount = pContext->WindowSize * PcdGet32 ( Tftp_AckMultiplier );\r
     }\r
     else {\r
-      DEBUG (( DEBUG_ERROR | DEBUG_TFTP_PORT,\r
-                "ERROR - Failed to stop TFT[ port timer, Status: %r\r\n",\r
-                Status ));\r
+      pContext->AckCount = PcdGet32 ( Tftp_AckLogBase ) << pContext->WindowSize;\r
     }\r
-  }\r
-\r
-  //\r
-  //  Return the operation status\r
-  //\r
-  DBG_EXIT_STATUS ( Status );\r
-  return Status;\r
-}\r
-\r
-/**\r
-  Send the next TFTP packet\r
-\r
-  @param [in] pTftpServer   The TFTP server control structure address.\r
-  @param [in] pContext      The context structure address.\r
-\r
-  @retval EFI_SUCCESS   Message processed successfully\r
-\r
-**/\r
-EFI_STATUS\r
-TftpTxPacket (\r
-  IN TSDT_TFTP_SERVER * pTftpServer,\r
-  IN TSDT_CONNECTION_CONTEXT * pContext\r
-  )\r
-{\r
-  ssize_t LengthInBytes;\r
-  EFI_STATUS Status;\r
-\r
-  DBG_ENTER ( );\r
 \r
-  //\r
-  //  Assume success\r
-  //\r
-  Status = EFI_SUCCESS;\r
+    //\r
+    //  Display the round trip time\r
+    //\r
+    DEBUG_CODE_BEGIN ( );\r
+    UINT64 DeltaTime;\r
+    UINT32 Value;\r
+    \r
+    DeltaTime = RShiftU64 ( pContext->Rtt2x, 1 );\r
+    if ( 1000 > DeltaTime ) {\r
+      DEBUG (( DEBUG_WINDOW,\r
+                "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %Ld nSec\r\n",\r
+                pContext->WindowSize,\r
+                pContext->Threshold,\r
+                pContext->AckCount,\r
+                DeltaTime ));\r
+    }\r
+    else if (( 1000 * 1000 ) > DeltaTime ) {\r
+      Value = (UINT32)DeltaTime;\r
+      DEBUG (( DEBUG_WINDOW,\r
+                "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d uSec\r\n",\r
+                pContext->WindowSize,\r
+                pContext->Threshold,\r
+                pContext->AckCount,\r
+                Value / 1000,\r
+                Value % 1000 ));\r
+    }\r
+    else if (( 1000 * 1000 * 1000 ) > DeltaTime ) {\r
+      Value = (UINT32)DivU64x32 ( DeltaTime, 1000 );\r
+      DEBUG (( DEBUG_WINDOW,\r
+                "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d mSec\r\n",\r
+                pContext->WindowSize,\r
+                pContext->Threshold,\r
+                pContext->AckCount,\r
+                Value / 1000,\r
+                Value % 1000 ));\r
+    }\r
+    else {\r
+      Value = (UINT32)DivU64x32 ( DeltaTime, 1000 * 1000 );\r
+      DEBUG (( DEBUG_WINDOW,\r
+                "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d Sec\r\n",\r
+                pContext->WindowSize,\r
+                pContext->Threshold,\r
+                pContext->AckCount,\r
+                Value / 1000,\r
+                Value % 1000 ));\r
+    }\r
+    DEBUG_CODE_END ( );\r
 \r
-  //\r
-  //  Send the TFTP packet\r
-  //\r
-  DEBUG (( DEBUG_TX,\r
-            "0x%08x: pContext sending 0x%08x bytes\r\n",\r
-            pContext,\r
-            pContext->TxBytes ));\r
-  LengthInBytes = sendto ( pTftpServer->TftpPort.fd,\r
-                           &pContext->TxBuffer[0],\r
-                           pContext->TxBytes,\r
-                           0,\r
-                           (struct sockaddr *)&pContext->RemoteAddress,\r
-                           pContext->RemoteAddress.sin_len );\r
-  if ( -1 == LengthInBytes ) {\r
-    DEBUG (( DEBUG_ERROR | DEBUG_TX,\r
-              "ERROR - Transmit failure, errno: 0x%08x\r\n",\r
-              errno ));\r
-    Status = EFI_DEVICE_ERROR;\r
+    //\r
+    //  Retransmit the first packet in the window\r
+    //\r
+    pPacket = pContext->pTxHead;\r
+    if ( NULL != pPacket ) {\r
+      PacketTx ( pContext, pPacket );\r
+    }\r
+    \r
+    DBG_EXIT ( );\r
   }\r
-\r
-  //\r
-  //  Return the operation status\r
-  //\r
-  DBG_EXIT_STATUS ( Status );\r
-  return Status;\r
 }\r
 \r
 \r
@@ -1469,25 +2209,46 @@ main (
   IN char **Argv\r
   )\r
 {\r
+  UINTN Index;\r
   TSDT_TFTP_SERVER * pTftpServer;\r
   EFI_STATUS Status;\r
+  UINT64 TriggerTime;\r
 \r
   //\r
-  //  Create a timer event to start TFTP port\r
+  //  Get the performance counter characteristics\r
   //\r
   pTftpServer = &mTftpServer;\r
+  if ( PcdGetBool ( Tftp_HighSpeed )\r
+    || PcdGetBool ( Tftp_Bandwidth )) {\r
+    pTftpServer->ClockFrequency = GetPerformanceCounterProperties ( &pTftpServer->Time1,\r
+                                                                  &pTftpServer->Time2 );\r
+  }\r
+\r
+  //\r
+  //  Create a timer event to start TFTP port\r
+  //\r
   Status = gBS->CreateEvent ( EVT_TIMER,\r
                               TPL_TFTP_SERVER,\r
                               NULL,\r
                               NULL,\r
                               &pTftpServer->TimerEvent );\r
   if ( !EFI_ERROR ( Status )) {\r
-    Status = TftpServerTimerStart ( pTftpServer );\r
+    //\r
+    //  Compute the poll interval\r
+    //\r
+    TriggerTime = TFTP_PORT_POLL_DELAY * ( 1000 * 10 );\r
+    Status = gBS->SetTimer ( pTftpServer->TimerEvent,\r
+                             TimerPeriodic,\r
+                             TriggerTime );\r
     if ( !EFI_ERROR ( Status )) {\r
+      DEBUG (( DEBUG_TFTP_PORT, "TFTP port timer started\r\n" ));\r
+\r
       //\r
       //  Run the TFTP server forever\r
       //\r
-      for ( ; ; ) {\r
+      pTftpServer->Udpv4Index = -1;\r
+      pTftpServer->Udpv6Index = -1;\r
+      do {\r
         //\r
         //  Poll the network layer to create the TFTP port\r
         //  for the tftp server.  More than one attempt may\r
@@ -1495,28 +2256,107 @@ main (
         //  the IP address and initialize the upper layers\r
         //  of the network stack.\r
         //\r
-        TftpServerTimer ( pTftpServer );\r
+        if ( DIM ( pTftpServer->TftpPort ) != pTftpServer->Entries ) {\r
+          do {\r
+            //\r
+            //  Wait a while before polling for a connection\r
+            //\r
+            if ( EFI_SUCCESS != gBS->CheckEvent ( pTftpServer->TimerEvent )) {\r
+              if ( 0 == pTftpServer->Entries ) {\r
+                break;\r
+              }\r
+              gBS->WaitForEvent ( 1, &pTftpServer->TimerEvent, &Index );\r
+            }\r
+\r
+            //\r
+            //  Poll for a network connection\r
+            //\r
+            TftpServerSocket ( pTftpServer,\r
+                               AF_INET,\r
+                               &pTftpServer->Udpv4Index );\r
+            TftpServerSocket ( pTftpServer,\r
+                               AF_INET6,\r
+                               &pTftpServer->Udpv6Index );\r
+          } while ( 0 == pTftpServer->Entries );\r
+        }\r
 \r
         //\r
         //  Poll the socket for activity\r
         //\r
         do {\r
           SocketPoll ( pTftpServer );\r
-        } while ( -1 != pTftpServer->TftpPort.fd );\r
 \r
-//\r
-// TODO: Remove the following test code\r
-//  Exit when the network connection is broken\r
-//\r
-break;\r
-      }\r
+          //\r
+          //  Normal TFTP lets the client request the retransmit by\r
+          //  sending another ACK for the previous packet\r
+          //\r
+          if ( PcdGetBool ( Tftp_HighSpeed )) {\r
+            UINT64 CurrentTime;\r
+            UINT64 ElapsedTime;\r
+            TSDT_CONNECTION_CONTEXT * pContext;\r
+            TFTP_PACKET * pPacket;\r
+\r
+            //\r
+            //  High speed TFTP uses an agressive retransmit to\r
+            //  get the TFTP client moving again when the ACK or\r
+            //  previous data packet was lost.\r
+            //\r
+            //  Get the current time\r
+            //\r
+            CurrentTime = GetPerformanceCounter ( );\r
+\r
+            //\r
+            //  Walk the list of contexts\r
+            //\r
+            pContext = pTftpServer->pContextList;\r
+            while ( NULL != pContext )\r
+            {\r
+              //\r
+              //  Check for a transmit timeout\r
+              //\r
+              pPacket = pContext->pTxHead;\r
+              if ( NULL != pPacket ) {\r
+                //\r
+                //  Compute the elapsed time\r
+                //\r
+                if ( pTftpServer->Time2 > pTftpServer->Time1 ) {\r
+                  ElapsedTime = CurrentTime - pPacket->TxTime;\r
+                }\r
+                else {\r
+                  ElapsedTime = pPacket->TxTime - CurrentTime;\r
+                }\r
+                ElapsedTime = GetTimeInNanoSecond ( ElapsedTime );\r
+\r
+                //\r
+                //  Determine if a retransmission is necessary\r
+                //\r
+                if ( ElapsedTime >= pContext->Rtt2x ) {\r
+                  DEBUG (( DEBUG_WINDOW,\r
+                            "0x%08x: Context TX timeout for packet 0x%08x, Window: %d\r\n",\r
+                            pContext,\r
+                            pPacket,\r
+                            pContext->WindowSize ));\r
+                  WindowTimeout ( pContext );\r
+                }\r
+              }\r
+\r
+              //\r
+              //  Set the next context\r
+              //\r
+              pContext = pContext->pNext;\r
+            }\r
+          }\r
+        } while ( DIM ( pTftpServer->TftpPort ) == pTftpServer->Entries );\r
+      } while ( !mbTftpServerExit );\r
 \r
       //\r
       //  Done with the timer event\r
       //\r
-      TftpServerTimerStop ( pTftpServer );\r
-      Status = gBS->CloseEvent ( pTftpServer->TimerEvent );\r
+      gBS->SetTimer ( pTftpServer->TimerEvent,\r
+                      TimerCancel,\r
+                      0 );\r
     }\r
+    gBS->CloseEvent ( pTftpServer->TimerEvent );\r
   }\r
 \r
   //\r