Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ota_basic example can receive new image via TCP. However - writing to flash with interrupts disabled causes data loss, and the TCP flow is very slow to recover. Linux sender quickly ramps up RTT timer to very long retry intervals, crippling performance & throughput. Running the update without the flash writes causes the data to be received quickly, so this is definitely an issue with the time taken for the erase cycle. Progress towards #10
- Loading branch information
1 parent
3797cf5
commit 147257e
Showing
10 changed files
with
449 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
PROGRAM=ota_basic | ||
OTA=1 | ||
EXTRA_COMPONENTS=extras/rboot-ota | ||
include ../../common.mk | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
/* A very simple OTA example | ||
* | ||
* Binds a TCP socket, reads an image from it and then flashes live. | ||
* | ||
* This lets you flash from the command line via netcat. | ||
* | ||
* NOT SUITABLE TO PUT ON THE INTERNET OR INTO A PRODUCTION ENVIRONMENT!!!! | ||
*/ | ||
#include "espressif/esp_common.h" | ||
#include "espressif/sdk_private.h" | ||
#include "FreeRTOS.h" | ||
#include "task.h" | ||
#include "esp8266.h" | ||
#include "rboot-ota.h" | ||
|
||
#include "lwip/err.h" | ||
#include "lwip/sockets.h" | ||
#include "lwip/sys.h" | ||
#include "lwip/netdb.h" | ||
#include "lwip/dns.h" | ||
|
||
#define FWPORT 12550 | ||
|
||
#if !defined(WIFI_SSID) || !defined(WIFI_PASS) | ||
#error "Please define macros WIFI_SSID & WIFI_PASS (here, or better in a local.h file at root level or in program dir." | ||
#endif | ||
|
||
void simpleOTATask(void *pvParameters) | ||
{ | ||
printf("Listening for firmware on port %d...\r\n", FWPORT); | ||
int s = socket(AF_INET, SOCK_STREAM, 0); | ||
if(s < 0) { | ||
printf("... Failed to allocate socket.\r\n"); | ||
return; | ||
} | ||
|
||
struct sockaddr_in s_addr = { | ||
.sin_family = AF_INET, | ||
.sin_addr = { | ||
.s_addr = INADDR_ANY, | ||
}, | ||
.sin_port = htons(FWPORT), | ||
}; | ||
if(bind(s, (struct sockaddr *) &s_addr, sizeof(s_addr)) < 0) | ||
{ | ||
printf("... Failed to bind.\r\n"); | ||
return; | ||
} | ||
|
||
if(listen(s, 0) == -1) { | ||
printf("... Failed to listen.\r\n"); | ||
return; | ||
} | ||
|
||
int client; | ||
while((client = accept(s, NULL, NULL)) != 0) | ||
{ | ||
printf("Got new socket. Trying OTA update...\r\n"); | ||
|
||
int slot = rboot_ota_update(client, -1, false); | ||
close(client); | ||
if(slot < 0) { | ||
printf("OTA update failed. :(.\r\n"); | ||
continue; | ||
} | ||
printf("OTA succeeded at slot %d!\r\n", slot); | ||
//rboot_set_current_rom(slot); | ||
//sdk_system_restart(); | ||
} | ||
printf("Failed to accept.\r\n"); | ||
close(s); | ||
return; | ||
} | ||
|
||
void user_init(void) | ||
{ | ||
sdk_uart_div_modify(0, UART_CLK_FREQ / 115200); | ||
|
||
printf("OTA Basic demo. Currently running on slot %d / %d.\r\n", | ||
rboot_get_current_rom(), RBOOT_MAX_ROMS); | ||
|
||
struct sdk_station_config config = { | ||
.ssid = WIFI_SSID, | ||
.password = WIFI_PASS, | ||
}; | ||
sdk_wifi_set_opmode(STATION_MODE); | ||
sdk_wifi_station_set_config(&config); | ||
|
||
xTaskCreate(simpleOTATask, (signed char *)"simpleOTATask", 512, NULL, 2, NULL); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
cpu xtensa | ||
|
||
# Show up to this many raw bytes of code/data | ||
show bytes 4 | ||
|
||
# ELF file | ||
# See example.def if you want to load raw binaries instead | ||
load build/ota_basic.out elf | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
extern void wDev_ProcessFiq(void); | ||
|
||
void call_wdev(void) | ||
{ | ||
wDev_ProcessFiq(); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Component makefile for extras/rboot-ota | ||
|
||
INC_DIRS += $(ROOT)extras/rboot-ota | ||
|
||
# args for passing into compile rule generation | ||
extras/rboot-ota_INC_DIR = $(ROOT)extras/rboot-ota | ||
extras/rboot-ota_SRC_DIR = $(ROOT)extras/rboot-ota | ||
|
||
$(eval $(call component_compile_rules,extras/rboot-ota)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
#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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
#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 <lwip/sockets.h> | ||
|
||
#include <FreeRTOS.h> | ||
#include <task.h> | ||
|
||
#define SECTOR_SIZE 0x1000 | ||
#define BOOT_CONFIG_SECTOR 1 | ||
|
||
#include "rboot-ota.h" | ||
|
||
#define ROM_MAGIC_OLD 0xe9 | ||
#define ROM_MAGIC_NEW 0xea | ||
|
||
|
||
// get the rboot config | ||
rboot_config_t rboot_get_config() { | ||
rboot_config_t conf; | ||
sdk_spi_flash_read(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32_t*)&conf, sizeof(rboot_config_t)); | ||
return conf; | ||
} | ||
|
||
// write the rboot config | ||
// preserves contents of rest of sector, so rest | ||
// of sector can be used to store user data | ||
// updates checksum automatically, if enabled | ||
bool rboot_set_config(rboot_config_t *conf) { | ||
uint8_t *buffer; | ||
#ifdef RBOOT_CONFIG_CHKSUM | ||
uint8_t chksum; | ||
uint8_t *ptr; | ||
#endif | ||
|
||
buffer = (uint8_t*)malloc(SECTOR_SIZE); | ||
if (!buffer) { | ||
printf("Failed to allocate sector buffer\r\n"); | ||
return false; | ||
} | ||
|
||
#ifdef BOOT_CONFIG_CHKSUM | ||
chksum = CHKSUM_INIT; | ||
for (ptr = (uint8_t*)conf; ptr < &conf->chksum; ptr++) { | ||
chksum ^= *ptr; | ||
} | ||
conf->chksum = chksum; | ||
#endif | ||
|
||
sdk_spi_flash_read(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32_t*)buffer, SECTOR_SIZE); | ||
memcpy(buffer, conf, sizeof(rboot_config_t)); | ||
vPortEnterCritical(); | ||
sdk_spi_flash_erase_sector(BOOT_CONFIG_SECTOR); | ||
vPortExitCritical(); | ||
taskYIELD(); | ||
vPortEnterCritical(); | ||
sdk_spi_flash_write(BOOT_CONFIG_SECTOR * SECTOR_SIZE, (uint32_t*)buffer, SECTOR_SIZE); | ||
vPortExitCritical(); | ||
free(buffer); | ||
return true; | ||
} | ||
|
||
// get current boot rom | ||
uint8_t rboot_get_current_rom() { | ||
rboot_config_t conf; | ||
conf = rboot_get_config(); | ||
return conf.current_rom; | ||
} | ||
|
||
// set current boot rom | ||
bool rboot_set_current_rom(uint8_t rom) { | ||
rboot_config_t conf; | ||
conf = rboot_get_config(); | ||
if (rom >= conf.count) return false; | ||
conf.current_rom = rom; | ||
return rboot_set_config(&conf); | ||
} | ||
|
||
int rboot_ota_update(int fd, int slot, bool reboot_now) | ||
{ | ||
rboot_config_t conf; | ||
conf = rboot_get_config(); | ||
|
||
if(slot == -1) { | ||
slot = (conf.current_rom + 1) % RBOOT_MAX_ROMS; | ||
} | ||
|
||
size_t sector = conf.roms[slot] / SECTOR_SIZE; | ||
uint8_t *sector_buf = (uint8_t *)malloc(SECTOR_SIZE); | ||
if(!sector_buf) { | ||
printf("Failed to malloc sector buffer\r\n"); | ||
return 0; | ||
} | ||
|
||
printf("New image going into sector %d @ 0x%lx\r\n", slot, conf.roms[slot]); | ||
|
||
/* Read bootloader header */ | ||
int r = read(fd, sector_buf, 8); | ||
if(r != 8 || (sector_buf[0] != ROM_MAGIC_OLD && sector_buf[0] != ROM_MAGIC_NEW)) { | ||
printf("Failed to read ESP bootloader header r=%d magic=%d.\r\n", r, sector_buf[0]); | ||
slot = -1; | ||
goto cleanup; | ||
} | ||
/* if we saw a v1.2 header, we can expect a v1.1 header after the first section */ | ||
bool in_new_header = (sector_buf[0] == ROM_MAGIC_NEW); | ||
|
||
int remaining_sections = sector_buf[1]; | ||
size_t offs = 8; | ||
size_t total_read = 8; | ||
size_t left_section = 0; /* Number of bytes left in this section */ | ||
|
||
while(remaining_sections > 0 || left_section > 0) | ||
{ | ||
if(left_section == 0) { | ||
/* No more bytes in this section, read the next section header */ | ||
|
||
if(offs + (in_new_header ? 16 : 8) > SECTOR_SIZE) { | ||
printf("PANIC: reading section header overflows sector boundary. FIXME.\r\n"); | ||
slot = -1; | ||
goto cleanup; | ||
} | ||
|
||
if(in_new_header && total_read > 8) | ||
{ | ||
/* expecting an "old" style 8 byte image header here, following irom0 */ | ||
int r = read(fd, sector_buf+offs, 8); | ||
if(r != 8 || sector_buf[offs] != ROM_MAGIC_OLD) { | ||
printf("Failed to read second flash header r=%d magic=0x%02x.\r\n", r, sector_buf[offs]); | ||
slot = -1; | ||
goto cleanup; | ||
} | ||
/* treat this number as the reliable number of remaining sections */ | ||
remaining_sections = sector_buf[offs+1]; | ||
in_new_header = false; | ||
} | ||
|
||
int r = read(fd, sector_buf+offs, 8); | ||
if(r != 8) { | ||
printf("Failed to read section header.\r\n"); | ||
slot = -1; | ||
goto cleanup; | ||
} | ||
offs += 8; | ||
total_read += 8; | ||
left_section = *((uint32_t *)(sector_buf+offs-4)); | ||
|
||
/* account for padding from the reported section size out to the alignment barrier */ | ||
size_t section_align = in_new_header ? 16 : 4; | ||
left_section = (left_section+section_align-1) & ~(section_align-1); | ||
|
||
remaining_sections--; | ||
printf("New section @ 0x%x length 0x%x align 0x%x remaining 0x%x\r\n", total_read, left_section, section_align, remaining_sections); | ||
} | ||
|
||
size_t bytes_to_read = left_section > (SECTOR_SIZE-offs) ? (SECTOR_SIZE-offs) : left_section; | ||
int r = read(fd, sector_buf+offs, bytes_to_read); | ||
if(r < 0) { | ||
printf("Failed to read from fd\r\n"); | ||
slot = -1; | ||
goto cleanup; | ||
} | ||
if(r == 0) { | ||
printf("EOF before end of image remaining_sections=%d this_section=%d\r\n", | ||
remaining_sections, left_section); | ||
slot = -1; | ||
goto cleanup; | ||
} | ||
offs += r; | ||
total_read += r; | ||
left_section -= r; | ||
|
||
if(offs == SECTOR_SIZE || (left_section == 0 && remaining_sections == 0)) { | ||
/* sector buffer is full, or we're at the very end, so write sector to flash */ | ||
printf("Sector buffer full. Erasing sector 0x%02x\r\n", sector); | ||
sdk_spi_flash_erase_sector(sector); | ||
printf("Erase done.\r\n"); | ||
//sdk_spi_flash_write(sector * SECTOR_SIZE, (uint32_t*)sector_buf, SECTOR_SIZE); | ||
printf("Flash done.\r\n"); | ||
offs = 0; | ||
sector++; | ||
} | ||
} | ||
|
||
/* Done reading image from fd and writing it out */ | ||
if(reboot_now) | ||
{ | ||
close(fd); | ||
conf.current_rom = slot; | ||
rboot_set_config(&conf); | ||
sdk_system_restart(); | ||
} | ||
|
||
cleanup: | ||
free(sector_buf); | ||
return slot; | ||
} | ||
|
Oops, something went wrong.