forked from mirrors/qmk_userspace
Merge commit '60b30c0363
' as 'lib/lufa'
This commit is contained in:
commit
8655d4f494
1455 changed files with 394541 additions and 0 deletions
891
lib/lufa/Bootloaders/DFU/BootloaderDFU.c
Normal file
891
lib/lufa/Bootloaders/DFU/BootloaderDFU.c
Normal file
|
@ -0,0 +1,891 @@
|
|||
/*
|
||||
LUFA Library
|
||||
Copyright (C) Dean Camera, 2017.
|
||||
|
||||
dean [at] fourwalledcubicle [dot] com
|
||||
www.lufa-lib.org
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright 2017 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
|
||||
without fee, provided that the above copyright notice appear in
|
||||
all copies and that both that the copyright notice and this
|
||||
permission notice and warranty disclaimer appear in supporting
|
||||
documentation, and that the name of the author not be used in
|
||||
advertising or publicity pertaining to distribution of the
|
||||
software without specific, written prior permission.
|
||||
|
||||
The author disclaims all warranties with regard to this
|
||||
software, including all implied warranties of merchantability
|
||||
and fitness. In no event shall the author be liable for any
|
||||
special, indirect or consequential damages or any damages
|
||||
whatsoever resulting from loss of use, data or profits, whether
|
||||
in an action of contract, negligence or other tortious action,
|
||||
arising out of or in connection with the use or performance of
|
||||
this software.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
*
|
||||
* Main source file for the DFU class bootloader. This file contains the complete bootloader logic.
|
||||
*/
|
||||
|
||||
#define INCLUDE_FROM_BOOTLOADER_C
|
||||
#include "BootloaderDFU.h"
|
||||
|
||||
/** Flag to indicate if the bootloader is currently running in secure mode, disallowing memory operations
|
||||
* other than erase. This is initially set to the value set by SECURE_MODE, and cleared by the bootloader
|
||||
* once a memory erase has completed in a bootloader session.
|
||||
*/
|
||||
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).
|
||||
*/
|
||||
static 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
|
||||
* acknowledged. Upon reception of the first the RunBootloader flag is cleared and the WaitForExit flag is set,
|
||||
* causing the bootloader to wait for the final exit command before shutting down.
|
||||
*/
|
||||
static bool WaitForExit = false;
|
||||
|
||||
/** Current DFU state machine state, one of the values in the DFU_State_t enum. */
|
||||
static uint8_t DFU_State = dfuIDLE;
|
||||
|
||||
/** Status code of the last executed DFU command. This is set to one of the values in the DFU_Status_t enum after
|
||||
* each operation, and returned to the host when a Get Status DFU request is issued.
|
||||
*/
|
||||
static uint8_t DFU_Status = OK;
|
||||
|
||||
/** Data containing the DFU command sent from the host. */
|
||||
static DFU_Command_t SentCommand;
|
||||
|
||||
/** Response to the last issued Read Data DFU command. Unlike other DFU commands, the read command
|
||||
* requires a single byte response from the bootloader containing the read data when the next DFU_UPLOAD command
|
||||
* is issued by the host.
|
||||
*/
|
||||
static uint8_t ResponseByte;
|
||||
|
||||
/** Pointer to the start of the user application. By default this is 0x0000 (the reset vector), however the host
|
||||
* may specify an alternate address when issuing the application soft-start command.
|
||||
*/
|
||||
static AppPtr_t AppStartPtr = (AppPtr_t)0x0000;
|
||||
|
||||
/** 64-bit flash page number. This is concatenated with the current 16-bit address on USB AVRs containing more than
|
||||
* 64KB of flash memory.
|
||||
*/
|
||||
static uint8_t Flash64KBPage = 0;
|
||||
|
||||
/** Memory start address, indicating the current address in the memory being addressed (either FLASH or EEPROM
|
||||
* depending on the issued command from the host).
|
||||
*/
|
||||
static uint16_t StartAddr = 0x0000;
|
||||
|
||||
/** Memory end address, indicating the end address to read from/write to in the memory being addressed (either FLASH
|
||||
* of EEPROM depending on the issued command from the host).
|
||||
*/
|
||||
static uint16_t EndAddr = 0x0000;
|
||||
|
||||
/** 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;
|
||||
|
||||
|
||||
/** 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 (boot_lock_fuse_bits_get(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 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.
|
||||
*/
|
||||
int main(void)
|
||||
{
|
||||
/* Configure hardware required by the bootloader */
|
||||
SetupHardware();
|
||||
|
||||
/* Turn on first LED on the board to indicate that the bootloader has started */
|
||||
LEDs_SetAllLEDs(LEDS_LED1 | LEDS_LED2);
|
||||
|
||||
/* Enable global interrupts so that the USB stack can function */
|
||||
GlobalInterruptEnable();
|
||||
|
||||
|
||||
#if (BOARD == BOARD_QMK)
|
||||
uint16_t keypress = 0;
|
||||
#endif
|
||||
|
||||
/* Run the USB management task while the bootloader is supposed to be running */
|
||||
while (RunBootloader || WaitForExit) {
|
||||
USB_USBTask();
|
||||
#if (BOARD == BOARD_QMK)
|
||||
bool pressed = (PIN(QMK_ESC_ROW) & NUM(QMK_ESC_ROW));
|
||||
if ((DFU_State == dfuIDLE) && (keypress > 5000) && pressed) {
|
||||
break;
|
||||
}
|
||||
if (pressed) {
|
||||
keypress++;
|
||||
} else {
|
||||
keypress = 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Reset configured hardware back to their original states for the user application */
|
||||
ResetHardware();
|
||||
|
||||
/* Start the user application */
|
||||
AppStartPtr();
|
||||
}
|
||||
|
||||
/** Configures all hardware required for the bootloader. */
|
||||
static void SetupHardware(void)
|
||||
{
|
||||
/* Disable watchdog if enabled by bootloader/fuses */
|
||||
MCUSR &= ~(1 << WDRF);
|
||||
wdt_disable();
|
||||
|
||||
/* Disable clock division */
|
||||
clock_prescale_set(clock_div_1);
|
||||
|
||||
/* Relocate the interrupt vector table to the bootloader section */
|
||||
MCUCR = (1 << IVCE);
|
||||
MCUCR = (1 << IVSEL);
|
||||
|
||||
#if (BOARD == BOARD_QMK)
|
||||
// column setup
|
||||
DDR(QMK_ESC_COL) |= NUM(QMK_ESC_COL);
|
||||
PORT(QMK_ESC_COL) |= NUM(QMK_ESC_COL);
|
||||
|
||||
// row setup
|
||||
DDR(QMK_ESC_ROW) |= NUM(QMK_ESC_ROW);
|
||||
#endif
|
||||
|
||||
/* Initialize the USB and other board hardware drivers */
|
||||
USB_Init();
|
||||
LEDs_Init();
|
||||
|
||||
/* Bootloader active LED toggle timer initialization */
|
||||
TIMSK1 = (1 << TOIE1);
|
||||
TCCR1B = ((1 << CS11) | (1 << CS10));
|
||||
|
||||
}
|
||||
|
||||
/** Resets all configured hardware required for the bootloader back to their original states. */
|
||||
static void ResetHardware(void)
|
||||
{
|
||||
/* Shut down the USB and other board hardware drivers */
|
||||
USB_Disable();
|
||||
LEDs_Disable();
|
||||
|
||||
/* Disable Bootloader active LED toggle timer */
|
||||
TIMSK1 = 0;
|
||||
TCCR1B = 0;
|
||||
|
||||
/* Relocate the interrupt vector table back to the application section */
|
||||
MCUCR = (1 << IVCE);
|
||||
MCUCR = 0;
|
||||
|
||||
#if (BOARD == BOARD_QMK)
|
||||
DDR(QMK_ESC_COL) = PORT(QMK_ESC_COL) = DDR(QMK_ESC_ROW) = PORT(QMK_ESC_ROW) = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** ISR to periodically toggle the LEDs on the board to indicate that the bootloader is active. */
|
||||
ISR(TIMER1_OVF_vect, ISR_BLOCK)
|
||||
{
|
||||
LEDs_ToggleLEDs(LEDS_LED1 | LEDS_LED2);
|
||||
}
|
||||
|
||||
/** Event handler for the USB_ControlRequest event. This is used to catch and process control requests sent to
|
||||
* the device from the USB host before passing along unhandled control requests to the library for processing
|
||||
* internally.
|
||||
*/
|
||||
void EVENT_USB_Device_ControlRequest(void)
|
||||
{
|
||||
/* Ignore any requests that aren't directed to the DFU interface */
|
||||
if ((USB_ControlRequest.bmRequestType & (CONTROL_REQTYPE_TYPE | CONTROL_REQTYPE_RECIPIENT)) !=
|
||||
(REQTYPE_CLASS | REQREC_INTERFACE))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Activity - toggle indicator LEDs */
|
||||
LEDs_ToggleLEDs(LEDS_LED1 | LEDS_LED2);
|
||||
|
||||
/* Get the size of the command and data from the wLength value */
|
||||
SentCommand.DataSize = USB_ControlRequest.wLength;
|
||||
|
||||
switch (USB_ControlRequest.bRequest)
|
||||
{
|
||||
case DFU_REQ_DNLOAD:
|
||||
Endpoint_ClearSETUP();
|
||||
|
||||
/* Check if bootloader is waiting to terminate */
|
||||
if (WaitForExit)
|
||||
{
|
||||
/* Bootloader is terminating - process last received command */
|
||||
ProcessBootloaderCommand();
|
||||
|
||||
/* Indicate that the last command has now been processed - free to exit bootloader */
|
||||
WaitForExit = false;
|
||||
}
|
||||
|
||||
/* If the request has a data stage, load it into the command struct */
|
||||
if (SentCommand.DataSize)
|
||||
{
|
||||
while (!(Endpoint_IsOUTReceived()))
|
||||
{
|
||||
if (USB_DeviceState == DEVICE_STATE_Unattached)
|
||||
return;
|
||||
}
|
||||
|
||||
/* First byte of the data stage is the DNLOAD request's command */
|
||||
SentCommand.Command = Endpoint_Read_8();
|
||||
|
||||
/* One byte of the data stage is the command, so subtract it from the total data bytes */
|
||||
SentCommand.DataSize--;
|
||||
|
||||
/* Load in the rest of the data stage as command parameters */
|
||||
for (uint8_t DataByte = 0; (DataByte < sizeof(SentCommand.Data)) &&
|
||||
Endpoint_BytesInEndpoint(); DataByte++)
|
||||
{
|
||||
SentCommand.Data[DataByte] = Endpoint_Read_8();
|
||||
SentCommand.DataSize--;
|
||||
}
|
||||
|
||||
/* Process the command */
|
||||
ProcessBootloaderCommand();
|
||||
}
|
||||
|
||||
/* Check if currently downloading firmware */
|
||||
if (DFU_State == dfuDNLOAD_IDLE)
|
||||
{
|
||||
if (!(SentCommand.DataSize))
|
||||
{
|
||||
DFU_State = dfuIDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Throw away the filler bytes before the start of the firmware */
|
||||
DiscardFillerBytes(DFU_FILLER_BYTES_SIZE);
|
||||
|
||||
/* Throw away the packet alignment filler bytes before the start of the firmware */
|
||||
DiscardFillerBytes(StartAddr % FIXED_CONTROL_ENDPOINT_SIZE);
|
||||
|
||||
/* Calculate the number of bytes remaining to be written */
|
||||
uint16_t BytesRemaining = ((EndAddr - StartAddr) + 1);
|
||||
|
||||
if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00)) // Write flash
|
||||
{
|
||||
/* Calculate the number of words to be written from the number of bytes to be written */
|
||||
uint16_t WordsRemaining = (BytesRemaining >> 1);
|
||||
|
||||
union
|
||||
{
|
||||
uint16_t Words[2];
|
||||
uint32_t Long;
|
||||
} CurrFlashAddress = {.Words = {StartAddr, Flash64KBPage}};
|
||||
|
||||
uint32_t CurrFlashPageStartAddress = CurrFlashAddress.Long;
|
||||
uint8_t WordsInFlashPage = 0;
|
||||
|
||||
while (WordsRemaining--)
|
||||
{
|
||||
/* Check if endpoint is empty - if so clear it and wait until ready for next packet */
|
||||
if (!(Endpoint_BytesInEndpoint()))
|
||||
{
|
||||
Endpoint_ClearOUT();
|
||||
|
||||
while (!(Endpoint_IsOUTReceived()))
|
||||
{
|
||||
if (USB_DeviceState == DEVICE_STATE_Unattached)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the next word into the current flash page */
|
||||
boot_page_fill(CurrFlashAddress.Long, Endpoint_Read_16_LE());
|
||||
|
||||
/* Adjust counters */
|
||||
WordsInFlashPage += 1;
|
||||
CurrFlashAddress.Long += 2;
|
||||
|
||||
/* See if an entire page has been written to the flash page buffer */
|
||||
if ((WordsInFlashPage == (SPM_PAGESIZE >> 1)) || !(WordsRemaining))
|
||||
{
|
||||
/* Commit the flash page to memory */
|
||||
boot_page_write(CurrFlashPageStartAddress);
|
||||
boot_spm_busy_wait();
|
||||
|
||||
/* Check if programming incomplete */
|
||||
if (WordsRemaining)
|
||||
{
|
||||
CurrFlashPageStartAddress = CurrFlashAddress.Long;
|
||||
WordsInFlashPage = 0;
|
||||
|
||||
/* Erase next page's temp buffer */
|
||||
boot_page_erase(CurrFlashAddress.Long);
|
||||
boot_spm_busy_wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Once programming complete, start address equals the end address */
|
||||
StartAddr = EndAddr;
|
||||
|
||||
/* Re-enable the RWW section of flash */
|
||||
boot_rww_enable();
|
||||
}
|
||||
else // Write EEPROM
|
||||
{
|
||||
while (BytesRemaining--)
|
||||
{
|
||||
/* Check if endpoint is empty - if so clear it and wait until ready for next packet */
|
||||
if (!(Endpoint_BytesInEndpoint()))
|
||||
{
|
||||
Endpoint_ClearOUT();
|
||||
|
||||
while (!(Endpoint_IsOUTReceived()))
|
||||
{
|
||||
if (USB_DeviceState == DEVICE_STATE_Unattached)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the byte from the USB interface and write to to the EEPROM */
|
||||
eeprom_update_byte((uint8_t*)StartAddr, Endpoint_Read_8());
|
||||
|
||||
/* Adjust counters */
|
||||
StartAddr++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Throw away the currently unused DFU file suffix */
|
||||
DiscardFillerBytes(DFU_FILE_SUFFIX_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
Endpoint_ClearOUT();
|
||||
|
||||
Endpoint_ClearStatusStage();
|
||||
|
||||
break;
|
||||
case DFU_REQ_UPLOAD:
|
||||
Endpoint_ClearSETUP();
|
||||
|
||||
while (!(Endpoint_IsINReady()))
|
||||
{
|
||||
if (USB_DeviceState == DEVICE_STATE_Unattached)
|
||||
return;
|
||||
}
|
||||
|
||||
if (DFU_State != dfuUPLOAD_IDLE)
|
||||
{
|
||||
if ((DFU_State == dfuERROR) && IS_ONEBYTE_COMMAND(SentCommand.Data, 0x01)) // Blank Check
|
||||
{
|
||||
/* Blank checking is performed in the DFU_DNLOAD request - if we get here we've told the host
|
||||
that the memory isn't blank, and the host is requesting the first non-blank address */
|
||||
Endpoint_Write_16_LE(StartAddr);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Idle state upload - send response to last issued command */
|
||||
Endpoint_Write_8(ResponseByte);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Determine the number of bytes remaining in the current block */
|
||||
uint16_t BytesRemaining = ((EndAddr - StartAddr) + 1);
|
||||
|
||||
if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00)) // Read FLASH
|
||||
{
|
||||
/* Calculate the number of words to be written from the number of bytes to be written */
|
||||
uint16_t WordsRemaining = (BytesRemaining >> 1);
|
||||
|
||||
union
|
||||
{
|
||||
uint16_t Words[2];
|
||||
uint32_t Long;
|
||||
} CurrFlashAddress = {.Words = {StartAddr, Flash64KBPage}};
|
||||
|
||||
while (WordsRemaining--)
|
||||
{
|
||||
/* Check if endpoint is full - if so clear it and wait until ready for next packet */
|
||||
if (Endpoint_BytesInEndpoint() == FIXED_CONTROL_ENDPOINT_SIZE)
|
||||
{
|
||||
Endpoint_ClearIN();
|
||||
|
||||
while (!(Endpoint_IsINReady()))
|
||||
{
|
||||
if (USB_DeviceState == DEVICE_STATE_Unattached)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the flash word and send it via USB to the host */
|
||||
#if (FLASHEND > 0xFFFF)
|
||||
Endpoint_Write_16_LE(pgm_read_word_far(CurrFlashAddress.Long));
|
||||
#else
|
||||
Endpoint_Write_16_LE(pgm_read_word(CurrFlashAddress.Long));
|
||||
#endif
|
||||
|
||||
/* Adjust counters */
|
||||
CurrFlashAddress.Long += 2;
|
||||
}
|
||||
|
||||
/* Once reading is complete, start address equals the end address */
|
||||
StartAddr = EndAddr;
|
||||
}
|
||||
else if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x02)) // Read EEPROM
|
||||
{
|
||||
while (BytesRemaining--)
|
||||
{
|
||||
/* Check if endpoint is full - if so clear it and wait until ready for next packet */
|
||||
if (Endpoint_BytesInEndpoint() == FIXED_CONTROL_ENDPOINT_SIZE)
|
||||
{
|
||||
Endpoint_ClearIN();
|
||||
|
||||
while (!(Endpoint_IsINReady()))
|
||||
{
|
||||
if (USB_DeviceState == DEVICE_STATE_Unattached)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the EEPROM byte and send it via USB to the host */
|
||||
Endpoint_Write_8(eeprom_read_byte((uint8_t*)StartAddr));
|
||||
|
||||
/* Adjust counters */
|
||||
StartAddr++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return to idle state */
|
||||
DFU_State = dfuIDLE;
|
||||
}
|
||||
|
||||
Endpoint_ClearIN();
|
||||
|
||||
Endpoint_ClearStatusStage();
|
||||
break;
|
||||
case DFU_REQ_GETSTATUS:
|
||||
Endpoint_ClearSETUP();
|
||||
|
||||
while (!(Endpoint_IsINReady()))
|
||||
{
|
||||
if (USB_DeviceState == DEVICE_STATE_Unattached)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Write 8-bit status value */
|
||||
Endpoint_Write_8(DFU_Status);
|
||||
|
||||
/* Write 24-bit poll timeout value */
|
||||
Endpoint_Write_8(0);
|
||||
Endpoint_Write_16_LE(0);
|
||||
|
||||
/* Write 8-bit state value */
|
||||
Endpoint_Write_8(DFU_State);
|
||||
|
||||
/* Write 8-bit state string ID number */
|
||||
Endpoint_Write_8(0);
|
||||
|
||||
Endpoint_ClearIN();
|
||||
|
||||
Endpoint_ClearStatusStage();
|
||||
break;
|
||||
case DFU_REQ_CLRSTATUS:
|
||||
Endpoint_ClearSETUP();
|
||||
|
||||
/* Reset the status value variable to the default OK status */
|
||||
DFU_Status = OK;
|
||||
|
||||
Endpoint_ClearStatusStage();
|
||||
break;
|
||||
case DFU_REQ_GETSTATE:
|
||||
Endpoint_ClearSETUP();
|
||||
|
||||
while (!(Endpoint_IsINReady()))
|
||||
{
|
||||
if (USB_DeviceState == DEVICE_STATE_Unattached)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Write the current device state to the endpoint */
|
||||
Endpoint_Write_8(DFU_State);
|
||||
|
||||
Endpoint_ClearIN();
|
||||
|
||||
Endpoint_ClearStatusStage();
|
||||
break;
|
||||
case DFU_REQ_ABORT:
|
||||
Endpoint_ClearSETUP();
|
||||
|
||||
/* Reset the current state variable to the default idle state */
|
||||
DFU_State = dfuIDLE;
|
||||
|
||||
Endpoint_ClearStatusStage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** Routine to discard the specified number of bytes from the control endpoint stream. This is used to
|
||||
* discard unused bytes in the stream from the host, including the memory program block suffix.
|
||||
*
|
||||
* \param[in] NumberOfBytes Number of bytes to discard from the host from the control endpoint
|
||||
*/
|
||||
static void DiscardFillerBytes(uint8_t NumberOfBytes)
|
||||
{
|
||||
while (NumberOfBytes--)
|
||||
{
|
||||
if (!(Endpoint_BytesInEndpoint()))
|
||||
{
|
||||
Endpoint_ClearOUT();
|
||||
|
||||
/* Wait until next data packet received */
|
||||
while (!(Endpoint_IsOUTReceived()))
|
||||
{
|
||||
if (USB_DeviceState == DEVICE_STATE_Unattached)
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Endpoint_Discard_8();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Routine to process an issued command from the host, via a DFU_DNLOAD request wrapper. This routine ensures
|
||||
* that the command is allowed based on the current secure mode flag value, and passes the command off to the
|
||||
* appropriate handler function.
|
||||
*/
|
||||
static void ProcessBootloaderCommand(void)
|
||||
{
|
||||
/* Check if device is in secure mode */
|
||||
if (IsSecure)
|
||||
{
|
||||
/* Don't process command unless it is a READ or chip erase command */
|
||||
if (!(((SentCommand.Command == COMMAND_WRITE) &&
|
||||
IS_TWOBYTE_COMMAND(SentCommand.Data, 0x00, 0xFF)) ||
|
||||
(SentCommand.Command == COMMAND_READ)))
|
||||
{
|
||||
/* Set the state and status variables to indicate the error */
|
||||
DFU_State = dfuERROR;
|
||||
DFU_Status = errWRITE;
|
||||
|
||||
/* Stall command */
|
||||
Endpoint_StallTransaction();
|
||||
|
||||
/* Don't process the command */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dispatch the required command processing routine based on the command type */
|
||||
switch (SentCommand.Command)
|
||||
{
|
||||
case COMMAND_PROG_START:
|
||||
ProcessMemProgCommand();
|
||||
break;
|
||||
case COMMAND_DISP_DATA:
|
||||
ProcessMemReadCommand();
|
||||
break;
|
||||
case COMMAND_WRITE:
|
||||
ProcessWriteCommand();
|
||||
break;
|
||||
case COMMAND_READ:
|
||||
ProcessReadCommand();
|
||||
break;
|
||||
case COMMAND_CHANGE_BASE_ADDR:
|
||||
if (IS_TWOBYTE_COMMAND(SentCommand.Data, 0x03, 0x00)) // Set 64KB flash page command
|
||||
Flash64KBPage = SentCommand.Data[2];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** Routine to concatenate the given pair of 16-bit memory start and end addresses from the host, and store them
|
||||
* in the StartAddr and EndAddr global variables.
|
||||
*/
|
||||
static void LoadStartEndAddresses(void)
|
||||
{
|
||||
union
|
||||
{
|
||||
uint8_t Bytes[2];
|
||||
uint16_t Word;
|
||||
} Address[2] = {{.Bytes = {SentCommand.Data[2], SentCommand.Data[1]}},
|
||||
{.Bytes = {SentCommand.Data[4], SentCommand.Data[3]}}};
|
||||
|
||||
/* Load in the start and ending read addresses from the sent data packet */
|
||||
StartAddr = Address[0].Word;
|
||||
EndAddr = Address[1].Word;
|
||||
}
|
||||
|
||||
/** Handler for a Memory Program command issued by the host. This routine handles the preparations needed
|
||||
* to write subsequent data from the host into the specified memory.
|
||||
*/
|
||||
static void ProcessMemProgCommand(void)
|
||||
{
|
||||
if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00) || // Write FLASH command
|
||||
IS_ONEBYTE_COMMAND(SentCommand.Data, 0x01)) // Write EEPROM command
|
||||
{
|
||||
/* Load in the start and ending read addresses */
|
||||
LoadStartEndAddresses();
|
||||
|
||||
/* If FLASH is being written to, we need to pre-erase the first page to write to */
|
||||
if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00))
|
||||
{
|
||||
union
|
||||
{
|
||||
uint16_t Words[2];
|
||||
uint32_t Long;
|
||||
} CurrFlashAddress = {.Words = {StartAddr, Flash64KBPage}};
|
||||
|
||||
/* Erase the current page's temp buffer */
|
||||
boot_page_erase(CurrFlashAddress.Long);
|
||||
boot_spm_busy_wait();
|
||||
}
|
||||
|
||||
/* Set the state so that the next DNLOAD requests reads in the firmware */
|
||||
DFU_State = dfuDNLOAD_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
/** Handler for a Memory Read command issued by the host. This routine handles the preparations needed
|
||||
* to read subsequent data from the specified memory out to the host, as well as implementing the memory
|
||||
* blank check command.
|
||||
*/
|
||||
static void ProcessMemReadCommand(void)
|
||||
{
|
||||
if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00) || // Read FLASH command
|
||||
IS_ONEBYTE_COMMAND(SentCommand.Data, 0x02)) // Read EEPROM command
|
||||
{
|
||||
/* Load in the start and ending read addresses */
|
||||
LoadStartEndAddresses();
|
||||
|
||||
/* Set the state so that the next UPLOAD requests read out the firmware */
|
||||
DFU_State = dfuUPLOAD_IDLE;
|
||||
}
|
||||
else if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x01)) // Blank check FLASH command
|
||||
{
|
||||
uint32_t CurrFlashAddress = 0;
|
||||
|
||||
while (CurrFlashAddress < (uint32_t)BOOT_START_ADDR)
|
||||
{
|
||||
/* Check if the current byte is not blank */
|
||||
#if (FLASHEND > 0xFFFF)
|
||||
if (pgm_read_byte_far(CurrFlashAddress) != 0xFF)
|
||||
#else
|
||||
if (pgm_read_byte(CurrFlashAddress) != 0xFF)
|
||||
#endif
|
||||
{
|
||||
/* Save the location of the first non-blank byte for response back to the host */
|
||||
Flash64KBPage = (CurrFlashAddress >> 16);
|
||||
StartAddr = CurrFlashAddress;
|
||||
|
||||
/* Set state and status variables to the appropriate error values */
|
||||
DFU_State = dfuERROR;
|
||||
DFU_Status = errCHECK_ERASED;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
CurrFlashAddress++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Handler for a Data Write command issued by the host. This routine handles non-programming commands such as
|
||||
* bootloader exit (both via software jumps and hardware watchdog resets) and flash memory erasure.
|
||||
*/
|
||||
static void ProcessWriteCommand(void)
|
||||
{
|
||||
if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x03)) // Start application
|
||||
{
|
||||
/* Indicate that the bootloader is terminating */
|
||||
WaitForExit = true;
|
||||
|
||||
/* Check if data supplied for the Start Program command - no data executes the program */
|
||||
if (SentCommand.DataSize)
|
||||
{
|
||||
if (SentCommand.Data[1] == 0x01) // Start via jump
|
||||
{
|
||||
union
|
||||
{
|
||||
uint8_t Bytes[2];
|
||||
AppPtr_t FuncPtr;
|
||||
} Address = {.Bytes = {SentCommand.Data[4], SentCommand.Data[3]}};
|
||||
|
||||
/* Load in the jump address into the application start address pointer */
|
||||
AppStartPtr = Address.FuncPtr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (SentCommand.Data[1] == 0x00) // Start via watchdog
|
||||
{
|
||||
/* Unlock the forced application start mode of the bootloader if it is restarted */
|
||||
MagicBootKey = MAGIC_BOOT_KEY;
|
||||
|
||||
/* Start the watchdog to reset the AVR once the communications are finalized */
|
||||
wdt_enable(WDTO_250MS);
|
||||
}
|
||||
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)
|
||||
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();
|
||||
|
||||
/* Memory has been erased, reset the security bit so that programming/reading is allowed */
|
||||
IsSecure = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Handler for a Data Read command issued by the host. This routine handles bootloader information retrieval
|
||||
* commands such as device signature and bootloader version retrieval.
|
||||
*/
|
||||
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];
|
||||
bool ReadAddressInvalid = false;
|
||||
|
||||
if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x00)) // Read bootloader info
|
||||
{
|
||||
if (DataIndexToRead < 3)
|
||||
ResponseByte = BootloaderInfo[DataIndexToRead];
|
||||
else
|
||||
ReadAddressInvalid = true;
|
||||
}
|
||||
else if (IS_ONEBYTE_COMMAND(SentCommand.Data, 0x01)) // Read signature byte
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue