From b9f8e8a648bd37a3685720f03e339a1384dc107d Mon Sep 17 00:00:00 2001
From: Angus Gratton <gus@projectgus.com>
Date: Thu, 10 Mar 2016 15:59:36 +1100
Subject: [PATCH 01/13] spi_flash.h: Add note that pointers need to be
 word-aligned

---
 include/espressif/spi_flash.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/include/espressif/spi_flash.h b/include/espressif/spi_flash.h
index df21f34..44c8677 100644
--- a/include/espressif/spi_flash.h
+++ b/include/espressif/spi_flash.h
@@ -31,7 +31,7 @@ sdk_SpiFlashOpResult sdk_spi_flash_erase_sector(uint16_t sec);
 /* Write data to flash.
 
    des_addr is byte offset to write to. Should be 4-byte aligned.
-   src is pointer to a buffer to read bytes from.
+   src is pointer to a buffer to read bytes from. Should be 4-byte aligned.
    size is length of buffer in bytes. Should be a multiple of 4.
 */
 sdk_SpiFlashOpResult sdk_spi_flash_write(uint32_t des_addr, const void *src, uint32_t size);
@@ -39,7 +39,7 @@ sdk_SpiFlashOpResult sdk_spi_flash_write(uint32_t des_addr, const void *src, uin
 /* Read data from flash.
 
    src_addr is byte offset to read from. Should be 4-byte aligned.
-   des is pointer to a buffer to read bytes into.
+   des is pointer to a buffer to read bytes into. Should be 4-byte aligned.
    size is number of bytes to read. Should be a multiple of 4.
 */
 sdk_SpiFlashOpResult sdk_spi_flash_read(uint32_t src_addr, void *des, uint32_t size);

From f38bb7459375f6b5c8da9cdb446482c7088307d9 Mon Sep 17 00:00:00 2001
From: Angus Gratton <gus@projectgus.com>
Date: Wed, 23 Mar 2016 11:12:20 +1100
Subject: [PATCH 02/13] OTA Images: Use esptool.py elf2image --version=2
 instead of requiring esptool2

---
 .travis.yml                            |  6 +-----
 common.mk                              | 26 +++++++-------------------
 utils/travis_build/install_esptool2.sh | 15 ---------------
 3 files changed, 8 insertions(+), 39 deletions(-)
 delete mode 100755 utils/travis_build/install_esptool2.sh

diff --git a/.travis.yml b/.travis.yml
index bb04286..0e1b62e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,15 +5,12 @@ env:
   OPENSDK_COMMIT=a48b12f
   CROSS_ROOT="${HOME}/toolchain-${OPENSDK_COMMIT}"
   CROSS_BINDIR="${CROSS_ROOT}/bin"
-  ESPTOOL2_COMMIT=ec0e2c7
-  ESPTOOL2_DIR="${HOME}/esptool2-${ESPTOOL2_COMMIT}"
-  PATH=${PATH}:${CROSS_BINDIR}:${ESPTOOL2_DIR}
   CROSS="ccache xtensa-lx106-elf-"
   MAKE_CMD="make WARNINGS_AS_ERRORS=1 -C examples/ build-examples"
+  PATH=${PATH}:${CROSS_BINDIR}
 cache:
   directories:
     - ${CROSS_ROOT}
-    - ${ESPTOOL2_DIR}
 addons:
   apt:
     packages:
@@ -37,7 +34,6 @@ addons:
     - git
 
 before_install:
-  - utils/travis_build/install_esptool2.sh
   - travis_wait 30 utils/travis_build/install_toolchain.sh
 
 script:
diff --git a/common.mk b/common.mk
index c8fee7a..0141a3a 100644
--- a/common.mk
+++ b/common.mk
@@ -57,15 +57,11 @@ PRINTF_SCANF_FLOAT_SUPPORT ?= 1
 
 # Set OTA to 1 to build an image that supports rBoot OTA bootloader
 #
-# Currently only works with 16mbit or more flash sizes, with 8mbit
-# images for each "slot"
+# Requires 16mbit or higher flash sizes, with 8mbit
+# images for each OTA "slot"
 OTA ?= 0
 
 ifeq ($(OTA),1)
-# for OTA, we build a "SDK v1.2 bootloader" compatible image where everything is in
-# one file (should work with the v1.2 binary bootloader, and the FOSS rBoot bootloader).
-IMGTOOL ?= esptool2
-
 # Tell C preprocessor that we're building for OTA
 CPPFLAGS = -DOTA
 endif
@@ -188,6 +184,9 @@ LIB_ARGS      = $(addprefix -l,$(LIBS))
 PROGRAM_OUT   = $(BUILD_DIR)$(PROGRAM).out
 LDFLAGS      += $(addprefix -T,$(LINKER_SCRIPTS))
 
+# firmware tool arguments
+ESPTOOL_ARGS=-fs $(FLASH_SIZE)m -fm $(FLASH_MODE) -ff $(FLASH_SPEED)m
+
 ifeq ($(OTA),0)
 # for non-OTA, we create two different files for uploading into the flash
 # these are the names and options to generate them
@@ -197,20 +196,9 @@ FW_FILE_1    = $(addprefix $(FIRMWARE_DIR),$(FW_ADDR_1).bin)
 FW_FILE_2    = $(addprefix $(FIRMWARE_DIR),$(FW_ADDR_2).bin)
 else
 # for OTA, it's a single monolithic image
-FW_FILE = $(addprefix $(FIRMWARE_DIR),$(PROGRAM).bin)
+FW_FILE = $(addprefix $(FIRMWARE_DIR),$(PROGRAM)-ota.bin)
 endif
 
-# firmware tool arguments
-ESPTOOL_ARGS=-fs $(FLASH_SIZE)m -fm $(FLASH_MODE) -ff $(FLASH_SPEED)m
-
-IMGTOOL_FLASH_SIZE_2=256
-IMGTOOL_FLASH_SIZE_4=512
-IMGTOOL_FLASH_SIZE_8=1024
-IMGTOOL_FLASH_SIZE_16=2048
-IMGTOOL_FLASH_SIZE_32=4096
-IMGTOOL_FLASH_SIZE=$(value IMGTOOL_FLASH_SIZE_$(FLASH_SIZE))
-IMGTOOL_ARGS=-$(IMGTOOL_FLASH_SIZE) -$(FLASH_MODE) -$(FLASH_SPEED)
-
 # Common include directories, shared across all "components"
 # components will add their include directories to this argument
 #
@@ -373,7 +361,7 @@ $(FW_FILE_1) $(FW_FILE_2): $(PROGRAM_OUT) $(FIRMWARE_DIR)
 	$(Q) $(ESPTOOL) elf2image $(ESPTOOL_ARGS) $< -o $(FIRMWARE_DIR)
 
 $(FW_FILE): $(PROGRAM_OUT) $(FIRMWARE_DIR)
-	$(Q) $(IMGTOOL) $(IMGTOOL_ARGS) -bin -boot2 $(PROGRAM_OUT) $(FW_FILE) .text .data .rodata
+	$(Q) $(ESPTOOL) elf2image --version=2 $(ESPTOOL_ARGS) $< -o $(FW_FILE)
 
 ifeq ($(OTA),0)
 flash: $(FW_FILE_1) $(FW_FILE_2)
diff --git a/utils/travis_build/install_esptool2.sh b/utils/travis_build/install_esptool2.sh
deleted file mode 100755
index 102fd6f..0000000
--- a/utils/travis_build/install_esptool2.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/bash
-set -uv
-
-# Called by Travis to install esptool2 to generate OTA-compatible build
-# images
-
-if test -f ${ESPTOOL2_DIR}/esptool2; then
-	echo "Using cached esptool2"
-	exit 0
-fi
-
-git clone https://github.com/raburton/esptool2 ${ESPTOOL2_DIR}
-cd ${ESPTOOL2_DIR}
-git reset --hard ${ESPTOOL2_COMMIT}
-make

From a3956af4ca72685ded1142879d32c61fabb993c7 Mon Sep 17 00:00:00 2001
From: Angus Gratton <gus@projectgus.com>
Date: Wed, 23 Mar 2016 14:59:19 +1100
Subject: [PATCH 03/13] Bootloader: Integrate rboot directly as 'bootloader'
 component

Currently using unpatched upstream rboot, but modified to build without esptool2.
---
 .gitmodules          |  4 +++
 bootloader/Makefile  | 71 ++++++++++++++++++++++++++++++++++++++++++++
 bootloader/README.md | 11 +++++++
 bootloader/rboot     |  1 +
 common.mk            | 11 +++++--
 5 files changed, 96 insertions(+), 2 deletions(-)
 create mode 100644 bootloader/Makefile
 create mode 100644 bootloader/README.md
 create mode 160000 bootloader/rboot

diff --git a/.gitmodules b/.gitmodules
index 52708f8..7a3370e 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -7,3 +7,7 @@
 [submodule "extras/jsmn/jsmn"]
 	path = extras/jsmn/jsmn
 	url = https://github.com/zserge/jsmn.git
+[submodule "bootloader/rboot"]
+	path = bootloader/rboot
+	url = https://github.com/raburton/rboot.git
+
diff --git a/bootloader/Makefile b/bootloader/Makefile
new file mode 100644
index 0000000..64a0ebc
--- /dev/null
+++ b/bootloader/Makefile
@@ -0,0 +1,71 @@
+# This is a wrapper around the rboot makefile, which gives us the parameters
+# we need to use rboot with esp-open-rtos
+#
+# The wrapper means we don't require esptool2 in the build process, so we can just use
+# esptool.py (still need xxd, grep, sed to generate the header - see below.)
+
+BUILD_DIR ?= build
+FIRMWARE_DIR ?= firmware
+
+# RBOOT configuration parameters.
+# RBOOT_BIG_FLASH is required for esp-open-rtos.
+export RBOOT_BIG_FLASH    = 1
+
+export RBOOT_BUILD_BASE=$(abspath $(BUILD_DIR))
+
+# Default ESPTOOL params, all the same as when using normal esp-open-rtos makefiles
+ESPTOOL ?= esptool.py
+ESPPORT ?= /dev/ttyUSB0
+ESPBAUD ?= 115200
+
+FLASH_SIZE ?= 16
+FLASH_MODE ?= qio
+FLASH_SPEED ?= 40
+
+ESPTOOL_ARGS=-fs $(FLASH_SIZE)m -fm $(FLASH_MODE) -ff $(FLASH_SPEED)m
+
+ifeq ("$(V)","1")
+Q :=
+else
+Q := @
+endif
+
+all: $(FIRMWARE_DIR)/rboot.bin
+
+rboot/Makefile:
+	$(error rboot git submodule is not checkedo out. Try running 'git submodule update --init --recursive')
+
+$(FIRMWARE_DIR)/rboot.bin: $(BUILD_DIR)/rboot.elf $(FIRMWARE_DIR)
+	@echo "FW rboot.bin"
+	$(Q) $(ESPTOOL) elf2image $(ESPTOOL_ARGS) $< -o $(BUILD_DIR)/
+	$(Q) mv $(BUILD_DIR)/0x00000.bin $@
+
+# rboot generates this header using the 'esptool2 -header' option. To try and avoid
+# esptool2 as a dependency, we try it here using grep, sed, xxd (all fairly common Unix tools)
+$(BUILD_DIR)/rboot-hex2a.h: $(BUILD_DIR)/rboot-stage2a.elf $(BUILD_DIR)
+	@echo "Extracting stub image header..."
+	$(Q) xtensa-lx106-elf-objcopy $< --only-section .text -Obinary $(BUILD_DIR)/rboot-hex2a.bin
+	$(Q) xxd -i $(BUILD_DIR)/rboot-hex2a.bin > $@.in
+	$(Q) sed -i "s/unsigned char .\+\[\]/const uint8 _text_data[]/" $@.in
+	$(Q) sed -i "s/unsigned int .\+_len/const uint32 _text_len/" $@.in
+	$(Q) echo "const uint32 entry_addr = $$(xtensa-lx106-elf-objdump -f $< | grep 'start address' | grep -o '0x.\+');" >> $@.in
+	$(Q) echo "const uint32 _text_addr = 0x$$(xtensa-lx106-elf-objdump -h -j .text $< | grep ".text" | grep -o '401.....' | head -n1);" >> $@.in
+	$(Q) mv $@.in $@
+
+$(BUILD_DIR)/rboot-stage2a.elf: $(BUILD_DIR)
+	$(Q) $(MAKE) -C rboot $(RBOOT_BUILD_BASE)/rboot-stage2a.elf
+
+$(BUILD_DIR)/rboot.elf: $(BUILD_DIR)/rboot-hex2a.h
+	$(Q) $(MAKE) -C rboot $(RBOOT_BUILD_BASE)/rboot.elf
+
+$(BUILD_DIR) $(FIRMWARE_DIR):
+	$(Q) mkdir -p "$@"
+
+flash: $(FIRMWARE_DIR)/rboot.bin
+	$(Q) $(ESPTOOL) -p $(ESPPORT) -b $(ESPBAUD) write_flash $(ESPTOOL_ARGS) 0x0 $<
+
+clean:
+	$(Q) rm -rf $(BUILD_DIR)
+	$(Q) rm -rf $(FIRMWARE_DIR)
+
+.PHONY: all clean flash erase_config
diff --git a/bootloader/README.md b/bootloader/README.md
new file mode 100644
index 0000000..6527dba
--- /dev/null
+++ b/bootloader/README.md
@@ -0,0 +1,11 @@
+OTA Bootloader (rboot) source module and support files.
+
+Can be used to build an esp-open-rtos compatible rboot bootloader, for use when OTA=1.
+
+It is also possible to use the upstream rboot verbatim, but *ensure that the `RBOOT_BIG_FLASH` option is enabled or images in slots other than 0 won't work correctly.
+
+rboot is an open source bootloader by Richard Burton:
+https://github.com/raburton/rboot
+
+See the contents of the 'rboot' directory for more information.
+
diff --git a/bootloader/rboot b/bootloader/rboot
new file mode 160000
index 0000000..4cf6132
--- /dev/null
+++ b/bootloader/rboot
@@ -0,0 +1 @@
+Subproject commit 4cf6132f6ee624318c04b2a8d9e37da64b7c283c
diff --git a/common.mk b/common.mk
index 0141a3a..73a998d 100644
--- a/common.mk
+++ b/common.mk
@@ -165,7 +165,7 @@ LINKER_SCRIPTS += $(ROOT)ld/common.ld $(ROOT)ld/rom.ld
 ####
 
 ifndef PROGRAM
-	$(error "Set the PROGRAM environment variable in your Makefile before including common.mk"
+$(error "Set the PROGRAM environment variable in your Makefile before including common.mk")
 endif
 
 # hacky way to get a single space value
@@ -223,7 +223,7 @@ Q := @
 vecho := @echo
 endif
 
-.PHONY: all clean debug_print
+.PHONY: all clean debug_print flash flash_bootloader erase_flash
 
 all: $(PROGRAM_OUT) $(FW_FILE_1) $(FW_FILE_2) $(FW_FILE)
 
@@ -361,6 +361,7 @@ $(FW_FILE_1) $(FW_FILE_2): $(PROGRAM_OUT) $(FIRMWARE_DIR)
 	$(Q) $(ESPTOOL) elf2image $(ESPTOOL_ARGS) $< -o $(FIRMWARE_DIR)
 
 $(FW_FILE): $(PROGRAM_OUT) $(FIRMWARE_DIR)
+	$(vecho) "FW $@"
 	$(Q) $(ESPTOOL) elf2image --version=2 $(ESPTOOL_ARGS) $< -o $(FW_FILE)
 
 ifeq ($(OTA),0)
@@ -372,6 +373,12 @@ flash: $(FW_FILE)
 	$(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) write_flash $(ESPTOOL_ARGS) 0x2000 $(FW_FILE)
 endif
 
+flash_bootloader:
+	$(MAKE) -C $(ROOT)/bootloader flash
+
+erase_flash:
+	$(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) erase_flash
+
 size: $(PROGRAM_OUT)
 	$(Q) $(CROSS)size --format=sysv $(PROGRAM_OUT)
 

From e671927bd0c9467d636d14a7297c1bc54a95e8e9 Mon Sep 17 00:00:00 2001
From: Angus Gratton <gus@projectgus.com>
Date: Wed, 23 Mar 2016 17:33:08 +1100
Subject: [PATCH 04/13] OTA: Add TFTP client mode, expand ota_basic example.

---
 examples/ota_basic/ota_basic.c |  40 ++++++++++++
 extras/rboot-ota/ota-tftp.c    | 114 +++++++++++++++++++++++++++++++--
 extras/rboot-ota/ota-tftp.h    |  14 ++++
 3 files changed, 162 insertions(+), 6 deletions(-)

diff --git a/examples/ota_basic/ota_basic.c b/examples/ota_basic/ota_basic.c
index d55c03a..ea4eedd 100644
--- a/examples/ota_basic/ota_basic.c
+++ b/examples/ota_basic/ota_basic.c
@@ -18,6 +18,45 @@
 #include "rboot.h"
 #include "rboot-api.h"
 
+#define TFTP_IMAGE_SERVER "192.168.1.23"
+#define TFTP_IMAGE_FILENAME1 "firmware1.bin"
+#define TFTP_IMAGE_FILENAME2 "firmware2.bin"
+
+void tftp_client_task(void *pvParameters)
+{
+    printf("TFTP client task starting...\n");
+    rboot_config conf;
+    conf = rboot_get_config();
+    int slot = (conf.current_rom + 1) % conf.count;
+    printf("Image will be saved in OTA slot %d.\n", slot);
+    if(slot == conf.current_rom) {
+        printf("FATAL ERROR: Only one OTA slot is configured!\n");
+        while(1) {}
+    }
+
+    /* Alternate between trying two different filenames. Probalby want to change this if making a practical
+       example!
+
+       Note: example will reboot into FILENAME1 if it is successfully downloaded, but FILENAME2 is ignored.
+    */
+    while(1) {
+        printf("Downloading %s to slot %d...\n", TFTP_IMAGE_FILENAME1, slot);
+        int res = ota_tftp_download(TFTP_IMAGE_SERVER, TFTP_PORT, TFTP_IMAGE_FILENAME1, 1000, slot);
+        printf("ota_tftp_download %s result %d\n", TFTP_IMAGE_FILENAME1, res);
+        if(res == 0) {
+            printf("Rebooting into slot %d...\n", slot);
+            rboot_set_current_rom(slot);
+            sdk_system_restart();
+        }
+        vTaskDelay(5000 / portTICK_RATE_MS);
+
+        printf("Downloading %s to slot %d...\n", TFTP_IMAGE_FILENAME2, slot);
+        res = ota_tftp_download(TFTP_IMAGE_SERVER, TFTP_PORT, TFTP_IMAGE_FILENAME2, 1000, slot);
+        printf("ota_tftp_download %s result %d\n", TFTP_IMAGE_FILENAME2, res);
+        vTaskDelay(5000 / portTICK_RATE_MS);
+    }
+}
+
 void user_init(void)
 {
     uart_set_baud(0, 115200);
@@ -39,4 +78,5 @@ void user_init(void)
     sdk_wifi_station_set_config(&config);
 
     ota_tftp_init_server(TFTP_PORT);
+    xTaskCreate(&tftp_client_task, (signed char *)"tftp_client", 1024, NULL, 2, NULL);
 }
diff --git a/extras/rboot-ota/ota-tftp.c b/extras/rboot-ota/ota-tftp.c
index d772927..0f4360a 100644
--- a/extras/rboot-ota/ota-tftp.c
+++ b/extras/rboot-ota/ota-tftp.c
@@ -28,6 +28,7 @@
 #define TFTP_FIRMWARE_FILE "firmware.bin"
 #define TFTP_OCTET_MODE "octet" /* non-case-sensitive */
 
+#define TFTP_OP_RRQ 1
 #define TFTP_OP_WRQ 2
 #define TFTP_OP_DATA 3
 #define TFTP_OP_ACK 4
@@ -43,8 +44,9 @@
 
 static void tftp_task(void *port_p);
 static char *tftp_get_field(int field, struct netbuf *netbuf);
-static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t limit_offs, size_t *received_len);
+static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t limit_offs, size_t *received_len, ip_addr_t *peer_addr, int peer_port);
 static err_t tftp_send_ack(struct netconn *nc, int block);
