Disable yellow LED driver for Pro-Micro
[pub/lufa.git] / Bootloaders / DFU / BootloaderDFU.c
index cebc0f4..acd0296 100644 (file)
@@ -1,13 +1,13 @@
 /*
              LUFA Library
-     Copyright (C) Dean Camera, 2014.
+     Copyright (C) Dean Camera, 2021.
 
   dean [at] fourwalledcubicle [dot] com
            www.lufa-lib.org
 */
 
 /*
-  Copyright 2014  Dean Camera (dean [at] fourwalledcubicle [dot] com)
+  Copyright 2021  Dean Camera (dean [at] fourwalledcubicle [dot] com)
 
   Permission to use, copy, modify, distribute, and sell this
   software and its documentation for any purpose is hereby granted
@@ -45,8 +45,9 @@ static bool IsSecure = SECURE_MODE;
 /** Flag to indicate if the bootloader should be running, or should exit and allow the application code to run
  *  via a soft reset. When cleared, the bootloader will abort, the USB interface will shut down and the application
  *  jumped to via an indirect jump to location 0x0000 (or other location specified by the host).
+ *  Use volatile to prevent compiler to do optimization, because we change this variable from interrupt context.
  */
-static bool RunBootloader = true;
+static volatile bool RunBootloader = true;
 
 /** Flag to indicate if the bootloader is waiting to exit. When the host requests the bootloader to exit and
  *  jump to the application address it specifies, it sends two sequential commands which must be properly
@@ -55,6 +56,21 @@ static bool RunBootloader = true;
  */
 static bool WaitForExit = false;
 
+/** Minimum time in seconds stay forced into bootloader mode before application will be started. This especially
+ *  helpful if the application does not support any update or reflashing or jumping back into bootloader. It prevents
+ *  to brick the device if now ISP programmer is available.
+ */
+#ifndef BL_TIME
+#define BL_TIME 5 /* seconds */
+#endif
+
+/** Minimum time ticks stay in bootloader before application is started or negative value to prevent leaving
+ *  bootloader. Timer 1 is a 16 bit timer, which overflows after 65536 cycles if it is load with zero. The
+ *  timer 1 prescaler is programmed to divide by 64 and the prescaler engine uses a clock_div_1 divisor, means
+ *  no divisor of the CPU clock frequency F_CPU.
+ */
+static int8_t ForceBootloaderTime = ((BL_TIME) > 0) ? (F_CPU / 64 * (BL_TIME) / 65536 + 1) : -1;
+
 /** Current DFU state machine state, one of the values in the DFU_State_t enum. */
 static uint8_t DFU_State = dfuIDLE;
 
@@ -131,18 +147,67 @@ void Application_Jump_Check(void)
 
                /* Re-enable JTAG debugging */
                JTAG_ENABLE();
+       #elif ((BOARD == BOARD_PROMICRO) || (BOARD == BOARD_MICRO))
+               /* Pro-Micro and Arduino Micro board use power-on reset, but no external reset. Both boards have
+                * the hardware bootloader pin HWBE enabled. Unfortunately only the external reset allows together
+                * with an enabled HWBE that the CPU start at the bootloader address independent of the FUSE_BOOTRST.
+                * That means the power-on reset will start just controlled by the FUSE_BOOTRST the bootloader or
+                * direct in the application and cannot be overridden by HWBE signal. Therfore FUSE_BOOTRST shall
+                * be enabled, otherwise the bootloader will not be reached for these boards.
+                * The bootloader checks FUSE_HWBE as *unprogammed* instead of FUSE_BOOTRST as programmed on other
+                * board variants to decide fast application start, without waiting the dedicted bootloader timeout
+                * in case of a USB, watchdog, brown-out or JTAG reset. If the watchdog reset was initiated from
+                * the bootloader marked with the MAGIC_BOOT_KEY this reset flag is reset. All other reset flags
+                * are left untouched to allow the application code checking the reset signals, especially in case
+                * of application fast start.
+                * The bootloader is entered always for external reset and power-on reset. But the bootloader is
+                * anyway exited after that dedicted timeout, if a reset-vector to the application is programmed.
+                * Once a DFU program interacts this the bootloader during this dedicted timeout, the timer stops
+                * and the application needs to be started by DFU bootloader command manually or using a reset.
+                */
+
+               /* Check if the device's forced Bootloader via Hardware Bootenable is unprogrammed */
+               if (BootloaderAPI_ReadFuse(GET_EXTENDED_FUSE_BITS) & ~FUSE_HWBE)
+               {
+                       /* If the reset source was not an external or power-on reset jump to the application */
+                       if (!(MCUSR & ((1 << EXTRF) || (1 << PORF))))
+                         JumpToApplication = true;
+               }
+               /* If the reset source was the bootloader and the key is correct, clear it and jump to the application;
+                * this can happen in the HWBE fuse is set, and the HBE pin is low during the watchdog reset */
+               if ((MCUSR & (1 << WDRF)) && (MagicBootKey == MAGIC_BOOT_KEY))
+               {
+                       JumpToApplication = true;
+
+                       /* Clear reset source */
+                       MCUSR &= ~(1 << WDRF);
+               }
        #else
                /* Check if the device's BOOTRST fuse is set */
