Enhance USB Interrupt responsibility
[pub/lufa.git] / Bootloaders / DFU / BootloaderDFU.c
index cafbd57..ce76abe 100644 (file)
@@ -1,13 +1,13 @@
 /*
              LUFA Library
 /*
              LUFA Library
-     Copyright (C) Dean Camera, 2018.
+     Copyright (C) Dean Camera, 2021.
 
   dean [at] fourwalledcubicle [dot] com
            www.lufa-lib.org
 */
 
 /*
 
   dean [at] fourwalledcubicle [dot] com
            www.lufa-lib.org
 */
 
 /*
-  Copyright 2018  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
 
   Permission to use, copy, modify, distribute, and sell this
   software and its documentation for any purpose is hereby granted
@@ -131,9 +131,44 @@ void Application_Jump_Check(void)
 
                /* Re-enable JTAG debugging */
                JTAG_ENABLE();
 
                /* 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 */
        #else
                /* Check if the device's BOOTRST fuse is set */
-               if (BootloaderAPI_ReadFuse(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))
                {
                        /* 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))
@@ -154,6 +189,9 @@ void Application_Jump_Check(void)
                }
        #endif
 
                }
        #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);
 
        /* Don't run the user application if the reset vector is blank (no app loaded) */
        bool ApplicationValid = (pgm_read_word_near(0) != 0xFFFF);
 
@@ -161,17 +199,16 @@ void Application_Jump_Check(void)
        if (JumpToApplication && ApplicationValid)
        {
                /* Turn off the watchdog */
        if (JumpToApplication && ApplicationValid)
        {
                /* Turn off the watchdog */
-               MCUSR &= ~(1 << WDRF);
                wdt_disable();
 
                wdt_disable();
 
-               /* Clear the boot key and jump to the user application */
-               MagicBootKey = 0;
-
                // cppcheck-suppress constStatement
                ((void (*)(void))0x0000)();
        }
 }
 
                // cppcheck-suppress constStatement
                ((void (*)(void))0x0000)();
        }
 }
 
+
+static volatile bool stayinbootloader;
+
 /** Main program entry point. This routine configures the hardware required by the bootloader, then continuously
  *  runs the bootloader processing routine until instructed to soft-exit, or hard-reset via the watchdog to start
  *  the loaded application code.
 /** Main program entry point. This routine configures the hardware required by the bootloader, then continuously
  *  runs the bootloader processing routine until instructed to soft-exit, or hard-reset via the watchdog to start
  *  the loaded application code.
@@ -188,8 +225,29 @@ int main(void)
        GlobalInterruptEnable();
 
        /* Run the USB management task while the bootloader is supposed to be running */
        GlobalInterruptEnable();
 
        /* Run the USB management task while the bootloader is supposed to be running */
+       stayinbootloader = false;
+
+       uint16_t i = 0;
        while (RunBootloader || WaitForExit)
        while (RunBootloader || WaitForExit)
-         USB_USBTask();
+       {
+               USB_USBTask();
+
+               if (!stayinbootloader)
+               {
+                       Delay_MS(1);
+                       if (i++ > 5000)
+                       {
+                               if (pgm_read_word_near(0) != 0xFFFF)
+                                       break;
+                               else
+                                       i = 0;
+                       }
+               }
+               else
+               {
+                       i = 0;
+               }
+       }
 
        /* Wait a short time to end all USB transactions and then disconnect */
        _delay_us(1000);
 
        /* Wait a short time to end all USB transactions and then disconnect */
        _delay_us(1000);
@@ -259,6 +317,8 @@ void EVENT_USB_Device_ControlRequest(void)
                return;
        }
 
                return;
        }
 
+stayinbootloader = true;
+
        /* Activity - toggle indicator LEDs */
        LEDs_ToggleLEDs(LEDS_LED1 | LEDS_LED2);
 
        /* Activity - toggle indicator LEDs */
        LEDs_ToggleLEDs(LEDS_LED1 | LEDS_LED2);
 
@@ -779,7 +839,7 @@ 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 */
                        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;
                        }
                }
                                  RunBootloader = false;
                        }
                }