From 990090a4cc9070837d31e66b58d40f0c3d038741 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Sun, 16 Mar 2014 14:41:11 +0200 Subject: Add usblib and utils --- utils/tftp.c | 710 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 710 insertions(+) create mode 100644 utils/tftp.c (limited to 'utils/tftp.c') diff --git a/utils/tftp.c b/utils/tftp.c new file mode 100644 index 0000000..674c307 --- /dev/null +++ b/utils/tftp.c @@ -0,0 +1,710 @@ +//***************************************************************************** +// +// tftp.c - A very simple lwIP TFTP server. +// +// Copyright (c) 2009-2014 Texas Instruments Incorporated. All rights reserved. +// Software License Agreement +// +// Texas Instruments (TI) is supplying this software for use solely and +// exclusively on TI's microcontroller products. The software is owned by +// TI and/or its suppliers, and is protected under applicable copyright +// laws. You may not combine this software with "viral" open-source +// software in order to form a larger program. +// +// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS. +// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT +// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY +// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL +// DAMAGES, FOR ANY REASON WHATSOEVER. +// +// This is part of revision 2.1.0.12573 of the Tiva Utility Library. +// +//***************************************************************************** + +#include +#include +#include +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "utils/uartstdio.h" +#include "utils/lwiplib.h" +#include "utils/ustdlib.h" + +//***************************************************************************** +// +//! \addtogroup tftp_api +//! @{ +// +//***************************************************************************** +#include "utils/tftp.h" + +//***************************************************************************** +// +// The TFTP commands. +// +//***************************************************************************** +#define TFTP_RRQ 1 +#define TFTP_WRQ 2 +#define TFTP_DATA 3 +#define TFTP_ACK 4 +#define TFTP_ERROR 5 + +//***************************************************************************** +// +// The UDP port for the TFTP server. +// +//***************************************************************************** +#define TFTP_PORT 69 + +//***************************************************************************** +// +// Application connection notification callback. +// +//***************************************************************************** +static tTFTPRequest g_pfnRequest; + +//***************************************************************************** +// +// Close the TFTP connection and free associated resources. +// +//***************************************************************************** +static void +TFTPClose(tTFTPConnection *psTFTP) +{ + // + // Tell the application we are closing the connection. + // + if(psTFTP->pfnClose) + { + psTFTP->pfnClose(psTFTP); + } + + // + // Close the underlying UDP connection. + // + udp_remove(psTFTP->psPCB); + + // + // Free the instance data structure. + // + mem_free(psTFTP); +} + +//***************************************************************************** +// +// Sends a TFTP error packet. +// +//***************************************************************************** +static void +TFTPErrorSend(tTFTPConnection *psTFTP, tTFTPError eError) +{ + uint32_t ui32Length; + uint8_t *pui8Data; + struct pbuf *p; + + // + // How big is this packet going to be? + // + ui32Length = 5 + strlen(psTFTP->pcErrorString); + + // + // Allocate a pbuf for this data packet. + // + p = pbuf_alloc(PBUF_TRANSPORT, ui32Length, PBUF_RAM); + if(!p) + { + return; + } + + // + // Get a pointer to the data packet. + // + pui8Data = (uint8_t *)p->payload; + + // + // Fill in the packet. + // + pui8Data[0] = (TFTP_ERROR >> 8) & 0xff; + pui8Data[1] = TFTP_ERROR & 0xff; + pui8Data[2] = ((uint32_t)eError >> 8) & 0xff; + pui8Data[3] = (uint32_t)eError & 0xff; + memcpy(&pui8Data[4], psTFTP->pcErrorString, ui32Length - 5); + + // + // Send the data packet. + // + udp_send(psTFTP->psPCB, p); + + // + // Free the pbuf. + // + pbuf_free(p); +} + +//***************************************************************************** +// +// Sends a TFTP data packet. +// +//***************************************************************************** +static void +TFTPDataSend(tTFTPConnection *psTFTP) +{ + uint32_t ui32Length; + uint8_t *pui8Data; + tTFTPError eError; + struct pbuf *p; + + // + // Determine the number of bytes to place into this packet. + // + if(psTFTP->ui32DataRemaining < (psTFTP->ui32BlockNum * TFTP_BLOCK_SIZE)) + { + ui32Length = psTFTP->ui32DataRemaining & (TFTP_BLOCK_SIZE - 1); + } + else + { + ui32Length = TFTP_BLOCK_SIZE; + } + + // + // Allocate a pbuf for this data packet. + // + p = pbuf_alloc(PBUF_TRANSPORT, ui32Length + 4, PBUF_RAM); + if(!p) + { + return; + } + + // + // Get a pointer to the data packet. + // + pui8Data = (uint8_t *)p->payload; + + // + // Fill in the packet header. + // + pui8Data[0] = (TFTP_DATA >> 8) & 0xff; + pui8Data[1] = TFTP_DATA & 0xff; + pui8Data[2] = (psTFTP->ui32BlockNum >> 8) & 0xff; + pui8Data[3] = psTFTP->ui32BlockNum & 0xff; + + // + // Ask the application to provide the data we need. + // + psTFTP->pui8Data = pui8Data + 4; + psTFTP->ui32DataLength = ui32Length; + eError = psTFTP->pfnGetData(psTFTP); + + // + // Send the data packet or, if an error was reported, send an error. + // + if(eError == TFTP_OK) + { + udp_send(psTFTP->psPCB, p); + } + else + { + TFTPErrorSend(psTFTP, eError); + TFTPClose(psTFTP); + } + + // + // Free the pbuf. + // + pbuf_free(p); +} + +//***************************************************************************** +// +// Send an ACK packet back to the TFTP client. +// +//***************************************************************************** +static void +TFTPDataAck(tTFTPConnection *psTFTP) +{ + uint8_t *pui8Data; + struct pbuf *p; + + // + // Allocate a pbuf for this data packet. + // + p = pbuf_alloc(PBUF_TRANSPORT, 4, PBUF_RAM); + if(!p) + { + return; + } + + // + // Get a pointer to the data packet. + // + pui8Data = (uint8_t *)p->payload; + + // + // Fill in the packet header. + // + pui8Data[0] = (TFTP_ACK >> 8) & 0xff; + pui8Data[1] = TFTP_ACK & 0xff; + pui8Data[2] = (psTFTP->ui32BlockNum >> 8) & 0xff; + pui8Data[3] = psTFTP->ui32BlockNum & 0xff; + + // + // Send the data packet. + // + udp_send(psTFTP->psPCB, p); + + // + // Free the pbuf. + // + pbuf_free(p); +} + +//***************************************************************************** +// +// Handles datagrams received from the TFTP data connection. +// +//***************************************************************************** +static void +TFTPDataRecv(void *arg, struct udp_pcb *upcb, struct pbuf *p, + struct ip_addr *addr, u16_t port) +{ + uint8_t *pui8Data; + uint32_t ui32Block; + struct pbuf *pBuf; + tTFTPConnection *psTFTP; + tTFTPError eRetcode; + + // + // Initialize our return code. + // + eRetcode = TFTP_ERR_NOT_DEFINED; + + // + // Get a pointer to the connection instance data. + // + psTFTP = (tTFTPConnection *)arg; + + // + // Get a pointer to the TFTP packet. + // + pui8Data = (uint8_t *)(p->payload); + + // + // If this is an ACK packet, send back the next block to satisfy an + // ongoing GET (read) request. + // + if((pui8Data[0] == ((TFTP_ACK >> 8) & 0xff)) && + (pui8Data[1] == (TFTP_ACK & 0xff))) + { + // + // Extract the block number from the acknowledge. + // + ui32Block = (pui8Data[2] << 8) + pui8Data[3]; + + // + // DEBUG ONLY! + // + UARTprintf("ACK %d\n", ui32Block); + + // + // See if there is more data to be sent. Note that we need the "<=" + // here to ensure that we send back a zero length packet in the case + // that the file is a multiple of 512 bytes (in other words, the last + // packet of valid data was a full packet). + // + if((ui32Block * TFTP_BLOCK_SIZE) <= psTFTP->ui32DataRemaining) + { + // + // Send the next block of the file. + // + psTFTP->ui32BlockNum = ui32Block + 1; + TFTPDataSend(psTFTP); + } + else + { + // + // The transfer is complete, so close the data connection. + // + TFTPClose(psTFTP); + psTFTP = NULL; + } + } + else + { + // + // If this is a DATA packet, get the payload and write it to the + // appropriate location in the serial flash. + // + if((pui8Data[0] == ((TFTP_DATA >> 8) & 0xff)) && + (pui8Data[1] == (TFTP_DATA & 0xff))) + { + // + // This is a data packet. Extract the block number from the packet + // and set the offset within the block (stored in + // ui32DataRemaining) to zero. + // + psTFTP->ui32BlockNum = (pui8Data[2] << 8) + pui8Data[3]; + psTFTP->ui32DataRemaining = 0; + psTFTP->ui32DataLength = p->len - 4; + + // + // Pass the data back to the application for handling. Remember + // that the data may be stored across several pbufs in the chain. + // We can't assume it is in a contiguous block. + // + psTFTP->pui8Data = pui8Data + 4; + pBuf = p; + + // + // Keep writing until we run out of data. + // + while(pBuf) + { + // + // Pass this block to the application. + // + eRetcode = psTFTP->pfnPutData(psTFTP); + + // + // Was the data written successfully? + // + if(eRetcode != TFTP_OK) + { + // + // No - drop out. + // + break; + } + + // + // Update the offset so that it is correct for the next pbuf + // in the chain. + // + psTFTP->ui32DataRemaining += psTFTP->ui32DataLength; + + // + // Move to the next pbuf in the chain + // + pBuf = pBuf->next; + if(pBuf) + { + psTFTP->pui8Data = pBuf->payload; + psTFTP->ui32DataLength = pBuf->len; + } + } + + // + // If we get here and there was an error reported, pass the error + // back to the TFTP client. + // + if(psTFTP && (eRetcode != TFTP_OK)) + { + // + // Send the error code to the client. + // + TFTPErrorSend(psTFTP, eRetcode); + + // + // Close the connection. + // + TFTPClose(psTFTP); + psTFTP = NULL; + } + else + { + // + // Acknowledge this block. + // + TFTPDataAck(psTFTP); + + // + // Is the transfer finished? + // + if(p->tot_len < (TFTP_BLOCK_SIZE + 4)) + { + // + // We got a short packet so the transfer is complete. + // Close the connection. + // + TFTPClose(psTFTP); + psTFTP = NULL; + } + } + } + else + { + // + // Is the client reporting an error? + // + if((pui8Data[0] == ((TFTP_ERROR >> 8) & 0xff)) && + (pui8Data[1] == (TFTP_ERROR & 0xff))) + { + // + // Yes - we got an error so close the connection. + // + TFTPClose(psTFTP); + psTFTP = NULL; + } + } + } + + // + // Free the pbuf. + // + pbuf_free(p); +} + +//***************************************************************************** +// +// Parses the request string to determine the transfer mode, netascii, octet or +// mail, for this request. +// +//***************************************************************************** +static tTFTPMode +TFTPModeGet(uint8_t *pui8Request, uint32_t ui32Len) +{ + uint32_t ui32Loop, ui32Max; + + // + // Look for the first zero after the start of the filename string (skipping + // the first two bytes of the request packet). + // + for(ui32Loop = 2; ui32Loop < ui32Len; ui32Loop++) + { + if(pui8Request[ui32Loop] == (uint8_t)0) + { + break; + } + } + + // + // Skip past the zero. + // + ui32Loop++; + + // + // Did we run off the end of the string? + // + if(ui32Loop >= ui32Len) + { + // + // Yes - this appears to be an invalid request. + // + return(TFTP_MODE_INVALID); + } + + // + // How much data do we have left to look for the mode string? + // + ui32Max = ui32Len - ui32Loop; + + // + // Now determine which of the modes this request asks for. Is it ASCII? + // + if(!ustrncasecmp("netascii", (char *)&pui8Request[ui32Loop], ui32Max)) + { + // + // This is an ASCII file transfer. + // + return(TFTP_MODE_NETASCII); + } + + // + // Binary transfer? + // + if(!ustrncasecmp("octet", (char *)&pui8Request[ui32Loop], ui32Max)) + { + // + // This is a binary file transfer. + // + return(TFTP_MODE_OCTET); + } + + // + // All other strings are invalid or obsolete ("mail" for example). + // + return(TFTP_MODE_INVALID); +} + +//***************************************************************************** +// +// Handles datagrams received on the TFTP server port. +// +//***************************************************************************** +static void +TFTPRecv(void *arg, struct udp_pcb *upcb, struct pbuf *p, struct ip_addr *addr, + u16_t port) +{ + uint8_t *pui8Data; + bool bGetRequest; + tTFTPMode eMode; + tTFTPError eRetcode; + tTFTPConnection *psTFTP; + + // + // Get a pointer to the TFTP packet. + // + pui8Data = (uint8_t *)(p->payload); + + // + // Is this a read (GET) request? + // + if((pui8Data[0] == ((TFTP_RRQ >> 8) & 0xff)) && + (pui8Data[1] == (TFTP_RRQ & 0xff))) + { + // + // Yes - remember that this is a GET request. + // + bGetRequest = true; + } + + // + // Is this a write (PUT) request? + // + else if((pui8Data[0] == ((TFTP_WRQ >> 8) & 0xff)) && + (pui8Data[1] == (TFTP_WRQ & 0xff))) + { + // + // Yes - remember that this is a PUT request. + // + bGetRequest = false; + } + else + { + // + // The request is neither GET nor PUT so just ignore it. + // + pbuf_free(p); + return; + } + + // + // What is the mode for this request? + // + eMode = TFTPModeGet(pui8Data, p->len); + + // + // Was the transfer mode valid? + // + if(eMode != TFTP_MODE_INVALID) + { + // + // The transfer mode is valid so allocate a new connection instance + // and pass this to the client to have it tell us how to proceed. + // + psTFTP = (tTFTPConnection *)mem_malloc(sizeof(tTFTPConnection)); + + // + // If we can't allocate the connection instance, all we can do is + // ignore the datagram. + // + if(!psTFTP) + { + pbuf_free(p); + return; + } + + // + // Clear out the structure and initialize a few fields. + // + memset(psTFTP, 0, sizeof(tTFTPConnection)); + psTFTP->pcErrorString = "Unknown error"; + + // + // Yes - create the new UDP connection and set things up to + // handle this request. + // + psTFTP->psPCB = udp_new(); + udp_recv(psTFTP->psPCB, TFTPDataRecv, psTFTP); + udp_connect(psTFTP->psPCB, addr, port); + + // + // Ask the application if it wants to proceed with this request. + // + eRetcode = g_pfnRequest(psTFTP, bGetRequest, (int8_t *)(pui8Data + 2), + eMode); + + // + // Does it want to go on? + // + if(eRetcode == TFTP_OK) + { + // + // Yes - what kind of request is this? + // + if(bGetRequest) + { + // + // For a GET request, we send back the first block of data. + // + psTFTP->ui32BlockNum = 1; + TFTPDataSend(psTFTP); + } + else + { + // + // For a PUT request, we acknowledge the transfer which tells + // the TFTP client that it can start sending us data. + // + psTFTP->ui32BlockNum = 0; + TFTPDataAck(psTFTP); + } + } + else + { + // + // The application indicated that there was an error. Send the + // error report and close the connection. + // + TFTPErrorSend(psTFTP, eRetcode); + TFTPClose(psTFTP); + psTFTP = NULL; + } + } + + // + // Free the pbuf. + // + pbuf_free(p); +} + +//***************************************************************************** +// +//! Initializes the TFTP server module. +//! +//! \param pfnRequest - A pointer to the function which the server will call +//! whenever a new incoming TFTP request is received. This function must +//! determine whether the request can be handled and return a value telling the +//! server whether to continue processing the request or ignore it. +//! +//! This function initializes the lwIP TFTP server and starts listening for +//! incoming requests from clients. It must be called after the network stack +//! is initialized using a call to lwIPInit(). +//! +//! \return None. +// +//***************************************************************************** +void +TFTPInit(tTFTPRequest pfnRequest) +{ + void *pcb; + + // + // Remember the application's notification callback. + // + g_pfnRequest = pfnRequest; + + // + // Start listening for incoming TFTP requests. + // + pcb = udp_new(); + udp_recv(pcb, TFTPRecv, NULL); + udp_bind(pcb, IP_ADDR_ANY, TFTP_PORT); +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** -- cgit v1.3.1