-               if (boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS) & FUSE_BOOTRST)
+               if (!(BootloaderAPI_ReadFuse(GET_HIGH_FUSE_BITS) & ~FUSE_BOOTRST))
                {
                        /* If the reset source was not an external reset or the key is correct, clear it and jump to the application */
                        if (!(MCUSR & (1 << EXTRF)) || (MagicBootKey == MAGIC_BOOT_KEY))
                          JumpToApplication = true;
 
+                       /* Clear reset source */
                        MCUSR &= ~(1 << EXTRF);
                }
+               else
+               {
+                       /* If the reset source was the bootloader and the key is correct, clear it and jump to the application;
+                        * this can happen in the HWBE fuse is set, and the HBE pin is low during the watchdog reset */
+                       if ((MCUSR & (1 << WDRF)) && (MagicBootKey == MAGIC_BOOT_KEY))
+                               JumpToApplication = true;
+
+                       /* Clear reset source */
+                       MCUSR &= ~(1 << WDRF);
+               }
        #endif
 
+       /* Clear the boot key in any case */
+       MagicBootKey = 0;
+
        /* Don't run the user application if the reset vector is blank (no app loaded) */
        bool ApplicationValid = (pgm_read_word_near(0) != 0xFFFF);
 
@@ -150,12 +215,8 @@ void Application_Jump_Check(void)
        if (JumpToApplication && ApplicationValid)
        {
                /* Turn off the watchdog */
-               MCUSR &= ~(1 << WDRF);
                wdt_disable();
 
-               /* Clear the boot key and jump to the user application */
-               MagicBootKey = 0;
-
                // cppcheck-suppress constStatement
                ((void (*)(void))0x0000)();
        }
@@ -180,6 +241,9 @@ int main(void)
        while (RunBootloader || WaitForExit)
          USB_USBTask();
 
+       /* Wait a short time to end all USB transactions and then disconnect */
+       _delay_us(1000);
+
        /* Reset configured hardware back to their original states for the user application */
        ResetHardware();
 
@@ -207,6 +271,7 @@ static void SetupHardware(void)
 
        /* Bootloader active LED toggle timer initialization */
        TIMSK1 = (1 << TOIE1);
+       /* config timer 1 prescaler to F_CPU / clock_div_1 / 64 */
        TCCR1B = ((1 << CS11) | (1 << CS10));
 }
 
@@ -230,6 +295,12 @@ static void ResetHardware(void)
 ISR(TIMER1_OVF_vect, ISR_BLOCK)
 {
        LEDs_ToggleLEDs(LEDS_LED1 | LEDS_LED2);
+       /* Count number for forced ticks not below zero */
+       if (ForceBootloaderTime > 0)
+         ForceBootloaderTime--;
+       /* check if it is time to leave the bootloader and a valid application exists */
+       if ((ForceBootloaderTime == 0) && (pgm_read_word_near(0) != 0xFFFF))
+         RunBootloader = false;
 }
 
 /** Event handler for the USB_ControlRequest event. This is used to catch and process control requests sent to
@@ -245,6 +316,9 @@ void EVENT_USB_Device_ControlRequest(void)
                return;
        }
 
+       /* prevent counter to reach zero */
+       ForceBootloaderTime = -1;
+
        /* Activity - toggle indicator LEDs */
        LEDs_ToggleLEDs(LEDS_LED1 | LEDS_LED2);
 
@@ -340,7 +414,7 @@ void EVENT_USB_Device_ControlRequest(void)
                                                        }
 
                                                        /* Write the next word into the current flash page */
-                                                       boot_page_fill(CurrFlashAddress.Long, Endpoint_Read_16_LE());
+                                                       BootloaderAPI_FillWord(CurrFlashAddress.Long, Endpoint_Read_16_LE());
 
                                                        /* Adjust counters */
                                                        WordsInFlashPage      += 1;
@@ -350,8 +424,7 @@ void EVENT_USB_Device_ControlRequest(void)
                                                        if ((WordsInFlashPage == (SPM_PAGESIZE >> 1)) || !(WordsRemaining))
                                                        {
                                                                /* Commit the flash page to memory */
-                                                               boot_page_write(CurrFlashPageStartAddress);
-                                                               boot_spm_busy_wait();
+                                                               BootloaderAPI_WritePage(CurrFlashPageStartAddress);
 
                                                                /* Check if programming incomplete */
                                                                if (WordsRemaining)
@@ -360,17 +433,13 @@ void EVENT_USB_Device_ControlRequest(void)
                                                                        WordsInFlashPage          = 0;
 
                                                                        /* Erase next page's temp buffer */
-                                                                       boot_page_erase(CurrFlashAddress.Long);
-                                                                       boot_spm_busy_wait();
+                                                                       BootloaderAPI_ErasePage(CurrFlashAddress.Long);
                                                                }
                                                        }
                                                }
 
                                                /* Once programming complete, start address equals the end address */
                                                StartAddr = EndAddr;
