X-Git-Url: http://git.linex4red.de/pub/USBasp.git/blobdiff_plain/e635872612fb054791a121a2920432574b9245bb..f2740a66bafa876dfcf2cdfa4c6f3e17395e8bf8:/Bootloaders/MassStorage/BootloaderMassStorage.c diff --git a/Bootloaders/MassStorage/BootloaderMassStorage.c b/Bootloaders/MassStorage/BootloaderMassStorage.c index 7adeaced7..ca3fd2897 100644 --- a/Bootloaders/MassStorage/BootloaderMassStorage.c +++ b/Bootloaders/MassStorage/BootloaderMassStorage.c @@ -1,13 +1,13 @@ /* LUFA Library - Copyright (C) Dean Camera, 2013. + Copyright (C) Dean Camera, 2018. dean [at] fourwalledcubicle [dot] com www.lufa-lib.org */ /* - Copyright 2013 Dean Camera (dean [at] fourwalledcubicle [dot] com) + Copyright 2018 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 @@ -33,6 +33,7 @@ * Main source file for the Mass Storage class bootloader. This file contains the complete bootloader logic. */ +#define INCLUDE_FROM_BOOTLOADER_MASSSTORAGE_C #include "BootloaderMassStorage.h" /** LUFA Mass Storage Class driver interface configuration and state information. This structure is @@ -43,7 +44,7 @@ USB_ClassInfo_MS_Device_t Disk_MS_Interface = { .Config = { - .InterfaceNumber = 0, + .InterfaceNumber = INTERFACE_ID_MassStorage, .DataINEndpoint = { .Address = MASS_STORAGE_IN_EPADDR, @@ -60,6 +61,97 @@ USB_ClassInfo_MS_Device_t Disk_MS_Interface = }, }; +/** 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 + * started via a forced watchdog reset. + */ +bool RunBootloader = true; + +/** Magic lock for forced application start. If the HWBE fuse is programmed and BOOTRST is unprogrammed, the bootloader + * will start if the /HWB line of the AVR is held low and the system is reset. However, if the /HWB line is still held + * low when the application attempts to start via a watchdog reset, the bootloader will re-start. If set to the value + * \ref MAGIC_BOOT_KEY the special init function \ref Application_Jump_Check() will force the application to start. + */ +uint16_t MagicBootKey ATTR_NO_INIT; + +/** Indicates if the bootloader is allowed to exit immediately if \ref RunBootloader is \c false. During shutdown all + * pending commands must be processed before jumping to the user-application, thus this tracks the main program loop + * iterations since a SCSI command from the host was received. + */ +static uint8_t TicksSinceLastCommand = 0; + + +/** Special startup routine to check if the bootloader was started via a watchdog reset, and if the magic application + * start key has been loaded into \ref MagicBootKey. If the bootloader started via the watchdog and the key is valid, + * this will force the user application to start via a software jump. + */ +void Application_Jump_Check(void) +{ + bool JumpToApplication = false; + + #if (BOARD == BOARD_LEONARDO) + /* Enable pull-up on the IO13 pin so we can use it to select the mode */ + PORTC |= (1 << 7); + Delay_MS(10); + + /* If IO13 is not jumpered to ground, start the user application instead */ + JumpToApplication = ((PINC & (1 << 7)) != 0); + + /* Disable pull-up after the check has completed */ + PORTC &= ~(1 << 7); + #elif ((BOARD == BOARD_XPLAIN) || (BOARD == BOARD_XPLAIN_REV1)) + /* Disable JTAG debugging */ + JTAG_DISABLE(); + + /* Enable pull-up on the JTAG TCK pin so we can use it to select the mode */ + PORTF |= (1 << 4); + Delay_MS(10); + + /* If the TCK pin is not jumpered to ground, start the user application instead */ + JumpToApplication = ((PINF & (1 << 4)) != 0); + + /* Re-enable JTAG debugging */ + JTAG_ENABLE(); + #else + /* Check if the device's BOOTRST fuse is set */ + 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 + + /* Don't run the user application if the reset vector is blank (no app loaded) */ + bool ApplicationValid = (pgm_read_word_near(0) != 0xFFFF); + + /* If a request has been made to jump to the user application, honor it */ + 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)(); + } +} /** Main program entry point. This routine configures the hardware required by the application, then * enters a loop to run the application tasks in sequence. @@ -71,11 +163,25 @@ int main(void) LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY); GlobalInterruptEnable(); - for (;;) + while (RunBootloader || TicksSinceLastCommand++ < 0xFF) { MS_Device_USBTask(&Disk_MS_Interface); USB_USBTask(); } + + /* Wait a short time to end all USB transactions and then disconnect */ + _delay_us(1000); + + /* Disconnect from the host - USB interface will be reset later along with the AVR */ + USB_Detach(); + + /* Unlock the forced application start mode of the bootloader if it is restarted */ + MagicBootKey = MAGIC_BOOT_KEY; + + /* Enable the watchdog and force a timeout to reset the AVR */ + wdt_enable(WDTO_250MS); + + for (;;); } /** Configures the board hardware and chip peripherals for the demo's functionality. */ @@ -153,5 +259,8 @@ bool CALLBACK_MS_Device_SCSICommandReceived(USB_ClassInfo_MS_Device_t* const MSI CommandSuccess = SCSI_DecodeSCSICommand(MSInterfaceInfo); LEDs_SetAllLEDs(LEDMASK_USB_READY); + /* Signal that a command was processed, must not exit bootloader yet */ + TicksSinceLastCommand = 0; + return CommandSuccess; }