Quality RTOS & Embedded Software

 Real time embedded FreeRTOS RSS feed 
Quick Start Supported MCUs PDF Books Trace Tools Ecosystem


Loading

TFTPserver improvements

Posted by glenenglish on September 22, 2016

attached is an improved TFTP server

I use it to write in the a FLASH ROM for loadng a XIlinx FPGA. This one works :-) . There was an inssue with double responses, and buffer returning. Only a few lines changed

Happy to assist anyone with it, or provide flash writing code .

tftpserver.c

~~~

/* FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd. All rights reserved

VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.

This file is part of the FreeRTOS distribution.

FreeRTOS is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License (version 2) as published by the
Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.

***************************************************************************
>>!   NOTE: The modification to the GPL is included to allow you to     !<<
>>!   distribute a combined work that includes FreeRTOS without being   !<<
>>!   obliged to provide the source code for proprietary components     !<<
>>!   outside of the FreeRTOS kernel.                                   !<<
***************************************************************************

FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  Full license text is available on the following
link: http://www.freertos.org/a00114.html

***************************************************************************
 *                                                                       *
 *    FreeRTOS provides completely free yet professionally developed,    *
 *    robust, strictly quality controlled, supported, and cross          *
 *    platform software that is more than just the market leader, it     *
 *    is the industry's de facto standard.                               *
 *                                                                       *
 *    Help yourself get started quickly while simultaneously helping     *
 *    to support the FreeRTOS project by purchasing a FreeRTOS           *
 *    tutorial book, reference manual, or both:                          *
 *    http://www.FreeRTOS.org/Documentation                              *
 *                                                                       *
***************************************************************************

http://www.FreeRTOS.org/FAQHelp.html - Having a problem?  Start by reading
the FAQ page "My application does not run, what could be wrong?".  Have you
defined configASSERT()?

http://www.FreeRTOS.org/support - In return for receiving this top quality
embedded software for free we request you assist our global community by
participating in the support forum.

http://www.FreeRTOS.org/training - Investing in training allows your team to
be as productive as possible as early as possible.  Now you can receive
FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers
Ltd, and the world's leading authority on the world's leading RTOS.

http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
including FreeRTOS+Trace - an indispensable productivity tool, a DOS
compatible FAT file system, and our tiny thread aware UDP/IP stack.

http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate.
Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS.

http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High
Integrity Systems ltd. to sell under the OpenRTOS brand.  Low cost OpenRTOS
licenses offer ticketed support, indemnification and commercial middleware.

http://www.SafeRTOS.com - High Integrity Systems also provide a safety
engineered and independently SIL3 certified version for use in safety and
mission critical applications that require provable dependability.

1 tab == 4 spaces!

*/

/* * A basic TFTP server that can currently only be used to receive files, and not * send files. This is a slim implementation intended for use in boot loaders * and other applications that require over the air reception of files. */

/* Standard includes. */

include
include
include

/* FreeRTOS includes. */

include "FreeRTOS.h"
include "task.h"

/* FreeRTOS+TCP includes. */

include "FreeRTOS_IP.h"
include "FreeRTOS_Sockets.h"
if( ipconfigALLOWSOCKETSENDWITHOUTBIND != 1 )
#error ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND must be set to one to use this TFTP server.
endif
if( configTICKRATEHZ > 1000 )
#error The TFTP server uses the pdMS_TO_TICKS() macro, so configTICK_RATE_HZ must be less than or equal to 1000
endif
ifndef ipconfigTFTPTIMEOUT_MS
#define ipconfigTFTP_TIME_OUT_MS			( 30000 )
endif
ifndef ipconfigTFTPMAXRETRIES
#define ipconfigTFTP_MAX_RETRIES			( 6 )
endif
define tftpPORTNUMBER ( ( uint16t ) 69 )
define tftpFILENAMEOFFSET ( 2 )
define tftpACKMESSAGELENGTH 4
define tftpMAXDATALENGTH ( ( size_t ) 512 )

