Patchwork [U-Boot,7/8] tftpput: implement tftp logic

login
register
mail settings
Submitter Simon Glass
Date Oct. 22, 2011, 4:51 a.m.
Message ID <1319259100-11376-8-git-send-email-sjg@chromium.org>
Download mbox | patch
Permalink /patch/121141/
State New, archived
Headers show

Comments

Simon Glass - Oct. 22, 2011, 4:51 a.m.
This adds logic to tftp.c to implement the tftp 'put' command, and
updates the README.

Signed-off-by: Simon Glass <sjg@chromium.org>
---
 README     |    2 +
 net/net.c  |    4 ++
 net/tftp.c |  114 +++++++++++++++++++++++++++++++++++++++++++++++++----------
 3 files changed, 100 insertions(+), 20 deletions(-)

Patch

diff --git a/README b/README
index 7e032a9..8261bfb 100644
--- a/README
+++ b/README
@@ -784,6 +784,7 @@  The following options need to be configured:
 		CONFIG_CMD_SOURCE	  "source" command Support
 		CONFIG_CMD_SPI		* SPI serial bus support
 		CONFIG_CMD_TFTPSRV	* TFTP transfer in server mode
+		CONFIG_CMD_TFTPPUT	* TFTP put command (upload)
 		CONFIG_CMD_TIME		* run command and report execution time
 		CONFIG_CMD_USB		* USB support
 		CONFIG_CMD_CDP		* Cisco Discover Protocol support
@@ -3340,6 +3341,7 @@  bootp	- boot image via network using BootP/TFTP protocol
 tftpboot- boot image via network using TFTP protocol
 	       and env variables "ipaddr" and "serverip"
 	       (and eventually "gatewayip")
+tftpput - upload a file via network using TFTP protocol
 rarpboot- boot image via network using RARP/TFTP protocol
 diskboot- boot from IDE devicebootd   - boot default, i.e., run 'bootcmd'
 loads	- load S-Record file over serial line