-
-                                               /* Re-enable the RWW section of flash */
-                                               boot_rww_enable();
                                        }
                                        else                                                   // Write EEPROM
                                        {
@@ -389,7 +458,7 @@ void EVENT_USB_Device_ControlRequest(void)
                                                        }
 
                                                        /* Read the byte from the USB interface and write to to the EEPROM */
-                                                       eeprom_write_byte((uint8_t*)StartAddr, Endpoint_Read_8());
+                                                       eeprom_update_byte((uint8_t*)StartAddr, Endpoint_Read_8());
 
                                                        /* Adjust counters */
                                                        StartAddr++;
@@ -680,8 +749,7 @@ static void ProcessMemProgCommand(void)
                        } CurrFlashAddress = {.Words = {StartAddr, Flash64KBPage}};
 
                        /* Erase the current page's temp buffer */
-                       boot_page_erase(CurrFlashAddress.Long);
-                       boot_spm_busy_wait();
+                       BootloaderAPI_ErasePage(CurrFlashAddress.Long);
                }
 
                /* Set the state so that the next DNLOAD requests reads in the firmware */
@@ -771,28 +839,16 @@ static void ProcessWriteCommand(void)
                        else                                                               // Start via jump
                        {
                                /* Set the flag to terminate the bootloader at next opportunity if a valid application has been loaded */
-                               if (pgm_read_word_near(0) == 0xFFFF)
+                               if (pgm_read_word_near(0) != 0xFFFF)
                                  RunBootloader = false;
                        }
                }
        }
        else if (IS_TWOBYTE_COMMAND(SentCommand.Data, 0x00, 0xFF))                 // Erase flash
        {
-               uint32_t CurrFlashAddress = 0;
-
                /* Clear the application section of flash */
-               while (CurrFlashAddress < (uint32_t)BOOT_START_ADDR)
-               {
-                       boot_page_erase(CurrFlashAddress);
-                       boot_spm_busy_wait();
-                       boot_page_write(CurrFlashAddress);
-                       boot_spm_busy_wait();
-
-                       CurrFlashAddress += SPM_PAGESIZE;
-               }
-
-               /* Re-enable the RWW section of flash as writing to the flash locks it out */
-               boot_rww_enable();
+               for (uint32_t CurrFlashAddress = 0; CurrFlashAddress < (uint32_t)BOOT_START_ADDR; CurrFlashAddress += SPM_PAGESIZE)
+                       BootloaderAPI_ErasePage(CurrFlashAddress);
 
                /* Memory has been erased, reset the security bit so that programming/reading is allowed */
                IsSecure = false;
@@ -807,18 +863,42 @@ static void ProcessReadCommand(void)
        const uint8_t BootloaderInfo[3] = {BOOTLOADER_VERSION, BOOTLOADER_ID_BYTE1, BOOTLOADER_ID_BYTE2};
        const uint8_t SignatureInfo[4]  = {0x58, AVR_SIGNATURE_1, AVR_SIGNATURE_2, AVR_SIGNATURE_3};
 
-       uint8_t DataIndexToRead = SentCommand.Data[1];
+       uint8_t DataIndexToRead    = SentCommand.Data[1];
+       bool    ReadAddressInvalid = false;
 
        if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00))                        // Read bootloader info
        {
-               ResponseByte = BootloaderInfo[DataIndexToRead];
+               if (DataIndexToRead < 3)
+                 ResponseByte = BootloaderInfo[DataIndexToRead];
+               else
+                 ReadAddressInvalid = true;
        }
        else if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x01))                    // Read signature byte
        {
-               if (DataIndexToRead < 0x60)
-                 ResponseByte = SignatureInfo[DataIndexToRead - 0x30];
-               else
-                 ResponseByte = SignatureInfo[DataIndexToRead - 0x60 + 3];
+               switch (DataIndexToRead)
+               {
+                       case 0x30:
+                               ResponseByte = SignatureInfo[0];
+                               break;
+                       case 0x31:
+                               ResponseByte = SignatureInfo[1];
+                               break;
+                       case 0x60:
+                               ResponseByte = SignatureInfo[2];
+                               break;
+                       case 0x61:
+                               ResponseByte = SignatureInfo[3];
+                               break;
+                       default:
+                               ReadAddressInvalid = true;
+                               break;
+               }
        }
-}
 
+       if (ReadAddressInvalid)
+       {
+               /* Set the state and status variables to indicate the error */
+               DFU_State  = dfuERROR;
+               DFU_Status = errADDRESS;
+       }
+}