/* * 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 "serial/uart.h" #include "stk500v2.h" #include "serial/serbridge.h" #include "serial/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; }