Click here to Skip to main content
15,896,269 members
Articles / Mobile Apps

TCP/IP Stack, FAT16 System on a Microcontroller

Rate me:
Please Sign up or sign in to vote.
4.53/5 (10 votes)
31 Jul 2011GPL35 min read 43.5K   2.1K   36  
TCP/IP Stack, FAT16 System on a Microcontroller
/* This source file is part of the ATMEL AVR-UC3-SoftwareFramework-1.7.0 Release */

/*This file has been prepared for Doxygen automatic documentation generation.*/
/*! \file *********************************************************************
 *
 * \brief Basic TFTP Server for AVR32 UC3.
 *
 * - Compiler:           GNU GCC for AVR32
 * - Supported devices:  All AVR32 devices can be used.
 * - AppNote:
 *
 * \author               Atmel Corporation: http://www.atmel.com \n
 *                       Support and FAQ: http://support.atmel.no/
 *
 *****************************************************************************/

/* Copyright (c) 2009 Atmel Corporation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. The name of Atmel may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an Atmel
 * AVR product.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
 *
 */

/*
  Implements a simplistic TFTP server.
  
  In order to put data on the TFTP server (not over 2048 bytes)
  tftp 192.168.0.2 PUT <src_filename>
  this will copy file from your hard drive to the RAM buffer of the application

  tftp 192.168.0.2 GET <dst_filename>
  this will copy file from the RAM buffer of the application to your hard drive
  You can then check that src_filename and dst_filename are identical    
*/

#if (TFTP_USED == 1)

#include <string.h>


/* Scheduler includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "partest.h"
#include "BasicTFTP.h"


/* Demo includes. */
#include "portmacro.h"

/* lwIP includes. */
#include "lwip/api.h"
#include "lwip/tcpip.h"
#include "lwip/memp.h"
#include "lwip/stats.h"
#include "lwip/opt.h"
#include "lwip/api.h"
#include "lwip/arch.h"
#include "lwip/sys.h"
#include "netif/loopif.h"
#include "lwip/sockets.h"

#define O_WRONLY 1
#define O_RDONLY 2


/* The port on which we listen. */
#define TFTP_PORT    ( 69 )

/* Delay on close error. */
#define TFTP_DELAY         ( 10 )

/* Delay on bind error. */
#define TFTP_ERROR_DELAY    ( 40 )

#define TFTP_LED     ( 4 )

char data_out[SEGSIZE+sizeof(struct tftphdr)];
char data_in[SEGSIZE+sizeof(struct tftphdr)];

//struct tftp_server *server;

/*------------------------------------------------------------*/
static char * errmsg[] = {
  "Undefined error code",               // 0 nothing defined
  "File not found",                     // 1 TFTP_ENOTFOUND 
  "Access violation",                   // 2 TFTP_EACCESS   
  "Disk full or allocation exceeded",   // 3 TFTP_ENOSPACE  
  "Illegal TFTP operation",             // 4 TFTP_EBADOP    
  "Unknown transfer ID",                // 5 TFTP_EBADID    
  "File already exists",                // 6 TFTP_EEXISTS   
  "No such user",                       // 7 TFTP_ENOUSER   
};


/* Send an error packet to the client */
static void 
tftpd_send_error(int s, struct tftphdr * reply, int err,
     struct sockaddr_in *from_addr, int from_len)
{
    if ( ( 0 <= err ) && ( sizeof(errmsg)/sizeof(errmsg[0]) > err) ) {
    reply->th_opcode = htons(ERROR);
    reply->th_code = htons(err);
    if ( (0 > err) || (sizeof(errmsg)/sizeof(errmsg[0]) <= err) )
        err = 0; // Do not copy a random string from hyperspace
    strcpy(reply->th_msg, errmsg[err]);
    sendto(s, reply, 4+strlen(reply->th_msg)+1, 0, 
     (struct sockaddr *)from_addr, from_len);
    }
}

portCHAR cRamBuffer[2048];
int lCurrentBlock = 0;
int lTotalLength = 0;


int tftpd_close_data_file(int fd)
{
  lCurrentBlock = 0;
  return (5);
}

int tftpd_open_data_file(int fd, int mode)
{
  lCurrentBlock = 0; 
  return (5);
}

int tftpd_read_data_file(int fd, portCHAR * buffer, int length)
{
int lReturnValue;

  if ((lTotalLength -= length) >= 0) {
    lReturnValue = length;
  }
  else
  {
    lReturnValue = lTotalLength + length;
    lTotalLength = 0;
  }
  memcpy(buffer, &cRamBuffer[lCurrentBlock * SEGSIZE], lReturnValue);
  lCurrentBlock++;
  return (lReturnValue);
}

//
// callback to store data to the RAM buffer
//
int tftpd_write_data_file(int fd, portCHAR * buffer, int length)
{
  lTotalLength += length;
  memcpy(&cRamBuffer[lCurrentBlock * SEGSIZE], buffer, length);
  lCurrentBlock++;
  return (length);
}

//
// Receive a file from the client
//
static void
tftpd_write_file(struct tftphdr *hdr,
                 struct sockaddr_in *from_addr, int from_len)
{