diff --git a/net/net.c b/net/net.c
index ce07ed6..89596d3 100644
--- a/net/net.c
+++ b/net/net.c
@@ -406,6 +406,9 @@  restart:
 		NetBootFileXferSize = 0;
 		switch (protocol) {
 		case TFTPGET:
+#ifdef CONFIG_CMD_TFTPPUT
+		case TFTPPUT:
+#endif
 			/* always use ARP to get server ethernet address */
 			TftpStart(protocol);
 			break;
@@ -1758,6 +1761,7 @@  static int net_check_prereq(enum proto_t protocol)
 	case NFS:
 #endif
 	case TFTPGET:
+	case TFTPPUT:
 		if (NetServerIP == 0) {
 			puts("*** ERROR: `serverip' not set\n");
 			return 1;
diff --git a/net/tftp.c b/net/tftp.c
index 3a58e32..02862c6 100644
--- a/net/tftp.c
+++ b/net/tftp.c
@@ -82,6 +82,11 @@  static int	TftpTsize;
 static short	TftpNumchars;
 #endif
 static int	TftpWriting;	/* 1 if writing, else 0 */
+#ifdef CONFIG_CMD_TFTPPUT
+static int	TftpFinalBlock;	/* 1 if we have sent the last block */
+#else
+#define TftpWriting	0
+#endif
 
 #define STATE_SEND_RRQ	1
 #define STATE_DATA	2
@@ -89,6 +94,7 @@  static int	TftpWriting;	/* 1 if writing, else 0 */
 #define STATE_BAD_MAGIC	4
 #define STATE_OACK	5
 #define STATE_RECV_WRQ	6
+#define STATE_SEND_WRQ	7
 
 /* default TFTP block size */
 #define TFTP_BLOCK_SIZE		512
@@ -191,6 +197,28 @@  store_block(unsigned block, uchar *src, unsigned len)
 		NetBootFileXferSize = newsize;
 }
 
+#ifdef CONFIG_CMD_TFTPPUT
+/**
+ * Load the next block from memory to be sent over tftp.
+ *
+ * @param block	Block number to send
+ * @param dst	Destination buffer for data
+ * @param len	Number of bytes in block (this one and every other)
+ * @return number of bytes loaded
+ */
+static int load_block(unsigned block, uchar *dst, unsigned len)
+{
+	ulong offset = (block - 1) * len + TftpBlockWrapOffset;
+	ulong tosend = len;
+
+	tosend = min(NetBootFileXferSize - offset, tosend);
+	(void)memcpy(dst, (void *)(save_addr + offset), tosend);
+	debug("%s: block=%d, offset=%ld, len=%d, tosend=%ld\n", __func__,
+		block, offset, len, tosend);
+	return tosend;
+}
+#endif
+
 static void TftpSend(void);
 static void TftpTimeout(void);
 
@@ -235,7 +263,7 @@  static void tftp_complete(void)
 static void
 TftpSend(void)
 {
-	volatile uchar *pkt;
+	uchar *pkt;
 	volatile uchar *xp;
 	int		len = 0;
 	volatile ushort *s;
@@ -251,14 +279,15 @@  TftpSend(void)
 	 *	We will always be sending some sort of packet, so
 	 *	cobble together the packet headers now.
 	 */
-	pkt = NetTxPacket + NetEthHdrSize() + IP_HDR_SIZE;
+	pkt = (uchar *)(NetTxPacket + NetEthHdrSize() + IP_HDR_SIZE);
 
 	switch (TftpState) {
-
 	case STATE_SEND_RRQ:
+	case STATE_SEND_WRQ:
 		xp = pkt;
 		s = (ushort *)pkt;
-		*s++ = htons(TFTP_RRQ);
+		*s++ = htons(TftpState == STATE_SEND_RRQ ? TFTP_RRQ
+				: TFTP_WRQ);
 		pkt = (uchar *)s;
 		strcpy((char *)pkt, tftp_filename);
 		pkt += strlen(tftp_filename) + 1;
@@ -270,8 +299,8 @@  TftpSend(void)
 		debug("send option \"timeout %s\"\n", (char *)pkt);
 		pkt += strlen((char *)pkt) + 1;
 #ifdef CONFIG_TFTP_TSIZE
-		memcpy((char *)pkt, "tsize\0000\0", 8);
-		pkt += 8;
+		pkt += sprintf((char *)pkt, "tsize%c%lu%c",
+				0, NetBootFileXferSize, 0);
 #endif
 		/* try for more effic. blk size */
 		pkt += sprintf((char *)pkt, "blksize%c%d%c",
@@ -302,9 +331,19 @@  TftpSend(void)
 	case STATE_DATA:
 		xp = pkt;
 		s = (ushort *)pkt;
-		*s++ = htons(TFTP_ACK);
-		*s++ = htons(TftpBlock);
-		pkt = (uchar *)s;
+		s[0] = htons(TFTP_ACK);
+		s[1] = htons(TftpBlock);
+		pkt = (uchar *)(s + 2);
+#ifdef CONFIG_CMD_TFTPPUT
+		if (TftpWriting) {
+			int toload = TftpBlkSize;
+			int loaded = load_block(TftpBlock, pkt, toload);
+
+			s[0] = htons(TFTP_DATA);
+			pkt += loaded;
+			TftpFinalBlock = (loaded < toload);
+		}
+#endif
 		len = pkt - xp;
 		break;
 
@@ -312,7 +351,8 @@  TftpSend(void)
 		xp = pkt;
 		s = (ushort *)pkt;
 		*s++ = htons(TFTP_ERROR);
-		*s++ = htons(3);
+			*s++ = htons(3);
+
 		pkt = (uchar *)s;
 		strcpy((char *)pkt, "File too large");
 		pkt += 14 /*strlen("File too large")*/ + 1;
@@ -342,7 +382,7 @@  TftpHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src,
 {
 	ushort proto;
 	ushort *s;
-	int i;
+	int i, block;
 
 	if (dest != TftpOurPort) {
 #ifdef CONFIG_MCAST_TFTP
@@ -352,7 +392,7 @@  TftpHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src,
 			return;
 	}
 	if (TftpState != STATE_SEND_RRQ && src != TftpRemotePort &&
-	    TftpState != STATE_RECV_WRQ)
+	    TftpState != STATE_RECV_WRQ && TftpState != STATE_SEND_WRQ)
 		return;
 
 	if (len < 2)
@@ -362,11 +402,28 @@  TftpHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src,
 	s = (ushort *)pkt;
 	proto = *s++;
 	pkt = (uchar *)s;
+	block = ntohs(*s);
 	switch (ntohs(proto)) {
 
 	case TFTP_RRQ:
+		break;
+
 	case TFTP_ACK:
+#ifdef CONFIG_CMD_TFTPPUT
+		debug("Ack block %d\n", block);
+		show_block_marker();
+		if (TftpWriting) {
+			if (TftpFinalBlock) {
+				tftp_complete();
+			} else {
+				TftpBlock = block + 1;
+				show_block_marker();
+				TftpSend(); /* Send next data block */
+			}
+		}
+#endif
 		break;
+
 	default:
 		break;
 
@@ -417,7 +474,14 @@  TftpHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src,
 			TftpState = STATE_DATA;	/* passive.. */
 		else
 #endif
-		TftpSend(); /* Send ACK */
+#ifdef CONFIG_CMD_TFTPPUT
+		if (TftpWriting) {
+			/* Get ready to send the first block */
+			TftpState = STATE_DATA;
+			TftpBlock++;
+		}
+#endif
+		TftpSend(); /* Send ACK or first data block */
 		break;
 	case TFTP_DATA:
 		if (len < 2)
@@ -626,8 +690,8 @@  void TftpStart(proto_t protocol)
 	}
 
 	printf("Using %s device\n", eth_get_name());
-	printf("TFTP from server %pI4"
-		"; our IP address is %pI4", &TftpRemoteIP, &NetOurIP);
+	printf("TFTP %s server %pI4; our IP address is %pI4",
+	       protocol == TFTPPUT ? "to" : "from", &TftpRemoteIP, &NetOurIP);
 
 	/* Check if we need to send across this subnet */
 	if (NetOurGatewayIP && NetOurSubnetMask) {
@@ -649,10 +713,21 @@  void TftpStart(proto_t protocol)
 
 	putc('\n');
 	TftpWriting = (protocol == TFTPPUT);
-
-	printf("Load address: 0x%lx\n", load_addr);
-
-	puts("Loading: *\b");
+#ifdef CONFIG_CMD_TFTPPUT
+	if (TftpWriting) {
+		printf("Save address: 0x%lx\n", save_addr);
+		printf("Save size:    0x%lx\n", save_size);
+		NetBootFileXferSize = save_size;
+		puts("Saving: *\b");
+		TftpState = STATE_SEND_WRQ;
+		TftpFinalBlock = 0;
+	}
+#endif
+	else {
+		printf("Load address: 0x%lx\n", load_addr);
+		puts("Loading: *\b");
+		TftpState = STATE_SEND_RRQ;
+	}
 
 	TftpTimeoutCountMax = TftpRRQTimeoutCountMax;
 
@@ -661,7 +736,6 @@  void TftpStart(proto_t protocol)
 
 	TftpRemotePort = WELL_KNOWN_PORT;
 	TftpTimeoutCount = 0;
-	TftpState = STATE_SEND_RRQ;
 	/* Use a pseudo-random port unless a specific port is set */
 	TftpOurPort = 1024 + (get_timer(0) % 3072);