have updater check HAVE_SPMINTEREFACE and raise error if not supported
[pub/USBaspLoader.git] / firmware / main.c
index 968cf61..c80ab7d 100644 (file)
@@ -1,13 +1,17 @@
 /* Name: main.c
  * Project: USBaspLoader
  * Author: Christian Starkjohann
 /* Name: main.c
  * Project: USBaspLoader
  * Author: Christian Starkjohann
+ * Author: Stephan Baerwolf
  * Creation Date: 2007-12-08
  * Creation Date: 2007-12-08
+ * Modification Date: 2012-11-10
  * Tabsize: 4
  * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH
  * License: GNU GPL v2 (see License.txt)
  * This Revision: $Id: main.c 786 2010-05-30 20:41:40Z cs $
  */
 
  * Tabsize: 4
  * Copyright: (c) 2007 by OBJECTIVE DEVELOPMENT Software GmbH
  * License: GNU GPL v2 (see License.txt)
  * This Revision: $Id: main.c 786 2010-05-30 20:41:40Z cs $
  */
 
+#include "spminterface.h"  /* must be included as first! */
+
 #include <avr/io.h>
 #include <avr/interrupt.h>
 #include <avr/pgmspace.h>
 #include <avr/io.h>
 #include <avr/interrupt.h>
 #include <avr/pgmspace.h>
 #include <avr/boot.h>
 #include <avr/eeprom.h>
 #include <util/delay.h>
 #include <avr/boot.h>
 #include <avr/eeprom.h>
 #include <util/delay.h>
+
+
+#if 0
+/*
+ * 29.09.2012 /  30.09.2012
+ * 
+ * Since cpufunc.h is not needed in this context and
+ * since it is not available in all toolchains, this include
+ * becomes deactivated by github issue-report.
+ * (In case of trouble it remains in sourcecode for reactivation.)
+ * 
+ * The autor would like to thank Lena-M for reporting this
+ * issue (https://github.com/baerwolf/USBaspLoader/issues/1).
+ */
+#include <avr/cpufunc.h>
+#endif
+
+#include <avr/boot.h>
+
 #include <string.h>
 
 #include <string.h>
 
+
+
 static void leaveBootloader() __attribute__((__noreturn__));
 
 #include "bootloaderconfig.h"
 #include "usbdrv/usbdrv.c"
 
 static void leaveBootloader() __attribute__((__noreturn__));
 
 #include "bootloaderconfig.h"
 #include "usbdrv/usbdrv.c"
 
+#ifndef BOOTLOADER_ADDRESS
+  #error need to know the bootloaders flash address!
+#endif
+
 /* ------------------------------------------------------------------------ */
 
 /* Request constants used by USBasp */
 /* ------------------------------------------------------------------------ */
 
 /* Request constants used by USBasp */
@@ -35,6 +64,15 @@ static void leaveBootloader() __attribute__((__noreturn__));
 #define USBASP_FUNC_WRITEEEPROM     8
 #define USBASP_FUNC_SETLONGADDRESS  9
 
 #define USBASP_FUNC_WRITEEEPROM     8
 #define USBASP_FUNC_SETLONGADDRESS  9
 
+// additional USBasp Commands
+#define USBASP_FUNC_SETISPSCK       10
+#define USBASP_FUNC_TPI_CONNECT      11
+#define USBASP_FUNC_TPI_DISCONNECT   12
+#define USBASP_FUNC_TPI_RAWREAD      13
+#define USBASP_FUNC_TPI_RAWWRITE     14
+#define USBASP_FUNC_TPI_READBLOCK    15
+#define USBASP_FUNC_TPI_WRITEBLOCK   16
+#define USBASP_FUNC_GETCAPABILITIES 127
 /* ------------------------------------------------------------------------ */
 
 #ifndef ulong
 /* ------------------------------------------------------------------------ */
 
 #ifndef ulong
@@ -44,16 +82,6 @@ static void leaveBootloader() __attribute__((__noreturn__));
 #   define uint     unsigned int
 #endif
 
 #   define uint     unsigned int
 #endif
 