    struct tftphdr *reply = (struct tftphdr *)data_out;
    struct tftphdr *response = (struct tftphdr *)data_in;
    int fd, len, ok, tries, closed, data_len, s;
    unsigned short block;
    struct timeval timeout;
    fd_set fds;
    int total_timeouts = 0;
    struct sockaddr_in client_addr, local_addr;
    socklen_t client_len;


    s = socket(AF_INET, SOCK_DGRAM, 0);
    if (s < 0) {
        return;
    }

    memset((char *)&local_addr, 0, sizeof(local_addr));
    local_addr.sin_family = AF_INET;
    local_addr.sin_len = sizeof(local_addr);
    local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    local_addr.sin_port = htons(INADDR_ANY);

    if (bind(s, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
        // Problem setting up my end
        close(s);
        return;
    }

    if ((fd = tftpd_open_data_file((int)hdr->th_stuff, O_WRONLY)) < 0) {
        tftpd_send_error(s,reply,TFTP_ENOTFOUND,from_addr, from_len);
        close(s);
        return;
    }

    ok = pdTRUE;
    closed = pdFALSE;
    block = 0;
    while (ok) {
        // Send ACK telling client he can send data
        reply->th_opcode = htons(ACK);
        reply->th_block = htons(block++); // postincrement
        for (tries = 0;  tries < TFTP_RETRIES_MAX;  tries++) {
            sendto(s, reply, 4, 0, (struct sockaddr *)from_addr, from_len);
        repeat_select:
            timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
            timeout.tv_usec = 0;
            FD_ZERO(&fds);
            FD_SET(s, &fds);
           vParTestToggleLED( TFTP_LED );
           if (lwip_select(s+1, &fds, 0, 0, &timeout) <= 0) {
                if (++total_timeouts > TFTP_TIMEOUT_MAX) {
                    tftpd_send_error(s,reply,TFTP_EBADOP,from_addr, from_len);
                    ok = pdFALSE;
                    break;
                }
                continue; // retry the send, using up one retry.
            }
            vParTestToggleLED( TFTP_LED );
            // Some data has arrived
            data_len = sizeof(data_in);
            client_len = sizeof(client_addr);
            if ((data_len = recvfrom(s, data_in, data_len, 0, 
                      (struct sockaddr *)&client_addr, &client_len)) < 0) {
                // What happened?  No data here!
                continue; // retry the send, using up one retry.
            }
            if (ntohs(response->th_opcode) == DATA &&
                ntohs(response->th_block) < block) {
                // Then it is repeat DATA with an old block; listen again,
                // but do not repeat sending the current ack, and do not
                // use up a retry count.  (we do re-send the ack if
                // subsequently we time out)
                goto repeat_select;
            }
            if (ntohs(response->th_opcode) == DATA &&
                ntohs(response->th_block) == block) {
                // Good data - write to file
                len = tftpd_write_data_file(fd, response->th_data, data_len-4);
                if (len < (data_len-4)) {
                    // File is "full"
                    tftpd_send_error(s,reply,TFTP_ENOSPACE,
                                     from_addr, from_len);     
                    ok = pdFALSE;  // Give up
                    break; // out of the retries loop
                }
                if (data_len < (SEGSIZE+4)) {
                    // End of file
                    closed = pdTRUE;
                    ok = pdFALSE;
                    vParTestSetLED( 0 , 0 );

                    if (tftpd_close_data_file(fd) == -1) {
                        tftpd_send_error(s,reply,TFTP_EACCESS,
                                         from_addr, from_len);

                       break;  // out of the retries loop
                    }
                    // Exception to the loop structure: we must ACK the last
                    // packet, the one that implied EOF:
                    reply->th_opcode = htons(ACK);
                    reply->th_block = htons(block++); // postincrement
                    sendto(s, reply, 4, 0, (struct sockaddr *)from_addr, from_len);
                    break; // out of the retries loop
                }
                // Happy!  Break out of the retries loop.
                break;
            }
        } // End of the retries loop.
        if (TFTP_RETRIES_MAX <= tries) {
            tftpd_send_error(s,reply,TFTP_EBADOP,from_addr, from_len);
            ok = pdFALSE;
        }
    }
    close(s);
    if (!closed) {
      tftpd_close_data_file(fd);
    }
}


//
// Send a file to the client
//
static void
tftpd_read_file(struct tftphdr *hdr,
                struct sockaddr_in *from_addr, int from_len)
{
    struct tftphdr *reply = (struct tftphdr *)data_out;
    struct tftphdr *response = (struct tftphdr *)data_in;
    int fd, len, tries, ok, data_len, s;
    unsigned short block;
    struct timeval timeout;
    fd_set fds;
    int total_timeouts = 0;
    struct sockaddr_in client_addr, local_addr;
    socklen_t client_len;

