Enhance USB Interrupt responsibility
[pub/lufa.git] / Bootloaders / DFU / BootloaderDFU.c
index 3167c89..ce76abe 100644 (file)
@@ -1,13 +1,13 @@
 /*
              LUFA Library
-     Copyright (C) Dean Camera, 2018.
+     Copyright (C) Dean Camera, 2021.
 
   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
@@ -131,6 +131,41 @@ 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 (!(BootloaderAPI_ReadFuse(GET_HIGH_FUSE_BITS) & ~FUSE_BOOTRST))
@@ -154,6 +189,9 @@ void Application_Jump_Check(void)
                }
        #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);
 
@@ -161,17 +199,16 @@ 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)();
        }
 }
 
+
+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.
@@ -188,8 +225,29 @@ int main(void)
        GlobalInterruptEnable();
 
        /* Run the USB management task while the bootloader is supposed to be running */
+       stayinbootloader = false;
+
+       uint16_t i = 0;
        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);
@@ -259,6 +317,8 @@ void EVENT_USB_Device_ControlRequest(void)
                return;
        }
 
+stayinbootloader = true;
+
        /* 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 */
-                               if (pgm_read_word_near(0) == 0xFFFF)
+                               if (pgm_read_word_near(0) != 0xFFFF)
                                  RunBootloader = false;
                        }
                }