-/* defaults if not in config file: */
-#ifndef HAVE_EEPROM_PAGED_ACCESS
-#   define HAVE_EEPROM_PAGED_ACCESS 0
-#endif
-#ifndef HAVE_EEPROM_BYTE_ACCESS
-#   define HAVE_EEPROM_BYTE_ACCESS  0
-#endif
-#ifndef BOOTLOADER_CAN_EXIT
-#   define  BOOTLOADER_CAN_EXIT     0
-#endif
 
 /* allow compatibility with avrusbboot's bootloaderconfig.h: */
 #ifdef BOOTLOADER_INIT
 
 /* allow compatibility with avrusbboot's bootloaderconfig.h: */
 #ifdef BOOTLOADER_INIT
@@ -85,29 +113,63 @@ typedef union longConverter{
     uchar   b[sizeof(addr_t)];
 }longConverter_t;
 
     uchar   b[sizeof(addr_t)];
 }longConverter_t;
 
-static uchar            requestBootLoaderExit;
-static longConverter_t  currentAddress; /* in bytes */
-static uchar            bytesRemaining;
-static uchar            isLastPage;
+
+#if BOOTLOADER_CAN_EXIT
+static volatile unsigned char  stayinloader = 0xfe;
+#endif
+
+static longConverter_t         currentAddress; /* in bytes */
+static uchar                   bytesRemaining;
+static uchar                   isLastPage;
 #if HAVE_EEPROM_PAGED_ACCESS
 #if HAVE_EEPROM_PAGED_ACCESS
-static uchar            currentRequest;
+static uchar                   currentRequest;
 #else
 #else
