chore(radio): unify the bootloader UI code (#6189)

Co-authored-by: Richard Li <richard.li@ces.hk>
This commit is contained in:
philmoz
2025-05-23 11:06:20 +10:00
committed by GitHub
co-authored by Richard Li
parent bb4fb456a6
commit dc0d85d035
53 changed files with 2159 additions and 3957 deletions
+112
View File
@@ -0,0 +1,112 @@
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
AddHardwareDefTarget(${HW_DESC_JSON})
AddHWGenTarget(${HW_DESC_JSON} hal_keys hal_keys.inc)
set(BOOTLOADER_SRC ${BOOTLOADER_SRC}
${CMAKE_CURRENT_BINARY_DIR}/hal_keys.inc
${RADIO_SRC_DIR}/os/time_nortos.cpp
${RADIO_SRC_DIR}/os/sleep_nortos.cpp
${RADIO_SRC_DIR}/keys.cpp
${RADIO_SRC_DIR}/strhelpers.cpp
${RADIO_SRC_DIR}/stamp.cpp
${RADIO_SRC_DIR}/syscalls.c
${RADIO_SRC_DIR}/crc.cpp
boot.cpp
)
if(FIRMWARE_FORMAT_UF2)
set(BOOTLOADER_SRC ${BOOTLOADER_SRC}
boot_uf2.cpp
boot_dfu.cpp
${RADIO_SRC_DIR}/io/uf2.cpp
${RADIO_SRC_DIR}/drivers/uf2_ghostfat.cpp
)
else()
set(BOOTLOADER_SRC ${BOOTLOADER_SRC}
boot_menu.cpp
bin_fw_files.cpp
)
endif()
if(DEBUG_SEGGER_RTT)
set(BOOTLOADER_SRC ${BOOTLOADER_SRC}
${RADIO_SRC_DIR}/thirdparty/Segger_RTT/RTT/SEGGER_RTT.c
)
endif()
# TODO: this shouldn't be needed at all
remove_definitions(-DDISK_CACHE)
remove_definitions(-DLUA)
remove_definitions(-DCLI)
remove_definitions(-DSEMIHOSTING)
remove_definitions(-DUSB_SERIAL)
if(NOT NO_LTO)
message("-- Use LTO to compile bootloader")
target_compile_options(stm32cube_ll PRIVATE -flto)
target_compile_options(stm32usb PRIVATE -flto)
target_compile_options(stm32usb_device_bl PRIVATE -flto)
target_compile_options(stm32usb_device_fw PRIVATE -flto)
target_compile_options(stm32_drivers PRIVATE -flto)
target_compile_options(stm32_drivers_w_dbg_fw PRIVATE -flto)
target_compile_options(stm32_drivers_w_dbg_bl PRIVATE -flto)
endif()
add_executable(bootloader EXCLUDE_FROM_ALL ${BOOTLOADER_SRC})
add_dependencies(bootloader ${BITMAPS_TARGET})
target_compile_definitions(bootloader PUBLIC BOOT)
target_compile_definitions(board_bl PUBLIC BOOT)
if(ARCH STREQUAL ARM)
target_link_libraries(bootloader PUBLIC cmsis)
endif()
target_link_options(bootloader BEFORE PRIVATE
-L${LINKER_DIR}/${TARGET_LINKER_DIR}/bootloader
-T${LINKER_DIR}/bootloader.ld
-Wl,-Map=bootloader.map,--cref,--no-warn-mismatch,--gc-sections
-lm --specs=nano.specs
)
if (DEFINED TARGET_EXTRAM_START)
target_link_options(bootloader PRIVATE
-Wl,--defsym=__EXTRAM_START__=${TARGET_EXTRAM_START}
)
endif()
if (DEFINED TARGET_EXTRAM_SIZE)
target_link_options(bootloader PRIVATE
-Wl,--defsym=__EXTRAM_SIZE__=${TARGET_EXTRAM_SIZE}
)
endif()
if (ENABLE_BOOTLOADER_DEBUG)
target_compile_definitions(board_bl PRIVATE -DDEBUG)
target_compile_definitions(bootloader PRIVATE -DDEBUG)
target_sources(bootloader PRIVATE
${RADIO_SRC_DIR}/serial.cpp
)
# uncomment to enable TRACE in drivers
# target_compile_definitions(stm32_drivers_w_dbg_bl PRIVATE -DDEBUG)
endif()
target_include_directories(bootloader
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
)
add_custom_command(
TARGET bootloader POST_BUILD
COMMAND ${CMAKE_OBJCOPY} -O binary bootloader.elf ${CMAKE_BINARY_DIR}/bootloader.bin
)
if(PYTHONINTERP_FOUND)
add_custom_command(
TARGET bootloader POST_BUILD
COMMAND ${PYTHON_EXECUTABLE} ${RADIO_DIRECTORY}/util/bin2lbm.py ${CMAKE_BINARY_DIR}/bootloader.bin bootloader.lbm
)
endif()
PrintTargetReport("bootloader")
+283
View File
@@ -0,0 +1,283 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <strings.h>
#include <memory.h>
#include "boot.h"
#include "board.h"
#include "sdcard.h"
#include "firmware_files.h"
#include "fw_version.h"
#include "strhelpers.h"
#include "hal/storage.h"
#include "flash_driver.h"
// Size of the block read when checking / writing BIN files
#define BLOCK_LEN 4096
// 'private'
static DIR dir;
static FIL FlashFile;
static uint32_t firmwareSize;
static uint32_t firmwareAddress = FIRMWARE_ADDRESS;
static uint32_t firmwareWritten = 0;
uint32_t unlocked = 0;
struct FWFileInfo {
TCHAR name[FF_MAX_LFN + 1];
unsigned int size;
};
// 'public' variables
static FWFileInfo fwFiles[MAX_FW_FILES];
static uint8_t Block_buffer[BLOCK_LEN];
static UINT BlockCount;
static void flashWriteBlock()
{
// TODO: use some board provided driver instead
uint32_t blockOffset = 0;
#if !defined(SIMU)
while (BlockCount) {
flashWrite((uint32_t *)firmwareAddress, (uint32_t *)&Block_buffer[blockOffset]);
blockOffset += FLASH_PAGESIZE;
firmwareAddress += FLASH_PAGESIZE;
if (BlockCount > FLASH_PAGESIZE) {
BlockCount -= FLASH_PAGESIZE;
}
else {
BlockCount = 0;
}
}
#endif // SIMU
}
void sdInit(void)
{
storageInit();
static FATFS fatFS __DMA;
if (f_mount(&fatFS, "", 1) == FR_OK) {
f_chdir("/");
}
}
void sdDone()
{
// unmount
f_mount(nullptr, "", 0);
storageDeInit();
}
FRESULT openFirmwareDir()
{
FRESULT fr = f_chdir(getFirmwarePath());
if (fr != FR_OK) return fr;
return f_opendir(&dir, ".");
}
static uint32_t isValidBufferStart(const uint8_t * buffer)
{
#if !defined(SIMU)
return isFirmwareStart(buffer);
#else
return 1;
#endif
}
FlashCheckRes checkFirmwareFile(unsigned int index, FlashCheckRes res)
{
if (res != FC_UNCHECKED)
return res;
if (openFirmwareFile(index) != FR_OK)
return FC_ERROR;
if (closeFirmwareFile() != FR_OK)
return FC_ERROR;
if (!isValidBufferStart(Block_buffer))
return FC_ERROR;
return FC_OK;
}
static FRESULT findNextBinFile(FILINFO* fno)
{
FRESULT fr;
do {
fr = f_readdir(&dir, fno);
if (fr != FR_OK || fno->fname[0] == 0)
break;
if (fno->fattrib & (AM_HID | AM_SYS | AM_DIR))
continue; // Skip folders and hidden files
if (fno->fname[0] == '.' && fno->fname[1] != '.')
continue; // Ignore hidden files under UNIX, but not ..
int32_t len = strlen(fno->fname) - 4;
if (len < 0)
continue;
if (fno->fname[len] != '.')
continue;
if ((fno->fname[len + 1] != 'b') && (fno->fname[len + 1] != 'B'))
continue;
if ((fno->fname[len + 2] != 'i') && (fno->fname[len + 2] != 'I'))
continue;
if ((fno->fname[len + 3] != 'n') && (fno->fname[len + 3] != 'N'))
continue;
// match!
break;
} while (1);
return fr;
}
unsigned int fetchFirmwareFiles(unsigned int index)
{
FILINFO file_info;
// rewind
if (f_readdir(&dir, NULL) != FR_OK)
return 0;
// skip 'index' .bin files
for (unsigned int i = 0; i < index; i++) {
if (findNextBinFile(&file_info) != FR_OK || file_info.fname[0] == 0)
return 0;
}
unsigned int i = 0;
for (; i < MAX_NAMES_ON_SCREEN+1; i++) {
if (findNextBinFile(&file_info) != FR_OK || file_info.fname[0] == 0)
return i;
strAppend(fwFiles[i].name, file_info.fname);
fwFiles[i].size = file_info.fsize;
}
return i;
}
const char* getFirmwareFileNameByIndex(unsigned int index)
{
if(index >= MAX_FW_FILES)
return "";
return fwFiles[index].name;
}
FRESULT openFirmwareFile(unsigned int index)
{
TCHAR full_path[FF_MAX_LFN+1];
FRESULT res;
// build full_path: [bin path]/[filename]
char* s = strAppend(full_path, getFirmwarePath());
s = strAppend(s, "/");
strAppend(s, fwFiles[index].name);
BlockCount = 0;
// open the file
res = f_open(&FlashFile, full_path, FA_READ);
if (res != FR_OK) return res;
// skip bootloader in firmware
res = f_lseek(&FlashFile, BOOTLOADER_SIZE);
if (res != FR_OK) return res;
// ... and fetch BLOCK_LEN bytes
res = f_read(&FlashFile, Block_buffer, BLOCK_LEN, &BlockCount);
if (BlockCount == BLOCK_LEN && isValidBufferStart(Block_buffer)) return res;
f_close(&FlashFile);
return FR_INVALID_OBJECT;
}
void getFileFirmwareVersion(VersionTag* tag)
{
memset(tag->flavour, 0, sizeof(tag->flavour));
const char * vers = getFirmwareVersion((const uint8_t *)Block_buffer);
if (!vers || (vers[0] == 'n' && vers[1] == 'o')) { // "no version found"
memcpy(tag->flavour, "unknown", sizeof("unknown"));
tag->version = "unknown";
tag->flavour[sizeof(tag->flavour)-1] = 0;
return;
}
tag->fork = vers;
// skip 'edgetx-' / 'opentx-'
vers += sizeof("edgetx-") - 1;
char* fl = tag->flavour;
while(*vers != '-')
*(fl++) = *(vers++);
tag->flavour[sizeof(tag->flavour)-1] = 0;
// skip '-'
tag->version = ++vers;
}
FRESULT readFirmwareFile()
{
BlockCount = 0;
return f_read(&FlashFile, Block_buffer, sizeof(Block_buffer), &BlockCount);
}
FRESULT closeFirmwareFile()
{
return f_close(&FlashFile);
}
void firmwareInitWrite(uint32_t index)
{
firmwareSize = fwFiles[index].size - BOOTLOADER_SIZE;
firmwareAddress = FIRMWARE_ADDRESS + BOOTLOADER_SIZE;
firmwareWritten = 0;
}
bool firmwareWriteBlock(uint32_t* progress)
{
flashWriteBlock();
firmwareWritten += sizeof(Block_buffer);
*progress = (100 * firmwareWritten) / firmwareSize;
readFirmwareFile();
if (BlockCount == 0 || firmwareWritten >= FLASHSIZE - BOOTLOADER_SIZE) {
return true;
}
return false;
}
+173
View File
@@ -0,0 +1,173 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "boot.h"
#include "hal/usb_driver.h"
#include "hal/abnormal_reboot.h"
#include "hal/rotary_encoder.h"
#include "board.h"
#include "debug.h"
#include "timers_driver.h"
#if defined(BLUETOOTH)
#include "bluetooth_driver.h"
#include "stm32_serial_driver.h"
#endif
#if defined(DEBUG_SEGGER_RTT)
#include "thirdparty/Segger_RTT/RTT/SEGGER_RTT.h"
#endif
#define REBOOT_CMD_DFU 0x55464442
#if !defined(SIMU)
// Bootloader marker:
// -> used to detect valid bootloader files
const uint8_t bootloaderVersion[] __attribute__ ((section(".version"), used)) =
{'B', 'O', 'O', 'T', '1', '0'};
#endif
#if defined(SIMU)
#define __weak
#elif !defined(__weak)
#define __weak __attribute__((weak))
#endif
#if !defined(SIMU)
typedef void (*fctptr_t)(void);
static __attribute__((noreturn)) void jumpTo(uint32_t addr)
{
__disable_irq();
__set_MSP(*(uint32_t*)addr);
fctptr_t reset_handler = (fctptr_t)*(uint32_t*)(addr + 4);
reset_handler();
while(1){}
}
// Optional board hook
__weak void boardBLEarlyInit() {}
__weak bool boardBLStartCondition() { return false; }
__weak void boardBLPreJump() {}
__weak void boardBLInit() {}
void bootloaderInitApp()
{
boardBLEarlyInit();
#if defined(DEBUG_SEGGER_RTT)
SEGGER_RTT_ConfigUpBuffer(0, NULL, NULL, 0, SEGGER_RTT_MODE_NO_BLOCK_SKIP);
#endif
pwrInit();
keysInit();
delaysInit();
bool boot_dfu = abnormalRebootGetCmd() == REBOOT_CMD_DFU;
if (!boot_dfu) {
bool start_firmware = true;
// wait a bit for the inputs to stabilize...
if (!WAS_RESET_BY_WATCHDOG_OR_SOFTWARE()) {
delay_ms(10);
}
start_firmware = !boardBLStartCondition();
if (start_firmware) {
// Start main application
boardBLPreJump();
jumpTo(APP_START_ADDRESS);
}
} else {
#if defined(FIRMWARE_QSPI)
abnormalRebootResetCmd();
setSelectedUsbMode(USB_DFU_MODE);
#endif
}
// TODO: move all this into board specifics
pwrOn();
#if defined(ROTARY_ENCODER_NAVIGATION) && !defined(USE_HATS_AS_KEYS)
rotaryEncoderInit();
#endif
#if defined(DEBUG)
initSerialPorts();
#endif
__enable_irq();
TRACE("\nBootloader started :)");
// TODO: move BT into board specifics
#if defined(BLUETOOTH)
// we shutdown the bluetooth module now to be sure it will be detected on
// firmware start
bluetoothInit(BLUETOOTH_DEFAULT_BAUDRATE, false);
#endif
timersInit();
usbInit();
boardBLInit();
}
#if defined(FIRMWARE_FORMAT_UF2)
// make linker happy
void per5ms() {}
#endif
int main()
#else // SIMU
void bootloaderInitApp() {}
int bootloaderMain()
#endif
{
// init hardware (may jump to app)
bootloaderInitApp();
// init screen
bootloaderInitScreen();
#if defined(FIRMWARE_FORMAT_UF2)
if (getSelectedUsbMode() == USB_DFU_MODE) {
bootloaderDFU();
}
#endif
#if defined(PWR_BUTTON_PRESS)
// wait until power button is released
while (pwrPressed()) {}
#endif
#if !defined(FIRMWARE_FORMAT_UF2)
bootloaderMenu();
#else
bootloaderUF2();
#endif
return 0;
}
+98
View File
@@ -0,0 +1,98 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#pragma once
#include <stdint.h>
#include "stamp.h"
#include "keys.h"
#include "hal/flash_driver.h"
#if LCD_W < 212
#if defined(VERSION_TAG)
#define BOOTLOADER_TITLE " Bootloader - " VERSION_TAG
#else
#define BOOTLOADER_TITLE " Bootloader " VERSION "-" VERSION_SUFFIX
#endif
#else
#if defined(VERSION_TAG)
#define BOOTLOADER_TITLE " EdgeTX Bootloader - " VERSION_TAG
#else
#define BOOTLOADER_TITLE " EdgeTX Bootloader - " VERSION "-" VERSION_SUFFIX
#endif
#endif
#define DISPLAY_CHAR_WIDTH (LCD_COLS+4)
// Bootloader states
enum BootloaderState {
ST_START,
ST_FLASH_MENU,
ST_DIR_CHECK,
ST_OPEN_DIR,
ST_FILE_LIST,
ST_FLASH_CHECK,
ST_FLASHING,
ST_FLASH_DONE,
ST_RESTORE_MENU,
ST_USB,
#if defined(SPI_FLASH)
ST_CLEAR_FLASH_CHECK,
ST_CLEAR_FLASH,
#endif
ST_RADIO_MENU,
ST_REBOOT,
};
// interactive bootloader
void bootloaderMenu();
// non-interactive UF2 bootloader
void bootloaderUF2();
// non-interactive DFU bootloader
void bootloaderDFU();
// Declarations of functions that need to be implemented
// for each target with a bootloader
// On bootloader start after lcdInit()
void bootloaderInitScreen();
// Depending on the state, up to two optional parameters are passed.
// See boot.cpp/main for more details
void bootloaderDrawScreen(BootloaderState st, int opt, const char* str = nullptr);
// Once for each file in a filename list on screen
void bootloaderDrawFilename(const char* str, uint8_t line, bool selected);
// get menu item count for main menu, by adding 1 to the baseCount, when a radio specific menu is used
uint32_t bootloaderGetMenuItemCount(int baseCount);
// when a radio specifc menu is used, all events are forwarded to that menu code
// returns true on submenu exit
bool bootloaderRadioMenu(uint32_t menuItem, event_t event);
void bootloaderDrawDFUScreen();
void sdInit();
void blExit();
+52
View File
@@ -0,0 +1,52 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "stm32_hal.h"
#include "boot.h"
#include "hal/usb_driver.h"
#include "os/time.h"
#include "lcd.h"
#define FRAME_INTERVAL_MS 20
void bootloaderDFU()
{
usbPlugged();
usbStart();
bootloaderDrawDFUScreen();
uint32_t next_frame = time_get_ms();
for (;;) {
if (time_get_ms() - next_frame >= FRAME_INTERVAL_MS) {
next_frame += FRAME_INTERVAL_MS;
if (!usbPlugged()) break;
bootloaderDrawDFUScreen();
lcdRefresh();
}
}
blExit();
NVIC_SystemReset();
}
+315
View File
@@ -0,0 +1,315 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "boot.h"
#include "firmware_files.h"
#include "hal/usb_driver.h"
#include "hal/rotary_encoder.h"
#include "os/time.h"
#include "lcd.h"
#define FRAME_INTERVAL_MS 20
#if defined(SPI_FLASH)
#define MAIN_MENU_LEN 3
#else
#define MAIN_MENU_LEN 2
#endif
#if defined(SPI_FLASH)
#include "diskio_spi_flash.h"
#define SEL_CLEAR_FLASH_STORAGE_MENU_LEN 2
#endif
void pollInputs()
{
keysPollingCycle();
// TODO: use rotaryEncoderPollingCycle() instead
#if defined(ROTARY_ENCODER_NAVIGATION) && !defined(USE_HATS_AS_KEYS)
static rotenc_t rePreviousValue;
rotenc_t reNewValue = rotaryEncoderGetValue();
int8_t scrollRE = reNewValue - rePreviousValue;
if (scrollRE) {
rePreviousValue = reNewValue;
pushEvent(scrollRE < 0 ? EVT_ROTARY_LEFT : EVT_ROTARY_RIGHT);
}
#endif
}
// poll inputs every 10ms
void per5ms()
{
static uint32_t cnt = 0;
if (++cnt & 1) { pollInputs(); }
}
FlashCheckRes valid;
int menuFlashFile(uint32_t index, event_t event)
{
valid = checkFirmwareFile(index, valid);
if (valid == FC_ERROR) {
if (event == EVT_KEY_BREAK(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_ENTER))
return 0;
return -1;
}
if (event == EVT_KEY_LONG(KEY_ENTER)) {
return openFirmwareFile(index) == FR_OK ? 1 : -1;
} else if (event == EVT_KEY_BREAK(KEY_EXIT))
return 0;
return -1;
}
void bootloaderMenu()
{
BootloaderState state = ST_START;
uint32_t vpos = 0;
uint32_t radioMenuItem = 0;
uint8_t index = 0;
FRESULT fr;
uint32_t nameCount = 0;
sdInit();
uint32_t next_frame = time_get_ms();
for (;;) {
if (time_get_ms() - next_frame >= FRAME_INTERVAL_MS) {
next_frame += FRAME_INTERVAL_MS;
if (state != ST_USB && state != ST_FLASHING
&& state != ST_FLASH_DONE && state != ST_RADIO_MENU) {
if (usbPlugged()) {
state = ST_USB;
#if !defined(SIMU)
usbStart();
#endif
}
}
if (state == ST_USB) {
if (usbPlugged() == 0) {
vpos = 0;
#if !defined(SIMU)
usbStop();
#endif
state = ST_START;
}
bootloaderDrawScreen(state, 0);
}
lcdRefreshWait();
event_t event = getEvent();
if (state == ST_START) {
bootloaderDrawScreen(state, vpos);
if (IS_NEXT_EVENT(event)) {
if (vpos < bootloaderGetMenuItemCount(MAIN_MENU_LEN) - 1) { vpos++; }
continue;
}
else if (IS_PREVIOUS_EVENT(event)) {
if (vpos > 0) { vpos--; }
continue;
}
else if (event == EVT_KEY_BREAK(KEY_ENTER)) {
switch (vpos) {
case 0:
state = ST_DIR_CHECK;
break;
#if defined(SPI_FLASH)
case 1:
state = ST_CLEAR_FLASH_CHECK;
vpos = 0;
break;
#endif
default:
if(vpos < bootloaderGetMenuItemCount(MAIN_MENU_LEN-1))
{
state = ST_RADIO_MENU;
radioMenuItem = vpos - MAIN_MENU_LEN - 1;
} else {
state = ST_REBOOT;
}
break;
}
// next loop
continue;
}
}
else if (state == ST_DIR_CHECK) {
nameCount = 0;
fr = openFirmwareDir();
if (fr == FR_OK)
nameCount = fetchFirmwareFiles(index);
if (nameCount > 0) {
index = vpos = 0;
state = ST_FILE_LIST;
continue;
} else {
bootloaderDrawScreen(state, fr);
if (event == EVT_KEY_BREAK(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_ENTER)) {
vpos = 0;
state = ST_START;
continue;
}
}
}
if (state == ST_FILE_LIST) {
uint32_t limit = MAX_NAMES_ON_SCREEN;
if (nameCount < limit) {
limit = nameCount;
}
if (IS_NEXT_EVENT(event)) {
if (vpos < limit - 1) {
vpos += 1;
} else {
if (nameCount > limit) {
index += 1;
nameCount = fetchFirmwareFiles(index);
}
}
} else if (IS_PREVIOUS_EVENT(event)) {
if (vpos > 0) {
vpos -= 1;
}
else {
if (index) {
index -= 1;
nameCount = fetchFirmwareFiles(index);
}
}
}
bootloaderDrawScreen(state, 0);
for (uint32_t i = 0; i < limit; i++) {
bootloaderDrawFilename(getFirmwareFileNameByIndex(i), i, (vpos == i));
}
if (event == EVT_KEY_BREAK(KEY_ENTER)) {
// Select file to flash
state = ST_FLASH_CHECK;
valid = FC_UNCHECKED;
continue;
} else if (event == EVT_KEY_BREAK(KEY_EXIT)) {
state = ST_START;
vpos = 0;
continue;
}
} else if (state == ST_FLASH_CHECK) {
bootloaderDrawScreen(state, valid, getFirmwareFileNameByIndex(vpos));
int result = menuFlashFile(vpos, event);
if (result == 0) {
// canceled
state = ST_FILE_LIST;
} else if (result == 1) {
// confirmed
firmwareInitWrite(vpos);
state = ST_FLASHING;
}
} else if (state == ST_FLASHING) {
uint32_t progress = 0;
bool done = firmwareWriteBlock(&progress);
bootloaderDrawScreen(state, progress);
if(done) {
state = ST_FLASH_DONE;
}
#if defined(SPI_FLASH)
} else if (state == ST_CLEAR_FLASH_CHECK) {
bootloaderDrawScreen(state, vpos);
if (event == EVT_KEY_LONG(KEY_ENTER))
{
state = ST_CLEAR_FLASH;
} else if (event == EVT_KEY_BREAK(KEY_EXIT)) {
vpos = 0;
state = ST_START;
continue;
}
} else if (state == ST_CLEAR_FLASH) {
bootloaderDrawScreen(state, 0);
lcdRefresh();
sdDone();
spiFlashDiskEraseAll();
sdInit();
vpos = 0;
state = ST_START;
#endif
} else if (state == ST_RADIO_MENU) {
if(bootloaderRadioMenu(radioMenuItem, event))
{
state = ST_START;
vpos = 0;
bootloaderDrawScreen(state, vpos);
}
}
if (state == ST_FLASH_DONE) {
if (event == EVT_KEY_BREAK(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_ENTER)) {
state = ST_START;
vpos = 0;
}
bootloaderDrawScreen(state, 100);
}
if (event == EVT_KEY_LONG(KEY_EXIT)) {
// Start the main application
state = ST_REBOOT;
}
lcdRefresh();
}
if (state != ST_FLASHING && state != ST_USB) {
if (pwrOffPressed()) {
sdDone();
boardOff();
}
}
if (state == ST_REBOOT) {
sdDone();
#if !defined(SIMU)
blExit();
NVIC_SystemReset();
#else
exit(1);
#endif
}
}
}
+120
View File
@@ -0,0 +1,120 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "boot.h"
#include "hal/usb_driver.h"
#include "hal/storage.h"
#include "hal/fatfs_diskio.h"
#include "os/time.h"
#include "thirdparty/FatFs/diskio.h"
#include "drivers/uf2_ghostfat.h"
#include "board.h"
#include "lcd.h"
#define FRAME_INTERVAL_MS 20
extern uf2_fat_write_state_t _uf2_write_state;
void bootloaderUF2()
{
BootloaderState state = ST_START;
// register SD storage
storageInit();
disk_initialize(0);
uint32_t next_frame = time_get_ms();
for (;;) {
if (time_get_ms() - next_frame >= FRAME_INTERVAL_MS) {
next_frame += FRAME_INTERVAL_MS;
keysPollingCycle();
event_t event = getEvent();
if (state != ST_FLASHING && event == EVT_KEY_LONG(KEY_EXIT)) {
// Start the main application
state = ST_REBOOT;
}
if (state == ST_REBOOT) {
storageDeInit();
#if !defined(SIMU)
blExit();
NVIC_SystemReset();
#else
exit(1);
#endif
}
if (state != ST_USB && state != ST_FLASHING && state != ST_FLASH_DONE) {
if (usbPlugged()) {
state = ST_USB;
#if !defined(SIMU)
usbStart();
#endif
} else if (pwrOffPressed()) {
storageDeInit();
boardOff();
}
}
if (state == ST_USB || state == ST_FLASH_DONE) {
if (usbPlugged() == 0) {
#if !defined(SIMU)
usbStop();
#endif
state = (state == ST_FLASH_DONE) ? ST_REBOOT : ST_START;
} else {
auto wr_st = uf2_fat_get_state();
if (wr_st->num_blocks != 0 && wr_st->num_blocks <= UF2_MAX_BLOCKS) {
state = ST_FLASHING;
}
}
bootloaderDrawScreen(state, 0);
}
if (state == ST_START) {
bootloaderDrawScreen(state, 0);
} else if (state == ST_FLASHING) {
auto wr_st = uf2_fat_get_state();
if (wr_st->num_blocks == 0 || wr_st->num_blocks > UF2_MAX_BLOCKS) {
state = ST_USB;
} else {
uint32_t progress = (wr_st->num_written * 100) / wr_st->num_blocks;
if (wr_st->num_written == wr_st->num_blocks) {
state = ST_FLASH_DONE;
uf2_fat_reset_state();
}
bootloaderDrawScreen(state, progress);
}
} else if (state == ST_FLASH_DONE) {
bootloaderDrawScreen(state, 100);
}
lcdRefresh();
}
}
}
+75
View File
@@ -0,0 +1,75 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#pragma once
#include <FatFs/ff.h>
#include "sdcard.h"
#include "boards/FirmwareVersionTag.h"
#define getFirmwarePath() (FIRMWARES_PATH)
#if LCD_W < 300 // B&W
constexpr int MAX_NAMES_ON_SCREEN = 6;
#elif LCD_W < LCD_H // Portrait
constexpr int MAX_NAMES_ON_SCREEN = (LCD_H - 143) / 25;
#else // Landscape
constexpr int MAX_NAMES_ON_SCREEN = (LCD_H - 119) / 25;
#endif
#define MAX_FW_FILES (MAX_NAMES_ON_SCREEN+1)
// Open directory for firmware files
FRESULT openFirmwareDir();
// Fetch file names and sizes into binFiles,
// starting at the provided index.
// Only files ending with ".bin" (case-insensitive)
// will be considered.
unsigned int fetchFirmwareFiles(unsigned int index);
const char* getFirmwareFileNameByIndex(unsigned int index);
// Open file indexed in binFiles and read the first BLOCK_LEN bytes
// Bootloader is skipped in firmware files
FRESULT openFirmwareFile(unsigned int index);
// Can be called right after openBinFile() to extract the version information
// from a firmware file
void getFileFirmwareVersion(VersionTag* tag);
// Read the next BLOCK_LEN bytes into 'Block_buffer'
// Check 'BlockCount' for # of bytes read
FRESULT readFirmwareFile();
// Close the previously opened file
FRESULT closeFirmwareFile();
enum FlashCheckRes {
FC_UNCHECKED=0,
FC_OK,
FC_ERROR
};
FlashCheckRes checkFirmwareFile(unsigned int index, FlashCheckRes res);
void firmwareInitWrite(uint32_t index);
bool firmwareEraseBlock(uint32_t* progress);
bool firmwareWriteBlock(uint32_t* progress);
+26
View File
@@ -0,0 +1,26 @@
/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
__attribute__ ((section(".bootloader"), used))
const unsigned char BootCode[] = {
#include "bootloader.lbm"
};