    s = socket(AF_INET, SOCK_DGRAM, 0);
    if (s < 0) {
        return;
    }
    memset((char *)&local_addr, 0, sizeof(local_addr));
    local_addr.sin_family = AF_INET;
    local_addr.sin_len = sizeof(local_addr);
    local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    local_addr.sin_port = htons(INADDR_ANY);
    if (bind(s, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
        // Problem setting up my end
        close(s);
        return;
    }
    if ((fd = tftpd_open_data_file((int)hdr->th_stuff, O_RDONLY)) < 0) {
        tftpd_send_error(s,reply,TFTP_ENOTFOUND,from_addr, from_len);
        close(s);
        return;
    }
    block = 0;
    ok = pdTRUE;
    while (ok) {
        // Read next chunk of file
        len = tftpd_read_data_file(fd, reply->th_data, SEGSIZE);
        reply->th_block = htons(++block); // preincrement
        reply->th_opcode = htons(DATA);
        for (tries = 0;  tries < TFTP_RETRIES_MAX;  tries++) {
            if (sendto(s, reply, 4+len, 0,
                       (struct sockaddr *)from_addr, from_len) < 0) {
                // Something went wrong with the network!
                ok = pdFALSE;
                break;
            }
        repeat_select:
            timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
            timeout.tv_usec = 0;
            FD_ZERO(&fds);
            FD_SET(s, &fds);
            vParTestToggleLED( TFTP_LED );
            if (select(s+1, &fds, 0, 0, &timeout) <= 0) {
                if (++total_timeouts > TFTP_TIMEOUT_MAX) {
                    tftpd_send_error(s,reply,TFTP_EBADOP,from_addr, from_len);
                    ok = pdFALSE;
                    break;
                }
                continue; // retry the send, using up one retry.
            }
            vParTestToggleLED( TFTP_LED );
            data_len = sizeof(data_in);
            client_len = sizeof(client_addr);
            if ((data_len = recvfrom(s, data_in, data_len, 0, 
                                     (struct sockaddr *)&client_addr,
                                     &client_len)) < 0) {
                // What happened?  Maybe someone lied to us...
                continue; // retry the send, using up one retry.
            }
            if ((ntohs(response->th_opcode) == ACK) &&
                (ntohs(response->th_block) < block)) {
                // Then it is a repeat ACK for an old block; listen again,
                // but do not repeat sending the current block, and do not
                // use up a retry count.  (we do re-send the data if
                // subsequently we time out)
                goto repeat_select;
            }
            if ((ntohs(response->th_opcode) == ACK) &&
                (ntohs(response->th_block) == block)) {
                // Happy!  Break out of the retries loop.
                break;
            }
        } // End of the retries loop.
        if (TFTP_RETRIES_MAX <= tries) {
            tftpd_send_error(s,reply,TFTP_EBADOP,from_addr, from_len);
            ok = pdFALSE;
        }
        if (len < SEGSIZE) {
            break; // That's end of file then.
        }
    }
    close(s);
    tftpd_close_data_file(fd);
}



portTASK_FUNCTION( vBasicTFTPServer, pvParameters )
{
    int lSocket;
    int lDataLen, lRecvLen;
    socklen_t lFromLen;
    struct sockaddr_in sLocalAddr, sFromAddr;
    portCHAR cData[SEGSIZE+sizeof(struct tftphdr)];
    struct tftphdr *sHdr = (struct tftphdr *)cData;

    // Set up port
    // Network order in info; host order in server:

    for (;;) {
        // Create socket
        lSocket = socket(AF_INET, SOCK_DGRAM, 0);
        if (lSocket < 0) {
            return;
        }
        memset((char *)&sLocalAddr, 0, sizeof(sLocalAddr));
        sLocalAddr.sin_family = AF_INET;
        sLocalAddr.sin_len = sizeof(sLocalAddr);
        sLocalAddr.sin_addr.s_addr = htonl(INADDR_ANY);
        sLocalAddr.sin_port = TFTP_PORT;

        if (bind(lSocket, (struct sockaddr *)&sLocalAddr, sizeof(sLocalAddr)) < 0) {
            // Problem setting up my end
            close(lSocket);
            return;
        }


        lRecvLen = sizeof(cData);
        lFromLen = sizeof(sFromAddr);
        lDataLen = recvfrom(lSocket, sHdr, lRecvLen, 0,
                            (struct sockaddr *)&sFromAddr, &lFromLen);
        vParTestSetLED( TFTP_LED , pdTRUE );
        close(lSocket); // so that other servers can bind to the TFTP socket

        if ( lDataLen < 0) {

        } else {
            switch (ntohs(sHdr->th_opcode)) {
            case WRQ:
                tftpd_write_file(sHdr, &sFromAddr, lFromLen);
                vParTestSetLED( TFTP_LED , pdFALSE );
                break;
            case RRQ:
                tftpd_read_file(sHdr, &sFromAddr, lFromLen);
                vParTestSetLED( TFTP_LED , pdFALSE );
                break;
            case ACK:
            case DATA:
            case ERROR:
                vParTestSetLED( TFTP_LED , pdFALSE );
                // Ignore
                break;
            default:
                for(;;)
                {
                  vParTestToggleLED( TFTP_LED );
                  vTaskDelay(200);                    
                }
             }
        }
    }
}
#endif

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Software Developer
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions