summaryrefslogtreecommitdiffstats
path: root/cfe/cfe/net/net_tftp.c
diff options
context:
space:
mode:
Diffstat (limited to 'cfe/cfe/net/net_tftp.c')
-rw-r--r--cfe/cfe/net/net_tftp.c921
1 files changed, 921 insertions, 0 deletions
diff --git a/cfe/cfe/net/net_tftp.c b/cfe/cfe/net/net_tftp.c
new file mode 100644
index 0000000..1c3e799
--- /dev/null
+++ b/cfe/cfe/net/net_tftp.c
@@ -0,0 +1,921 @@
+/* *********************************************************************
+ * Broadcom Common Firmware Environment (CFE)
+ *
+ * TFTP Client File: net_tftp.c
+ *
+ * This module contains a TFTP client.
+ *
+ * Author: Mitch Lichtenberg (mpl@broadcom.com)
+ *
+ *********************************************************************
+ *
+ * Copyright 2000,2001,2002,2003
+ * Broadcom Corporation. All rights reserved.
+ *
+ * This software is furnished under license and may be used and
+ * copied only in accordance with the following terms and
+ * conditions. Subject to these conditions, you may download,
+ * copy, install, use, modify and distribute modified or unmodified
+ * copies of this software in source and/or binary form. No title
+ * or ownership is transferred hereby.
+ *
+ * 1) Any source code used, modified or distributed must reproduce
+ * and retain this copyright notice and list of conditions
+ * as they appear in the source file.
+ *
+ * 2) No right is granted to use any trade name, trademark, or
+ * logo of Broadcom Corporation. The "Broadcom Corporation"
+ * name may not be used to endorse or promote products derived
+ * from this software without the prior written permission of
+ * Broadcom Corporation.
+ *
+ * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
+ * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
+ * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR 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), EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************* */
+
+
+#include "lib_types.h"
+#include "lib_string.h"
+#include "lib_queue.h"
+#include "lib_malloc.h"
+#include "lib_printf.h"
+
+#include "cfe_error.h"
+#include "cfe_fileops.h"
+
+/* Foxconn add start by Cliff Wang, 03/23/2010 */
+#include "cfe_console.h"
+/* Foxconn add end by Cliff Wang, 03/23/2010 */
+
+#include "net_ebuf.h"
+#include "net_ether.h"
+
+#include "cfe.h"
+
+#include "cfe_loader.h"
+
+#include "net_api.h"
+
+
+
+/* *********************************************************************
+ * TFTP protocol
+ ********************************************************************* */
+
+#define UDP_PROTO_TFTP 69
+
+#define TFTP_BLOCKSIZE 512
+
+#define TFTP_OP_RRQ 1
+#define TFTP_OP_WRQ 2
+#define TFTP_OP_DATA 3
+#define TFTP_OP_ACK 4
+#define TFTP_OP_ERROR 5
+
+#define TFTP_ERR_DISKFULL 3
+
+#define TFTP_MAX_RETRIES 8
+
+#define TFTP_RRQ_TIMEOUT 1 /* seconds */
+#define TFTP_RECV_TIMEOUT 1 /* seconds */
+
+/* *********************************************************************
+ * TFTP context
+ ********************************************************************* */
+
+typedef struct tftp_fsctx_s {
+ int dummy;
+} tftp_fsctx_t;
+
+typedef struct tftp_info_s {
+ int tftp_socket;
+ uint8_t tftp_data[TFTP_BLOCKSIZE];
+ int tftp_blklen;
+ int tftp_blkoffset;
+ int tftp_fileoffset;
+ uint16_t tftp_blknum;
+ uint8_t tftp_ipaddr[IP_ADDR_LEN];
+ int tftp_lastblock;
+ int tftp_error;
+ int tftp_filemode;
+ char *tftp_filename;
+} tftp_info_t;
+
+/* *********************************************************************
+ * Prototypes
+ ********************************************************************* */
+
+static int tftp_fileop_init(void **fsctx,void *devicename);
+static int tftp_fileop_open(void **ref,void *fsctx,char *filename,int mode);
+static int tftp_fileop_read(void *ref,uint8_t *buf,int len);
+static int tftp_fileop_write(void *ref,uint8_t *buf,int len);
+static int tftp_fileop_seek(void *ref,int offset,int how);
+static void tftp_fileop_close(void *ref);
+static void tftp_fileop_uninit(void *fsctx);
+
+/* Foxconn add start by Cliff Wang, 03/23/2010 */
+int tftp_max_retries = TFTP_MAX_RETRIES;
+int tftp_rrq_timeout = TFTP_RRQ_TIMEOUT;
+int tftp_recv_timeout = TFTP_RECV_TIMEOUT;
+/* Foxconn add end by Cliff Wang, 03/23/2010 */
+
+/* *********************************************************************
+ * TFTP fileio dispatch table
+ ********************************************************************* */
+
+const fileio_dispatch_t tftp_fileops = {
+ "tftp",
+ LOADFLG_NOBB | FSYS_TYPE_NETWORK,
+ tftp_fileop_init,
+ tftp_fileop_open,
+ tftp_fileop_read,
+ tftp_fileop_write,
+ tftp_fileop_seek,
+ tftp_fileop_close,
+ tftp_fileop_uninit
+};
+
+
+/* *********************************************************************
+ * _tftp_open(info,hostname,filename,mode)
+ *
+ * Open a file on a remote host, using the TFTP protocol.
+ *
+ * Input parameters:
+ * info - TFTP information
+ * hostname - host name or IP address of remote host
+ * filename - name of file on remote system
+ * mode - file open mode, read or write
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+static int _tftp_open(tftp_info_t *info,char *hostname,char *filename,int mode)
+{
+ ebuf_t *buf = NULL;
+ const char *datamode = "octet";
+ uint16_t type,error,block;
+ int res;
+ int retries;
+
+ /*
+ * Look up the remote host's IP address
+ */
+
+ res = dns_lookup(hostname,info->tftp_ipaddr);
+ if (res < 0) return res;
+
+ /*
+ * Open a UDP socket to the TFTP server
+ */
+
+ info->tftp_socket = udp_socket(UDP_PROTO_TFTP);
+ info->tftp_lastblock = 0;
+ info->tftp_error = 0;
+ info->tftp_filemode = mode;
+
+ /*
+ * Try to send the RRQ packet to open the file
+ */
+
+ for (retries = 0; retries < TFTP_MAX_RETRIES; retries++) {
+
+ buf = udp_alloc();
+ if (!buf) break;
+
+ if (info->tftp_filemode == FILE_MODE_READ) {
+ ebuf_append_u16_be(buf,TFTP_OP_RRQ); /* read file */
+ }
+ else {
+ ebuf_append_u16_be(buf,TFTP_OP_WRQ); /* write file */
+ }
+ ebuf_append_bytes(buf,filename,strlen(filename)+1);
+ ebuf_append_bytes(buf,datamode,strlen(datamode)+1);
+
+ udp_send(info->tftp_socket,buf,info->tftp_ipaddr);
+
+ buf = udp_recv_with_timeout(info->tftp_socket,TFTP_RRQ_TIMEOUT);
+ if (buf) break;
+ }
+
+ /*
+ * If we got no response, bail now.
+ */
+
+ if (!buf) {
+ udp_close(info->tftp_socket);
+ info->tftp_socket = -1;
+ return CFE_ERR_TIMEOUT;
+ }
+
+ /*
+ * Otherwise, process the response.
+ */
+
+ ebuf_get_u16_be(buf,type);
+
+ switch (type) {
+ case TFTP_OP_ACK:
+ /*
+ * Acks are what we get back on a WRQ command,
+ * but are otherwise unexpected.
+ */
+ if (info->tftp_filemode == FILE_MODE_WRITE) {
+ udp_connect(info->tftp_socket,(uint16_t) buf->eb_usrdata);
+ info->tftp_blknum = 1;
+ info->tftp_blklen = 0;
+ udp_free(buf);
+ return 0;
+ break;
+ }
+ /* fall through */
+ case TFTP_OP_RRQ:
+ case TFTP_OP_WRQ:
+ default:
+ /*
+ * we aren't expecting any of these messages
+ */
+ udp_free(buf);
+ udp_close(info->tftp_socket);
+ info->tftp_socket = -1;
+ return CFE_ERR_PROTOCOLERR;
+
+ case TFTP_OP_ERROR:
+ /*
+ * Process the error return (XXX: remove xprintf here)
+ */
+ ebuf_get_u16_be(buf,error);
+ xprintf("TFTP error %d: %s\n",error,ebuf_ptr(buf));
+ udp_free(buf);
+ udp_close(info->tftp_socket);
+ info->tftp_socket = -1;
+ return CFE_ERR_PROTOCOLERR;
+
+ case TFTP_OP_DATA:
+ /*
+ * Yay, we've got data! Store the first block.
+ */
+ ebuf_get_u16_be(buf,block);
+ udp_connect(info->tftp_socket,(uint16_t) buf->eb_usrdata);
+ info->tftp_blknum = block;
+ info->tftp_blklen = ebuf_length(buf);
+ ebuf_get_bytes(buf,info->tftp_data,ebuf_length(buf));
+ udp_free(buf);
+ if (info->tftp_blklen < TFTP_BLOCKSIZE) {
+ info->tftp_lastblock = 1; /* EOF */
+ }
+ return 0;
+ break;
+
+ }
+}
+
+
+/* *********************************************************************
+ * _tftp_readmore(info)
+ *
+ * Read the next block of the file. We do this by acking the
+ * previous block. Once that block is acked, the TFTP server
+ * should send the next block to us.
+ *
+ * Input parameters:
+ * info - TFTP information
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+static int _tftp_readmore(tftp_info_t *info)
+{
+ ebuf_t *buf;
+ uint16_t cmd,block;
+ int retries;
+
+ /*
+ * If we've already read the last block, there is no more
+ */
+
+ if (info->tftp_lastblock) return 1;
+ if (info->tftp_error) return CFE_ERR_TIMEOUT;
+
+ /*
+ * Otherwise, ack the current block so another one will come
+ */
+
+ for (retries = 0; retries < TFTP_MAX_RETRIES; retries++) {
+
+ buf = udp_alloc();
+ if (!buf) return -1;
+
+ /*
+ * Send the ack
+ */
+
+ ebuf_append_u16_be(buf,TFTP_OP_ACK);
+ ebuf_append_u16_be(buf,info->tftp_blknum);
+ udp_send(info->tftp_socket,buf,info->tftp_ipaddr);
+
+ /*
+ * Wait for some response, retransmitting as necessary
+ */
+
+ /* Foxconn add start by Cliff Wang, 03/23/2010 */
+ buf = udp_recv_with_timeout(info->tftp_socket,tftp_recv_timeout);
+ // buf = udp_recv_with_timeout(info->tftp_socket,TFTP_RECV_TIMEOUT);
+ /* Foxconn add end by Cliff Wang, 03/23/2010 */
+
+ if (buf == NULL) continue;
+
+ /*
+ * Got a response, make sure it's right
+ */
+
+ ebuf_get_u16_be(buf,cmd);
+ if (cmd != TFTP_OP_DATA) {
+ udp_free(buf);
+ continue;
+ }
+
+ ebuf_get_u16_be(buf,block);
+ if (block != (info->tftp_blknum+1)) {
+ udp_free(buf);
+ continue;
+ }
+
+ /*
+ * It's the correct response. Copy the user data
+ */
+
+ info->tftp_blknum = block;
+ info->tftp_blklen = ebuf_length(buf);
+ ebuf_get_bytes(buf,info->tftp_data,ebuf_length(buf));
+ udp_free(buf);
+ break;
+ }
+
+ /*
+ * If the block is less than 512 bytes long, it's the EOF block.
+ */
+
+ if (retries == TFTP_MAX_RETRIES) {
+ info->tftp_error = 1;
+ return CFE_ERR_TIMEOUT;
+ }
+
+ if (info->tftp_blklen < TFTP_BLOCKSIZE) {
+ info->tftp_lastblock = 1; /* EOF */
+ }
+
+ return 0;
+}
+
+/* *********************************************************************
+ * _tftp_writemore(info)
+ *
+ * Write the next block of the file, sending the data from our
+ * holding buffer. Note that the holding buffer must be full
+ * or else we consider this to be an EOF.
+ *
+ * Input parameters:
+ * info - TFTP information
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+static int _tftp_writemore(tftp_info_t *info)
+{
+ ebuf_t *buf;
+ uint16_t cmd,block,error;
+ int retries;
+
+ /*
+ * If we've already written the last block, there is no more
+ */
+
+ if (info->tftp_lastblock) return 1;
+ if (info->tftp_error) return CFE_ERR_TIMEOUT;
+
+ /*
+ * Otherwise, send a block
+ */
+
+ for (retries = 0; retries < TFTP_MAX_RETRIES; retries++) {
+
+ buf = udp_alloc();
+ if (!buf) return -1;
+
+ /*
+ * Send the data
+ */
+
+ ebuf_append_u16_be(buf,TFTP_OP_DATA);
+ ebuf_append_u16_be(buf,info->tftp_blknum);
+ ebuf_append_bytes(buf,info->tftp_data,info->tftp_blklen);
+ udp_send(info->tftp_socket,buf,info->tftp_ipaddr);
+
+ /*
+ * Wait for some response, retransmitting as necessary
+ */
+
+ /* Foxconn add start by Cliff Wang, 03/23/2010 */
+ buf = udp_recv_with_timeout(info->tftp_socket, tftp_recv_timeout);
+ // buf = udp_recv_with_timeout(info->tftp_socket,TFTP_RECV_TIMEOUT);
+ /* Foxconn add end by Cliff Wang, 03/23/2010 */
+
+ if (buf == NULL) continue;
+
+ /*
+ * Got a response, make sure it's right
+ */
+
+ ebuf_get_u16_be(buf,cmd);
+
+ if (cmd == TFTP_OP_ERROR) {
+ /*
+ * Process the error return (XXX: remove xprintf here)
+ */
+ ebuf_get_u16_be(buf,error);
+ xprintf("TFTP write error %d: %s\n",error,ebuf_ptr(buf));
+ info->tftp_error = 1;
+ info->tftp_lastblock = 1;
+ udp_free(buf);
+ return CFE_ERR_IOERR;
+ }
+
+ if (cmd != TFTP_OP_ACK) {
+ udp_free(buf);
+ continue;
+ }
+
+ ebuf_get_u16_be(buf,block);
+ if (block != (info->tftp_blknum)) {
+ udp_free(buf);
+ continue;
+ }
+
+ /*
+ * It's the correct response. Update the block #
+ */
+
+ info->tftp_blknum++;
+ if (info->tftp_blklen != TFTP_BLOCKSIZE) info->tftp_lastblock = 1;
+ udp_free(buf);
+ break;
+ }
+
+ /*
+ * If we had some failure, mark the stream with an error
+ */
+
+ if (retries == TFTP_MAX_RETRIES) {
+ info->tftp_error = 1;
+ return CFE_ERR_TIMEOUT;
+ }
+
+ return 0;
+}
+
+/* Foxconn add start by Cliff Wang, 03/23/2010 */
+static int _tftpd_open(tftp_info_t *info,char *hostname,char *filename,int mode)
+{
+ ebuf_t *buf = NULL;
+ uint16_t type;
+ int res;
+ int retries;
+ char ch = 0;
+
+ /*
+ * * Open a UDP socket
+ * */
+
+ info->tftp_socket = udp_socket(UDP_PROTO_TFTP);
+ info->tftp_lastblock = 0;
+ info->tftp_error = 0;
+ info->tftp_filemode = mode;
+
+ /*
+ * * Listen for requests
+ * */
+
+ res = udp_bind(info->tftp_socket,UDP_PROTO_TFTP);
+ if (res < 0) return res;
+
+ res = CFE_ERR_TIMEOUT;
+
+ for (retries = 0; retries < tftp_max_retries; retries++) {
+ while (console_status()) {
+ console_read(&ch,1);
+ if (ch == 3) break;
+ }
+
+ if (ch == 3) {
+ res = CFE_ERR_INTR;
+ break;
+ }
+
+ buf = udp_recv_with_timeout(info->tftp_socket,tftp_recv_timeout);
+ if (buf == NULL) continue;
+
+ /*
+ * * Process the request
+ * */
+
+ ebuf_get_u16_be(buf,type);
+
+ switch (type) {
+ case TFTP_OP_RRQ:
+ udp_connect(info->tftp_socket,(uint16_t) buf->eb_usrdata);
+ memcpy(info->tftp_ipaddr,buf->eb_usrptr,IP_ADDR_LEN);
+ info->tftp_blknum = 1;
+ info->tftp_blklen = 0;
+ res = 0;
+ break;
+
+ case TFTP_OP_WRQ:
+ udp_connect(info->tftp_socket,(uint16_t) buf->eb_usrdata);
+ memcpy(info->tftp_ipaddr,buf->eb_usrptr,IP_ADDR_LEN);
+ info->tftp_blknum = 0;
+ res = _tftp_readmore(info);
+ break;
+
+ default:
+ /*
+ * aren't expecting any of these messages
+ * */
+ res = CFE_ERR_PROTOCOLERR;
+ break;
+ }
+
+ udp_free(buf);
+ break;
+ }
+
+ if (res) {
+ udp_close(info->tftp_socket);
+ info->tftp_socket = -1;
+ }
+
+ return res;
+}
+
+
+/* *********************************************************************
+ * _tftp_close(info)
+ *
+ * Close a TFTP file. There are two cases for what we do
+ * here. If we're closing the file mid-stream, send an error
+ * packet to tell the host we're getting out early. Otherwise,
+ * just ack the final packet and the connection will close
+ * gracefully.
+ *
+ * Input parameters:
+ * info - TFTP info
+ *
+ * Return value:
+ * 0 if ok, else error code
+ ********************************************************************* */
+
+static int _tftp_close(tftp_info_t *info)
+{
+ ebuf_t *buf;
+ const char *emsg = "transfer cancelled"; /* some error message */
+
+ if (info->tftp_socket == -1) return 0;
+
+ if (info->tftp_filemode == FILE_MODE_READ) {
+ buf = udp_alloc();
+ if (buf) {
+ /* If we're on the EOF packet, just send an ack */
+ if (info->tftp_lastblock) {
+ ebuf_append_u16_be(buf,TFTP_OP_ACK);
+ ebuf_append_u16_be(buf,info->tftp_blknum);
+ }
+ else {
+ ebuf_append_u16_be(buf,TFTP_OP_ERROR);
+ ebuf_append_u16_be(buf,TFTP_ERR_DISKFULL);
+ ebuf_append_bytes(buf,emsg,strlen(emsg)+1);
+ }
+ udp_send(info->tftp_socket,buf,info->tftp_ipaddr);
+ }
+ }
+ else {
+ /* Just flush out the remaining write data, if any */
+ _tftp_writemore(info);
+ }
+
+ udp_close(info->tftp_socket);
+ info->tftp_socket = -1;
+ return 0;
+}
+
+
+
+/* *********************************************************************
+ * tftp_fileop_init(fsctx,device)
+ *
+ * Create a file system device context for the TFTP service
+ *
+ * Input parameters:
+ * fsctx - location to place context information
+ * device - underlying device (unused)
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+static int tftp_fileop_init(void **fsctx,void *dev)
+{
+ void *ref;
+
+ ref = KMALLOC(sizeof(tftp_fsctx_t),0);
+
+ if (!ref) return CFE_ERR_NOMEM;
+ *fsctx = ref;
+ return 0;
+}
+
+/* *********************************************************************
+ * tftp_fileop_open(ref,fsctx,filename,mode)
+ *
+ * This is the filesystem entry point for opening a TFTP file.
+ * Allocate a tftp_info structure, open the TFTP file, and
+ * return a handle.
+ *
+ * Input parameters:
+ * ref - location to place reference data (the TFTP structure)
+ * fsctx - filesystem context
+ * filename - name of remote file to open
+ * mode - FILE_MODE_READ or FILE_MODE_WRITE
+ *
+ * Return value:
+ * 0 if ok
+ * else error code
+ ********************************************************************* */
+
+static int tftp_fileop_open(void **ref,void *fsctx,char *filename,int mode)
+{
+ tftp_info_t *info;
+ char *host;
+ char *file;
+ int res;
+
+ if ((mode != FILE_MODE_READ) && (mode != FILE_MODE_WRITE)) {
+ /* must be either read or write, not both */
+ return CFE_ERR_UNSUPPORTED;
+ }
+
+ /* Allocate the tftp info structure */
+
+ info = KMALLOC(sizeof(tftp_info_t),0);
+ if (!info) {
+ return CFE_ERR_NOMEM;
+ }
+
+
+ info->tftp_filename = lib_strdup(filename);
+ if (!info->tftp_filename) {
+ KFREE(info);
+ return CFE_ERR_NOMEM;
+ }
+
+ lib_chop_filename(info->tftp_filename,&host,&file);
+
+ /* Open the file */
+ /* Foxconn add start by Cliff Wang, 03/23/2010 */
+ if (!*host && !*file) {
+ /* TFTP server if hostname and filename are not specified */
+ res = _tftpd_open(info,host,file,mode);
+ } else {
+ res = _tftp_open(info,host,file,mode);
+ }
+ /* Foxconn add end by Cliff Wang, 03/23/2010 */
+
+ if (res == 0) {
+ info->tftp_blkoffset = 0;
+ info->tftp_fileoffset = 0;
+ *ref = info;
+ }
+ else {
+ KFREE(info->tftp_filename);
+ KFREE(info);
+ *ref = NULL;
+ }
+ return res;
+}
+
+/* *********************************************************************
+ * tftp_fileop_read(ref,buf,len)
+ *
+ * Read some bytes from the remote TFTP file. Do this by copying
+ * data from the block buffer, and reading more data from the
+ * remote file as necessary.
+ *
+ * Input parameters:
+ * ref - tftp_info structure
+ * buf - destination buffer address (NULL to read data and
+ * not copy it anywhere)
+ * len - number of bytes to read
+ *
+ * Return value:
+ * number of bytes read, 0 for EOF, <0 if an error occured.
+ ********************************************************************* */
+
+static int tftp_fileop_read(void *ref,uint8_t *buf,int len)
+{
+ tftp_info_t *info = (tftp_info_t *) ref;
+ int copied = 0;
+ int amtcopy;
+ int res;
+
+ if (info->tftp_error) return CFE_ERR_IOERR;
+ if (info->tftp_filemode == FILE_MODE_WRITE) return CFE_ERR_UNSUPPORTED;
+
+ while (len) {
+
+ if (info->tftp_blkoffset >= info->tftp_blklen) break;
+ amtcopy = len;
+
+ if (amtcopy > (info->tftp_blklen-info->tftp_blkoffset)) {
+ amtcopy = (info->tftp_blklen-info->tftp_blkoffset);
+ }
+
+ if (buf) {
+ memcpy(buf,&(info->tftp_data[info->tftp_blkoffset]),amtcopy);
+ buf += amtcopy;
+ }
+
+ info->tftp_blkoffset += amtcopy;
+ len -= amtcopy;
+ info->tftp_fileoffset += amtcopy;
+ copied += amtcopy;
+
+ if (info->tftp_blkoffset >= info->tftp_blklen) {
+ res = _tftp_readmore(info);
+ if (res != 0) break;
+ info->tftp_blkoffset = 0;
+ }
+ }
+
+ return copied;
+}
+
+/* *********************************************************************
+ * tftp_fileop_write(ref,buf,len)
+ *
+ * Write some bytes to the remote TFTP file. Do this by copying
+ * data to the block buffer, and writing data to the
+ * remote file as necessary.
+ *
+ * Input parameters:
+ * ref - tftp_info structure
+ * buf - source buffer address
+ * len - number of bytes to write
+ *
+ * Return value:
+ * number of bytes written, 0 for EOF, <0 if an error occured.
+ ********************************************************************* */
+
+static int tftp_fileop_write(void *ref,uint8_t *buf,int len)
+{
+ tftp_info_t *info = (tftp_info_t *) ref;
+ int copied = 0;
+ int amtcopy;
+ int res;
+
+ if (info->tftp_error) return CFE_ERR_IOERR;
+ if (info->tftp_filemode == FILE_MODE_READ) return CFE_ERR_UNSUPPORTED;
+
+ while (len) {
+
+ amtcopy = len;
+ if (amtcopy > (TFTP_BLOCKSIZE - info->tftp_blklen)) {
+ amtcopy = (TFTP_BLOCKSIZE - info->tftp_blklen);
+ }
+
+ memcpy(&(info->tftp_data[info->tftp_blklen]),buf,amtcopy);
+ buf += amtcopy;
+
+ info->tftp_blklen += amtcopy;
+ len -= amtcopy;
+ info->tftp_fileoffset += amtcopy;
+ copied += amtcopy;
+
+ if (info->tftp_blklen == TFTP_BLOCKSIZE) {
+ res = _tftp_writemore(info);
+ if (res != 0) {
+ break;
+ }
+ info->tftp_blklen = 0;
+ }
+ }
+
+ return copied;
+}
+
+/* *********************************************************************
+ * tftp_fileop_seek(ref,offset,how)
+ *
+ * Seek within a TFTP file. Note that you can only seek *forward*,
+ * as TFTP doesn't really let you go backwards. (I suppose you
+ * could reopen the file, but thus far nobody needs to go
+ * backwards). You can only seek in a file in read mode.
+ *
+ * Input parameters:
+ * ref - our tftp information
+ * offset - distance to move
+ * how - how to move, (FILE_SEEK_*)
+ *
+ * Return value:
+ * new offset, or <0 if an error occured.
+ ********************************************************************* */
+
+static int tftp_fileop_seek(void *ref,int offset,int how)
+{
+ tftp_info_t *info = (tftp_info_t *) ref;
+ int delta;
+ int startloc;
+ int res;
+
+ if (info->tftp_filemode == FILE_MODE_WRITE) return CFE_ERR_UNSUPPORTED;
+
+ switch (how) {
+ case FILE_SEEK_BEGINNING:
+ startloc = info->tftp_fileoffset;
+ break;
+ case FILE_SEEK_CURRENT:
+ startloc = 0;
+ break;
+ default:
+ startloc = 0;
+ break;
+ }
+
+ delta = offset - startloc;
+ if (delta < 0) {
+ xprintf("Warning: negative seek on tftp file attempted\n");
+ return CFE_ERR_UNSUPPORTED;
+ }
+ res = tftp_fileop_read(ref,NULL,delta);
+ if (res < 0) return res;
+
+ return info->tftp_fileoffset;
+}
+
+
+/* *********************************************************************
+ * tftp_fileop_close(ref)
+ *
+ * Close the TFTP file.
+ *
+ * Input parameters:
+ * ref - our TFTP info
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void tftp_fileop_close(void *ref)
+{
+ tftp_info_t *info = (tftp_info_t *) ref;
+
+ _tftp_close(info);
+
+ KFREE(info->tftp_filename);
+ KFREE(info);
+}
+
+
+/* *********************************************************************
+ * tftp_fileop_uninit(fsctx)
+ *
+ * Uninitialize the filesystem context, freeing allocated
+ * resources.
+ *
+ * Input parameters:
+ * fsctx - our context
+ *
+ * Return value:
+ * nothing
+ ********************************************************************* */
+
+static void tftp_fileop_uninit(void *fsctx)
+{
+ KFREE(fsctx);
+}