-static const uchar      currentRequest = 0;
+static const uchar             currentRequest = 0;
 #endif
 
 static const uchar  signatureBytes[4] = {
 #ifdef SIGNATURE_BYTES
     SIGNATURE_BYTES
 #endif
 
 static const uchar  signatureBytes[4] = {
 #ifdef SIGNATURE_BYTES
     SIGNATURE_BYTES
-#elif defined (__AVR_ATmega8__) || defined (__AVR_ATmega8HVA__)
+#elif defined (__AVR_ATmega8__) || defined (__AVR_ATmega8A__) || defined (__AVR_ATmega8HVA__)
     0x1e, 0x93, 0x07, 0
     0x1e, 0x93, 0x07, 0
-#elif defined (__AVR_ATmega48__) || defined (__AVR_ATmega48P__)
+#elif defined (__AVR_ATmega32__)
+    0x1e, 0x95, 0x02, 0
+#elif defined (__AVR_ATmega48__) || defined (__AVR_ATmega48A__) || defined (__AVR_ATmega48P__)
+    #error ATmega48 does not support bootloaders!
     0x1e, 0x92, 0x05, 0
     0x1e, 0x92, 0x05, 0
-#elif defined (__AVR_ATmega88__) || defined (__AVR_ATmega88P__)
+#elif defined (__AVR_ATmega48PA__)
+    #error ATmega48 does not support bootloaders!
+    0x1e, 0x92, 0x0A, 0
+#elif defined (__AVR_ATmega88__) || defined (__AVR_ATmega88A__) || defined (__AVR_ATmega88P__)
     0x1e, 0x93, 0x0a, 0
     0x1e, 0x93, 0x0a, 0
-#elif defined (__AVR_ATmega168__) || defined (__AVR_ATmega168P__)
+#elif defined (__AVR_ATmega88PA__)
+    0x1e, 0x93, 0x0F, 0
+#elif defined (__AVR_ATmega164A__)
+    0x1e, 0x94, 0x0f, 0
+#elif defined (__AVR_ATmega164P__)
+    0x1e, 0x94, 0x0a, 0
+#elif defined (__AVR_ATmega168__) || defined (__AVR_ATmega168A__) || defined (__AVR_ATmega168P__)
     0x1e, 0x94, 0x06, 0
     0x1e, 0x94, 0x06, 0
+#elif defined (__AVR_ATmega168PA__)
+    0x1e, 0x94, 0x0B, 0
+#elif defined (__AVR_ATmega324A__)
+    0x1e, 0x95, 0x15, 0
+#elif defined (__AVR_ATmega324P__)
+    0x1e, 0x95, 0x08, 0
+#elif defined (__AVR_ATmega328__)
+    0x1e, 0x95, 0x14, 0
 #elif defined (__AVR_ATmega328P__)
     0x1e, 0x95, 0x0f, 0
 #elif defined (__AVR_ATmega328P__)
     0x1e, 0x95, 0x0f, 0
+#elif defined (__AVR_ATmega644__) || defined (__AVR_ATmega644A__)
+    0x1e, 0x96, 0x09, 0
+#elif defined (__AVR_ATmega644P__) || defined (__AVR_ATmega644PA__)
+    0x1e, 0x96, 0x0a, 0
+#elif defined (__AVR_ATmega128__)
+    0x1e, 0x97, 0x02, 0
+#elif defined (__AVR_ATmega1284__)
+    0x1e, 0x97, 0x06, 0
+#elif defined (__AVR_ATmega1284P__)
+    0x1e, 0x97, 0x05, 0
 #else
 #   error "Device signature is not known, please edit main.c!"
 #endif
 #else
 #   error "Device signature is not known, please edit main.c!"
 #endif
@@ -120,12 +182,14 @@ static void (*nullVector)(void) __attribute__((__noreturn__));
 static void leaveBootloader()
 {
     DBG1(0x01, 0, 0);
 static void leaveBootloader()
 {
     DBG1(0x01, 0, 0);
-    bootLoaderExit();
     cli();
     cli();
+    usbDeviceDisconnect();
+    bootLoaderExit();
     USB_INTR_ENABLE = 0;
     USB_INTR_CFG = 0;       /* also reset config bits */
     GICR = (1 << IVCE);     /* enable change of interrupt vectors */
     GICR = (0 << IVSEL);    /* move interrupts to application flash section */
     USB_INTR_ENABLE = 0;
     USB_INTR_CFG = 0;       /* also reset config bits */
     GICR = (1 << IVCE);     /* enable change of interrupt vectors */
     GICR = (0 << IVSEL);    /* move interrupts to application flash section */
+    
 /* We must go through a global function pointer variable instead of writing
  *  ((void (*)(void))0)();
  * because the compiler optimizes a constant 0 to "rcall 0" which is not
 /* We must go through a global function pointer variable instead of writing
  *  ((void (*)(void))0)();
  * because the compiler optimizes a constant 0 to "rcall 0" which is not
@@ -151,6 +215,36 @@ static uchar    replyBuffer[4];
         if(rq->wValue.bytes[0] == 0x30){        /* read signature */
             rval = rq->wIndex.bytes[0] & 3;
             rval = signatureBytes[rval];
         if(rq->wValue.bytes[0] == 0x30){        /* read signature */
             rval = rq->wIndex.bytes[0] & 3;
             rval = signatureBytes[rval];
+#if HAVE_READ_LOCK_FUSE
+#if defined (__AVR_ATmega8__) || defined (__AVR_ATmega8A__) || defined (__AVR_ATmega32__)
+        }else if(rq->wValue.bytes[0] == 0x58 && rq->wValue.bytes[1] == 0x00){  /* read lock bits */
+            rval = boot_lock_fuse_bits_get(GET_LOCK_BITS);
+        }else if(rq->wValue.bytes[0] == 0x50 && rq->wValue.bytes[1] == 0x00){  /* read lfuse bits */
+            rval = boot_lock_fuse_bits_get(GET_LOW_FUSE_BITS);
+        }else if(rq->wValue.bytes[0] == 0x58 && rq->wValue.bytes[1] == 0x08){  /* read hfuse bits */
+            rval = boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS);
+
+#elif defined (__AVR_ATmega48__)   || defined (__AVR_ATmega48A__)   || defined (__AVR_ATmega48P__)   || defined (__AVR_ATmega48PA__)  ||  \
+      defined (__AVR_ATmega88__)   || defined (__AVR_ATmega88A__)   || defined (__AVR_ATmega88P__)   || defined (__AVR_ATmega88PA__)  ||  \
+      defined (__AVR_ATmega164A__) || defined (__AVR_ATmega164P__)  ||                                                                       \
+      defined (__AVR_ATmega168__)  || defined (__AVR_ATmega168A__)  || defined (__AVR_ATmega168P__)  || defined (__AVR_ATmega168PA__) ||  \
+      defined (__AVR_ATmega324A__) || defined (__AVR_ATmega324P__)  ||                                                               \
+      defined (__AVR_ATmega328__)  || defined (__AVR_ATmega328P__)  ||                                                               \
+      defined (__AVR_ATmega644__)  || defined (__AVR_ATmega644A__)  || defined (__AVR_ATmega644P__) || defined (__AVR_ATmega644PA__)  ||  \
+      defined (__AVR_ATmega128__)  ||                                                                                                        \
+      defined (__AVR_ATmega1284__) || defined (__AVR_ATmega1284P__)
+        }else if(rq->wValue.bytes[0] == 0x58 && rq->wValue.bytes[1] == 0x00){  /* read lock bits */
+            rval = boot_lock_fuse_bits_get(GET_LOCK_BITS);
+        }else if(rq->wValue.bytes[0] == 0x50 && rq->wValue.bytes[1] == 0x00){  /* read lfuse bits */
+            rval = boot_lock_fuse_bits_get(GET_LOW_FUSE_BITS);
+        }else if(rq->wValue.bytes[0] == 0x58 && rq->wValue.bytes[1] == 0x08){  /* read hfuse bits */
+            rval = boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS);
+        }else if(rq->wValue.bytes[0] == 0x50 && rq->wValue.bytes[1] == 0x08){  /* read efuse bits */
+            rval = boot_lock_fuse_bits_get(GET_EXTENDED_FUSE_BITS );
+#else
+       #warning "HAVE_READ_LOCK_FUSE is activated but MCU unknown -> will not support this feature"
+#endif
+#endif
 #if HAVE_EEPROM_BYTE_ACCESS
         }else if(rq->wValue.bytes[0] == 0xa0){  /* read EEPROM byte */
             rval = eeprom_read_byte((void *)address.word);
 #if HAVE_EEPROM_BYTE_ACCESS
         }else if(rq->wValue.bytes[0] == 0xa0){  /* read EEPROM byte */
             rval = eeprom_read_byte((void *)address.word);
@@ -176,7 +270,7 @@ static uchar    replyBuffer[4];
         }
         replyBuffer[3] = rval;
         len = 4;
         }
         replyBuffer[3] = rval;
         len = 4;
-    }else if(rq->bRequest == USBASP_FUNC_ENABLEPROG){
+    }else if((rq->bRequest == USBASP_FUNC_ENABLEPROG) || (rq->bRequest == USBASP_FUNC_SETISPSCK)){
         /* replyBuffer[0] = 0; is never touched and thus always 0 which means success */
         len = 1;
     }else if(rq->bRequest >= USBASP_FUNC_READFLASH && rq->bRequest <= USBASP_FUNC_SETLONGADDRESS){
         /* replyBuffer[0] = 0; is never touched and thus always 0 which means success */
         len = 1;
     }else if(rq->bRequest >= USBASP_FUNC_READFLASH && rq->bRequest <= USBASP_FUNC_SETLONGADDRESS){
@@ -194,12 +288,17 @@ static uchar    replyBuffer[4];
 #endif
             len = 0xff; /* hand over to usbFunctionRead() / usbFunctionWrite() */
         }
 #endif
             len = 0xff; /* hand over to usbFunctionRead() / usbFunctionWrite() */
         }
