| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 | //! @file// Based on the OptiBoot project// https://github.com/Optiboot/optiboot// Licence GLP 2 or later.#include "Marlin.h"#include "w25x20cl.h"#include "stk500.h"#include "bootapp.h"#define OPTIBOOT_MAJVER 6#define OPTIBOOT_CUSTOMVER 0#define OPTIBOOT_MINVER 2static unsigned const int __attribute__((section(".version")))   optiboot_version = 256*(OPTIBOOT_MAJVER + OPTIBOOT_CUSTOMVER) + OPTIBOOT_MINVER;/* Watchdog settings */#define WATCHDOG_OFF    (0)#define WATCHDOG_16MS   (_BV(WDE))#define WATCHDOG_32MS   (_BV(WDP0) | _BV(WDE))#define WATCHDOG_64MS   (_BV(WDP1) | _BV(WDE))#define WATCHDOG_125MS  (_BV(WDP1) | _BV(WDP0) | _BV(WDE))#define WATCHDOG_250MS  (_BV(WDP2) | _BV(WDE))#define WATCHDOG_500MS  (_BV(WDP2) | _BV(WDP0) | _BV(WDE))#define WATCHDOG_1S     (_BV(WDP2) | _BV(WDP1) | _BV(WDE))#define WATCHDOG_2S     (_BV(WDP2) | _BV(WDP1) | _BV(WDP0) | _BV(WDE))#define WATCHDOG_4S     (_BV(WDP3) | _BV(WDE))#define WATCHDOG_8S     (_BV(WDP3) | _BV(WDP0) | _BV(WDE))#if 0#define W25X20CL_SIGNATURE_0 9#define W25X20CL_SIGNATURE_1 8#define W25X20CL_SIGNATURE_2 7#else//FIXME this is a signature of ATmega2560!#define W25X20CL_SIGNATURE_0 0x1E#define W25X20CL_SIGNATURE_1 0x98#define W25X20CL_SIGNATURE_2 0x01#endifstatic void watchdogConfig(uint8_t x) {  WDTCSR = _BV(WDCE) | _BV(WDE);  WDTCSR = x;}static void watchdogReset() {  __asm__ __volatile__ (    "wdr\n"  );}#define RECV_READY ((UCSR0A & _BV(RXC0)) != 0)static uint8_t getch(void) {  uint8_t ch;  while(! RECV_READY) ;  if (!(UCSR0A & _BV(FE0))) {      /*       * A Framing Error indicates (probably) that something is talking       * to us at the wrong bit rate.  Assume that this is because it       * expects to be talking to the application, and DON'T reset the       * watchdog.  This should cause the bootloader to abort and run       * the application "soon", if it keeps happening.  (Note that we       * don't care that an invalid char is returned...)       */    watchdogReset();  }  ch = UDR0;  return ch;}static void putch(char ch) {  while (!(UCSR0A & _BV(UDRE0)));  UDR0 = ch;}static void verifySpace() {  if (getch() != CRC_EOP) {    putch(STK_FAILED);    watchdogConfig(WATCHDOG_16MS);    // shorten WD timeout    while (1)           // and busy-loop so that WD causes      ;             //  a reset and app start.  }  putch(STK_INSYNC);}static void getNch(uint8_t count) {  do getch(); while (--count);  verifySpace();}typedef uint16_t pagelen_t;static const char entry_magic_send   [] PROGMEM = "start\n";static const char entry_magic_receive[] PROGMEM = "w25x20cl_enter\n";static const char entry_magic_cfm    [] PROGMEM = "w25x20cl_cfm\n";struct block_t;extern struct block_t *block_buffer;//! @brief Enter an STK500 compatible Optiboot boot loader waiting for flashing the languages to an external flash memory.//! @return 1 if "start\n" was not sent. Optiboot was skipped//! @return 0 if "start\n" was sent. Optiboot ran normally. No need to send "start\n" in setup()uint8_t optiboot_w25x20cl_enter(){  if (boot_app_flags & BOOT_APP_FLG_USER0) return 1;  uint8_t ch;  uint8_t rampz = 0;  register uint16_t address = 0;  register pagelen_t length;  // Use the planner's queue for the receive / transmit buffers.//  uint8_t *buff = (uint8_t*)block_buffer;  uint8_t buff[260];  // bitmap of pages to be written. Bit is set to 1 if the page has already been erased.  uint8_t pages_erased = 0;  // Handshake sequence: Initialize the serial line, flush serial line, send magic, receive magic.  // If the magic is not received on time, or it is not received correctly, continue to the application.  {    watchdogReset();    unsigned long  boot_timeout = 2000000;    unsigned long  boot_timer = 0;    const char    *ptr = entry_magic_send;    const char    *end = strlen_P(entry_magic_send) + ptr;    const uint8_t selectedSerialPort_bak = selectedSerialPort;    // Flush the serial line.    while (RECV_READY) {      watchdogReset();      // Dummy register read (discard)      (void)(*(char *)UDR0);    }    selectedSerialPort = 0; //switch to Serial0    MYSERIAL.flush(); //clear RX buffer    int SerialHead = rx_buffer.head;    // Send the initial magic string.    while (ptr != end)      putch(pgm_read_byte(ptr ++));    watchdogReset();    // Wait for two seconds until a magic string (constant entry_magic) is received    // from the serial line.    ptr = entry_magic_receive;    end = strlen_P(entry_magic_receive) + ptr;    while (ptr != end) {      while (rx_buffer.head == SerialHead) {        watchdogReset();        delayMicroseconds(1);        if (++ boot_timer > boot_timeout)        {          // Timeout expired, continue with the application.          selectedSerialPort = selectedSerialPort_bak; //revert Serial setting          return 0;        }      }      ch = rx_buffer.buffer[SerialHead];      SerialHead = (unsigned int)(SerialHead + 1) % RX_BUFFER_SIZE;      if (pgm_read_byte(ptr ++) != ch)      {          // Magic was not received correctly, continue with the application          selectedSerialPort = selectedSerialPort_bak; //revert Serial setting          return 0;      }      watchdogReset();    }    cbi(UCSR0B, RXCIE0); //disable the MarlinSerial0 interrupt    // Send the cfm magic string.    ptr = entry_magic_cfm;    while (ptr != end)      putch(pgm_read_byte(ptr ++));  }  spi_init();  w25x20cl_init();  watchdogConfig(WATCHDOG_OFF);  /* Forever loop: exits by causing WDT reset */  for (;;) {    /* get character from UART */    ch = getch();    if(ch == STK_GET_PARAMETER) {      unsigned char which = getch();      verifySpace();      /*       * Send optiboot version as "SW version"       * Note that the references to memory are optimized away.       */      if (which == STK_SW_MINOR) {        putch(optiboot_version & 0xFF);      } else if (which == STK_SW_MAJOR) {        putch(optiboot_version >> 8);      } else {        /*         * GET PARAMETER returns a generic 0x03 reply for               * other parameters - enough to keep Avrdude happy         */        putch(0x03);      }    }    else if(ch == STK_SET_DEVICE) {      // SET DEVICE is ignored      getNch(20);    }    else if(ch == STK_SET_DEVICE_EXT) {      // SET DEVICE EXT is ignored      getNch(5);    }    else if(ch == STK_LOAD_ADDRESS) {      // LOAD ADDRESS      uint16_t newAddress;      // Workaround for the infamous ';' bug in the Prusa3D usb to serial converter.      // Send the binary data by nibbles to avoid transmitting the ';' character.      newAddress  = getch();      newAddress |= getch();      newAddress |= (((uint16_t)getch()) << 8);      newAddress |= (((uint16_t)getch()) << 8);      // Transfer top bit to LSB in rampz      if (newAddress & 0x8000)        rampz |= 0x01;      else        rampz &= 0xFE;      newAddress += newAddress; // Convert from word address to byte address      address = newAddress;      verifySpace();    }    else if(ch == STK_UNIVERSAL) {      // LOAD_EXTENDED_ADDRESS is needed in STK_UNIVERSAL for addressing more than 128kB      if ( AVR_OP_LOAD_EXT_ADDR == getch() ) {        // get address        getch();  // get '0'        rampz = (rampz & 0x01) | ((getch() << 1) & 0xff);  // get address and put it in rampz        getNch(1); // get last '0'        // response        putch(0x00);      }      else {        // everything else is ignored        getNch(3);        putch(0x00);      }    }    /* Write memory, length is big endian and is in bytes */    else if(ch == STK_PROG_PAGE) {      // PROGRAM PAGE - we support flash programming only, not EEPROM      uint8_t desttype;      uint8_t *bufPtr;      pagelen_t savelength;      // Read the page length, with the length transferred each nibble separately to work around      // the Prusa's USB to serial infamous semicolon issue.      length  = ((pagelen_t)getch()) << 8;      length |= ((pagelen_t)getch()) << 8;      length |= getch();      length |= getch();      savelength = length;      // Read the destination type. It should always be 'F' as flash.      desttype = getch();      // read a page worth of contents      bufPtr = buff;      do *bufPtr++ = getch();      while (--length);      // Read command terminator, start reply      verifySpace();      if (desttype == 'E') {        while (1) ; // Error: wait for WDT      } else {        uint32_t addr = (((uint32_t)rampz) << 16) | address;        // During a single bootloader run, only erase a 64kB block once.        // An 8bit bitmask 'pages_erased' covers 512kB of FLASH memory.        if ((address == 0) && (pages_erased & (1 << (addr >> 16))) == 0) {          w25x20cl_wait_busy();          w25x20cl_enable_wr();          w25x20cl_block64_erase(addr);          pages_erased |= (1 << (addr >> 16));        }        w25x20cl_wait_busy();        w25x20cl_enable_wr();        w25x20cl_page_program(addr, buff, savelength);        w25x20cl_wait_busy();        w25x20cl_disable_wr();      }    }    /* Read memory block mode, length is big endian.  */    else if(ch == STK_READ_PAGE) {      uint32_t addr = (((uint32_t)rampz) << 16) | address;      register pagelen_t i;      // Read the page length, with the length transferred each nibble separately to work around      // the Prusa's USB to serial infamous semicolon issue.      length  = ((pagelen_t)getch()) << 8;      length |= ((pagelen_t)getch()) << 8;      length |= getch();      length |= getch();      // Read the destination type. It should always be 'F' as flash. It is not checked.      (void)getch();      verifySpace();      w25x20cl_wait_busy();      w25x20cl_rd_data(addr, buff, length);      for (i = 0; i < length; ++ i)        putch(buff[i]);    }    /* Get device signature bytes  */    else if(ch == STK_READ_SIGN) {      // READ SIGN - return what Avrdude wants to hear      verifySpace();      putch(W25X20CL_SIGNATURE_0);      putch(W25X20CL_SIGNATURE_1);      putch(W25X20CL_SIGNATURE_2);    }    else if (ch == STK_LEAVE_PROGMODE) { /* 'Q' */      // Adaboot no-wait mod      watchdogConfig(WATCHDOG_16MS);      verifySpace();    }    else {      // This covers the response to commands like STK_ENTER_PROGMODE      verifySpace();    }    putch(STK_OK);  }}
 |