typedef enum { eReadRequest = 1, eWriteRequest, eData, eAck, eError } eTFTPOpcode_t;

/* Error codes from the RFC. */ typedef enum { eFileNotFound = 1, eAccessViolation, eDiskFull, eIllegalTFTPOperation, eUnknownTransferID, eFileAlreadyExists } eTFTPErrorCode_t;

include "packstructstart.h"

struct DataPacketHeader { uint16t usOpcode; uint16t usBlockNumber; }

include "packstructend.h"

typedef struct DataPacketHeader TFTPBlockNumberHeader_t;

/* this will be the flash write routine */ extern sizet fffwrite(void * data, sizet nBytes, sizet blocksToWrite, uint32_t blocknumber);

/* * Manages a single TFTP connection at a time. */ static void prvSimpleTFTPServerTask( void *pvParameters );

/* * Manage the reception of a file. If the file is received correctly then * return pdPASS, otherwise return pdFAIL. */ static BaseTypet prvReceiveFile( void *pxFile, struct freertossockaddr *pxClient );

/* * Send an error frame to the client. */ static void prvSendTFTPError( Sockett xSocket, struct freertossockaddr *pxClient, eTFTPErrorCode_t eErrorCode );

/* * Check a received write request contains a potentially valid file name string, * and is a binary mode transfer. If so return a pointer to the file name with * the write request packet received from the network, otherwise return NULL. / static const char prvValidateWriteRequest( Sockett xSocket, struct freertossockaddr *pxClient, uint8_t *pucUDPPayloadBuffer );

/* * Called after a valid write request has been received to first check the file * does not already exist, and if the file does not exist, create the file ready * to be written. If the file did already exist, or if the file could not be * created, then NULL is returned - otherwise a handle to the created file is * returned. / static void prvValidateFileToWrite( Sockett xSocket, struct freertossockaddr *pxClient, const char *pcFileName );

/* * Send an acknowledgement packet to pxClient with block number usBlockNumber. */ static void prvSendAcknowledgement( Sockett xSocket, struct freertossockaddr *pxClient, uint16_t usBlockNumber );

/* The index for the error string below MUST match the value of the applicable eTFTPErrorCode_t error code value. / static const char *cErrorStrings[] = { NULL, / Not valid. */ "File not found.", "Access violation.", "Disk full or allocation exceeded.", "Illegal TFTP operation.", "Unknown transfer ID.", "File already exists.", "No such user." };

/-----------------------------------------------------------/

void vStartTFTPServerTask( uint16t usStackSize, UBaseTypet uxPriority ) { /* A single server task is created. Currently this is only capable of managing one TFTP transfer at a time. / xTaskCreate( prvSimpleTFTPServerTask, "TFTPd", usStackSize, NULL, uxPriority, NULL ); } /-----------------------------------------------------------*/

static void prvSimpleTFTPServerTask( void *pvParameters ) { int32t lBytes; uint8t *pucUDPPayloadBuffer; struct freertossockaddr xClient, xBindAddress; uint32t xClientLength = sizeof( xClient ), ulIPAddress; Socket_t xTFTPListeningSocket; const char *pcFileName; void *pxFile; int HaveReleasedBuffer = 0;

/* Just to prevent compiler warnings. */
( void ) pvParameters;

/* Attempt to open the socket.  The receive block time defaults to the max
delay, so there is no need to set that separately. */
xTFTPListeningSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );
configASSERT( xTFTPListeningSocket != FREERTOS_INVALID_SOCKET );

/* Bind to the standard TFTP port. */
FreeRTOS_GetAddressConfiguration( &ulIPAddress, NULL, NULL, NULL );
xBindAddress.sin_addr = ulIPAddress;
xBindAddress.sin_port = FreeRTOS_htons( tftpPORT_NUMBER );
FreeRTOS_bind( xTFTPListeningSocket, &xBindAddress, sizeof( xBindAddress ) );

for( ;; )
{
	/* Look for the start of a new transfer on the TFTP port.  ulFlags has
	the zero copy bit set (FREERTOS_ZERO_COPY) indicating to the stack that
	a reference to the received data should be passed out to this task using
	the second parameter to the FreeRTOS_recvfrom() call.  When this is done
	the IP stack is no longer responsible for releasing the buffer, and the
	task *must* return the buffer to the stack when it is no longer
	needed. */
	lBytes = FreeRTOS_recvfrom( xTFTPListeningSocket, ( void * ) &pucUDPPayloadBuffer, 0, FREERTOS_ZERO_COPY, &xClient, &xClientLength );

	if( lBytes > 0 ) {

		/* Could this be a new write request?  The opcode is contained in
		the first two bytes of the received data. */

		HaveReleasedBuffer=0;

		if( ( pucUDPPayloadBuffer[ 0 ] == ( uint8_t ) 0 ) && ( pucUDPPayloadBuffer[ 1 ] == ( uint8_t ) eWriteRequest ) ) {
			pcFileName = prvValidateWriteRequest( xTFTPListeningSocket, &xClient, pucUDPPayloadBuffer );

			if( pcFileName != NULL )	{
				pxFile = prvValidateFileToWrite( xTFTPListeningSocket, &xClient, pcFileName );
				FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer );/* to do - better structure */
                HaveReleasedBuffer=1;
				if( pxFile != NULL ) {
					/* Manage reception of the reset of the file. */
					prvReceiveFile( pxFile, &xClient );
				}
				else {
					/* bad file name */
                    prvSendTFTPError( xTFTPListeningSocket, &xClient, eFileNotFound );
				}
			}
			else {
				prvSendTFTPError( xTFTPListeningSocket, &xClient, eFileNotFound );

			}
		}
		else {

			/* Not a transfer ID handled by this server. */
			prvSendTFTPError( xTFTPListeningSocket, &xClient, eUnknownTransferID );
		}

		if (!HaveReleasedBuffer) FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer );/* to do - better structure */
	}
	else {
		/* socket error  ?*/
	}
}

} /-----------------------------------------------------------/

static BaseTypet prvReceiveFile( void *pxFile, struct freertossockaddr *pxClient ) { BaseTypet xReturn = pdPASS, xRetries = 0; uint16t usExpectedBlockNumber; Sockett xTFTPRxSocket = FREERTOSINVALIDSOCKET; TickTypet xRxTimeout = pdMSTOTICKS( ipconfigTFTPTIMEOUTMS ); int32t lBytes; uint8t *pucFileBuffer; struct freertossockaddr xClient; uint32t xClientLength = sizeof( xClient ); TFTPBlockNumberHeadert *pxHeader; sizet xBlocksWritten, xBytesOfFileDataReceived = tftpMAXDATA_LENGTH;

const size_t xBlocksToWrite = 1;

/* The file is open for writing, now create the socket on which the file
will be received from the client.  Note the socket is not bound	here - so
will be automatically bound to a port number selected by the IP stack when
it is used for the first time. */
xTFTPRxSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );



if((xTFTPRxSocket != FREERTOS_INVALID_SOCKET )&&( xTFTPRxSocket != FREERTOS_INVALID_SOCKET )) {
	/* The socket's Rx block time is set to the user configurable timeout
	value. */
	FreeRTOS_setsockopt( xTFTPRxSocket, 0, FREERTOS_SO_RCVTIMEO, &xRxTimeout, sizeof( xRxTimeout ) );

	/* Acknowledge the write request so the client starts to send the file.
	The first acknowledgment does not have a corresponding block number so
	the special case block number 0 is used. */
	usExpectedBlockNumber = 0;

	/*
	after first return, seems that some TFTP clients can't turnaround fast enough for this embedded
	so we just delay a little more.....
	*/
	vTaskDelay(pdMS_TO_TICKS( 50 ));

	prvSendAcknowledgement( xTFTPRxSocket, pxClient, usExpectedBlockNumber );/* acknowledge block 0 (no data)  */


	do {
		/* Wait for next data packet.  Zero copy is used so it is the
		responsibility of this task to free the received data once it is no
		longer required. */
		lBytes = FreeRTOS_recvfrom( xTFTPRxSocket, ( void * ) &pucFileBuffer, 0, FREERTOS_ZERO_COPY, &xClient, &xClientLength );

		if( lBytes <= 0 ) {
			/* timed out ???? really - there is a 30 second timeout- it should have recovered.. give up now*/

			FreeRTOS_printf( ( "Error: Timeout.\n" ) );
if 1
			xRetries++;
			if( xRetries > ipconfigTFTP_MAX_RETRIES ) 	{
				FreeRTOS_printf( ( "Error: Retry limit exceeded.\n" ) );
				xReturn = pdFAIL;
			}
else
			xReturn = pdFAIL;
endif
		}

		else {
			/* Data received.  It is expected to be the next sequential
			block. */
			usExpectedBlockNumber++;
			pxHeader = ( TFTPBlockNumberHeader_t * ) pucFileBuffer;
			pxHeader->usOpcode = FreeRTOS_ntohs( pxHeader->usOpcode );
			pxHeader->usBlockNumber = FreeRTOS_ntohs( pxHeader->usBlockNumber );

			/* Is the data as expected and from the expected IP address and
			port? */
			if( ( pxHeader->usOpcode == ( uint16_t ) eData ) 		 &&
				( pxHeader->usBlockNumber == usExpectedBlockNumber ) &&
				( pxClient->sin_addr == xClient.sin_addr ) 			 &&
				( pxClient->sin_port == xClient.sin_port ) )
				{
				/* Everything in the packet other than the header is file
				data. */
				xBytesOfFileDataReceived = ( size_t ) lBytes - sizeof( TFTPBlockNumberHeader_t );
				FreeRTOS_printf( ( "Received %d bytes of file data.\n", ( int ) xBytesOfFileDataReceived ) );
if 1
				xReturn = CheckWriteVerifyRecvTFTPBlock( pucFileBuffer + sizeof( TFTPBlockNumberHeader_t ),
											xBytesOfFileDataReceived,
											(pxHeader->usBlockNumber)-1);
else

xReturn = 0; /* dry run without hitting flash */

endif
				FreeRTOS_ReleaseUDPPayloadBuffer( pucFileBuffer );

				if( xReturn != 0 ) {
					/* File could not be written. */
					prvSendTFTPError( xTFTPRxSocket, pxClient, eAccessViolation );
					xReturn = pdFAIL;
				}
				else {
   					/* Ack the data then write the data to the file. */
					prvSendAcknowledgement( xTFTPRxSocket, pxClient, usExpectedBlockNumber );
					xReturn = pdPASS;
					/* Start to receive the next block. */
					xRetries = 0;
				}
			}
			else 	{
				FreeRTOS_ReleaseUDPPayloadBuffer( pucFileBuffer );
				prvSendTFTPError( xTFTPRxSocket, pxClient, eIllegalTFTPOperation );
				xReturn = pdFAIL;
			}
		}

	  /* Until a disk write fails, or the maximum number of retries is
	  exceeded, or fewer bytes than tftpMAX_DATA_LENGTH are received (which
	  indicates the end of the file). */
	} while( ( xReturn != pdFAIL ) && ( xBytesOfFileDataReceived == tftpMAX_DATA_LENGTH ) );

	FreeRTOS_printf( ( "Closing connection.\n" ) );
	FreeRTOS_closesocket( xTFTPRxSocket );

}
else
{
	/* An error could be returned here, but it is probably cleaner to just
	time out as the error would have to be sent via the listening socket
	outside of this function. */
	FreeRTOS_printf( ( "Could not create socket to receive file.\n" ) );
}

return xReturn;

} /-----------------------------------------------------------/

