* Copyright 2011 Comelit Group SpA,
* Luca Ceresoli <luca.ceresoli@comelit.it>
*/
-
#include <common.h>
#include <command.h>
#include <efi_loader.h>
/* The number of hashes we printed */
static short tftp_tsize_num_hash;
#endif
+/* The window size negotiated */
+static ushort tftp_windowsize;
+/* Next block to send ack to */
+static ushort tftp_next_ack;
+/* Last nack block we send */
+static ushort tftp_last_nack;
#ifdef CONFIG_CMD_TFTPPUT
/* 1 if writing, else 0 */
static int tftp_put_active;
* (but those using CONFIG_IP_DEFRAG may want to set a larger block in cfg file)
*/
+/* When windowsize is defined to 1,
+ * tftp behaves the same way as it was
+ * never declared
+ */
+#ifdef CONFIG_TFTP_WINDOWSIZE
+#define TFTP_WINDOWSIZE CONFIG_TFTP_WINDOWSIZE
+#else
+#define TFTP_WINDOWSIZE 1
+#endif
+
static unsigned short tftp_block_size = TFTP_BLOCK_SIZE;
static unsigned short tftp_block_size_option = CONFIG_TFTP_BLOCKSIZE;
+static unsigned short tftp_window_size_option = TFTP_WINDOWSIZE;
static inline int store_block(int block, uchar *src, unsigned int len)
{
/* try for more effic. blk size */
pkt += sprintf((char *)pkt, "blksize%c%d%c",
0, tftp_block_size_option, 0);
+
+ /* try for more effic. window size.
+ * Implemented only for tftp get.
+ * Don't bother sending if it's 1
+ */
+ if (tftp_state == STATE_SEND_RRQ && tftp_window_size_option > 1)
+ pkt += sprintf((char *)pkt, "windowsize%c%d%c",
+ 0, tftp_window_size_option, 0);
len = pkt - xp;
break;
(char *)pkt + i + 6, tftp_tsize);
}
#endif
+ if (strcasecmp((char *)pkt + i, "windowsize") == 0) {
+ tftp_windowsize =
+ simple_strtoul((char *)pkt + i + 11,
+ NULL, 10);
+ debug("windowsize = %s, %d\n",
+ (char *)pkt + i + 11, tftp_windowsize);
+ }
}
+
+ tftp_next_ack = tftp_windowsize;
+
#ifdef CONFIG_CMD_TFTPPUT
if (tftp_put_active && tftp_state == STATE_OACK) {
/* Get ready to send the first block */
if (len < 2)
return;
len -= 2;
- tftp_cur_block = ntohs(*(__be16 *)pkt);
+
+ if (ntohs(*(__be16 *)pkt) != (ushort)(tftp_cur_block + 1)) {
+ debug("Received unexpected block: %d, expected: %d\n",
+ ntohs(*(__be16 *)pkt),
+ (ushort)(tftp_cur_block + 1));
+ /*
+ * If one packet is dropped most likely
+ * all other buffers in the window
+ * that will arrive will cause a sending NACK.
+ * This just overwellms the server, let's just send one.
+ */
+ if (tftp_last_nack != tftp_cur_block) {
+ tftp_send();
+ tftp_last_nack = tftp_cur_block;
+ tftp_next_ack = (ushort)(tftp_cur_block +
+ tftp_windowsize);
+ }
+ break;
+ }
+
+ tftp_cur_block++;
+ tftp_cur_block %= TFTP_SEQUENCE_SIZE;
if (tftp_state == STATE_SEND_RRQ)
debug("Server did not acknowledge any options!\n");
* Acknowledge the block just received, which will prompt
* the remote for the next one.
*/
- tftp_send();
+ if (tftp_cur_block == tftp_next_ack) {
+ tftp_send();
+ tftp_next_ack += tftp_windowsize;
+ }
- if (len < tftp_block_size)
+ if (len < tftp_block_size) {
+ tftp_send();
tftp_complete();
+ }
break;
case TFTP_ERROR:
if (ep != NULL)
tftp_block_size_option = simple_strtol(ep, NULL, 10);
+ ep = env_get("tftpwindowsize");
+ if (ep != NULL)
+ tftp_window_size_option = simple_strtol(ep, NULL, 10);
+
ep = env_get("tftptimeout");
if (ep != NULL)
timeout_ms = simple_strtol(ep, NULL, 10);
}
#endif
- debug("TFTP blocksize = %i, timeout = %ld ms\n",
- tftp_block_size_option, timeout_ms);
+ debug("TFTP blocksize = %i, TFTP windowsize = %d timeout = %ld ms\n",
+ tftp_block_size_option, tftp_window_size_option, timeout_ms);
tftp_remote_ip = net_server_ip;
if (!net_parse_bootfile(&tftp_remote_ip, tftp_filename, MAX_LEN)) {
tftp_our_port = simple_strtol(ep, NULL, 10);
#endif
tftp_cur_block = 0;
-
+ tftp_windowsize = 1;
+ tftp_last_nack = 0;
/* zero out server ether in case the server ip has changed */
memset(net_server_ethaddr, 0, 6);
/* Revert tftp_block_size to dflt */