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