void prvSendAcknowledgement( Sockett xSocket, struct freertossockaddr pxClient, uint16_t usBlockNumber ) { / Small fixed size buffer, so not much to be gained by using the zero copy interface, just send the buffer directly. */ TFTPBlockNumberHeader_t xAckMessage;

xAckMessage.usOpcode = FreeRTOS_htons( ( ( uint16_t ) eAck ) );
xAckMessage.usBlockNumber = FreeRTOS_htons( usBlockNumber );

FreeRTOS_sendto( xSocket, ( void * ) &xAckMessage, tftpACK_MESSAGE_LENGTH, 0, pxClient, sizeof( struct freertos_sockaddr ) );

} /-----------------------------------------------------------/

static void* prvValidateFileToWrite( Sockett xSocket, struct freertossockaddr *pxClient, const char *pcFileName ) { void *pxFile;

FreeRTOS_printf( ( "Write request for %s received\n", pcFileName ) );

/* The file cannot be received if it already exists.  Attempt to open the
file in read mode to see if it exists. */
#if 0
if (strcasecmp(pcFileName,"fpga2_k70t") == 0) return (void *)1;
else return (void *)0;
#else
	return (void *)1;/* any file... */
#endif

} /-----------------------------------------------------------/

static const char* prvValidateWriteRequest( Sockett xSocket, struct freertossockaddr *pxClient, uint8t *pucUDPPayloadBuffer ) { char *pcFileName; BaseTypet x; const char *pcOctedMode = "octet";

/* pcFileName is set to point to the file name which is inside the write
request frame, so its important not to free the frame until the operation is
over.  The start of the file name string is after the opcode, so two bytes
into the packet. */
pcFileName = ( char * ) &( pucUDPPayloadBuffer[ tftpFILE_NAME_OFFSET ] );
const int ffconfigMAX_FILENAME = 16;
/* Sanity check the file name. */
for( x = 0; x < ffconfigMAX_FILENAME; x++ )
{
	if( pcFileName[ x ] == 0x00 )
	{
		/* The end of the string was located. */
		break;
	}
	else if( ( pcFileName[ x ] < ' ' ) || ( pcFileName[ x ] > '~' ) )
	{
		/* Not a valid file name character. */
		pcFileName = NULL;
		break;
	}
	else
	{
		/* Just a character in the file name. */
	}
}

if( pcFileName != NULL )
{
	/* Only binary transfers are supported, indicated by an 'octet' mode
	string following the file name. +1 to move past the null terminator to
	the start of the next string. */
	x++;
	if( strcasecmp( pcOctedMode, ( const char * ) &( pucUDPPayloadBuffer[ tftpFILE_NAME_OFFSET + x ] ) ) != 0 )
	{
		/* Not the expected mode. */
		prvSendTFTPError( xSocket, pxClient, eIllegalTFTPOperation );
		pcFileName = NULL;
	}
}
else
{
	prvSendTFTPError( xSocket, pxClient, eFileNotFound );
}

return pcFileName;

} /-----------------------------------------------------------/

