Signed-off-by: lpleahy git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@13007 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			2368 lines
		
	
	
		
			66 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2368 lines
		
	
	
		
			66 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  This is a simple TFTP server application
 | 
						|
 | 
						|
  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 <TftpServer.h>
 | 
						|
 | 
						|
TSDT_TFTP_SERVER mTftpServer;       ///<  TFTP server's control structure
 | 
						|
volatile BOOLEAN mbTftpServerExit;  ///<  Set TRUE to cause TFTP server to exit
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Read file data into a buffer
 | 
						|
 | 
						|
  @param [in] pContext    Connection context structure address
 | 
						|
 | 
						|
  @retval TRUE if a read error occurred
 | 
						|
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
BufferFill (
 | 
						|
  IN TSDT_CONNECTION_CONTEXT * pContext
 | 
						|
  )
 | 
						|
{
 | 
						|
  BOOLEAN bReadError;
 | 
						|
  size_t BytesRead;
 | 
						|
  UINT64 LengthInBytes;
 | 
						|
 | 
						|
  DBG_ENTER ( );
 | 
						|
 | 
						|
  //
 | 
						|
  //  Use break instead of goto
 | 
						|
  //
 | 
						|
  bReadError = FALSE;
 | 
						|
  for ( ; ; ) {
 | 
						|
    //
 | 
						|
    //  Determine if there is any work to do
 | 
						|
    //
 | 
						|
    LengthInBytes = DIM ( pContext->FileData ) >> 1;
 | 
						|
    if (( pContext->ValidBytes > LengthInBytes )
 | 
						|
      || ( 0 == pContext->BytesRemaining )) {
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Determine the number of bytes to read
 | 
						|
    //
 | 
						|
    if ( LengthInBytes > pContext->BytesRemaining ) {
 | 
						|
      LengthInBytes = pContext->BytesRemaining;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Read in the next portion of the file
 | 
						|
    //
 | 
						|
    BytesRead = fread ( pContext->pFill,
 | 
						|
                        1,
 | 
						|
                        (size_t)LengthInBytes,
 | 
						|
                        pContext->File );
 | 
						|
    if ( -1 == BytesRead ) {
 | 
						|
      bReadError = TRUE;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Account for the file data read
 | 
						|
    //
 | 
						|
    pContext->BytesRemaining -= BytesRead;
 | 
						|
    pContext->ValidBytes += BytesRead;
 | 
						|
    DEBUG (( DEBUG_FILE_BUFFER,
 | 
						|
              "0x%08x: Buffer filled with %Ld bytes, %Ld bytes ramaining\r\n",
 | 
						|
              pContext->pFill,
 | 
						|
              BytesRead,
 | 
						|
              pContext->BytesRemaining ));
 | 
						|
 | 
						|
    //
 | 
						|
    //  Set the next buffer location
 | 
						|
    //
 | 
						|
    pContext->pFill += BytesRead;
 | 
						|
    if ( pContext->pEnd <= pContext->pFill ) {
 | 
						|
      pContext->pFill = &pContext->FileData[ 0 ];
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Verify that the end of the buffer is reached
 | 
						|
    //
 | 
						|
    ASSERT ( 0 == ( DIM ( pContext->FileData ) & 1 ));
 | 
						|
    break;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //  Return the read status
 | 
						|
  //
 | 
						|
  DBG_EXIT ( );
 | 
						|
  return bReadError;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Add a connection context to the list of connection contexts.
 | 
						|
 | 
						|
  @param [in] pTftpServer   Address of the ::TSDT_TFTP_SERVER structure
 | 
						|
  @param [in] SocketFd      Socket file descriptor
 | 
						|
 | 
						|
  @retval Context structure address, NULL if allocation fails
 | 
						|
 | 
						|
**/
 | 
						|
TSDT_CONNECTION_CONTEXT *
 | 
						|
ContextAdd (
 | 
						|
  IN TSDT_TFTP_SERVER * pTftpServer,
 | 
						|
  IN int SocketFd
 | 
						|
  )
 | 
						|
{
 | 
						|
  TSDT_CONNECTION_CONTEXT * pContext;
 | 
						|
  TFTP_PACKET * pEnd;
 | 
						|
  TFTP_PACKET * pPacket;
 | 
						|
 | 
						|
  DBG_ENTER ( );
 | 
						|
 | 
						|
  //
 | 
						|
  //  Allocate a new context
 | 
						|
  //
 | 
						|
  pContext = (TSDT_CONNECTION_CONTEXT *)AllocateZeroPool ( sizeof ( *pContext ));
 | 
						|
  if ( NULL != pContext ) {
 | 
						|
    //
 | 
						|
    //  Initialize the context
 | 
						|
    //
 | 
						|
    pContext->SocketFd = SocketFd;
 | 
						|
    CopyMem ( &pContext->RemoteAddress,
 | 
						|
              &pTftpServer->RemoteAddress,
 | 
						|
              sizeof ( pContext->RemoteAddress ));
 | 
						|
    pContext->BlockSize = 512;
 | 
						|
 | 
						|
    //
 | 
						|
    //  Buffer management
 | 
						|
    //
 | 
						|
    pContext->pFill = &pContext->FileData[ 0 ];
 | 
						|
    pContext->pEnd = &pContext->FileData[ sizeof ( pContext->FileData )];
 | 
						|
    pContext->pBuffer = pContext->pFill;
 | 
						|
 | 
						|
    //
 | 
						|
    //  Window management
 | 
						|
    //
 | 
						|
    pContext->MaxTimeout = MultU64x32 ( PcdGet32 ( Tftp_MaxTimeoutInSec ),
 | 
						|
                                        2 * 1000 * 1000 * 1000 );
 | 
						|
    pContext->Rtt2x = pContext->MaxTimeout;
 | 
						|
    pContext->WindowSize = MAX_PACKETS;
 | 
						|
    WindowTimeout ( pContext );
 | 
						|
 | 
						|
    //
 | 
						|
    //  Place the packets on the free list
 | 
						|
    //
 | 
						|
    pPacket = &pContext->Tx[ 0 ];
 | 
						|
    pEnd = &pPacket[ DIM ( pContext->Tx )];
 | 
						|
    while ( pEnd > pPacket ) {
 | 
						|
      PacketFree ( pContext, pPacket );
 | 
						|
      pPacket += 1;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Display the new context
 | 
						|
    //
 | 
						|
    if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {
 | 
						|
      DEBUG (( DEBUG_PORT_WORK,
 | 
						|
                "0x%08x: Context for %d.%d.%d.%d:%d\r\n",
 | 
						|
                pContext,
 | 
						|
                (UINT8)pTftpServer->RemoteAddress.v4.sin_addr.s_addr,
 | 
						|
                (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ),
 | 
						|
                (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ),
 | 
						|
                (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ),
 | 
						|
                htons ( pTftpServer->RemoteAddress.v4.sin_port )));
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      DEBUG (( DEBUG_PORT_WORK,
 | 
						|
                "0x%08x: Context for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",
 | 
						|
                pContext,
 | 
						|
                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],
 | 
						|
                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],
 | 
						|
                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],
 | 
						|
                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],
 | 
						|
                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],
 | 
						|
                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],
 | 
						|
                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],
 | 
						|
                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],
 | 
						|
                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],
 | 
						|
                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],
 | 
						|
                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],
 | 
						|
                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],
 | 
						|
                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],
 | 
						|
                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],
 | 
						|
                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],
 | 
						|
                pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],
 | 
						|
                htons ( pTftpServer->RemoteAddress.v6.sin6_port )));
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Add the context to the context list
 | 
						|
    //
 | 
						|
    pContext->pNext = pTftpServer->pContextList;
 | 
						|
    pTftpServer->pContextList = pContext;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //  Return the connection context
 | 
						|
  //
 | 
						|
  DBG_EXIT_STATUS ( pContext );
 | 
						|
  return pContext;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Locate a remote connection context.
 | 
						|
 | 
						|
  @param [in] pTftpServer   Address of the ::TSDT_TFTP_SERVER structure
 | 
						|
  @param [in] pIpAddress    The start of the remote IP address in network order
 | 
						|
  @param [in] Port          The remote port number
 | 
						|
 | 
						|
  @retval Context structure address, NULL if not found
 | 
						|
 | 
						|
**/
 | 
						|
TSDT_CONNECTION_CONTEXT *
 | 
						|
