From bd38e2e00b770ed4e0584e53b00ea4230cac30af Mon Sep 17 00:00:00 2001 From: dannybackx Date: Sun, 30 Oct 2016 09:51:47 +0100 Subject: [PATCH 01/14] Code to query the IP address data for our WiFi interface. This allows the microcontroller to query e.g. the IP address of the IoT device and communicate it to trusted parties. --- cmd/cmd.h | 2 ++ cmd/handlers.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/cmd/cmd.h b/cmd/cmd.h index 3b4e8ab..03549cc 100644 --- a/cmd/cmd.h +++ b/cmd/cmd.h @@ -58,6 +58,8 @@ typedef enum { CMD_SOCKET_SETUP = 40, // set-up callbacks CMD_SOCKET_SEND, // send data over UDP socket + CMD_GET_WIFI_INFO = 50,// Query IP address info + } CmdName; typedef void (*cmdfunc_t)(CmdPacket *cmd); diff --git a/cmd/handlers.c b/cmd/handlers.c index c191091..ee43982 100644 --- a/cmd/handlers.c +++ b/cmd/handlers.c @@ -17,6 +17,7 @@ #ifdef SOCKET #include #endif +#include #ifdef CMD_DBG #define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0) @@ -29,6 +30,7 @@ static void cmdSync(CmdPacket *cmd); static void cmdWifiStatus(CmdPacket *cmd); static void cmdGetTime(CmdPacket *cmd); static void cmdAddCallback(CmdPacket *cmd); +static void cmdGetWifiInfo(CmdPacket *cmd); // keep track of last status sent to uC so we can notify it when it changes static uint8_t lastWifiStatus = wifiIsDisconnected; @@ -44,6 +46,7 @@ const CmdList commands[] = { {CMD_WIFI_STATUS, "WIFI_STATUS", cmdWifiStatus}, {CMD_CB_ADD, "ADD_CB", cmdAddCallback}, {CMD_GET_TIME, "GET_TIME", cmdGetTime}, + {CMD_GET_WIFI_INFO, "GET_WIFI_INFO", cmdGetWifiInfo}, #ifdef MQTT {CMD_MQTT_SETUP, "MQTT_SETUP", MQTTCMD_Setup}, {CMD_MQTT_PUBLISH, "MQTT_PUB", MQTTCMD_Publish}, @@ -178,6 +181,32 @@ cmdGetTime(CmdPacket *cmd) { return; } +// Command handler for IP information +static void ICACHE_FLASH_ATTR +cmdGetWifiInfo(CmdPacket *cmd) { + CmdRequest req; + + cmdRequest(&req, cmd); + if(cmd->argc != 0 || cmd->value == 0) { + cmdResponseStart(CMD_RESP_V, 0, 0); + cmdResponseEnd(); + return; + } + + uint32_t callback = req.cmd->value; + + struct ip_info info; + wifi_get_ip_info(0, &info); + + cmdResponseStart(CMD_RESP_CB, callback, 3); + cmdResponseBody(&info.ip.addr, sizeof(info.ip.addr)); + cmdResponseBody(&info.netmask.addr, sizeof(info.netmask.addr)); + cmdResponseBody(&info.gw.addr, sizeof(info.gw.addr)); + cmdResponseEnd(); + + return; +} + // Command handler to add a callback to the named-callbacks list, this is for a callback to the uC static void ICACHE_FLASH_ATTR cmdAddCallback(CmdPacket *cmd) { From 49fc8c96435b5b0775d830bc59c3405f4e683f78 Mon Sep 17 00:00:00 2001 From: dannybackx Date: Sun, 30 Oct 2016 15:40:55 +0100 Subject: [PATCH 02/14] First source code changes for security. This basically parameterizes the serial bridges to ports 23 and 2323. --- Makefile | 11 ++++++++++ esp-link/config.h | 4 ++++ esp-link/main.c | 11 +++++++++- serial/serbridge.c | 55 ++++++++++++++++++++++------------------------ serial/serbridge.h | 3 ++- 5 files changed, 53 insertions(+), 31 deletions(-) diff --git a/Makefile b/Makefile index 4172785..c626ff2 100644 --- a/Makefile +++ b/Makefile @@ -99,6 +99,15 @@ MCU_ISP_PIN ?= 13 LED_CONN_PIN ?= 0 # GPIO pin used for "serial activity" LED, active low LED_SERIAL_PIN ?= 14 +# +# Default settings for access over TCP/IP connections +# +# Modes are 0 (unsecure), 1 (disabled), 2 (secure) +# +PORT1_MODE ?= 0 +PORT1_PORTNUMBER ?= 23 +PORT2_MODE ?= 0 +PORT2_PORTNUMBER ?= 2323 # --------------- esp-link modules config options --------------- @@ -243,6 +252,8 @@ CFLAGS += -Os -ggdb -std=c99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno- -D__ets__ -DICACHE_FLASH -Wno-address -DFIRMWARE_SIZE=$(ESP_FLASH_MAX) \ -DMCU_RESET_PIN=$(MCU_RESET_PIN) -DMCU_ISP_PIN=$(MCU_ISP_PIN) \ -DLED_CONN_PIN=$(LED_CONN_PIN) -DLED_SERIAL_PIN=$(LED_SERIAL_PIN) \ + -DPORT1_PORTNUMBER=$(PORT1_PORTNUMBER) -DPORT1_MODE=$(PORT1_MODE) \ + -DPORT2_PORTNUMBER=$(PORT2_PORTNUMBER) -DPORT2_MODE=$(PORT2_MODE) \ -DVERSION="$(VERSION)" # linker flags used to generate the main object file diff --git a/esp-link/config.h b/esp-link/config.h index 65195d2..fecf5c3 100644 --- a/esp-link/config.h +++ b/esp-link/config.h @@ -41,6 +41,10 @@ typedef struct { int8_t data_bits; int8_t parity; int8_t stop_bits; + int8_t port1_mode, // Security + port2_mode; + uint16_t port1_portnumber, + port2_portnumber; } FlashConfig; extern FlashConfig flashConfig; diff --git a/esp-link/main.c b/esp-link/main.c index e775de9..a2c40d7 100644 --- a/esp-link/main.c +++ b/esp-link/main.c @@ -89,6 +89,7 @@ HttpdBuiltInUrl builtInUrls[] = { { "/wifi/connstatus", cgiWiFiConnStatus, NULL }, { "/wifi/setmode", cgiWiFiSetMode, NULL }, { "/wifi/special", cgiWiFiSpecial, NULL }, + { "/wifi/security", jsonWiFiSecurity, NULL }, { "/wifi/apinfo", cgiApSettingsInfo, NULL }, { "/wifi/apchange", cgiApSettingsChange, NULL }, { "/system/info", cgiSystemInfo, NULL }, @@ -179,7 +180,15 @@ user_init(void) { WEB_Init(); // init the wifi-serial transparent bridge (port 23) - serbridgeInit(23, 2323); + flashConfig.port1_portnumber = 23; + flashConfig.port2_portnumber = 2323; + flashConfig.port1_mode = 0; + flashConfig.port2_mode = 0; + + serbridgeInit(); + serbridgeStart(0, flashConfig.port1_portnumber, flashConfig.port1_mode); + serbridgeStart(1, flashConfig.port2_portnumber, flashConfig.port2_mode); + uart_add_recv_cb(&serbridgeUartCb); #ifdef SHOW_HEAP_USE os_timer_disarm(&prHeapTimer); diff --git a/serial/serbridge.c b/serial/serbridge.c index cebc561..02517cf 100644 --- a/serial/serbridge.c +++ b/serial/serbridge.c @@ -16,9 +16,8 @@ #define syslog(X1...) #endif -static struct espconn serbridgeConn1; // plain bridging port -static struct espconn serbridgeConn2; // programming port -static esp_tcp serbridgeTcp1, serbridgeTcp2; +static struct espconn serbridgeConn[2]; // plain bridging port +static esp_tcp serbridgeTcp[2]; static int8_t mcu_reset_pin, mcu_isp_pin; uint8_t in_mcu_flashing; // for disabling slip during MCU flashing @@ -421,7 +420,7 @@ serbridgeConnectCb(void *arg) connData[i].readytosend = true; connData[i].conn_mode = cmInit; // if it's the second port we start out in programming mode - if (conn->proto.tcp->local_port == serbridgeConn2.proto.tcp->local_port) + if (conn->proto.tcp->local_port == serbridgeConn[1].proto.tcp->local_port) connData[i].conn_mode = cmPGMInit; espconn_regist_recvcb(conn, serbridgeRecvCb); @@ -470,35 +469,33 @@ serbridgeInitPins() // Start transparent serial bridge TCP server on specified port (typ. 23) void ICACHE_FLASH_ATTR -serbridgeInit(int port1, int port2) +serbridgeInit() { serbridgeInitPins(); os_memset(connData, 0, sizeof(connData)); - os_memset(&serbridgeTcp1, 0, sizeof(serbridgeTcp1)); - os_memset(&serbridgeTcp2, 0, sizeof(serbridgeTcp2)); - - // set-up the primary port for plain bridging - serbridgeConn1.type = ESPCONN_TCP; - serbridgeConn1.state = ESPCONN_NONE; - serbridgeTcp1.local_port = port1; - serbridgeConn1.proto.tcp = &serbridgeTcp1; - - espconn_regist_connectcb(&serbridgeConn1, serbridgeConnectCb); - espconn_accept(&serbridgeConn1); - espconn_tcp_set_max_con_allow(&serbridgeConn1, MAX_CONN); - espconn_regist_time(&serbridgeConn1, SER_BRIDGE_TIMEOUT, 0); - - // set-up the secondary port for programming - serbridgeConn2.type = ESPCONN_TCP; - serbridgeConn2.state = ESPCONN_NONE; - serbridgeTcp2.local_port = port2; - serbridgeConn2.proto.tcp = &serbridgeTcp2; - - espconn_regist_connectcb(&serbridgeConn2, serbridgeConnectCb); - espconn_accept(&serbridgeConn2); - espconn_tcp_set_max_con_allow(&serbridgeConn2, MAX_CONN); - espconn_regist_time(&serbridgeConn2, SER_BRIDGE_TIMEOUT, 0); + os_memset(&serbridgeTcp[0], 0, sizeof(serbridgeTcp[0])); + os_memset(&serbridgeTcp[1], 0, sizeof(serbridgeTcp[1])); +} + +// Start transparent serial bridge TCP server on specified port (typ. 23) +void ICACHE_FLASH_ATTR +serbridgeStart(int ix, int port, int mode) +{ + if (ix < 0 || ix > 2) // FIXME hardcoded limit + return; + if (0 < port && port < 65536) { + // set-up the primary port for plain bridging + serbridgeConn[ix].type = ESPCONN_TCP; + serbridgeConn[ix].state = ESPCONN_NONE; + serbridgeTcp[ix].local_port = port; + serbridgeConn[ix].proto.tcp = &serbridgeTcp[ix]; + + espconn_regist_connectcb(&serbridgeConn[ix], serbridgeConnectCb); + espconn_accept(&serbridgeConn[ix]); + espconn_tcp_set_max_con_allow(&serbridgeConn[ix], MAX_CONN); + espconn_regist_time(&serbridgeConn[ix], SER_BRIDGE_TIMEOUT, 0); + } } int ICACHE_FLASH_ATTR serbridgeInMCUFlashing() diff --git a/serial/serbridge.h b/serial/serbridge.h index ed661e1..36f66d0 100644 --- a/serial/serbridge.h +++ b/serial/serbridge.h @@ -31,7 +31,8 @@ typedef struct serbridgeConnData { } serbridgeConnData; // port1 is transparent&programming, second port is programming only -void ICACHE_FLASH_ATTR serbridgeInit(int port1, int port2); +void ICACHE_FLASH_ATTR serbridgeInit(); +void ICACHE_FLASH_ATTR serbridgeStart(int ix, int port, int mode); void ICACHE_FLASH_ATTR serbridgeInitPins(void); void ICACHE_FLASH_ATTR serbridgeUartCb(char *buf, short len); void ICACHE_FLASH_ATTR serbridgeReset(); From a246823f973f63e52744cab6931a9d2b4a6255e7 Mon Sep 17 00:00:00 2001 From: dannybackx Date: Sun, 30 Oct 2016 15:43:03 +0100 Subject: [PATCH 03/14] Initialize the new fields --- esp-link/config.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esp-link/config.c b/esp-link/config.c index d12f7a7..b3302f7 100644 --- a/esp-link/config.c +++ b/esp-link/config.c @@ -35,6 +35,10 @@ FlashConfig flashDefault = { .data_bits = EIGHT_BITS, .parity = NONE_BITS, .stop_bits = ONE_STOP_BIT, + .port1_mode = PORT1_MODE, + .port2_mode = PORT2_MODE, + .port1_portnumber = PORT1_PORTNUMBER, + .port2_portnumber = PORT2_PORTNUMBER, }; typedef union { From c402bd62b9304b276c0f680fe5f3921c9045db4a Mon Sep 17 00:00:00 2001 From: dannybackx Date: Sun, 30 Oct 2016 17:24:00 +0100 Subject: [PATCH 04/14] Add a declaration missing from the previous commit --- esp-link/cgiwifi.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esp-link/cgiwifi.h b/esp-link/cgiwifi.h index 667f27d..4588d8b 100644 --- a/esp-link/cgiwifi.h +++ b/esp-link/cgiwifi.h @@ -13,6 +13,8 @@ int cgiWiFiConnect(HttpdConnData *connData); int cgiWiFiSetMode(HttpdConnData *connData); int cgiWiFiConnStatus(HttpdConnData *connData); int cgiWiFiSpecial(HttpdConnData *connData); +int cgiWiFiSecurity(HttpdConnData *connData); +int jsonWiFiSecurity(HttpdConnData *connData); int cgiApSettingsChange(HttpdConnData *connData); int cgiApSettingsInfo(HttpdConnData *connData); void configWifiIP(); From a33dc84c05d0793b483423e0645676dc8640666c Mon Sep 17 00:00:00 2001 From: dannybackx Date: Sun, 30 Oct 2016 17:37:31 +0100 Subject: [PATCH 05/14] Work in progress... --- esp-link/cgiwifi.c | 97 +++++++++++++++++++++++++++++++++++++++++- html/ui.js | 19 +++++++++ html/wifi/wifiSta.html | 64 ++++++++++++++++++++++++++++ html/wifi/wifiSta.js | 62 +++++++++++++++++++++++++++ 4 files changed, 241 insertions(+), 1 deletion(-) diff --git a/esp-link/cgiwifi.c b/esp-link/cgiwifi.c index 70d40dc..284704e 100644 --- a/esp-link/cgiwifi.c +++ b/esp-link/cgiwifi.c @@ -34,7 +34,7 @@ bool mdns_started = false; // ===== wifi status change callbacks static WifiStateChangeCb wifi_state_change_cb[4]; -// Temp store for new staion config +// Temp store for new station config struct station_config stconf; // Temp store for new ap config @@ -54,6 +54,8 @@ static char *wifiReasons[] = { static char *wifiMode[] = { 0, "STA", "AP", "AP+STA" }; static char *wifiPhy[] = { 0, "11b", "11g", "11n" }; +static char *portMode[] = { "unsecure", "disabled", "secure" }; + void (*wifiStatusCb)(uint8_t); // callback when wifi status changes static char* ICACHE_FLASH_ATTR wifiGetReason(void) { @@ -824,6 +826,99 @@ int ICACHE_FLASH_ATTR cgiWifiInfo(HttpdConnData *connData) { return HTTPD_CGI_DONE; } +static char *portMode2string(int8_t m) { + if (m < 0 || m > 2) return "?"; + return portMode[m]; +} + +// print various Wifi information into json buffer +int ICACHE_FLASH_ATTR printWiFiSecurity(char *buff) { + int len; + + len = os_sprintf(buff, + "\"port1mode\" : \"%s\", \"port1portnumber\" : \"%d\", \"port1pwd\" : \"%s\", " + "\"port2mode\" : \"%s\", \"port2portnumber\" : \"%d\", \"port2pwd\" : \"%s\" ", + portMode2string(flashConfig.port1_mode), flashConfig.port1_portnumber, "", + portMode2string(flashConfig.port2_mode), flashConfig.port2_portnumber, "" + ); + + return len; +} + +// Cgi to return various Wifi information +int ICACHE_FLASH_ATTR jsonWiFiSecurity(HttpdConnData *connData) { + char buff[1024]; + + if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. + + os_strcpy(buff, "{"); + printWiFiSecurity(buff+1); + os_strcat(buff, "}"); + + jsonHeader(connData, 200); + httpdSend(connData, buff, -1); + return HTTPD_CGI_DONE; +} + +// Change security settings +int ICACHE_FLASH_ATTR cgiWiFiSecurity(HttpdConnData *connData) { + char dhcp[8]; + char staticip[20]; + char netmask[20]; + char gateway[20]; + + if (connData->conn==NULL) return HTTPD_CGI_DONE; + + // get args and their string lengths + int dl = httpdFindArg(connData->getArgs, "dhcp", dhcp, sizeof(dhcp)); + int sl = httpdFindArg(connData->getArgs, "staticip", staticip, sizeof(staticip)); + int nl = httpdFindArg(connData->getArgs, "netmask", netmask, sizeof(netmask)); + int gl = httpdFindArg(connData->getArgs, "gateway", gateway, sizeof(gateway)); + + if (!(dl > 0 && sl >= 0 && nl >= 0 && gl >= 0)) { + jsonHeader(connData, 400); + httpdSend(connData, "Request is missing fields", -1); + return HTTPD_CGI_DONE; + } + + char url[64]; // redirect URL + if (os_strcmp(dhcp, "off") == 0) { + // parse static IP params + struct ip_info ipi; + bool ok = parse_ip(staticip, &ipi.ip); + if (nl > 0) ok = ok && parse_ip(netmask, &ipi.netmask); + else IP4_ADDR(&ipi.netmask, 255, 255, 255, 0); + if (gl > 0) ok = ok && parse_ip(gateway, &ipi.gw); + else ipi.gw.addr = 0; + if (!ok) { + jsonHeader(connData, 400); + httpdSend(connData, "Cannot parse static IP config", -1); + return HTTPD_CGI_DONE; + } + // save the params in flash + flashConfig.staticip = ipi.ip.addr; + flashConfig.netmask = ipi.netmask.addr; + flashConfig.gateway = ipi.gw.addr; + // construct redirect URL + os_sprintf(url, "{\"url\": \"http://%d.%d.%d.%d\"}", IP2STR(&ipi.ip)); + + } else { + // dynamic IP + flashConfig.staticip = 0; + os_sprintf(url, "{\"url\": \"http://%s\"}", flashConfig.hostname); + } + + configSave(); // ignore error... + // schedule change-over + os_timer_disarm(&reassTimer); + os_timer_setfn(&reassTimer, configWifiIP, NULL); + os_timer_arm(&reassTimer, 1000, 0); // 1 second for the response of this request to make it + // return redirect info + jsonHeader(connData, 200); + httpdSend(connData, url, -1); + return HTTPD_CGI_DONE; +} + // Check string againt invalid characters int ICACHE_FLASH_ATTR checkString(char *str){ for(int i=0; iChange! + +
+

Security Settings

+
+ Disable or protect station access +
+ Port 1 + + + +
+
+
+ + +
+
+ + +
+ +
+ Port 2 + + + +
+
+
+ + +
+
+ + +
+ + +
+
+ @@ -78,6 +131,17 @@ onLoad(function() { getWifiInfo(); bnd($("#wifiform"), "submit", changeWifiAp); bnd($("#specform"), "submit", changeSpecial); + + getSecurityInfo(); + bnd($("#securform"), "submit", changeSecurity); + bnd($("#secure-port1-roff"), "click", doPort1Off); + bnd($("#secure-port1-ron"), "click", doPort1Unsecure); + bnd($("#secure-port1-rpwd"), "click", doPort1Secure); + + bnd($("#secure-port2-roff"), "click", doPort2Off); + bnd($("#secure-port2-ron"), "click", doPort2Unsecure); + bnd($("#secure-port2-rpwd"), "click", doPort2Secure); + bnd($("#dhcp-ron"), "click", doDhcp); bnd($("#dhcp-roff"), "click", doStatic); scanTimeout = window.setTimeout(scanAPs, 500); diff --git a/html/wifi/wifiSta.js b/html/wifi/wifiSta.js index 0ab167a..f46e822 100644 --- a/html/wifi/wifiSta.js +++ b/html/wifi/wifiSta.js @@ -192,6 +192,68 @@ function changeSpecial(e) { }); } +function changeSecurity(e) { + e.preventDefault(); + var url = "security"; + + url += "?port1=" + document.querySelector('input[name="port1"]:checked').value; + url += "&portnumber=" + encodeURIComponent($("#port1-portnumber").value); + url += "&password=" + encodeURIComponent($("#port1-password").value); + + url += "?port2=" + document.querySelector('input[name="port2"]:checked').value; + url += "&portnumber=" + encodeURIComponent($("#port2-portnumber").value); + url += "&password=" + encodeURIComponent($("#port2-password").value); + + hideWarning(); + var cb = $("#secure-button"); + addClass(cb, 'pure-button-disabled'); + ajaxSpin("POST", url, function(resp) { + removeClass(cb, 'pure-button-disabled'); + getSecurityInfo(); + //getWifiInfo(); // it takes 1 second for new settings to be applied + }, function(s, st) { + showWarning("Error: "+st); + removeClass(cb, 'pure-button-disabled'); + getSecurityInfo(); + }); +} + +function doPort1Secure() { + $('#port1-off').setAttribute('hidden', ''); + $('#port1-pn').removeAttribute('hidden'); + $('#port1-pwd').removeAttribute('hidden'); +} + +function doPort1Off() { + $('#port1-off').removeAttribute('hidden'); + $('#port1-pn').setAttribute('hidden', ''); + $('#port1-pwd').setAttribute('hidden', ''); +} + +function doPort1Unsecure() { + $('#port1-off').setAttribute('hidden', ''); + $('#port1-pn').removeAttribute('hidden'); + $('#port1-pwd').setAttribute('hidden', ''); +} + +function doPort2Secure() { + $('#port2-off').setAttribute('hidden', ''); + $('#port2-pn').removeAttribute('hidden'); + $('#port2-pwd').removeAttribute('hidden'); +} + +function doPort2Off() { + $('#port2-off').removeAttribute('hidden'); + $('#port2-pn').setAttribute('hidden', ''); + $('#port2-pwd').setAttribute('hidden', ''); +} + +function doPort2Unsecure() { + $('#port2-off').setAttribute('hidden', ''); + $('#port2-pn').removeAttribute('hidden'); + $('#port2-pwd').setAttribute('hidden', ''); +} + function doDhcp() { $('#dhcp-on').removeAttribute('hidden'); $('#dhcp-off').setAttribute('hidden', ''); From fcced95547e377ce8ea2b686355772951bb5ead0 Mon Sep 17 00:00:00 2001 From: dannybackx Date: Mon, 31 Oct 2016 21:16:36 +0100 Subject: [PATCH 06/14] This may bring us back to the original submission, if my subversion tricks are right. --- cmd/cmd.h | 2 - cmd/handlers.c | 29 ------------- esp-link/cgiwifi.c | 97 +----------------------------------------- esp-link/cgiwifi.h | 2 - esp-link/config.c | 4 -- html/ui.js | 19 --------- html/wifi/wifiSta.html | 64 ---------------------------- html/wifi/wifiSta.js | 62 --------------------------- 8 files changed, 1 insertion(+), 278 deletions(-) diff --git a/cmd/cmd.h b/cmd/cmd.h index 03549cc..3b4e8ab 100644 --- a/cmd/cmd.h +++ b/cmd/cmd.h @@ -58,8 +58,6 @@ typedef enum { CMD_SOCKET_SETUP = 40, // set-up callbacks CMD_SOCKET_SEND, // send data over UDP socket - CMD_GET_WIFI_INFO = 50,// Query IP address info - } CmdName; typedef void (*cmdfunc_t)(CmdPacket *cmd); diff --git a/cmd/handlers.c b/cmd/handlers.c index ee43982..c191091 100644 --- a/cmd/handlers.c +++ b/cmd/handlers.c @@ -17,7 +17,6 @@ #ifdef SOCKET #include #endif -#include #ifdef CMD_DBG #define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0) @@ -30,7 +29,6 @@ static void cmdSync(CmdPacket *cmd); static void cmdWifiStatus(CmdPacket *cmd); static void cmdGetTime(CmdPacket *cmd); static void cmdAddCallback(CmdPacket *cmd); -static void cmdGetWifiInfo(CmdPacket *cmd); // keep track of last status sent to uC so we can notify it when it changes static uint8_t lastWifiStatus = wifiIsDisconnected; @@ -46,7 +44,6 @@ const CmdList commands[] = { {CMD_WIFI_STATUS, "WIFI_STATUS", cmdWifiStatus}, {CMD_CB_ADD, "ADD_CB", cmdAddCallback}, {CMD_GET_TIME, "GET_TIME", cmdGetTime}, - {CMD_GET_WIFI_INFO, "GET_WIFI_INFO", cmdGetWifiInfo}, #ifdef MQTT {CMD_MQTT_SETUP, "MQTT_SETUP", MQTTCMD_Setup}, {CMD_MQTT_PUBLISH, "MQTT_PUB", MQTTCMD_Publish}, @@ -181,32 +178,6 @@ cmdGetTime(CmdPacket *cmd) { return; } -// Command handler for IP information -static void ICACHE_FLASH_ATTR -cmdGetWifiInfo(CmdPacket *cmd) { - CmdRequest req; - - cmdRequest(&req, cmd); - if(cmd->argc != 0 || cmd->value == 0) { - cmdResponseStart(CMD_RESP_V, 0, 0); - cmdResponseEnd(); - return; - } - - uint32_t callback = req.cmd->value; - - struct ip_info info; - wifi_get_ip_info(0, &info); - - cmdResponseStart(CMD_RESP_CB, callback, 3); - cmdResponseBody(&info.ip.addr, sizeof(info.ip.addr)); - cmdResponseBody(&info.netmask.addr, sizeof(info.netmask.addr)); - cmdResponseBody(&info.gw.addr, sizeof(info.gw.addr)); - cmdResponseEnd(); - - return; -} - // Command handler to add a callback to the named-callbacks list, this is for a callback to the uC static void ICACHE_FLASH_ATTR cmdAddCallback(CmdPacket *cmd) { diff --git a/esp-link/cgiwifi.c b/esp-link/cgiwifi.c index 284704e..70d40dc 100644 --- a/esp-link/cgiwifi.c +++ b/esp-link/cgiwifi.c @@ -34,7 +34,7 @@ bool mdns_started = false; // ===== wifi status change callbacks static WifiStateChangeCb wifi_state_change_cb[4]; -// Temp store for new station config +// Temp store for new staion config struct station_config stconf; // Temp store for new ap config @@ -54,8 +54,6 @@ static char *wifiReasons[] = { static char *wifiMode[] = { 0, "STA", "AP", "AP+STA" }; static char *wifiPhy[] = { 0, "11b", "11g", "11n" }; -static char *portMode[] = { "unsecure", "disabled", "secure" }; - void (*wifiStatusCb)(uint8_t); // callback when wifi status changes static char* ICACHE_FLASH_ATTR wifiGetReason(void) { @@ -826,99 +824,6 @@ int ICACHE_FLASH_ATTR cgiWifiInfo(HttpdConnData *connData) { return HTTPD_CGI_DONE; } -static char *portMode2string(int8_t m) { - if (m < 0 || m > 2) return "?"; - return portMode[m]; -} - -// print various Wifi information into json buffer -int ICACHE_FLASH_ATTR printWiFiSecurity(char *buff) { - int len; - - len = os_sprintf(buff, - "\"port1mode\" : \"%s\", \"port1portnumber\" : \"%d\", \"port1pwd\" : \"%s\", " - "\"port2mode\" : \"%s\", \"port2portnumber\" : \"%d\", \"port2pwd\" : \"%s\" ", - portMode2string(flashConfig.port1_mode), flashConfig.port1_portnumber, "", - portMode2string(flashConfig.port2_mode), flashConfig.port2_portnumber, "" - ); - - return len; -} - -// Cgi to return various Wifi information -int ICACHE_FLASH_ATTR jsonWiFiSecurity(HttpdConnData *connData) { - char buff[1024]; - - if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. - - os_strcpy(buff, "{"); - printWiFiSecurity(buff+1); - os_strcat(buff, "}"); - - jsonHeader(connData, 200); - httpdSend(connData, buff, -1); - return HTTPD_CGI_DONE; -} - -// Change security settings -int ICACHE_FLASH_ATTR cgiWiFiSecurity(HttpdConnData *connData) { - char dhcp[8]; - char staticip[20]; - char netmask[20]; - char gateway[20]; - - if (connData->conn==NULL) return HTTPD_CGI_DONE; - - // get args and their string lengths - int dl = httpdFindArg(connData->getArgs, "dhcp", dhcp, sizeof(dhcp)); - int sl = httpdFindArg(connData->getArgs, "staticip", staticip, sizeof(staticip)); - int nl = httpdFindArg(connData->getArgs, "netmask", netmask, sizeof(netmask)); - int gl = httpdFindArg(connData->getArgs, "gateway", gateway, sizeof(gateway)); - - if (!(dl > 0 && sl >= 0 && nl >= 0 && gl >= 0)) { - jsonHeader(connData, 400); - httpdSend(connData, "Request is missing fields", -1); - return HTTPD_CGI_DONE; - } - - char url[64]; // redirect URL - if (os_strcmp(dhcp, "off") == 0) { - // parse static IP params - struct ip_info ipi; - bool ok = parse_ip(staticip, &ipi.ip); - if (nl > 0) ok = ok && parse_ip(netmask, &ipi.netmask); - else IP4_ADDR(&ipi.netmask, 255, 255, 255, 0); - if (gl > 0) ok = ok && parse_ip(gateway, &ipi.gw); - else ipi.gw.addr = 0; - if (!ok) { - jsonHeader(connData, 400); - httpdSend(connData, "Cannot parse static IP config", -1); - return HTTPD_CGI_DONE; - } - // save the params in flash - flashConfig.staticip = ipi.ip.addr; - flashConfig.netmask = ipi.netmask.addr; - flashConfig.gateway = ipi.gw.addr; - // construct redirect URL - os_sprintf(url, "{\"url\": \"http://%d.%d.%d.%d\"}", IP2STR(&ipi.ip)); - - } else { - // dynamic IP - flashConfig.staticip = 0; - os_sprintf(url, "{\"url\": \"http://%s\"}", flashConfig.hostname); - } - - configSave(); // ignore error... - // schedule change-over - os_timer_disarm(&reassTimer); - os_timer_setfn(&reassTimer, configWifiIP, NULL); - os_timer_arm(&reassTimer, 1000, 0); // 1 second for the response of this request to make it - // return redirect info - jsonHeader(connData, 200); - httpdSend(connData, url, -1); - return HTTPD_CGI_DONE; -} - // Check string againt invalid characters int ICACHE_FLASH_ATTR checkString(char *str){ for(int i=0; iChange! - -
-

Security Settings

-
- Disable or protect station access -
- Port 1 - - - -
-
-
- - -
-
- - -
- -
- Port 2 - - - -
-
-
- - -
-
- - -
- - -
-
- @@ -131,17 +78,6 @@ onLoad(function() { getWifiInfo(); bnd($("#wifiform"), "submit", changeWifiAp); bnd($("#specform"), "submit", changeSpecial); - - getSecurityInfo(); - bnd($("#securform"), "submit", changeSecurity); - bnd($("#secure-port1-roff"), "click", doPort1Off); - bnd($("#secure-port1-ron"), "click", doPort1Unsecure); - bnd($("#secure-port1-rpwd"), "click", doPort1Secure); - - bnd($("#secure-port2-roff"), "click", doPort2Off); - bnd($("#secure-port2-ron"), "click", doPort2Unsecure); - bnd($("#secure-port2-rpwd"), "click", doPort2Secure); - bnd($("#dhcp-ron"), "click", doDhcp); bnd($("#dhcp-roff"), "click", doStatic); scanTimeout = window.setTimeout(scanAPs, 500); diff --git a/html/wifi/wifiSta.js b/html/wifi/wifiSta.js index f46e822..0ab167a 100644 --- a/html/wifi/wifiSta.js +++ b/html/wifi/wifiSta.js @@ -192,68 +192,6 @@ function changeSpecial(e) { }); } -function changeSecurity(e) { - e.preventDefault(); - var url = "security"; - - url += "?port1=" + document.querySelector('input[name="port1"]:checked').value; - url += "&portnumber=" + encodeURIComponent($("#port1-portnumber").value); - url += "&password=" + encodeURIComponent($("#port1-password").value); - - url += "?port2=" + document.querySelector('input[name="port2"]:checked').value; - url += "&portnumber=" + encodeURIComponent($("#port2-portnumber").value); - url += "&password=" + encodeURIComponent($("#port2-password").value); - - hideWarning(); - var cb = $("#secure-button"); - addClass(cb, 'pure-button-disabled'); - ajaxSpin("POST", url, function(resp) { - removeClass(cb, 'pure-button-disabled'); - getSecurityInfo(); - //getWifiInfo(); // it takes 1 second for new settings to be applied - }, function(s, st) { - showWarning("Error: "+st); - removeClass(cb, 'pure-button-disabled'); - getSecurityInfo(); - }); -} - -function doPort1Secure() { - $('#port1-off').setAttribute('hidden', ''); - $('#port1-pn').removeAttribute('hidden'); - $('#port1-pwd').removeAttribute('hidden'); -} - -function doPort1Off() { - $('#port1-off').removeAttribute('hidden'); - $('#port1-pn').setAttribute('hidden', ''); - $('#port1-pwd').setAttribute('hidden', ''); -} - -function doPort1Unsecure() { - $('#port1-off').setAttribute('hidden', ''); - $('#port1-pn').removeAttribute('hidden'); - $('#port1-pwd').setAttribute('hidden', ''); -} - -function doPort2Secure() { - $('#port2-off').setAttribute('hidden', ''); - $('#port2-pn').removeAttribute('hidden'); - $('#port2-pwd').removeAttribute('hidden'); -} - -function doPort2Off() { - $('#port2-off').removeAttribute('hidden'); - $('#port2-pn').setAttribute('hidden', ''); - $('#port2-pwd').setAttribute('hidden', ''); -} - -function doPort2Unsecure() { - $('#port2-off').setAttribute('hidden', ''); - $('#port2-pn').removeAttribute('hidden'); - $('#port2-pwd').setAttribute('hidden', ''); -} - function doDhcp() { $('#dhcp-on').removeAttribute('hidden'); $('#dhcp-off').setAttribute('hidden', ''); From 121576cd150c7fcb529be0ff58cc70c168e5374c Mon Sep 17 00:00:00 2001 From: dannybackx Date: Mon, 31 Oct 2016 21:28:48 +0100 Subject: [PATCH 07/14] Bring code back, with changes cfr review --- cmd/cmd.h | 1 + cmd/handlers.c | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/cmd/cmd.h b/cmd/cmd.h index 3b4e8ab..a0abadf 100644 --- a/cmd/cmd.h +++ b/cmd/cmd.h @@ -42,6 +42,7 @@ typedef enum { CMD_CB_ADD, CMD_CB_EVENTS, CMD_GET_TIME, // get current time in seconds since the unix epoch + CMD_GET_WIFI_INFO, // query ip address info CMD_MQTT_SETUP = 10, // set-up callbacks CMD_MQTT_PUBLISH, // publish a message diff --git a/cmd/handlers.c b/cmd/handlers.c index c191091..09fd1de 100644 --- a/cmd/handlers.c +++ b/cmd/handlers.c @@ -17,6 +17,7 @@ #ifdef SOCKET #include #endif +#include #ifdef CMD_DBG #define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0) @@ -44,6 +45,7 @@ const CmdList commands[] = { {CMD_WIFI_STATUS, "WIFI_STATUS", cmdWifiStatus}, {CMD_CB_ADD, "ADD_CB", cmdAddCallback}, {CMD_GET_TIME, "GET_TIME", cmdGetTime}, + {CMD_GET_WIFI_INFO, "GET_WIFI_INFO", cmdGetWifiInfo}, #ifdef MQTT {CMD_MQTT_SETUP, "MQTT_SETUP", MQTTCMD_Setup}, {CMD_MQTT_PUBLISH, "MQTT_PUB", MQTTCMD_Publish}, @@ -178,6 +180,30 @@ cmdGetTime(CmdPacket *cmd) { return; } +// Command handler for IP information +static void ICACHE_FLASH_ATTR +cmdGetWifiInfo(CmdPacket *cmd) { + CmdRequest req; + + cmdRequest(&req, cmd); + if(cmd->argc != 0 || cmd->value == 0) { + cmdResponseStart(CMD_RESP_V, 0, 0); + cmdResponseEnd(); + return; + } + + uint32_t callback = req.cmd->value; + + struct ip_info info; + wifi_get_ip_info(0, &info); + + cmdResponseStart(CMD_RESP_CB, callback, 3); + cmdResponseBody(&info.ip.addr, sizeof(info.ip.addr)); + cmdResponseBody(&info.netmask.addr, sizeof(info.netmask.addr)); + cmdResponseBody(&info.gw.addr, sizeof(info.gw.addr)); + cmdResponseEnd(); +} + // Command handler to add a callback to the named-callbacks list, this is for a callback to the uC static void ICACHE_FLASH_ATTR cmdAddCallback(CmdPacket *cmd) { From 49ff52d07070db90e5d9658f455c363a842d9578 Mon Sep 17 00:00:00 2001 From: dannybackx Date: Mon, 31 Oct 2016 21:30:42 +0100 Subject: [PATCH 08/14] Fix --- cmd/handlers.c | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/handlers.c b/cmd/handlers.c index 09fd1de..2b98239 100644 --- a/cmd/handlers.c +++ b/cmd/handlers.c @@ -29,6 +29,7 @@ static void cmdNull(CmdPacket *cmd); static void cmdSync(CmdPacket *cmd); static void cmdWifiStatus(CmdPacket *cmd); static void cmdGetTime(CmdPacket *cmd); +static void cmdGetWifiInfo(CmdPacket *cmd); static void cmdAddCallback(CmdPacket *cmd); // keep track of last status sent to uC so we can notify it when it changes From 416538ff3922167202201bd26b360c10c2cc19de Mon Sep 17 00:00:00 2001 From: dannybackx Date: Mon, 31 Oct 2016 21:33:31 +0100 Subject: [PATCH 09/14] Fix --- esp-link/main.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/esp-link/main.c b/esp-link/main.c index a2c40d7..e775de9 100644 --- a/esp-link/main.c +++ b/esp-link/main.c @@ -89,7 +89,6 @@ HttpdBuiltInUrl builtInUrls[] = { { "/wifi/connstatus", cgiWiFiConnStatus, NULL }, { "/wifi/setmode", cgiWiFiSetMode, NULL }, { "/wifi/special", cgiWiFiSpecial, NULL }, - { "/wifi/security", jsonWiFiSecurity, NULL }, { "/wifi/apinfo", cgiApSettingsInfo, NULL }, { "/wifi/apchange", cgiApSettingsChange, NULL }, { "/system/info", cgiSystemInfo, NULL }, @@ -180,15 +179,7 @@ user_init(void) { WEB_Init(); // init the wifi-serial transparent bridge (port 23) - flashConfig.port1_portnumber = 23; - flashConfig.port2_portnumber = 2323; - flashConfig.port1_mode = 0; - flashConfig.port2_mode = 0; - - serbridgeInit(); - serbridgeStart(0, flashConfig.port1_portnumber, flashConfig.port1_mode); - serbridgeStart(1, flashConfig.port2_portnumber, flashConfig.port2_mode); - + serbridgeInit(23, 2323); uart_add_recv_cb(&serbridgeUartCb); #ifdef SHOW_HEAP_USE os_timer_disarm(&prHeapTimer); From 31b84e695c74d59536c393e40e4879133b3d3654 Mon Sep 17 00:00:00 2001 From: dannybackx Date: Tue, 1 Nov 2016 08:04:13 +0100 Subject: [PATCH 10/14] More cleanup of my mess --- Makefile | 9 -------- serial/serbridge.c | 55 ++++++++++++++++++++++++---------------------- serial/serbridge.h | 3 +-- 3 files changed, 30 insertions(+), 37 deletions(-) diff --git a/Makefile b/Makefile index c626ff2..0b52b9f 100644 --- a/Makefile +++ b/Makefile @@ -99,15 +99,6 @@ MCU_ISP_PIN ?= 13 LED_CONN_PIN ?= 0 # GPIO pin used for "serial activity" LED, active low LED_SERIAL_PIN ?= 14 -# -# Default settings for access over TCP/IP connections -# -# Modes are 0 (unsecure), 1 (disabled), 2 (secure) -# -PORT1_MODE ?= 0 -PORT1_PORTNUMBER ?= 23 -PORT2_MODE ?= 0 -PORT2_PORTNUMBER ?= 2323 # --------------- esp-link modules config options --------------- diff --git a/serial/serbridge.c b/serial/serbridge.c index 02517cf..cebc561 100644 --- a/serial/serbridge.c +++ b/serial/serbridge.c @@ -16,8 +16,9 @@ #define syslog(X1...) #endif -static struct espconn serbridgeConn[2]; // plain bridging port -static esp_tcp serbridgeTcp[2]; +static struct espconn serbridgeConn1; // plain bridging port +static struct espconn serbridgeConn2; // programming port +static esp_tcp serbridgeTcp1, serbridgeTcp2; static int8_t mcu_reset_pin, mcu_isp_pin; uint8_t in_mcu_flashing; // for disabling slip during MCU flashing @@ -420,7 +421,7 @@ serbridgeConnectCb(void *arg) connData[i].readytosend = true; connData[i].conn_mode = cmInit; // if it's the second port we start out in programming mode - if (conn->proto.tcp->local_port == serbridgeConn[1].proto.tcp->local_port) + if (conn->proto.tcp->local_port == serbridgeConn2.proto.tcp->local_port) connData[i].conn_mode = cmPGMInit; espconn_regist_recvcb(conn, serbridgeRecvCb); @@ -469,33 +470,35 @@ serbridgeInitPins() // Start transparent serial bridge TCP server on specified port (typ. 23) void ICACHE_FLASH_ATTR -serbridgeInit() +serbridgeInit(int port1, int port2) { serbridgeInitPins(); os_memset(connData, 0, sizeof(connData)); - os_memset(&serbridgeTcp[0], 0, sizeof(serbridgeTcp[0])); - os_memset(&serbridgeTcp[1], 0, sizeof(serbridgeTcp[1])); -} - -// Start transparent serial bridge TCP server on specified port (typ. 23) -void ICACHE_FLASH_ATTR -serbridgeStart(int ix, int port, int mode) -{ - if (ix < 0 || ix > 2) // FIXME hardcoded limit - return; - if (0 < port && port < 65536) { - // set-up the primary port for plain bridging - serbridgeConn[ix].type = ESPCONN_TCP; - serbridgeConn[ix].state = ESPCONN_NONE; - serbridgeTcp[ix].local_port = port; - serbridgeConn[ix].proto.tcp = &serbridgeTcp[ix]; - - espconn_regist_connectcb(&serbridgeConn[ix], serbridgeConnectCb); - espconn_accept(&serbridgeConn[ix]); - espconn_tcp_set_max_con_allow(&serbridgeConn[ix], MAX_CONN); - espconn_regist_time(&serbridgeConn[ix], SER_BRIDGE_TIMEOUT, 0); - } + os_memset(&serbridgeTcp1, 0, sizeof(serbridgeTcp1)); + os_memset(&serbridgeTcp2, 0, sizeof(serbridgeTcp2)); + + // set-up the primary port for plain bridging + serbridgeConn1.type = ESPCONN_TCP; + serbridgeConn1.state = ESPCONN_NONE; + serbridgeTcp1.local_port = port1; + serbridgeConn1.proto.tcp = &serbridgeTcp1; + + espconn_regist_connectcb(&serbridgeConn1, serbridgeConnectCb); + espconn_accept(&serbridgeConn1); + espconn_tcp_set_max_con_allow(&serbridgeConn1, MAX_CONN); + espconn_regist_time(&serbridgeConn1, SER_BRIDGE_TIMEOUT, 0); + + // set-up the secondary port for programming + serbridgeConn2.type = ESPCONN_TCP; + serbridgeConn2.state = ESPCONN_NONE; + serbridgeTcp2.local_port = port2; + serbridgeConn2.proto.tcp = &serbridgeTcp2; + + espconn_regist_connectcb(&serbridgeConn2, serbridgeConnectCb); + espconn_accept(&serbridgeConn2); + espconn_tcp_set_max_con_allow(&serbridgeConn2, MAX_CONN); + espconn_regist_time(&serbridgeConn2, SER_BRIDGE_TIMEOUT, 0); } int ICACHE_FLASH_ATTR serbridgeInMCUFlashing() diff --git a/serial/serbridge.h b/serial/serbridge.h index 36f66d0..ed661e1 100644 --- a/serial/serbridge.h +++ b/serial/serbridge.h @@ -31,8 +31,7 @@ typedef struct serbridgeConnData { } serbridgeConnData; // port1 is transparent&programming, second port is programming only -void ICACHE_FLASH_ATTR serbridgeInit(); -void ICACHE_FLASH_ATTR serbridgeStart(int ix, int port, int mode); +void ICACHE_FLASH_ATTR serbridgeInit(int port1, int port2); void ICACHE_FLASH_ATTR serbridgeInitPins(void); void ICACHE_FLASH_ATTR serbridgeUartCb(char *buf, short len); void ICACHE_FLASH_ATTR serbridgeReset(); From 353fb42e083f624c6200c76a8eea819b33e4cc0b Mon Sep 17 00:00:00 2001 From: dannybackx Date: Tue, 1 Nov 2016 08:04:56 +0100 Subject: [PATCH 11/14] Forgot --- Makefile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Makefile b/Makefile index 0b52b9f..4172785 100644 --- a/Makefile +++ b/Makefile @@ -243,8 +243,6 @@ CFLAGS += -Os -ggdb -std=c99 -Werror -Wpointer-arith -Wundef -Wall -Wl,-EL -fno- -D__ets__ -DICACHE_FLASH -Wno-address -DFIRMWARE_SIZE=$(ESP_FLASH_MAX) \ -DMCU_RESET_PIN=$(MCU_RESET_PIN) -DMCU_ISP_PIN=$(MCU_ISP_PIN) \ -DLED_CONN_PIN=$(LED_CONN_PIN) -DLED_SERIAL_PIN=$(LED_SERIAL_PIN) \ - -DPORT1_PORTNUMBER=$(PORT1_PORTNUMBER) -DPORT1_MODE=$(PORT1_MODE) \ - -DPORT2_PORTNUMBER=$(PORT2_PORTNUMBER) -DPORT2_MODE=$(PORT2_MODE) \ -DVERSION="$(VERSION)" # linker flags used to generate the main object file From 3e26004ed564beface858cca7412c1761dbce568 Mon Sep 17 00:00:00 2001 From: dannybackx Date: Tue, 1 Nov 2016 08:05:42 +0100 Subject: [PATCH 12/14] Remove --- esp-link/config.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/esp-link/config.h b/esp-link/config.h index fecf5c3..65195d2 100644 --- a/esp-link/config.h +++ b/esp-link/config.h @@ -41,10 +41,6 @@ typedef struct { int8_t data_bits; int8_t parity; int8_t stop_bits; - int8_t port1_mode, // Security - port2_mode; - uint16_t port1_portnumber, - port2_portnumber; } FlashConfig; extern FlashConfig flashConfig; From d20c343329cac8d15c904c79bc45e69eb84f2d47 Mon Sep 17 00:00:00 2001 From: dannybackx Date: Tue, 1 Nov 2016 08:53:56 +0100 Subject: [PATCH 13/14] Add MAC address as remarked in the code review --- cmd/handlers.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/handlers.c b/cmd/handlers.c index 2b98239..db2826c 100644 --- a/cmd/handlers.c +++ b/cmd/handlers.c @@ -197,11 +197,14 @@ cmdGetWifiInfo(CmdPacket *cmd) { struct ip_info info; wifi_get_ip_info(0, &info); + uint8_t mac[6]; + wifi_get_macaddr(0, mac); - cmdResponseStart(CMD_RESP_CB, callback, 3); + cmdResponseStart(CMD_RESP_CB, callback, 4); cmdResponseBody(&info.ip.addr, sizeof(info.ip.addr)); cmdResponseBody(&info.netmask.addr, sizeof(info.netmask.addr)); cmdResponseBody(&info.gw.addr, sizeof(info.gw.addr)); + cmdResponseBody(mac, sizeof(mac)); cmdResponseEnd(); } From 41c4726edf1119acc2ce24e8d7960f56d20aa8df Mon Sep 17 00:00:00 2001 From: dannybackx Date: Sun, 5 Feb 2017 14:47:45 +0100 Subject: [PATCH 14/14] Upload an initial working version that can flash an Arduino Mega --- esp-link/cgimega.c | 1353 ++++++++++++++++++++++++++++++++++++++++ esp-link/cgimega.h | 14 + esp-link/cgioptiboot.c | 2 + esp-link/main.c | 9 + esp-link/stk500v2.h | 114 ++++ megaflash | 111 ++++ 6 files changed, 1603 insertions(+) create mode 100644 esp-link/cgimega.c create mode 100644 esp-link/cgimega.h create mode 100644 esp-link/stk500v2.h create mode 100755 megaflash diff --git a/esp-link/cgimega.c b/esp-link/cgimega.c new file mode 100644 index 0000000..1f12543 --- /dev/null +++ b/esp-link/cgimega.c @@ -0,0 +1,1353 @@ +/* + * STK500v2 implementation for esp-link + * Copyright (c) 2016-2017 by Danny Backx + * + * based on the framework for "optiboot" (STK500 v1 protocol) in esp-link + * which is copyright (c) 2015 by Thorsten von Eicken + * + * see LICENSE.txt in the esp-link repo + * + * Documentation about the protocol used : see http://www.atmel.com/Images/doc2591.pdf + * + */ + +#include +#include +#include "cgi.h" +#include "config.h" +#include "uart.h" +#include "stk500v2.h" +#include "serbridge.h" +#include "serled.h" + +#define INIT_DELAY 150 // wait this many millisecs before sending anything +#define BAUD_INTERVAL 600 // interval after which we change baud rate +#define PGM_TIMEOUT 20000 // timeout after sync is achieved, in milliseconds +#define PGM_INTERVAL 200 // send sync at this interval in ms when in programming mode +#define ATTEMPTS 8 // number of attempts total to make + +#ifdef OPTIBOOT_DBG +#define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0) +#else +#define DBG(format, ...) do { } while(0) +#endif + +#define DBG_GPIO5 1 // define to 1 to use GPIO5 to trigger scope + +//===== global state + +static ETSTimer optibootTimer; + +static enum { // overall programming states + stateInit = 0, // initial delay + stateSync, // waiting to hear back + stateVar1, // Steps in reading subsequent parameters + stateVar2, + stateVar3, + stateGetSig1, // Reading device signature + stateGetSig2, + stateGetSig3, + stateGetFuse1, + stateGetFuse2, + stateGetFuse3, + stateProg, // programming... +} progState; +static char* progStates[] = { "init", "sync", "var1", "var2", "var3", + "sig1", "sig2", "sig3", + "fuse1", "fuse2", "fuse3", + "prog" }; +static short baudCnt; // counter for sync attempts at different baud rates +static short ackWait; // counter of expected ACKs +static uint32_t baudRate; // baud rate at which we're programming + +#define RESP_SZ 64 +static char responseBuf[RESP_SZ]; // buffer to accumulate responses from optiboot +static short responseLen = 0; // amount accumulated so far +#define ERR_MAX 128 +static char errMessage[ERR_MAX]; // error message + +#define MAX_PAGE_SZ 512 // max flash page size supported +#define MAX_SAVED 512 // max chars in saved buffer +// structure used to remember request details from one callback to the next +// allocated dynamically so we don't burn so much static RAM +static struct optibootData { + char *saved; // buffer for saved incomplete hex records + char *pageBuf; // buffer for received data to be sent to AVR + uint16_t pageLen; // number of bytes in pageBuf + uint16_t pgmSz; // size of flash page to be programmed at a time + uint16_t pgmDone; // number of bytes programmed + uint32_t address; // address to write next page to + uint32_t startTime; // time of program POST request + HttpdConnData *conn; // request doing the programming, so we can cancel it + bool eof; // got EOF record +} *optibootData; + +// STK500v2 variables +static int hardwareVersion, + firmwareVersionMajor, + firmwareVersionMinor, + vTarget; +static uint8_t signature[3]; +uint8_t lfuse, hfuse, efuse; + +// Sequence number of current command/answer. Increment before sending packet. +static int cur_seqno; + +// Structure of a STK500v2 message +struct packet { + uint8_t start; // 0x1B + uint8_t seqno; + uint8_t ms1, ms2; + uint8_t token; // 0x0E + uint8_t *body; // documented max length is 275 + uint8_t cksum; // xor of all bytes including start and body +} pbuf; + +// forward function references +static void megaTimerCB(void *); +static void megaUartRecv(char *buffer, short length); +static bool processRecord(char *buf, short len); +static bool programPage(void); +static void armTimer(uint32_t ms); +static void initBaud(void); +static void initPacket(); +#if 0 +static void cleanupPacket(); +#endif +static int readSyncPacket(); +static void sendSyncPacket(); +static void sendQueryPacket(int param); +static void sendQuerySignaturePacket(int param); +static void sendRebootMCUQuery(); +static void readRebootMCUReply(); +static void sendLoadAddressQuery(uint32_t); +static void sendProgramPageQuery(char *, int); +static void readProgramPageReply(); +static bool reply_ok = false; +static int readReadFuseReply(); +static int getFuseReply(char *, int); +static void sendReadFuseQuery(char fuse); + +static void debug_reset(); +static bool debug(); +static int debug_cnt = 0; +static void debugOn(bool on); + +static void ICACHE_FLASH_ATTR optibootInit() { + debug_reset(); + + progState = stateInit; + baudCnt = 0; + uart0_baud(flashConfig.baud_rate); + ackWait = 0; + errMessage[0] = 0; + responseLen = 0; + programmingCB = NULL; + if (optibootData != NULL) { + if (optibootData->conn != NULL) + optibootData->conn->cgiPrivData = (void *)-1; // signal that request has been aborted + if (optibootData->pageBuf) os_free(optibootData->pageBuf); + if (optibootData->saved) os_free(optibootData->saved); + os_free(optibootData); + optibootData = NULL; + } + os_timer_disarm(&optibootTimer); + DBG("OB init\n"); + + reply_ok = false; +} + +// append one string to another but visually escape non-printing characters in the second +// string using \x00 hex notation, max is the max chars in the concatenated string. +static void ICACHE_FLASH_ATTR appendPretty(char *buf, int max, char *raw, int rawLen) { + int off = strlen(buf); + max -= off + 1; // for null termination + for (int i=0; i= ' ' && c <= '~') { + buf[off++] = c; + } else if (c == '\n') { + buf[off++] = '\\'; + buf[off++] = 'n'; + } else if (c == '\r') { + buf[off++] = '\\'; + buf[off++] = 'r'; + } else { + buf[off++] = '\\'; + buf[off++] = 'x'; + buf[off++] = '0'+(unsigned char)((c>>4)+((c>>4)>9?7:0)); + buf[off++] = '0'+(unsigned char)((c&0xf)+((c&0xf)>9?7:0)); + } + } + buf[off] = 0; +} + +//===== Cgi to reset AVR and get Optiboot into sync +int ICACHE_FLASH_ATTR cgiMegaSync(HttpdConnData *connData) { + if (connData->conn==NULL) + return HTTPD_CGI_DONE; // Connection aborted. Clean up. + + // check that we know the reset pin, else error out with that + if (flashConfig.reset_pin < 0) { + errorResponse(connData, 400, "No reset pin defined"); + + } else if (connData->requestType == HTTPD_METHOD_POST) { + if (debug()) os_printf("cgiMegaSync POST\n"); + + // issue reset + optibootInit(); + baudRate = flashConfig.baud_rate; + programmingCB = megaUartRecv; + initBaud(); + serbridgeReset(); +#if DBG_GPIO5 + makeGpio(5); + gpio_output_set(0, (1<<5), (1<<5), 0); // output 0 +#endif + + // start sync timer + os_timer_disarm(&optibootTimer); + os_timer_setfn(&optibootTimer, megaTimerCB, NULL); + os_timer_arm(&optibootTimer, INIT_DELAY, 0); + + // respond with optimistic OK + noCacheHeaders(connData, 204); + httpdEndHeaders(connData); + httpdSend(connData, "", 0); + + } else if (connData->requestType == HTTPD_METHOD_GET) { + if (debug()) os_printf("cgiMegaSync GET\n"); + + noCacheHeaders(connData, 200); + httpdEndHeaders(connData); + if (!errMessage[0] && progState >= stateProg) { + char buf[64]; + DBG("OB got sync\n"); + os_sprintf(buf, "SYNC at %d baud, board %02x.%02x.%02x, hardware v%d, firmware %d.%d", + baudRate, signature[0], signature[1], signature[2], + hardwareVersion, firmwareVersionMajor, firmwareVersionMinor); + httpdSend(connData, buf, -1); + } else if (errMessage[0] && progState == stateSync) { + DBG("OB cannot sync\n"); + char buf[512]; + os_sprintf(buf, "FAILED to SYNC: %s, got: %d chars\r\n", errMessage, responseLen); + appendPretty(buf, 512, responseBuf, responseLen); + httpdSend(connData, buf, -1); + + // All done, cleanup + // cleanupPacket(); + } else { + httpdSend(connData, errMessage[0] ? errMessage : "NOT READY", -1); + } + + } else { + errorResponse(connData, 404, "Only GET and POST supported"); + } + + return HTTPD_CGI_DONE; +} + +// verify that N chars are hex characters +static bool ICACHE_FLASH_ATTR checkHex(char *buf, short len) { + while (len--) { + char c = *buf++; + if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) + continue; + DBG("OB non-hex\n"); + os_sprintf(errMessage, "Non hex char in POST record: '%c'/0x%02x", c, c); + return false; + } + return true; +} + +// get hex value of some hex characters +static uint32_t ICACHE_FLASH_ATTR getHexValue(char *buf, short len) { + uint32_t v = 0; + while (len--) { + v = (v<<4) | (uint32_t)(*buf & 0xf); + if (*buf > '9') v += 9; + buf++; + } + return v; +} + +// Allocate and initialize, to be called upon first CGI query +static void ICACHE_FLASH_ATTR initPacket() { + cur_seqno = 0; + + pbuf.body = os_malloc(275); + pbuf.start = MESSAGE_START; // 0x1B + pbuf.token = TOKEN; // 0x0E +} + +#if 0 +// To be called after flashing terminates +static void ICACHE_FLASH_ATTR cleanupPacket() { + if (pbuf.body != 0) + os_free(pbuf.body); + pbuf.body = 0; +} +#endif + +static void ICACHE_FLASH_ATTR writePacket() { + int i, len; + len = (pbuf.ms1 << 8) + pbuf.ms2; + + // Copy sequence number after incrementing it. + pbuf.seqno = ++cur_seqno; + + // Compute cksum + pbuf.cksum = 0; + pbuf.cksum ^= pbuf.start; + pbuf.cksum ^= pbuf.seqno; + pbuf.cksum ^= pbuf.ms1; + pbuf.cksum ^= pbuf.ms2; + pbuf.cksum ^= pbuf.token; + for (i=0; i> 1; + + if (pbuf.body == 0) + return; + pbuf.body[0] = CMD_LOAD_ADDRESS; + pbuf.body[1] = 0xFF & (a2 >> 24); + pbuf.body[2] = 0xFF & (a2 >> 16); + pbuf.body[3] = 0xFF & (a2 >> 8); + pbuf.body[4] = 0xFF & a2; + + writePacket(); +} + +static void ICACHE_FLASH_ATTR readLoadAddressReply() { + reply_ok = false; + + int len = readPacket(); + if (len == 2) { + if (pbuf.body[0] == CMD_LOAD_ADDRESS && pbuf.body[1] == STATUS_CMD_OK) { + reply_ok = true; + // os_printf("LoadAddress Reply : ok\n"); + return; + } + } + + os_printf("LoadAddress : %02x %02x (expected %02x %02x), len %d exp 2\n", + pbuf.body[0], pbuf.body[1], CMD_LOAD_ADDRESS, STATUS_CMD_OK, len); +} + +static void ICACHE_FLASH_ATTR sendProgramPageQuery(char *data, int pgmLen) { + int totalLen = pgmLen + 10; + int mode = 0xC1; // already includes the 0x80 "write page" bit + + pbuf.ms1 = totalLen >> 8; + pbuf.ms2 = totalLen & 0xFF; + + pbuf.body[0] = CMD_PROGRAM_FLASH_ISP; + pbuf.body[1] = pgmLen >> 8; + pbuf.body[2] = pgmLen & 0xFF; + pbuf.body[3] = mode; // mode byte + pbuf.body[4] = 0x0A; // delay + pbuf.body[5] = 0x40; // cmd1 + pbuf.body[6] = 0x4C; // cmd2 + pbuf.body[7] = 0x20; // cmd3 + pbuf.body[8] = 0x00; // poll1 + pbuf.body[9] = 0x00; // poll2 + for (int i=0, j=10; ipost->len); + + if (connData->conn==NULL) + return HTTPD_CGI_DONE; // Connection aborted. Clean up. + if (!optibootData) + if (debug()) DBG("OB pgm: state=%d postLen=%d\n", progState, connData->post->len); + + // check that we have sync + if (errMessage[0] || progState < stateProg) { + DBG("OB not in sync, state=%d (%s), err=%s\n", progState, progStates[progState], errMessage); + errorResponse(connData, 400, errMessage[0] ? errMessage : "Optiboot not in sync"); + return HTTPD_CGI_DONE; + } + + // check that we don't have two concurrent programming requests going on + if (connData->cgiPrivData == (void *)-1) { + DBG("OB aborted\n"); + errorResponse(connData, 400, "Request got aborted by a concurrent sync request"); + return HTTPD_CGI_DONE; + } + + // allocate data structure to track programming + if (!optibootData) { + optibootData = os_zalloc(sizeof(struct optibootData)); + char *saved = os_zalloc(MAX_SAVED+1); // need space for string terminator + char *pageBuf = os_zalloc(MAX_PAGE_SZ+MAX_SAVED/2); + if (!optibootData || !pageBuf || !saved) { + errorResponse(connData, 400, "Out of memory"); + return HTTPD_CGI_DONE; + } + optibootData->pageBuf = pageBuf; + optibootData->saved = saved; + optibootData->startTime = system_get_time(); +#if 1 + + optibootData->pgmSz = 256; // HACK FIX ME + // Try to force this to write 256 bytes per page +#else + optibootData->pgmSz = 128; // hard coded for 328p for now, should be query string param +#endif + DBG("OB data alloc\n"); + optibootData->address = 0x80000000; // HACK FIX ME + } + + // iterate through the data received and program the AVR one block at a time + HttpdPostData *post = connData->post; + char *saved = optibootData->saved; + while (post->buffLen > 0) { + // first fill-up the saved buffer + short saveLen = strlen(saved); + if (saveLen < MAX_SAVED) { + short cpy = MAX_SAVED-saveLen; + if (cpy > post->buffLen) cpy = post->buffLen; + os_memcpy(saved+saveLen, post->buff, cpy); + saveLen += cpy; + saved[saveLen] = 0; // string terminator + os_memmove(post->buff, post->buff+cpy, post->buffLen-cpy); + post->buffLen -= cpy; + //DBG("OB cp %d buff->saved\n", cpy); + } + + // process HEX records + while (saveLen >= 11) { // 11 is minimal record length + // skip any CR/LF + short skip = 0; + while (skip < saveLen && (saved[skip] == '\n' || saved[skip] == '\r')) + skip++; + if (skip > 0) { + // shift out cr/lf (keep terminating \0) + os_memmove(saved, saved+skip, saveLen+1-skip); + saveLen -= skip; + if (saveLen < 11) break; + DBG("OB skip %d cr/lf\n", skip); + } + + // inspect whether we have a proper record start + if (saved[0] != ':') { + DBG("OB found non-: start\n"); + os_sprintf(errMessage, "Expected start of record in POST data, got %s", saved); + errorResponse(connData, 400, errMessage); + optibootInit(); + return HTTPD_CGI_DONE; + } + + if (!checkHex(saved+1, 2)) { + errorResponse(connData, 400, errMessage); + optibootInit(); + return HTTPD_CGI_DONE; + } + uint8_t recLen = getHexValue(saved+1, 2); + //DBG("OB record %d\n", recLen); + + // process record + if (saveLen >= 11+recLen*2) { + if (!processRecord(saved, 11+recLen*2)) { + DBG("OB process err %s\n", errMessage); + errorResponse(connData, 400, errMessage); + optibootInit(); + return HTTPD_CGI_DONE; + } + short shift = 11+recLen*2; + os_memmove(saved, saved+shift, saveLen+1-shift); + saveLen -= shift; + //DBG("OB %d byte record\n", shift); + } else { + break; + } + } + } + + short code; + if (post->received < post->len) { + //DBG("OB pgm need more\n"); + return HTTPD_CGI_MORE; + } + + if (optibootData->eof) { +#if 0 + // tell optiboot to reboot into the sketch + sendRebootMCUQuery(); + readRebootMCUReply(); + // cleanupPacket(); +#endif + code = 200; + // calculate some stats + float dt = ((system_get_time() - optibootData->startTime)/1000)/1000.0; // in seconds + uint16_t pgmDone = optibootData->pgmDone; + optibootInit(); + os_sprintf(errMessage, "Success. %d bytes at %d baud in %d.%ds, %dB/s %d%% efficient", + pgmDone, baudRate, (int)dt, (int)(dt*10)%10, (int)(pgmDone/dt), + (int)(100.0*(10.0*pgmDone/baudRate)/dt)); + } else { + code = 400; + optibootInit(); + os_strcpy(errMessage, "Improperly terminated POST data"); + } + + DBG("OB pgm done: %d -- %s\n", code, errMessage); + noCacheHeaders(connData, code); + httpdEndHeaders(connData); + httpdSend(connData, errMessage, -1); + errMessage[0] = 0; + + return HTTPD_CGI_DONE; +} + +// verify checksum +static bool ICACHE_FLASH_ATTR verifyChecksum(char *buf, short len) { + uint8_t sum = 0; + while (len >= 2) { + sum += (uint8_t)getHexValue(buf, 2); + buf += 2; + len -= 2; + } + return sum == 0; +} + +// Process a hex record -- assumes that the records starts with ':' & hex length +static bool ICACHE_FLASH_ATTR processRecord(char *buf, short len) { + buf++; len--; // skip leading ':' + // check we have all hex chars + if (!checkHex(buf, len)) return false; + // verify checksum + if (!verifyChecksum(buf, len)) { + buf[len] = 0; + os_sprintf(errMessage, "Invalid checksum for record %s", buf); + return false; + } + // dispatch based on record type + uint8_t type = getHexValue(buf+6, 2); + switch (type) { + case 0x00: { // data + //DBG("OB REC data %ld pglen=%d\n", getHexValue(buf, 2), optibootData->pageLen); + uint32_t addr = getHexValue(buf+2, 4); + // check whether we need to program previous record(s) + if (optibootData->pageLen > 0 && + addr != ((optibootData->address+optibootData->pageLen)&0xffff)) { + //DBG("OB addr chg\n"); + os_printf("processRecord addr chg, len %d, addr 0x%04x\n", optibootData->pageLen, addr); + if (!programPage()) return false; + } + // set address, unless we're adding to the end (programPage may have changed pageLen) + if (optibootData->pageLen == 0) { + optibootData->address = (optibootData->address & 0xffff0000) | addr; + //DBG("OB set-addr 0x%lx\n", optibootData->address); + } + // append record + uint16_t recLen = getHexValue(buf, 2); + for (uint16_t i=0; ipageBuf[optibootData->pageLen++] = getHexValue(buf+8+2*i, 2); + // program page, if we have a full page + if (optibootData->pageLen >= optibootData->pgmSz) { + //DBG("OB full\n"); + os_printf("processRecord %d, call programPage() %08x\n", optibootData->pgmSz, optibootData->address); + if (!programPage()) return false; + } + break; } + case 0x01: // EOF + DBG("OB EOF\n"); + // program any remaining partial page +#if 1 + if (optibootData->pageLen > 0) { + os_printf("processRecord remaining partial page, len %d, addr 0x%04x\n", optibootData->pageLen, optibootData->address); + if (!programPage()) return false; + } + optibootData->eof = true; +#else + if (optibootData->pageLen > 0) { + // HACK : fill up with 0xFF + while (optibootData->pageLen < 256) { + optibootData->pageBuf[optibootData->pageLen++] = 0xFF; + } + if (!programPage()) return false; + } + optibootData->eof = true; +#endif + break; + case 0x04: // address + DBG("OB address 0x%x\n", getHexValue(buf+8, 4) << 16); + // program any remaining partial page + if (optibootData->pageLen > 0) { + os_printf("processRecord 0x04 remaining partial page, len %d, addr 0x%04x\n", optibootData->pageLen, optibootData->address); + if (!programPage()) return false; + } + optibootData->address = getHexValue(buf+8, 4) << 16; + break; + case 0x05: // start address + // ignore, there's no way to tell optiboot that... + break; + default: + DBG("OB bad record type\n"); + os_sprintf(errMessage, "Invalid/unknown record type: 0x%02x", type); + return false; + } + return true; +} + +// Program a flash page +static bool ICACHE_FLASH_ATTR programPage(void) { + if (debug()) + os_printf("programPage len %d addr 0x%04x\n", optibootData->pageLen, optibootData->address); + + if (optibootData->pageLen == 0) + return true; + armTimer(PGM_TIMEOUT); // keep the timerCB out of the picture + + uint16_t pgmLen = optibootData->pageLen; + if (pgmLen > optibootData->pgmSz) + pgmLen = optibootData->pgmSz; + // DBG("OB pgm %d@0x%x\n", pgmLen, optibootData->address); + + // send address to optiboot (little endian format) +#ifdef DBG_GPIO5 + gpio_output_set((1<<5), 0, (1<<5), 0); // output 1 +#endif + ackWait++; + + uint32_t addr = optibootData->address; + sendLoadAddressQuery(addr); + readLoadAddressReply(); + + armTimer(PGM_TIMEOUT); + if (! reply_ok) { + DBG("OB pgm failed in load address\n"); + return false; + } + armTimer(PGM_TIMEOUT); + // os_printf("OB sent address 0x%04x\n", addr); + + // send page content + sendProgramPageQuery(optibootData->pageBuf, pgmLen); + + armTimer(PGM_TIMEOUT); + readProgramPageReply(); + armTimer(PGM_TIMEOUT); + if (!reply_ok) { + DBG("OB pgm failed in prog page\n"); + return false; + } + + // shift data out of buffer + os_memmove(optibootData->pageBuf, optibootData->pageBuf+pgmLen, optibootData->pageLen-pgmLen); + optibootData->pageLen -= pgmLen; +#if 0 + optibootData->address += pgmLen; +#else + os_printf("Address old %08x ", optibootData->address); + optibootData->address += pgmLen; + os_printf(" new %08x\n", optibootData->address); +#endif + optibootData->pgmDone += pgmLen; + + // DBG("OB pgm OK\n"); + return true; +} + +//===== Rebooting and getting sync + +static void ICACHE_FLASH_ATTR armTimer(uint32_t ms) { + os_timer_disarm(&optibootTimer); + os_timer_arm(&optibootTimer, ms, 0); +} + +static int baudRates[] = { 0, 9600, 57600, 115200 }; + +static void ICACHE_FLASH_ATTR setBaud() { + baudRate = baudRates[(baudCnt++) % 4]; + uart0_baud(baudRate); + //DBG("OB changing to %ld baud\n", baudRate); +} + +static void ICACHE_FLASH_ATTR initBaud() { + baudRates[0] = flashConfig.baud_rate; + setBaud(); +} + +/* + * The timer callback does two things : + * 1. initially we await a timeout to start chatting with the Mega. This is good. + * 2. in other cases, timeouts are a problem, we failed somewhere. + */ +static void ICACHE_FLASH_ATTR megaTimerCB(void *arg) { + if (debug()) os_printf("megaTimerCB state %d (%s)\n", progState, progStates[progState]); + + switch (progState) { + case stateInit: // initial delay expired, send sync chars + initPacket(); + + // os_printf("Reset pin %d to LOW ...", flashConfig.reset_pin); + GPIO_OUTPUT_SET(flashConfig.reset_pin, 0); + os_delay_us(2000L); // Keep reset line low for 2 ms + GPIO_OUTPUT_SET(flashConfig.reset_pin, 1); + // os_printf(" and up again.\n"); + + os_delay_us(2000L); // Now wait an additional 2 ms before sending packets + + // Send a couple of packets, until we get a reply + int ok = -1, i = 0; + while (ok < 0 && i++ < 8) { + os_delay_us(200L); + sendSyncPacket(); + cur_seqno++; + ok = readSyncPacket(); + } + if (ok < 0) + break; // Don't increment progState + + progState++; + + // Discard input until now + responseLen = 0; + + // Send a query, reply to be picked up by megaUartRecv(). + sendQueryPacket(PARAM_HW_VER); + + // Trigger timeout + armTimer(BAUD_INTERVAL-INIT_DELAY); + return; + case stateSync: // oops, must have not heard back!? + case stateProg: // we're programming and we timed-out of inaction + default: // we're trying to get some info from optiboot and it should have responded! + break; + } +} + +/* + * Receive response from optiboot. + * + * Based on our state, pick the response and interpret accordingly; then move state forward + * while sending the next query. + * + * This plays a ping-pong game between us and the MCU. + * Initial query is sent from megaTimerCB(). + */ +static void ICACHE_FLASH_ATTR megaUartRecv(char *buf, short length) { + int ok; + + //if (progState > stateGetSig3) + if (debug()) { + os_printf("megaUartRecv %d bytes, ", length); + for (int i=0; i= 9) { + if (responseBuf[4] == TOKEN && responseBuf[5] == CMD_GET_PARAMETER && responseBuf[6] == STATUS_CMD_OK) { + hardwareVersion = responseBuf[7]; + ok++; + } + os_memcpy(responseBuf, responseBuf+9, responseLen-9); + responseLen -= 9; + } + progState++; + sendQueryPacket(PARAM_SW_MAJOR); + armTimer(PGM_INTERVAL); // reset timer + break; + case stateVar1: + if (responseLen >= 9) { + if (responseBuf[4] == TOKEN && responseBuf[5] == CMD_GET_PARAMETER && responseBuf[6] == STATUS_CMD_OK) { + firmwareVersionMajor = responseBuf[7]; + ok++; + } + os_memcpy(responseBuf, responseBuf+9, responseLen-9); + responseLen -= 9; + } + progState++; + sendQueryPacket(PARAM_SW_MINOR); + armTimer(PGM_INTERVAL); // reset timer + break; + case stateVar2: + if (responseLen >= 9) { + if (responseBuf[4] == TOKEN && responseBuf[5] == CMD_GET_PARAMETER && responseBuf[6] == STATUS_CMD_OK) { + firmwareVersionMinor = responseBuf[7]; + ok++; + } + os_memcpy(responseBuf, responseBuf+9, responseLen-9); + responseLen -= 9; + } + progState++; + sendQueryPacket(PARAM_VTARGET); + armTimer(PGM_INTERVAL); // reset timer + break; + case stateVar3: + if (responseLen >= 9) { + if (responseBuf[4] == TOKEN && responseBuf[5] == CMD_GET_PARAMETER && responseBuf[6] == STATUS_CMD_OK) { + vTarget = responseBuf[7]; + ok++; + } + os_memcpy(responseBuf, responseBuf+9, responseLen-9); + responseLen -= 9; + } + + progState++; + sendQuerySignaturePacket(0); + if (debug()) + os_printf("Hardware version %d, firmware %d.%d. Vtarget = %d.%d V\n", + hardwareVersion, firmwareVersionMajor, firmwareVersionMinor, vTarget / 16, vTarget % 16); + armTimer(PGM_INTERVAL); // reset timer + break; + case stateGetSig1: + if (responseLen >= 13) { + if (responseBuf[4] == TOKEN && responseBuf[5] == CMD_SPI_MULTI && responseBuf[3] == 7) { + signature[0] = responseBuf[10]; + } + os_memcpy(responseBuf, responseBuf+13, responseLen-13); + responseLen -= 13; + progState++; + sendQuerySignaturePacket(1); + } + armTimer(PGM_INTERVAL); // reset timer + break; + case stateGetSig2: + if (responseLen >= 13) { + if (responseBuf[4] == TOKEN && responseBuf[5] == CMD_SPI_MULTI && responseBuf[3] == 7) { + signature[1] = responseBuf[10]; + } + os_memcpy(responseBuf, responseBuf+13, responseLen-13); + responseLen -= 13; + progState++; + sendQuerySignaturePacket(2); + } + armTimer(PGM_INTERVAL); // reset timer + break; + case stateGetSig3: + if (responseLen >= 13) { + if (responseBuf[4] == TOKEN && responseBuf[5] == CMD_SPI_MULTI && responseBuf[3] == 7) { + signature[2] = responseBuf[10]; + } + os_memcpy(responseBuf, responseBuf+13, responseLen-13); + responseLen -= 13; + progState++; + + if (debug()) os_printf("Board signature %02x.%02x.%02x\n", signature[0], signature[1], signature[2]); + sendReadFuseQuery('l'); + } + armTimer(PGM_INTERVAL); // reset timer + break; + + case stateGetFuse1: + lfuse = getFuseReply(buf, length); + sendReadFuseQuery('h'); + progState++; + break; + + case stateGetFuse2: + hfuse = getFuseReply(buf, length); + sendReadFuseQuery('e'); + progState++; + break; + + case stateGetFuse3: + efuse = getFuseReply(buf, length); + if (debug()) + os_printf("Fuses %02x %02x %02x\n", lfuse, hfuse, efuse); + progState++; + break; + + case stateProg: + // Not sure what to do here. + break; + } +} + +int ICACHE_FLASH_ATTR cgiMegaRead(HttpdConnData *connData) { + // os_printf("cgiMegaRead %s\n", connData->url); + + int p, i, len; + char *buffer, s[12]; + + debug_cnt++; + + // sscanf(connData->url + 14, "0x%x,%x", &p, &len); + char *ptr = connData->url + 14; + p = strtoul(ptr, &ptr, 16); + ptr++; // skip comma + len = strtoul(ptr, NULL, 16); + // os_printf("cgiMegaRead : %x %x\n", p, len); + + buffer = os_malloc(len / 16 * 60); + if (buffer == 0) { + espconn_send(connData->conn, (uint8_t *)"Cannot allocate sufficient memory\n", 35); + return HTTPD_CGI_DONE; + } + buffer[0] = 0; + + // initPacket(); + sendLoadAddressQuery(p); + readLoadAddressReply(); + if (! reply_ok) { + espconn_send(connData->conn, (uint8_t *)"Load address failed\n", 20); + return HTTPD_CGI_DONE; + } + + for (i=0; iconn, (uint8_t *)"Unknown problem\n", 16); + return HTTPD_CGI_DONE; + } + + int j; + + os_sprintf(s, "%08x: ", p + i); + strcat(buffer, s); + + for (j=0; j<16; j++) { + os_sprintf(s, "%02x ", flash[j]); + strcat(buffer, s); + } + strcat(buffer, "\n"); + } + espconn_send(connData->conn, (uint8_t *)buffer, strlen(buffer)); + + // cleanupPacket(); + + // espconn_sent(connData->conn, "This is the output ... :-)\n", 27); + return HTTPD_CGI_DONE; +} + +void ICACHE_FLASH_ATTR sendReadFuseQuery(char fuse) { + pbuf.ms1 = 0; + pbuf.ms2 = 8; + pbuf.body[0] = CMD_SPI_MULTI; // 0x1D + pbuf.body[1] = 4; + pbuf.body[2] = 4; + pbuf.body[3] = 0; + switch (fuse) { + case 'l': + pbuf.body[4] = 0x50; pbuf.body[5] = 0x00; + break; + case 'h': + pbuf.body[4] = 0x58; pbuf.body[5] = 0x08; + break; + case 'e': + pbuf.body[4] = 0x50; pbuf.body[5] = 0x08; + break; + } + pbuf.body[6] = 0; + pbuf.body[7] = 0; + + writePacket(); +} + +/* + * To read the feedback directly. + */ +int ICACHE_FLASH_ATTR readReadFuseReply() { + reply_ok = false; + int len = readPacket(); + if (len != 7) { + os_printf("readReadFuseReply: packet len %d, expexted 13.\n", len); + return -1; + } + if (pbuf.body[0] == CMD_SPI_MULTI && pbuf.body[1] == STATUS_CMD_OK) { + reply_ok = true; + return pbuf.body[5]; + } + return 0; +} + +/* + * Called from megaUartRecv so read from the buffer passed as parameter, + * don't call readPacket(). + */ +int ICACHE_FLASH_ATTR getFuseReply(char *ptr, int len) { + reply_ok = false; + if (len != 13) { + os_printf("readReadFuseReply: packet len %d, expexted 13.\n", len); + return -1; + } + if (ptr[4] != TOKEN && ptr[0] != MESSAGE_START) + return -1; + if (ptr[5] == CMD_SPI_MULTI && ptr[6] == STATUS_CMD_OK) { + reply_ok = true; + return ptr[10]; + } + return 0; +} + +int ICACHE_FLASH_ATTR readFuse(char fuse) { + sendReadFuseQuery(fuse); + int x = readReadFuseReply(); + return x; +} + +/* + * /pgmmega/fuse/r1 reads fuse 1 + * /pgmmega/fuse/1 writes fuse 1 + */ +int ICACHE_FLASH_ATTR cgiMegaFuse(HttpdConnData *connData) { + os_printf("cgiMegaFuse %s\n", connData->url); + + // decode url + char fuse = 'l'; + + // read it + int x = readFuse(fuse); + + // provide result + + // handle error cases + if (reply_ok) { + char buf[20]; + os_sprintf(buf, "Fuse '%c' : 0x%x", fuse, x); + + noCacheHeaders(connData, 200); + httpdEndHeaders(connData); + httpdSend(connData, buf, 0); + } else { + errorResponse(connData, 404, "Failed to reboot the MCU"); + } + + return HTTPD_CGI_DONE; +} + +/* + * Reboot the MCU, after which it will be in Arduino mode and we'll have lost + * synchronized communication with it. + */ +int ICACHE_FLASH_ATTR cgiMegaRebootMCU(HttpdConnData *connData) { + sendRebootMCUQuery(); + readRebootMCUReply(); + + if (reply_ok) { + noCacheHeaders(connData, 200); + httpdEndHeaders(connData); + httpdSend(connData, "", 0); + } else { + errorResponse(connData, 404, "Failed to reboot the MCU"); + } + return HTTPD_CGI_DONE; +} + +/* + * Several versions of a function to selectively enable debug output + */ +static void ICACHE_FLASH_ATTR debug_reset() { + debug_cnt = 0; + debugOn(false); +} + +static bool debug_on = false; + +static bool ICACHE_FLASH_ATTR debug() { +#if 0 + debug_cnt++; + if (debug_cnt < 30) + return true; + return false; +#endif +#if 0 + return true; +#endif + +#if 0 + if (debug_cnt > 0) + return true; + return false; +#endif + +#if 0 + if (optibootData == 0) + return false; + if ((0x80000080 <= optibootData->address) && (optibootData->address <= 0x80000100)) + return true; + return false; +#endif + + return debug_on; +} + +static void debugOn(bool on) { + debug_on = on; +} diff --git a/esp-link/cgimega.h b/esp-link/cgimega.h new file mode 100644 index 0000000..e30a2d1 --- /dev/null +++ b/esp-link/cgimega.h @@ -0,0 +1,14 @@ +// Copyright (c) 2016-2017 by Danny Backx, see LICENSE.txt in the esp-link repo + +#ifndef CGIMEGA_H +#define CGIMEGA_H + +#include + +int ICACHE_FLASH_ATTR cgiMegaSync(HttpdConnData *connData); +int ICACHE_FLASH_ATTR cgiMegaData(HttpdConnData *connData); +int ICACHE_FLASH_ATTR cgiMegaRead(HttpdConnData *connData); +int ICACHE_FLASH_ATTR cgiMegaFuse(HttpdConnData *connData); +int ICACHE_FLASH_ATTR cgiMegaRebootMCU(HttpdConnData *connData); + +#endif diff --git a/esp-link/cgioptiboot.c b/esp-link/cgioptiboot.c index ed87d0e..4cb14b3 100644 --- a/esp-link/cgioptiboot.c +++ b/esp-link/cgioptiboot.c @@ -1,5 +1,7 @@ // Copyright (c) 2015 by Thorsten von Eicken, see LICENSE.txt in the esp-link repo +// Protocol used : https://github.com/Optiboot/optiboot/wiki/HowOptibootWorks + #include #include #include "cgi.h" diff --git a/esp-link/main.c b/esp-link/main.c index e775de9..d9cd5f6 100644 --- a/esp-link/main.c +++ b/esp-link/main.c @@ -19,6 +19,7 @@ #include "cgimqtt.h" #include "cgiflash.h" #include "cgioptiboot.h" +#include "cgimega.h" #include "cgiwebserversetup.h" #include "auth.h" #include "espfs.h" @@ -69,8 +70,16 @@ HttpdBuiltInUrl builtInUrls[] = { { "/flash/next", cgiGetFirmwareNext, NULL }, { "/flash/upload", cgiUploadFirmware, NULL }, { "/flash/reboot", cgiRebootFirmware, NULL }, + { "/pgm/sync", cgiOptibootSync, NULL }, { "/pgm/upload", cgiOptibootData, NULL }, + + { "/pgmmega/sync", cgiMegaSync, NULL }, // Start programming mode + { "/pgmmega/upload", cgiMegaData, NULL }, // Upload stuff + { "/pgmmega/read/*", cgiMegaRead, NULL }, // Download stuff (to verify) + { "/pgmmega/fuse/*", cgiMegaFuse, NULL }, // Read or write fuse + { "/pgmmega/rebootmcu", cgiMegaRebootMCU, NULL }, // Get out of programming mode + { "/log/text", ajaxLog, NULL }, { "/log/dbg", ajaxLogDbg, NULL }, { "/log/reset", cgiReset, NULL }, diff --git a/esp-link/stk500v2.h b/esp-link/stk500v2.h new file mode 100644 index 0000000..b7512d1 --- /dev/null +++ b/esp-link/stk500v2.h @@ -0,0 +1,114 @@ +//**** ATMEL AVR - A P P L I C A T I O N N O T E ************************ +//* +//* Title: AVR068 - STK500 Communication Protocol +//* Filename: command.h +//* Version: 1.0 +//* Last updated: 31.01.2005 +//* +//* Support E-mail: avr@atmel.com +//* +//************************************************************************** + +// *****************[ STK message constants ]*************************** + +#define MESSAGE_START 0x1B //= ESC = 27 decimal +#define TOKEN 0x0E + +// *****************[ STK general command constants ]************************** + +#define CMD_SIGN_ON 0x01 +#define CMD_SET_PARAMETER 0x02 +#define CMD_GET_PARAMETER 0x03 +#define CMD_SET_DEVICE_PARAMETERS 0x04 +#define CMD_OSCCAL 0x05 +#define CMD_LOAD_ADDRESS 0x06 +#define CMD_FIRMWARE_UPGRADE 0x07 + + +// *****************[ STK ISP command constants ]****************************** + +#define CMD_ENTER_PROGMODE_ISP 0x10 +#define CMD_LEAVE_PROGMODE_ISP 0x11 +#define CMD_CHIP_ERASE_ISP 0x12 +#define CMD_PROGRAM_FLASH_ISP 0x13 +#define CMD_READ_FLASH_ISP 0x14 +#define CMD_PROGRAM_EEPROM_ISP 0x15 +#define CMD_READ_EEPROM_ISP 0x16 +#define CMD_PROGRAM_FUSE_ISP 0x17 +#define CMD_READ_FUSE_ISP 0x18 +#define CMD_PROGRAM_LOCK_ISP 0x19 +#define CMD_READ_LOCK_ISP 0x1A +#define CMD_READ_SIGNATURE_ISP 0x1B +#define CMD_READ_OSCCAL_ISP 0x1C +#define CMD_SPI_MULTI 0x1D + +// *****************[ STK PP command constants ]******************************* + +#define CMD_ENTER_PROGMODE_PP 0x20 +#define CMD_LEAVE_PROGMODE_PP 0x21 +#define CMD_CHIP_ERASE_PP 0x22 +#define CMD_PROGRAM_FLASH_PP 0x23 +#define CMD_READ_FLASH_PP 0x24 +#define CMD_PROGRAM_EEPROM_PP 0x25 +#define CMD_READ_EEPROM_PP 0x26 +#define CMD_PROGRAM_FUSE_PP 0x27 +#define CMD_READ_FUSE_PP 0x28 +#define CMD_PROGRAM_LOCK_PP 0x29 +#define CMD_READ_LOCK_PP 0x2A +#define CMD_READ_SIGNATURE_PP 0x2B +#define CMD_READ_OSCCAL_PP 0x2C + +#define CMD_SET_CONTROL_STACK 0x2D + +// *****************[ STK HVSP command constants ]***************************** + +#define CMD_ENTER_PROGMODE_HVSP 0x30 +#define CMD_LEAVE_PROGMODE_HVSP 0x31 +#define CMD_CHIP_ERASE_HVSP 0x32 +#define CMD_PROGRAM_FLASH_HVSP 0x33 +#define CMD_READ_FLASH_HVSP 0x34 +#define CMD_PROGRAM_EEPROM_HVSP 0x35 +#define CMD_READ_EEPROM_HVSP 0x36 +#define CMD_PROGRAM_FUSE_HVSP 0x37 +#define CMD_READ_FUSE_HVSP 0x38 +#define CMD_PROGRAM_LOCK_HVSP 0x39 +#define CMD_READ_LOCK_HVSP 0x3A +#define CMD_READ_SIGNATURE_HVSP 0x3B +#define CMD_READ_OSCCAL_HVSP 0x3C + +// *****************[ STK status constants ]*************************** + +// Success +#define STATUS_CMD_OK 0x00 + +// Warnings +#define STATUS_CMD_TOUT 0x80 +#define STATUS_RDY_BSY_TOUT 0x81 +#define STATUS_SET_PARAM_MISSING 0x82 + +// Errors +#define STATUS_CMD_FAILED 0xC0 +#define STATUS_CKSUM_ERROR 0xC1 +#define STATUS_CMD_UNKNOWN 0xC9 + +// *****************[ STK parameter constants ]*************************** +#define PARAM_BUILD_NUMBER_LOW 0x80 +#define PARAM_BUILD_NUMBER_HIGH 0x81 +#define PARAM_HW_VER 0x90 +#define PARAM_SW_MAJOR 0x91 +#define PARAM_SW_MINOR 0x92 +#define PARAM_VTARGET 0x94 +#define PARAM_VADJUST 0x95 +#define PARAM_OSC_PSCALE 0x96 +#define PARAM_OSC_CMATCH 0x97 +#define PARAM_SCK_DURATION 0x98 +#define PARAM_TOPCARD_DETECT 0x9A +#define PARAM_STATUS 0x9C +#define PARAM_DATA 0x9D +#define PARAM_RESET_POLARITY 0x9E +#define PARAM_CONTROLLER_INIT 0x9F + +// *****************[ STK answer constants ]*************************** + +#define ANSWER_CKSUM_ERROR 0xB0 + diff --git a/megaflash b/megaflash new file mode 100755 index 0000000..31e84a3 --- /dev/null +++ b/megaflash @@ -0,0 +1,111 @@ +#! /bin/bash +# +# Flash an Arduino Mega with STK500v2 using the esp-link built-in programmer +# Basically we first reset the AVR and get in sync, and then send the hex file +# +# ---------------------------------------------------------------------------- +# "THE BEER-WARE LICENSE" (Revision 42): +# Thorsten von Eicken wrote this file. As long as you retain +# this notice you can do whatever you want with this stuff. If we meet some day, +# and you think this stuff is worth it, you can buy me a beer in return. +# ---------------------------------------------------------------------------- + +show_help() { + cat </dev/null; then + echo "ERROR: Cannot find curl: it is required for this script." >&2 + exit 1 +fi + +start=`date +%s` + +# ===== Parse arguments + +verbose= + +while getopts "hvx:" opt; do + case "$opt" in + h) show_help; exit 0 ;; + v) verbose=1 ;; + x) foo="$OPTARG" ;; + '?') show_help >&2; exit 1 ;; + esac +done + +# Shift off the options and optional --. +shift "$((OPTIND-1))" + +# Get the fixed arguments +if [[ $# != 2 ]]; then + show_help >&2 + exit 1 +fi +hostname=$1 +hex=$2 + +re='[-A-Za-z0-9.]+' +if [[ ! "$hostname" =~ $re ]]; then + echo "ERROR: hostname ${hostname} is not a valid hostname or ip address" >&2 + exit 1 +fi + +if [[ ! -r "$hex" ]]; then + echo "ERROR: cannot read hex file ($hex)" >&2 + exit 1 +fi + +# ===== Get AVR in sync + +[[ -n "$verbose" ]] && echo "Resetting AVR with http://$hostname/pgmmega/sync" >&2 +v=; [[ -n "$verbose" ]] && v=-v +sync=`curl -m 10 $v -s -w '%{http_code}' -XPOST "http://$hostname/pgmmega/sync"` +if [[ $? != 0 || "$sync" != 204 ]]; then + echo "Error resetting AVR" >&2 + exit 1 +fi + +while true; do + sync=`curl -m 10 $v -s "http://$hostname/pgmmega/sync"` + if [[ $? != 0 ]]; then + echo "Error checking sync" >&2 + exit 1 + fi + case "$sync" in + SYNC*) + echo "AVR in $sync" >&2 + break;; + "NOT READY"*) + [[ -n "$verbose" ]] && echo " Waiting for sync..." >&2 + ;; + *) + echo "Error checking sync: $sync" >&2 + exit 1 + ;; + esac + sleep 0.1 +done + +# ===== Send HEX file + +[[ -n "$verbose" ]] && echo "Sending HEX file for programming" >&2 +sync=`curl -m 10 $v -s -g -d "@$hex" "http://$hostname/pgmmega/upload"` +echo $sync +if [[ $? != 0 || ! "$sync" =~ ^Success ]]; then + echo "Error programming AVR" >&2 + exit 1 +fi + +sec=$(( `date +%s` - $start )) +echo "Success, took $sec seconds" >&2 +exit 0