static void prvSendTFTPError( Sockett xSocket, struct freertossockaddr pxClient, eTFTPErrorCodet eErrorCode ) { uint8t *pucUDPPayloadBuffer = NULL; const sizet xFixedSizePart = ( sizet ) 5; / 2 byte opcode, plus two byte error code, plus string terminating 0. / const sizet xNumberOfErrorStrings = sizeof( cErrorStrings ) / sizeof( char * ); sizet xErrorCode = ( size_t ) eErrorCode, xTotalLength = 0; / Only initialised to keep compiler quiet. */ const char *pcErrorString = NULL; int32_t lReturned;

/* The total size of the packet to be sent depends on the length of the
error string. */
if( xErrorCode < xNumberOfErrorStrings )
{
	pcErrorString = cErrorStrings[ xErrorCode ];

	/* This task is going to send using the zero copy interface.  The data
	being sent is therefore written directly into a buffer that is passed
	into, rather than copied into, the FreeRTOS_sendto() function.  First
	obtain a buffer of adequate length from the IP stack into which the
	error packet will be written.  Although a max delay is used, the actual
	delay will be capped to ipconfigMAX_SEND_BLOCK_TIME_TICKS. */
	xTotalLength = strlen( pcErrorString ) + xFixedSizePart;
	pucUDPPayloadBuffer = ( uint8_t * ) FreeRTOS_GetUDPPayloadBuffer( xTotalLength, portMAX_DELAY );
}

if( pucUDPPayloadBuffer != NULL )
{
	FreeRTOS_printf( ( "Error: %s\n", pcErrorString ) );

	/* Create error packet: Opcode. */
	pucUDPPayloadBuffer[ 0 ] = 0;
	pucUDPPayloadBuffer[ 1 ] = ( uint8_t ) eError;

	/* Create error packet: Error code. */
	pucUDPPayloadBuffer[ 2 ] = 0;
	pucUDPPayloadBuffer[ 3 ] = ( uint8_t ) eErrorCode;

	/* Create error packet: Error string. */
	strcpy( ( ( char * ) &( pucUDPPayloadBuffer[ 4 ] ) ), pcErrorString );

	/* Pass the buffer into the send function.  ulFlags has the
	FREERTOS_ZERO_COPY bit set so the IP stack will take control of the
	buffer rather than copy data out of the buffer. */
	lReturned = FreeRTOS_sendto( xSocket,  						/* The socket to which the error frame is sent. */
								( void * ) pucUDPPayloadBuffer, /* A pointer to the the data being sent. */
								xTotalLength, 					/* The length of the data being sent. */
								FREERTOS_ZERO_COPY, 			/* ulFlags with the FREERTOS_ZERO_COPY bit set. */
								pxClient, 			/* Where the data is being sent. */
								sizeof( *pxClient ) );

	if( lReturned == 0 )
	{
		/* The send operation failed, so this task is still responsible
		for the buffer obtained from the IP stack.  To ensure the buffer
		is not lost it must either be used again, or, as in this case,
		returned to the IP stack using FreeRTOS_ReleaseUDPPayloadBuffer(). */
		FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayloadBuffer );
	}
	else
	{
		/* The send was successful so the IP stack is now managing the
		buffer pointed to by pucUDPPayloadBuffer, and the IP stack will
		return the buffer once it has been sent. */
	}
}

}

~~~


TFTPserver improvements

Posted by rtel on September 25, 2016

Great thanks. Sorry it took so long for your post to appear - for some reason it went into moderation and we don't receive notifications when that happens (it should happen in the first place).


TFTPserver improvements

Posted by glenenglish on September 30, 2016

No sweat. couldnt figure out how to attach a file ?


[ Back to the top ]    [ About FreeRTOS ]    [ Privacy ]    [ Sitemap ]    [ ]


Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.

Latest News

NXP tweet showing LPC5500 (ARMv8-M Cortex-M33) running FreeRTOS.

Meet Richard Barry and learn about running FreeRTOS on RISC-V at FOSDEM 2019

Version 10.1.1 of the FreeRTOS kernel is available for immediate download. MIT licensed.

View a recording of the "OTA Update Security and Reliability" webinar, presented by TI and AWS.


Careers

FreeRTOS and other embedded software careers at AWS.



FreeRTOS Partners

ARM Connected RTOS partner for all ARM microcontroller cores

Espressif ESP32

IAR Partner

Microchip Premier RTOS Partner

RTOS partner of NXP for all NXP ARM microcontrollers

Renesas

STMicro RTOS partner supporting ARM7, ARM Cortex-M3, ARM Cortex-M4 and ARM Cortex-M0

Texas Instruments MCU Developer Network RTOS partner for ARM and MSP430 microcontrollers

OpenRTOS and SafeRTOS

Xilinx Microblaze and Zynq partner