glenenglish wrote on Thursday, 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 <stdint.h>
#include <stdio.h>
#include <string.h>
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
/* FreeRTOS+TCP includes. */
#include "FreeRTOS_IP.h"
#include "FreeRTOS_Sockets.h"
#if( ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND != 1 )
#error ipconfigALLOW_SOCKET_SEND_WITHOUT_BIND must be set to one to use this TFTP server.
#endif
#if( configTICK_RATE_HZ > 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 ipconfigTFTP_TIME_OUT_MS
#define ipconfigTFTP_TIME_OUT_MS ( 30000 )
#endif
#ifndef ipconfigTFTP_MAX_RETRIES
#define ipconfigTFTP_MAX_RETRIES ( 6 )
#endif
#define tftpPORT_NUMBER ( ( uint16_t ) 69 )
#define tftpFILE_NAME_OFFSET ( 2 )
#define tftpACK_MESSAGE_LENGTH 4
#define tftpMAX_DATA_LENGTH ( ( 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 "pack_struct_start.h"
struct DataPacketHeader
{
uint16_t usOpcode;
uint16_t usBlockNumber;
}
#include "pack_struct_end.h"
typedef struct DataPacketHeader TFTPBlockNumberHeader_t;
/* this will be the flash write routine */
extern size_t ff_fwrite(void * data, size_t nBytes, size_t 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 BaseType_t prvReceiveFile( void *pxFile, struct freertos_sockaddr *pxClient );
/*
* Send an error frame to the client.
*/
static void prvSendTFTPError( Socket_t xSocket, struct freertos_sockaddr *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( Socket_t xSocket, struct freertos_sockaddr *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( Socket_t xSocket, struct freertos_sockaddr *pxClient, const char *pcFileName );
/*
* Send an acknowledgement packet to pxClient with block number usBlockNumber.
*/
static void prvSendAcknowledgement( Socket_t xSocket, struct freertos_sockaddr *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( uint16_t usStackSize, UBaseType_t 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 )
{
int32_t lBytes;
uint8_t *pucUDPPayloadBuffer;
struct freertos_sockaddr xClient, xBindAddress;
uint32_t 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 BaseType_t prvReceiveFile( void *pxFile, struct freertos_sockaddr *pxClient )
{
BaseType_t xReturn = pdPASS, xRetries = 0;
uint16_t usExpectedBlockNumber;
Socket_t xTFTPRxSocket = FREERTOS_INVALID_SOCKET;
TickType_t xRxTimeout = pdMS_TO_TICKS( ipconfigTFTP_TIME_OUT_MS );
int32_t lBytes;
uint8_t *pucFileBuffer;
struct freertos_sockaddr xClient;
uint32_t xClientLength = sizeof( xClient );
TFTPBlockNumberHeader_t *pxHeader;
size_t xBlocksWritten, xBytesOfFileDataReceived = tftpMAX_DATA_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( Socket_t xSocket, struct freertos_sockaddr *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( Socket_t xSocket, struct freertos_sockaddr *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( Socket_t xSocket, struct freertos_sockaddr *pxClient, uint8_t *pucUDPPayloadBuffer )
{
char *pcFileName;
BaseType_t 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( Socket_t xSocket, struct freertos_sockaddr *pxClient, eTFTPErrorCode_t eErrorCode )
{
uint8_t *pucUDPPayloadBuffer = NULL;
const size_t xFixedSizePart = ( size_t ) 5; /* 2 byte opcode, plus two byte error code, plus string terminating 0. */
const size_t xNumberOfErrorStrings = sizeof( cErrorStrings ) / sizeof( char * );
size_t 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. */
}
}
}