diff options
| author | Yuval Adam <yuv.adm@gmail.com> | 2014-03-16 14:41:11 +0200 |
|---|---|---|
| committer | Yuval Adam <yuv.adm@gmail.com> | 2014-03-16 14:41:11 +0200 |
| commit | 990090a4cc9070837d31e66b58d40f0c3d038741 (patch) | |
| tree | cf1b905082c364e9b223e0c5058566103138dae5 /utils/tftp.c | |
| parent | 7f4da522479c0f00126219f0c23b804c3a93d7a6 (diff) | |
Add usblib and utils
Diffstat (limited to 'utils/tftp.c')
| -rw-r--r-- | utils/tftp.c | 710 |
1 files changed, 710 insertions, 0 deletions
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 <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#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.
+//! @}
+//
+//*****************************************************************************
|
