Files
esp_wifi_repeater/user/rboot-ota.c
T
2018-09-11 23:28:29 +02:00

354 lines
9.8 KiB
C

//////////////////////////////////////////////////
// rBoot OTA sample code for ESP8266 C API.
// Copyright 2015 Richard A Burton
// richardaburton@gmail.com
// See license.txt for license terms.
// OTA code based on SDK sample from Espressif.
//////////////////////////////////////////////////
#include <c_types.h>
#include <user_interface.h>
#include <espconn.h>
#include <mem.h>
#include <osapi.h>
#include "rboot-ota.h"
#include "config_flash.h"
extern sysconfig_t config;
#ifdef __cplusplus
extern "C" {
#endif
#define UPGRADE_FLAG_IDLE 0x00
#define UPGRADE_FLAG_START 0x01
#define UPGRADE_FLAG_FINISH 0x02
typedef struct {
uint8 rom_slot; // rom slot to update, or FLASH_BY_ADDR
ota_callback callback; // user callback when completed
uint32 total_len;
uint32 content_len;
struct espconn *conn;
ip_addr_t ip;
rboot_write_status write_status;
} upgrade_status;
static upgrade_status *upgrade;
static os_timer_t ota_timer;
// clean up at the end of the update
// will call the user call back to indicate completion
void ICACHE_FLASH_ATTR rboot_ota_deinit() {
bool result;
uint8 rom_slot;
ota_callback callback;
struct espconn *conn;
os_timer_disarm(&ota_timer);
// save only remaining bits of interest from upgrade struct
// then we can clean it up early, so disconnect callback
// can distinguish between us calling it after update finished
// or being called earlier in the update process
conn = upgrade->conn;
rom_slot = upgrade->rom_slot;
callback = upgrade->callback;
// clean up
os_free(upgrade);
upgrade = 0;
// if connected, disconnect and clean up connection
if (conn) espconn_disconnect(conn);
// check for completion
if (system_upgrade_flag_check() == UPGRADE_FLAG_FINISH) {
result = true;
} else {
system_upgrade_flag_set(UPGRADE_FLAG_IDLE);
result = false;
}
// call user call back
if (callback) {
callback(result, rom_slot);
}
}
// called when connection receives data (hopefully the rom)
static void ICACHE_FLASH_ATTR upgrade_recvcb(void *arg, char *pusrdata, unsigned short length) {
char *ptrData, *ptrLen, *ptr;
// disarm the timer
os_timer_disarm(&ota_timer);
// first reply?
if (upgrade->content_len == 0) {
// valid http response?
if ((ptrLen = (char*)os_strstr(pusrdata, "Content-Length: "))
&& (ptrData = (char*)os_strstr(ptrLen, "\r\n\r\n"))
&& (os_strncmp(pusrdata + 9, "200", 3) == 0)) {
// end of header/start of data
ptrData += 4;
// length of data after header in this chunk
length -= (ptrData - pusrdata);
// running total of download length
upgrade->total_len += length;
// process current chunk
if (!rboot_write_flash(&upgrade->write_status, (uint8*)ptrData, length)) {
// write error
rboot_ota_deinit();
return;
}
// work out total download size
ptrLen += 16;
ptr = (char *)os_strstr(ptrLen, "\r\n");
*ptr = '\0'; // destructive
upgrade->content_len = atoi(ptrLen);
} else {
// fail, not a valid http header/non-200 response/etc.
rboot_ota_deinit();
return;
}
} else {
// not the first chunk, process it
upgrade->total_len += length;
if (!rboot_write_flash(&upgrade->write_status, (uint8*)pusrdata, length)) {
// write error
rboot_ota_deinit();
return;
}
}
// check if we are finished
if (upgrade->total_len == upgrade->content_len) {
system_upgrade_flag_set(UPGRADE_FLAG_FINISH);
// clean up and call user callback
rboot_ota_deinit();
} else if (upgrade->conn->state != ESPCONN_READ) {
// fail, but how do we get here? premature end of stream?
rboot_ota_deinit();
} else {
// timer for next recv
os_timer_setfn(&ota_timer, (os_timer_func_t *)rboot_ota_deinit, 0);
os_timer_arm(&ota_timer, OTA_NETWORK_TIMEOUT, 0);
}
}
// disconnect callback, clean up the connection
// we also call this ourselves
static void ICACHE_FLASH_ATTR upgrade_disconcb(void *arg) {
// use passed ptr, as upgrade struct may have gone by now
struct espconn *conn = (struct espconn*)arg;
os_timer_disarm(&ota_timer);
if (conn) {
if (conn->proto.tcp) {
os_free(conn->proto.tcp);
}
os_free(conn);
}
// is upgrade struct still around?
// if so disconnect was from remote end, or we called
// ourselves to cleanup a failed connection attempt
// must ensure disconnect was for this upgrade attempt,
// not a previous one! this call back is async so another
// upgrade struct may have been created already
if (upgrade && (upgrade->conn == conn)) {
// mark connection as gone
upgrade->conn = 0;
// end the update process
rboot_ota_deinit();
}
}
// successfully connected to update server, send the request
static void ICACHE_FLASH_ATTR upgrade_connect_cb(void *arg) {
uint8 *request;
// disable the timeout
os_timer_disarm(&ota_timer);
// register connection callbacks
espconn_regist_disconcb(upgrade->conn, upgrade_disconcb);
espconn_regist_recvcb(upgrade->conn, upgrade_recvcb);
// http request string
request = (uint8 *)os_malloc(512);
if (!request) {
to_console("No ram!\r\n");
rboot_ota_deinit();
return;
}
os_sprintf((char*)request,
"GET /%s HTTP/1.0\r\nHost: %s\r\n%s",
(upgrade->rom_slot == FLASH_BY_ADDR ? OTA_FILE : (upgrade->rom_slot == 0 ? OTA_ROM0 : OTA_ROM1)),
config.ota_host, HTTP_HEADER);
// send the http request, with timeout for reply
os_timer_setfn(&ota_timer, (os_timer_func_t *)rboot_ota_deinit, 0);
os_timer_arm(&ota_timer, OTA_NETWORK_TIMEOUT, 0);
espconn_sent(upgrade->conn, request, os_strlen((char*)request));
os_free(request);
}
// connection attempt timed out
static void ICACHE_FLASH_ATTR connect_timeout_cb() {
to_console("Connect timeout.\r\n");
// not connected so don't call disconnect on the connection
// but call our own disconnect callback to do the cleanup
upgrade_disconcb(upgrade->conn);
}
static const char* ICACHE_FLASH_ATTR esp_errstr(sint8 err) {
switch(err) {
case ESPCONN_OK:
return "No error, everything OK.";
case ESPCONN_MEM:
return "Out of memory error.";
case ESPCONN_TIMEOUT:
return "Timeout.";
case ESPCONN_RTE:
return "Routing problem.";
case ESPCONN_INPROGRESS:
return "Operation in progress.";
case ESPCONN_ABRT:
return "Connection aborted.";
case ESPCONN_RST:
return "Connection reset.";
case ESPCONN_CLSD:
return "Connection closed.";
case ESPCONN_CONN:
return "Not connected.";
case ESPCONN_ARG:
return "Illegal argument.";
case ESPCONN_ISCONN:
return "Already connected.";
}
}
// call back for lost connection
static void ICACHE_FLASH_ATTR upgrade_recon_cb(void *arg, sint8 errType) {
to_console("Connection error: ");
to_console(esp_errstr(errType));
to_console("\r\n");
// not connected so don't call disconnect on the connection
// but call our own disconnect callback to do the cleanup
upgrade_disconcb(upgrade->conn);
}
// call back for dns lookup
static void ICACHE_FLASH_ATTR upgrade_resolved(const char *name, ip_addr_t *ip, void *arg) {
if (ip == 0) {
to_console("DNS lookup failed for: ");
to_console(config.ota_host);
to_console("\r\n");
// not connected so don't call disconnect on the connection
// but call our own disconnect callback to do the cleanup
upgrade_disconcb(upgrade->conn);
return;
}
// set up connection
upgrade->conn->type = ESPCONN_TCP;
upgrade->conn->state = ESPCONN_NONE;
upgrade->conn->proto.tcp->local_port = espconn_port();
upgrade->conn->proto.tcp->remote_port = config.ota_port;
*(ip_addr_t*)upgrade->conn->proto.tcp->remote_ip = *ip;
// set connection call backs
espconn_regist_connectcb(upgrade->conn, upgrade_connect_cb);
espconn_regist_reconcb(upgrade->conn, upgrade_recon_cb);
// try to connect
espconn_connect(upgrade->conn);
// set connection timeout timer
os_timer_disarm(&ota_timer);
os_timer_setfn(&ota_timer, (os_timer_func_t *)connect_timeout_cb, 0);
os_timer_arm(&ota_timer, OTA_NETWORK_TIMEOUT, 0);
}
// start the ota process, with user supplied options
bool ICACHE_FLASH_ATTR rboot_ota_start(ota_callback callback) {
uint8 slot;
rboot_config bootconf;
err_t result;
// check not already updating
if (system_upgrade_flag_check() == UPGRADE_FLAG_START) {
return false;
}
// create upgrade status structure
upgrade = (upgrade_status*)os_zalloc(sizeof(upgrade_status));
if (!upgrade) {
to_console("No ram!\r\n");
return false;
}
// store the callback
upgrade->callback = callback;
// get details of rom slot to update
bootconf = rboot_get_config();
slot = bootconf.current_rom;
if (slot == 0) slot = 1; else slot = 0;
upgrade->rom_slot = slot;
// flash to rom slot
upgrade->write_status = rboot_write_init(bootconf.roms[upgrade->rom_slot]);
// to flash a file (e.g. containing a filesystem) to an arbitrary location
// (e.g. 0x40000 bytes after the start of the rom) use code this like instead:
// Note: address must be start of a sector (multiple of 4k)!
//upgrade->write_status = rboot_write_init(bootconf.roms[upgrade->rom_slot] + 0x40000);
//upgrade->rom_slot = FLASH_BY_ADDR;
// create connection
upgrade->conn = (struct espconn *)os_zalloc(sizeof(struct espconn));
if (!upgrade->conn) {
to_console("No ram!\r\n");
os_free(upgrade);
return false;
}
upgrade->conn->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp));
if (!upgrade->conn->proto.tcp) {
to_console("No ram!\r\n");
os_free(upgrade->conn);
os_free(upgrade);
return false;
}
// set update flag
system_upgrade_flag_set(UPGRADE_FLAG_START);
// dns lookup
result = espconn_gethostbyname(upgrade->conn, config.ota_host, &upgrade->ip, upgrade_resolved);
if (result == ESPCONN_OK) {
// hostname is already cached or is actually a dotted decimal ip address
upgrade_resolved(0, &upgrade->ip, upgrade->conn);
} else if (result == ESPCONN_INPROGRESS) {
// lookup taking place, will call upgrade_resolved on completion
} else {
to_console("DNS error!\r\n");
os_free(upgrade->conn->proto.tcp);
os_free(upgrade->conn);
os_free(upgrade);
return false;
}
return true;
}
#ifdef __cplusplus
}
#endif