ContextFind (
 | 
						|
  IN TSDT_TFTP_SERVER * pTftpServer
 | 
						|
  )
 | 
						|
{
 | 
						|
  TSDT_CONNECTION_CONTEXT * pContext;
 | 
						|
 | 
						|
  DBG_ENTER ( );
 | 
						|
 | 
						|
  //
 | 
						|
  //  Walk the list of connection contexts
 | 
						|
  //
 | 
						|
  pContext = pTftpServer->pContextList;
 | 
						|
  while ( NULL != pContext ) {
 | 
						|
    //
 | 
						|
    //  Attempt to locate the remote network connection
 | 
						|
    //
 | 
						|
    if ( 0 == memcmp ( &pTftpServer->RemoteAddress,
 | 
						|
                       &pContext->RemoteAddress,
 | 
						|
                       pTftpServer->RemoteAddress.v6.sin6_len )) {
 | 
						|
      //
 | 
						|
      //  The connection was found
 | 
						|
      //
 | 
						|
      DEBUG (( DEBUG_TFTP_REQUEST,
 | 
						|
                "0x%08x: pContext found\r\n",
 | 
						|
                pContext ));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Set the next context
 | 
						|
    //
 | 
						|
    pContext = pContext->pNext;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //  Return the connection context structure address
 | 
						|
  //
 | 
						|
  DBG_EXIT_HEX ( pContext );
 | 
						|
  return pContext;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Remove a context from the list.
 | 
						|
 | 
						|
  @param [in] pTftpServer   Address of the ::TSDT_TFTP_SERVER structure
 | 
						|
  @param [in] pContext      Address of a ::TSDT_CONNECTION_CONTEXT structure
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
ContextRemove (
 | 
						|
  IN TSDT_TFTP_SERVER * pTftpServer,
 | 
						|
  IN TSDT_CONNECTION_CONTEXT * pContext
 | 
						|
  )
 | 
						|
{
 | 
						|
  TSDT_CONNECTION_CONTEXT * pNextContext;
 | 
						|
  TSDT_CONNECTION_CONTEXT * pPreviousContext;
 | 
						|
 | 
						|
  DBG_ENTER ( );
 | 
						|
 | 
						|
  //
 | 
						|
  //  Attempt to locate the context in the list
 | 
						|
  //
 | 
						|
  pPreviousContext = NULL;
 | 
						|
  pNextContext = pTftpServer->pContextList;
 | 
						|
  while ( NULL != pNextContext ) {
 | 
						|
    //
 | 
						|
    //  Determine if the context was found
 | 
						|
    //
 | 
						|
    if ( pNextContext == pContext ) {
 | 
						|
      //
 | 
						|
      //  Remove the context from the list
 | 
						|
      //
 | 
						|
      if ( NULL == pPreviousContext ) {
 | 
						|
        pTftpServer->pContextList = pContext->pNext;
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        pPreviousContext->pNext = pContext->pNext;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Set the next context
 | 
						|
    //
 | 
						|
    pPreviousContext = pNextContext;
 | 
						|
    pNextContext = pNextContext->pNext;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //  Determine if the context was found
 | 
						|
  //
 | 
						|
  if ( NULL != pContext ) {
 | 
						|
    //
 | 
						|
    //  Return the resources
 | 
						|
    //
 | 
						|
    gBS->FreePool ( pContext );
 | 
						|
  }
 | 
						|
 | 
						|
  DBG_EXIT ( );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Queue data packets for transmission
 | 
						|
 | 
						|
  @param [in] pContext    Connection context structure address
 | 
						|
 | 
						|
  @retval TRUE if a read error occurred
 | 
						|
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
PacketFill (
 | 
						|
  IN TSDT_CONNECTION_CONTEXT * pContext
 | 
						|
  )
 | 
						|
{
 | 
						|
  BOOLEAN bReadError;
 | 
						|
  UINT64 LengthInBytes;
 | 
						|
  UINT8 * pBuffer;
 | 
						|
  TFTP_PACKET * pPacket;
 | 
						|
 | 
						|
  DBG_ENTER ( );
 | 
						|
 | 
						|
  //
 | 
						|
  //  Use break instead of goto
 | 
						|
  //
 | 
						|
  bReadError = FALSE;
 | 
						|
  for ( ; ; ) {
 | 
						|
    //
 | 
						|
    //  Fill the buffer if necessary
 | 
						|
    //
 | 
						|
    bReadError = BufferFill ( pContext );
 | 
						|
    if ( bReadError ) {
 | 
						|
      //
 | 
						|
      //  File access mode not supported
 | 
						|
      //
 | 
						|
      DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,
 | 
						|
                "ERROR - File read failure!\r\n" ));
 | 
						|
 | 
						|
      //
 | 
						|
      //  Tell the client of the error
 | 
						|
      //
 | 
						|
      SendError ( pContext,
 | 
						|
                  TFTP_ERROR_SEE_MSG,
 | 
						|
                  (UINT8 *)"Read failure" );
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Determine if any packets can be filled
 | 
						|
    //
 | 
						|
    if ( pContext->bEofSent
 | 
						|
      || ( NULL == pContext->pFreeList )) {
 | 
						|
      //
 | 
						|
      //  All of the packets are filled
 | 
						|
      //
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Set the TFTP opcode and block number
 | 
						|
    //
 | 
						|
    pPacket = PacketGet ( pContext );
 | 
						|
    pBuffer = &pPacket->TxBuffer[ 0 ];
 | 
						|
    *pBuffer++ = 0;
 | 
						|
    *pBuffer++ = TFTP_OP_DATA;
 | 
						|
    *pBuffer++ = (UINT8)( pContext->BlockNumber >> 8 );
 | 
						|
    *pBuffer++ = (UINT8)pContext->BlockNumber;
 | 
						|
 | 
						|
    //
 | 
						|
    //  Determine how much data needs to be sent
 | 
						|
    //
 | 
						|
    LengthInBytes = pContext->BlockSize;
 | 
						|
    if (( pContext->BytesToSend < TFTP_MAX_BLOCK_SIZE )
 | 
						|
      && ( LengthInBytes > pContext->BytesToSend )) {
 | 
						|
      LengthInBytes = pContext->BytesToSend;
 | 
						|
      pContext->bEofSent = TRUE;
 | 
						|
    }
 | 
						|
    DEBUG (( DEBUG_TX_PACKET,
 | 
						|
              "0x%08x: Packet, Block %d filled with %d bytes\r\n",
 | 
						|
              pPacket,
 | 
						|
              pContext->BlockNumber,
 | 
						|
              (UINT32)LengthInBytes ));
 | 
						|
    
 | 
						|
    //
 | 
						|
    //  Copy the file data into the packet
 | 
						|
    //
 | 
						|
    pPacket->TxBytes = (ssize_t)( 2 + 2 + LengthInBytes );
 | 
						|
    if ( 0 < LengthInBytes ) {
 | 
						|
      CopyMem ( pBuffer,
 | 
						|
                pContext->pBuffer,
 | 
						|
                (UINTN)LengthInBytes );
 | 
						|
      DEBUG (( DEBUG_FILE_BUFFER,
 | 
						|
                "0x%08x: Buffer consumed %d bytes of file data\r\n",
 | 
						|
                pContext->pBuffer,
 | 
						|
                LengthInBytes ));
 | 
						|
 | 
						|
      //
 | 
						|
      //  Account for the file data consumed
 | 
						|
      //
 | 
						|
      pContext->ValidBytes -= LengthInBytes;
 | 
						|
      pContext->BytesToSend -= LengthInBytes;
 | 
						|
      pContext->pBuffer += LengthInBytes;
 | 
						|
      if ( pContext->pEnd <= pContext->pBuffer ) {
 | 
						|
        pContext->pBuffer = &pContext->FileData[ 0 ];
 | 
						|
      }
 | 
						|
    }
 | 
						|
    
 | 
						|
    //
 | 
						|
    //  Queue the packet for transmission
 | 
						|
    //
 | 
						|
    PacketQueue ( pContext, pPacket );
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //  Return the read status
 | 
						|
  //
 | 
						|
  DBG_EXIT ( );
 | 
						|
  return bReadError;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Free the packet
 | 
						|
 | 
						|
  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure
 | 
						|
  @param [in] pPacket     Address of a ::TFTP_PACKET structure
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
PacketFree(
 | 
						|
  IN TSDT_CONNECTION_CONTEXT * pContext,
 | 
						|
  IN TFTP_PACKET * pPacket
 | 
						|
  )
 | 
						|
{
 | 
						|
  DBG_ENTER ( );
 | 
						|
 | 
						|
  //
 | 
						|
  //  Don't free the error packet
 | 
						|
  //
 | 
						|
  if ( pPacket != &pContext->ErrorPacket ) {
 | 
						|
    //
 | 
						|
    //  Place the packet on the free list
 | 
						|
    //
 | 
						|
    pPacket->pNext = pContext->pFreeList;
 | 
						|
    pContext->pFreeList = pPacket;
 | 
						|
    DEBUG (( DEBUG_TX_PACKET,
 | 
						|
              "0x%08x: Packet queued to free list\r\n",
 | 
						|
              pPacket ));
 | 
						|
  }
 | 
						|
 | 
						|
  DBG_EXIT ( );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Get a packet from the free list for transmission
 | 
						|
 | 
						|
  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure
 | 
						|
 | 
						|
  @retval Address of a ::TFTP_PACKET structure
 | 
						|
 | 
						|
**/
 | 
						|
TFTP_PACKET *
 | 
						|
PacketGet (
 | 
						|
  IN TSDT_CONNECTION_CONTEXT * pContext
 | 
						|
  )
 | 
						|
{
 | 
						|
  TFTP_PACKET * pPacket;
 | 
						|
 | 
						|
  DBG_ENTER ( );
 | 
						|
 | 
						|
  //
 | 
						|
  //  Get the next packet from the free list
 | 
						|
  //
 | 
						|
  pPacket = pContext->pFreeList;
 | 
						|
  if ( NULL != pPacket ) {
 | 
						|
    pContext->pFreeList = pPacket->pNext;
 | 
						|
    pPacket->RetryCount = 0;
 | 
						|
    DEBUG (( DEBUG_TX_PACKET,
 | 
						|
              "0x%08x: Packet removed from free list\r\n",
 | 
						|
              pPacket ));
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //  Return the packet
 | 
						|
  //
 | 
						|
  DBG_EXIT_HEX ( pPacket );
 | 
						|
  return pPacket;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Queue the packet for transmission
 | 
						|
 | 
						|
  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure
 | 
						|
  @param [in] pPacket     Address of a ::TFTP_PACKET structure
 | 
						|
 | 
						|
  @retval TRUE if a transmission error has occurred
 | 
						|
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
PacketQueue (
 | 
						|
  IN TSDT_CONNECTION_CONTEXT * pContext,
 | 
						|
  IN TFTP_PACKET * pPacket
 | 
						|
  )
 | 
						|
{
 | 
						|
  BOOLEAN bTransmitError;
 | 
						|
  TFTP_PACKET * pTail;
 | 
						|
  EFI_STATUS Status;
 | 
						|
 | 
						|
  DBG_ENTER ( );
 | 
						|
 | 
						|
  //
 | 
						|
  //  Account for this data block
 | 
						|
  //
 | 
						|
  pPacket->BlockNumber = pContext->BlockNumber;
 | 
						|
  pContext->BlockNumber += 1;
 | 
						|
 | 
						|
  //
 | 
						|
  //  Queue the packet for transmission
 | 
						|
  //
 | 
						|
  pTail = pContext->pTxTail;
 | 
						|
  if ( NULL == pTail ) {
 | 
						|
    pContext->pTxHead = pPacket;
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    pTail->pNext = pPacket;
 | 
						|
  }
 | 
						|
  pContext->pTxTail = pPacket;
 | 
						|
  pPacket->pNext = NULL;
 | 
						|
  DEBUG (( DEBUG_TX_PACKET,
 | 
						|
            "0x%08x: Packet queued to TX list\r\n",
 | 
						|
            pPacket ));
 | 
						|
 | 
						|
  //
 | 
						|
  //  Start the transmission if necessary
 | 
						|
  //
 | 
						|
  bTransmitError = FALSE;
 | 
						|
  if ( pContext->PacketsInWindow < pContext->WindowSize ) {
 | 
						|
    Status = PacketTx ( pContext, pPacket );
 | 
						|
    bTransmitError = (BOOLEAN)( EFI_ERROR ( Status ));
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //  Return the transmit status
 | 
						|
  //
 | 
						|
  DBG_EXIT_TF ( bTransmitError );
 | 
						|
  return bTransmitError;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Remove a packet from the transmit queue
 | 
						|
 | 
						|
  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure
 | 
						|
 | 
						|
**/
 | 
						|
TFTP_PACKET *
 | 
						|
PacketRemove(
 | 
						|
  IN TSDT_CONNECTION_CONTEXT * pContext
 | 
						|
  )
 | 
						|
{
 | 
						|
  TFTP_PACKET * pNext;
 | 
						|
  TFTP_PACKET * pPacket;
 | 
						|
 | 
						|
  DBG_ENTER ( );
 | 
						|
 | 
						|
  //
 | 
						|
  //  Remove a packet from the transmit queue
 | 
						|
  //
 | 
						|
  //
 | 
						|
  pPacket = pContext->pTxHead;
 | 
						|
  if ( NULL != pPacket ) {
 | 
						|
    pNext = pPacket->pNext;
 | 
						|
    pContext->pTxHead = pNext;
 | 
						|
    if ( NULL == pNext ) {
 | 
						|
      pContext->pTxTail = NULL;
 | 
						|
    }
 | 
						|
    DEBUG (( DEBUG_TX_PACKET,
 | 
						|
              "0x%08x: Packet removed from TX list\r\n",
 | 
						|
              pPacket ));
 | 
						|
 | 
						|
    //
 | 
						|
    //  Remove this packet from the window
 | 
						|
    //
 | 
						|
    pContext->PacketsInWindow -= 1;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //  Return the packet
 | 
						|
  //
 | 
						|
  DBG_EXIT_HEX ( pPacket );
 | 
						|
  return pPacket;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Transmit the packet
 | 
						|
 | 
						|
  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure
 | 
						|
  @param [in] pPacket     Address of a ::TFTP_PACKET structure
 | 
						|
 | 
						|
  @retval EFI_SUCCESS   Message processed successfully
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
PacketTx (
 | 
						|
  IN TSDT_CONNECTION_CONTEXT * pContext,
 | 
						|
  IN TFTP_PACKET * pPacket
 | 
						|
  )
 | 
						|
{
 | 
						|
  ssize_t LengthInBytes;
 | 
						|
  EFI_STATUS Status;
 | 
						|
 | 
						|
  DBG_ENTER ( );
 | 
						|
 | 
						|
  //
 | 
						|
  //  Assume success
 | 
						|
  //
 | 
						|
  Status = EFI_SUCCESS;
 | 
						|
 | 
						|
  //
 | 
						|
  //  Determine if this packet should be transmitted
 | 
						|
  //
 | 
						|
  if ( PcdGet32 ( Tftp_MaxRetry ) >= pPacket->RetryCount ) {
 | 
						|
    pPacket->RetryCount += 1;
 | 
						|
 | 
						|
    //
 | 
						|
    //  Display the operation
 | 
						|
    //
 | 
						|
    DEBUG (( DEBUG_TX_PACKET,
 | 
						|
              "0x%08x: Packet transmiting\r\n",
 | 
						|
              pPacket ));
 | 
						|
    DEBUG (( DEBUG_TX,
 | 
						|
              "0x%08x: pContext sending 0x%08x bytes\r\n",
 | 
						|
              pContext,
 | 
						|
              pPacket->TxBytes ));
 | 
						|
 | 
						|
    //
 | 
						|
    //  Keep track of when the packet was transmitted
 | 
						|
    //
 | 
						|
    if ( PcdGetBool ( Tftp_HighSpeed )) {
 | 
						|
      pPacket->TxTime = GetPerformanceCounter ( );
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Send the TFTP packet
 | 
						|
    //
 | 
						|
    pContext->PacketsInWindow += 1;
 | 
						|
    LengthInBytes = sendto ( pContext->SocketFd,
 | 
						|
                             &pPacket->TxBuffer[ 0 ],
 | 
						|
                             pPacket->TxBytes,
 | 
						|
                             0,
 | 
						|
                             (struct sockaddr *)&pContext->RemoteAddress,
 | 
						|
                             pContext->RemoteAddress.sin6_len );
 | 
						|
    if ( -1 == LengthInBytes ) {
 | 
						|
      DEBUG (( DEBUG_ERROR | DEBUG_TX,
 | 
						|
                "ERROR - Transmit failure, errno: 0x%08x\r\n",
 | 
						|
                errno ));
 | 
						|
      pContext->PacketsInWindow -= 1;
 | 
						|
      Status = EFI_DEVICE_ERROR;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    //
 | 
						|
    //  Too many retries
 | 
						|
    //
 | 
						|
    Status = EFI_NO_RESPONSE;
 | 
						|
    DEBUG (( DEBUG_WARN | DEBUG_WINDOW,
 | 
						|
              "WARNING - No response from TFTP client\r\n" ));
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //  Return the operation status
 | 
						|
  //
 | 
						|
  DBG_EXIT_STATUS ( Status );
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Process the work for the sockets.
 | 
						|
 | 
						|
  @param [in] pTftpServer   Address of the ::TSDT_TFTP_SERVER structure
 | 
						|
  @param [in] pIndex        Address of an index into the pollfd array
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
PortWork (
 | 
						|
  IN TSDT_TFTP_SERVER * pTftpServer,
 | 
						|
  IN int * pIndex
 | 
						|
  )
 | 
						|
{
 | 
						|
  int Index;
 | 
						|
  TSDT_CONNECTION_CONTEXT * pContext;
 | 
						|
  struct pollfd * pTftpPort;
 | 
						|
  socklen_t RemoteAddressLength;
 | 
						|
  int revents;
 | 
						|
 | 
						|
  DBG_ENTER ( );
 | 
						|
 | 
						|
  //
 | 
						|
  //  Locate the port
 | 
						|
  //
 | 
						|
  Index = *pIndex;
 | 
						|
  if ( -1 != Index ) {
 | 
						|
    pTftpPort = &pTftpServer->TftpPort[ *pIndex ];
 | 
						|
 | 
						|
    //
 | 
						|
    //  Handle input events
 | 
						|
    //
 | 
						|
    revents = pTftpPort->revents;
 | 
						|
    pTftpPort->revents = 0;
 | 
						|
    if ( 0 != ( revents & POLLRDNORM )) {
 | 
						|
      //
 | 
						|
      //  Receive the message from the remote system
 | 
						|
      //
 | 
						|
      RemoteAddressLength = sizeof ( pTftpServer->RemoteAddress );
 | 
						|
      pTftpServer->RxBytes = recvfrom ( pTftpPort->fd,
 | 
						|
                                        &pTftpServer->RxBuffer[ 0 ],
 | 
						|
                                        sizeof ( pTftpServer->RxBuffer ),
 | 
						|
                                        0,
 | 
						|
                                        (struct sockaddr *) &pTftpServer->RemoteAddress,
 | 
						|
                                        &RemoteAddressLength );
 | 
						|
      if ( -1 != pTftpServer->RxBytes ) {
 | 
						|
        if ( PcdGetBool ( Tftp_HighSpeed )) {
 | 
						|
          pTftpServer->RxTime = GetPerformanceCounter ( );
 | 
						|
        }
 | 
						|
        if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {
 | 
						|
          DEBUG (( DEBUG_TFTP_PORT,
 | 
						|
                   "Received %d bytes from %d.%d.%d.%d:%d\r\n",
 | 
						|
                   pTftpServer->RxBytes,
 | 
						|
                   pTftpServer->RemoteAddress.v4.sin_addr.s_addr & 0xff,
 | 
						|
                   ( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ) & 0xff,
 | 
						|
                   ( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ) & 0xff,
 | 
						|
                   ( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ) & 0xff,
 | 
						|
                   htons ( pTftpServer->RemoteAddress.v4.sin_port )));
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          DEBUG (( DEBUG_TFTP_PORT,
 | 
						|
                   "Received %d bytes from [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",
 | 
						|
                   pTftpServer->RxBytes,
 | 
						|
                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],
 | 
						|
                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],
 | 
						|
                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],
 | 
						|
                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],
 | 
						|
                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],
 | 
						|
                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],
 | 
						|
                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],
 | 
						|
                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],
 | 
						|
                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],
 | 
						|
                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],
 | 
						|
                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],
 | 
						|
                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],
 | 
						|
                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],
 | 
						|
                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],
 | 
						|
                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],
 | 
						|
                   pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],
 | 
						|
                   htons ( pTftpServer->RemoteAddress.v6.sin6_port )));
 | 
						|
        }
 | 
						|
 | 
						|
        //
 | 
						|
        //  Lookup connection context using the remote system address and port
 | 
						|
        //  to determine if an existing connection to this remote
 | 
						|
        //  system exists
 | 
						|
        //
 | 
						|
        pContext = ContextFind ( pTftpServer );
 | 
						|
 | 
						|
        //
 | 
						|
        //  Process the received message
 | 
						|
        //
 | 
						|
        TftpProcessRequest ( pTftpServer, pContext, pTftpPort->fd );
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        //
 | 
						|
        //  Receive error on the TFTP server port
 | 
						|
        //  Close the server socket
 | 
						|
        //
 | 
						|
        DEBUG (( DEBUG_ERROR,
 | 
						|
                  "ERROR - Failed receive on TFTP server port, errno: 0x%08x\r\n",
 | 
						|
                  errno ));
 | 
						|
        revents |= POLLHUP;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Handle the close event
 | 
						|
    //
 | 
						|
    if ( 0 != ( revents & POLLHUP )) {
 | 
						|
      //
 | 
						|
      //  Close the port
 | 
						|
      //
 | 
						|
      close ( pTftpPort->fd );
 | 
						|
      pTftpPort->fd = -1;
 | 
						|
      *pIndex = -1;
 | 
						|
      pTftpServer->Entries -= 1;
 | 
						|
      ASSERT ( 0 <= pTftpServer->Entries );
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  DBG_EXIT ( );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Build and send an error packet
 | 
						|
 | 
						|
  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure
 | 
						|
  @param [in] Error       Error number for the packet
 | 
						|
  @param [in] pError      Zero terminated error string address
 | 
						|
 | 
						|
  @retval EFI_SUCCESS     Message processed successfully
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
SendError (
 | 
						|
  IN TSDT_CONNECTION_CONTEXT * pContext,
 | 
						|
  IN UINT16 Error,
 | 
						|
  IN UINT8 * pError
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT8 Character;
 | 
						|
  UINT8 * pBuffer;
 | 
						|
  TFTP_PACKET * pPacket;
 | 
						|
  EFI_STATUS Status;
 | 
						|
 | 
						|
  DBG_ENTER ( );
 | 
						|
 | 
						|
  //
 | 
						|
  //  Build the error packet
 | 
						|
  //
 | 
						|
  pPacket = &pContext->ErrorPacket;
 | 
						|
  pBuffer = &pPacket->TxBuffer[ 0 ];
 | 
						|
  pBuffer[ 0 ] = 0;
 | 
						|
  pBuffer[ 1 ] = TFTP_OP_ERROR;
 | 
						|
  pBuffer[ 2 ] = (UINT8)( Error >> 8 );
 | 
						|
  pBuffer[ 3 ] = (UINT8)Error;
 | 
						|
 | 
						|
  //
 | 
						|
  //  Copy the zero terminated string into the buffer
 | 
						|
  //
 | 
						|
  pBuffer += 4;
 | 
						|
  do {
 | 
						|
    Character = *pError++;
 | 
						|
    *pBuffer++ = Character;
 | 
						|
  } while ( 0 != Character );
 | 
						|
 | 
						|
  //
 | 
						|
  //  Send the error message
 | 
						|
  //
 | 
						|
  pPacket->TxBytes = pBuffer - &pPacket->TxBuffer[ 0 ];
 | 
						|
  Status = PacketTx ( pContext, pPacket );
 | 
						|
 | 
						|
  //
 | 
						|
  //  Return the operation status
 | 
						|
  //
 | 
						|
  DBG_EXIT_STATUS ( Status );
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Scan the list of sockets and process any pending work
 | 
						|
 | 
						|
  @param [in] pTftpServer   Address of the ::TSDT_TFTP_SERVER structure
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
SocketPoll (
 | 
						|
  IN TSDT_TFTP_SERVER * pTftpServer
 | 
						|
  )
 | 
						|
{
 | 
						|
  int FDCount;
 | 
						|
 | 
						|
  DEBUG (( DEBUG_SOCKET_POLL, "Entering SocketPoll\r\n" ));
 | 
						|
 | 
						|
  //
 | 
						|
  //  Determine if any ports are active
 | 
						|
  //
 | 
						|
  if ( 0 != pTftpServer->Entries ) {
 | 
						|
    FDCount = poll ( &pTftpServer->TftpPort[ 0 ],
 | 
						|
                     pTftpServer->Entries,
 | 
						|
                     CLIENT_POLL_DELAY );
 | 
						|
    if ( 0 < FDCount ) {
 | 
						|
      //
 | 
						|
      //  Process this port
 | 
						|
      //
 | 
						|
      PortWork ( pTftpServer, &pTftpServer->Udpv4Index );
 | 
						|
      PortWork ( pTftpServer, &pTftpServer->Udpv6Index );
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  DEBUG (( DEBUG_SOCKET_POLL, "Exiting SocketPoll\r\n" ));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Process the ACK
 | 
						|
 | 
						|
  @param [in] pTftpServer   Address of the ::TSDT_TFTP_SERVER structure
 | 
						|
  @param [in] pContext    Connection context structure address
 | 
						|
 | 
						|
  @retval TRUE if the context should be closed
 | 
						|
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
TftpAck (
 | 
						|
  IN TSDT_TFTP_SERVER * pTftpServer,
 | 
						|
  IN TSDT_CONNECTION_CONTEXT * pContext
 | 
						|
  )
 | 
						|
{
 | 
						|
  INTN AckNumber;
 | 
						|
  BOOLEAN bCloseContext;
 | 
						|
  UINT16 BlockNumber;
 | 
						|
  UINT8 * pBuffer;
 | 
						|
  TFTP_PACKET * pPacket;
 | 
						|
  EFI_STATUS Status;
 | 
						|
 | 
						|
  DBG_ENTER ( );
 | 
						|
 | 
						|
  //
 | 
						|
  //  Use break instead of goto
 | 
						|
  //
 | 
						|
  bCloseContext = FALSE;
 | 
						|
  for ( ; ; ) {
 | 
						|
    //
 | 
						|
    //  Validate the parameters
 | 
						|
    //
 | 
						|
    if ( NULL == pContext ) {
 | 
						|
      if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {
 | 
						|
        DEBUG (( DEBUG_ERROR,
 | 
						|
                  "ERROR - File not open for %d.%d.%d.%d:%d\r\n",
 | 
						|
                  (UINT8)pTftpServer->RemoteAddress.v4.sin_addr.s_addr,
 | 
						|
                  (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ),
 | 
						|
                  (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ),
 | 
						|
                  (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ),
 | 
						|
                  htons ( pTftpServer->RemoteAddress.v4.sin_port )));
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        DEBUG (( DEBUG_ERROR,
 | 
						|
                  "ERROR - File not open for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],
 | 
						|
                  htons ( pTftpServer->RemoteAddress.v6.sin6_port )));
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Verify that the ACK was expected
 | 
						|
    //
 | 
						|
    pPacket = pContext->pTxHead;
 | 
						|
    if ( NULL == pPacket ) {
 | 
						|
      //
 | 
						|
      //  ACK not expected!
 | 
						|
      //
 | 
						|
      DEBUG (( DEBUG_ERROR,
 | 
						|
                "ERROR - Expecting data not ACKs for pContext 0x%08x\r\n",
 | 
						|
                pContext ));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Get the ACKed block number
 | 
						|
    //
 | 
						|
    pBuffer = &pTftpServer->RxBuffer[ 0 ];
 | 
						|
    BlockNumber = HTONS ( *(UINT16 *)&pBuffer[ 2 ]);
 | 
						|
 | 
						|
    //
 | 
						|
    //  Determine if this is the correct ACK
 | 
						|
    //
 | 
						|
    DEBUG (( DEBUG_TFTP_ACK,
 | 
						|
              "ACK for block 0x%04x received\r\n",
 | 
						|
              BlockNumber ));
 | 
						|
    AckNumber = BlockNumber - pPacket->BlockNumber;
 | 
						|
    if (( 0 > AckNumber ) || ( AckNumber >= (INTN)pContext->PacketsInWindow )){
 | 
						|
      DEBUG (( DEBUG_WARN | DEBUG_TFTP_ACK,
 | 
						|
                "WARNING - Expecting ACK 0x%0x4 not received ACK 0x%08x\r\n",
 | 
						|
                pPacket->BlockNumber,
 | 
						|
                BlockNumber ));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Release the ACKed packets
 | 
						|
    //
 | 
						|
    do {
 | 
						|
      //
 | 
						|
      //  Remove the packet from the transmit list and window
 | 
						|
      //
 | 
						|
      pPacket = PacketRemove ( pContext );
 | 
						|
 | 
						|
      //
 | 
						|
      //  Get the block number of this packet
 | 
						|
      //
 | 
						|
      AckNumber = pPacket->BlockNumber;
 | 
						|
 | 
						|
      //
 | 
						|
      //  Increase the size of the transmit window
 | 
						|
      //
 | 
						|
      if ( PcdGetBool ( Tftp_HighSpeed )
 | 
						|
        && ( AckNumber == BlockNumber )) {
 | 
						|
        WindowAck ( pTftpServer, pContext, pPacket );
 | 
						|
      }
 | 
						|
 | 
						|
      //
 | 
						|
      //  Free this packet
 | 
						|
      //
 | 
						|
      PacketFree ( pContext, pPacket );
 | 
						|
    } while (( NULL != pContext->pTxHead ) && ( AckNumber != BlockNumber ));
 | 
						|
 | 
						|
    //
 | 
						|
    //  Fill the window with packets
 | 
						|
    //
 | 
						|
    pPacket = pContext->pTxHead;
 | 
						|
    while (( NULL != pPacket )
 | 
						|
      && ( pContext->PacketsInWindow < pContext->WindowSize )
 | 
						|
      && ( !bCloseContext )) {
 | 
						|
      Status = PacketTx ( pContext, pPacket );
 | 
						|
      bCloseContext = (BOOLEAN)( EFI_ERROR ( Status ));
 | 
						|
      pPacket = pPacket->pNext;
 | 
						|
    }
 | 
						|
    
 | 
						|
    //
 | 
						|
    //  Get more packets ready for transmission
 | 
						|
    //
 | 
						|
    PacketFill ( pContext );
 | 
						|
 | 
						|
    //
 | 
						|
    //  Close the context when the last packet is ACKed
 | 
						|
    //
 | 
						|
    if ( 0 == pContext->PacketsInWindow ) {
 | 
						|
      bCloseContext = TRUE;
 | 
						|
 | 
						|
      //
 | 
						|
      //  Display the bandwidth
 | 
						|
      //
 | 
						|
      if ( PcdGetBool ( Tftp_Bandwidth )) {
 | 
						|
        UINT64 Bandwidth;
 | 
						|
        UINT64 DeltaTime;
 | 
						|
        UINT64 NanoSeconds;
 | 
						|
        UINT32 Value;
 | 
						|
 | 
						|
        //
 | 
						|
        //  Compute the download time
 | 
						|
        //
 | 
						|
        DeltaTime = GetPerformanceCounter ( );
 | 
						|
        if ( pTftpServer->Time2 > pTftpServer->Time1 ) {
 | 
						|
          DeltaTime = DeltaTime - pContext->TimeStart;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          DeltaTime = pContext->TimeStart - DeltaTime;
 | 
						|
        }
 | 
						|
        NanoSeconds = GetTimeInNanoSecond ( DeltaTime );
 | 
						|
        Bandwidth = pContext->LengthInBytes;
 | 
						|
        DEBUG (( DEBUG_WINDOW,
 | 
						|
                  "File Length %Ld, Transfer Time: %d.%03d Sec\r\n",
 | 
						|
                  Bandwidth,
 | 
						|
                  DivU64x32 ( NanoSeconds, 1000 * 1000 * 1000 ),
 | 
						|
                  ((UINT32)DivU64x32 ( NanoSeconds, 1000 * 1000 )) % 1000 ));
 | 
						|
 | 
						|
        //
 | 
						|
        //  Display the round trip time
 | 
						|
        //
 | 
						|
        Bandwidth = MultU64x32 ( Bandwidth, 8 * 1000 * 1000 );
 | 
						|
        Bandwidth /= NanoSeconds;
 | 
						|
        if ( 1000 > Bandwidth ) {
 | 
						|
          Value = (UINT32)Bandwidth;
 | 
						|
          Print ( L"Bandwidth: %d Kbits/Sec\r\n",
 | 
						|
                  Value );
 | 
						|
        }
 | 
						|
        else if (( 1000 * 1000 ) > Bandwidth ) {
 | 
						|
          Value = (UINT32)Bandwidth;
 | 
						|
          Print ( L"Bandwidth: %d.%03d Mbits/Sec\r\n",
 | 
						|
                  Value / 1000,
 | 
						|
                  Value % 1000 );
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          Value = (UINT32)DivU64x32 ( Bandwidth, 1000 );
 | 
						|
          Print ( L"Bandwidth: %d.%03d Gbits/Sec\r\n",
 | 
						|
                  Value / 1000,
 | 
						|
                  Value % 1000 );
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //  Return the operation status
 | 
						|
  //
 | 
						|
  DBG_EXIT ( );
 | 
						|
  return bCloseContext;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Get the next TFTP option
 | 
						|
 | 
						|
  @param [in] pOption       Address of a zero terminated option string
 | 
						|
  @param [in] pEnd          End of buffer address
 | 
						|
  @param [in] ppNextOption  Address to receive the address of the next
 | 
						|
                            zero terminated option string
 | 
						|
 | 
						|
  @retval EFI_SUCCESS   Message processed successfully
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
TftpOptionGet (
 | 
						|
  IN UINT8 * pOption,
 | 
						|
  IN UINT8 * pEnd,
 | 
						|
  IN UINT8 ** ppNextOption
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT8 * pNextOption;
 | 
						|
  EFI_STATUS Status;
 | 
						|
 | 
						|
  //
 | 
						|
  //  Locate the end of the option
 | 
						|
  //
 | 
						|
  pNextOption = pOption;
 | 
						|
  while (( pEnd > pNextOption ) && ( 0 != *pNextOption )) {
 | 
						|
    pNextOption += 1;
 | 
						|
  }
 | 
						|
  if ( pEnd <= pNextOption ) {
 | 
						|
    //
 | 
						|
    //  Error - end of buffer reached
 | 
						|
    //
 | 
						|
    DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,
 | 
						|
              "ERROR - Option without zero termination received!\r\n" ));
 | 
						|
    Status = EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    //
 | 
						|
    //  Zero terminated option found
 | 
						|
    //
 | 
						|
    pNextOption += 1;
 | 
						|
 | 
						|
    //
 | 
						|
    //  Display the zero terminated ASCII option string
 | 
						|
    //
 | 
						|
    DEBUG (( DEBUG_TFTP_REQUEST,
 | 
						|
              "Option: %a\r\n",
 | 
						|
              pOption ));
 | 
						|
    Status = EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //  Return the next option address
 | 
						|
  //
 | 
						|
  *ppNextOption = pNextOption;
 | 
						|
 | 
						|
  //
 | 
						|
  //  Return the operation status
 | 
						|
  //
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Place an option value into the option acknowledgement
 | 
						|
 | 
						|
  @param [in] pOack     Option acknowledgement address
 | 
						|
  @param [in] Value     Value to translate into ASCII decimal
 | 
						|
 | 
						|
  @return               Option acknowledgement address
 | 
						|
 | 
						|
**/
 | 
						|
UINT8 *
 | 
						|
TftpOptionSet (
 | 
						|
  IN UINT8 * pOack,
 | 
						|
  IN UINT64 Value
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT64 NextValue;
 | 
						|
 | 
						|
  //
 | 
						|
  //  Determine the next value
 | 
						|
  //
 | 
						|
  NextValue = Value / 10;
 | 
						|
 | 
						|
  //
 | 
						|
  //  Supress leading zeros
 | 
						|
  //
 | 
						|
  if ( 0 != NextValue ) {
 | 
						|
    pOack = TftpOptionSet ( pOack, NextValue );
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //  Output this digit
 | 
						|
  //
 | 
						|
  *pOack++ = (UINT8)( Value - ( NextValue * 10 ) + '0' );
 | 
						|
 | 
						|
  //
 | 
						|
  //  Return the next option acknowledgement location
 | 
						|
  //
 | 
						|
  return pOack;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Process the TFTP request
 | 
						|
 | 
						|
  @param [in] pContext  Address of a ::TSDT_CONNECTION_CONTEXT structure
 | 
						|
  @param [in] pOption   Address of the first zero terminated option string
 | 
						|
  @param [in] pEnd      End of buffer address
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
TftpOptions (
 | 
						|
  IN TSDT_CONNECTION_CONTEXT * pContext,
 | 
						|
  IN UINT8 * pOption,
 | 
						|
  IN UINT8 * pEnd
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT8 * pNextOption;
 | 
						|
  UINT8 * pOack;
 | 
						|
  TFTP_PACKET * pPacket;
 | 
						|
  UINT8 * pTemp;
 | 
						|
  UINT8 * pValue;
 | 
						|
  EFI_STATUS Status;
 | 
						|
  INT32 Value;
 | 
						|
 | 
						|
  //
 | 
						|
  //  Get a packet
 | 
						|
  //
 | 
						|
  pPacket = PacketGet ( pContext );
 | 
						|
 | 
						|
  //
 | 
						|
  //  Start the OACK packet
 | 
						|
  //  Let the OACK handle the parsing errors
 | 
						|
  //  See http://tools.ietf.org/html/rfc2347
 | 
						|
  //
 | 
						|
  pOack = &pPacket->TxBuffer[ 0 ];
 | 
						|
  *pOack++ = 0;
 | 
						|
  *pOack++ = TFTP_OP_OACK;
 | 
						|
  pPacket->TxBytes = 2;
 | 
						|
  pPacket->BlockNumber = 0;
 | 
						|
 | 
						|
  //
 | 
						|
  //  Walk the list of options
 | 
						|
  //
 | 
						|
  do {
 | 
						|
    //
 | 
						|
    //  Get the next option, skip junk at end of message
 | 
						|
    //
 | 
						|
    Status = TftpOptionGet ( pOption, pEnd, &pNextOption );
 | 
						|
    if ( !EFI_ERROR ( Status )) {
 | 
						|
      //
 | 
						|
      //  Process the option
 | 
						|
      //
 | 
						|
 | 
						|
      //
 | 
						|
      //  blksize - See http://tools.ietf.org/html/rfc2348
 | 
						|
      //
 | 
						|
      pValue = pNextOption;
 | 
						|
      if ( 0 == strcasecmp ((char *)pOption, "blksize" )) {
 | 
						|
        //
 | 
						|
        //  Get the value
 | 
						|
        //
 | 
						|
        Status = TftpOptionGet ( pValue, pEnd, &pNextOption );
 | 
						|
        if ( !EFI_ERROR ( Status )) {
 | 
						|
          //
 | 
						|
          //  Validate the block size, skip non-numeric block sizes
 | 
						|
          //
 | 
						|
          Status = TftpOptionValue ( pValue, &Value );
 | 
						|
          if ( !EFI_ERROR ( Status )) {
 | 
						|
            //
 | 
						|
            //  Propose a smaller block size if necessary
 | 
						|
            //
 | 
						|
            if ( Value > TFTP_MAX_BLOCK_SIZE ) {
 | 
						|
              Value = TFTP_MAX_BLOCK_SIZE;
 | 
						|
            }
 | 
						|
 | 
						|
            //
 | 
						|
            //  Set the new block size
 | 
						|
            //
 | 
						|
            pContext->BlockSize = Value;
 | 
						|
            DEBUG (( DEBUG_TFTP_REQUEST,
 | 
						|
                      "Using block size of %d bytes\r\n",
 | 
						|
                      pContext->BlockSize ));
 | 
						|
 | 
						|
            //
 | 
						|
            //  Update the OACK
 | 
						|
            //
 | 
						|
            pTemp = pOack;
 | 
						|
            *pOack++ = 'b';
 | 
						|
            *pOack++ = 'l';
 | 
						|
            *pOack++ = 'k';
 | 
						|
            *pOack++ = 's';
 | 
						|
            *pOack++ = 'i';
 | 
						|
            *pOack++ = 'z';
 | 
						|
            *pOack++ = 'e';
 | 
						|
            *pOack++ = 0;
 | 
						|
            pOack = TftpOptionSet ( pOack, pContext->BlockSize );
 | 
						|
            *pOack++ = 0;
 | 
						|
            pPacket->TxBytes += pOack - pTemp;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      //
 | 
						|
      //  timeout - See http://tools.ietf.org/html/rfc2349
 | 
						|
      //
 | 
						|
      else if ( 0 == strcasecmp ((char *)pOption, "timeout" )) {
 | 
						|
        //
 | 
						|
        //  Get the value
 | 
						|
        //
 | 
						|
        Status = TftpOptionGet ( pValue, pEnd, &pNextOption );
 | 
						|
        if ( !EFI_ERROR ( Status )) {
 | 
						|
          Status = TftpOptionValue ( pValue, &Value );
 | 
						|
          if ( !EFI_ERROR ( Status )) {
 | 
						|
            //
 | 
						|
            //  Set the timeout value
 | 
						|
            //
 | 
						|
            pContext->MaxTimeout = Value;
 | 
						|
            DEBUG (( DEBUG_TFTP_REQUEST,
 | 
						|
                      "Using timeout of %d seconds\r\n",
 | 
						|
                      pContext->MaxTimeout ));
 | 
						|
 | 
						|
            //
 | 
						|
            //  Update the OACK
 | 
						|
            //
 | 
						|
            pTemp = pOack;
 | 
						|
            *pOack++ = 't';
 | 
						|
            *pOack++ = 'i';
 | 
						|
            *pOack++ = 'm';
 | 
						|
            *pOack++ = 'e';
 | 
						|
            *pOack++ = 'o';
 | 
						|
            *pOack++ = 'u';
 | 
						|
            *pOack++ = 't';
 | 
						|
            *pOack++ = 0;
 | 
						|
            pOack = TftpOptionSet ( pOack, pContext->MaxTimeout );
 | 
						|
            *pOack++ = 0;
 | 
						|
            pPacket->TxBytes += pOack - pTemp;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      //
 | 
						|
      //  tsize - See http://tools.ietf.org/html/rfc2349
 | 
						|
      //
 | 
						|
      else if ( 0 == strcasecmp ((char *)pOption, "tsize" )) {
 | 
						|
        //
 | 
						|
        //  Get the value
 | 
						|
        //
 | 
						|
        Status = TftpOptionGet ( pValue, pEnd, &pNextOption );
 | 
						|
        if ( !EFI_ERROR ( Status )) {
 | 
						|
          Status = TftpOptionValue ( pValue, &Value );
 | 
						|
          if ( !EFI_ERROR ( Status )) {
 | 
						|
            //
 | 
						|
            //  Return the file size
 | 
						|
            //
 | 
						|
            DEBUG (( DEBUG_TFTP_REQUEST,
 | 
						|
                      "Returning file size of %Ld bytes\r\n",
 | 
						|
                      pContext->LengthInBytes ));
 | 
						|
 | 
						|
            //
 | 
						|
            //  Update the OACK
 | 
						|
            //
 | 
						|
            pTemp = pOack;
 | 
						|
            *pOack++ = 't';
 | 
						|
            *pOack++ = 's';
 | 
						|
            *pOack++ = 'i';
 | 
						|
            *pOack++ = 'z';
 | 
						|
            *pOack++ = 'e';
 | 
						|
            *pOack++ = 0;
 | 
						|
            pOack = TftpOptionSet ( pOack, pContext->LengthInBytes );
 | 
						|
            *pOack++ = 0;
 | 
						|
            pPacket->TxBytes += pOack - pTemp;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        //
 | 
						|
        //  Unknown option - Ignore it
 | 
						|
        //
 | 
						|
        DEBUG (( DEBUG_WARN | DEBUG_TFTP_REQUEST,
 | 
						|
                  "WARNING - Skipping unknown option: %a\r\n",
 | 
						|
                  pOption ));
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Set the next option
 | 
						|
    //
 | 
						|
    pOption = pNextOption;
 | 
						|
  } while ( pEnd > pOption );
 | 
						|
 | 
						|
  //
 | 
						|
  //  Transmit the OACK if necessary
 | 
						|
  //
 | 
						|
  if ( 2 < pPacket->TxBytes ) {
 | 
						|
    PacketQueue ( pContext, pPacket );
 | 
						|
  }
 | 
						|
  else {
 | 
						|
    PacketFree ( pContext, pPacket );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Process the TFTP request
 | 
						|
 | 
						|
  @param [in] pOption   Address of the first zero terminated option string
 | 
						|
  @param [in] pValue    Address to receive the value
 | 
						|
 | 
						|
  @retval EFI_SUCCESS   Option translated into a value
 | 
						|
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
TftpOptionValue (
 | 
						|
  IN UINT8 * pOption,
 | 
						|
  IN INT32 * pValue
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT8 Digit;
 | 
						|
  EFI_STATUS Status;
 | 
						|
  INT32 Value;
 | 
						|
 | 
						|
  //
 | 
						|
  //  Assume success
 | 
						|
  //
 | 
						|
  Status = EFI_SUCCESS;
 | 
						|
 | 
						|
  //
 | 
						|
  //  Walk the characters in the option
 | 
						|
  //
 | 
						|
  Value = 0;
 | 
						|
  while ( 0 != *pOption ) {
 | 
						|
    //
 | 
						|
    //  Convert the next digit to binary
 | 
						|
    //
 | 
						|
    Digit = *pOption++;
 | 
						|
    if (( '0' <= Digit ) && ( '9' >= Digit )) {
 | 
						|
      Value *= 10;
 | 
						|
      Value += Digit - '0';
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,
 | 
						|
                "ERROR - Invalid character '0x%02x' in the value\r\n",
 | 
						|
                Digit ));
 | 
						|
      Status = EFI_INVALID_PARAMETER;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //  Return the value
 | 
						|
  //
 | 
						|
  *pValue = Value;
 | 
						|
 | 
						|
  //
 | 
						|
  //  Return the conversion status
 | 
						|
  //
 | 
						|
  return Status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Process the TFTP request
 | 
						|
 | 
						|
  @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
 | 
						|
  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure
 | 
						|
  @param [in] SocketFd    Socket file descriptor
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
TftpProcessRequest (
 | 
						|
  IN TSDT_TFTP_SERVER * pTftpServer,
 | 
						|
  IN TSDT_CONNECTION_CONTEXT * pContext,
 | 
						|
  IN int SocketFd
 | 
						|
  )
 | 
						|
{
 | 
						|
  BOOLEAN bCloseContext;
 | 
						|
  UINT16 Opcode;
 | 
						|
 | 
						|
  DBG_ENTER ( );
 | 
						|
 | 
						|
  //
 | 
						|
  //  Get the opcode
 | 
						|
  //
 | 
						|
  Opcode = HTONS ( *(UINT16 *)&pTftpServer->RxBuffer[ 0 ]);
 | 
						|
  DEBUG (( DEBUG_TFTP_REQUEST,
 | 
						|
            "TFTP Opcode: 0x%08x\r\n",
 | 
						|
            Opcode ));
 | 
						|
 | 
						|
  //
 | 
						|
  //  Validate the parameters
 | 
						|
  //
 | 
						|
  bCloseContext = FALSE;
 | 
						|
  switch ( Opcode ) {
 | 
						|
  default:
 | 
						|
    DEBUG (( DEBUG_TFTP_REQUEST,
 | 
						|
              "ERROR - Unknown TFTP opcode: %d\r\n",
 | 
						|
              Opcode ));
 | 
						|
    break;
 | 
						|
 | 
						|
  case TFTP_OP_ACK:
 | 
						|
    bCloseContext = TftpAck ( pTftpServer, pContext );
 | 
						|
    break;
 | 
						|
 | 
						|
  case TFTP_OP_READ_REQUEST:
 | 
						|
    bCloseContext = TftpRead ( pTftpServer, pContext, SocketFd );
 | 
						|
    break;
 | 
						|
 | 
						|
 | 
						|
 | 
						|
  
 | 
						|
  case TFTP_OP_DATA:
 | 
						|
    if ( NULL == pContext ) {
 | 
						|
      if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {
 | 
						|
        DEBUG (( DEBUG_ERROR,
 | 
						|
                  "ERROR - File not open for %d.%d.%d.%d:%d\r\n",
 | 
						|
                  (UINT8)pTftpServer->RemoteAddress.v4.sin_addr.s_addr,
 | 
						|
                  (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ),
 | 
						|
                  (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ),
 | 
						|
                  (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ),
 | 
						|
                  htons ( pTftpServer->RemoteAddress.v4.sin_port )));
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        DEBUG (( DEBUG_ERROR,
 | 
						|
                  "ERROR - File not open for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],
 | 
						|
                  htons ( pTftpServer->RemoteAddress.v6.sin6_port )));
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    if ( 0 != pContext->PacketsInWindow ) {
 | 
						|
      DEBUG (( DEBUG_ERROR,
 | 
						|
                "ERROR - Expecting ACKs not data for pContext 0x%08x\r\n",
 | 
						|
                pContext ));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    if ( pTftpServer->RxBytes > (ssize_t)( pContext->BlockSize + 2 + 2 )) {
 | 
						|
      DEBUG (( DEBUG_ERROR,
 | 
						|
                "ERROR - Receive data length of %d > %d bytes (maximum block size) for pContext 0x%08x\r\n",
 | 
						|
                pTftpServer->RxBytes - 2 - 2,
 | 
						|
                pContext->BlockSize,
 | 
						|
                pContext ));
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    break;
 | 
						|
 | 
						|
  case TFTP_OP_ERROR:
 | 
						|
    if ( NULL == pContext ) {
 | 
						|
      if ( AF_INET == pTftpServer->RemoteAddress.v4.sin_family ) {
 | 
						|
        DEBUG (( DEBUG_ERROR,
 | 
						|
                  "ERROR - File not open for %d.%d.%d.%d:%d\r\n",
 | 
						|
                  (UINT8)pTftpServer->RemoteAddress.v4.sin_addr.s_addr,
 | 
						|
                  (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 8 ),
 | 
						|
                  (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 16 ),
 | 
						|
                  (UINT8)( pTftpServer->RemoteAddress.v4.sin_addr.s_addr >> 24 ),
 | 
						|
                  htons ( pTftpServer->RemoteAddress.v4.sin_port )));
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        DEBUG (( DEBUG_ERROR,
 | 
						|
                  "ERROR - File not open for [%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]:%d\r\n",
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 0 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 1 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 2 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 3 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 4 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 5 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 6 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 7 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 8 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 9 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 10 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 11 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 12 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 13 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 14 ],
 | 
						|
                  pTftpServer->RemoteAddress.v6.sin6_addr.__u6_addr.__u6_addr8[ 15 ],
 | 
						|
                  htons ( pTftpServer->RemoteAddress.v6.sin6_port )));
 | 
						|
      }
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //  Determine if the context should be closed
 | 
						|
  //
 | 
						|
  if ( bCloseContext ) {
 | 
						|
    ContextRemove ( pTftpServer, pContext );
 | 
						|
  }
 | 
						|
 | 
						|
  DBG_EXIT ( );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Process the read request
 | 
						|
 | 
						|
  @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
 | 
						|
  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure
 | 
						|
  @param [in] SocketFd    Socket file descriptor
 | 
						|
 | 
						|
  @retval TRUE if the context should be closed
 | 
						|
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
TftpRead (
 | 
						|
  IN TSDT_TFTP_SERVER * pTftpServer,
 | 
						|
  IN TSDT_CONNECTION_CONTEXT * pContext,
 | 
						|
  IN int SocketFd
 | 
						|
  )
 | 
						|
{
 | 
						|
  BOOLEAN bCloseContext;
 | 
						|
  struct stat FileStatus;
 | 
						|
  UINT8 * pBuffer;
 | 
						|
  UINT8 * pEnd;
 | 
						|
  UINT8 * pFileName;
 | 
						|
  UINT8 * pMode;
 | 
						|
  UINT8 * pOption;
 | 
						|
  CHAR8 * pReadMode;
 | 
						|
  UINT64 TimeStart;
 | 
						|
 | 
						|
  DBG_ENTER ( );
 | 
						|
 | 
						|
  //
 | 
						|
  //  Log the receive time
 | 
						|
  //
 | 
						|
  TimeStart = 0;
 | 
						|
  if ( PcdGetBool ( Tftp_Bandwidth )) {
 | 
						|
    TimeStart = GetPerformanceCounter ( );
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //  Close the context if necessary
 | 
						|
  //
 | 
						|
  bCloseContext = FALSE;
 | 
						|
  if ( NULL != pContext ) {
 | 
						|
    ContextRemove ( pTftpServer, pContext );
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //  Use break instead of goto
 | 
						|
  //
 | 
						|
  for ( ; ; ) {
 | 
						|
    //
 | 
						|
    //  Create the connection context
 | 
						|
    //
 | 
						|
    pContext = ContextAdd ( pTftpServer, SocketFd );
 | 
						|
    if ( NULL == pContext ) {
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Set the start time
 | 
						|
    //
 | 
						|
    if ( PcdGetBool ( Tftp_Bandwidth )) {
 | 
						|
      pContext->TimeStart = TimeStart;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Locate the mode
 | 
						|
    //
 | 
						|
    pBuffer = &pTftpServer->RxBuffer[ 0 ];
 | 
						|
    pEnd = &pBuffer[ pTftpServer->RxBytes ];
 | 
						|
    pFileName = &pBuffer[ 2 ];
 | 
						|
    pMode = pFileName;
 | 
						|
    while (( pEnd > pMode ) && ( 0 != *pMode )) {
 | 
						|
      pMode += 1;
 | 
						|
    }
 | 
						|
    if ( pEnd <= pMode ) {
 | 
						|
      //
 | 
						|
      //  Mode not found
 | 
						|
      //
 | 
						|
      DEBUG (( DEBUG_ERROR | DEBUG_RX,
 | 
						|
                "ERROR - File mode not found\r\n" ));
 | 
						|
      //
 | 
						|
      //  Tell the client of the error
 | 
						|
      //
 | 
						|
      SendError ( pContext,
 | 
						|
                  TFTP_ERROR_SEE_MSG,
 | 
						|
                  (UINT8 *)"File open mode not found" );
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    pMode += 1;
 | 
						|
    DEBUG (( DEBUG_TFTP_REQUEST,
 | 
						|
              "TFTP - FileName: %a\r\n",
 | 
						|
              pFileName ));
 | 
						|
 | 
						|
    //
 | 
						|
    //  Locate the options
 | 
						|
    //
 | 
						|
    pOption = pMode;
 | 
						|
    while (( pEnd > pOption ) && ( 0 != *pOption )) {
 | 
						|
      pOption += 1;
 | 
						|
    }
 | 
						|
    if ( pEnd <= pOption ) {
 | 
						|
      //
 | 
						|
      //  End of mode not found
 | 
						|
      //
 | 
						|
      DEBUG (( DEBUG_ERROR | DEBUG_RX,
 | 
						|
                "ERROR - File mode not valid\r\n" ));
 | 
						|
      //
 | 
						|
      //  Tell the client of the error
 | 
						|
      //
 | 
						|
      SendError ( pContext,
 | 
						|
                  TFTP_ERROR_SEE_MSG,
 | 
						|
                  (UINT8 *)"File open mode not valid" );
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    pOption += 1;
 | 
						|
    DEBUG (( DEBUG_TFTP_REQUEST,
 | 
						|
              "TFTP - Mode: %a\r\n",
 | 
						|
              pMode ));
 | 
						|
 | 
						|
    //
 | 
						|
    //  Verify the mode is supported
 | 
						|
    //
 | 
						|
    pReadMode = "r";
 | 
						|
    if ( 0 == strcasecmp ((char *)pMode, "octet" )) {
 | 
						|
      //
 | 
						|
      //  Read the file as binary input
 | 
						|
      //
 | 
						|
      pReadMode = "rb";
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Determine the file length
 | 
						|
    //
 | 
						|
    pContext->File = fopen ((const char *)pFileName, pReadMode );
 | 
						|
    if (( NULL == pContext->File )
 | 
						|
        || ( -1 == stat ((const char *)pFileName, &FileStatus ))) {
 | 
						|
      //
 | 
						|
      //  File not found
 | 
						|
      //
 | 
						|
      DEBUG (( DEBUG_ERROR | DEBUG_TFTP_REQUEST,
 | 
						|
                ( NULL == pContext->File )
 | 
						|
                ? "ERROR - File not found!\r\n"
 | 
						|
                : "ERROR - Unable to determine file %a size!\r\n",
 | 
						|
                pFileName ));
 | 
						|
 | 
						|
      //
 | 
						|
      //  Tell the client of the error
 | 
						|
      //
 | 
						|
      SendError ( pContext,
 | 
						|
                  TFTP_ERROR_NOT_FOUND,
 | 
						|
                  (UINT8 *)"File not found" );
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    pContext->LengthInBytes = FileStatus.st_size;
 | 
						|
    pContext->BytesRemaining = pContext->LengthInBytes;
 | 
						|
    pContext->BytesToSend = pContext->LengthInBytes;
 | 
						|
 | 
						|
    //
 | 
						|
    //  Display the file size
 | 
						|
    //
 | 
						|
    DEBUG_CODE_BEGIN ( );
 | 
						|
    UINT32 Value;
 | 
						|
 | 
						|
    if ( 1024 > pContext->LengthInBytes ) {
 | 
						|
      Value = (UINT32)pContext->LengthInBytes;
 | 
						|
      DEBUG (( DEBUG_FILE_BUFFER,
 | 
						|
                "%a size: %d Bytes\r\n",
 | 
						|
                pFileName,
 | 
						|
                Value ));
 | 
						|
    }
 | 
						|
    else if (( 1024 * 1024 ) > pContext->LengthInBytes ) {
 | 
						|
      Value = (UINT32)pContext->LengthInBytes;
 | 
						|
      DEBUG (( DEBUG_FILE_BUFFER,
 | 
						|
                "%a size: %d.%03d KiBytes (%Ld Bytes)\r\n",
 | 
						|
                pFileName,
 | 
						|
                Value / 1024,
 | 
						|
                (( Value % 1024 ) * 1000 ) / 1024,
 | 
						|
                pContext->LengthInBytes ));
 | 
						|
    }
 | 
						|
    else if (( 1024 * 1024 * 1024 ) > pContext->LengthInBytes ) {
 | 
						|
      Value = (UINT32)DivU64x32 ( pContext->LengthInBytes, 1024 );
 | 
						|
      DEBUG (( DEBUG_FILE_BUFFER,
 | 
						|
                "%a size: %d.%03d MiBytes (%Ld Bytes)\r\n",
 | 
						|
                pFileName,
 | 
						|
                Value / 1024,
 | 
						|
                (( Value % 1024 ) * 1000 ) / 1024,
 | 
						|
                pContext->LengthInBytes ));
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      Value = (UINT32)DivU64x32 ( pContext->LengthInBytes, 1024 * 1024 );
 | 
						|
      DEBUG (( DEBUG_FILE_BUFFER,
 | 
						|
                "%a size: %d.%03d GiBytes (%Ld Bytes)\r\n",
 | 
						|
                pFileName,
 | 
						|
                Value / 1024,
 | 
						|
                (( Value % 1024 ) * 1000 ) / 1024,
 | 
						|
                pContext->LengthInBytes ));
 | 
						|
    }
 | 
						|
    DEBUG_CODE_END ( );
 | 
						|
 | 
						|
    //
 | 
						|
    //  Process the options
 | 
						|
    //
 | 
						|
    if ( pEnd > pOption ) {
 | 
						|
      TftpOptions ( pContext, pOption, pEnd );
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      //
 | 
						|
      //  Skip the open ACK
 | 
						|
      //
 | 
						|
      pContext->BlockNumber = 1;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Send the first packet (OACK or data block)
 | 
						|
    //
 | 
						|
    bCloseContext = PacketFill ( pContext );
 | 
						|
    break;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //  Return the close status
 | 
						|
  //
 | 
						|
  DBG_EXIT ( );
 | 
						|
  return bCloseContext;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Create the port for the TFTP server
 | 
						|
 | 
						|
  This routine polls the network layer to create the TFTP port for the
 | 
						|
  TFTP server.  More than one attempt may be necessary since it may take
 | 
						|
  some time to get the IP address and initialize the upper layers of
 | 
						|
  the network stack.
 | 
						|
 | 
						|
  @param [in] pTftpServer   Address of the ::TSDT_TFTP_SERVER structure
 | 
						|
  @param [in] AddressFamily The address family to use for the conection.
 | 
						|
  @param [in] pIndex        Address of the index into the port array
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
TftpServerSocket (
 | 
						|
  IN TSDT_TFTP_SERVER * pTftpServer,
 | 
						|
  IN sa_family_t AddressFamily,
 | 
						|
  IN int * pIndex
 | 
						|
  )
 | 
						|
{
 | 
						|
  int SocketStatus;
 | 
						|
  struct pollfd * pTftpPort;
 | 
						|
  UINT16 TftpPort;
 | 
						|
  union {
 | 
						|
    struct sockaddr_in v4;
 | 
						|
    struct sockaddr_in6 v6;
 | 
						|
  } TftpServerAddress;
 | 
						|
 | 
						|
  DEBUG (( DEBUG_SERVER_TIMER, "Entering TftpServerListen\r\n" ));
 | 
						|
 | 
						|
  //
 | 
						|
  //  Determine if the socket is already initialized
 | 
						|
  //
 | 
						|
  if ( -1 == *pIndex ) {
 | 
						|
    //
 | 
						|
    //  Attempt to create the socket for the TFTP server
 | 
						|
    //
 | 
						|
    pTftpPort = &pTftpServer->TftpPort[ pTftpServer->Entries ];
 | 
						|
    pTftpPort->fd = socket ( AddressFamily,
 | 
						|
                             SOCK_DGRAM,
 | 
						|
                             IPPROTO_UDP );
 | 
						|
    if ( -1 != pTftpPort->fd ) {
 | 
						|
      //
 | 
						|
      //  Initialize the poll structure
 | 
						|
      //
 | 
						|
      pTftpPort->events = POLLRDNORM | POLLHUP;
 | 
						|
      pTftpPort->revents = 0;
 | 
						|
 | 
						|
      //
 | 
						|
      //  Set the socket address
 | 
						|
      //
 | 
						|
      TftpPort = 69;
 | 
						|
      ZeroMem ( &TftpServerAddress, sizeof ( TftpServerAddress ));
 | 
						|
      TftpServerAddress.v4.sin_port = htons ( TftpPort );
 | 
						|
      if ( AF_INET == AddressFamily ) {
 | 
						|
        TftpServerAddress.v4.sin_len = sizeof ( TftpServerAddress.v4 );
 | 
						|
        TftpServerAddress.v4.sin_family = AF_INET;
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        TftpServerAddress.v6.sin6_len = sizeof ( TftpServerAddress.v6 );
 | 
						|
        TftpServerAddress.v6.sin6_family = AF_INET6;
 | 
						|
      }
 | 
						|
 | 
						|
      //
 | 
						|
      //  Bind the socket to the TFTP port
 | 
						|
      //
 | 
						|
      SocketStatus = bind ( pTftpPort->fd,
 | 
						|
                            (struct sockaddr *) &TftpServerAddress,
 | 
						|
                            TftpServerAddress.v6.sin6_len );
 | 
						|
      if ( -1 != SocketStatus ) {
 | 
						|
        DEBUG (( DEBUG_TFTP_PORT,
 | 
						|
                  "0x%08x: Socket bound to port %d\r\n",
 | 
						|
                  pTftpPort->fd,
 | 
						|
                  TftpPort ));
 | 
						|
 | 
						|
        //
 | 
						|
        //  Account for this connection
 | 
						|
        //
 | 
						|
        *pIndex = pTftpServer->Entries;
 | 
						|
        pTftpServer->Entries += 1;
 | 
						|
        ASSERT ( DIM ( pTftpServer->TftpPort ) >= pTftpServer->Entries );
 | 
						|
      }
 | 
						|
 | 
						|
      //
 | 
						|
      //  Release the socket if necessary
 | 
						|
      //
 | 
						|
      if ( -1 == SocketStatus ) {
 | 
						|
        close ( pTftpPort->fd );
 | 
						|
        pTftpPort->fd = -1;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  DEBUG (( DEBUG_SERVER_TIMER, "Exiting TftpServerListen\r\n" ));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Update the window due to the ACK
 | 
						|
 | 
						|
  @param [in] pTftpServer Address of the ::TSDT_TFTP_SERVER structure
 | 
						|
  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure
 | 
						|
  @param [in] pPacket     Address of a ::TFTP_PACKET structure
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
WindowAck (
 | 
						|
  IN TSDT_TFTP_SERVER * pTftpServer,
 | 
						|
  IN TSDT_CONNECTION_CONTEXT * pContext,
 | 
						|
  IN TFTP_PACKET * pPacket
 | 
						|
  )
 | 
						|
{
 | 
						|
  if ( PcdGetBool ( Tftp_HighSpeed )) {
 | 
						|
    UINT64 DeltaTime;
 | 
						|
    UINT64 NanoSeconds;
 | 
						|
 | 
						|
    DBG_ENTER ( );
 | 
						|
 | 
						|
    //
 | 
						|
    //  Compute the round trip time
 | 
						|
    //
 | 
						|
    if ( pTftpServer->Time2 > pTftpServer->Time1 ) {
 | 
						|
      DeltaTime = pTftpServer->RxTime - pPacket->TxTime;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      DeltaTime = pPacket->TxTime - pTftpServer->RxTime;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Adjust the round trip time
 | 
						|
    //
 | 
						|
    NanoSeconds = GetTimeInNanoSecond ( DeltaTime );
 | 
						|
    DeltaTime = RShiftU64 ( pContext->Rtt2x, ACK_SHIFT );
 | 
						|
    pContext->Rtt2x += NanoSeconds + NanoSeconds - DeltaTime;
 | 
						|
    if ( pContext->Rtt2x > pContext->MaxTimeout ) {
 | 
						|
      pContext->Rtt2x = pContext->MaxTimeout;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Account for the ACK
 | 
						|
    //
 | 
						|
    if ( pContext->WindowSize < MAX_PACKETS ) {
 | 
						|
      pContext->AckCount -= 1;
 | 
						|
      if ( 0 == pContext->AckCount ) {
 | 
						|
        //
 | 
						|
        //  Increase the window
 | 
						|
        //
 | 
						|
        pContext->WindowSize += 1;
 | 
						|
 | 
						|
        //
 | 
						|
        //  Set the ACK count
 | 
						|
        //
 | 
						|
        if ( pContext->WindowSize < pContext->Threshold ) {
 | 
						|
          pContext->AckCount = pContext->WindowSize * PcdGet32 ( Tftp_AckMultiplier );
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          pContext->AckCount = PcdGet32 ( Tftp_AckLogBase ) << pContext->WindowSize;
 | 
						|
        }
 | 
						|
 | 
						|
        //
 | 
						|
        //  Display the round trip time
 | 
						|
        //
 | 
						|
        DEBUG_CODE_BEGIN ( );
 | 
						|
        UINT32 Value;
 | 
						|
        
 | 
						|
        DeltaTime = RShiftU64 ( pContext->Rtt2x, 1 );
 | 
						|
        if ( 1000 > DeltaTime ) {
 | 
						|
          DEBUG (( DEBUG_WINDOW,
 | 
						|
                    "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %Ld nSec\r\n",
 | 
						|
                    pContext->WindowSize,
 | 
						|
                    pContext->Threshold,
 | 
						|
                    pContext->AckCount,
 | 
						|
                    DeltaTime ));
 | 
						|
        }
 | 
						|
        else if (( 1000 * 1000 ) > DeltaTime ) {
 | 
						|
          Value = (UINT32)DeltaTime;
 | 
						|
          DEBUG (( DEBUG_WINDOW,
 | 
						|
                    "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d uSec\r\n",
 | 
						|
                    pContext->WindowSize,
 | 
						|
                    pContext->Threshold,
 | 
						|
                    pContext->AckCount,
 | 
						|
                    Value / 1000,
 | 
						|
                    Value % 1000 ));
 | 
						|
        }
 | 
						|
        else if (( 1000 * 1000 * 1000 ) > DeltaTime ) {
 | 
						|
          Value = (UINT32)DivU64x32 ( DeltaTime, 1000 );
 | 
						|
          DEBUG (( DEBUG_WINDOW,
 | 
						|
                    "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d mSec\r\n",
 | 
						|
                    pContext->WindowSize,
 | 
						|
                    pContext->Threshold,
 | 
						|
                    pContext->AckCount,
 | 
						|
                    Value / 1000,
 | 
						|
                    Value % 1000 ));
 | 
						|
        }
 | 
						|
        else {
 | 
						|
          Value = (UINT32)DivU64x32 ( DeltaTime, 1000 * 1000 );
 | 
						|
          DEBUG (( DEBUG_WINDOW,
 | 
						|
                    "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d Sec\r\n",
 | 
						|
                    pContext->WindowSize,
 | 
						|
                    pContext->Threshold,
 | 
						|
                    pContext->AckCount,
 | 
						|
                    Value / 1000,
 | 
						|
                    Value % 1000 ));
 | 
						|
        }
 | 
						|
        DEBUG_CODE_END ( );
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    DBG_EXIT ( );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  A timeout has occurred, close the window
 | 
						|
 | 
						|
  @param [in] pContext    Address of a ::TSDT_CONNECTION_CONTEXT structure
 | 
						|
 | 
						|
**/
 | 
						|
VOID
 | 
						|
WindowTimeout (
 | 
						|
  IN TSDT_CONNECTION_CONTEXT * pContext
 | 
						|
  )
 | 
						|
{
 | 
						|
  if ( PcdGetBool ( Tftp_HighSpeed )) {
 | 
						|
    TFTP_PACKET * pPacket;
 | 
						|
 | 
						|
    DBG_ENTER ( );
 | 
						|
 | 
						|
    //
 | 
						|
    //  Set the threshold at half the previous window size
 | 
						|
    //
 | 
						|
    pContext->Threshold = ( pContext->WindowSize + 1 ) >> 1;
 | 
						|
 | 
						|
    //
 | 
						|
    //  Close the transmit window
 | 
						|
    //
 | 
						|
    pContext->WindowSize = 1;
 | 
						|
    pContext->PacketsInWindow = 0;
 | 
						|
 | 
						|
    //
 | 
						|
    //  Double the round trip time
 | 
						|
    //
 | 
						|
    pContext->Rtt2x = LShiftU64 ( pContext->Rtt2x, 1 );
 | 
						|
    if ( pContext->Rtt2x > pContext->MaxTimeout ) {
 | 
						|
      pContext->Rtt2x = pContext->MaxTimeout;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Set the ACK count
 | 
						|
    //
 | 
						|
    if ( pContext->WindowSize < pContext->Threshold ) {
 | 
						|
      pContext->AckCount = pContext->WindowSize * PcdGet32 ( Tftp_AckMultiplier );
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      pContext->AckCount = PcdGet32 ( Tftp_AckLogBase ) << pContext->WindowSize;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    //  Display the round trip time
 | 
						|
    //
 | 
						|
    DEBUG_CODE_BEGIN ( );
 | 
						|
    UINT64 DeltaTime;
 | 
						|
    UINT32 Value;
 | 
						|
    
 | 
						|
    DeltaTime = RShiftU64 ( pContext->Rtt2x, 1 );
 | 
						|
    if ( 1000 > DeltaTime ) {
 | 
						|
      DEBUG (( DEBUG_WINDOW,
 | 
						|
                "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %Ld nSec\r\n",
 | 
						|
                pContext->WindowSize,
 | 
						|
                pContext->Threshold,
 | 
						|
                pContext->AckCount,
 | 
						|
                DeltaTime ));
 | 
						|
    }
 | 
						|
    else if (( 1000 * 1000 ) > DeltaTime ) {
 | 
						|
      Value = (UINT32)DeltaTime;
 | 
						|
      DEBUG (( DEBUG_WINDOW,
 | 
						|
                "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d uSec\r\n",
 | 
						|
                pContext->WindowSize,
 | 
						|
                pContext->Threshold,
 | 
						|
                pContext->AckCount,
 | 
						|
                Value / 1000,
 | 
						|
                Value % 1000 ));
 | 
						|
    }
 | 
						|
    else if (( 1000 * 1000 * 1000 ) > DeltaTime ) {
 | 
						|
      Value = (UINT32)DivU64x32 ( DeltaTime, 1000 );
 | 
						|
      DEBUG (( DEBUG_WINDOW,
 | 
						|
                "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d mSec\r\n",
 | 
						|
                pContext->WindowSize,
 | 
						|
                pContext->Threshold,
 | 
						|
                pContext->AckCount,
 | 
						|
                Value / 1000,
 | 
						|
                Value % 1000 ));
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      Value = (UINT32)DivU64x32 ( DeltaTime, 1000 * 1000 );
 | 
						|
      DEBUG (( DEBUG_WINDOW,
 | 
						|
                "WindowSize: %d, Threshold: %d, AckCount: %4d, RTT: %d.%03d Sec\r\n",
 | 
						|
                pContext->WindowSize,
 | 
						|
                pContext->Threshold,
 | 
						|
                pContext->AckCount,
 | 
						|
                Value / 1000,
 | 
						|
                Value % 1000 ));
 | 
						|
    }
 | 
						|
    DEBUG_CODE_END ( );
 | 
						|
 | 
						|
    //
 | 
						|
    //  Retransmit the first packet in the window
 | 
						|
    //
 | 
						|
    pPacket = pContext->pTxHead;
 | 
						|
    if ( NULL != pPacket ) {
 | 
						|
      PacketTx ( pContext, pPacket );
 | 
						|
    }
 | 
						|
    
 | 
						|
    DBG_EXIT ( );
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Entry point for the TFTP server application.
 | 
						|
 | 
						|
  @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
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN Index;
 | 
						|
  TSDT_TFTP_SERVER * pTftpServer;
 | 
						|
  EFI_STATUS Status;
 | 
						|
  UINT64 TriggerTime;
 | 
						|
 | 
						|
  //
 | 
						|
  //  Get the performance counter characteristics
 | 
						|
  //
 | 
						|
  pTftpServer = &mTftpServer;
 | 
						|
  if ( PcdGetBool ( Tftp_HighSpeed )
 | 
						|
    || PcdGetBool ( Tftp_Bandwidth )) {
 | 
						|
    pTftpServer->ClockFrequency = GetPerformanceCounterProperties ( &pTftpServer->Time1,
 | 
						|
                                                                  &pTftpServer->Time2 );
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //  Create a timer event to start TFTP port
 | 
						|
  //
 | 
						|
  Status = gBS->CreateEvent ( EVT_TIMER,
 | 
						|
                              TPL_TFTP_SERVER,
 | 
						|
                              NULL,
 | 
						|
                              NULL,
 | 
						|
                              &pTftpServer->TimerEvent );
 | 
						|
  if ( !EFI_ERROR ( Status )) {
 | 
						|
    //
 | 
						|
    //  Compute the poll interval
 | 
						|
    //
 | 
						|
    TriggerTime = TFTP_PORT_POLL_DELAY * ( 1000 * 10 );
 | 
						|
    Status = gBS->SetTimer ( pTftpServer->TimerEvent,
 | 
						|
                             TimerPeriodic,
 | 
						|
                             TriggerTime );
 | 
						|
    if ( !EFI_ERROR ( Status )) {
 | 
						|
      DEBUG (( DEBUG_TFTP_PORT, "TFTP port timer started\r\n" ));
 | 
						|
 | 
						|
      //
 | 
						|
      //  Run the TFTP server forever
 | 
						|
      //
 | 
						|
      pTftpServer->Udpv4Index = -1;
 | 
						|
      pTftpServer->Udpv6Index = -1;
 | 
						|
      do {
 | 
						|
        //
 | 
						|
        //  Poll the network layer to create the TFTP port
 | 
						|
        //  for the tftp server.  More than one attempt may
 | 
						|
        //  be necessary since it may take some time to get
 | 
						|
        //  the IP address and initialize the upper layers
 | 
						|
        //  of the network stack.
 | 
						|
        //
 | 
						|
        if ( DIM ( pTftpServer->TftpPort ) != pTftpServer->Entries ) {
 | 
						|
          do {
 | 
						|
            //
 | 
						|
            //  Wait a while before polling for a connection
 | 
						|
            //
 | 
						|
            if ( EFI_SUCCESS != gBS->CheckEvent ( pTftpServer->TimerEvent )) {
 | 
						|
              if ( 0 == pTftpServer->Entries ) {
 | 
						|
                break;
 | 
						|
              }
 | 
						|
              gBS->WaitForEvent ( 1, &pTftpServer->TimerEvent, &Index );
 | 
						|
            }
 | 
						|
 | 
						|
            //
 | 
						|
            //  Poll for a network connection
 | 
						|
            //
 | 
						|
            TftpServerSocket ( pTftpServer,
 | 
						|
                               AF_INET,
 | 
						|
                               &pTftpServer->Udpv4Index );
 | 
						|
            TftpServerSocket ( pTftpServer,
 | 
						|
                               AF_INET6,
 | 
						|
                               &pTftpServer->Udpv6Index );
 | 
						|
          } while ( 0 == pTftpServer->Entries );
 | 
						|
        }
 | 
						|
 | 
						|
        //
 | 
						|
        //  Poll the socket for activity
 | 
						|
        //
 | 
						|
        do {
 | 
						|
          SocketPoll ( pTftpServer );
 | 
						|
 | 
						|
          //
 | 
						|
          //  Normal TFTP lets the client request the retransmit by
 | 
						|
          //  sending another ACK for the previous packet
 | 
						|
          //
 | 
						|
          if ( PcdGetBool ( Tftp_HighSpeed )) {
 | 
						|
            UINT64 CurrentTime;
 | 
						|
            UINT64 ElapsedTime;
 | 
						|
            TSDT_CONNECTION_CONTEXT * pContext;
 | 
						|
            TFTP_PACKET * pPacket;
 | 
						|
 | 
						|
            //
 | 
						|
            //  High speed TFTP uses an agressive retransmit to
 | 
						|
            //  get the TFTP client moving again when the ACK or
 | 
						|
            //  previous data packet was lost.
 | 
						|
            //
 | 
						|
            //  Get the current time
 | 
						|
            //
 | 
						|
            CurrentTime = GetPerformanceCounter ( );
 | 
						|
 | 
						|
            //
 | 
						|
            //  Walk the list of contexts
 | 
						|
            //
 | 
						|
            pContext = pTftpServer->pContextList;
 | 
						|
            while ( NULL != pContext )
 | 
						|
            {
 | 
						|
              //
 | 
						|
              //  Check for a transmit timeout
 | 
						|
              //
 | 
						|
              pPacket = pContext->pTxHead;
 | 
						|
              if ( NULL != pPacket ) {
 | 
						|
                //
 | 
						|
                //  Compute the elapsed time
 | 
						|
                //
 | 
						|
                if ( pTftpServer->Time2 > pTftpServer->Time1 ) {
 | 
						|
                  ElapsedTime = CurrentTime - pPacket->TxTime;
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                  ElapsedTime = pPacket->TxTime - CurrentTime;
 | 
						|
                }
 | 
						|
                ElapsedTime = GetTimeInNanoSecond ( ElapsedTime );
 | 
						|
 | 
						|
                //
 | 
						|
                //  Determine if a retransmission is necessary
 | 
						|
                //
 | 
						|
                if ( ElapsedTime >= pContext->Rtt2x ) {
 | 
						|
                  DEBUG (( DEBUG_WINDOW,
 | 
						|
                            "0x%08x: Context TX timeout for packet 0x%08x, Window: %d\r\n",
 | 
						|
                            pContext,
 | 
						|
                            pPacket,
 | 
						|
                            pContext->WindowSize ));
 | 
						|
                  WindowTimeout ( pContext );
 | 
						|
                }
 | 
						|
              }
 | 
						|
 | 
						|
              //
 | 
						|
              //  Set the next context
 | 
						|
              //
 | 
						|
              pContext = pContext->pNext;
 | 
						|
            }
 | 
						|
          }
 | 
						|
        } while ( DIM ( pTftpServer->TftpPort ) == pTftpServer->Entries );
 | 
						|
      } while ( !mbTftpServerExit );
 | 
						|
 | 
						|
      //
 | 
						|
      //  Done with the timer event
 | 
						|
      //
 | 
						|
      gBS->SetTimer ( pTftpServer->TimerEvent,
 | 
						|
                      TimerCancel,
 | 
						|
                      0 );
 | 
						|
    }
 | 
						|
    gBS->CloseEvent ( pTftpServer->TimerEvent );
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  //  Return the final status
 | 
						|
  //
 | 
						|
  DBG_EXIT_STATUS ( Status );
 | 
						|
  return Status;
 | 
						|
}
 |