From a139c6de516968e2a0e7a21c153404eebea94c9e Mon Sep 17 00:00:00 2001 From: Jonathan McDowell Date: Mon, 3 Sep 2018 20:03:35 +0100 Subject: [PATCH] Add initial OTA upgrade support Add support for receiving a new flash image over HTTP and do a check to see if one is available at boot up. Will program the alternative bank after doing a check for version.txt with a later version number that we're running at present. --- Makefile | 2 +- ota.c | 273 ++++++++++++++++++++++++++++++++++++++++++++++++++++ ota.h | 8 ++ user_main.c | 2 + 4 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 ota.c create mode 100644 ota.h diff --git a/Makefile b/Makefile index 511cfd5..29c1392 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ LDFLAGS = -nostdlib -Wl,--no-check-sections -Wl,--gc-sections -Wl,-static \ -L$(SDKDIR)/xtensa-lx106-elf/lib APP = clock -OBJS = user_main.o max7219.o spi.o clock.o +OBJS = user_main.o clock.o max7219.o ota.o spi.o all: rom0.bin rom1.bin diff --git a/ota.c b/ota.c new file mode 100644 index 0000000..b4b39d2 --- /dev/null +++ b/ota.c @@ -0,0 +1,273 @@ +/* + * Copyright 2018 Jonathan McDowell + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include +#include + +#include "ota.h" +#include "project_config.h" + +struct ota_status { + struct espconn conn; + bool do_update; + uint8_t slot; + uint32_t rcvd_len; + uint32_t content_len; + ip_addr_t server_ip; +}; + +static void ICACHE_FLASH_ATTR ota_finish(struct ota_status *upgrade) +{ + espconn_disconnect(&upgrade->conn); + + if (system_upgrade_flag_check() == UPGRADE_FLAG_FINISH) { + os_printf("Rebooting into new ROM.\n"); + system_upgrade_reboot(); + } else { + system_upgrade_flag_set(UPGRADE_FLAG_IDLE); + } + + return; +} + +static void ICACHE_FLASH_ATTR ota_receive(void *arg, char *buf, + unsigned short len) +{ + uint8_t maj, min; + char *verstr; + char *lenhdr, *data, *ptr; + struct ota_status *upgrade = arg; + int sectors; + + if (!upgrade->do_update) { + if ((os_strncmp(buf + 9, "200", 3) != 0)) { + os_printf("Failed to fetch version info: %.3s\n", + buf + 9); + return; + } + /* We're checking if we need an update; look for the version */ + verstr = os_strstr(buf, "ESP8266-Upgrade-Version: "); + if (!verstr) { + os_printf("Couldn't find version. Got data: %s\n", + buf); + return; + } + verstr += strlen("ESP8266-Upgrade-Version: "); + + maj = strtol(verstr, &verstr, 10); + if (*verstr != '.') { + os_printf("Parsed major version %d, " + "but unexpected %c\n", + maj, *verstr); + } + verstr++; + min = strtol(verstr, NULL, 10); + + os_printf("Got version %d.%d; I have version %d.%d\n", + maj, min, VER_MAJ, VER_MIN); + if (maj > VER_MAJ || (maj == VER_MAJ && min > VER_MIN)) { + os_printf("Need upgrade.\n"); + upgrade->do_update = true; + } + } else { + /* Trying to read the rom image */ + + /* First reply? */ + if (upgrade->content_len == 0) { + if ((lenhdr = os_strstr(buf, "Content-Length: ")) && + (data = os_strstr(lenhdr, "\r\n\r\n")) && + (os_strncmp(buf + 9, "200", 3) == 0)) { + + data += 4; + len -= (data - buf); + + lenhdr += 16; /* Content-Length: */ + ptr = os_strstr(lenhdr, "\r\n"); + *ptr = '\0'; + upgrade->content_len = atoi(lenhdr); + os_printf("Reading %d bytes of image.\n,", + upgrade->content_len); + + if (upgrade->content_len > 0x6B000) { + os_printf("Image too large.\n"); + upgrade->do_update = false; + ota_finish(upgrade); + return; + } + + sectors = (upgrade->content_len + + SPI_FLASH_SEC_SIZE - 1) >> 12; + while (sectors--) { + spi_flash_erase_sector(sectors + + (upgrade->slot ? 129 : 1)); + } + + spi_flash_write( + (upgrade->slot ? 0x81000 : 0x1000), + (void *) data, len); + upgrade->rcvd_len = len; + + } else { + ptr = os_strstr(buf, "\r\n"); + *ptr = '\0'; + os_printf("Error getting ROM data: %s\n", buf); + upgrade->do_update = false; + ota_finish(upgrade); + return; + } + } else { + spi_flash_write((upgrade->slot ? 0x81000 : 0x1000) + + upgrade->rcvd_len, (void *) buf, len); + upgrade->rcvd_len += len; + } + + if (upgrade->rcvd_len == upgrade->content_len) { + upgrade->do_update = false; + system_upgrade_flag_set(UPGRADE_FLAG_FINISH); + ota_finish(upgrade); + } + } +} + +static void ICACHE_FLASH_ATTR ota_sent(void *arg) +{ + /* Callback when all data sent by us down TCP connection */ +} + +static void ICACHE_FLASH_ATTR ota_connect(void *arg) +{ + struct ota_status *upgrade = arg; + int len; + char buf[256]; + + espconn_regist_recvcb(&upgrade->conn, ota_receive); + espconn_regist_sentcb(&upgrade->conn, ota_sent); + + if (upgrade->do_update) { + os_printf("Sending rom image request header.\n"); + len = os_sprintf(buf, "GET %srom%d.bin HTTP/1.0\r\n" + "Host: %s:%d\r\n" + "Connection: close\r\n" + "User-Agent: ESP8266 " PROJECT "\r\n" + "\r\n", + UPGRADE_PATH, + upgrade->slot, + UPGRADE_HOST, + 80); + } else { + os_printf("Sending version check request header.\n"); + len = os_sprintf(buf, "GET %s%s HTTP/1.1\r\n" + "Host: %s:%d\r\n" + "Connection: close\r\n" + "User-Agent: ESP8266 " PROJECT "\r\n" + "\r\n", + UPGRADE_PATH, + "version.txt", + UPGRADE_HOST, + 80); + } + + espconn_send(&upgrade->conn, (uint8_t *) buf, len); +} + +static void ICACHE_FLASH_ATTR ota_disconnect(void *arg) +{ + struct ota_status *upgrade = arg; + + if (upgrade == NULL) { + return; + } + + espconn_delete(&upgrade->conn); + + if (!upgrade->do_update) { + if (upgrade->conn.proto.tcp != NULL) { + os_free(upgrade->conn.proto.tcp); + } + os_free(upgrade); + system_upgrade_flag_set(UPGRADE_FLAG_IDLE); + return; + } + + /* Reuse conn for the rom download */ + upgrade->conn.state = ESPCONN_NONE; + + espconn_connect(&upgrade->conn); +} + +static void ICACHE_FLASH_ATTR ota_error(void *arg, sint8 err) +{ + os_printf("Upgrade disconnected with error: %d.\n", err); + ota_disconnect(arg); +} + +static void ICACHE_FLASH_ATTR ota_got_dns(const char *name, ip_addr_t *ip, + void *arg) +{ + struct ota_status *upgrade = arg; + + if (ip == NULL) { + os_printf("Upgrade DNS request failed.\n"); + os_free(upgrade); + system_upgrade_flag_set(UPGRADE_FLAG_IDLE); + return; + } + + upgrade->conn.type = ESPCONN_TCP; + upgrade->conn.state = ESPCONN_NONE; + upgrade->conn.proto.tcp = (esp_tcp *)os_malloc(sizeof(esp_tcp)); + upgrade->conn.proto.tcp->local_port = espconn_port(); + upgrade->conn.proto.tcp->remote_port = 80; /* FIXME; HTTPS */ + + os_memcpy(upgrade->conn.proto.tcp->remote_ip, &ip->addr, 4); + + espconn_regist_connectcb(&upgrade->conn, ota_connect); + espconn_regist_disconcb(&upgrade->conn, ota_disconnect); + espconn_regist_reconcb(&upgrade->conn, ota_error); + + espconn_connect(&upgrade->conn); +} + +bool ICACHE_FLASH_ATTR ota_check() +{ + struct ota_status *upgrade = NULL; + + /* Don't start an upgrade if one is in progress */ + if (system_upgrade_flag_check() == UPGRADE_FLAG_START) { + return false; + } + + upgrade = (struct ota_status *) os_zalloc(sizeof(struct ota_status)); + if (!upgrade) { + os_printf("Couldn't allocate memory for upgrade structure.\n"); + return false; + } + + system_upgrade_flag_set(UPGRADE_FLAG_START); + + upgrade->slot = system_upgrade_userbin_check() ? 0 : 1; + + /* Kick off the DNS lookup to start */ + espconn_gethostbyname(&upgrade->conn, UPGRADE_HOST, + &upgrade->server_ip, + ota_got_dns); + + return true; +} diff --git a/ota.h b/ota.h new file mode 100644 index 0000000..a86f3fb --- /dev/null +++ b/ota.h @@ -0,0 +1,8 @@ +#ifndef USER_OTA_H_ +#define USER_OTA_H_ + +#include + +bool ICACHE_FLASH_ATTR ota_check(void); + +#endif /* USER_OTA_H_ */ diff --git a/user_main.c b/user_main.c index 0ec6056..c6ae567 100644 --- a/user_main.c +++ b/user_main.c @@ -23,6 +23,7 @@ #include "clock.h" #include "max7219.h" +#include "ota.h" #include "spi.h" struct station_config wificfg; @@ -112,6 +113,7 @@ void ICACHE_FLASH_ATTR wifi_callback(System_Event_t *evt) break; case EVENT_STAMODE_GOT_IP: ntp_get_time(); + ota_check(); default: break; } -- 2.39.2