rusefi/firmware/hw_layer/mmc_card.cpp

988 lines
23 KiB
C++

/**
* @file mmc_card.cpp
*
* @date Dec 28, 2013
* @author Kot_dnz
* @author Andrey Belomutskiy, (c) 2012-2020
*
* default pinouts in case of SPI2 connected to MMC: PB13 - SCK, PB14 - MISO, PB15 - MOSI, PD4 - CS, 3.3v
* default pinouts in case of SPI3 connected to MMC: PB3 - SCK, PB4 - MISO, PB5 - MOSI, PD4 - CS, 3.3v
*
*
* todo: extract some logic into a controller file
*/
#include "pch.h"
#if EFI_FILE_LOGGING
#include "buffered_writer.h"
#include "status_loop.h"
#include "binary_logging.h"
// Divide logs into 32Mb chunks.
// With this opstion defined SW will pre-allocate file with given size and
// should not touch FAT structures until file is fully filled
// This should protect FS from corruption at sudden power loss
#define LOGGER_MAX_FILE_SIZE (32 * 1024 * 1024)
// at about 20Hz we write about 2Kb per second, looks like we flush once every ~2 seconds
#define F_SYNC_FREQUENCY 10
static bool sdLoggerReady = false;
#if EFI_PROD_CODE
#include <stdio.h>
#include <string.h>
#include "mmc_card.h"
#include "ff.h"
#include "mmc_card_util.h"
#include "mass_storage_init.h"
#include "hellen_meta.h"
#include "rtc_helper.h"
// TODO: do we need this additioal layer of buffering?
// FIL structure already have buffer of FF_MAX_SS size
// check if it is better to increase FF_MAX_SS and drop BufferedWriter?
struct SdLogBufferWriter final : public BufferedWriter<512> {
bool failed = false;
int start(FIL *fd) {
if (m_fd) {
efiPrintf("SD logger already started!");
return -1;
}
totalLoggedBytes = 0;
writeCounter = 0;
m_fd = fd;
return 0;
}
void stop() {
m_fd = nullptr;
flush();
totalLoggedBytes = 0;
writeCounter = 0;
}
size_t writen() {
return totalLoggedBytes;
}
size_t writeInternal(const char* buffer, size_t count) override {
if ((!m_fd) || (failed)) {
return 0;
}
size_t bytesWritten;
efiAssert(ObdCode::CUSTOM_STACK_6627, hasLotsOfRemainingStack(), "sdlow#3", 0);
FRESULT err = f_write(m_fd, buffer, count, &bytesWritten);
if (err) {
printError("log file write", err);
failed = true;
return 0;
} else if (bytesWritten != count) {
printError("log file write partitial", err);
failed = true;
return 0;
} else {
writeCounter++;
totalLoggedBytes += count;
if (writeCounter >= F_SYNC_FREQUENCY) {
/**
* Performance optimization: not f_sync after each line, f_sync is probably a heavy operation
* todo: one day someone should actually measure the relative cost of f_sync
*/
f_sync(m_fd);
writeCounter = 0;
}
}
return bytesWritten;
}
private:
FIL *m_fd = nullptr;
size_t totalLoggedBytes = 0;
size_t writeCounter = 0;
};
#else // not EFI_PROD_CODE (simulator)
#include <fstream>
class SdLogBufferWriter final : public BufferedWriter<512> {
public:
bool failed = false;
SdLogBufferWriter()
: m_stream("rusefi_simulator_log.mlg", std::ios::binary | std::ios::trunc)
{
sdLoggerReady = true;
}
size_t writeInternal(const char* buffer, size_t count) override {
m_stream.write(buffer, count);
m_stream.flush();
return count;
}
private:
std::ofstream m_stream;
};
#endif
static NO_CACHE SdLogBufferWriter logBuffer;
#if EFI_PROD_CODE
// This is dirty workaround to fix compilation without adding this function prototype
// to error_handling.h file that will also need to add "ff.h" include to same file and
// cause simulator fail to build.
extern void errorHandlerWriteReportFile(FIL *fd);
extern int errorHandlerCheckReportFiles();
extern void errorHandlerDeleteReports();
typedef enum {
SD_STATUS_INIT = 0,
SD_STATUS_MOUNTED,
SD_STATUS_MOUNT_FAILED,
SD_STATUS_OPEN_FAILED,
SD_STATUS_SEEK_FAILED,
SD_STATUS_NOT_INSERTED,
SD_STATUS_CONNECTING,
SD_STATUS_MSD,
SD_STATUS_MMC_FAILED
} SD_STATUS;
// todo: shall we migrate to enum with enum2string for consistency? maybe not until we start reading sdStatus?
static const char *sdStatusNames[] =
{
"INIT",
"MOUNTED",
"MOUNT_FAILED",
"OPEN_FAILED",
"SEEK_FAILED",
"NOT_INSERTED",
"CONNECTING",
"MSD",
"MMC_CONNECT_FAILED"
};
static const char *sdStatusName(SD_STATUS status)
{
return sdStatusNames[status];
}
static SD_STATUS sdStatus = SD_STATUS_INIT;
static SD_MODE sdMode = SD_MODE_IDLE;
// by default we want SD card for logs
static SD_MODE sdTargerMode = SD_MODE_ECU;
static bool sdNeedRemoveReports = false;
/**
* on't re-read SD card spi device after boot - it could change mid transaction (TS thread could preempt),
* which will cause disaster (usually multiple-unlock of the same mutex in UNLOCK_SD_SPI)
*/
static spi_device_e mmcSpiDevice = SPI_NONE;
#define RUSEFI_LOG_PREFIX "re_"
#define PREFIX_LEN 3
#define SHORT_TIME_LEN 13
#define FILE_LIST_MAX_COUNT 20
#if HAL_USE_MMC_SPI
/**
* MMC driver instance.
*/
MMCDriver MMCD1;
/* MMC/SD over SPI driver configuration.*/
static MMCConfig mmccfg = {
.spip = NULL,
.lscfg = &mmc_ls_spicfg,
.hscfg = &mmc_hs_spicfg
};
#if MMC_USE_MUTUAL_EXCLUSION == TRUE
#define LOCK_SD_SPI()
#define UNLOCK_SD_SPI()
#else
#define LOCK_SD_SPI() lockSpi(mmcSpiDevice)
#define UNLOCK_SD_SPI() unlockSpi(mmcSpiDevice)
#endif
#endif /* HAL_USE_MMC_SPI */
/**
* fatfs MMC/SPI
*/
static NO_CACHE FATFS MMC_FS;
static void sdLoggerSetReady(bool value) {
sdLoggerReady = value;
}
static bool sdLoggerIsReady(void) {
return sdLoggerReady;
}
/* See ff.h FRESULT enum */
static const char *fatErrors[] = {
"FR_OK: Succeeded",
"FR_DISK_ERR: A hard error occurred in the low level disk I/O layer",
"FR_INT_ERR: Assertion failed",
"FR_NOT_READY: The physical drive cannot work",
"FR_NO_FILE: Could not find the file",
"FR_NO_PATH: Could not find the path",
"FR_INVALID_NAME: The path name format is invalid",
"FR_DENIED: Access denied due to prohibited access or directory full",
"FR_EXIST: Access denied due to prohibited access",
"FR_INVALID_OBJECT: The file/directory object is invalid",
"FR_WRITE_PROTECTED: The physical drive is write protected",
"FR_INVALID_DRIVE: The logical drive number is invalid",
"FR_NOT_ENABLED: The volume has no work area",
"FR_NO_FILESYSTEM: There is no valid FAT volume",
"FR_MKFS_ABORTED: The f_mkfs() aborted due to any problem",
"FR_TIMEOUT: Could not get a grant to access the volume within defined period",
"FR_LOCKED: The operation is rejected according to the file sharing policy",
"FR_NOT_ENOUGH_CORE: LFN working buffer could not be allocated",
"FR_TOO_MANY_OPEN_FILES: Number of open files > FF_FS_LOCK",
"FR_INVALID_PARAMETER: Given parameter is invalid"
};
// print FAT error function
void printError(const char *str, FRESULT f_error) {
static int fatFsErrors = 0;
if (fatFsErrors++ > 16) {
// no reason to spam the console
return;
}
efiPrintf("FATfs Error \"%s\" %d %s", str, f_error, f_error <= FR_INVALID_PARAMETER ? fatErrors[f_error] : "unknown");
}
// Warning: shared between all FS users, please release it after use
static FIL FDLogFile NO_CACHE;
extern int logFileIndex;
static char logName[_MAX_FILLER + 20];
static void printMmcPinout() {
efiPrintf("MMC CS %s", hwPortname(engineConfiguration->sdCardCsPin));
// todo: we need to figure out the right SPI pinout, not just SPI2
// efiPrintf("MMC SCK %s:%d", portname(EFI_SPI2_SCK_PORT), EFI_SPI2_SCK_PIN);
// efiPrintf("MMC MISO %s:%d", portname(EFI_SPI2_MISO_PORT), EFI_SPI2_MISO_PIN);
// efiPrintf("MMC MOSI %s:%d", portname(EFI_SPI2_MOSI_PORT), EFI_SPI2_MOSI_PIN);
}
static void sdStatistics() {
printMmcPinout();
efiPrintf("SD enabled=%s status=%s", boolToString(engineConfiguration->isSdCardEnabled),
sdStatusName(sdStatus));
printSpiConfig("SD", mmcSpiDevice);
#if HAL_USE_MMC_SPI && (defined(STM32F4XX) || defined(STM32F7XX))
efiPrintf("HS clock %d Hz", spiGetBaseClock(mmccfg.spip) / (2 << ((mmc_hs_spicfg.cr1 & SPI_CR1_BR_Msk) >> SPI_CR1_BR_Pos)));
efiPrintf("LS clock %d Hz", spiGetBaseClock(mmccfg.spip) / (2 << ((mmc_ls_spicfg.cr1 & SPI_CR1_BR_Msk) >> SPI_CR1_BR_Pos)));
#endif
if (sdLoggerIsReady()) {
efiPrintf("filename=%s size=%d", logName, logBuffer.writen());
}
#if EFI_FILE_LOGGING
efiPrintf("%d SD card fields", getSdCardFieldsCount());
#endif
}
static void sdSetMode(const char *mode) {
if (strcmp(mode, "pc") == 0) {
sdCardRequestMode(SD_MODE_PC);
} else if (strcmp(mode, "ecu") == 0) {
sdCardRequestMode(SD_MODE_ECU);
} else {
efiPrintf("Invalid mode %s allowed modes pc and ecu", mode);
}
}
static void prepareLogFileName() {
strcpy(logName, RUSEFI_LOG_PREFIX);
char *ptr;
// TS SD protocol supports only short 8 symbol file names, good thing that we do not use TS SD protocol!
bool result = dateToStringShort(&logName[PREFIX_LEN]);
if (result) {
ptr = &logName[PREFIX_LEN + SHORT_TIME_LEN];
} else {
ptr = itoa10(&logName[PREFIX_LEN], logFileIndex);
}
if (engineConfiguration->sdTriggerLog) {
strcat(ptr, ".teeth");
} else {
strcat(ptr, DOT_MLG);
}
}
/**
* @brief Create a new file with the specified name
*
* This function saves the name of the file in a global variable
* so that we can later append to that file
*/
static void sdLoggerCreateFile(FIL *fd) {
// clear the memory
memset(fd, 0, sizeof(FIL));
prepareLogFileName();
efiPrintf("starting log file %s", logName);
// Create new file. If file is exist - truncate and overwrite, we need header to be at zero offset.
FRESULT err = f_open(fd, logName, FA_CREATE_ALWAYS | FA_WRITE);
if (err != FR_OK && err != FR_EXIST) {
sdStatus = SD_STATUS_OPEN_FAILED;
warning(ObdCode::CUSTOM_ERR_SD_MOUNT_FAILED, "SD: mount failed");
printError("log file create", err); // else - show error
return;
}
#ifdef LOGGER_MAX_FILE_SIZE
//pre-allocate data ahead
err = f_expand(fd, LOGGER_MAX_FILE_SIZE, /* Find and allocate */ 1);
if (err != FR_OK) {
printError("pre-allocate", err);
// this is not critical
}
#endif
// SD logger is ok
sdLoggerSetReady(true);
}
static void sdLoggerCloseFile(FIL *fd)
{
#ifdef LOGGER_MAX_FILE_SIZE
// truncate file to actual size
f_truncate(fd);
#endif
// close file
f_close(fd);
// f_sync is called internally
//f_sync(&FDLogFile);
// SD logger is inactive
sdLoggerSetReady(false);
}
static void removeFile(const char *pathx) {
if (sdMode != SD_MODE_ECU) {
efiPrintf("SD card should be mounted to ECU");
return;
}
f_unlink(pathx);
}
#if HAL_USE_USB_MSD
static chibios_rt::BinarySemaphore usbConnectedSemaphore(/* taken =*/ true);
void onUsbConnectedNotifyMmcI() {
usbConnectedSemaphore.signalI();
}
#endif /* HAL_USE_USB_MSD */
#if HAL_USE_MMC_SPI
/*
* Attempts to initialize the MMC card connected over SPI.
* Returns a BaseBlockDevice* corresponding to the SD card if successful, otherwise nullptr.
*/
static BaseBlockDevice* initializeMmcBlockDevice() {
// Configures and activates the MMC peripheral.
mmcSpiDevice = engineConfiguration->sdCardSpiDevice;
// todo: reuse initSpiCs method?
mmc_hs_spicfg.ssport = mmc_ls_spicfg.ssport = getHwPort("mmc", engineConfiguration->sdCardCsPin);
mmc_hs_spicfg.sspad = mmc_ls_spicfg.sspad = getHwPin("mmc", engineConfiguration->sdCardCsPin);
mmccfg.spip = getSpiDevice(mmcSpiDevice);
// Invalid SPI device, abort.
if (!mmccfg.spip) {
return nullptr;
}
// max SPI rate is 25 MHz after init
spiCalcClockDiv(mmccfg.spip, &mmc_hs_spicfg, 25 * 1000 * 1000);
// and 250 KHz during initialization
spiCalcClockDiv(mmccfg.spip, &mmc_ls_spicfg, 250 * 1000);
// We think we have everything for the card, let's try to mount it!
mmcObjectInit(&MMCD1);
mmcStart(&MMCD1, &mmccfg);
// Performs the initialization procedure on the inserted card.
LOCK_SD_SPI();
if (blkConnect(&MMCD1) != HAL_SUCCESS) {
sdStatus = SD_STATUS_MMC_FAILED;
UNLOCK_SD_SPI();
return nullptr;
}
// We intentionally never unlock in case of success, we take exclusive access of that spi device for SD use
return reinterpret_cast<BaseBlockDevice*>(&MMCD1);
}
static void deinitializeMmcBlockDevide() {
blkDisconnect(&MMCD1); // Brings the driver in a state safe for card removal.
mmcStop(&MMCD1); // Disables the MMC peripheral.
UNLOCK_SD_SPI();
}
#endif /* HAL_USE_MMC_SPI */
#ifndef RE_SDC_MODE
#define RE_SDC_MODE SDC_MODE_4BIT
#endif // RE_SDC_MODE
// Some ECUs are wired for SDIO/SDMMC instead of SPI
#ifdef EFI_SDC_DEVICE
static const SDCConfig sdcConfig = {
.bus_width = RE_SDC_MODE
};
/*
* Attempts to initialize the MMC card connected over SDIO.
* Returns a BaseBlockDevice* corresponding to the SD card if successful, otherwise nullptr.
*/
static BaseBlockDevice* initializeMmcBlockDevice() {
sdcStart(&EFI_SDC_DEVICE, &sdcConfig);
if (blkConnect(&EFI_SDC_DEVICE) != HAL_SUCCESS) {
return nullptr;
}
return reinterpret_cast<BaseBlockDevice*>(&EFI_SDC_DEVICE);
}
static void deinitializeMmcBlockDevide() {
blkDisconnect(&EFI_SDC_DEVICE);
sdcStop(&EFI_SDC_DEVICE);
}
#endif /* EFI_SDC_DEVICE */
#if HAL_USE_USB_MSD
static bool useMsdMode() {
if (engineConfiguration->alwaysWriteSdCard) {
return false;
}
// Wait for the USB stack to wake up, or a 15 second timeout, whichever occurs first
msg_t usbResult = usbConnectedSemaphore.wait(TIME_MS2I(15000));
return usbResult == MSG_OK;
}
#endif // HAL_USE_USB_MSD
static BaseBlockDevice* cardBlockDevice = nullptr;
// Initialize SD card.
static bool initMmc() {
// Don't try to mount SD card in case of fatal error - hardware may be in an unexpected state
if (hasFirmwareError()) {
return false;
}
cardBlockDevice = initializeMmcBlockDevice();
#if EFI_TUNER_STUDIO
// If not null, card is present
engine->outputChannels.sd_present = cardBlockDevice != nullptr;
#endif
return (cardBlockDevice != nullptr);
}
static void deinitMmc() {
if (cardBlockDevice) {
deinitializeMmcBlockDevide();
}
cardBlockDevice = nullptr;
engine->outputChannels.sd_present = false;
}
// Mount the SD card.
// Returns true if the filesystem was successfully mounted for writing.
static bool mountMmc() {
bool ret = false;
// if no card, don't try to mount FS
if (cardBlockDevice != nullptr) {
// We were able to connect the SD card, mount the filesystem
memset(&MMC_FS, 0, sizeof(FATFS));
ret = (f_mount(&MMC_FS, "/", /* Mount immediately */ 1) == FR_OK);
if (ret == false) {
sdStatus = SD_STATUS_MOUNT_FAILED;
efiPrintf("MMC/SD card mount failed!");
}
}
if (ret) {
sdStatus = SD_STATUS_MOUNTED;
efiPrintf("MMC/SD mounted!");
}
#if EFI_TUNER_STUDIO
engine->outputChannels.sd_logging_internal = ret;
#endif
return ret;
}
/*
* MMC card un-mount.
* @return true if we had SD card alive
*/
static void unmountMmc() {
// FATFS: Unregister work area prior to discard it
f_mount(NULL, 0, 0);
#if EFI_TUNER_STUDIO
engine->outputChannels.sd_logging_internal = false;
#endif
efiPrintf("SD card unmounted");
}
#else // not EFI_PROD_CODE (simulator)
bool initMmc() {
// Stub so the loop thinks the MMC is present
return true;
}
bool mountMmc() {
// Stub so the loop thinks the MMC mounted OK
return true;
}
#endif // EFI_PROD_CODE
#if EFI_PROD_CODE
// Log 'regular' ECU log to MLG file
static int mlgLogger();
// Log binary trigger log
static int sdTriggerLogger();
static bool sdLoggerInitDone = false;
static bool sdLoggerFailed = false;
static int sdLogger()
{
int ret = 0;
if (!sdLoggerInitDone) {
incLogFileName(&FDLogFile);
sdLoggerCreateFile(&FDLogFile);
logBuffer.start(&FDLogFile);
resetFileLogging();
sdLoggerInitDone = true;
}
if (!sdLoggerFailed) {
if (engineConfiguration->sdTriggerLog) {
ret = sdTriggerLogger();
} else {
ret = mlgLogger();
}
}
if (ret < 0) {
sdLoggerFailed = true;
}
#ifdef LOGGER_MAX_FILE_SIZE
// check if we need to start next log file
// in next write (assume same size as current) will cross LOGGER_MAX_FILE_SIZE boundary
// TODO: use f_tell() instead ?
if (logBuffer.writen() + ret > LOGGER_MAX_FILE_SIZE) {
logBuffer.stop();
sdLoggerCloseFile(&FDLogFile);
//start new file
incLogFileName(&FDLogFile);
sdLoggerCreateFile(&FDLogFile);
logBuffer.start(&FDLogFile);
resetFileLogging();
}
#endif
return ret;
}
static void sdLoggerStart(void)
{
sdLoggerInitDone = false;
sdLoggerFailed = false;
#if EFI_TOOTH_LOGGER
// TODO: cache this config option untill sdLoggerStop()
if (engineConfiguration->sdTriggerLog) {
EnableToothLogger();
}
#endif
}
static void sdLoggerStop(void)
{
sdLoggerCloseFile(&FDLogFile);
#if EFI_TOOTH_LOGGER
// TODO: cache this config option untill sdLoggerStop()
if (engineConfiguration->sdTriggerLog) {
DisableToothLogger();
}
#endif
}
// TODO: optimal cluster size?
// buffer is needed only for f_mkfs, use some shared buffer?
#define FATFS_CLUSTER_SIZE 1024
static NO_CACHE BYTE formatBuff[FATFS_CLUSTER_SIZE];
static bool sdFormat()
{
//FRESULT ret = f_mkfs("", nullptr, formatBuff, sizeof(formatBuff));
FRESULT ret = f_mkfs("", nullptr, formatBuff, sizeof(formatBuff));
if (ret) {
printError("format failed", ret);
warning(ObdCode::CUSTOM_ERR_SD_MOUNT_FAILED, "SD: format failed");
return false;
}
ret = f_setlabel(SD_CARD_LABEL);
if (ret) {
printError("setlabel failed", ret);
// this is not critical
}
return true;
}
static int sdModeSwitchToIdle(SD_MODE from)
{
switch (from) {
case SD_MODE_IDLE:
return 0;
case SD_MODE_ECU:
sdLoggerStop();
unmountMmc();
return 0;
case SD_MODE_PC:
deattachMsdSdCard();
return 0;
case SD_MODE_UNMOUNT:
return 0;
case SD_MODE_FORMAT:
//not allowed to interrupt formating process
return -1;
}
efiPrintf("Invalid SD card thread state: %d", static_cast<int>(from));
return -1;
}
static int sdModeSwitcher()
{
if (sdTargerMode == SD_MODE_IDLE) {
return 0;
}
if (sdMode == sdTargerMode) {
// already here
sdTargerMode = SD_MODE_IDLE;
return 0;
}
if (sdMode != SD_MODE_IDLE) {
int ret = sdModeSwitchToIdle(sdMode);
if (ret) {
return ret;
}
sdMode = SD_MODE_IDLE;
}
if (sdMode != SD_MODE_IDLE) {
return -1;
}
// Now SD card is in idle state, we can switch into target state
switch (sdTargerMode) {
case SD_MODE_IDLE:
return 0;
case SD_MODE_UNMOUNT:
// everithing is done in sdModeSwitchToIdle();
sdMode = SD_MODE_UNMOUNT;
sdTargerMode = SD_MODE_IDLE;
return 0;
case SD_MODE_ECU:
if (mountMmc()) {
sdLoggerStart();
sdMode = SD_MODE_ECU;
} else {
// failed to mount SD card to ECU, go to idle
sdMode = SD_MODE_IDLE;
}
sdTargerMode = SD_MODE_IDLE;
return 0;
case SD_MODE_PC:
attachMsdSdCard(cardBlockDevice);
sdStatus = SD_STATUS_MSD;
sdMode = SD_MODE_PC;
sdTargerMode = SD_MODE_IDLE;
return 0;
case SD_MODE_FORMAT:
if (sdFormat()) {
// formated ok
}
sdMode = SD_MODE_IDLE;
// TODO: return to mode that was used before format was requested!
sdTargerMode = SD_MODE_IDLE;
return 0;
}
// should not happen
return -1;
}
static int sdModeExecuter()
{
switch (sdMode) {
case SD_MODE_IDLE:
case SD_MODE_PC:
case SD_MODE_UNMOUNT:
case SD_MODE_FORMAT:
// nothing to do in these state, just sleep
chThdSleepMilliseconds(TIME_MS2I(100));
return 0;
case SD_MODE_ECU:
if (sdNeedRemoveReports) {
errorHandlerDeleteReports();
sdNeedRemoveReports = false;
}
// execute logger
return sdLogger();
}
return 0;
}
static int sdReportStorageInit()
{
if (mountMmc()) {
// write error report file if needed
errorHandlerWriteReportFile(&FDLogFile);
// check for any exist reports
errorHandlerCheckReportFiles();
// done with SD card
unmountMmc();
return 0;
}
// card is failed to mount
return -1;
}
static THD_WORKING_AREA(mmcThreadStack, 3 * UTILITY_THREAD_STACK_SIZE); // MMC monitor thread
static THD_FUNCTION(MMCmonThread, arg) {
(void)arg;
chRegSetThreadName("MMC Card Logger");
#if HW_HELLEN && EFI_PROD_CODE
// on mega-module we manage SD card power supply
while (!getHellenBoardEnabled()) {
// wait until board enables peripheral
chThdSleepMilliseconds(100);
if (getTimeNowS() > 4 && !isIgnVoltage()) {
// looks like vehicle is OFF and we are hooked to USB - turn on peripheral to get Mass Storage Device USB profile
efiPrintf(" *** turning board ON to power SD card ***");
hellenEnableEn();
break;
}
}
chThdSleepMilliseconds(300);
#endif
sdStatus = SD_STATUS_CONNECTING;
if (!initMmc()) {
efiPrintf("Card is not preset/failed to init");
sdStatus = SD_STATUS_NOT_INSERTED;
// give up until next boot
goto die;
}
// Try to mount SD card, drop critical report if needed and check for previously stored reports
sdReportStorageInit();
#if HAL_USE_USB_MSD
// Wait for the USB stack to wake up, or a 15 second timeout, whichever occurs first
// If we have a device AND USB is connected, mount the card to USB, otherwise
// mount the null device and try to mount the filesystem ourselves
if (useMsdMode()) {
sdTargerMode = SD_MODE_PC;
}
#endif
while (1) {
sdModeSwitcher();
sdModeExecuter();
}
die:
// bring SD interface to safe state
deinitMmc();
efiPrintf("SD logger has died!");
// exiting thread will create zombie!
while(1) {
chThdSleepMilliseconds(100);
}
}
static int mlgLogger() {
// TODO: move this check somewhere out of here!
// if the SPI device got un-picked somehow, cancel SD card
// Don't do this check at all if using SDMMC interface instead of SPI
#if EFI_PROD_CODE && !defined(EFI_SDC_DEVICE)
if (engineConfiguration->sdCardSpiDevice == SPI_NONE) {
return 0;
}
#endif
systime_t before = chVTGetSystemTime();
size_t writen = writeSdLogLine(logBuffer);
// Something went wrong (already handled), so cancel further writes
if (logBuffer.failed) {
return -1;
}
auto freq = engineConfiguration->sdCardLogFrequency;
if (freq > 250) {
freq = 250;
} else if (freq < 1) {
freq = 1;
}
systime_t period = CH_CFG_ST_FREQUENCY / freq;
chThdSleepUntilWindowed(before, before + period);
return writen;
}
static int sdTriggerLogger() {
size_t toWrite = 0;
#if EFI_TOOTH_LOGGER
auto buffer = GetToothLoggerBufferBlocking();
// can return nullptr
if (buffer) {
toWrite = buffer->nextIdx * sizeof(composite_logger_s);
logBuffer.write(reinterpret_cast<const char*>(buffer->buffer), toWrite);
if (logBuffer.failed) {
return -1;
}
ReturnToothLoggerBuffer(buffer);
}
#endif /* EFI_TOOTH_LOGGER */
return toWrite;
}
#endif // EFI_PROD_CODE
void updateSdCardLiveFlags() {
#if EFI_PROD_CODE
if (cardBlockDevice) {
engine->outputChannels.sd_active_wr = (blkGetDriverState(cardBlockDevice) == BLK_WRITING);
engine->outputChannels.sd_active_rd = (blkGetDriverState(cardBlockDevice) == BLK_READING);
} else
#endif // EFI_PROD_CODE
{
engine->outputChannels.sd_active_wr = false;
engine->outputChannels.sd_active_rd = false;
}
}
// Pre-config load init
void initEarlyMmcCard() {
#if EFI_PROD_CODE
logName[0] = 0;
addConsoleAction("sdinfo", sdStatistics);
addConsoleActionS("del", removeFile);
addConsoleActionS("sdmode", sdSetMode);
addConsoleAction("delreports", sdCardRemoveReportFiles);
//incLogFileName() use same shared FDLogFile, calling it while FDLogFile is used by log writer will cause damage
//addConsoleAction("incfilename", incLogFileName);
#endif // EFI_PROD_CODE
}
static bool isSdCardEnabled() {
return engineConfiguration->isSdCardEnabled &&
engineConfiguration->sdCardSpiDevice != SPI_NONE &&
isBrainPinValid(engineConfiguration->sdCardCsPin);
}
void initMmcCard() {
if (!isSdCardEnabled()) {
// do not even bother starting the thread if SD card is not enabled & configured on start-up
return;
}
#if EFI_PROD_CODE
chThdCreateStatic(mmcThreadStack, sizeof(mmcThreadStack), PRIO_MMC, (tfunc_t)(void*) MMCmonThread, NULL);
#endif // EFI_PROD_CODE
}
#if EFI_PROD_CODE
void sdCardRequestMode(SD_MODE mode)
{
if (sdTargerMode == SD_MODE_IDLE) {
sdTargerMode = mode;
}
}
void sdCardRemoveReportFiles() {
if (sdMode != SD_MODE_ECU) {
efiPrintf("SD card should be mounted to ECU");
return;
}
sdNeedRemoveReports = true;
}
#endif // EFI_PROD_CODE
#endif /* EFI_FILE_LOGGING */