+static err_t tftp_send_rrq(struct netconn *nc, const char *filename);
 static void tftp_send_error(struct netconn *nc, int err_code, const char *err_msg);
 
 void ota_tftp_init_server(int listen_port)
@@ -52,6 +54,60 @@ void ota_tftp_init_server(int listen_port)
     xTaskCreate(tftp_task, (signed char *)"tftpOTATask", 512, (void *)listen_port, 2, NULL);
 }
 
+err_t ota_tftp_download(const char *server, int port, const char *filename, int timeout, int ota_slot)
+{
+    rboot_config rboot_config = rboot_get_config();
+    /* Validate the OTA slot parameter */
+    if(rboot_config.current_rom == ota_slot || rboot_config.count <= ota_slot)
+    {
+        return ERR_VAL;
+    }
+    /* This is all we need to know from the rboot config - where we need
+       to write data to.
+    */
+    uint32_t flash_offset = rboot_config.roms[ota_slot];
+
+    struct netconn *nc = netconn_new (NETCONN_UDP);
+    err_t err;
+    if(!nc) {
+        return ERR_IF;
+    }
+
+    netconn_set_recvtimeout(nc, timeout);
+
+    /* try to bind our client port as our local port,
+       or keep trying the next 10 ports after it */
+    int local_port = port-1;
+    do {
+        err = netconn_bind(nc, IP_ADDR_ANY, ++local_port);
+    } while(err == ERR_USE && local_port < port + 10);
+    if(err) {
+        netconn_delete(nc);
+        return err;
+    }
+
+    ip_addr_t addr;
+    err = netconn_gethostbyname(server, &addr);
+    if(err) {
+        netconn_delete(nc);
+        return err;
+    }
+
+    netconn_connect(nc, &addr, port);
+
+    err = tftp_send_rrq(nc, filename);
+    if(err) {
+        netconn_delete(nc);
+        return err;
+    }
+
+    size_t received_len;
+    err = tftp_receive_data(nc, flash_offset, flash_offset+MAX_IMAGE_SIZE, &received_len, &addr, port);
+    netconn_delete(nc);
+    return err;
+}
+
+
 static void tftp_task(void *listen_port)
 {
     struct netconn *nc = netconn_new (NETCONN_UDP);
@@ -100,7 +156,7 @@ static void tftp_task(void *listen_port)
 
         /* check mode */
         char *mode = tftp_get_field(1, netbuf);
-        if(!mode || strcmp("octet", mode)) {
+        if(!mode || strcmp(TFTP_OCTET_MODE, mode)) {
             tftp_send_error(nc, TFTP_ERR_ILLEGAL, "Mode must be octet/binary");
             free(mode);
             netbuf_delete(netbuf);
@@ -133,7 +189,8 @@ static void tftp_task(void *listen_port)
 
         /* Finished WRQ phase, start TFTP data transfer */
         size_t received_len;
-        int recv_err = tftp_receive_data(nc, conf.roms[slot], conf.roms[slot]+MAX_IMAGE_SIZE, &received_len);
+        netconn_set_recvtimeout(nc, 10000);
+        int recv_err = tftp_receive_data(nc, conf.roms[slot], conf.roms[slot]+MAX_IMAGE_SIZE, &received_len, NULL, 0);
 
         netconn_disconnect(nc);
         printf("OTA TFTP receive data result %d bytes %d\r\n", recv_err, received_len);
@@ -182,20 +239,48 @@ static char *tftp_get_field(int field, struct netbuf *netbuf)
     return result;
 }
 
-static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t limit_offs, size_t *received_len)
+#define TFTP_TIMEOUT_RETRANSMITS 10
+
+static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t limit_offs, size_t *received_len, ip_addr_t *peer_addr, int peer_port)
 {
     *received_len = 0;
     const int DATA_PACKET_SZ = 512 + 4; /*( packet size plus header */
     uint32_t start_offs = write_offs;
     int block = 1;
 
-    struct netbuf *netbuf;
+    struct netbuf *netbuf = 0;
+    int retries = TFTP_TIMEOUT_RETRANSMITS;
 
     while(1)
     {
-        netconn_set_recvtimeout(nc, 10000);
+        if(peer_addr) {
+            netconn_disconnect(nc);
+        }
+
         err_t err = netconn_recv(nc, &netbuf);
+
+        if(peer_addr) {
+            if(netbuf) {
+                /* For TFTP server, the UDP connection is already established. But for client,
+                   we don't know what port the server is using until we see the first data
+                   packet - so we connect here.
+                */
+                netconn_connect(nc, netbuf_fromaddr(netbuf), netbuf_fromport(netbuf));
+                peer_addr = 0;
+            } else {
+                /* Otherwise, temporarily re-connect so we can send errors */
+                netconn_connect(nc, peer_addr, peer_port);
+            }
+        }
+
         if(err == ERR_TIMEOUT) {
+            if(retries-- > 0 && block > 1) {
+                /* Retransmit the last ACK, wait for repeat data block.
+
+                 This doesn't work for the first block, have to time out and start again. */
+                tftp_send_ack(nc, block-1);
+                continue;
+            }
             tftp_send_error(nc, TFTP_ERR_ILLEGAL, "Timeout");
             return ERR_TIMEOUT;
         }
@@ -225,6 +310,9 @@ static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t lim
             }
         }
 
+        /* Reset retry count if we got valid data */
+        retries = TFTP_TIMEOUT_RETRANSMITS;
+
         if(write_offs % SECTOR_SIZE == 0) {
             sdk_spi_flash_erase_sector(write_offs / SECTOR_SIZE);
         }
@@ -325,3 +413,17 @@ static void tftp_send_error(struct netconn *nc, int err_code, const char *err_ms
     netconn_send(nc, err);
     netbuf_delete(err);
 }
+
+static err_t tftp_send_rrq(struct netconn *nc, const char *filename)
+{
+    struct netbuf *rrqbuf = netbuf_new();
+    uint16_t *rrqdata = (uint16_t *)netbuf_alloc(rrqbuf, 4 + strlen(filename) + strlen(TFTP_OCTET_MODE));
+    rrqdata[0] = htons(TFTP_OP_RRQ);
+    char *rrq_filename = (char *)&rrqdata[1];
+    strcpy(rrq_filename, filename);
+    strcpy(rrq_filename + strlen(filename) + 1, TFTP_OCTET_MODE);
+
+    err_t err = netconn_send(nc, rrqbuf);
+    netbuf_delete(rrqbuf);
+    return err;
+}
diff --git a/extras/rboot-ota/ota-tftp.h b/extras/rboot-ota/ota-tftp.h
index f08b28c..cccac2a 100644
--- a/extras/rboot-ota/ota-tftp.h
+++ b/extras/rboot-ota/ota-tftp.h
@@ -1,5 +1,8 @@
 #ifndef _OTA_TFTP_H
 #define _OTA_TFTP_H
+
+#include "lwip/err.h"
+
 /* TFTP Server OTA Support
  *
  * To use, call ota_tftp_init_server() which will start the TFTP server task
@@ -30,6 +33,17 @@
  */
 void ota_tftp_init_server(int listen_port);
 
+/* Attempt to make a TFTP client connection and download the specified filename.
+
+   'timeout' is in milliseconds, and is timeout for any UDP exchange
+   _not_ the entire download.
+
+   Returns 0 on success, LWIP err.h values for errors.
+
+   Does not change the current firmware slot, or reboot.
+ */
+err_t ota_tftp_download(const char *server, int port, const char *filename, int timeout, int ota_slot);
+
 #define TFTP_PORT 69
 
 #endif

From 6eceb5843cd9b2cacec083d8554ab40a246a5cab Mon Sep 17 00:00:00 2001
From: Angus Gratton <gus@projectgus.com>
Date: Wed, 23 Mar 2016 18:01:09 +1100
Subject: [PATCH 05/13] OTA: Move OTA-aware Cache_Read_Enable to core

Otherwise images built with OTA=1 are only OTA-suitable if they also
link rboot-ota.
---
 extras/rboot-ota/rboot-cache.S => core/spiflash-cache-enable.S | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename extras/rboot-ota/rboot-cache.S => core/spiflash-cache-enable.S (100%)

diff --git a/extras/rboot-ota/rboot-cache.S b/core/spiflash-cache-enable.S
similarity index 100%
rename from extras/rboot-ota/rboot-cache.S
rename to core/spiflash-cache-enable.S

From 03559de5cba418ec43bbca22ed9c0a7db663c740 Mon Sep 17 00:00:00 2001
From: Angus Gratton <gus@projectgus.com>
Date: Tue, 3 May 2016 11:07:10 +1000
Subject: [PATCH 06/13] Move rboot_verify_image to rboot-api

Removes rboot-integration.c, removes need for clients to include rboot-integration.h
---
 examples/ota_basic/ota_basic.c       |   1 -
 extras/rboot-ota/rboot-api.c         | 146 +++++++++++++++++++++++++++
 extras/rboot-ota/rboot-api.h         |  16 +++
 extras/rboot-ota/rboot-integration.c | 132 ------------------------
 extras/rboot-ota/rboot-integration.h |   3 -
 5 files changed, 162 insertions(+), 136 deletions(-)
 delete mode 100644 extras/rboot-ota/rboot-integration.c

diff --git a/examples/ota_basic/ota_basic.c b/examples/ota_basic/ota_basic.c
index ea4eedd..1a2d826 100644
--- a/examples/ota_basic/ota_basic.c
+++ b/examples/ota_basic/ota_basic.c
@@ -14,7 +14,6 @@
 #include "ssid_config.h"
 
 #include "ota-tftp.h"
-#include "rboot-integration.h"
 #include "rboot.h"
 #include "rboot-api.h"
 
diff --git a/extras/rboot-ota/rboot-api.c b/extras/rboot-ota/rboot-api.c
index 7fe5340..9283acb 100644
--- a/extras/rboot-ota/rboot-api.c
+++ b/extras/rboot-ota/rboot-api.c
@@ -4,6 +4,8 @@
 // richardaburton@gmail.com
 // See license.txt for license terms.
 // OTA code based on SDK sample from Espressif.
+//
+// esp-open-rtos additions Copyright 2016 Angus Gratton
 //////////////////////////////////////////////////
 
 #include <rboot.h>
@@ -209,6 +211,150 @@ bool ICACHE_FLASH_ATTR rboot_get_last_boot_mode(uint8 *mode) {
 }
 #endif
 
+/* NOTE: Functions below here were added for esp-open-rtos only */
+
+uint32_t rboot_get_slot_offset(uint8_t slot) {
+    rboot_config conf;
+    conf = rboot_get_config();
+    if (slot >= conf.count) return (uint32_t)-1;
+    return conf.roms[slot];
+}
+
+/* Structures for parsing the rboot OTA image format */
+typedef struct __attribute__((packed)) {
+    uint8_t magic;
+    uint8_t section_count;
+    uint8_t val[2]; /* flash size & speed when placed @ offset 0, I thik ignored otherwise */
+    uint32_t entrypoint;
+} image_header_t;
+
+typedef struct __attribute__((packed)) {
+    uint32_t load_addr;
+    uint32_t length;
+} section_header_t;
+
+#define ROM_MAGIC_OLD 0xe9
+#define ROM_MAGIC_NEW 0xea
+
+bool rboot_verify_image(uint32_t initial_offset, uint32_t *image_length, const char **error_message)
+{
+    uint32_t offset = initial_offset;
+    char *error = NULL;
+    RBOOT_DEBUG("rboot_verify_image: verifying image at 0x%08x\n", initial_offset);
+    if(offset % 4) {
+        error = "Unaligned flash offset";
+        goto fail;
+    }
+
+    /* sanity limit on how far we can read */
+    uint32_t end_limit = offset + 0x100000;
+    image_header_t image_header __attribute__((aligned(4)));
+    if(sdk_spi_flash_read(offset, &image_header, sizeof(image_header_t))) {
+        error = "Flash fail";
+        goto fail;
+    }
+
+    offset += sizeof(image_header_t);
+
+    if(image_header.magic != ROM_MAGIC_OLD && image_header.magic != ROM_MAGIC_NEW) {
+        error = "Missing initial magic";
+        goto fail;
+    }
+
+    bool is_new_header = (image_header.magic == ROM_MAGIC_NEW); /* a v1.2/rboot header, so expect a v1.1 header after the initial section */
+
+    int remaining_sections = image_header.section_count;
+
+    uint8_t checksum = CHKSUM_INIT;
+
+    while(remaining_sections > 0 && offset < end_limit)
+    {
+        /* read section header */
+        section_header_t header __attribute__((aligned(4)));
+        if(sdk_spi_flash_read(offset, &header, sizeof(section_header_t))) {
+            error = "Flash fail";
+            goto fail;
+        }
+
+        RBOOT_DEBUG("Found section @ 0x%08x (abs 0x%08x) length %d load 0x%08x\n", offset-initial_offset, offset, header.length, header.load_addr);
+        offset += sizeof(section_header_t);
+
+        if(header.length+offset > end_limit) {
+            break; /* sanity check: will reading section take us off end of expected flashregion? */
+        }
+
+        if(header.length % 4) {
+            error = "Header length not modulo 4";
+            goto fail;
+        }
+
+        if(!is_new_header) {
+            /* Add individual data of the section to the checksum. */
+            char chunk[16] __attribute__((aligned(4)));
+            for(int i = 0; i < header.length; i++) {
+                if(i % sizeof(chunk) == 0)
+                    sdk_spi_flash_read(offset+i, (uint32_t *)chunk, sizeof(chunk));
+                checksum ^= chunk[i % sizeof(chunk)];
+            }
+        }
+
+        offset += header.length;
+        /* pad section to 4 byte align */
+        offset = (offset+3) & ~3;
+
+        remaining_sections--;
+
+        if(is_new_header) {
+            /* pad to a 16 byte offset */
+            offset = (offset+15) & ~15;
+
+            /* expect a v1.1 header here at start of "real" sections */
+            sdk_spi_flash_read(offset, (uint32_t *)&image_header, sizeof(image_header_t));
+            offset += sizeof(image_header_t);
+            if(image_header.magic != ROM_MAGIC_OLD) {
+                error = "Bad second magic";
+                goto fail;
+            }
+            remaining_sections = image_header.section_count;
+            is_new_header = false;
+        }
+    }
+
+    if(remaining_sections > 0) {
+        error = "Image truncated";
+        goto fail;
+    }
+
+    /* add a byte for the image checksum (actually comes after the padding) */
+    offset++;
+    /* pad the image length to a 16 byte boundary */
+    offset = (offset+15) & ~15;
+
+    uint32_t read_checksum;
+    sdk_spi_flash_read(offset-1, &read_checksum, 1);
+    if((uint8_t)read_checksum != checksum) {
+        error = "Invalid checksum";
+        goto fail;
+    }
+
+    RBOOT_DEBUG("rboot_verify_image: verified expected 0x%08x bytes.\n", offset - initial_offset);
+
+    if(image_length)
+        *image_length = offset - initial_offset;
+
+    return true;
+
+ fail:
+    if(error_message)
+        *error_message = error;
+    if(error) {
+        printf("%s: %s\n", __func__, error);
+    }
+    if(image_length)
+        *image_length = offset - initial_offset;
+    return false;
+}
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/extras/rboot-ota/rboot-api.h b/extras/rboot-ota/rboot-api.h
index 5a39b4d..5968827 100644
--- a/extras/rboot-ota/rboot-api.h
+++ b/extras/rboot-ota/rboot-api.h
@@ -128,6 +128,22 @@ bool ICACHE_FLASH_ATTR rboot_get_last_boot_rom(uint8 *rom);
 bool ICACHE_FLASH_ATTR rboot_get_last_boot_mode(uint8 *mode);
 #endif
 
+/* ADDITIONS TO RBOOT-API FOR ESP-OPEN-RTOS FOLLOW */
+
+/* Returns offset of given rboot slot, or (uint32_t)-1 if slot is invalid.
+ */
+uint32_t rboot_get_slot_offset(uint8_t slot);
+
+/** @description Verify basic image parameters - headers, CRC8 checksum.
+
+    @param Offset of image to verify. Can use rboot_get_slot_offset() to find.
+    @param Optional pointer will return the total valid length of the image.
+    @param Optional pointer to a static human-readable error message if fails.
+
+    @return True for valid, False for invalid.
+**/
+bool rboot_verify_image(uint32_t offset, uint32_t *image_length, const char **error_message);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/extras/rboot-ota/rboot-integration.c b/extras/rboot-ota/rboot-integration.c
deleted file mode 100644
index 266d566..0000000
--- a/extras/rboot-ota/rboot-integration.c
+++ /dev/null
@@ -1,132 +0,0 @@
-#include <stdint.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <espressif/spi_flash.h>
-#include <espressif/esp_system.h>
-
-#include <FreeRTOS.h>
-#include <task.h>
-
-#include <rboot.h>
-
-#define ROM_MAGIC_OLD 0xe9
-#define ROM_MAGIC_NEW 0xea
-
-typedef struct __attribute__((packed)) {
-    uint8_t magic;
-    uint8_t section_count;
-    uint8_t val[2]; /* flash size & speed when placed @ offset 0, I think ignored otherwise */
-    uint32_t entrypoint;
-} image_header_t;
-
-typedef struct __attribute__((packed)) {
-    uint32_t load_addr;
-    uint32_t length;
-} section_header_t;
-
-
-// Check that a valid-looking rboot image is found at this offset on the flash, and
-// takes up 'expected_length' bytes.
-bool rboot_verify_image(uint32_t offset, uint32_t expected_length, const char **error_message)
-{
-    char *error = NULL;
-    if(offset % 4) {
-        error = "Unaligned flash offset";
-        goto fail;
-    }
-
-    uint32_t end_offset = offset + expected_length;
-    image_header_t image_header;
-    sdk_spi_flash_read(offset, (uint32_t *)&image_header, sizeof(image_header_t));
-    offset += sizeof(image_header_t);
-
-    if(image_header.magic != ROM_MAGIC_OLD && image_header.magic != ROM_MAGIC_NEW) {
-        error = "Missing initial magic";
-        goto fail;
-    }
-
-    bool is_new_header = (image_header.magic == ROM_MAGIC_NEW); /* a v1.2/rboot header, so expect a v1.1 header after the initial section */
-
-    int remaining_sections = image_header.section_count;
-
-    uint8_t checksum = CHKSUM_INIT;
-
-    while(remaining_sections > 0 && offset < end_offset)
-    {
-        /* read section header */
-        section_header_t header;
-        sdk_spi_flash_read(offset, (uint32_t *)&header, sizeof(section_header_t));
-        RBOOT_DEBUG("Found section @ 0x%08lx length 0x%08lx load 0x%08lx padded 0x%08lx\r\n", offset, header.length, header.load_addr, header.length);
-        offset += sizeof(section_header_t);
-
-
-        if(header.length+offset > end_offset) {
-            break; /* sanity check: will reading section take us off end of expected flashregion? */
-        }
-
-        if(!is_new_header) {
-            /* Add individual data of the section to the checksum. */
-            char chunk[16];
-            for(int i = 0; i < header.length; i++) {
-                if(i % sizeof(chunk) == 0)
-                    sdk_spi_flash_read(offset+i, (uint32_t *)chunk, sizeof(chunk));
-                checksum ^= chunk[i % sizeof(chunk)];
-            }
-        }
-
-        offset += header.length;
-        /* pad section to 4 byte align */
-        offset = (offset+3) & ~3;
-
-        remaining_sections--;
-
-        if(is_new_header) {
-            /* pad to a 16 byte offset */
-            offset = (offset+15) & ~15;
-
-            /* expect a v1.1 header here at start of "real" sections */
-            sdk_spi_flash_read(offset, (uint32_t *)&image_header, sizeof(image_header_t));
-            offset += sizeof(image_header_t);
-            if(image_header.magic != ROM_MAGIC_OLD) {
-                error = "Bad second magic";
-                goto fail;
-            }
-            remaining_sections = image_header.section_count;
-            is_new_header = false;
-        }
-    }
-
-    if(remaining_sections > 0) {
-        error = "Image truncated";
-        goto fail;
-    }
-
-    /* add a byte for the image checksum (actually comes after the padding) */
-    offset++;
-    /* pad the image length to a 16 byte boundary */
-    offset = (offset+15) & ~15;
-
-    uint8_t read_checksum;
-    sdk_spi_flash_read(offset-1, (uint32_t *)&read_checksum, 1); /* not sure if this will work */
-    if(read_checksum != checksum) {
-        error = "Invalid checksum";
-        goto fail;
-    }
-
-    if(offset != end_offset) {
-        error = "Wrong length";
-        goto fail;
-    }
-
-    RBOOT_DEBUG("rboot_verify_image: verified expected 0x%08lx bytes.\r\n", expected_length);
-    return true;
-
- fail:
-    if(error_message)
-        *error_message = error;
-    printf("%s: %s\r\n", __func__, error);
-    return false;
-}
diff --git a/extras/rboot-ota/rboot-integration.h b/extras/rboot-ota/rboot-integration.h
index 00c1bcb..199d7a2 100644
--- a/extras/rboot-ota/rboot-integration.h
+++ b/extras/rboot-ota/rboot-integration.h
@@ -28,7 +28,4 @@
 #define RBOOT_DEBUG(f_, ...)
 #endif
 
-// Check that a valid-looking rboot image is found at this offset on the flash, and
-// takes up 'expected_length' bytes.
-bool rboot_verify_image(uint32_t offset, uint32_t expected_length, const char **error_message);
 #endif // __RBOOT_INTEGRATION_H__

From 53b2b50241ee691285d2e44295f429d3d0883614 Mon Sep 17 00:00:00 2001
From: Angus Gratton <gus@projectgus.com>
Date: Wed, 23 Mar 2016 19:44:27 +1100
Subject: [PATCH 07/13] rboot: Add cryptographic digest support for OTA images
 & SHA256 example

---
 examples/ota_basic/Makefile    |  2 +-
 examples/ota_basic/ota_basic.c | 47 +++++++++++++++++++++++++++++++---
 extras/rboot-ota/ota-tftp.c    |  4 ++-
 extras/rboot-ota/rboot-api.c   | 14 ++++++++++
 extras/rboot-ota/rboot-api.h   | 27 +++++++++++++++++++
 5 files changed, 88 insertions(+), 6 deletions(-)

diff --git a/examples/ota_basic/Makefile b/examples/ota_basic/Makefile
index 84649c1..aafb05a 100644
--- a/examples/ota_basic/Makefile
+++ b/examples/ota_basic/Makefile
@@ -1,5 +1,5 @@
 PROGRAM=ota_basic
 OTA=1
-EXTRA_COMPONENTS=extras/rboot-ota
+EXTRA_COMPONENTS=extras/rboot-ota extras/mbedtls
 include ../../common.mk
 
diff --git a/examples/ota_basic/ota_basic.c b/examples/ota_basic/ota_basic.c
index 1a2d826..68e6c71 100644
--- a/examples/ota_basic/ota_basic.c
+++ b/examples/ota_basic/ota_basic.c
@@ -6,12 +6,14 @@
  *
  * NOT SUITABLE TO PUT ON THE INTERNET OR INTO A PRODUCTION ENVIRONMENT!!!!
  */
+#include <string.h>
 #include "espressif/esp_common.h"
 #include "esp/uart.h"
 #include "FreeRTOS.h"
 #include "task.h"
 #include "esp8266.h"
 #include "ssid_config.h"
+#include "mbedtls/sha256.h"
 
 #include "ota-tftp.h"
 #include "rboot.h"
@@ -21,6 +23,9 @@
 #define TFTP_IMAGE_FILENAME1 "firmware1.bin"
 #define TFTP_IMAGE_FILENAME2 "firmware2.bin"
 
+/* Output of the command 'sha256sum firmware1.bin' */
+static const char *FIRMWARE1_SHA256 = "88199daff8b9e76975f685ec7f95bc1df3c61bd942a33a54a40707d2a41e5488";
+
 void tftp_client_task(void *pvParameters)
 {
     printf("TFTP client task starting...\n");
@@ -43,9 +48,43 @@ void tftp_client_task(void *pvParameters)
         int res = ota_tftp_download(TFTP_IMAGE_SERVER, TFTP_PORT, TFTP_IMAGE_FILENAME1, 1000, slot);
         printf("ota_tftp_download %s result %d\n", TFTP_IMAGE_FILENAME1, res);
         if(res == 0) {
-            printf("Rebooting into slot %d...\n", slot);
-            rboot_set_current_rom(slot);
-            sdk_system_restart();
+            printf("Looks valid, calculating SHA256...\n");
+            uint32_t length;
+            bool valid = rboot_verify_image(conf.roms[slot], &length, NULL);
+            static mbedtls_sha256_context ctx;
+            mbedtls_sha256_init(&ctx);
+            mbedtls_sha256_starts(&ctx, 0);
+            valid = valid && rboot_digest_image(conf.roms[slot], length, (rboot_digest_update_fn)mbedtls_sha256_update, &ctx);
+            static uint8_t hash_result[32];
+            mbedtls_sha256_finish(&ctx, hash_result);
+            mbedtls_sha256_free(&ctx);
+
+            if(!valid)
+            {
+                printf("Not valid after all :(\n");
+            }
+            else
+            {
+                printf("Image SHA256 = ");
+                bool valid = true;
+                for(int i = 0; i < sizeof(hash_result); i++) {
+                    char hexbuf[3];
+                    snprintf(hexbuf, 3, "%02x", hash_result[i]);
+                    printf(hexbuf);
+                    if(strncmp(hexbuf, FIRMWARE1_SHA256+i*2, 2))
+                        valid = false;
+
+                }
+                printf("\n");
+                if (valid) {
+                    printf("SHA256 Matches. Rebooting into slot %d...\n", slot);
+                    rboot_set_current_rom(slot);
+                    sdk_system_restart();
+                }
+                else {
+                    printf("Downloaded image SHA256 didn't match expected '%s'\n", FIRMWARE1_SHA256);
+                }
+            }
         }
         vTaskDelay(5000 / portTICK_RATE_MS);
 
@@ -77,5 +116,5 @@ void user_init(void)
     sdk_wifi_station_set_config(&config);
 
     ota_tftp_init_server(TFTP_PORT);
-    xTaskCreate(&tftp_client_task, (signed char *)"tftp_client", 1024, NULL, 2, NULL);
+    xTaskCreate(&tftp_client_task, (signed char *)"tftp_client", 2048, NULL, 2, NULL);
 }
diff --git a/extras/rboot-ota/ota-tftp.c b/extras/rboot-ota/ota-tftp.c
index 0f4360a..1dee8cd 100644
--- a/extras/rboot-ota/ota-tftp.c
+++ b/extras/rboot-ota/ota-tftp.c
@@ -369,7 +369,9 @@ static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t lim
                it so the client gets an indication if things were successful.
             */
             const char *err = "Unknown validation error";
-            if(!rboot_verify_image(start_offs, *received_len, &err)) {
+            uint32_t image_length;
+            if(!rboot_verify_image(start_offs, &image_length, &err)
+               || image_length != *received_len) {
                 tftp_send_error(nc, TFTP_ERR_ILLEGAL, err);
                 return ERR_VAL;
             }
diff --git a/extras/rboot-ota/rboot-api.c b/extras/rboot-ota/rboot-api.c
index 9283acb..b3faac2 100644
--- a/extras/rboot-ota/rboot-api.c
+++ b/extras/rboot-ota/rboot-api.c
@@ -355,6 +355,20 @@ bool rboot_verify_image(uint32_t initial_offset, uint32_t *image_length, const c
     return false;
 }
 
+bool rboot_digest_image(uint32_t offset, uint32_t image_length, rboot_digest_update_fn update_fn, void *update_ctx)
+{
+    uint8_t buf[32] __attribute__((aligned(4)));
+    for(int i = 0; i < image_length; i += sizeof(buf)) {
+        if(sdk_spi_flash_read(offset+i, buf, sizeof(buf)))
+            return false;
+        uint32_t digest_len = sizeof(buf);
+        if(i + digest_len  > image_length)
+            digest_len = image_length - i;
+        update_fn(update_ctx, buf, digest_len);
+    }
+    return true;
+}
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/extras/rboot-ota/rboot-api.h b/extras/rboot-ota/rboot-api.h
index 5968827..a74eeb2 100644
--- a/extras/rboot-ota/rboot-api.h
+++ b/extras/rboot-ota/rboot-api.h
@@ -144,6 +144,33 @@ uint32_t rboot_get_slot_offset(uint8_t slot);
 **/
 bool rboot_verify_image(uint32_t offset, uint32_t *image_length, const char **error_message);
 
+
+/* @description Digest callback prototype, designed to be compatible with
+   mbedtls digest functions (SHA, MD5, etc.)
+
+   See the ota_basic example to see an example of calculating the
+   SHA256 digest of an OTA image.
+*/
+typedef void (*rboot_digest_update_fn)(void * ctx, void *data, size_t data_len);
+
+/** @description Calculate a digest over the image at the offset specified
+
+    @note This function is actually a generic function that hashes SPI
+   flash contents, doesn't know anything about rboot image format. Use
+   rboot_verify_image to ensure a well-formed OTA image.
+
+   @param offset - Starting offset of image to hash (should be 4 byte aligned.)
+
+   @param image_length - Length of image to hash (should be 4 byte aligned.)
+
+   @param update_fn - Function to update digest (see rboot_digest_update_fn for details)
+
+   @param update_ctx - Context argument for digest update function.
+
+   @return True if digest completes successfully, false if digest function failed part way through
+**/
+bool rboot_digest_image(uint32_t offset, uint32_t image_length, rboot_digest_update_fn update_fn, void *update_ctx);
+
 #ifdef __cplusplus
 }
 #endif

From 1f1881a45237c0c030a89bf225c4e3396207fa12 Mon Sep 17 00:00:00 2001
From: Angus Gratton <gus@projectgus.com>
Date: Thu, 24 Mar 2016 15:35:23 +1100
Subject: [PATCH 08/13] rboot-ota: Always put a checksum in rboot config

Means bootloader will still work if configured to verify the checksum
---
 extras/rboot-ota/rboot-config.h      | 14 --------------
 extras/rboot-ota/rboot-integration.h |  7 +++++++
 extras/rboot-ota/rboot.h             |  9 +++++++--
 3 files changed, 14 insertions(+), 16 deletions(-)
 delete mode 100644 extras/rboot-ota/rboot-config.h

diff --git a/extras/rboot-ota/rboot-config.h b/extras/rboot-ota/rboot-config.h
deleted file mode 100644
index 5ca80a6..0000000
--- a/extras/rboot-ota/rboot-config.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef _RBOOT_CONFIG_H
-/* rboot configuration parameters.
-
-   Must match the config in the compiler bootloader rboot.h. Values below are the rboot.h defaults.
-
-   Override rboot parameters by editing this file, or (much better
-   alternative) copy rboot-config.h to your program directory or
-   program/include/ directory, then edit it to override these values.
-*/
-
-#define RBOOT_MAX_ROMS 4
-//#define RBOOT_CONFIG_CHECKSUM
-
-#endif
diff --git a/extras/rboot-ota/rboot-integration.h b/extras/rboot-ota/rboot-integration.h
index 199d7a2..064217b 100644
--- a/extras/rboot-ota/rboot-integration.h
+++ b/extras/rboot-ota/rboot-integration.h
@@ -28,4 +28,11 @@
 #define RBOOT_DEBUG(f_, ...)
 #endif
 
+/* Enable checksumming when writing out the config,
+   so if the bootloader is built with checksumming then
+   it will still work.
+*/
+#define BOOT_CONFIG_CHKSUM
+
+
 #endif // __RBOOT_INTEGRATION_H__
diff --git a/extras/rboot-ota/rboot.h b/extras/rboot-ota/rboot.h
index e09aa7e..750c4d7 100644
--- a/extras/rboot-ota/rboot.h
+++ b/extras/rboot-ota/rboot.h
@@ -18,8 +18,13 @@ extern "C" {
 // if you aren't using gcc you may need to do this
 //#define BOOT_NO_ASM
 
-// uncomment to have a checksum on the boot config
-//#define BOOT_CONFIG_CHKSUM
+// Note: enabling RBOOT_CONFIG_CHECKSUM here means the config sector
+// is always written by esp-open-rtos with a checksum.
+//
+// This means it will work whether the bootloader has config
+// checksumming enabled or not.
+
+#define BOOT_CONFIG_CHKSUM
 
 // uncomment to enable big flash support (>1MB)
 #define BOOT_BIG_FLASH

From d9202af2aab2a692c32772ac7772267701a527e8 Mon Sep 17 00:00:00 2001
From: Angus Gratton <gus@projectgus.com>
Date: Sat, 7 May 2016 17:25:28 +1000
Subject: [PATCH 09/13] Use latest upstream rboot, always build with OTA - use
 prebuilt rboot if none is compiled locally.

---
 bootloader/Makefile                           |  48 ++---
 bootloader/README.md                          |  11 +-
 bootloader/firmware_prebuilt/blank_config.bin | Bin 0 -> 2048 bytes
 bootloader/firmware_prebuilt/rboot.bin        | Bin 0 -> 3104 bytes
 bootloader/rboot                              |   2 +-
 bootloader/rboot.h                            |  23 +++
 common.mk                                     | 172 +-----------------
 core/spiflash-cache-enable.S                  |   4 -
 examples/ota_basic/Makefile                   |   1 -
 examples/ota_basic/ota_basic.c                |   1 -
 extras/rboot-ota/component.mk                 |   5 +-
 extras/rboot-ota/ota-tftp.c                   |   1 -
 extras/rboot-ota/rboot-api.c                  |   2 +-
 extras/rboot-ota/rboot-api.h                  |   1 +
 extras/rboot-ota/rboot-integration.h          |   4 +
 extras/rboot-ota/rboot.h                      | 116 ------------
 ld/nonota.ld                                  |  15 --
 ld/ota.ld                                     |  15 --
 ld/{common.ld => program.ld}                  |  18 +-
 parameters.mk                                 | 141 ++++++++++++++
 20 files changed, 216 insertions(+), 364 deletions(-)
 create mode 100644 bootloader/firmware_prebuilt/blank_config.bin
 create mode 100644 bootloader/firmware_prebuilt/rboot.bin
 create mode 100644 bootloader/rboot.h
 delete mode 100644 extras/rboot-ota/rboot.h
 delete mode 100644 ld/nonota.ld
 delete mode 100644 ld/ota.ld
 rename ld/{common.ld => program.ld} (93%)
 create mode 100644 parameters.mk

diff --git a/bootloader/Makefile b/bootloader/Makefile
index 64a0ebc..1928d01 100644
--- a/bootloader/Makefile
+++ b/bootloader/Makefile
@@ -1,34 +1,17 @@
 # This is a wrapper around the rboot makefile, which gives us the parameters
-# we need to use rboot with esp-open-rtos
+# we need to use rboot with esp-open-rtos.
+#
+# Use 'make bootloader' to build a custom bootloader.
+#
+# 'make flash' for any esp-open-rtos program will use the compiled
+# bootloader if it exists, or a prebuilt bootloader if no custom
+# bootloader was compiled.
 #
 # The wrapper means we don't require esptool2 in the build process, so we can just use
 # esptool.py (still need xxd, grep, sed to generate the header - see below.)
+BOOTLOADER_DIR:=$(dir $(abspath $(lastword $(MAKEFILE_LIST))))
 
-BUILD_DIR ?= build
-FIRMWARE_DIR ?= firmware
-
-# RBOOT configuration parameters.
-# RBOOT_BIG_FLASH is required for esp-open-rtos.
-export RBOOT_BIG_FLASH    = 1
-
-export RBOOT_BUILD_BASE=$(abspath $(BUILD_DIR))
-
-# Default ESPTOOL params, all the same as when using normal esp-open-rtos makefiles
-ESPTOOL ?= esptool.py
-ESPPORT ?= /dev/ttyUSB0
-ESPBAUD ?= 115200
-
-FLASH_SIZE ?= 16
-FLASH_MODE ?= qio
-FLASH_SPEED ?= 40
-
-ESPTOOL_ARGS=-fs $(FLASH_SIZE)m -fm $(FLASH_MODE) -ff $(FLASH_SPEED)m
-
-ifeq ("$(V)","1")
-Q :=
-else
-Q := @
-endif
+include ../parameters.mk
 
 all: $(FIRMWARE_DIR)/rboot.bin
 
@@ -52,14 +35,19 @@ $(BUILD_DIR)/rboot-hex2a.h: $(BUILD_DIR)/rboot-stage2a.elf $(BUILD_DIR)
 	$(Q) echo "const uint32 _text_addr = 0x$$(xtensa-lx106-elf-objdump -h -j .text $< | grep ".text" | grep -o '401.....' | head -n1);" >> $@.in
 	$(Q) mv $@.in $@
 
+
+RBOOT_BUILD_BASE="$(abspath $(BUILD_DIR))"
+RBOOT_FW_BASE="$(abspath $(FIRMWARE_DIR))"
+MAKE_VARS=RBOOT_EXTRA_INCDIR=$(BOOTLOADER_DIR) RBOOT_BUILD_BASE=$(RBOOT_BUILD_BASE) RBOOT_FW_BASE=$(RBOOT_FW_BASE)
+
 $(BUILD_DIR)/rboot-stage2a.elf: $(BUILD_DIR)
-	$(Q) $(MAKE) -C rboot $(RBOOT_BUILD_BASE)/rboot-stage2a.elf
+	$(Q) $(MAKE) -C rboot $(RBOOT_BUILD_BASE)/rboot-stage2a.elf $(MAKE_VARS)
 
 $(BUILD_DIR)/rboot.elf: $(BUILD_DIR)/rboot-hex2a.h
-	$(Q) $(MAKE) -C rboot $(RBOOT_BUILD_BASE)/rboot.elf
+	$(Q) $(MAKE) -C rboot $(RBOOT_BUILD_BASE)/rboot.elf $(MAKE_VARS)
 
 $(BUILD_DIR) $(FIRMWARE_DIR):
-	$(Q) mkdir -p "$@"
+	mkdir -p $@
 
 flash: $(FIRMWARE_DIR)/rboot.bin
 	$(Q) $(ESPTOOL) -p $(ESPPORT) -b $(ESPBAUD) write_flash $(ESPTOOL_ARGS) 0x0 $<
@@ -68,4 +56,4 @@ clean:
 	$(Q) rm -rf $(BUILD_DIR)
 	$(Q) rm -rf $(FIRMWARE_DIR)
 
-.PHONY: all clean flash erase_config
+.PHONY: all
diff --git a/bootloader/README.md b/bootloader/README.md
index 6527dba..710205b 100644
--- a/bootloader/README.md
+++ b/bootloader/README.md
@@ -1,11 +1,12 @@
 OTA Bootloader (rboot) source module and support files.
 
-Can be used to build an esp-open-rtos compatible rboot bootloader, for use when OTA=1.
-
-It is also possible to use the upstream rboot verbatim, but *ensure that the `RBOOT_BIG_FLASH` option is enabled or images in slots other than 0 won't work correctly.
-
 rboot is an open source bootloader by Richard Burton:
 https://github.com/raburton/rboot
 
-See the contents of the 'rboot' directory for more information.
+Can be used to build an esp-open-rtos compatible rboot bootloader. Run 'make bootloader' in this directory to compile a new bootloader.
 
+Compiling a new bootloader is optional, there's a prebuilt one in the "firmware_prebuilt" directory that will be used if no new bootloader was compiled.
+
+It is also possible to use rboot from upstream verbatim, but *ensure that the `RBOOT_BIG_FLASH` option is enabled or images in slots other than 0 won't work correctly.
+
+See the contents of the 'rboot' directory for more information on rboot.
diff --git a/bootloader/firmware_prebuilt/blank_config.bin b/bootloader/firmware_prebuilt/blank_config.bin
new file mode 100644
index 0000000000000000000000000000000000000000..e9784eb4c8849062d374dd2058af8814b024f3bd
GIT binary patch
literal 2048
ccmZQz7zLvtFd71*Aut*OqaiRF0wXO100;m80RR91

literal 0
HcmV?d00001

diff --git a/bootloader/firmware_prebuilt/rboot.bin b/bootloader/firmware_prebuilt/rboot.bin
new file mode 100644
index 0000000000000000000000000000000000000000..6cc646c3bae7c7b7fb7d3930d4f9b4a36d2f1f55
GIT binary patch
literal 3104
zcmb7G4Nw%<9e;1{0|Lu=cPL@NOy4eXK+Tc8<szWDu(u+h$k$bg(=;<I9)}l^Lp&sL
zk`6_qq}UE>ClaR_kJP9!X~tU2*lE%n*AzeI%(T>|NvEb)rkSYqgTYiK;=TUg9;T#|
znND`*x9|UZ@Bjbrd%t~OmpLT;E!C<ZWL3UMMc?|FUyYQ#L(+gP;>PohoK_vxR*h*Z
z$F-_Sty0t!e1}F5+AZ^3#rKhnkr=+5zt1KLii~mpxU6{`1~U&|m2F5obR~b^N6>V}
z>Bi@xV$7<BQ(217S&GIJcOhgvmaWH3hHqraR^A|MD*uqf;>P?Ad{K8(r`FlVKTOS<
zml6K~xn>fF(0R+}C-8aBk>bWRe5bY?|A{$v3d<XNZ~ZtUvU^`ovnRg9CONhfC3LPk
zay>FV{Lb#ur?JC0Xv!@g5T)w1t*)D-K18ZQ$_i=X3yCOQN}WUsx|B(2*FNj$gJ`K!
zG$2AH^E`xj!%{)zP;Q<=rPC+M7csJ!k%@`W=@TxUFH1J&>!dnKa&+=kl4L9rL=MTn
zGegqTMF$&eZ(d801vNL1kdQ2RL`^NWb2}rHpCp9@#y=OonjHUS5u@97ZuB(@QgT8y
z{GF(<YD`!;E>ukll?QAY6QDPxv<%r$Q<)KE8*k8qiYgqQ8+i9I?MvElmu{Y--}62r
zYEv}6*7i)xDzT|3(jkUB)<+@)RdI`!K(W#wMyH?Ldu=C=HeaO;*Qs_Dg0_i3AZS~c
zBKC9?MViEL)AGn77X*~B04PiC5XIXwChQ{<4>%vc&71R&e5W;YzSJ?db$^z^-qy}=
zgmg#Z-L}en<BD5!6PN0AnC&B0z3+$^5h5G_CM>Z62)f#nD9ip~qO1Vm0w{phfOP;L
zpaakY=mQJ@1_5EfalpHPGk^<#IA9v!>P?g(NS)0}f=~&R>i}(lLBJ_M46xyCSKcKk
z(X>_Jp$}GwnajEah&_yyL)l0{tB|r9FbwvdFR6VbE?l4wY(A@f(s0=`<%G3^|IPj#
zo5ehb7c*NpPw@mZL~I}rdtzh4S&FZ+ijfE%ornB(t1wEFOJn3cT46&7`9+HmrFdNT
z`ZcZKkzck5zoqz7z2+nxO+kLuD*T4xPxP7-w88*!!YPXXtXI52g?A_(W44I7#wZeg
zMe&D>R#+gvofnQ%d`{<S?0dZM3yMc|ZU!#%!ZC`YI)~pQyhic+j0mq&{2nvdV2F$5
zg{D)w-<)LLgA@<zJe}pW2tT9vxXx1$Jp9e#y}U3;@tb-b%iT}$QAUJ)6d%@&y%hgk
zH}+6Gq#FYimOO|5k^n!p!3_N)KT~Cqudrq{oNX0|97cFQqivb82l4R2e4gwvIeRH@
zFBQ%@@qnJ|g=DUuFf#r}>uN6k1mQpMhVdjem|`@`YvKEPR8Rb;HlE`%2lBSVI_<Qi
zPLJ&>S?0Dp%^fx5is7|Vby1~!v%VlyuY^dsB!_QEE~%b$kvyrsq>B_u2MYHOT456r
zIWw?PI|3JnrI$nfwYlD&BB}CZX)jC>W=c}1^5o)Pj$4*&AibQSI%&@9<%~}y&9q+5
zv{5%7J0`57Jf9<;<%;)Lix%U9&2HI}B4Sg1kyWUocnuqygCBhx!6TGYem<^aRnBTU
zX-UZ`I%^m;BCV31=W_fD&7+w4<cQH`*w+n^h0kDlO6VW%5$s`cV;%$lkoD1Uf%yCd
zIZBI%xt%W~)2s9a5QyhGbcuGa&KkE?VASa4D?CV8NZoU=%!2t{9jb@VjPC$Fd-Ar!
zR$prbNvFrUi*-FWfSyg?peKk4k}^Hlb&(e7M}^&k){3N_e#xAPnwg$n(DR90wcalk
z4=?qCp3H1&3Kb7O;srgE4T2Z+Oqy9<&@*YKdpVODHM3CH8>>0Dh5vvzHP@#8{!Pkr
z<|Z^Um3aQ5dya$0hz%p1!kqu84mD#_`Ymf&`1W3lFqyzpY)XRv8iWe+(&b`z?8Sc2
zB}<(KSz|R5k<iXG<h-5$Bc~|pF+6AB?f5zi68@U7Itft;{uiU#XNesDvu5Gb#6k<t
ziM2-I?};3-fQzYOt;viLFG9(`G@xsF2(%0{Eu*z&xi*E#IljO2aW*~sK+c~3C+CaO
zvBD#-*g#Hwe)h5X**ja$KG3r?sb`<m(*t@gPU^`TNK#K`&P(da%rww*qgoH9>zMY-
zL~m_sj{i9`TUiKJm>5=8yiA27iHS0T-_pnbdct`)p@!pM(H!wvINV_lWzD}d+D`Wj
zB@Wn9H=ofyZn)&R<Ft<uc4!!3Z@!02M`^~K7#uywIZaCpVFS_+6lgQDjS%+m3xGJ7
z-}YhmAhy3J??U_zH-?K4T!+qcU)_fAZ8jl9jH_&Lt5G#ie=KX<=-~Csi@w+2vGvD3
z#&5l^4#6^X<7;zn4|B>tNsR?xVxf)<*v>3*8o3g~pRtN77aJeF2d8}c<b)DZHKoyb
zH+kTTg6A8~d>H>N7=M_9QD>xes=;80Y;_j83MJwooq_cmv`(Ma+|?Nhwkxfhv_M<o
z`rxLFG*r>1ZP`G+8`##el)zDoT%@Ks5P}AH<XLvfEZe!{zoclEQ&;=O_F#9r9-Pcu
z6ZFmGZCF{S*EiIyUBis>8D2iaXZq36(&B@TKtCi>Q?+fTA4>|0?=yK;eaS34IpVsG
zP$1a8l&FDL;%6h%6MVhRyJk7@X(26{`^nBgDA3+Yd@X*ht1U#DnQ-fa?fyV(A@f+-
zzEx`r_(-T_QwQk`ZX#P++FI6!T6}sd95mq1;_tV!>iaRX-5<IC1>HGFq?!0NEU*4i
z=3TJBw~z$3kY??EelOH6@O{0mHb`27L7(0oZ7cjst+}n`fBM6+EtpA?x;;_G!2ap1
GC;dN{7~$*y

literal 0
HcmV?d00001

diff --git a/bootloader/rboot b/bootloader/rboot
index 4cf6132..30afbaa 160000
--- a/bootloader/rboot
+++ b/bootloader/rboot
@@ -1 +1 @@
-Subproject commit 4cf6132f6ee624318c04b2a8d9e37da64b7c283c
+Subproject commit 30afbaa777e00abf9d7d469fb3345f118c4975c1
diff --git a/bootloader/rboot.h b/bootloader/rboot.h
new file mode 100644
index 0000000..9260a64
--- /dev/null
+++ b/bootloader/rboot.h
@@ -0,0 +1,23 @@
+/* rboot header overrides
+
+   This "wrapper" header contains default values for building rboot
+   on/for esp-open-rtos. It gets included both when building the
+   bootloader and when building extras/rboot-ota support. It includes
+   the default bootloader/rboot/rboot.h header via the gcc
+   include_next mechanism.
+*/
+#ifndef __RBOOT_H__
+
+// Big flash support is required for esp-open-rtos (we use 8Mbit
+// "slots" only.)
+#define BOOT_BIG_FLASH
+
+// enable 2 way communication between
+// rBoot and the user app via the esp rtc data area
+#define BOOT_RTC_ENABLED
+
+// Call 'main' rboot.h to pick up defaults for other parameters
+#include_next "rboot.h"
+
+#endif
+
diff --git a/common.mk b/common.mk
index 73a998d..172fbe2 100644
--- a/common.mk
+++ b/common.mk
@@ -20,149 +20,7 @@
 # assume the 'root' directory (ie top of the tree) is the directory common.mk is in
 ROOT := $(dir $(lastword $(MAKEFILE_LIST)))
 
-# include optional local overrides at the root level, then in program directory
-#
-# Create either of these files for local system overrides if possible,
-# instead of editing this makefile directly.
--include $(ROOT)local.mk
--include local.mk
-
-# Flash size in megabits
-# Valid values are same as for esptool.py - 2,4,8,16,32
-FLASH_SIZE ?= 16
-
-# Flash mode, valid values are same as for esptool.py - qio,qout,dio.dout
-FLASH_MODE ?= qio
-
-# Flash speed in MHz, valid values are same as for esptool.py - 80, 40, 26, 20
-FLASH_SPEED ?= 40
-
-# Output directories to store intermediate compiled files
-# relative to the program directory
-BUILD_DIR ?= $(PROGRAM_DIR)build/
-FIRMWARE_DIR ?= $(PROGRAM_DIR)firmware/
-
-# esptool.py from https://github.com/themadinventor/esptool
-ESPTOOL ?= esptool.py
-# serial port settings for esptool.py
-ESPPORT ?= /dev/ttyUSB0
-ESPBAUD ?= 115200
-
-# set this to 0 if you don't need floating point support in printf/scanf
-# this will save approx 14.5KB flash space and 448 bytes of statically allocated
-# data RAM
-#
-# NB: Setting the value to 0 requires a recent esptool.py (Feb 2016 / commit ebf02c9)
-PRINTF_SCANF_FLOAT_SUPPORT ?= 1
-
-# Set OTA to 1 to build an image that supports rBoot OTA bootloader
-#
-# Requires 16mbit or higher flash sizes, with 8mbit
-# images for each OTA "slot"
-OTA ?= 0
-
-ifeq ($(OTA),1)
-# Tell C preprocessor that we're building for OTA
-CPPFLAGS = -DOTA
-endif
-
-FLAVOR ?= release # or debug
-
-# Compiler names, etc. assume gdb
-CROSS ?= xtensa-lx106-elf-
-
-# Path to the filteroutput.py tool
-FILTEROUTPUT ?= $(ROOT)/utils/filteroutput.py
-
-AR = $(CROSS)ar
-CC = $(CROSS)gcc
-CPP = $(CROSS)cpp
-LD = $(CROSS)gcc
-NM = $(CROSS)nm
-C++ = $(CROSS)g++
-SIZE = $(CROSS)size
-OBJCOPY = $(CROSS)objcopy
-OBJDUMP = $(CROSS)objdump
-
-# Source components to compile and link. Each of these are subdirectories
-# of the root, with a 'component.mk' file.
-COMPONENTS     ?= $(EXTRA_COMPONENTS) FreeRTOS lwip core
-
-# binary esp-iot-rtos SDK libraries to link. These are pre-processed prior to linking.
-SDK_LIBS		?= main net80211 phy pp wpa
-
-# open source libraries linked in
-LIBS ?= hal gcc c
-
-# set to 0 if you want to use the toolchain libc instead of esp-open-rtos newlib
-OWN_LIBC ?= 1
-
-# Note: you will need a recent esp
-ENTRY_SYMBOL ?= call_user_start
-
-# Set this to zero if you don't want individual function & data sections
-# (some code may be slightly slower, linking will be slighty slower,
-# but compiled code size will come down a small amount.)
-SPLIT_SECTIONS ?= 1
-
-# Set this to 1 to have all compiler warnings treated as errors (and stop the
-# build).  This is recommended whenever you are working on code which will be
-# submitted back to the main project, as all submitted code will be expected to
-# compile without warnings to be accepted.
-WARNINGS_AS_ERRORS ?= 0
-
-# Common flags for both C & C++_
-C_CXX_FLAGS ?= -Wall -Wl,-EL -nostdlib $(EXTRA_C_CXX_FLAGS)
-# Flags for C only
-CFLAGS		?= $(C_CXX_FLAGS) -std=gnu99 $(EXTRA_CFLAGS)
-# Flags for C++ only
-CXXFLAGS	?= $(C_CXX_FLAGS) -fno-exceptions -fno-rtti $(EXTRA_CXXFLAGS)
-
-# these aren't technically preprocesor args, but used by all 3 of C, C++, assembler
-CPPFLAGS	+= -mlongcalls -mtext-section-literals
-
-LDFLAGS		= -nostdlib -Wl,--no-check-sections -L$(BUILD_DIR)sdklib -L$(ROOT)lib -u $(ENTRY_SYMBOL) -Wl,-static -Wl,-Map=$(BUILD_DIR)$(PROGRAM).map $(EXTRA_LDFLAGS)
-
-ifeq ($(WARNINGS_AS_ERRORS),1)
-    C_CXX_FLAGS += -Werror
-endif
-
-ifeq ($(SPLIT_SECTIONS),1)
-  C_CXX_FLAGS += -ffunction-sections -fdata-sections
-  LDFLAGS += -Wl,-gc-sections
-endif
-
-ifeq ($(FLAVOR),debug)
-    C_CXX_FLAGS += -g -O0
-    LDFLAGS += -g -O0
-else ifeq ($(FLAVOR),sdklike)
-    # These are flags intended to produce object code as similar as possible to
-    # the output of the compiler used to build the SDK libs (for comparison of
-    # disassemblies when coding replacement routines).  It is not normally
-    # intended to be used otherwise.
-    CFLAGS += -O2 -Os -fno-inline -fno-ipa-cp -fno-toplevel-reorder
-    LDFLAGS += -O2
-else
-    C_CXX_FLAGS += -g -O2
-    LDFLAGS += -g -O2
-endif
-
-GITSHORTREV=\"$(shell cd $(ROOT); git rev-parse --short -q HEAD 2> /dev/null)\"
-ifeq ($(GITSHORTREV),\"\")
-  GITSHORTREV="\"(nogit)\"" # (same length as a short git hash)
-endif
-CPPFLAGS += -DGITSHORTREV=$(GITSHORTREV)
-
-ifeq ($(OTA),0)
-LINKER_SCRIPTS  = $(ROOT)ld/nonota.ld
-else
-LINKER_SCRIPTS = $(ROOT)ld/ota.ld
-endif
-LINKER_SCRIPTS += $(ROOT)ld/common.ld $(ROOT)ld/rom.ld
-
-####
-#### no user configurable options below here
-####
+include $(ROOT)parameters.mk
 
 ifndef PROGRAM
 $(error "Set the PROGRAM environment variable in your Makefile before including common.mk")
@@ -184,20 +42,7 @@ LIB_ARGS      = $(addprefix -l,$(LIBS))
 PROGRAM_OUT   = $(BUILD_DIR)$(PROGRAM).out
 LDFLAGS      += $(addprefix -T,$(LINKER_SCRIPTS))
 
-# firmware tool arguments
-ESPTOOL_ARGS=-fs $(FLASH_SIZE)m -fm $(FLASH_MODE) -ff $(FLASH_SPEED)m
-
-ifeq ($(OTA),0)
-# for non-OTA, we create two different files for uploading into the flash
-# these are the names and options to generate them
-FW_ADDR_1	= 0x00000
-FW_ADDR_2	= 0x20000
-FW_FILE_1    = $(addprefix $(FIRMWARE_DIR),$(FW_ADDR_1).bin)
-FW_FILE_2    = $(addprefix $(FIRMWARE_DIR),$(FW_ADDR_2).bin)
-else
-# for OTA, it's a single monolithic image
-FW_FILE = $(addprefix $(FIRMWARE_DIR),$(PROGRAM)-ota.bin)
-endif
+FW_FILE = $(addprefix $(FIRMWARE_DIR),$(PROGRAM).bin)
 
 # Common include directories, shared across all "components"
 # components will add their include directories to this argument
@@ -223,7 +68,7 @@ Q := @
 vecho := @echo
 endif
 
-.PHONY: all clean debug_print flash flash_bootloader erase_flash
+.PHONY: all clean flash erase_flash
 
 all: $(PROGRAM_OUT) $(FW_FILE_1) $(FW_FILE_2) $(FW_FILE)
 
@@ -364,17 +209,8 @@ $(FW_FILE): $(PROGRAM_OUT) $(FIRMWARE_DIR)
 	$(vecho) "FW $@"
 	$(Q) $(ESPTOOL) elf2image --version=2 $(ESPTOOL_ARGS) $< -o $(FW_FILE)
 
-ifeq ($(OTA),0)
-flash: $(FW_FILE_1) $(FW_FILE_2)
-	$(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) write_flash $(ESPTOOL_ARGS) $(FW_ADDR_2) $(FW_FILE_2) $(FW_ADDR_1) $(FW_FILE_1)
-else
 flash: $(FW_FILE)
-	$(vecho) "Flashing OTA image slot 0 (bootloader not updated)"
-	$(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) write_flash $(ESPTOOL_ARGS) 0x2000 $(FW_FILE)
-endif
-
-flash_bootloader:
-	$(MAKE) -C $(ROOT)/bootloader flash
+	$(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) write_flash $(ESPTOOL_ARGS) 0x0 $(RBOOT_BIN) 0x1000 $(RBOOT_CONF) 0x2000 $(FW_FILE)
 
 erase_flash:
 	$(ESPTOOL) -p $(ESPPORT) --baud $(ESPBAUD) erase_flash
diff --git a/core/spiflash-cache-enable.S b/core/spiflash-cache-enable.S
index 2363a98..3e06f4a 100644
--- a/core/spiflash-cache-enable.S
+++ b/core/spiflash-cache-enable.S
@@ -12,8 +12,6 @@
  * Copyright (C) 2015 Superhouse Automation Pty Ltd
  * BSD Licensed as described in the file LICENSE
 */
-#ifdef OTA
-
 #define RBOOT_CONFIG_BASE (0x40200000 + 0x1000)
 #define RBOOT_ROMS_OFFS 0x8 /* offset of rboot_config_t.roms array in config */
 
@@ -74,5 +72,3 @@ Cache_Read_Enable:
 	movi a0, cache_return_save /* restore a0 return address */
 	l32i a0, a0, 0
 	ret.n
-
-#endif
diff --git a/examples/ota_basic/Makefile b/examples/ota_basic/Makefile
index aafb05a..3f2ea5a 100644
--- a/examples/ota_basic/Makefile
+++ b/examples/ota_basic/Makefile
@@ -1,5 +1,4 @@
 PROGRAM=ota_basic
-OTA=1
 EXTRA_COMPONENTS=extras/rboot-ota extras/mbedtls
 include ../../common.mk
 
diff --git a/examples/ota_basic/ota_basic.c b/examples/ota_basic/ota_basic.c
index 68e6c71..9ab3f20 100644
--- a/examples/ota_basic/ota_basic.c
+++ b/examples/ota_basic/ota_basic.c
@@ -16,7 +16,6 @@
 #include "mbedtls/sha256.h"
 
 #include "ota-tftp.h"
-#include "rboot.h"
 #include "rboot-api.h"
 
 #define TFTP_IMAGE_SERVER "192.168.1.23"
diff --git a/extras/rboot-ota/component.mk b/extras/rboot-ota/component.mk
index 690b7bc..62cf14f 100644
--- a/extras/rboot-ota/component.mk
+++ b/extras/rboot-ota/component.mk
@@ -1,8 +1,9 @@
 # Component makefile for extras/rboot-ota
 
-INC_DIRS += $(rboot-ota_ROOT)
+# global include directories need to find rboot.h for integration, even 
+# when just including rboot-api.h :(
+INC_DIRS += $(rboot-ota_ROOT) $(ROOT)bootloader $(ROOT)bootloader/rboot
 
-# args for passing into compile rule generation
 rboot-ota_SRC_DIR =  $(rboot-ota_ROOT)
 
 $(eval $(call component_compile_rules,rboot-ota))
diff --git a/extras/rboot-ota/ota-tftp.c b/extras/rboot-ota/ota-tftp.c
index 1dee8cd..b6472a3 100644
--- a/extras/rboot-ota/ota-tftp.c
+++ b/extras/rboot-ota/ota-tftp.c
@@ -22,7 +22,6 @@
 #include <espressif/esp_system.h>
 
 #include "ota-tftp.h"
-#include "rboot.h"
 #include "rboot-api.h"
 
 #define TFTP_FIRMWARE_FILE "firmware.bin"
diff --git a/extras/rboot-ota/rboot-api.c b/extras/rboot-ota/rboot-api.c
index b3faac2..b9a5e57 100644
--- a/extras/rboot-ota/rboot-api.c
+++ b/extras/rboot-ota/rboot-api.c
@@ -8,7 +8,7 @@
 // esp-open-rtos additions Copyright 2016 Angus Gratton
 //////////////////////////////////////////////////
 
-#include <rboot.h>
+#include <rboot-api.h>
 #include <string.h>
 //#include <c_types.h>
 //#include <spi_flash.h>
diff --git a/extras/rboot-ota/rboot-api.h b/extras/rboot-ota/rboot-api.h
index a74eeb2..29c11bd 100644
--- a/extras/rboot-ota/rboot-api.h
+++ b/extras/rboot-ota/rboot-api.h
@@ -12,6 +12,7 @@
  *  @{
 */
 
+#include <rboot-integration.h>
 #include <rboot.h>
 
 #ifdef __cplusplus
diff --git a/extras/rboot-ota/rboot-integration.h b/extras/rboot-ota/rboot-integration.h
index 064217b..005e9ea 100644
--- a/extras/rboot-ota/rboot-integration.h
+++ b/extras/rboot-ota/rboot-integration.h
@@ -9,6 +9,10 @@
 #include <FreeRTOS.h>
 #include <task.h>
 
+/***************************************************
+ * Platform configuration definitions              *
+ ***************************************************/
+
 #define uint8 uint8_t
 #define uint16 uint16_t
 #define uint32 uint32_t
diff --git a/extras/rboot-ota/rboot.h b/extras/rboot-ota/rboot.h
deleted file mode 100644
index 750c4d7..0000000
--- a/extras/rboot-ota/rboot.h
+++ /dev/null
@@ -1,116 +0,0 @@
-#ifndef __RBOOT_H__
-#define __RBOOT_H__
-
-//////////////////////////////////////////////////
-// rBoot open source boot loader for ESP8266.
-// Copyright 2015 Richard A Burton
-// richardaburton@gmail.com
-// See license.txt for license terms.
-//////////////////////////////////////////////////
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define RBOOT_INTEGRATION
-
-// uncomment to use only c code
-// if you aren't using gcc you may need to do this
-//#define BOOT_NO_ASM
-
-// Note: enabling RBOOT_CONFIG_CHECKSUM here means the config sector
-// is always written by esp-open-rtos with a checksum.
-//
-// This means it will work whether the bootloader has config
-// checksumming enabled or not.
-
-#define BOOT_CONFIG_CHKSUM
-
-// uncomment to enable big flash support (>1MB)
-#define BOOT_BIG_FLASH
-
-// uncomment to enable 2 way communication between
-// rBoot and the user app via the esp rtc data area
-#define BOOT_RTC_ENABLED
-
-// uncomment to enable GPIO booting
-//#define BOOT_GPIO_ENABLED
-
-// uncomment to include .irom0.text section in the checksum
-// roms must be built with esptool2 using -iromchksum option
-//#define BOOT_IROM_CHKSUM
-
-// uncomment to add a boot delay, allows you time to connect
-// a terminal before rBoot starts to run and output messages
-// value is in microseconds
-//#define BOOT_DELAY_MICROS 2000000
-
-// increase if required
-#define MAX_ROMS 4
-
-#define CHKSUM_INIT 0xef
-
-#define SECTOR_SIZE 0x1000
-#define BOOT_CONFIG_SECTOR 1
-
-#define BOOT_CONFIG_MAGIC 0xe1
-#define BOOT_CONFIG_VERSION 0x01
-
-#define MODE_STANDARD 0x00
-#define MODE_GPIO_ROM 0x01
-#define MODE_TEMP_ROM 0x02
-
-#define RBOOT_RTC_MAGIC 0x2334ae68
-#define RBOOT_RTC_READ 1
-#define RBOOT_RTC_WRITE 0
-#define RBOOT_RTC_ADDR 64
-
-#ifdef RBOOT_INTEGRATION
-#include <rboot-integration.h>
-#endif
-
-/** @brief  Structure containing rBoot configuration
- *  @note   ROM addresses must be multiples of 0x1000 (flash sector aligned).
- *          Without BOOT_BIG_FLASH only the first 8Mbit (1MB) of the chip will
- *          be memory mapped so ROM slots containing .irom0.text sections must
- *          remain below 0x100000. Slots beyond this will only be accessible via
- *          spi read calls, so use these for stored resources, not code. With
- *          BOOT_BIG_FLASH the flash will be mapped in chunks of 8MBit (1MB), so
- *          ROMs can be anywhere, but must not straddle two 8MBit (1MB) blocks.
- *  @ingroup rboot
-*/
-typedef struct {
-	uint8 magic;           ///< Our magic, identifies rBoot configuration - should be BOOT_CONFIG_MAGIC
-	uint8 version;         ///< Version of configuration structure - should be BOOT_CONFIG_VERSION
-	uint8 mode;            ///< Boot loader mode (MODE_STANDARD | MODE_GPIO_ROM)
-	uint8 current_rom;     ///< Currently selected ROM (will be used for next standard boot)
-	uint8 gpio_rom;        ///< ROM to use for GPIO boot (hardware switch) with mode set to MODE_GPIO_ROM
-	uint8 count;           ///< Quantity of ROMs available to boot
-	uint8 unused[2];       ///< Padding (not used)
-	uint32 roms[MAX_ROMS]; ///< Flash addresses of each ROM
-#ifdef BOOT_CONFIG_CHKSUM
-	uint8 chksum;          ///< Checksum of this configuration structure (if BOOT_CONFIG_CHKSUM defined)
-#endif
-} rboot_config;
-
-#ifdef BOOT_RTC_ENABLED
-/** @brief  Structure containing rBoot status/control data
- *  @note   This structure is used to, optionally, communicate between rBoot and
- *          the user app. It is stored in the ESP RTC data area.
- *  @ingroup rboot
-*/
-typedef struct {
-	uint32 magic;           ///< Magic, identifies rBoot RTC data - should be RBOOT_RTC_MAGIC
-	uint8 next_mode;        ///< The next boot mode, defaults to MODE_STANDARD - can be set to MODE_TEMP_ROM
-	uint8 last_mode;        ///< The last (this) boot mode - can be MODE_STANDARD, MODE_GPIO_ROM or MODE_TEMP_ROM
-	uint8 last_rom;         ///< The last (this) boot rom number
-	uint8 temp_rom;         ///< The next boot rom number when next_mode set to MODE_TEMP_ROM
-	uint8 chksum;           ///< Checksum of this structure this will be updated for you passed to the API
-} rboot_rtc_data;
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/ld/nonota.ld b/ld/nonota.ld
deleted file mode 100644
index 97054e6..0000000
--- a/ld/nonota.ld
+++ /dev/null
@@ -1,15 +0,0 @@
-/* Memory layout for esp-open-rtos when not using OTA
-   (ie ROM bootloader only, no second stage rboot)
- */
-MEMORY
-{
-  dport0_0_seg :                      	org = 0x3FF00000, len = 0x10
-  dram0_0_seg :                       	org = 0x3FFE8000, len = 0x14000
-  iram1_0_seg :                       	org = 0x40100000, len = 0x08000
-/* irom0 section, mapped from SPI flash
-  - Origin is offset by 0x20000 to leave space for bootloader RAM sections.
-
-  - Length is max 8Mbit of mappable flash, minus start offset
-*/
-  irom0_0_seg :                       	org = 0x40220000, len = (1M - 0x20000)
-}
diff --git a/ld/ota.ld b/ld/ota.ld
deleted file mode 100644
index da32ce1..0000000
--- a/ld/ota.ld
+++ /dev/null
@@ -1,15 +0,0 @@
-/* Memory layout for esp-open-rtos when using OTA second stage bootloader */
-
-MEMORY
-{
-  dport0_0_seg :                      	org = 0x3FF00000, len = 0x10
-  dram0_0_seg :                       	org = 0x3FFE8000, len = 0x14000
-  iram1_0_seg :                       	org = 0x40100000, len = 0x08000
-/* irom0 section, mapped from SPI flash
-  - Origin is offset by 0x2010 to create spacer for second stage bootloader image,
-    header.
-
-  - Length is max 8Mbit of mappable flash, minus start offset
-*/
-  irom0_0_seg :                       	org = 0x40202010, len = (1M - 0x2010)
-}
diff --git a/ld/common.ld b/ld/program.ld
similarity index 93%
rename from ld/common.ld
rename to ld/program.ld
index 444c8e7..88fa0f4 100644
--- a/ld/common.ld
+++ b/ld/program.ld
@@ -1,7 +1,17 @@
-/*
- * Common (OTA and non-OTA) parts for the esp-open-rtos Linker Script
- *
- */
+/* Memory layout for esp-open-rtos when using OTA second stage bootloader */
+MEMORY
+{
+  dport0_0_seg :                      	org = 0x3FF00000, len = 0x10
+  dram0_0_seg :                       	org = 0x3FFE8000, len = 0x14000
+  iram1_0_seg :                       	org = 0x40100000, len = 0x08000
+/* irom0 section, mapped from SPI flash
+  - Origin is offset by 0x2010 to create spacer for second stage bootloader image,
+    header.
+
+  - Length is max 8Mbit of mappable flash, minus start offset
+*/
+  irom0_0_seg :                       	org = 0x40202010, len = (1M - 0x2010)
+}
 
 /* FreeRTOS memory management functions
 
diff --git a/parameters.mk b/parameters.mk
new file mode 100644
index 0000000..e750e83
--- /dev/null
+++ b/parameters.mk
@@ -0,0 +1,141 @@
+# Parameters for the esp-open-rtos make process
+#
+# You can edit this file to change parameters, but a better option is
+# to create a local.mk file and add overrides there. The local.mk file
+# can be in the root directory, or the program directory alongside the
+# Makefile, or both.
+#
+-include $(ROOT)local.mk
+-include local.mk
+
+# Flash size in megabits
+# Valid values are same as for esptool.py - 2,4,8,16,32
+FLASH_SIZE ?= 16
+
+# Flash mode, valid values are same as for esptool.py - qio,qout,dio.dout
+FLASH_MODE ?= qio
+
+# Flash speed in MHz, valid values are same as for esptool.py - 80, 40, 26, 20
+FLASH_SPEED ?= 40
+
+# Output directories to store intermediate compiled files
+# relative to the program directory
+BUILD_DIR ?= $(PROGRAM_DIR)build/
+FIRMWARE_DIR ?= $(PROGRAM_DIR)firmware/
+
+# esptool.py from https://github.com/themadinventor/esptool
+ESPTOOL ?= esptool.py
+# serial port settings for esptool.py
+ESPPORT ?= /dev/ttyUSB0
+ESPBAUD ?= 115200
+
+# firmware tool arguments
+ESPTOOL_ARGS=-fs $(FLASH_SIZE)m -fm $(FLASH_MODE) -ff $(FLASH_SPEED)m
+
+
+# set this to 0 if you don't need floating point support in printf/scanf
+# this will save approx 14.5KB flash space and 448 bytes of statically allocated
+# data RAM
+#
+# NB: Setting the value to 0 requires a recent esptool.py (Feb 2016 / commit ebf02c9)
+PRINTF_SCANF_FLOAT_SUPPORT ?= 1
+
+FLAVOR ?= release # or debug
+
+# Compiler names, etc. assume gdb
+CROSS ?= xtensa-lx106-elf-
+
+# Path to the filteroutput.py tool
+FILTEROUTPUT ?= $(ROOT)/utils/filteroutput.py
+
+AR = $(CROSS)ar
+CC = $(CROSS)gcc
+CPP = $(CROSS)cpp
+LD = $(CROSS)gcc
+NM = $(CROSS)nm
+C++ = $(CROSS)g++
+SIZE = $(CROSS)size
+OBJCOPY = $(CROSS)objcopy
+OBJDUMP = $(CROSS)objdump
+
+# Source components to compile and link. Each of these are subdirectories
+# of the root, with a 'component.mk' file.
+COMPONENTS     ?= $(EXTRA_COMPONENTS) FreeRTOS lwip core
+
+# binary esp-iot-rtos SDK libraries to link. These are pre-processed prior to linking.
+SDK_LIBS		?= main net80211 phy pp wpa
+
+# open source libraries linked in
+LIBS ?= hal gcc c
+
+# set to 0 if you want to use the toolchain libc instead of esp-open-rtos newlib
+OWN_LIBC ?= 1
+
+# Note: you will need a recent esp
+ENTRY_SYMBOL ?= call_user_start
+
+# Set this to zero if you don't want individual function & data sections
+# (some code may be slightly slower, linking will be slighty slower,
+# but compiled code size will come down a small amount.)
+SPLIT_SECTIONS ?= 1
+
+# Set this to 1 to have all compiler warnings treated as errors (and stop the
+# build).  This is recommended whenever you are working on code which will be
+# submitted back to the main project, as all submitted code will be expected to
+# compile without warnings to be accepted.
+WARNINGS_AS_ERRORS ?= 0
+
+# Common flags for both C & C++_
+C_CXX_FLAGS ?= -Wall -Wl,-EL -nostdlib $(EXTRA_C_CXX_FLAGS)
+# Flags for C only
+CFLAGS		?= $(C_CXX_FLAGS) -std=gnu99 $(EXTRA_CFLAGS)
+# Flags for C++ only
+CXXFLAGS	?= $(C_CXX_FLAGS) -fno-exceptions -fno-rtti $(EXTRA_CXXFLAGS)
+
+# these aren't all technically preprocesor args, but used by all 3 of C, C++, assembler
+CPPFLAGS	+= -mlongcalls -mtext-section-literals
+
+LDFLAGS		= -nostdlib -Wl,--no-check-sections -L$(BUILD_DIR)sdklib -L$(ROOT)lib -u $(ENTRY_SYMBOL) -Wl,-static -Wl,-Map=$(BUILD_DIR)$(PROGRAM).map $(EXTRA_LDFLAGS)
+
+ifeq ($(WARNINGS_AS_ERRORS),1)
+    C_CXX_FLAGS += -Werror
+endif
+
+ifeq ($(SPLIT_SECTIONS),1)
+  C_CXX_FLAGS += -ffunction-sections -fdata-sections
+  LDFLAGS += -Wl,-gc-sections
+endif
+
+ifeq ($(FLAVOR),debug)
+    C_CXX_FLAGS += -g -O0
+    LDFLAGS += -g -O0
+else ifeq ($(FLAVOR),sdklike)
+    # These are flags intended to produce object code as similar as possible to
+    # the output of the compiler used to build the SDK libs (for comparison of
+    # disassemblies when coding replacement routines).  It is not normally
+    # intended to be used otherwise.
+    CFLAGS += -O2 -Os -fno-inline -fno-ipa-cp -fno-toplevel-reorder
+    LDFLAGS += -O2
+else
+    C_CXX_FLAGS += -g -O2
+    LDFLAGS += -g -O2
+endif
+
+GITSHORTREV=\"$(shell cd $(ROOT); git rev-parse --short -q HEAD 2> /dev/null)\"
+ifeq ($(GITSHORTREV),\"\")
+  GITSHORTREV="\"(nogit)\"" # (same length as a short git hash)
+endif
+CPPFLAGS += -DGITSHORTREV=$(GITSHORTREV)
+
+LINKER_SCRIPTS += $(ROOT)ld/program.ld $(ROOT)ld/rom.ld
+
+# rboot firmware binary paths for flashing
+RBOOT_BIN = $(ROOT)bootloader/firmware/rboot.bin
+RBOOT_PREBUILT_BIN = $(ROOT)bootloader/firmware_prebuilt/rboot.bin
+RBOOT_CONF = $(ROOT)bootloader/firmware_prebuilt/blank_config.bin
+
+# if a custom bootloader hasn't been compiled, use the
+# prebuilt binary from the source tree
+ifeq (,$(wildcard $(RBOOT_BIN)))
+RBOOT_BIN=$(RBOOT_PREBUILT_BIN)
+endif

From d62fd4899a8e61fd2a27a9b1f3b5d5705b0a8e9c Mon Sep 17 00:00:00 2001
From: Angus Gratton <gus@projectgus.com>
Date: Mon, 23 May 2016 12:23:04 +1000
Subject: [PATCH 10/13] ota_basic example cleanup

---
 examples/ota_basic/ota_basic.c | 115 ++++++++++++++++++++-------------
 1 file changed, 69 insertions(+), 46 deletions(-)

diff --git a/examples/ota_basic/ota_basic.c b/examples/ota_basic/ota_basic.c
index 9ab3f20..61b50b1 100644
--- a/examples/ota_basic/ota_basic.c
+++ b/examples/ota_basic/ota_basic.c
@@ -1,6 +1,8 @@
 /* A very simple OTA example
  *
- * Binds a TCP socket, reads an image from it over TFTP and then flashes live.
+ * Tries to run both a TFTP client and a TFTP server simultaneously, either will accept a TTP firmware and update it.
+ *
+ * Not a realistic OTA setup, this needs adapting (choose either client or server) before you'd want to use it.
  *
  * For more information about esp-open-rtos OTA see https://github.com/SuperHouse/esp-open-rtos/wiki/OTA-Update-Configuration
  *
@@ -18,6 +20,7 @@
 #include "ota-tftp.h"
 #include "rboot-api.h"
 
+/* TFTP client will request this image filenames from this server */
 #define TFTP_IMAGE_SERVER "192.168.1.23"
 #define TFTP_IMAGE_FILENAME1 "firmware1.bin"
 #define TFTP_IMAGE_FILENAME2 "firmware2.bin"
@@ -25,6 +28,68 @@
 /* Output of the command 'sha256sum firmware1.bin' */
 static const char *FIRMWARE1_SHA256 = "88199daff8b9e76975f685ec7f95bc1df3c61bd942a33a54a40707d2a41e5488";
 
+/* Example function to TFTP download a firmware file and verify its SHA256 before
+   booting into it.
+*/
+static void tftpclient_download_and_verify_file1(int slot, rboot_config *conf)
+{
+    printf("Downloading %s to slot %d...\n", TFTP_IMAGE_FILENAME1, slot);
+    int res = ota_tftp_download(TFTP_IMAGE_SERVER, TFTP_PORT+1, TFTP_IMAGE_FILENAME1, 1000, slot);
+    printf("ota_tftp_download %s result %d\n", TFTP_IMAGE_FILENAME1, res);
+
+    if (res != 0) {
+        return;
+    }
+
+    printf("Looks valid, calculating SHA256...\n");
+    uint32_t length;
+    bool valid = rboot_verify_image(conf->roms[slot], &length, NULL);
+    static mbedtls_sha256_context ctx;
+    mbedtls_sha256_init(&ctx);
+    mbedtls_sha256_starts(&ctx, 0);
+    valid = valid && rboot_digest_image(conf->roms[slot], length, (rboot_digest_update_fn)mbedtls_sha256_update, &ctx);
+    static uint8_t hash_result[32];
+    mbedtls_sha256_finish(&ctx, hash_result);
+    mbedtls_sha256_free(&ctx);
+
+    if(!valid)
+    {
+        printf("Not valid after all :(\n");
+        return;
+    }
+
+    printf("Image SHA256 = ");
+    valid = true;
+    for(int i = 0; i < sizeof(hash_result); i++) {
+        char hexbuf[3];
+        snprintf(hexbuf, 3, "%02x", hash_result[i]);
+        printf(hexbuf);
+        if(strncmp(hexbuf, FIRMWARE1_SHA256+i*2, 2))
+            valid = false;
+    }
+    printf("\n");
+
+    if(!valid) {
+        printf("Downloaded image SHA256 didn't match expected '%s'\n", FIRMWARE1_SHA256);
+        return;
+    }
+
+    printf("SHA256 Matches. Rebooting into slot %d...\n", slot);
+    rboot_set_current_rom(slot);
+    sdk_system_restart();
+}
+
+/* Much simpler function that just downloads a file via TFTP into an rboot slot.
+
+   (
+ */
+static void tftpclient_download_file2(int slot)
+{
+    printf("Downloading %s to slot %d...\n", TFTP_IMAGE_FILENAME2, slot);
+    int res = ota_tftp_download(TFTP_IMAGE_SERVER, TFTP_PORT+1, TFTP_IMAGE_FILENAME2, 1000, slot);
+    printf("ota_tftp_download %s result %d\n", TFTP_IMAGE_FILENAME2, res);
+}
+
 void tftp_client_task(void *pvParameters)
 {
     printf("TFTP client task starting...\n");
@@ -43,53 +108,10 @@ void tftp_client_task(void *pvParameters)
        Note: example will reboot into FILENAME1 if it is successfully downloaded, but FILENAME2 is ignored.
     */
     while(1) {
-        printf("Downloading %s to slot %d...\n", TFTP_IMAGE_FILENAME1, slot);
-        int res = ota_tftp_download(TFTP_IMAGE_SERVER, TFTP_PORT, TFTP_IMAGE_FILENAME1, 1000, slot);
-        printf("ota_tftp_download %s result %d\n", TFTP_IMAGE_FILENAME1, res);
-        if(res == 0) {
-            printf("Looks valid, calculating SHA256...\n");
-            uint32_t length;
-            bool valid = rboot_verify_image(conf.roms[slot], &length, NULL);
-            static mbedtls_sha256_context ctx;
-            mbedtls_sha256_init(&ctx);
-            mbedtls_sha256_starts(&ctx, 0);
-            valid = valid && rboot_digest_image(conf.roms[slot], length, (rboot_digest_update_fn)mbedtls_sha256_update, &ctx);
-            static uint8_t hash_result[32];
-            mbedtls_sha256_finish(&ctx, hash_result);
-            mbedtls_sha256_free(&ctx);
-
-            if(!valid)
-            {
-                printf("Not valid after all :(\n");
-            }
-            else
-            {
-                printf("Image SHA256 = ");
-                bool valid = true;
-                for(int i = 0; i < sizeof(hash_result); i++) {
-                    char hexbuf[3];
-                    snprintf(hexbuf, 3, "%02x", hash_result[i]);
-                    printf(hexbuf);
-                    if(strncmp(hexbuf, FIRMWARE1_SHA256+i*2, 2))
-                        valid = false;
-
-                }
-                printf("\n");
-                if (valid) {
-                    printf("SHA256 Matches. Rebooting into slot %d...\n", slot);
-                    rboot_set_current_rom(slot);
-                    sdk_system_restart();
-                }
-                else {
-                    printf("Downloaded image SHA256 didn't match expected '%s'\n", FIRMWARE1_SHA256);
-                }
-            }
-        }
+        tftpclient_download_and_verify_file1(slot, &conf);
         vTaskDelay(5000 / portTICK_RATE_MS);
 
-        printf("Downloading %s to slot %d...\n", TFTP_IMAGE_FILENAME2, slot);
-        res = ota_tftp_download(TFTP_IMAGE_SERVER, TFTP_PORT, TFTP_IMAGE_FILENAME2, 1000, slot);
-        printf("ota_tftp_download %s result %d\n", TFTP_IMAGE_FILENAME2, res);
+        tftpclient_download_file2(slot);
         vTaskDelay(5000 / portTICK_RATE_MS);
     }
 }
@@ -114,6 +136,7 @@ void user_init(void)
     sdk_wifi_set_opmode(STATION_MODE);
     sdk_wifi_station_set_config(&config);
 
+    printf("Starting TFTP server...");
     ota_tftp_init_server(TFTP_PORT);
     xTaskCreate(&tftp_client_task, (signed char *)"tftp_client", 2048, NULL, 2, NULL);
 }

From 84856f80a9c31a12814168330e7865c3ce39fd32 Mon Sep 17 00:00:00 2001
From: Kenshi Kawaguchi <kenshi@recurse.ca>
Date: Wed, 25 May 2016 00:07:13 -0700
Subject: [PATCH 11/13] ota_tftp_download takes an optional receive_cb that
 will report on the status of the TFTP transfer

---
 extras/rboot-ota/ota-tftp.c | 17 ++++++++++++-----
 extras/rboot-ota/ota-tftp.h |  8 +++++++-
 2 files changed, 19 insertions(+), 6 deletions(-)

diff --git a/extras/rboot-ota/ota-tftp.c b/extras/rboot-ota/ota-tftp.c
index b6472a3..99ebdf7 100644
--- a/extras/rboot-ota/ota-tftp.c
+++ b/extras/rboot-ota/ota-tftp.c
@@ -43,7 +43,7 @@
 
 static void tftp_task(void *port_p);
 static char *tftp_get_field(int field, struct netbuf *netbuf);
-static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t limit_offs, size_t *received_len, ip_addr_t *peer_addr, int peer_port);
+static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t limit_offs, size_t *received_len, ip_addr_t *peer_addr, int peer_port, tftp_receive_cb receive_cb);
 static err_t tftp_send_ack(struct netconn *nc, int block);
 static err_t tftp_send_rrq(struct netconn *nc, const char *filename);
 static void tftp_send_error(struct netconn *nc, int err_code, const char *err_msg);
@@ -53,7 +53,8 @@ void ota_tftp_init_server(int listen_port)
     xTaskCreate(tftp_task, (signed char *)"tftpOTATask", 512, (void *)listen_port, 2, NULL);
 }
 
-err_t ota_tftp_download(const char *server, int port, const char *filename, int timeout, int ota_slot)
+err_t ota_tftp_download(const char *server, int port, const char *filename,
+                        int timeout, int ota_slot, tftp_receive_cb receive_cb)
 {
     rboot_config rboot_config = rboot_get_config();
     /* Validate the OTA slot parameter */
@@ -101,7 +102,8 @@ err_t ota_tftp_download(const char *server, int port, const char *filename, int
     }
 
     size_t received_len;
-    err = tftp_receive_data(nc, flash_offset, flash_offset+MAX_IMAGE_SIZE, &received_len, &addr, port);
+    err = tftp_receive_data(nc, flash_offset, flash_offset+MAX_IMAGE_SIZE,
+                            &received_len, &addr, port, receive_cb);
     netconn_delete(nc);
     return err;
 }
@@ -189,7 +191,7 @@ static void tftp_task(void *listen_port)
         /* Finished WRQ phase, start TFTP data transfer */
         size_t received_len;
         netconn_set_recvtimeout(nc, 10000);
-        int recv_err = tftp_receive_data(nc, conf.roms[slot], conf.roms[slot]+MAX_IMAGE_SIZE, &received_len, NULL, 0);
+        int recv_err = tftp_receive_data(nc, conf.roms[slot], conf.roms[slot]+MAX_IMAGE_SIZE, &received_len, NULL, 0, NULL);
 
         netconn_disconnect(nc);
         printf("OTA TFTP receive data result %d bytes %d\r\n", recv_err, received_len);
@@ -240,7 +242,7 @@ static char *tftp_get_field(int field, struct netbuf *netbuf)
 
 #define TFTP_TIMEOUT_RETRANSMITS 10
 
-static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t limit_offs, size_t *received_len, ip_addr_t *peer_addr, int peer_port)
+static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t limit_offs, size_t *received_len, ip_addr_t *peer_addr, int peer_port, tftp_receive_cb receive_cb)
 {
     *received_len = 0;
     const int DATA_PACKET_SZ = 512 + 4; /*( packet size plus header */
@@ -382,6 +384,11 @@ static err_t tftp_receive_data(struct netconn *nc, size_t write_offs, size_t lim
             return ack_err;
         }
 
+        // Make sure ack was successful before calling callback.
+        if(receive_cb) {
+            receive_cb(*received_len);
+        }
+
         if(len < DATA_PACKET_SZ) {
             return ERR_OK;
         }
diff --git a/extras/rboot-ota/ota-tftp.h b/extras/rboot-ota/ota-tftp.h
index cccac2a..6be63aa 100644
--- a/extras/rboot-ota/ota-tftp.h
+++ b/extras/rboot-ota/ota-tftp.h
@@ -3,6 +3,8 @@
 
 #include "lwip/err.h"
 
+typedef void (*tftp_receive_cb)(size_t bytes_received);
+
 /* TFTP Server OTA Support
  *
  * To use, call ota_tftp_init_server() which will start the TFTP server task
@@ -41,8 +43,12 @@ void ota_tftp_init_server(int listen_port);
    Returns 0 on success, LWIP err.h values for errors.
 
    Does not change the current firmware slot, or reboot.
+
+   receive_cb: called repeatedly after each successful packet that
+   has been written to flash and ACKed.  Can pass NULL to omit.
  */
-err_t ota_tftp_download(const char *server, int port, const char *filename, int timeout, int ota_slot);
+err_t ota_tftp_download(const char *server, int port, const char *filename,
+                        int timeout, int ota_slot, tftp_receive_cb receive_cb);
 
 #define TFTP_PORT 69
 

From 7fe2020785dfa360a3f49946f426ec385327dbf4 Mon Sep 17 00:00:00 2001
From: Angus Gratton <gus@projectgus.com>
Date: Thu, 26 May 2016 09:19:40 +1000
Subject: [PATCH 12/13] ota_basic example: Update TFTP client calls

---
 examples/ota_basic/ota_basic.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/examples/ota_basic/ota_basic.c b/examples/ota_basic/ota_basic.c
index 61b50b1..c2050d5 100644
--- a/examples/ota_basic/ota_basic.c
+++ b/examples/ota_basic/ota_basic.c
@@ -34,7 +34,7 @@ static const char *FIRMWARE1_SHA256 = "88199daff8b9e76975f685ec7f95bc1df3c61bd94
 static void tftpclient_download_and_verify_file1(int slot, rboot_config *conf)
 {
     printf("Downloading %s to slot %d...\n", TFTP_IMAGE_FILENAME1, slot);
-    int res = ota_tftp_download(TFTP_IMAGE_SERVER, TFTP_PORT+1, TFTP_IMAGE_FILENAME1, 1000, slot);
+    int res = ota_tftp_download(TFTP_IMAGE_SERVER, TFTP_PORT+1, TFTP_IMAGE_FILENAME1, 1000, slot, NULL);
     printf("ota_tftp_download %s result %d\n", TFTP_IMAGE_FILENAME1, res);
 
     if (res != 0) {
@@ -86,7 +86,7 @@ static void tftpclient_download_and_verify_file1(int slot, rboot_config *conf)
 static void tftpclient_download_file2(int slot)
 {
     printf("Downloading %s to slot %d...\n", TFTP_IMAGE_FILENAME2, slot);
-    int res = ota_tftp_download(TFTP_IMAGE_SERVER, TFTP_PORT+1, TFTP_IMAGE_FILENAME2, 1000, slot);
+    int res = ota_tftp_download(TFTP_IMAGE_SERVER, TFTP_PORT+1, TFTP_IMAGE_FILENAME2, 1000, slot, NULL);
     printf("ota_tftp_download %s result %d\n", TFTP_IMAGE_FILENAME2, res);
 }
 

From 34094d233c9a4497621f3cf23edfd1fcb6e1209d Mon Sep 17 00:00:00 2001
From: Angus Gratton <gus@projectgus.com>
Date: Sat, 28 May 2016 11:31:29 +1000
Subject: [PATCH 13/13] Travis: build rboot bootloader as part of automated
 build

---
 .travis.yml | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/.travis.yml b/.travis.yml
index 0e1b62e..4bd0884 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -32,6 +32,7 @@ addons:
     - python-serial
     - sed
     - git
+    - vim-common
 
 before_install:
   - travis_wait 30 utils/travis_build/install_toolchain.sh
@@ -42,3 +43,5 @@ script:
   - sed -i "s%#error%//#error%" include/ssid_config.h
   # Don't verbose-build all examples (too much output), only verbose-build errors
   - ( ${MAKE_CMD} ) || ( ${MAKE_CMD} V=1 )
+  # build bootloader
+  - make -C bootloader/