-#if BOOTLOADER_CAN_EXIT
+
     }else if(rq->bRequest == USBASP_FUNC_DISCONNECT){
     }else if(rq->bRequest == USBASP_FUNC_DISCONNECT){
-        requestBootLoaderExit = 1;      /* allow proper shutdown/close of connection */
+
+#if BOOTLOADER_CAN_EXIT
+      stayinloader        &= (0xfe);
 #endif
     }else{
 #endif
     }else{
-        /* ignore: USBASP_FUNC_CONNECT */
+        /* ignore: others, but could be USBASP_FUNC_CONNECT */
+#if BOOTLOADER_CAN_EXIT
+       stayinloader       |= (0x01);
+#endif
     }
     return len;
 }
     }
     return len;
 }
@@ -221,16 +320,10 @@ uchar   isLast;
     }else{
         uchar i;
         for(i = 0; i < len;){
     }else{
         uchar i;
         for(i = 0; i < len;){
-#if !HAVE_CHIP_ERASE
-            if((currentAddress.w[0] & (SPM_PAGESIZE - 1)) == 0){    /* if page start: erase */
-                DBG1(0x33, 0, 0);
-#   ifndef NO_FLASH_WRITE
-                cli();
-                boot_page_erase(CURRENT_ADDRESS);   /* erase page */
-                sei();
-                boot_spm_busy_wait();               /* wait until page is erased */
-#   endif
-            }
+#if HAVE_BLB11_SOFTW_LOCKBIT
+           if (CURRENT_ADDRESS >= (addr_t)(BOOTLOADER_ADDRESS)) {
+             return 1;
+           }
 #endif
             i += 2;
             DBG1(0x32, 0, 0);
 #endif
             i += 2;
             DBG1(0x32, 0, 0);
@@ -241,6 +334,15 @@ uchar   isLast;
             data += 2;
             /* write page when we cross page boundary or we have the last partial page */
             if((currentAddress.w[0] & (SPM_PAGESIZE - 1)) == 0 || (isLast && i >= len && isLastPage)){
             data += 2;
             /* write page when we cross page boundary or we have the last partial page */
             if((currentAddress.w[0] & (SPM_PAGESIZE - 1)) == 0 || (isLast && i >= len && isLastPage)){
+#if !HAVE_CHIP_ERASE
+                DBG1(0x33, 0, 0);
+#   ifndef NO_FLASH_WRITE
+                cli();
+                boot_page_erase(CURRENT_ADDRESS - 2);   /* erase page */
+                sei();
+                boot_spm_busy_wait();                   /* wait until page is erased */
+#   endif
+#endif
                 DBG1(0x34, 0, 0);
 #ifndef NO_FLASH_WRITE
                 cli();
                 DBG1(0x34, 0, 0);
 #ifndef NO_FLASH_WRITE
                 cli();
@@ -287,7 +389,6 @@ uchar   i = 0;
     /* enforce USB re-enumerate: */
     usbDeviceDisconnect();  /* do this while interrupts are disabled */
     while(--i){         /* fake USB disconnect for > 250 ms */
     /* enforce USB re-enumerate: */
     usbDeviceDisconnect();  /* do this while interrupts are disabled */
     while(--i){         /* fake USB disconnect for > 250 ms */
-        wdt_reset();
         _delay_ms(1);
     }
     usbDeviceConnect();
         _delay_ms(1);
     }
     usbDeviceConnect();
@@ -297,7 +398,6 @@ uchar   i = 0;
 int __attribute__((noreturn)) main(void)
 {
     /* initialize  */
 int __attribute__((noreturn)) main(void)
 {
     /* initialize  */
-    wdt_disable();      /* main app may have enabled watchdog */
     bootLoaderInit();
     odDebugInit();
     DBG1(0x00, 0, 0);
     bootLoaderInit();
     odDebugInit();
     DBG1(0x00, 0, 0);
@@ -306,19 +406,29 @@ int __attribute__((noreturn)) main(void)
     GICR = (1 << IVSEL); /* move interrupts to boot flash section */
 #endif
     if(bootLoaderCondition()){
     GICR = (1 << IVSEL); /* move interrupts to boot flash section */
 #endif
     if(bootLoaderCondition()){
-        uchar i = 0, j = 0;
+#if NEED_WATCHDOG
+       wdt_disable();    /* main app may have enabled watchdog */
+#endif
         initForUsbConnectivity();
         do{
             usbPoll();
 #if BOOTLOADER_CAN_EXIT
         initForUsbConnectivity();
         do{
             usbPoll();
 #if BOOTLOADER_CAN_EXIT
-            if(requestBootLoaderExit){
-                if(--i == 0){
-                    if(--j == 0)
-                        break;
-                }
-            }
+       if (stayinloader >= 0x10) {
+         if (!bootLoaderCondition()) {
+           stayinloader-=0x10;
+         } 
+       } else {
+         if (bootLoaderCondition()) {
+           if (stayinloader > 1) stayinloader-=2;
+         }
+       }
+#endif
+
+#if BOOTLOADER_CAN_EXIT
+        }while (stayinloader); /* main event loop, if BOOTLOADER_CAN_EXIT*/
+#else
+        }while (1);            /* main event loop */
 #endif
 #endif
-        }while(bootLoaderCondition());  /* main event loop */
     }
     leaveBootloader();
 }
     }
     leaveBootloader();
 }