|  |  |  | /*
 | 
					
						
							|  |  |  |              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_INPUT) & NUM(QMK_ESC_INPUT)); | 
					
						
							|  |  |  | 		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)
 | 
					
						
							|  |  |  | 		// output setup
 | 
					
						
							|  |  |  | 		DDR(QMK_ESC_OUTPUT) |= NUM(QMK_ESC_OUTPUT); | 
					
						
							|  |  |  | 		PORT(QMK_ESC_OUTPUT) |= NUM(QMK_ESC_OUTPUT); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// input setup
 | 
					
						
							|  |  |  | 		DDR(QMK_ESC_INPUT) |= NUM(QMK_ESC_INPUT); | 
					
						
							|  |  |  | 	#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_OUTPUT) = PORT(QMK_ESC_OUTPUT) = DDR(QMK_ESC_INPUT) = PORT(QMK_ESC_INPUT) = 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; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |