Commit 06f53eed authored by Dean Camera's avatar Dean Camera
Browse files

Fixed bootloaders not disabling global interrupts during erase and write...

Fixed bootloaders not disabling global interrupts during erase and write operations (thanks to Zoltan).
parent 544c4dc9
...@@ -37,16 +37,22 @@ ...@@ -37,16 +37,22 @@
void BootloaderAPI_ErasePage(const uint32_t Address) void BootloaderAPI_ErasePage(const uint32_t Address)
{ {
boot_page_erase_safe(Address); ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
boot_spm_busy_wait(); {
boot_rww_enable(); boot_page_erase_safe(Address);
boot_spm_busy_wait();
boot_rww_enable();
}
} }
void BootloaderAPI_WritePage(const uint32_t Address) void BootloaderAPI_WritePage(const uint32_t Address)
{ {
boot_page_write_safe(Address); ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
boot_spm_busy_wait(); {
boot_rww_enable(); boot_page_write_safe(Address);
boot_spm_busy_wait();
boot_rww_enable();
}
} }
void BootloaderAPI_FillWord(const uint32_t Address, const uint16_t Word) void BootloaderAPI_FillWord(const uint32_t Address, const uint16_t Word)
...@@ -71,5 +77,8 @@ uint8_t BootloaderAPI_ReadLock(void) ...@@ -71,5 +77,8 @@ uint8_t BootloaderAPI_ReadLock(void)
void BootloaderAPI_WriteLock(const uint8_t LockBits) void BootloaderAPI_WriteLock(const uint8_t LockBits)
{ {
boot_lock_bits_set_safe(LockBits); ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
boot_lock_bits_set_safe(LockBits);
}
} }
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
/* Includes: */ /* Includes: */
#include <avr/io.h> #include <avr/io.h>
#include <avr/boot.h> #include <avr/boot.h>
#include <util/atomic.h>
#include <stdbool.h> #include <stdbool.h>
#include <LUFA/Common/Common.h> #include <LUFA/Common/Common.h>
......
...@@ -97,7 +97,7 @@ void Application_Jump_Check(void) ...@@ -97,7 +97,7 @@ void Application_Jump_Check(void)
JTAG_ENABLE(); JTAG_ENABLE();
#else #else
/* Check if the device's BOOTRST fuse is set */ /* Check if the device's BOOTRST fuse is set */
if (boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS) & FUSE_BOOTRST) if (BootloaderAPI_ReadFuse(GET_HIGH_FUSE_BITS) & FUSE_BOOTRST)
{ {
/* If the reset source was not an external reset or the key is correct, clear it and jump to the application */ /* If 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)) if (!(MCUSR & (1 << EXTRF)) || (MagicBootKey == MAGIC_BOOT_KEY))
...@@ -297,9 +297,6 @@ static void ReadWriteMemoryBlock(const uint8_t Command) ...@@ -297,9 +297,6 @@ static void ReadWriteMemoryBlock(const uint8_t Command)
/* Check if command is to read a memory block */ /* Check if command is to read a memory block */
if (Command == AVR109_COMMAND_BlockRead) if (Command == AVR109_COMMAND_BlockRead)
{ {
/* Re-enable RWW section */
boot_rww_enable();
while (BlockSize--) while (BlockSize--)
{ {
if (MemoryType == MEMORY_TYPE_FLASH) if (MemoryType == MEMORY_TYPE_FLASH)
...@@ -332,10 +329,7 @@ static void ReadWriteMemoryBlock(const uint8_t Command) ...@@ -332,10 +329,7 @@ static void ReadWriteMemoryBlock(const uint8_t Command)
uint32_t PageStartAddress = CurrAddress; uint32_t PageStartAddress = CurrAddress;
if (MemoryType == MEMORY_TYPE_FLASH) if (MemoryType == MEMORY_TYPE_FLASH)
{ BootloaderAPI_ErasePage(PageStartAddress);
boot_page_erase(PageStartAddress);
boot_spm_busy_wait();
}
while (BlockSize--) while (BlockSize--)
{ {
...@@ -345,7 +339,7 @@ static void ReadWriteMemoryBlock(const uint8_t Command) ...@@ -345,7 +339,7 @@ static void ReadWriteMemoryBlock(const uint8_t Command)
if (HighByte) if (HighByte)
{ {
/* Write the next FLASH word to the current FLASH page */ /* Write the next FLASH word to the current FLASH page */
boot_page_fill(CurrAddress, ((FetchNextCommandByte() << 8) | LowByte)); BootloaderAPI_FillWord(CurrAddress, ((FetchNextCommandByte() << 8) | LowByte));
/* Increment the address counter after use */ /* Increment the address counter after use */
CurrAddress += 2; CurrAddress += 2;
...@@ -371,10 +365,7 @@ static void ReadWriteMemoryBlock(const uint8_t Command) ...@@ -371,10 +365,7 @@ static void ReadWriteMemoryBlock(const uint8_t Command)
if (MemoryType == MEMORY_TYPE_FLASH) if (MemoryType == MEMORY_TYPE_FLASH)
{ {
/* Commit the flash page to memory */ /* Commit the flash page to memory */
boot_page_write(PageStartAddress); BootloaderAPI_WritePage(PageStartAddress);
/* Wait until write operation has completed */
boot_spm_busy_wait();
} }
/* Send response byte back to the host */ /* Send response byte back to the host */
...@@ -516,12 +507,7 @@ static void CDC_Task(void) ...@@ -516,12 +507,7 @@ static void CDC_Task(void)
{ {
/* Clear the application section of flash */ /* Clear the application section of flash */
for (uint32_t CurrFlashAddress = 0; CurrFlashAddress < (uint32_t)BOOT_START_ADDR; CurrFlashAddress += SPM_PAGESIZE) for (uint32_t CurrFlashAddress = 0; CurrFlashAddress < (uint32_t)BOOT_START_ADDR; CurrFlashAddress += SPM_PAGESIZE)
{ BootloaderAPI_ErasePage(CurrFlashAddress);
boot_page_erase(CurrFlashAddress);
boot_spm_busy_wait();
boot_page_write(CurrFlashAddress);
boot_spm_busy_wait();
}
/* Send confirmation byte back to the host */ /* Send confirmation byte back to the host */
WriteNextResponseByte('\r'); WriteNextResponseByte('\r');
...@@ -530,7 +516,7 @@ static void CDC_Task(void) ...@@ -530,7 +516,7 @@ static void CDC_Task(void)
else if (Command == AVR109_COMMAND_WriteLockbits) else if (Command == AVR109_COMMAND_WriteLockbits)
{ {
/* Set the lock bits to those given by the host */ /* Set the lock bits to those given by the host */
boot_lock_bits_set(FetchNextCommandByte()); BootloaderAPI_WriteLock(FetchNextCommandByte());
/* Send confirmation byte back to the host */ /* Send confirmation byte back to the host */
WriteNextResponseByte('\r'); WriteNextResponseByte('\r');
...@@ -538,19 +524,19 @@ static void CDC_Task(void) ...@@ -538,19 +524,19 @@ static void CDC_Task(void)
#endif #endif
else if (Command == AVR109_COMMAND_ReadLockbits) else if (Command == AVR109_COMMAND_ReadLockbits)
{ {
WriteNextResponseByte(boot_lock_fuse_bits_get(GET_LOCK_BITS)); WriteNextResponseByte(BootloaderAPI_ReadLock());
} }
else if (Command == AVR109_COMMAND_ReadLowFuses) else if (Command == AVR109_COMMAND_ReadLowFuses)
{ {
WriteNextResponseByte(boot_lock_fuse_bits_get(GET_LOW_FUSE_BITS)); WriteNextResponseByte(BootloaderAPI_ReadFuse(GET_LOW_FUSE_BITS));
} }
else if (Command == AVR109_COMMAND_ReadHighFuses) else if (Command == AVR109_COMMAND_ReadHighFuses)
{ {
WriteNextResponseByte(boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS)); WriteNextResponseByte(BootloaderAPI_ReadFuse(GET_HIGH_FUSE_BITS));
} }
else if (Command == AVR109_COMMAND_ReadExtendedFuses) else if (Command == AVR109_COMMAND_ReadExtendedFuses)
{ {
WriteNextResponseByte(boot_lock_fuse_bits_get(GET_EXTENDED_FUSE_BITS)); WriteNextResponseByte(BootloaderAPI_ReadFuse(GET_EXTENDED_FUSE_BITS));
} }
#if !defined(NO_BLOCK_SUPPORT) #if !defined(NO_BLOCK_SUPPORT)
else if (Command == AVR109_COMMAND_GetBlockWriteSupport) else if (Command == AVR109_COMMAND_GetBlockWriteSupport)
...@@ -571,7 +557,7 @@ static void CDC_Task(void) ...@@ -571,7 +557,7 @@ static void CDC_Task(void)
else if (Command == AVR109_COMMAND_FillFlashPageWordHigh) else if (Command == AVR109_COMMAND_FillFlashPageWordHigh)
{ {
/* Write the high byte to the current flash page */ /* Write the high byte to the current flash page */
boot_page_fill(CurrAddress, FetchNextCommandByte()); BootloaderAPI_FillWord(CurrAddress, FetchNextCommandByte());
/* Send confirmation byte back to the host */ /* Send confirmation byte back to the host */
WriteNextResponseByte('\r'); WriteNextResponseByte('\r');
...@@ -579,7 +565,7 @@ static void CDC_Task(void) ...@@ -579,7 +565,7 @@ static void CDC_Task(void)
else if (Command == AVR109_COMMAND_FillFlashPageWordLow) else if (Command == AVR109_COMMAND_FillFlashPageWordLow)
{ {
/* Write the low byte to the current flash page */ /* Write the low byte to the current flash page */
boot_page_fill(CurrAddress | 0x01, FetchNextCommandByte()); BootloaderAPI_FillWord(CurrAddress | 0x01, FetchNextCommandByte());
/* Increment the address */ /* Increment the address */
CurrAddress += 2; CurrAddress += 2;
...@@ -590,10 +576,7 @@ static void CDC_Task(void) ...@@ -590,10 +576,7 @@ static void CDC_Task(void)
else if (Command == AVR109_COMMAND_WriteFlashPage) else if (Command == AVR109_COMMAND_WriteFlashPage)
{ {
/* Commit the flash page to memory */ /* Commit the flash page to memory */
boot_page_write(CurrAddress); BootloaderAPI_WritePage(CurrAddress);
/* Wait until write operation has completed */
boot_spm_busy_wait();
/* Send confirmation byte back to the host */ /* Send confirmation byte back to the host */
WriteNextResponseByte('\r'); WriteNextResponseByte('\r');
......
...@@ -37,16 +37,22 @@ ...@@ -37,16 +37,22 @@
void BootloaderAPI_ErasePage(const uint32_t Address) void BootloaderAPI_ErasePage(const uint32_t Address)
{ {
boot_page_erase_safe(Address); ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
boot_spm_busy_wait(); {
boot_rww_enable(); boot_page_erase_safe(Address);
boot_spm_busy_wait();
boot_rww_enable();
}
} }
void BootloaderAPI_WritePage(const uint32_t Address) void BootloaderAPI_WritePage(const uint32_t Address)
{ {
boot_page_write_safe(Address); ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
boot_spm_busy_wait(); {
boot_rww_enable(); boot_page_write_safe(Address);
boot_spm_busy_wait();
boot_rww_enable();
}
} }
void BootloaderAPI_FillWord(const uint32_t Address, const uint16_t Word) void BootloaderAPI_FillWord(const uint32_t Address, const uint16_t Word)
...@@ -71,6 +77,8 @@ uint8_t BootloaderAPI_ReadLock(void) ...@@ -71,6 +77,8 @@ uint8_t BootloaderAPI_ReadLock(void)
void BootloaderAPI_WriteLock(const uint8_t LockBits) void BootloaderAPI_WriteLock(const uint8_t LockBits)
{ {
boot_lock_bits_set_safe(LockBits); ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
boot_lock_bits_set_safe(LockBits);
}
} }
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
/* Includes: */ /* Includes: */
#include <avr/io.h> #include <avr/io.h>
#include <avr/boot.h> #include <avr/boot.h>
#include <util/atomic.h>
#include <stdbool.h> #include <stdbool.h>
#include <LUFA/Common/Common.h> #include <LUFA/Common/Common.h>
......
...@@ -133,7 +133,7 @@ void Application_Jump_Check(void) ...@@ -133,7 +133,7 @@ void Application_Jump_Check(void)
JTAG_ENABLE(); JTAG_ENABLE();
#else #else
/* Check if the device's BOOTRST fuse is set */ /* Check if the device's BOOTRST fuse is set */
if (boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS) & FUSE_BOOTRST) if (BootloaderAPI_ReadFuse(GET_HIGH_FUSE_BITS) & FUSE_BOOTRST)
{ {
/* If the reset source was not an external reset or the key is correct, clear it and jump to the application */ /* If 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)) if (!(MCUSR & (1 << EXTRF)) || (MagicBootKey == MAGIC_BOOT_KEY))
...@@ -351,7 +351,7 @@ void EVENT_USB_Device_ControlRequest(void) ...@@ -351,7 +351,7 @@ void EVENT_USB_Device_ControlRequest(void)
} }
/* Write the next word into the current flash page */ /* Write the next word into the current flash page */
boot_page_fill(CurrFlashAddress.Long, Endpoint_Read_16_LE()); BootloaderAPI_FillWord(CurrFlashAddress.Long, Endpoint_Read_16_LE());
/* Adjust counters */ /* Adjust counters */
WordsInFlashPage += 1; WordsInFlashPage += 1;
...@@ -361,8 +361,7 @@ void EVENT_USB_Device_ControlRequest(void) ...@@ -361,8 +361,7 @@ void EVENT_USB_Device_ControlRequest(void)
if ((WordsInFlashPage == (SPM_PAGESIZE >> 1)) || !(WordsRemaining)) if ((WordsInFlashPage == (SPM_PAGESIZE >> 1)) || !(WordsRemaining))
{ {
/* Commit the flash page to memory */ /* Commit the flash page to memory */
boot_page_write(CurrFlashPageStartAddress); BootloaderAPI_WritePage(CurrFlashPageStartAddress);
boot_spm_busy_wait();
/* Check if programming incomplete */ /* Check if programming incomplete */
if (WordsRemaining) if (WordsRemaining)
...@@ -371,17 +370,13 @@ void EVENT_USB_Device_ControlRequest(void) ...@@ -371,17 +370,13 @@ void EVENT_USB_Device_ControlRequest(void)
WordsInFlashPage = 0; WordsInFlashPage = 0;
/* Erase next page's temp buffer */ /* Erase next page's temp buffer */
boot_page_erase(CurrFlashAddress.Long); BootloaderAPI_ErasePage(CurrFlashAddress.Long);
boot_spm_busy_wait();
} }
} }
} }
/* Once programming complete, start address equals the end address */ /* Once programming complete, start address equals the end address */
StartAddr = EndAddr; StartAddr = EndAddr;
/* Re-enable the RWW section of flash */
boot_rww_enable();
} }
else // Write EEPROM else // Write EEPROM
{ {
...@@ -691,8 +686,7 @@ static void ProcessMemProgCommand(void) ...@@ -691,8 +686,7 @@ static void ProcessMemProgCommand(void)
} CurrFlashAddress = {.Words = {StartAddr, Flash64KBPage}}; } CurrFlashAddress = {.Words = {StartAddr, Flash64KBPage}};
/* Erase the current page's temp buffer */ /* Erase the current page's temp buffer */
boot_page_erase(CurrFlashAddress.Long); BootloaderAPI_ErasePage(CurrFlashAddress.Long);
boot_spm_busy_wait();
} }
/* Set the state so that the next DNLOAD requests reads in the firmware */ /* Set the state so that the next DNLOAD requests reads in the firmware */
...@@ -789,21 +783,9 @@ static void ProcessWriteCommand(void) ...@@ -789,21 +783,9 @@ static void ProcessWriteCommand(void)
} }
else if (IS_TWOBYTE_COMMAND(SentCommand.Data, 0x00, 0xFF)) // Erase flash else if (IS_TWOBYTE_COMMAND(SentCommand.Data, 0x00, 0xFF)) // Erase flash
{ {
uint32_t CurrFlashAddress = 0;
/* Clear the application section of flash */ /* Clear the application section of flash */
while (CurrFlashAddress < (uint32_t)BOOT_START_ADDR) for (uint32_t CurrFlashAddress = 0; CurrFlashAddress < (uint32_t)BOOT_START_ADDR; CurrFlashAddress += SPM_PAGESIZE)
{ BootloaderAPI_ErasePage(CurrFlashAddress);
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 */ /* Memory has been erased, reset the security bit so that programming/reading is allowed */
IsSecure = false; IsSecure = false;
......
...@@ -164,8 +164,11 @@ void EVENT_USB_Device_ControlRequest(void) ...@@ -164,8 +164,11 @@ void EVENT_USB_Device_ControlRequest(void)
else if (PageAddress < BOOT_START_ADDR) else if (PageAddress < BOOT_START_ADDR)
{ {
/* Erase the given FLASH page, ready to be programmed */ /* Erase the given FLASH page, ready to be programmed */
boot_page_erase(PageAddress); ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
boot_spm_busy_wait(); {
boot_page_erase(PageAddress);
boot_spm_busy_wait();
}
/* Write each of the FLASH page's bytes in sequence */ /* Write each of the FLASH page's bytes in sequence */
for (uint8_t PageWord = 0; PageWord < (SPM_PAGESIZE / 2); PageWord++) for (uint8_t PageWord = 0; PageWord < (SPM_PAGESIZE / 2); PageWord++)
...@@ -182,8 +185,11 @@ void EVENT_USB_Device_ControlRequest(void) ...@@ -182,8 +185,11 @@ void EVENT_USB_Device_ControlRequest(void)
} }
/* Write the filled FLASH page to memory */ /* Write the filled FLASH page to memory */
boot_page_write(PageAddress); ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
boot_spm_busy_wait(); {
boot_page_write(PageAddress);
boot_spm_busy_wait();
}
/* Re-enable RWW section */ /* Re-enable RWW section */
boot_rww_enable(); boot_rww_enable();
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#include <avr/boot.h> #include <avr/boot.h>
#include <avr/power.h> #include <avr/power.h>
#include <avr/interrupt.h> #include <avr/interrupt.h>
#include <util/atomic.h>
#include <stdbool.h> #include <stdbool.h>
#include "Descriptors.h" #include "Descriptors.h"
......
...@@ -37,16 +37,22 @@ ...@@ -37,16 +37,22 @@
void BootloaderAPI_ErasePage(const uint32_t Address) void BootloaderAPI_ErasePage(const uint32_t Address)
{ {
boot_page_erase_safe(Address); ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
boot_spm_busy_wait(); {
boot_rww_enable(); boot_page_erase_safe(Address);
boot_spm_busy_wait();
boot_rww_enable();
}
} }
void BootloaderAPI_WritePage(const uint32_t Address) void BootloaderAPI_WritePage(const uint32_t Address)
{ {
boot_page_write_safe(Address); ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
boot_spm_busy_wait(); {
boot_rww_enable(); boot_page_write_safe(Address);
boot_spm_busy_wait();
boot_rww_enable();
}
} }
void BootloaderAPI_FillWord(const uint32_t Address, const uint16_t Word) void BootloaderAPI_FillWord(const uint32_t Address, const uint16_t Word)
...@@ -71,6 +77,8 @@ uint8_t BootloaderAPI_ReadLock(void) ...@@ -71,6 +77,8 @@ uint8_t BootloaderAPI_ReadLock(void)
void BootloaderAPI_WriteLock(const uint8_t LockBits) void BootloaderAPI_WriteLock(const uint8_t LockBits)
{ {
boot_lock_bits_set_safe(LockBits); ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
boot_lock_bits_set_safe(LockBits);
}
} }
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
/* Includes: */ /* Includes: */
#include <avr/io.h> #include <avr/io.h>
#include <avr/boot.h> #include <avr/boot.h>
#include <util/atomic.h>
#include <stdbool.h> #include <stdbool.h>
#include <LUFA/Common/Common.h> #include <LUFA/Common/Common.h>
......
...@@ -114,7 +114,7 @@ void Application_Jump_Check(void) ...@@ -114,7 +114,7 @@ void Application_Jump_Check(void)
JTAG_ENABLE(); JTAG_ENABLE();
#else #else
/* Check if the device's BOOTRST fuse is set */ /* Check if the device's BOOTRST fuse is set */
if (boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS) & FUSE_BOOTRST) if (BootloaderAPI_ReadFuse(GET_HIGH_FUSE_BITS) & FUSE_BOOTRST)
{ {
/* If the reset source was not an external reset or the key is correct, clear it and jump to the application */ /* If 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)) if (!(MCUSR & (1 << EXTRF)) || (MagicBootKey == MAGIC_BOOT_KEY))
......
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
#include <string.h> #include <string.h>
#include "Descriptors.h" #include "Descriptors.h"
#include "BootloaderAPI.h"
#include "Config/AppConfig.h" #include "Config/AppConfig.h"
#include "Lib/SCSI.h" #include "Lib/SCSI.h"
......
...@@ -37,16 +37,22 @@ ...@@ -37,16 +37,22 @@
void BootloaderAPI_ErasePage(const uint32_t Address) void BootloaderAPI_ErasePage(const uint32_t Address)
{ {
boot_page_erase_safe(Address); ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
boot_spm_busy_wait(); {
boot_rww_enable(); boot_page_erase_safe(Address);
boot_spm_busy_wait();
boot_rww_enable();
}
} }
void BootloaderAPI_WritePage(const uint32_t Address) void BootloaderAPI_WritePage(const uint32_t Address)
{ {
boot_page_write_safe(Address); ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
boot_spm_busy_wait(); {
boot_rww_enable(); boot_page_write_safe(Address);
boot_spm_busy_wait();
boot_rww_enable();
}
} }
void BootloaderAPI_FillWord(const uint32_t Address, const uint16_t Word) void BootloaderAPI_FillWord(const uint32_t Address, const uint16_t Word)
...@@ -71,5 +77,8 @@ uint8_t BootloaderAPI_ReadLock(void) ...@@ -71,5 +77,8 @@ uint8_t BootloaderAPI_ReadLock(void)
void BootloaderAPI_WriteLock(const uint8_t LockBits) void BootloaderAPI_WriteLock(const uint8_t LockBits)
{ {
boot_lock_bits_set_safe(LockBits); ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
{
boot_lock_bits_set_safe(LockBits);
}
} }
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
/* Includes: */ /* Includes: */
#include <avr/io.h> #include <avr/io.h>
#include <avr/boot.h> #include <avr/boot.h>
#include <util/atomic.h>
#include <stdbool.h> #include <stdbool.h>