diff --git a/esp-link/cgimega.c b/esp-link/cgimega.c new file mode 100644 index 0000000..d5acec0 --- /dev/null +++ b/esp-link/cgimega.c @@ -0,0 +1,1167 @@ +/* + * 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 + * + * Note the Intel HEX format is read by this code. + * Format description is e.g. in http://www.keil.com/support/docs/1584/ + * Summary : each line (HEX file record) is formatted as ":llaaaatt[dd...]cc" + * : is the colon that starts every Intel HEX record. + * ll is the record-length field that represents the number of data bytes (dd) in the record. + * aaaa is the address field that represents the starting address for subsequent data. + * tt is the field that represents the HEX record type, which may be one of the following: + * 00 - data record + * 01 - end-of-file record + * 02 - extended segment address record + * 04 - extended linear address record + * 05 - start linear address record (MDK-ARM only) + * dd is a data field that represents one byte of data. A record may have multiple data bytes. + * The number of data bytes in the record must match the number specified by the ll field. + * cc is the checksum field that represents the checksum of the record. The checksum is + * calculated by summing the values of all hexadecimal digit pairs in the record modulo + * 256 and taking the two's complement. + * + */ + +#include +#include +#include "cgi.h" +#include "config.h" +#include "uart.h" +#include "stk500v2.h" +#include "serbridge.h" +#include "serled.h" +#include "pgmshared.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 + +#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 MAX_PAGE_SZ 512 // max flash page size supported +#define MAX_SAVED 512 // max chars in saved buffer + +// 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 void armTimer(uint32_t ms); +static void initBaud(void); +static void initPacket(); +static void allocateOptibootData(); +#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()) DBG("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()) DBG("cgiMegaSync GET\n"); + + noCacheHeaders(connData, 200); + httpdEndHeaders(connData); + if (!errMessage[0] && progState >= stateProg) { + char buf[64]; + if (optibootData == NULL) + allocateOptibootData(); + DBG("OB got sync\n"); + os_sprintf(buf, "SYNC at %d baud, board %02x.%02x.%02x, hardware v%d, firmware %d.%d", + baudRate, optibootData->signature[0], optibootData->signature[1], optibootData->signature[2], + optibootData->hardwareVersion, optibootData->firmwareVersionMajor, optibootData->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; +} + +// 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 +} + +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; + // DBG("LoadAddress Reply : ok\n"); + return; + } + } + + DBG("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; ipageBuf = pageBuf; + optibootData->saved = saved; + optibootData->startTime = system_get_time(); + optibootData->mega = true; + optibootData->pgmSz = 256; // Try to force this to write 256 bytes per page + DBG("OB data alloc\n"); + + optibootData->address = optibootData->segment; +} + +/* + * This is called on /pgmmega/upload . + * + * Functionality provided : flash the hex file that is sent to the Arduino Mega. + * Prerequisite : a succesfull call to /pgmmega/sync to force the Mega into programming mode + * and set up synchronized communication with it. + */ +int ICACHE_FLASH_ATTR cgiMegaData(HttpdConnData *connData) { + // DBG("cgiMegaData state=%d postLen=%d\n", progState, connData->post->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) { + allocateOptibootData(); + if (!optibootData || !optibootData->pageBuf || !optibootData->saved) { + errorResponse(connData, 400, "Out of memory"); + return HTTPD_CGI_DONE; + } + } + + // 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) { + code = 200; + // calculate some stats + float dt = ((system_get_time() - optibootData->startTime)/1000)/1000.0; // in seconds + uint32_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; +} + +// Program a flash page +bool ICACHE_FLASH_ATTR megaProgramPage(void) { + if (optibootData == NULL) + allocateOptibootData(); + if (debug()) + DBG("programPage len %d addr 0x%04x\n", optibootData->pageLen, optibootData->address + optibootData->segment); + + 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 + optibootData->segment); + + // 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 + optibootData->segment; + sendLoadAddressQuery(addr); + readLoadAddressReply(); + + armTimer(PGM_TIMEOUT); + if (! reply_ok) { + DBG("OB pgm failed in load address\n"); + return false; + } + armTimer(PGM_TIMEOUT); + // DBG("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 1 + optibootData->address += pgmLen; +#else + DBG("Address old %08x ", optibootData->address + optibootData->segment); + optibootData->address += pgmLen; + DBG(" new %08x\n", optibootData->address + optibootData->segment); +#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()) DBG("megaTimerCB state %d (%s)\n", progState, progStates[progState]); + + switch (progState) { + case stateInit: // initial delay expired, send sync chars + initPacket(); + + // DBG("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); + // DBG(" 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()) { + DBG("megaUartRecv %d bytes, ", length); + for (int i=0; i= 9) { + if (responseBuf[4] == TOKEN && responseBuf[5] == CMD_GET_PARAMETER && responseBuf[6] == STATUS_CMD_OK) { + optibootData->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) { + optibootData->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) { + optibootData->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) { + optibootData->vTarget = responseBuf[7]; + ok++; + } + os_memcpy(responseBuf, responseBuf+9, responseLen-9); + responseLen -= 9; + } + + progState++; + sendQuerySignaturePacket(0); + if (debug()) + DBG("Hardware version %d, firmware %d.%d. Vtarget = %d.%d V\n", + optibootData->hardwareVersion, optibootData->firmwareVersionMajor, optibootData->firmwareVersionMinor, optibootData->vTarget / 16, optibootData->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) { + optibootData->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) { + optibootData->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) { + optibootData->signature[2] = responseBuf[10]; + } + os_memcpy(responseBuf, responseBuf+13, responseLen-13); + responseLen -= 13; + progState++; + + if (debug()) DBG("Board signature %02x.%02x.%02x\n", optibootData->signature[0], optibootData->signature[1], optibootData->signature[2]); + sendReadFuseQuery('l'); + } + armTimer(PGM_INTERVAL); // reset timer + break; + + case stateGetFuse1: + optibootData->lfuse = getFuseReply(buf, length); + sendReadFuseQuery('h'); + progState++; + break; + + case stateGetFuse2: + optibootData->hfuse = getFuseReply(buf, length); + sendReadFuseQuery('e'); + progState++; + break; + + case stateGetFuse3: + optibootData->efuse = getFuseReply(buf, length); + if (debug()) + DBG("Fuses %02x %02x %02x\n", optibootData->lfuse, optibootData->hfuse, optibootData->efuse); + progState++; + break; + + case stateProg: + // Not sure what to do here. + break; + } +} + +int ICACHE_FLASH_ATTR cgiMegaRead(HttpdConnData *connData) { + // DBG("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); + // DBG("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) { + DBG("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) { + DBG("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) { + DBG("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 ((0x0000F000 <= optibootData->address) && (optibootData->address <= 0x00010200)) + 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 f367092..a220fbd 100644 --- a/esp-link/cgioptiboot.c +++ b/esp-link/cgioptiboot.c @@ -1,5 +1,10 @@ // Copyright (c) 2015 by Thorsten von Eicken, see LICENSE.txt in the esp-link repo +// Some code moved to esp-link/pgmshared.c to avoid code duplication. +// Those changes are Copyright (c) 2017 by Danny Backx. + +// Protocol used : https://github.com/Optiboot/optiboot/wiki/HowOptibootWorks + #include #include "cgi.h" #include "cgioptiboot.h" @@ -10,6 +15,8 @@ #include "mqtt_cmd.h" #include "serled.h" +#include "pgmshared.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 @@ -42,33 +49,12 @@ static short ackWait; // counter of expected ACKs static uint16_t optibootVers; 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; // forward function references static void optibootTimerCB(void *); static void optibootUartRecv(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); @@ -175,30 +161,6 @@ int ICACHE_FLASH_ATTR cgiOptibootSync(HttpdConnData *connData) { 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; -} - //===== Cgi to write firmware to Optiboot, requires prior sync call int ICACHE_FLASH_ATTR cgiOptibootData(HttpdConnData *connData) { if (connData->conn==NULL) return HTTPD_CGI_DONE; // Connection aborted. Clean up. @@ -228,6 +190,7 @@ int ICACHE_FLASH_ATTR cgiOptibootData(HttpdConnData *connData) { errorResponse(connData, 400, "Out of memory"); return HTTPD_CGI_DONE; } + optibootData->mega = false; optibootData->pageBuf = pageBuf; optibootData->saved = saved; optibootData->startTime = system_get_time(); @@ -332,80 +295,6 @@ int ICACHE_FLASH_ATTR cgiOptibootData(HttpdConnData *connData) { 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"); - 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"); - if (!programPage()) return false; - } - break; } - case 0x01: // EOF - DBG("OB EOF\n"); - // program any remaining partial page - if (optibootData->pageLen > 0) - if (!programPage()) return false; - optibootData->eof = true; - break; - case 0x04: // address - DBG("OB address 0x%x\n", getHexValue(buf+8, 4) << 16); - // program any remaining partial page - if (optibootData->pageLen > 0) - 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; -} - // Poll UART for ACKs, max 50ms static bool pollAck() { char recv[16]; @@ -426,7 +315,7 @@ static bool pollAck() { } // Program a flash page -static bool ICACHE_FLASH_ATTR programPage(void) { +bool ICACHE_FLASH_ATTR optibootProgramPage(void) { if (optibootData->pageLen == 0) return true; armTimer(PGM_TIMEOUT); // keep the timerCB out of the picture diff --git a/esp-link/main.c b/esp-link/main.c index 314dd72..60f6a19 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/pgmshared.c b/esp-link/pgmshared.c new file mode 100644 index 0000000..44e157a --- /dev/null +++ b/esp-link/pgmshared.c @@ -0,0 +1,149 @@ +// Copyright (c) 2015 by Thorsten von Eicken, see LICENSE.txt in the esp-link repo +// Copyright (c) 2016-2017 by Danny Backx + +#include +#include +#include "cgi.h" +#include "config.h" +#include "uart.h" +#include "stk500v2.h" +#include "serbridge.h" +#include "serled.h" + +#include "pgmshared.h" + +struct optibootData *optibootData; + +char responseBuf[RESP_SZ]; // buffer to accumulate responses from optiboot +short responseLen = 0; // amount accumulated so far +char errMessage[ERR_MAX]; // error message + +// verify that N chars are hex characters +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 +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; +} + +// 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; +} + +// We've not built one function that works on both, but kept the different functions. +// This calls one or the other +static bool ICACHE_FLASH_ATTR programPage() { + if (optibootData->mega) + return megaProgramPage(); + return optibootProgramPage(); +} + +// Process a hex record -- assumes that the records starts with ':' & hex length +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: { // Intel HEX data record + //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"); + //DBG("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"); + DBG("processRecord %d, call programPage() %08x\n", optibootData->pgmSz, optibootData->address + optibootData->segment); + if (!programPage()) return false; + } + break; } + case 0x01: // Intel HEX EOF record + DBG("OB EOF\n"); + // program any remaining partial page +#if 1 + if (optibootData->pageLen > 0) { + // DBG("processRecord remaining partial page, len %d, addr 0x%04x\n", optibootData->pageLen, optibootData->address + optibootData->segment); + 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: // Intel HEX address record + DBG("OB address 0x%x\n", getHexValue(buf+8, 4) << 16); + // program any remaining partial page + if (optibootData->pageLen > 0) { + DBG("processRecord 0x04 remaining partial page, len %d, addr 0x%04x\n", + optibootData->pageLen, optibootData->address + optibootData->segment); + if (!programPage()) return false; + } + optibootData->address = getHexValue(buf+8, 4) << 16; + break; + case 0x05: // Intel HEX start address (MDK-ARM only) + // ignore, there's no way to tell optiboot that... + break; + case 0x02: // Intel HEX extended segment address record + // Depending on the case, just ignoring this record could solve the problem + // optibootData->segment = getHexValue(buf+8, 4) << 4; + DBG("OB segment 0x%08X\n", optibootData->segment); + return true; + default: + // DBG("OB bad record type\n"); + DBG(errMessage, "Invalid/unknown record type: 0x%02x, packet %s", type, buf); + return false; + } + return true; +} + diff --git a/esp-link/pgmshared.h b/esp-link/pgmshared.h new file mode 100644 index 0000000..d69979c --- /dev/null +++ b/esp-link/pgmshared.h @@ -0,0 +1,54 @@ +// Copyright (c) 2015 by Thorsten von Eicken, see LICENSE.txt in the esp-link repo +// Copyright (c) 2017 by Danny Backx + +#ifndef _PGM_SHARED_H_ +#define _PGM_SHARED_H_ + +#include "user_config.h" + +#define RESP_SZ 64 +#define ERR_MAX 128 + +extern char responseBuf[RESP_SZ]; +extern short responseLen; +extern char errMessage[ERR_MAX]; + +// structure used to remember request details from one callback to the next +// allocated dynamically so we don't burn so much static RAM +extern 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 + uint32_t pgmDone; // number of bytes programmed + uint32_t address; // address to write next page to + uint32_t segment; // for extended segment addressing, added to the address field + uint32_t startTime; // time of program POST request + HttpdConnData *conn; // request doing the programming, so we can cancel it + bool eof; // got EOF record + + // Whether to use the Mega (STK500v2) protocol + bool mega; + + // STK500v2 variables + int hardwareVersion, + firmwareVersionMajor, + firmwareVersionMinor, + vTarget; + uint8_t signature[3]; + uint8_t lfuse, hfuse, efuse; +} *optibootData; + +bool ICACHE_FLASH_ATTR checkHex(char *buf, short len); +uint32_t ICACHE_FLASH_ATTR getHexValue(char *buf, short len); +bool ICACHE_FLASH_ATTR processRecord(char *buf, short len); +bool megaProgramPage(void); +bool optibootProgramPage(void); + +#ifdef OPTIBOOT_DBG +#define DBG(format, ...) do { os_printf(format, ## __VA_ARGS__); } while(0) +#else +#define DBG(format, ...) do { } while(0) +#endif + +#endif 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..5358027 --- /dev/null +++ b/megaflash @@ -0,0 +1,114 @@ +#! /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. +# +# Danny Backx wrote the changes for Arduino Mega. +# ---------------------------------------------------------------------------- + +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"` + sync=`curl $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 20 $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 diff --git a/socket/socket.c b/socket/socket.c index 30ebb52..1814cc2 100644 --- a/socket/socket.c +++ b/socket/socket.c @@ -36,6 +36,8 @@ typedef struct { // Instead, we allocate a fixed pool of connections an round-robin. What this means is that the // attached MCU should really use at most as many SOCKET connections as there are slots in the pool. #define MAX_SOCKET 4 +#define MAX_RECEIVE_PACKET_LENGTH 100 + static SocketClient socketClient[MAX_SOCKET]; static uint8_t socketNum = 0xff; // index into socketClient for next slot to allocate @@ -48,12 +50,23 @@ socketclient_recv_cb(void *arg, char *pusrdata, unsigned short length) { uint8_t clientNum = client->conn_num; uint8_t cb_type = USERCB_RECV; DBG_SOCK("SOCKET #%d: Received %d bytes: %s\n", client-socketClient, length, pusrdata); - cmdResponseStart(CMD_RESP_CB, client->resp_cb, 4); - cmdResponseBody(&cb_type, 1); - cmdResponseBody(&clientNum, 1); - cmdResponseBody(&length, 2); - cmdResponseBody(pusrdata, length); - cmdResponseEnd(); + + unsigned short position = 0; + do + { + unsigned short msgLen = length - position; + if( msgLen > MAX_RECEIVE_PACKET_LENGTH ) + msgLen = MAX_RECEIVE_PACKET_LENGTH; + + cmdResponseStart(CMD_RESP_CB, client->resp_cb, 4); + cmdResponseBody(&cb_type, 1); + cmdResponseBody(&clientNum, 1); + cmdResponseBody(&msgLen, 2); + cmdResponseBody(pusrdata + position, msgLen); + cmdResponseEnd(); + + position += msgLen; + }while(position < length ); if (client->sock_mode != SOCKET_TCP_SERVER) { // We don't wait for a response DBG_SOCK("SOCKET #%d: disconnect after receiving\n", client-socketClient);