| 
					
				 | 
			
			
				@@ -0,0 +1,308 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Based on the OptiBoot project 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// https://github.com/Optiboot/optiboot 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// Licence GLP 2 or later. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "Marlin.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "w25x20cl.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "stk500.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define OPTIBOOT_MAJVER 6 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define OPTIBOOT_CUSTOMVER 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define OPTIBOOT_MINVER 2 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static 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 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#endif 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static 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; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+void optiboot_w25x20cl_enter() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  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; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Initialize the serial line. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    UCSR0A |= (1 << U2X0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    UBRR0L = (((float)(F_CPU))/(((float)(115200))*8.0)-1.0+0.5); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    UCSR0B = (1 << RXEN0) | (1 << TXEN0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Flush the serial line. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    while (RECV_READY) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      watchdogReset(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      // Dummy register read (discard) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      (void)(*(char *)UDR0); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Send the initial magic string. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    while (ptr != end) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      putch(pgm_read_byte_far(ptr ++)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    watchdogReset(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Wait for one second 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 (! RECV_READY) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        watchdogReset(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        delayMicroseconds(1); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (++ boot_timer > boot_timeout) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          // Timeout expired, continue with the application. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      ch = UDR0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (pgm_read_byte_far(ptr ++) != ch) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          // Magic was not received correctly, continue with the application 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      watchdogReset(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // Send the cfm magic string. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ptr = entry_magic_cfm; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    while (ptr != end) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      putch(pgm_read_byte_far(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)) == 0) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          w25x20cl_wait_busy(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          w25x20cl_enable_wr(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          w25x20cl_block64_erase(addr); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          pages_erased |= (1 << addr); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        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; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      uint8_t desttype; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      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. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      desttype = 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); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 |