net: tftpput: implement tftp logic
This adds logic to tftp.c to implement the tftp 'put' command, and updates the README. Signed-off-by: Simon Glass <sjg@chromium.org>utp
parent
1aec244acf
commit
1fb7cd498e
2
README
2
README
|
@ -788,6 +788,7 @@ The following options need to be configured:
|
||||||
CONFIG_CMD_SOURCE "source" command Support
|
CONFIG_CMD_SOURCE "source" command Support
|
||||||
CONFIG_CMD_SPI * SPI serial bus support
|
CONFIG_CMD_SPI * SPI serial bus support
|
||||||
CONFIG_CMD_TFTPSRV * TFTP transfer in server mode
|
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_TIME * run command and report execution time
|
||||||
CONFIG_CMD_USB * USB support
|
CONFIG_CMD_USB * USB support
|
||||||
CONFIG_CMD_CDP * Cisco Discover Protocol support
|
CONFIG_CMD_CDP * Cisco Discover Protocol support
|
||||||
|
@ -3344,6 +3345,7 @@ bootp - boot image via network using BootP/TFTP protocol
|
||||||
tftpboot- boot image via network using TFTP protocol
|
tftpboot- boot image via network using TFTP protocol
|
||||||
and env variables "ipaddr" and "serverip"
|
and env variables "ipaddr" and "serverip"
|
||||||
(and eventually "gatewayip")
|
(and eventually "gatewayip")
|
||||||
|
tftpput - upload a file via network using TFTP protocol
|
||||||
rarpboot- boot image via network using RARP/TFTP protocol
|
rarpboot- boot image via network using RARP/TFTP protocol
|
||||||
diskboot- boot from IDE devicebootd - boot default, i.e., run 'bootcmd'
|
diskboot- boot from IDE devicebootd - boot default, i.e., run 'bootcmd'
|
||||||
loads - load S-Record file over serial line
|
loads - load S-Record file over serial line
|
||||||
|
|
|
@ -408,6 +408,9 @@ restart:
|
||||||
NetBootFileXferSize = 0;
|
NetBootFileXferSize = 0;
|
||||||
switch (protocol) {
|
switch (protocol) {
|
||||||
case TFTPGET:
|
case TFTPGET:
|
||||||
|
#ifdef CONFIG_CMD_TFTPPUT
|
||||||
|
case TFTPPUT:
|
||||||
|
#endif
|
||||||
/* always use ARP to get server ethernet address */
|
/* always use ARP to get server ethernet address */
|
||||||
TftpStart(protocol);
|
TftpStart(protocol);
|
||||||
break;
|
break;
|
||||||
|
@ -1794,6 +1797,7 @@ static int net_check_prereq(enum proto_t protocol)
|
||||||
case NFS:
|
case NFS:
|
||||||
#endif
|
#endif
|
||||||
case TFTPGET:
|
case TFTPGET:
|
||||||
|
case TFTPPUT:
|
||||||
if (NetServerIP == 0) {
|
if (NetServerIP == 0) {
|
||||||
puts("*** ERROR: `serverip' not set\n");
|
puts("*** ERROR: `serverip' not set\n");
|
||||||
return 1;
|
return 1;
|
||||||
|
|
136
net/tftp.c
136
net/tftp.c
|
@ -81,6 +81,12 @@ static int TftpTsize;
|
||||||
/* The number of hashes we printed */
|
/* The number of hashes we printed */
|
||||||
static short TftpNumchars;
|
static short TftpNumchars;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CONFIG_CMD_TFTPPUT
|
||||||
|
static int TftpWriting; /* 1 if writing, else 0 */
|
||||||
|
static int TftpFinalBlock; /* 1 if we have sent the last block */
|
||||||
|
#else
|
||||||
|
#define TftpWriting 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#define STATE_SEND_RRQ 1
|
#define STATE_SEND_RRQ 1
|
||||||
#define STATE_DATA 2
|
#define STATE_DATA 2
|
||||||
|
@ -88,6 +94,7 @@ static short TftpNumchars;
|
||||||
#define STATE_BAD_MAGIC 4
|
#define STATE_BAD_MAGIC 4
|
||||||
#define STATE_OACK 5
|
#define STATE_OACK 5
|
||||||
#define STATE_RECV_WRQ 6
|
#define STATE_RECV_WRQ 6
|
||||||
|
#define STATE_SEND_WRQ 7
|
||||||
|
|
||||||
/* default TFTP block size */
|
/* default TFTP block size */
|
||||||
#define TFTP_BLOCK_SIZE 512
|
#define TFTP_BLOCK_SIZE 512
|
||||||
|
@ -201,6 +208,29 @@ void new_transfer(void)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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)
|
||||||
|
{
|
||||||
|
/* We may want to get the final block from the previous set */
|
||||||
|
ulong offset = ((int)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 TftpSend(void);
|
||||||
static void TftpTimeout(void);
|
static void TftpTimeout(void);
|
||||||
|
|
||||||
|
@ -216,9 +246,9 @@ static void show_block_marker(void)
|
||||||
putc('#');
|
putc('#');
|
||||||
TftpNumchars++;
|
TftpNumchars++;
|
||||||
}
|
}
|
||||||
}
|
} else
|
||||||
#endif
|
#endif
|
||||||
else {
|
{
|
||||||
if (((TftpBlock - 1) % 10) == 0)
|
if (((TftpBlock - 1) % 10) == 0)
|
||||||
putc('#');
|
putc('#');
|
||||||
else if ((TftpBlock % (10 * HASHES_PER_LINE)) == 0)
|
else if ((TftpBlock % (10 * HASHES_PER_LINE)) == 0)
|
||||||
|
@ -279,7 +309,7 @@ static void tftp_complete(void)
|
||||||
static void
|
static void
|
||||||
TftpSend(void)
|
TftpSend(void)
|
||||||
{
|
{
|
||||||
volatile uchar *pkt;
|
uchar *pkt;
|
||||||
volatile uchar *xp;
|
volatile uchar *xp;
|
||||||
int len = 0;
|
int len = 0;
|
||||||
volatile ushort *s;
|
volatile ushort *s;
|
||||||
|
@ -295,14 +325,15 @@ TftpSend(void)
|
||||||
* We will always be sending some sort of packet, so
|
* We will always be sending some sort of packet, so
|
||||||
* cobble together the packet headers now.
|
* cobble together the packet headers now.
|
||||||
*/
|
*/
|
||||||
pkt = NetTxPacket + NetEthHdrSize() + IP_HDR_SIZE;
|
pkt = (uchar *)(NetTxPacket + NetEthHdrSize() + IP_HDR_SIZE);
|
||||||
|
|
||||||
switch (TftpState) {
|
switch (TftpState) {
|
||||||
|
|
||||||
case STATE_SEND_RRQ:
|
case STATE_SEND_RRQ:
|
||||||
|
case STATE_SEND_WRQ:
|
||||||
xp = pkt;
|
xp = pkt;
|
||||||
s = (ushort *)pkt;
|
s = (ushort *)pkt;
|
||||||
*s++ = htons(TFTP_RRQ);
|
*s++ = htons(TftpState == STATE_SEND_RRQ ? TFTP_RRQ :
|
||||||
|
TFTP_WRQ);
|
||||||
pkt = (uchar *)s;
|
pkt = (uchar *)s;
|
||||||
strcpy((char *)pkt, tftp_filename);
|
strcpy((char *)pkt, tftp_filename);
|
||||||
pkt += strlen(tftp_filename) + 1;
|
pkt += strlen(tftp_filename) + 1;
|
||||||
|
@ -314,8 +345,8 @@ TftpSend(void)
|
||||||
debug("send option \"timeout %s\"\n", (char *)pkt);
|
debug("send option \"timeout %s\"\n", (char *)pkt);
|
||||||
pkt += strlen((char *)pkt) + 1;
|
pkt += strlen((char *)pkt) + 1;
|
||||||
#ifdef CONFIG_TFTP_TSIZE
|
#ifdef CONFIG_TFTP_TSIZE
|
||||||
memcpy((char *)pkt, "tsize\0000\0", 8);
|
pkt += sprintf((char *)pkt, "tsize%c%lu%c",
|
||||||
pkt += 8;
|
0, NetBootFileXferSize, 0);
|
||||||
#endif
|
#endif
|
||||||
/* try for more effic. blk size */
|
/* try for more effic. blk size */
|
||||||
pkt += sprintf((char *)pkt, "blksize%c%d%c",
|
pkt += sprintf((char *)pkt, "blksize%c%d%c",
|
||||||
|
@ -346,9 +377,19 @@ TftpSend(void)
|
||||||
case STATE_DATA:
|
case STATE_DATA:
|
||||||
xp = pkt;
|
xp = pkt;
|
||||||
s = (ushort *)pkt;
|
s = (ushort *)pkt;
|
||||||
*s++ = htons(TFTP_ACK);
|
s[0] = htons(TFTP_ACK);
|
||||||
*s++ = htons(TftpBlock);
|
s[1] = htons(TftpBlock);
|
||||||
pkt = (uchar *)s;
|
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;
|
len = pkt - xp;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -356,7 +397,8 @@ TftpSend(void)
|
||||||
xp = pkt;
|
xp = pkt;
|
||||||
s = (ushort *)pkt;
|
s = (ushort *)pkt;
|
||||||
*s++ = htons(TFTP_ERROR);
|
*s++ = htons(TFTP_ERROR);
|
||||||
*s++ = htons(3);
|
*s++ = htons(3);
|
||||||
|
|
||||||
pkt = (uchar *)s;
|
pkt = (uchar *)s;
|
||||||
strcpy((char *)pkt, "File too large");
|
strcpy((char *)pkt, "File too large");
|
||||||
pkt += 14 /*strlen("File too large")*/ + 1;
|
pkt += 14 /*strlen("File too large")*/ + 1;
|
||||||
|
@ -380,6 +422,15 @@ TftpSend(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void icmp_handler(unsigned type, unsigned code, unsigned dest,
|
||||||
|
IPaddr_t sip, unsigned src, uchar *pkt, unsigned len)
|
||||||
|
{
|
||||||
|
if (type == ICMP_NOT_REACH && code == ICMP_NOT_REACH_PORT) {
|
||||||
|
/* Oh dear the other end has gone away */
|
||||||
|
restart("TFTP server died");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
TftpHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src,
|
TftpHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src,
|
||||||
unsigned len)
|
unsigned len)
|
||||||
|
@ -396,7 +447,7 @@ TftpHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (TftpState != STATE_SEND_RRQ && src != TftpRemotePort &&
|
if (TftpState != STATE_SEND_RRQ && src != TftpRemotePort &&
|
||||||
TftpState != STATE_RECV_WRQ)
|
TftpState != STATE_RECV_WRQ && TftpState != STATE_SEND_WRQ)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (len < 2)
|
if (len < 2)
|
||||||
|
@ -409,8 +460,30 @@ TftpHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src,
|
||||||
switch (ntohs(proto)) {
|
switch (ntohs(proto)) {
|
||||||
|
|
||||||
case TFTP_RRQ:
|
case TFTP_RRQ:
|
||||||
case TFTP_ACK:
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TFTP_ACK:
|
||||||
|
#ifdef CONFIG_CMD_TFTPPUT
|
||||||
|
if (TftpWriting) {
|
||||||
|
if (TftpFinalBlock) {
|
||||||
|
tftp_complete();
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Move to the next block. We want our block
|
||||||
|
* count to wrap just like the other end!
|
||||||
|
*/
|
||||||
|
int block = ntohs(*s);
|
||||||
|
int ack_ok = (TftpBlock == block);
|
||||||
|
|
||||||
|
TftpBlock = (unsigned short)(block + 1);
|
||||||
|
update_block_number();
|
||||||
|
if (ack_ok)
|
||||||
|
TftpSend(); /* Send next data block */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -459,7 +532,14 @@ TftpHandler(uchar *pkt, unsigned dest, IPaddr_t sip, unsigned src,
|
||||||
TftpState = STATE_DATA; /* passive.. */
|
TftpState = STATE_DATA; /* passive.. */
|
||||||
else
|
else
|
||||||
#endif
|
#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;
|
break;
|
||||||
case TFTP_DATA:
|
case TFTP_DATA:
|
||||||
if (len < 2)
|
if (len < 2)
|
||||||
|
@ -648,8 +728,8 @@ void TftpStart(enum proto_t protocol)
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Using %s device\n", eth_get_name());
|
printf("Using %s device\n", eth_get_name());
|
||||||
printf("TFTP from server %pI4"
|
printf("TFTP %s server %pI4; our IP address is %pI4",
|
||||||
"; our IP address is %pI4", &TftpRemoteIP, &NetOurIP);
|
protocol == TFTPPUT ? "to" : "from", &TftpRemoteIP, &NetOurIP);
|
||||||
|
|
||||||
/* Check if we need to send across this subnet */
|
/* Check if we need to send across this subnet */
|
||||||
if (NetOurGatewayIP && NetOurSubnetMask) {
|
if (NetOurGatewayIP && NetOurSubnetMask) {
|
||||||
|
@ -670,19 +750,31 @@ void TftpStart(enum proto_t protocol)
|
||||||
}
|
}
|
||||||
|
|
||||||
putc('\n');
|
putc('\n');
|
||||||
|
#ifdef CONFIG_CMD_TFTPPUT
|
||||||
printf("Load address: 0x%lx\n", load_addr);
|
TftpWriting = (protocol == TFTPPUT);
|
||||||
|
if (TftpWriting) {
|
||||||
puts("Loading: *\b");
|
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;
|
||||||
|
new_transfer();
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
printf("Load address: 0x%lx\n", load_addr);
|
||||||
|
puts("Loading: *\b");
|
||||||
|
TftpState = STATE_SEND_RRQ;
|
||||||
|
}
|
||||||
|
|
||||||
TftpTimeoutCountMax = TftpRRQTimeoutCountMax;
|
TftpTimeoutCountMax = TftpRRQTimeoutCountMax;
|
||||||
|
|
||||||
NetSetTimeout(TftpTimeoutMSecs, TftpTimeout);
|
NetSetTimeout(TftpTimeoutMSecs, TftpTimeout);
|
||||||
NetSetHandler(TftpHandler);
|
NetSetHandler(TftpHandler);
|
||||||
|
net_set_icmp_handler(icmp_handler);
|
||||||
|
|
||||||
TftpRemotePort = WELL_KNOWN_PORT;
|
TftpRemotePort = WELL_KNOWN_PORT;
|
||||||
TftpTimeoutCount = 0;
|
TftpTimeoutCount = 0;
|
||||||
TftpState = STATE_SEND_RRQ;
|
|
||||||
/* Use a pseudo-random port unless a specific port is set */
|
/* Use a pseudo-random port unless a specific port is set */
|
||||||
TftpOurPort = 1024 + (get_timer(0) % 3072);
|
TftpOurPort = 1024 + (get_timer(0) % 3072);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue