From 50d6d129cc99a4a114710361d28f136c89d26513 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Mon, 17 Aug 2026 09:37:56 +1000 Subject: [PATCH 1/6] modules:ChibiOS: update for faster USB mass storage Use the ChibiOS USB MSD implementation with chunked multi-block SCSI transfers, pipelining and improved media error handling. --- modules/ChibiOS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ChibiOS b/modules/ChibiOS index b8f4f3c03d70c6..9aebaf4a404422 160000 --- a/modules/ChibiOS +++ b/modules/ChibiOS @@ -1 +1 @@ -Subproject commit b8f4f3c03d70c65a09c77c2199204a02c20af4d4 +Subproject commit 9aebaf4a40442277d01bd9dd38c13713fc45ef41 From 6aa511ac6b9f7a78b233a534983eb96ff40d3b6c Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sun, 16 Aug 2026 21:11:37 +1000 Subject: [PATCH 2/6] AP_HAL: add USB mass storage reboot interface --- libraries/AP_HAL/AP_HAL_Boards.h | 4 ++++ libraries/AP_HAL/Util.h | 8 +++++++- libraries/AP_HAL/board/chibios.h | 8 ++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/libraries/AP_HAL/AP_HAL_Boards.h b/libraries/AP_HAL/AP_HAL_Boards.h index 6bb859cfdb14d1..0270859d7949f6 100644 --- a/libraries/AP_HAL/AP_HAL_Boards.h +++ b/libraries/AP_HAL/AP_HAL_Boards.h @@ -228,6 +228,10 @@ #define HAL_OS_LITTLEFS_IO 0 #endif +#ifndef AP_REBOOT_MASS_STORAGE_ENABLED +#define AP_REBOOT_MASS_STORAGE_ENABLED 0 +#endif + #ifndef HAL_BARO_DEFAULT #define HAL_BARO_DEFAULT HAL_BARO_NONE #endif diff --git a/libraries/AP_HAL/Util.h b/libraries/AP_HAL/Util.h index 9f3de509cac8e0..f73f18546a48e3 100644 --- a/libraries/AP_HAL/Util.h +++ b/libraries/AP_HAL/Util.h @@ -24,6 +24,11 @@ class AP_HAL::Util { // return true if the reason for the reboot was a watchdog reset virtual bool was_watchdog_reset() const { return false; } +#if AP_REBOOT_MASS_STORAGE_ENABLED + // support an early application mode which exports the SD card over USB + virtual bool request_usb_msd() { return false; } +#endif + // return true if safety was off and this was a watchdog reset bool was_watchdog_safety_off() const { return was_watchdog_reset() && persistent_data.safety_state == SAFETY_ARMED; @@ -77,7 +82,8 @@ class AP_HAL::Util { uint8_t fault_thd_prio; char thread_name4[4]; int8_t scheduler_task; - bool armed; // true if vehicle was armed + bool armed : 1; // true if vehicle was armed + bool boot_to_mass_storage : 1; enum safety_state safety_state; bool boot_to_dfu; // true if we should reboot to DFU on boot }; diff --git a/libraries/AP_HAL/board/chibios.h b/libraries/AP_HAL/board/chibios.h index 33ec03a7d5b79b..5448731b3743c3 100644 --- a/libraries/AP_HAL/board/chibios.h +++ b/libraries/AP_HAL/board/chibios.h @@ -30,6 +30,14 @@ #define HAL_PROGRAM_SIZE_LIMIT_KB (BOARD_FLASH_SIZE+EXT_FLASH_SIZE_MB*1024) #endif +#ifndef AP_REBOOT_MASS_STORAGE_ENABLED +#if HAL_PROGRAM_SIZE_LIMIT_KB >= 2048 +#define AP_REBOOT_MASS_STORAGE_ENABLED 1 +#else +#define AP_REBOOT_MASS_STORAGE_ENABLED 0 +#endif +#endif + #ifndef HAL_NUM_CAN_IFACES #define HAL_NUM_CAN_IFACES 0 #endif From 0ab596934f2f5f7ee05a193cab1550bd578693fd Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sun, 16 Aug 2026 21:11:45 +1000 Subject: [PATCH 3/6] AP_HAL_ChibiOS: add early USB microSD service Add an early boot service that exports the microSD block device over USB mass storage before the filesystem and normal flight application start. The service retains exclusive ownership until a power cycle and continues servicing the watchdog. Enable the service on supported boards, provide explicit build control, increase the MSD worker stacks for the SD wait path, scope the ChibiOS fixed-width serial warning suppression to the MSD object, and reject explicit enable requests on unsupported boards. --- .../AP_HAL_ChibiOS/HAL_ChibiOS_Class.cpp | 7 + libraries/AP_HAL_ChibiOS/USB_MSD.cpp | 225 ++++++++++++++++++ libraries/AP_HAL_ChibiOS/USB_MSD.h | 15 ++ libraries/AP_HAL_ChibiOS/Util.cpp | 9 + libraries/AP_HAL_ChibiOS/Util.h | 16 +- .../hwdef/common/chibios_board.mk | 6 + .../AP_HAL_ChibiOS/hwdef/common/stm32_util.h | 1 - .../hwdef/scripts/chibios_hwdef.py | 54 ++++- .../hwdef/scripts/defaults_bootloader.h | 1 + .../hwdef/scripts/defaults_iofirmware.h | 2 + .../hwdef/scripts/defaults_periph.h | 2 + libraries/AP_HAL_ChibiOS/sdcard.cpp | 74 +++--- libraries/AP_HAL_ChibiOS/sdcard.h | 4 +- 13 files changed, 375 insertions(+), 41 deletions(-) create mode 100644 libraries/AP_HAL_ChibiOS/USB_MSD.cpp create mode 100644 libraries/AP_HAL_ChibiOS/USB_MSD.h diff --git a/libraries/AP_HAL_ChibiOS/HAL_ChibiOS_Class.cpp b/libraries/AP_HAL_ChibiOS/HAL_ChibiOS_Class.cpp index 21e88b408eb40f..421321d51e6519 100644 --- a/libraries/AP_HAL_ChibiOS/HAL_ChibiOS_Class.cpp +++ b/libraries/AP_HAL_ChibiOS/HAL_ChibiOS_Class.cpp @@ -25,6 +25,7 @@ #include #include "shared_dma.h" #include "sdcard.h" +#include "USB_MSD.h" #include #include "hwdef/common/usbcfg.h" #include "hwdef/common/stm32_util.h" @@ -367,6 +368,12 @@ void HAL_ChibiOS::run(int argc, char * const argv[], Callbacks* callbacks) const AP::sitl()->init(); #endif // AP_SIM_ENABLED +#if AP_REBOOT_MASS_STORAGE_ENABLED && HAL_USB_MSD_BOOT_ENABLED + if (ChibiOS::usb_msd_boot_requested()) { + ChibiOS::usb_msd_run(); + } +#endif + #if HAL_USE_SERIAL_USB == TRUE usb_initialise(); #endif diff --git a/libraries/AP_HAL_ChibiOS/USB_MSD.cpp b/libraries/AP_HAL_ChibiOS/USB_MSD.cpp new file mode 100644 index 00000000000000..fc1d41cc99d2d5 --- /dev/null +++ b/libraries/AP_HAL_ChibiOS/USB_MSD.cpp @@ -0,0 +1,225 @@ +/* + * This file is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This file 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 + +#if CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS + +#include "USB_MSD.h" + +#if AP_REBOOT_MASS_STORAGE_ENABLED && HAL_USB_MSD_BOOT_ENABLED + +#include +#include + +#include "hwdef/common/stm32_util.h" +#include "hwdef/common/watchdog.h" +#include "sdcard.h" +#include "shared_dma.h" + +#if !defined(STM32H7) && !defined(STM32F7) && !defined(STM32F4) +#error HAL_USB_MSD_BOOT_ENABLED is only supported on STM32H7, STM32F7 and STM32F4 +#endif + +#if HAL_USE_USB_MSD != TRUE || (HAL_USE_SDC != TRUE && HAL_USE_MMC_SPI != TRUE) || HAL_USE_SERIAL_USB != TRUE +#error HAL_USB_MSD_BOOT_ENABLED requires USB MSD, a microSD block device and USB support +#endif + +extern const AP_HAL::HAL& hal; + +namespace ChibiOS { + +static constexpr size_t USB_MSD_IO_SIZE = 4096; + +static const uint8_t device_descriptor_data[18] = { + USB_DESC_DEVICE( + 0x0110, + 0x00, + 0x00, + 0x00, + 0x40, + HAL_USB_VENDOR_ID, + HAL_USB_PRODUCT_ID, + 0x0200, + 0, + 1, + 0, + 1) +}; + +static const USBDescriptor device_descriptor = { + sizeof(device_descriptor_data), + device_descriptor_data +}; + +static const uint8_t configuration_descriptor_data[32] = { + USB_DESC_CONFIGURATION(32, 1, 1, 0, 0xC0, 50), + USB_DESC_INTERFACE(0, 0, 2, 0x08, 0x06, 0x50, 0), + USB_DESC_ENDPOINT(0x01, 0x02, 0x0040, 0), + USB_DESC_ENDPOINT(0x81, 0x02, 0x0040, 0) +}; + +static const USBDescriptor configuration_descriptor = { + sizeof(configuration_descriptor_data), + configuration_descriptor_data +}; + +static const uint8_t string0[] = { + USB_DESC_BYTE(4), + USB_DESC_BYTE(USB_DESCRIPTOR_STRING), + USB_DESC_WORD(0x0409) +}; + +static const uint8_t product_string[] = { + USB_DESC_BYTE(36), USB_DESC_BYTE(USB_DESCRIPTOR_STRING), + 'A', 0, 'r', 0, 'd', 0, 'u', 0, 'P', 0, 'i', 0, 'l', 0, 'o', 0, 't', 0, + ' ', 0, 'S', 0, 'D', 0, ' ', 0, 'C', 0, 'a', 0, 'r', 0, 'd', 0 +}; + +static const USBDescriptor string_descriptors[] = { + { sizeof(string0), string0 }, + { sizeof(product_string), product_string } +}; + +static const USBDescriptor *get_descriptor(USBDriver *usbp, uint8_t type, + uint8_t index, uint16_t language) +{ + (void)usbp; + (void)language; + + switch (type) { + case USB_DESCRIPTOR_DEVICE: + return &device_descriptor; + case USB_DESCRIPTOR_CONFIGURATION: + return &configuration_descriptor; + case USB_DESCRIPTOR_STRING: + if (index < ARRAY_SIZE(string_descriptors)) { + return &string_descriptors[index]; + } + break; + } + return nullptr; +} + +static USBInEndpointState ep1_in_state; +static USBOutEndpointState ep1_out_state; + +static const USBEndpointConfig ep1_config = { + USB_EP_MODE_TYPE_BULK, + nullptr, + nullptr, + nullptr, + 0x0040, + 0x0040, + &ep1_in_state, + &ep1_out_state, + 2, + nullptr +}; + +static void usb_event(USBDriver *usbp, usbevent_t event) +{ + if (event != USB_EVENT_CONFIGURED) { + return; + } + + chSysLockFromISR(); + usbInitEndpointI(usbp, 1, &ep1_config); + chSysUnlockFromISR(); +} + +static const USBConfig usb_config = { + usb_event, + get_descriptor, + msd_request_hook, + nullptr +}; + +bool usb_msd_boot_requested() +{ + AP_HAL::Util::PersistentData persistent_data {}; + stm32_watchdog_load(reinterpret_cast(&persistent_data), + (sizeof(persistent_data) + 3) / 4); + if (!persistent_data.boot_to_mass_storage) { + return false; + } + persistent_data.boot_to_mass_storage = false; + stm32_watchdog_save(reinterpret_cast(&persistent_data), + (sizeof(persistent_data) + 3) / 4); + return true; +} + +void usb_msd_set_boot_request() +{ + hal.util->persistent_data.boot_to_mass_storage = true; + stm32_watchdog_save(reinterpret_cast(&hal.util->persistent_data), + (sizeof(hal.util->persistent_data) + 3) / 4); +} + +void usb_msd_run() +{ + peripheral_power_enable(); + +#if AP_HAL_SHARED_DMA_ENABLED + Shared_DMA::init(); +#endif + + // Mass-storage mode is dedicated to bulk transfers, so deliberately use + // no SD slowdown for maximum speed. Normal flight logging honours + // BRD_SD_SLOWDOWN for maximum robustness. + if (!sdcard_init_raw(0, 3)) { + return; + } + + auto *msdp = static_cast(calloc(1, sizeof(USBMassStorageDriver))); + auto *block_buffer = static_cast( + hal.util->malloc_type(USB_MSD_IO_SIZE * 2, AP_HAL::Util::MEM_FILESYSTEM)); + if (msdp == nullptr || block_buffer == nullptr) { + free(msdp); + hal.util->free_type(block_buffer, USB_MSD_IO_SIZE * 2, AP_HAL::Util::MEM_FILESYSTEM); + return; + } + +#if STM32_OTG2_IS_OTG1 + auto *usbp = &USBD2; +#else + auto *usbp = &USBD1; +#endif + + usbDisconnectBus(usbp); + chThdSleep(chTimeUS2I(1500)); + usbStart(usbp, &usb_config); + + msdObjectInit(msdp); + msdStart(msdp, usbp, sdcard_get_block_device(), + block_buffer, block_buffer + USB_MSD_IO_SIZE, USB_MSD_IO_SIZE, + nullptr, nullptr, nullptr, nullptr); + + usbConnectBus(usbp); + +#if !defined(DISABLE_WATCHDOG) + stm32_watchdog_init(); +#endif + // Mass-storage mode deliberately owns the SD card until power is removed; + // never return to the flight firmware while the USB host may retain access. + while (true) { +#if !defined(DISABLE_WATCHDOG) + stm32_watchdog_pat(); +#endif + chThdSleepMilliseconds(50); + } +} + +} + +#endif // AP_REBOOT_MASS_STORAGE_ENABLED && HAL_USB_MSD_BOOT_ENABLED +#endif // HAL_BOARD_CHIBIOS diff --git a/libraries/AP_HAL_ChibiOS/USB_MSD.h b/libraries/AP_HAL_ChibiOS/USB_MSD.h new file mode 100644 index 00000000000000..bb67d124b9da8b --- /dev/null +++ b/libraries/AP_HAL_ChibiOS/USB_MSD.h @@ -0,0 +1,15 @@ +#pragma once + +#ifndef HAL_USB_MSD_BOOT_ENABLED +#define HAL_USB_MSD_BOOT_ENABLED 0 +#endif + +namespace ChibiOS { + +#if AP_REBOOT_MASS_STORAGE_ENABLED && HAL_USB_MSD_BOOT_ENABLED +bool usb_msd_boot_requested(); +void usb_msd_set_boot_request(); +void usb_msd_run(); +#endif + +} diff --git a/libraries/AP_HAL_ChibiOS/Util.cpp b/libraries/AP_HAL_ChibiOS/Util.cpp index 31f57b9f76b673..b0a9382e6d8d55 100644 --- a/libraries/AP_HAL_ChibiOS/Util.cpp +++ b/libraries/AP_HAL_ChibiOS/Util.cpp @@ -60,6 +60,15 @@ extern AP_IOMCU iomcu; extern const AP_HAL::HAL& hal; using namespace ChibiOS; + +#if AP_REBOOT_MASS_STORAGE_ENABLED && HAL_USB_MSD_BOOT_ENABLED +bool Util::request_usb_msd() +{ + usb_msd_set_boot_request(); + return true; +} +#endif + #if CH_CFG_USE_HEAP == TRUE /** diff --git a/libraries/AP_HAL_ChibiOS/Util.h b/libraries/AP_HAL_ChibiOS/Util.h index 5eb2260db4d9d1..49a2567044fe64 100644 --- a/libraries/AP_HAL_ChibiOS/Util.h +++ b/libraries/AP_HAL_ChibiOS/Util.h @@ -19,6 +19,7 @@ #include #include "AP_HAL_ChibiOS_Namespace.h" #include "AP_HAL_ChibiOS.h" +#include "USB_MSD.h" #include #include @@ -67,6 +68,10 @@ class ChibiOS::Util : public AP_HAL::Util { // return true if the reason for the reboot was a watchdog reset bool was_watchdog_reset() const override; +#if AP_REBOOT_MASS_STORAGE_ENABLED && HAL_USB_MSD_BOOT_ENABLED + bool request_usb_msd() override; +#endif + #if CH_DBG_ENABLE_STACK_CHECK == TRUE // request information on running threads void thread_info(ExpandingString &str) override; @@ -135,9 +140,14 @@ class ChibiOS::Util : public AP_HAL::Util { FlashBootloader flash_bootloader() override; #endif - // stm32F4 and F7 have 20 total RTC backup registers. We use the first one for boot type - // flags, so 19 available for persistent data - static_assert(sizeof(persistent_data) <= 19*4, "watchdog persistent data too large"); + // STM32F4 has 20 total RTC backup registers. We use the first one for boot + // flags, leaving 19 registers for the common persistent data ABI. + static_assert(sizeof(persistent_data) == 19*4, + "watchdog persistent data layout changed"); + static_assert(offsetof(AP_HAL::Util::PersistentData, safety_state) == 74, + "watchdog persistent data layout changed"); + static_assert(offsetof(AP_HAL::Util::PersistentData, boot_to_dfu) == 75, + "bootloader persistent data layout changed"); #if HAL_ENABLE_SAVE_PERSISTENT_PARAMS // save/load key persistent parameters in bootloader sector diff --git a/libraries/AP_HAL_ChibiOS/hwdef/common/chibios_board.mk b/libraries/AP_HAL_ChibiOS/hwdef/common/chibios_board.mk index 610ccf11ae7dc1..2f0013736ba77f 100644 --- a/libraries/AP_HAL_ChibiOS/hwdef/common/chibios_board.mk +++ b/libraries/AP_HAL_ChibiOS/hwdef/common/chibios_board.mk @@ -279,3 +279,9 @@ ULIBS = # End of user defines ############################################################################## include $(HWDEF)/common/chibios_common.mk + +# ChibiOS intentionally fills the fixed-width USB MSD serial-number field +# without a trailing NUL. Limit the warning suppression to that source file. +ifeq ($(USE_USB_MSD),yes) +$(OBJDIR)/hal_usb_msd.o: CWARN += -Wno-unterminated-string-initialization +endif diff --git a/libraries/AP_HAL_ChibiOS/hwdef/common/stm32_util.h b/libraries/AP_HAL_ChibiOS/hwdef/common/stm32_util.h index 7d78d41b82b15b..21f137cafac46a 100644 --- a/libraries/AP_HAL_ChibiOS/hwdef/common/stm32_util.h +++ b/libraries/AP_HAL_ChibiOS/hwdef/common/stm32_util.h @@ -196,4 +196,3 @@ void stm32_disable_cm4_core(void); #ifdef __cplusplus } #endif - diff --git a/libraries/AP_HAL_ChibiOS/hwdef/scripts/chibios_hwdef.py b/libraries/AP_HAL_ChibiOS/hwdef/scripts/chibios_hwdef.py index 377ba8193fdb68..658700b37f2a54 100644 --- a/libraries/AP_HAL_ChibiOS/hwdef/scripts/chibios_hwdef.py +++ b/libraries/AP_HAL_ChibiOS/hwdef/scripts/chibios_hwdef.py @@ -56,10 +56,13 @@ class ChibiOSHWDef(hwdef.HWDef): UART_TYPE_RE = re.compile(r'UART(\d+)') UART_LABEL_RE = re.compile(r'UART(\d+)_(RX|TX|CTS|RTS|CTS_GPIO)') - def __init__(self, bootloader=False, signed_fw=False, default_params_filepath=None, **kwargs): + def __init__(self, bootloader=False, signed_fw=False, mass_storage_option=None, + default_params_filepath=None, **kwargs): super(ChibiOSHWDef, self).__init__(**kwargs) self.bootloader = bootloader self.signed_fw = signed_fw + self.mass_storage_option = mass_storage_option + self.usb_mass_storage_enabled = False self.default_params_filepath = default_params_filepath self.processed_defaults_filepath = None self.have_defaults_file = False @@ -1056,6 +1059,17 @@ def write_mcu_config(self, f): if 'OTG2' in self.bytype: f.write('#define STM32_USB_USE_OTG2 TRUE\n') + if self.is_normal_fw(): + f.write('#define AP_REBOOT_MASS_STORAGE_ENABLED %u\n' % self.usb_mass_storage_enabled) + if self.usb_mass_storage_enabled: + f.write(''' +#define HAL_USB_MSD_BOOT_ENABLED 1 +#define HAL_USE_USB_MSD TRUE +#define USB_MSD_THREAD_WA_SIZE 1024 +#define USB_USE_WAIT TRUE +''') + self.build_flags.append('USE_USB_MSD=yes') + if 'ETH1' in self.bytype: self.enable_networking(f) f.write(''' @@ -1116,14 +1130,13 @@ def write_mcu_config(self, f): if d.startswith('define '): if 'HAL_USE_CAN' in d: using_chibios_can = True + if d.split()[1] == 'AP_REBOOT_MASS_STORAGE_ENABLED': + continue f.write('#define %s\n' % d[7:]) if self.intdefines.get('AP_NETWORKING_ENABLED', 0) == 1: self.enable_networking(f) - if self.intdefines.get('HAL_USE_USB_MSD', 0) == 1: - self.build_flags.append('USE_USB_MSD=yes') - if self.have_type_prefix('CAN') and not using_chibios_can: self.enable_can(f) else: @@ -3055,7 +3068,7 @@ def add_firmware_defaults_from_file(self, f, filename, description): ''' % (description, content, description)) def is_io_fw(self): - return int(self.env_vars.get('IOMCU_FW', 0)) != 0 + return self.get_config('IOMCU_FW', default=0, required=False, type=int) != 0 def add_iomcu_firmware_defaults(self, f): '''add default defines IO firmwares''' @@ -3154,6 +3167,35 @@ def get_stale_defines(self): }) return ret + def setup_usb_mass_storage(self): + '''setup USB mass storage support''' + flash_size = self.get_config('FLASH_SIZE_KB', type=int) + ext_flash_size = self.get_config('EXT_FLASH_SIZE_MB', default=0, type=int) + program_size_limit = self.intdefines.get( + 'HAL_PROGRAM_SIZE_LIMIT_KB', flash_size + ext_flash_size * 1024) + mcu_defines = self.get_mcu_config('DEFINES', False) or {} + fastboot_enabled = self.intdefines.get( + 'AP_FASTBOOT_ENABLED', int(mcu_defines.get('AP_FASTBOOT_ENABLED', 1))) == 1 + default_mass_storage = (self.is_normal_fw() and + program_size_limit >= 2048 and fastboot_enabled) + mass_storage_option = self.mass_storage_option + if mass_storage_option is None: + mass_storage_option = self.intdefines.get('AP_REBOOT_MASS_STORAGE_ENABLED') + mass_storage_requested = (default_mass_storage if mass_storage_option is None else + bool(mass_storage_option)) + supported_mcu = self.mcu_series.startswith(('STM32F4', 'STM32F7', 'STM32H7')) + have_sdcard = (not self.dataflash_list and + (self.have_type_prefix('SDIO') or self.have_type_prefix('SDMMC') or + self.has_sdcard_spi())) + have_usb = 'OTG1' in self.bytype + self.usb_mass_storage_enabled = (self.is_normal_fw() and mass_storage_requested and + supported_mcu and fastboot_enabled and have_sdcard and have_usb) + if mass_storage_option is not None and mass_storage_option > 0 and not self.usb_mass_storage_enabled: + self.error('USB mass storage unavailable (requires normal STM32F4/F7/H7 firmware, ' + 'persistent reboot state, USB and microSD)') + if mass_storage_option is None and default_mass_storage and not self.usb_mass_storage_enabled: + self.progress('USB mass storage unavailable (requires STM32F4/F7/H7, USB and microSD)') + def run(self): # process input file self.process_hwdefs() @@ -3166,6 +3208,8 @@ def run(self): self.mcu_type = self.get_config('MCU', 1) self.progress("Setup for MCU %s" % self.mcu_type) + self.setup_usb_mass_storage() + # put USE_BOOTLOADER_FROM_BOARD into the environment so the # build process can use it when generating hex files: use_bootloader_from_board = self.get_config('USE_BOOTLOADER_FROM_BOARD', default=None, required=False) diff --git a/libraries/AP_HAL_ChibiOS/hwdef/scripts/defaults_bootloader.h b/libraries/AP_HAL_ChibiOS/hwdef/scripts/defaults_bootloader.h index 8600cb4b513d5b..809187ed6df93e 100644 --- a/libraries/AP_HAL_ChibiOS/hwdef/scripts/defaults_bootloader.h +++ b/libraries/AP_HAL_ChibiOS/hwdef/scripts/defaults_bootloader.h @@ -4,6 +4,7 @@ #define HAL_DSHOT_ALARM_ENABLED 0 #define HAL_LOGGING_ENABLED 0 #define HAL_SCHEDULER_ENABLED 0 +#define AP_REBOOT_MASS_STORAGE_ENABLED 0 // bootloaders *definitely* don't use the FFT library: #ifndef HAL_GYROFFT_ENABLED diff --git a/libraries/AP_HAL_ChibiOS/hwdef/scripts/defaults_iofirmware.h b/libraries/AP_HAL_ChibiOS/hwdef/scripts/defaults_iofirmware.h index 7e3ed4765b7135..69dc8e91a3b5b9 100644 --- a/libraries/AP_HAL_ChibiOS/hwdef/scripts/defaults_iofirmware.h +++ b/libraries/AP_HAL_ChibiOS/hwdef/scripts/defaults_iofirmware.h @@ -1,6 +1,8 @@ // this file is inserted (by chibios_hwdef.py) into hwdef.h when // configuring for iofirmware builds +#define AP_REBOOT_MASS_STORAGE_ENABLED 0 + #define HAL_DSHOT_ALARM_ENABLED 0 #define HAL_LOGGING_ENABLED 0 diff --git a/libraries/AP_HAL_ChibiOS/hwdef/scripts/defaults_periph.h b/libraries/AP_HAL_ChibiOS/hwdef/scripts/defaults_periph.h index f1cf94be4ae9df..9a4dbf6d35fff8 100644 --- a/libraries/AP_HAL_ChibiOS/hwdef/scripts/defaults_periph.h +++ b/libraries/AP_HAL_ChibiOS/hwdef/scripts/defaults_periph.h @@ -1,6 +1,8 @@ // this file is inserted (by chibios_hwdef.py) into hwdef.h when // configuring for AP_Periph builds +#define AP_REBOOT_MASS_STORAGE_ENABLED 0 + #ifndef AP_SCHEDULER_ENABLED #define AP_SCHEDULER_ENABLED 0 #endif diff --git a/libraries/AP_HAL_ChibiOS/sdcard.cpp b/libraries/AP_HAL_ChibiOS/sdcard.cpp index e57d0461c1521e..e9b8f0c9cdbd6c 100644 --- a/libraries/AP_HAL_ChibiOS/sdcard.cpp +++ b/libraries/AP_HAL_ChibiOS/sdcard.cpp @@ -47,21 +47,10 @@ static SPIConfig lowspeed; static SPIConfig highspeed; #endif -/* - initialise microSD card if avaialble. This is called during - AP_BoardConfig initialisation. The parameter BRD_SD_SLOWDOWN - controls a scaling factor on the microSD clock - */ -bool sdcard_init() +// initialise the microSD block device without mounting its filesystem +bool sdcard_init_raw(uint8_t sd_slowdown, uint8_t tries) { #if HAL_USE_FATFS -#ifndef HAL_BOOTLOADER_BUILD - WITH_SEMAPHORE(sem); - - uint8_t sd_slowdown = AP_BoardConfig::get_sdcard_slowdown(); -#else - uint8_t sd_slowdown = 0; // maybe take from a define? -#endif #if HAL_USE_SDC #if STM32_SDC_USE_SDMMC2 == TRUE @@ -102,7 +91,6 @@ bool sdcard_init() sdcard_stop(); } - const uint8_t tries = 3; for (uint8_t i=0; i(&SDCD2); +#else + return reinterpret_cast(&SDCD1); +#endif +#elif HAL_USE_MMC_SPI + return reinterpret_cast(&MMCD1); +#else + return nullptr; +#endif +} + +bool sdcard_init() +{ +#if HAL_USE_FATFS +#ifndef HAL_BOOTLOADER_BUILD + WITH_SEMAPHORE(sem); + const uint8_t sd_slowdown = AP_BoardConfig::get_sdcard_slowdown(); +#else + const uint8_t sd_slowdown = 0; +#endif + + for (uint8_t i = 0; i < 3; i++) { + if (!sdcard_init_raw(sd_slowdown, 1)) { + continue; + } + if (f_mount(&SDC_FS, "/", 1) == FR_OK) { + printf("Successfully mounted SDCard (slowdown=%u)\n", (unsigned)sd_slowdown); + return true; + } + sdcard_stop(); + } +#endif + return false; +} + /* stop sdcard interface (for reboot) */ diff --git a/libraries/AP_HAL_ChibiOS/sdcard.h b/libraries/AP_HAL_ChibiOS/sdcard.h index 50c3c55e49b921..ddecb95d74843a 100644 --- a/libraries/AP_HAL_ChibiOS/sdcard.h +++ b/libraries/AP_HAL_ChibiOS/sdcard.h @@ -15,13 +15,15 @@ */ #pragma once -#include +#include namespace AP_HAL { class SPIDevice; } bool sdcard_init(); +bool sdcard_init_raw(uint8_t slowdown, uint8_t tries); +BaseBlockDevice *sdcard_get_block_device(); void sdcard_stop(); bool sdcard_retry(); AP_HAL::SPIDevice *sdcard_get_spi_device(); From d31f30359e41e84aeebed0a8967c9fa2ccd11820 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sun, 16 Aug 2026 21:11:50 +1000 Subject: [PATCH 4/6] GCS_MAVLink: add reboot to USB storage action --- libraries/GCS_MAVLink/GCS_Common.cpp | 36 ++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/libraries/GCS_MAVLink/GCS_Common.cpp b/libraries/GCS_MAVLink/GCS_Common.cpp index 570fd7a99c6519..a90eeb0186d207 100644 --- a/libraries/GCS_MAVLink/GCS_Common.cpp +++ b/libraries/GCS_MAVLink/GCS_Common.cpp @@ -3653,6 +3653,20 @@ MAV_RESULT GCS_MAVLINK::handle_preflight_reboot(const mavlink_command_int_t &pac #endif } + const bool reboot = is_equal(packet.param1, static_cast(REBOOT_SHUTDOWN_ACTION_REBOOT)); + const bool reboot_to_bootloader = is_equal(packet.param1, + static_cast(REBOOT_SHUTDOWN_ACTION_REBOOT_TO_BOOTLOADER)); + +#if AP_REBOOT_MASS_STORAGE_ENABLED + const bool reboot_to_mass_storage = is_equal(packet.param1, + static_cast(REBOOT_SHUTDOWN_ACTION_REBOOT_TO_MASS_STORAGE)); + + // exporting writable storage must never be entered while armed + if (reboot_to_mass_storage && hal.util->get_soft_armed()) { + return MAV_RESULT_FAILED; + } +#endif + // refuse reboot when armed: if (hal.util->get_soft_armed()) { /// but allow it if forced: @@ -3662,10 +3676,21 @@ MAV_RESULT GCS_MAVLINK::handle_preflight_reboot(const mavlink_command_int_t &pac } } - if (!(is_equal(packet.param1, 1.0f) || is_equal(packet.param1, 3.0f))) { - // param1 must be 1 or 3 - 1 being reboot, 3 being reboot-to-bootloader +#if AP_REBOOT_MASS_STORAGE_ENABLED + const bool supported_reboot_action = reboot || reboot_to_bootloader || reboot_to_mass_storage; +#else + const bool supported_reboot_action = reboot || reboot_to_bootloader; +#endif + if (!supported_reboot_action) { + // param1 must select a supported reboot action + return MAV_RESULT_UNSUPPORTED; + } + +#if AP_REBOOT_MASS_STORAGE_ENABLED + if (reboot_to_mass_storage && !hal.util->request_usb_msd()) { return MAV_RESULT_UNSUPPORTED; } +#endif #if CONFIG_HAL_BOARD == HAL_BOARD_SITL { // autotest relies in receiving the ACK for the reboot. Ensure @@ -3686,13 +3711,10 @@ MAV_RESULT GCS_MAVLINK::handle_preflight_reboot(const mavlink_command_int_t &pac msg.sysid, msg.compid); - // when packet.param1 == 3 we reboot to hold in bootloader - const bool hold_in_bootloader = is_equal(packet.param1, 3.0f); - #if AP_VEHICLE_ENABLED - AP::vehicle()->reboot(hold_in_bootloader); // not expected to return + AP::vehicle()->reboot(reboot_to_bootloader); // not expected to return #else - hal.scheduler->reboot(hold_in_bootloader); + hal.scheduler->reboot(reboot_to_bootloader); #endif return MAV_RESULT_FAILED; From 15ea6c7f0f6cac90f643f8130445b9138c1dd116 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Mon, 17 Aug 2026 07:24:29 +1000 Subject: [PATCH 5/6] waf: add mass storage build selection --- Tools/ardupilotwaf/chibios.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Tools/ardupilotwaf/chibios.py b/Tools/ardupilotwaf/chibios.py index 85b1122bed2e4f..d6ef365caa93fa 100644 --- a/Tools/ardupilotwaf/chibios.py +++ b/Tools/ardupilotwaf/chibios.py @@ -633,6 +633,17 @@ def bldpath(path): env.DEFINES += [ 'CANARD_MULTI_IFACE=1' ] setup_optimization(cfg.env) +def get_build_option_value(env, name): + '''return the value of a build option, or None if it was not specified''' + enable_option = 'enable_' + name + disable_option = 'disable_' + name + if env.OPTIONS.get(enable_option, False) or env.OPTIONS.get(enable_option.lower(), False): + return 1 + if env.OPTIONS.get(disable_option, False) or env.OPTIONS.get(disable_option.lower(), False): + return 0 + return None + + def generate_hwdef_h(env): '''run chibios_hwdef.py''' if env.BOOTLOADER: @@ -660,6 +671,7 @@ def generate_hwdef_h(env): outdir=hwdef_out, bootloader=bootloader_flag, signed_fw=bool(env.AP_SIGNED_FIRMWARE), + mass_storage_option=get_build_option_value(env, 'MASS_STORAGE'), hwdef=hwdef, # stringify like old subprocess based invocation. note that no error is # generated if this path is missing! From fe9d648074becf19796ce16282eb97cd380e0993 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Mon, 17 Aug 2026 07:24:29 +1000 Subject: [PATCH 6/6] Tools: add mass storage build option --- Tools/scripts/build_options.py | 1 + Tools/scripts/extract_features.py | 1 + 2 files changed, 2 insertions(+) diff --git a/Tools/scripts/build_options.py b/Tools/scripts/build_options.py index 5523ccbcac684e..d862cf347a041a 100644 --- a/Tools/scripts/build_options.py +++ b/Tools/scripts/build_options.py @@ -420,6 +420,7 @@ def config_option(self): Feature('Other', 'GyroFFT', 'HAL_GYROFFT_ENABLED', 'Enable In-Flight gyro FFT calculations', 0, None), Feature('Other', 'NMEA_OUTPUT', 'HAL_NMEA_OUTPUT_ENABLED', 'Enable NMEA output', 0, None), Feature('Other', 'SDCARD_FORMATTING', 'AP_FILESYSTEM_FORMAT_ENABLED', 'Enable Formatting of microSD cards', 0, None), + Feature('Other', 'MASS_STORAGE', 'AP_REBOOT_MASS_STORAGE_ENABLED', 'Enable reboot to USB mass storage', 0, None), Feature('Other', 'BOOTLOADER_FLASHING', 'AP_BOOTLOADER_FLASHING_ENABLED', 'Enable Bootloader flashing', 0, "FILESYSTEM_ROMFS"), # noqa Feature('Other', 'SERIALDEVICE_REGISTER', 'AP_SERIALMANAGER_REGISTER_ENABLED', 'Enable Serial device registration', 0, None), # noqa diff --git a/Tools/scripts/extract_features.py b/Tools/scripts/extract_features.py index b0d682e9938f8a..7de7e725db3ee4 100755 --- a/Tools/scripts/extract_features.py +++ b/Tools/scripts/extract_features.py @@ -34,6 +34,7 @@ def __init__(self, filename, nm="arm-none-eabi-nm", strings="strings"): self.features = [ ('AP_ADVANCEDFAILSAFE_ENABLED', r'AP_AdvancedFailsafe::heartbeat\b',), ('AP_BOOTLOADER_FLASHING_ENABLED', 'ChibiOS::Util::flash_bootloader',), + ('AP_REBOOT_MASS_STORAGE_ENABLED', r'ChibiOS::usb_msd_run',), ('AP_AIRSPEED_ENABLED', 'AP_Airspeed::AP_Airspeed',), ('AP_AIRSPEED_{type}_ENABLED', r'AP_Airspeed_(?P.*)::init',),