Files
ardupilot/libraries/AP_HAL_ChibiOS/sdcard.cpp
T
Andrew Tridgell cf57b7c86a 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.
2026-08-28 17:18:39 +10:00

327 lines
8.0 KiB
C++

/*
* 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.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <hal.h>
#include "SPIDevice.h"
#include "sdcard.h"
#include "bouncebuffer.h"
#include "CrashDump.h"
#include "hwdef/common/spi_hook.h"
#include <AP_BoardConfig/AP_BoardConfig.h>
#include <AP_Filesystem/AP_Filesystem.h>
#include "stm32_util.h"
extern const AP_HAL::HAL& hal;
#if HAL_USE_FATFS
static FATFS SDC_FS; // FATFS object
#ifndef HAL_BOOTLOADER_BUILD
static HAL_Semaphore sem;
#endif
static bool sdcard_running;
#endif
#if HAL_USE_SDC
static SDCConfig sdcconfig = {
SDC_MODE_4BIT,
0
};
#elif HAL_USE_MMC_SPI
MMCDriver MMCD1;
static AP_HAL::SPIDevice *device;
static MMCConfig mmcconfig;
static SPIConfig lowspeed;
static SPIConfig highspeed;
#endif
// initialise the microSD block device without mounting its filesystem
bool sdcard_init_raw(uint8_t sd_slowdown, uint8_t tries)
{
#if HAL_USE_FATFS
#if HAL_USE_SDC
#if STM32_SDC_USE_SDMMC2 == TRUE
auto &sdcd = SDCD2;
#else
auto &sdcd = SDCD1;
#endif
if (sdcd.bouncebuffer == nullptr) {
// allocate 4k-32k bouncebuffer for microSD to match size in
// AP_Logger
#if defined(STM32H7)
bouncebuffer_init(&sdcd.bouncebuffer, AP_FATFS_MAX_IO_SIZE, true);
// allocation failure, pick a smaller size
if (sdcd.bouncebuffer->dma_buf == nullptr) {
bouncebuffer_init(&sdcd.bouncebuffer, AP_FATFS_MIN_IO_SIZE, true);
#if AP_FILESYSTEM_FATFS_ENABLED
AP_Filesystem_FATFS::set_io_size(AP_FATFS_MIN_IO_SIZE);
#endif
} else {
#if AP_FILESYSTEM_FATFS_ENABLED
AP_Filesystem_FATFS::set_io_size(AP_FATFS_MAX_IO_SIZE);
#endif
}
#else
bouncebuffer_init(&sdcd.bouncebuffer, AP_FATFS_MAX_IO_SIZE, false);
#if AP_FILESYSTEM_FATFS_ENABLED
AP_Filesystem_FATFS::set_io_size(AP_FATFS_MAX_IO_SIZE);
#endif
#endif
if (sdcd.bouncebuffer->dma_buf == nullptr) { // we are never going to be able to log
sdcard_running = false;
return false;
}
}
if (sdcard_running) {
sdcard_stop();
}
for (uint8_t i=0; i<tries; i++) {
sdcconfig.slowdown = sd_slowdown;
sdcStart(&sdcd, &sdcconfig);
if(sdcConnect(&sdcd) == HAL_FAILED) {
sdcStop(&sdcd);
continue;
}
sdcard_running = true;
return true;
}
#elif HAL_USE_MMC_SPI
if (MMCD1.buffer == nullptr) {
// allocate 16 byte non-cacheable buffer for microSD
MMCD1.buffer = (uint8_t*)malloc_axi_sram(MMC_BUFFER_SIZE);
}
if (sdcard_running) {
sdcard_stop();
}
sdcard_running = true;
if (device == nullptr) {
device = AP_HAL::get_HAL().spi->get_device_ptr("sdcard");
if (!device) {
printf("No sdcard SPI device found\n");
sdcard_running = false;
return false;
}
}
device->set_slowdown(sd_slowdown);
mmcObjectInit(&MMCD1, MMCD1.buffer);
mmcconfig.spip = (static_cast<ChibiOS::SPIDevice*>(device))->get_driver();
mmcconfig.hscfg = &highspeed;
mmcconfig.lscfg = &lowspeed;
// try the requested number of times to initialise the microSD interface
for (uint8_t i=0; i<tries; i++) {
mmcStart(&MMCD1, &mmcconfig);
if (mmcConnect(&MMCD1) == HAL_FAILED) {
mmcStop(&MMCD1);
continue;
}
sdcard_running = true;
return true;
}
#endif
sdcard_running = false;
#endif // HAL_USE_FATFS
return false;
}
BaseBlockDevice *sdcard_get_block_device()
{
#if HAL_USE_SDC
#if STM32_SDC_USE_SDMMC2 == TRUE
return reinterpret_cast<BaseBlockDevice *>(&SDCD2);
#else
return reinterpret_cast<BaseBlockDevice *>(&SDCD1);
#endif
#elif HAL_USE_MMC_SPI
return reinterpret_cast<BaseBlockDevice *>(&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)
*/
void sdcard_stop(void)
{
#if AP_CRASHDUMP_FATFS_ENABLED && (HAL_USE_SDC || \
(HAL_USE_MMC_SPI && CRASHDUMP_SD_SPI_SUPPORTED_MCU))
// Do this before unmounting or disabling the peripheral clock. A fault
// after this point must not try to use the cached sector map.
crashdump_sd_invalidate();
#endif
#if HAL_USE_FATFS
// unmount
f_mount(nullptr, "/", 1);
#endif
#if HAL_USE_SDC
#if STM32_SDC_USE_SDMMC2 == TRUE
auto &sdcd = SDCD2;
#else
auto &sdcd = SDCD1;
#endif
if (sdcard_running) {
sdcDisconnect(&sdcd);
sdcStop(&sdcd);
sdcard_running = false;
}
#elif HAL_USE_MMC_SPI
if (sdcard_running) {
mmcDisconnect(&MMCD1);
mmcStop(&MMCD1);
sdcard_running = false;
}
#endif
}
bool sdcard_retry(void)
{
#if HAL_USE_FATFS
#if AP_CRASHDUMP_FATFS_ENABLED && (HAL_USE_SDC || \
(HAL_USE_MMC_SPI && CRASHDUMP_SD_SPI_SUPPORTED_MCU))
const bool sdcard_was_running = sdcard_running;
#endif
if (!sdcard_running) {
if (sdcard_init()) {
#if AP_FILESYSTEM_FILE_WRITING_ENABLED
// create APM directory
AP::FS().mkdir("/APM");
#endif
}
}
#if AP_CRASHDUMP_FATFS_ENABLED && (HAL_USE_SDC || \
(HAL_USE_MMC_SPI && CRASHDUMP_SD_SPI_SUPPORTED_MCU))
if (sdcard_running &&
(!sdcard_was_running || !crashdump_sd_ready())) {
crashdump_sd_init();
}
#endif
return sdcard_running;
#endif
return false;
}
#if HAL_USE_MMC_SPI
AP_HAL::SPIDevice *sdcard_get_spi_device()
{
return device;
}
/*
hooks to allow hal_mmc_spi.c to work with HAL_ChibiOS SPI
layer. This provides bounce buffers for DMA, DMA channel sharing and
bus locking
*/
void spiStartHook(SPIDriver *spip, const SPIConfig *config)
{
device->set_speed(config == &lowspeed ?
AP_HAL::Device::SPEED_LOW : AP_HAL::Device::SPEED_HIGH);
}
void spiStopHook(SPIDriver *spip)
{
}
__RAMFUNC__ void spiAcquireBusHook(SPIDriver *spip)
{
if (sdcard_running) {
ChibiOS::SPIDevice *devptr = static_cast<ChibiOS::SPIDevice*>(device);
devptr->acquire_bus(true, true);
}
}
__RAMFUNC__ void spiReleaseBusHook(SPIDriver *spip)
{
if (sdcard_running) {
ChibiOS::SPIDevice *devptr = static_cast<ChibiOS::SPIDevice*>(device);
devptr->acquire_bus(false, true);
}
}
__RAMFUNC__ void spiSelectHook(SPIDriver *spip)
{
if (sdcard_running) {
device->get_semaphore()->take_blocking();
device->set_chip_select(true);
}
}
__RAMFUNC__ void spiUnselectHook(SPIDriver *spip)
{
if (sdcard_running) {
device->set_chip_select(false);
device->get_semaphore()->give();
}
}
void spiIgnoreHook(SPIDriver *spip, size_t n)
{
if (sdcard_running) {
device->clock_pulse(n);
}
}
__RAMFUNC__ void spiSendHook(SPIDriver *spip, size_t n, const void *txbuf)
{
if (sdcard_running) {
device->transfer((const uint8_t *)txbuf, n, nullptr, 0);
}
}
__RAMFUNC__ void spiReceiveHook(SPIDriver *spip, size_t n, void *rxbuf)
{
if (sdcard_running) {
device->transfer(nullptr, 0, (uint8_t *)rxbuf, n);
}
}
#endif