this software.\r
*/\r
\r
+#if AUX_BOOT_SECTION_SIZE > 0\r
+\r
+#warning Using a AUX bootloader section in addition to the defined bootloader space (see documentation).\r
+\r
+; Trampoline to jump over the AUX bootloader section to the start of the bootloader,\r
+; on devices where an AUX bootloader section is used.\r
+.section .boot_aux_trampoline, "ax"\r
+.global Boot_AUX_Trampoline\r
+Boot_AUX_Trampoline:\r
+ jmp BOOT_START_ADDR\r
+\r
+#endif\r
+\r
; Trampolines to actual API implementations if the target address is outside the\r
; range of a rjmp instruction (can happen with large bootloader sections)\r
.section .apitable_trampolines, "ax"\r
};
+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);
+ JumpToApplication |= ((PINC & (1 << 7)) != 0);
+ PORTC &= ~(1 << 7);
+ #endif
+
+ if (JumpToApplication)
+ {
+ // 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.
*/
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);
/* Includes: */
#include <avr/io.h>
#include <avr/wdt.h>
- #include <avr/power.h>
#include <avr/interrupt.h>
#include <string.h>
#define LEDMASK_USB_BUSY LEDS_LED2
/* Function Prototypes: */
- static void SetupHardware(void);
+ int main(void) AUX_BOOT_SECTION;
- void EVENT_USB_Device_Connect(void);
- void EVENT_USB_Device_Disconnect(void);
- void EVENT_USB_Device_ConfigurationChanged(void);
- void EVENT_USB_Device_ControlRequest(void);
+ static void SetupHardware(void) AUX_BOOT_SECTION;
- bool CALLBACK_MS_Device_SCSICommandReceived(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo);
+ void Application_Jump_Check(void) ATTR_INIT_SECTION(3);
+
+ void EVENT_USB_Device_Connect(void) AUX_BOOT_SECTION;
+ void EVENT_USB_Device_Disconnect(void) AUX_BOOT_SECTION;
+ void EVENT_USB_Device_ConfigurationChanged(void) AUX_BOOT_SECTION;
+ void EVENT_USB_Device_ControlRequest(void) AUX_BOOT_SECTION;
+
+ bool CALLBACK_MS_Device_SCSICommandReceived(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) AUX_BOOT_SECTION;
#endif
*
* \li Series 7 USB AVRs (AT90USBxxx7)
* \li Series 6 USB AVRs (AT90USBxxx6)
+ * \li Series 4 USB AVRs (ATMEGAxxU4) - <i>See \ref SSec_Aux_Space</i>
+ * \li Series 2 USB AVRs (AT90USBxx2, ATMEGAxxU2) - <i>See \ref SSec_Aux_Space</i>
*
* \section Sec_Info USB Information:
*
* firmware image file, to load firmware onto the AVR.
*
* Out of the box this bootloader builds for the AT90USB1287 with an 8KB bootloader section size, and will fit
- * into 8KB of bootloader space. If you wish to alter this size and/or change the AVR model, you will need to
+ * into 6KB of bootloader space. If you wish to alter this size and/or change the AVR model, you will need to
* edit the MCU, FLASH_SIZE_KB and BOOT_SECTION_SIZE_KB values in the accompanying makefile.
*
* When the bootloader is running, the board's LED(s) will flash at regular intervals to distinguish the
* #define BOOTLOADER_ADDRESS_LENGTH 4
* \endcode
*
+ * From the application the API support of the bootloader can be detected by reading the FLASH memory bytes located at address
+ * \c BOOTLOADER_MAGIC_SIGNATURE_START and comparing them to the value \c BOOTLOADER_MAGIC_SIGNATURE. The class of bootloader
+ * can be determined by reading the FLASH memory bytes located at address \c BOOTLOADER_CLASS_SIGNATURE_START and comparing them
+ * to the value \c BOOTLOADER_MASS_STORAGE_SIGNATURE. The start address of the bootloader can be retrieved by reading the bytes
+ * of FLASH memory starting from address \c BOOTLOADER_ADDRESS_START.
+ *
+ * \subsection SSec_Aux_Space Auxiliary Bootloader Section
+ * To make the bootloader function on smaller devices (those with a physical bootloader section of smaller than 6KB) a second
+ * section of memory (called the <i>Auxiliary Bootloader Section</i>) is added before the start of the real bootloader section,
+ * and is filled with a portion of the bootloader code. This allows smaller devices to run the bootloader, at the cost of an
+ * additional portion of the device's FLASH (the bootloader section size in KB subtracted from the 6KB total size). A small
+ * trampoline is inserted at the start of the auxiliary section so that the bootloader will run normally in the case of a blank
+ * application section.
+ *
+ * On devices supporting a 8KB bootloader section size, the AUX section is not created in the final binary.
+ *
* \subsection SSec_API_MemLayout Device Memory Map
* The following illustration indicates the final memory map of the device when loaded with the bootloader.
*
* | |
* | |
* | |
+ * | |
+ * +----------------------------+ FLASHEND - BOOT_SECTION_SIZE - BOOT_AUX_SECTION_SIZE
+ * | Booloader Start Trampoline |
+ * | (Not User App. Accessible) |
+ * +----------------------------+ FLASHEND - BOOT_SECTION_SIZE - BOOT_AUX_SECTION_SIZE + 4
+ * | |
+ * | Auxiliary Bootloader |
+ * | Space for Smaller Devices |
+ * | (Not User App. Accessible) |
+ * | |
* +----------------------------+ FLASHEND - BOOT_SECTION_SIZE
* | |
* | Bootloader Application |
* +----------------------------+ FLASHEND
* \endverbatim
*
- * Bootloaders reporting a device release revision number of 1.00 or greater are bootloader API enabled. From the application
- * the API support of the bootloader can be detected by reading the FLASH memory bytes located at address \c BOOTLOADER_MAGIC_SIGNATURE_START
- * and comparing them to the value \c BOOTLOADER_MAGIC_SIGNATURE. The class of bootloader can be determined by reading the
- * FLASH memory bytes located at address \c BOOTLOADER_CLASS_SIGNATURE_START and comparing them to the value \c BOOTLOADER_MASS_STORAGE_SIGNATURE.
- * The start address of the bootloader can be retrieved by reading the bytes of FLASH memory starting from address \c BOOTLOADER_ADDRESS_START.
- *
* \section Sec_Options Project Options
*
* The following defines can be found in this demo, which can control the demo behaviour when defined, or changed in value.
#define USE_RAM_DESCRIPTORS
// #define USE_FLASH_DESCRIPTORS
// #define USE_EEPROM_DESCRIPTORS
-// #define NO_INTERNAL_SERIAL
+ #define NO_INTERNAL_SERIAL
#define FIXED_CONTROL_ENDPOINT_SIZE 8
#define DEVICE_STATE_AS_GPIOR 0
#define FIXED_NUM_CONFIGURATIONS 1
.ManufacturerStrIndex = NO_DESCRIPTOR,
.ProductStrIndex = NO_DESCRIPTOR,
- .SerialNumStrIndex = USE_INTERNAL_SERIAL,
+ .SerialNumStrIndex = NO_DESCRIPTOR,
.NumberOfConfigurations = FIXED_NUM_CONFIGURATIONS
};
}
};
-/** Language descriptor structure. This descriptor, located in FLASH memory, is returned when the host requests
- * the string descriptor with index 0 (the first index). It is actually an array of 16-bit integers, which indicate
- * via the language ID table available at USB.org what languages the device supports for its string descriptors.
- */
-const USB_Descriptor_String_t LanguageString =
-{
- .Header = {.Size = USB_STRING_LEN(1), .Type = DTYPE_String},
-
- .UnicodeString = {LANGUAGE_ID_ENG}
-};
-
/** This function is called by the library when in device mode, and must be overridden (see library "USB Descriptors"
* documentation) by the application code so that the address and size of a requested descriptor can be given
* to the USB library. When the device receives a Get Descriptor request on the control endpoint, this function
const uint8_t wIndex,
const void** const DescriptorAddress)
{
- const uint8_t DescriptorType = (wValue >> 8);
- const uint8_t DescriptorNumber = (wValue & 0xFF);
+ const uint8_t DescriptorType = (wValue >> 8);
const void* Address = NULL;
uint16_t Size = NO_DESCRIPTOR;
- switch (DescriptorType)
+ /* If/Else If chain compiles slightly smaller than a switch case */
+ if (DescriptorType == DTYPE_Device)
+ {
+ Address = &DeviceDescriptor;
+ Size = sizeof(USB_Descriptor_Device_t);
+ }
+ else if (DescriptorType == DTYPE_Configuration)
{
- case DTYPE_Device:
- Address = &DeviceDescriptor;
- Size = sizeof(USB_Descriptor_Device_t);
- break;
- case DTYPE_Configuration:
- Address = &ConfigurationDescriptor;
- Size = sizeof(USB_Descriptor_Configuration_t);
- break;
- case DTYPE_String:
- if (!(DescriptorNumber))
- {
- Address = &LanguageString;
- Size = pgm_read_byte(&LanguageString.Header.Size);
- }
-
- break;
+ Address = &ConfigurationDescriptor;
+ Size = sizeof(USB_Descriptor_Configuration_t);
}
*DescriptorAddress = Address;
uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue,
const uint8_t wIndex,
const void** const DescriptorAddress)
- ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(3);
+ ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(3) AUX_BOOT_SECTION;
#endif
case SCSI_CMD_READ_CAPACITY_10:
CommandSuccess = SCSI_Command_Read_Capacity_10(MSInterfaceInfo);
break;
- case SCSI_CMD_SEND_DIAGNOSTIC:
- CommandSuccess = SCSI_Command_Send_Diagnostic(MSInterfaceInfo);
- break;
case SCSI_CMD_WRITE_10:
CommandSuccess = SCSI_Command_ReadWrite_10(MSInterfaceInfo, DATA_WRITE);
break;
case SCSI_CMD_MODE_SENSE_6:
CommandSuccess = SCSI_Command_ModeSense_6(MSInterfaceInfo);
break;
+ case SCSI_CMD_SEND_DIAGNOSTIC:
case SCSI_CMD_START_STOP_UNIT:
case SCSI_CMD_TEST_UNIT_READY:
case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
return true;
}
-/** Command processing for an issued SCSI SEND DIAGNOSTIC command. This command performs a quick check of the Dataflash ICs on the
- * board, and indicates if they are present and functioning correctly. Only the Self-Test portion of the diagnostic command is
- * supported.
- *
- * \param[in] MSInterfaceInfo Pointer to the Mass Storage class interface structure that the command is associated with
- *
- * \return Boolean true if the command completed successfully, false otherwise.
- */
-static bool SCSI_Command_Send_Diagnostic(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo)
-{
- /* Check to see if the SELF TEST bit is not set */
- if (!(MSInterfaceInfo->State.CommandBlock.SCSICommandData[1] & (1 << 2)))
- {
- /* Only self-test supported - update SENSE key and fail the command */
- SCSI_SET_SENSE(SCSI_SENSE_KEY_ILLEGAL_REQUEST,
- SCSI_ASENSE_INVALID_FIELD_IN_CDB,
- SCSI_ASENSEQ_NO_QUALIFIER);
-
- return false;
- }
-
- /* Succeed the command and update the bytes transferred counter */
- MSInterfaceInfo->State.CommandBlock.DataTransferLength = 0;
-
- return true;
-}
-
/** Command processing for an issued SCSI READ (10) or WRITE (10) command. This command reads in the block start address
* and total number of blocks to process, then calls the appropriate low-level Dataflash routine to handle the actual
* reading and writing of the data.
#include <LUFA/Drivers/USB/USB.h>
- #include "../BootloaderMassStorage.h"
#include "../Descriptors.h"
#include "VirtualFAT.h"
#define DEVICE_TYPE_BLOCK 0x00
/* Function Prototypes: */
- bool SCSI_DecodeSCSICommand(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo);
+ bool SCSI_DecodeSCSICommand(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) AUX_BOOT_SECTION;
#if defined(INCLUDE_FROM_SCSI_C)
- static bool SCSI_Command_Inquiry(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo);
- static bool SCSI_Command_Request_Sense(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo);
- static bool SCSI_Command_Read_Capacity_10(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo);
- static bool SCSI_Command_Send_Diagnostic(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo);
+ static bool SCSI_Command_Inquiry(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) AUX_BOOT_SECTION;
+ static bool SCSI_Command_Request_Sense(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) AUX_BOOT_SECTION;
+ static bool SCSI_Command_Read_Capacity_10(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) AUX_BOOT_SECTION;
static bool SCSI_Command_ReadWrite_10(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo,
- const bool IsDataRead);
- static bool SCSI_Command_ModeSense_6(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo);
+ const bool IsDataRead) AUX_BOOT_SECTION;
+ static bool SCSI_Command_ModeSense_6(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo) AUX_BOOT_SECTION;
#endif
#endif
if ((BlockNumber >= 4) && (BlockNumber < (4 + FILE_SECTORS(FIRMWARE_FILE_SIZE_BYTES))))
{
+ #if (FLASHEND > 0xFFFF)
uint32_t WriteFlashAddress = (uint32_t)(BlockNumber - 4) * SECTOR_SIZE_BYTES;
+ #else
+ uint16_t WriteFlashAddress = (uint16_t)(BlockNumber - 4) * SECTOR_SIZE_BYTES;
+ #endif
for (uint16_t i = 0; i < SECTOR_SIZE_BYTES; i += 2)
{
if ((WriteFlashAddress % SPM_PAGESIZE) == 0)
{
/* Erase the given FLASH page, ready to be programmed */
- boot_page_erase(WriteFlashAddress);
- boot_spm_busy_wait();
+ BootloaderAPI_ErasePage(WriteFlashAddress);
}
/* Write the next data word to the FLASH page */
- boot_page_fill(WriteFlashAddress, (BlockBuffer[i + 1] << 8) | BlockBuffer[i]);
+ BootloaderAPI_FillWord(WriteFlashAddress, (BlockBuffer[i + 1] << 8) | BlockBuffer[i]);
WriteFlashAddress += 2;
if ((WriteFlashAddress % SPM_PAGESIZE) == 0)
{
/* Write the filled FLASH page to memory */
- boot_page_write(WriteFlashAddress - SPM_PAGESIZE);
- boot_spm_busy_wait();
+ BootloaderAPI_WritePage(WriteFlashAddress - SPM_PAGESIZE);
}
}
}
default: /* Blocks 4 onwards: Data allocation section */
if ((BlockNumber >= 4) && (BlockNumber < (4 + FILE_SECTORS(FIRMWARE_FILE_SIZE_BYTES))))
{
+ #if (FLASHEND > 0xFFFF)
uint32_t ReadFlashAddress = (uint32_t)(BlockNumber - 4) * SECTOR_SIZE_BYTES;
for (uint16_t i = 0; i < SECTOR_SIZE_BYTES; i++)
BlockBuffer[i] = pgm_read_byte_far(ReadFlashAddress++);
+ #else
+ uint16_t ReadFlashAddress = (uint16_t)(BlockNumber - 4) * SECTOR_SIZE_BYTES;
+
+ for (uint16_t i = 0; i < SECTOR_SIZE_BYTES; i++)
+ BlockBuffer[i] = pgm_read_byte(ReadFlashAddress++);
+ #endif
}
break;
* PC via the USB Mass Storage interface.
*
* \param[in] MSInterfaceInfo Pointer to a structure containing a Mass Storage Class configuration and state
- * \param[in] BlockAddress Data block starting address for the write sequence
- * \param[in] TotalBlocks Number of blocks of data to write
+ * \param[in] BlockAddress Data block starting address for the write sequence
+ * \param[in] TotalBlocks Number of blocks of data to write
*/
void VirtualFAT_WriteBlocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo,
const uint32_t BlockAddress,
* to the host PC via the USB Mass Storage interface.
*
* \param[in] MSInterfaceInfo Pointer to a structure containing a Mass Storage Class configuration and state
- * \param[in] BlockAddress Data block starting address for the read sequence
- * \param[in] TotalBlocks Number of blocks of data to read
+ * \param[in] BlockAddress Data block starting address for the read sequence
+ * \param[in] TotalBlocks Number of blocks of data to read
*/
void VirtualFAT_ReadBlocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo,
const uint32_t BlockAddress,
#include <LUFA/Drivers/USB/USB.h>
+ #include "../BootloaderAPI.h"
+
/* Macros: */
/** Size of the virtual FIRMWARE.BIN file in bytes. */
- #define FIRMWARE_FILE_SIZE_BYTES (FLASHEND - (FLASHEND - BOOT_START_ADDR))
+ #define FIRMWARE_FILE_SIZE_BYTES (FLASHEND - (FLASHEND - BOOT_START_ADDR) - AUX_BOOT_SECTION_SIZE)
/** Number of sectors that comprise a single logical disk cluster. */
#define SECTOR_PER_CLUSTER 4
#if defined(INCLUDE_FROM_VIRTUAL_FAT_C)
static void UpdateFAT12ClusterEntry(uint8_t* const FATTable,
const uint16_t Index,
- const uint16_t ChainEntry);
- static void WriteVirtualBlock(const uint16_t BlockNumber);
- static void ReadVirtualBlock(const uint16_t BlockNumber);
+ const uint16_t ChainEntry) AUX_BOOT_SECTION;
+ static void WriteVirtualBlock(const uint16_t BlockNumber) AUX_BOOT_SECTION;
+ static void ReadVirtualBlock(const uint16_t BlockNumber) AUX_BOOT_SECTION;
#endif
void VirtualFAT_WriteBlocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo,
const uint32_t BlockAddress,
- uint16_t TotalBlocks);
+ uint16_t TotalBlocks) AUX_BOOT_SECTION;
void VirtualFAT_ReadBlocks(USB_ClassInfo_MS_Device_t* const MSInterfaceInfo,
const uint32_t BlockAddress,
- uint16_t TotalBlocks);
+ uint16_t TotalBlocks) AUX_BOOT_SECTION;
#endif
# match the target's total FLASH size and the bootloader size set in the\r
# device's fuses.\r
FLASH_SIZE_KB = 128\r
-BOOT_SECTION_SIZE_KB = 8\r
+BOOT_SECTION_SIZE_KB = 4\r
\r
# Bootloader address calculation formulas\r
# Do not modify these macros, but rather modify the dependent values above.\r
CALC_ADDRESS_IN_HEX = $(shell printf "0x%X" $$(( $(1) )) )\r
BOOT_START_OFFSET = $(call CALC_ADDRESS_IN_HEX, ($(FLASH_SIZE_KB) - $(BOOT_SECTION_SIZE_KB)) * 1024 )\r
-BOOT_SEC_OFFSET = $(call CALC_ADDRESS_IN_HEX, ($(FLASH_SIZE_KB) * 1024) - $(strip $(1)) )\r
+BOOT_SEC_OFFSET = $(call CALC_ADDRESS_IN_HEX, ($(FLASH_SIZE_KB) * 1024) - ($(strip $(1))) )\r
\r
# Bootloader linker section flags for relocating the API table sections to\r
# known FLASH addresses - these should not normally be user-edited.\r
-BOOT_SECTION_LD_FLAG = -Wl,--section-start=.apitable_$(strip $(1))=$(call BOOT_SEC_OFFSET, $(3)) -Wl,--undefined=BootloaderAPI_$(strip $(2))\r
-BOOT_API_LD_FLAGS = $(call BOOT_SECTION_LD_FLAG, trampolines, Trampolines, 96)\r
-BOOT_API_LD_FLAGS += $(call BOOT_SECTION_LD_FLAG, jumptable, JumpTable, 32)\r
-BOOT_API_LD_FLAGS += $(call BOOT_SECTION_LD_FLAG, signatures, Signatures, 8)\r
+BOOT_SECTION_LD_FLAG = -Wl,--section-start=$(strip $(1))=$(call BOOT_SEC_OFFSET, $(3)) -Wl,--undefined=$(strip $(2))\r
+BOOT_API_LD_FLAGS = $(call BOOT_SECTION_LD_FLAG, .apitable_trampolines, BootloaderAPI_Trampolines, 96)\r
+BOOT_API_LD_FLAGS += $(call BOOT_SECTION_LD_FLAG, .apitable_jumptable, BootloaderAPI_JumpTable, 32)\r
+BOOT_API_LD_FLAGS += $(call BOOT_SECTION_LD_FLAG, .apitable_signatures, BootloaderAPI_Signatures, 8)\r
+\r
+# Check if the bootloader needs an AUX section, located before the real bootloader section to store some of the\r
+# bootloader code. This is required for 32KB and smaller devices, where the actual bootloader is 6KB but the maximum\r
+# bootloader section size is 4KB. The actual usable application space will be reduced by 6KB for these devices.\r
+ifeq ($(BOOT_SECTION_SIZE_KB),8)\r
+ CC_FLAGS += -DAUX_BOOT_SECTION='' -DAUX_BOOT_SECTION_SIZE=0\r
+else\r
+ AUX_BOOT_SECTION_KB = (6 - $(BOOT_SECTION_SIZE_KB))\r
+\r
+ CC_FLAGS += -DAUX_BOOT_SECTION='__attribute__((section(".boot_aux")))' -DAUX_BOOT_SECTION_SIZE='($(AUX_BOOT_SECTION_KB) * 1024)'\r
+ LD_FLAGS += -Wl,--section-start=.boot_aux=$(call BOOT_SEC_OFFSET, (($(BOOT_SECTION_SIZE_KB) + $(AUX_BOOT_SECTION_KB)) * 1024 - 16))\r
+ LD_FLAGS += $(call BOOT_SECTION_LD_FLAG, .boot_aux_trampoline, Boot_AUX_Trampoline, ($(BOOT_SECTION_SIZE_KB) + $(AUX_BOOT_SECTION_KB)) * 1024)\r
+endif\r
\r
# Default target\r
all:\r