diff --git a/README.md b/README.md index ee2f62f2..356ccf75 100644 --- a/README.md +++ b/README.md @@ -709,6 +709,26 @@ Rebooting currently running image: xsc_boot_copy -r ``` +## Preparation of a fresh rootfs and SD card + +This section summarizes important changes between a fresh rootfs and the current +EIVE implementation + +### rootfs + +- Mount point `/mnt/sd0` created for SD card 0. Created with `mkdir` +- Mount point `/mnt/sd1` created for SD card 1. Created with `mkdir` +- Folder `scripts` in `/home/root` folder. +- `scripts` folder currently contains `update_main_components.sh` script + +### SD Cards + +- Folder `bin` for binaries, for example the OBSW +- Folder `misc` for miscellaneous files +- Folder `tc` for telecommands +- Folder `tm` for telemetry +- Folder `xdi` for XDI components (e.g. for firmware or device tree updates) + # Running the EIVE OBSW on a Raspberry Pi Special section for running the EIVE OBSW on the Raspberry Pi. diff --git a/bsp_q7s/boardconfig/q7sConfig.h.in b/bsp_q7s/boardconfig/q7sConfig.h.in index 9bed2a4a..11dec25c 100644 --- a/bsp_q7s/boardconfig/q7sConfig.h.in +++ b/bsp_q7s/boardconfig/q7sConfig.h.in @@ -4,6 +4,17 @@ #include #cmakedefine01 Q7S_SIMPLE_MODE + +#define Q7S_SD_NONE 0 +#define Q7S_SD_COLD_REDUNDANT 1 +#define Q7S_SD_HOT_REDUNDANT 2 +// The OBSW will perform different actions to set up the SD cards depending on the flag set here +// Set to Q7S_SD_NONE: Don't do anything +// Set to Q7S_COLD_REDUNDANT: On startup, get the prefered SD card, turn it on and mount it, and +// turn off the second SD card if it is on +// Set to Q7S_HOT_REDUNDANT: On startup, turn on both SD cards and mount them +#define Q7S_SD_CARD_CONFIG Q7S_SD_COLD_REDUNDANT + #define Q7S_ADD_RTD_DEVICES 0 /* Only one of those 2 should be enabled! */ diff --git a/bsp_q7s/core/CoreController.cpp b/bsp_q7s/core/CoreController.cpp index 98ddb347..c4863260 100644 --- a/bsp_q7s/core/CoreController.cpp +++ b/bsp_q7s/core/CoreController.cpp @@ -1,4 +1,6 @@ #include "CoreController.h" +#include "q7sConfig.h" + #include "../memory/SdCardManager.h" CoreController::CoreController(object_id_t objectId): @@ -22,39 +24,125 @@ LocalPoolDataSetBase* CoreController::getDataSetHandle(sid_t sid) { } ReturnValue_t CoreController::initialize() { - // Find a way to store this non-volatile outside of SD cards (ProASIC?) - sd::SdCard preferredSdCard = sd::SdCard::SLOT_0; - std::string printoutString; - if(preferredSdCard == sd::SdCard::SLOT_0) { - printoutString = "0"; - } - else { - printoutString = "1"; - } - // Creator or update status file - ReturnValue_t result = SdCardManager::instance()->updateSdCardStateFile(); - if(result != HasReturnvaluesIF::RETURN_OK) { - sif::warning << "CoreController::initialize: Updating SD card state file failed" - << std::endl; - } - - sif::info << "Switching on SD card " << printoutString << ".." << std::endl; - - result = SdCardManager::instance()->switchOnSdCard(preferredSdCard); - if(result == SdCardManager::ALREADY_ON) { - sif::info << "SD card " << printoutString << " is already on" << std::endl; - } - else if(result != HasReturnvaluesIF::RETURN_OK) { - sif::warning << "CoreController::initialize: Turning SD card on failed" - << std::endl; - } - else { - sif::info << "SD card " << printoutString << " was switched on" << std::endl; - } - return HasReturnvaluesIF::RETURN_OK; + return sdCardInit(); } ReturnValue_t CoreController::checkModeCommand(Mode_t mode, Submode_t submode, uint32_t *msToReachTheMode) { return HasReturnvaluesIF::RETURN_OK; } + +ReturnValue_t CoreController::sdCardInit() { +#if Q7S_SD_CARD_CONFIG == Q7S_SD_NONE + sif::info << "No SD card initialization will be performed" << std::endl; + return HasReturnvaluesIF::RETURN_OK; +#else + SdCardManager* sdcMan = SdCardManager::instance(); + + // Create update status file + ReturnValue_t result = sdcMan->updateSdCardStateFile(); + if(result != HasReturnvaluesIF::RETURN_OK) { + sif::warning << "CoreController::initialize: Updating SD card state file failed" + << std::endl; + } + + auto sdStatus = std::pair(sd::SdStatus::OFF, sd::SdStatus::OFF); + result = sdcMan->getSdCardActiveStatus(sdStatus); + if(result != HasReturnvaluesIF::RETURN_OK) { + sif::warning << "Getting SD card activity status failed" << std::endl; + } + + // Use a lambda to avoid duplicate code + auto setUpSdCard = [&](sd::SdCard sdCard, sd::SdStatus status, std::string sdString) { + std::string mountString; + if(sdCard == sd::SdCard::SLOT_0) { + mountString = SdCardManager::SD_0_MOUNT_POINT; + } + else { + mountString = SdCardManager::SD_1_MOUNT_POINT; + } + + if(status == sd::SdStatus::OFF) { + sif::info << "Switching on and mounting SD card " << sdString << " at " << + mountString << std::endl; + return sdcMan->switchOnSdCard(sdCard, true, &sdStatus); + } + else if(status == sd::SdStatus::ON) { + sif::info << "Mounting SD card " << sdString << " at " << mountString << std::endl; + return sdcMan->mountSdCard(sdCard); + } + else { + sif::info << "SD card " << sdString << " already on and mounted at " << + mountString << std::endl; + return SdCardManager::ALREADY_MOUNTED; + } + }; + +#if Q7S_SD_CARD_CONFIG == Q7S_SD_COLD_REDUNDANT + sd::SdCard preferredSdCard = sd::SdCard::SLOT_0; + result = sdcMan->getPreferredSdCard(preferredSdCard); + if(result != HasReturnvaluesIF::RETURN_OK) { + sif::warning << "Could not get preferred SD card information from the scratch buffer" + << std::endl; + } + std::string preferredString; + sd::SdStatus preferredStatus = sd::SdStatus::OFF; + + sd::SdStatus otherStatus = sd::SdStatus::OFF; + std::string otherString; + sd::SdCard otherSdc = sd::SdCard::SLOT_0; + + if(preferredSdCard == sd::SdCard::SLOT_0) { + preferredStatus = sdStatus.first; + preferredString = "0"; + otherSdc = sd::SdCard::SLOT_1; + otherStatus = sdStatus.second; + otherString = "1"; + } + else { + preferredString = "1"; + preferredStatus = sdStatus.second; + otherStatus = sdStatus.first; + otherSdc = sd::SdCard::SLOT_0; + otherString = "0"; + } + + sif::info << "Cold redundant SD card configuration, preferred SD card " << + preferredString << std::endl; + + result = setUpSdCard(preferredSdCard, preferredStatus, preferredString); + if(result != SdCardManager::ALREADY_MOUNTED and result != HasReturnvaluesIF::RETURN_OK) { + sif::warning << "Setting up preferred card " << otherString << + " in cold redundant mode failed" << std::endl; + // Try other SD card and mark set up operation as failed + setUpSdCard(otherSdc, otherStatus, otherString); + result = HasReturnvaluesIF::RETURN_FAILED; + } + + if(result != HasReturnvaluesIF::RETURN_FAILED and otherStatus != sd::SdStatus::OFF) { + sif::info << "Switching off secondary SD card " << otherString << std::endl; + // Switch off other SD card in cold redundant mode if setting up preferred one walked + // without issues + result = sdcMan->switchOffSdCard(otherSdc, otherStatus, &sdStatus); + if(result != HasReturnvaluesIF::RETURN_OK and result != SdCardManager::ALREADY_OFF) { + sif::warning << "Switching off secondary SD card " << otherString << + " in cold redundant mode failed" << std::endl; + } + } + + // Update status file + sdcMan->updateSdCardStateFile(); + return HasReturnvaluesIF::RETURN_OK; +#elif Q7S_SD_CARD_CONFIG == Q7S_SD_HOT_REDUNDANT + sif::info << "Hot redundant SD card configuration" << std::endl; + + setUpSdCard(sd::SdCard::SLOT_0, sdStatus.first, "0"); + setUpSdCard(sd::SdCard::SLOT_1, sdStatus.second, "1"); + // Update status file + sdcMan->updateSdCardStateFile(); + return HasReturnvaluesIF::RETURN_OK; +#endif + +#endif /* Q7S_SD_CARD_CONFIG != Q7S_SD_NONE */ + +} diff --git a/bsp_q7s/core/CoreController.h b/bsp_q7s/core/CoreController.h index 674ed073..940a9097 100644 --- a/bsp_q7s/core/CoreController.h +++ b/bsp_q7s/core/CoreController.h @@ -17,6 +17,8 @@ private: LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override; ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode, uint32_t *msToReachTheMode); + + ReturnValue_t sdCardInit(); }; diff --git a/bsp_q7s/memory/CMakeLists.txt b/bsp_q7s/memory/CMakeLists.txt index ada948fb..6c7d0a94 100644 --- a/bsp_q7s/memory/CMakeLists.txt +++ b/bsp_q7s/memory/CMakeLists.txt @@ -1,6 +1,5 @@ target_sources(${TARGET_NAME} PRIVATE FileSystemHandler.cpp - SdCardAccess.cpp SdCardManager.cpp scratchApi.cpp ) \ No newline at end of file diff --git a/bsp_q7s/memory/FileSystemHandler.cpp b/bsp_q7s/memory/FileSystemHandler.cpp index 5f7e0435..1519b92b 100644 --- a/bsp_q7s/memory/FileSystemHandler.cpp +++ b/bsp_q7s/memory/FileSystemHandler.cpp @@ -4,9 +4,11 @@ #include "fsfw/memory/GenericFileSystemMessage.h" #include "fsfw/ipc/QueueFactory.h" +#include + FileSystemHandler::FileSystemHandler(object_id_t fileSystemHandler): SystemObject(fileSystemHandler) { - mq = QueueFactory::instance()->createMessageQueue(50); + mq = QueueFactory::instance()->createMessageQueue(FS_MAX_QUEUE_SIZE); } FileSystemHandler::~FileSystemHandler() { @@ -15,33 +17,86 @@ FileSystemHandler::~FileSystemHandler() { ReturnValue_t FileSystemHandler::performOperation(uint8_t unsignedChar) { while(true) { - CommandMessage filemsg; - ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; - while(true) { - result = mq->receiveMessage(&filemsg); - if(result == MessageQueueIF::EMPTY) { - break; - } - else if(result != HasReturnvaluesIF::RETURN_FAILED) { - sif::warning << "FileSystemHandler::performOperation: Message reception failed!" - << std::endl; - break; - } - Command_t command = filemsg.getCommand(); - switch(command) { - case(GenericFileSystemMessage::CMD_CREATE_DIRECTORY): { - break; - } - case(GenericFileSystemMessage::CMD_CREATE_FILE): { - break; - } - } + try { + fileSystemHandlerLoop(); } + catch(std::bad_alloc& e) { + // Restart OBSW, hints at a memory leak + sif::error << "Allocation error in FileSystemHandler::performOperation" + << e.what() << std::endl; + // TODO: If we trigger an event, it might not get sent because were restarting + // Set up an error file or a special flag in the scratch buffer. + // TODO: CoreController: Implement function to restart OBC + } + } +} - // This task will have a low priority and will run permanently in the background - // so we will just run in a permanent loop here and check file system - // messages permanently - TaskFactory::instance()->delayTask(1000); + +void FileSystemHandler::fileSystemHandlerLoop() { + CommandMessage filemsg; + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + while(true) { + if(opCounter % 5 == 0) { + fileSystemCheckup(); + } + result = mq->receiveMessage(&filemsg); + if(result == MessageQueueIF::EMPTY) { + break; + } + else if(result != HasReturnvaluesIF::RETURN_FAILED) { + sif::warning << "FileSystemHandler::performOperation: Message reception failed!" + << std::endl; + break; + } + Command_t command = filemsg.getCommand(); + switch(command) { + case(GenericFileSystemMessage::CMD_CREATE_DIRECTORY): { + break; + } + case(GenericFileSystemMessage::CMD_CREATE_FILE): { + break; + } + } + opCounter++; + } + + // This task will have a low priority and will run permanently in the background + // so we will just run in a permanent loop here and check file system + // messages permanently + TaskFactory::instance()->delayTask(1000); +} + +void FileSystemHandler::fileSystemCheckup() { + SdCardManager::SdStatusPair statusPair; + sdcMan->getSdCardActiveStatus(statusPair); + sd::SdCard preferredSdCard; + sdcMan->getPreferredSdCard(preferredSdCard); + if((preferredSdCard == sd::SdCard::SLOT_0) and + (statusPair.first == sd::SdStatus::MOUNTED)) { + currentMountPrefix = SdCardManager::SD_0_MOUNT_POINT; + } + if((preferredSdCard == sd::SdCard::SLOT_1) and + (statusPair.second == sd::SdStatus::MOUNTED)) { + currentMountPrefix = SdCardManager::SD_1_MOUNT_POINT; + } + else { + std::string sdString; + if(preferredSdCard == sd::SdCard::SLOT_0) { + sdString = "0"; + } + else { + sdString = "1"; + } + sif::warning << "FileSystemHandler::performOperation: Inconsistent" << + " state detected. Preferred SD card is " << sdString << + " but does not appear to be mounted. Attempting fix.." << std::endl; + // This function will appear to fix the inconsistent state + ReturnValue_t result = sdcMan->sanitizeState(&preferredSdCard, &statusPair); + if(result != HasReturnvaluesIF::RETURN_OK) { + // Oh no. + // TODO: Trigger medium severity event + sif::error << "Fix failed" << std::endl; + } } } @@ -49,17 +104,81 @@ MessageQueueId_t FileSystemHandler::getCommandQueue() const { return mq->getId(); } +ReturnValue_t FileSystemHandler::initialize() { + sdcMan = SdCardManager::instance(); + sd::SdCard preferredSdCard; + ReturnValue_t result = sdcMan->getPreferredSdCard(preferredSdCard); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + if(preferredSdCard == sd::SdCard::SLOT_0) { + currentMountPrefix = SdCardManager::SD_0_MOUNT_POINT; + } + else if(preferredSdCard == sd::SdCard::SLOT_1) { + currentMountPrefix = SdCardManager::SD_1_MOUNT_POINT; + } + return HasReturnvaluesIF::RETURN_OK; +} + ReturnValue_t FileSystemHandler::appendToFile(const char *repositoryPath, const char *filename, const uint8_t *data, size_t size, uint16_t packetNumber, void *args) { + // A double slash between repo and filename should not be an issue, so add it in any case + std::string fullPath = currentMountPrefix + std::string(repositoryPath) + "/" + + std::string(filename); return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t FileSystemHandler::createFile(const char *repositoryPath, const char *filename, const uint8_t *data, size_t size, void *args) { + // A double slash between repo and filename should not be an issue, so add it in any case + std::string fullPath = currentMountPrefix + std::string(repositoryPath) + "/" + + std::string(filename); return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t FileSystemHandler::deleteFile(const char *repositoryPath, const char *filename, void *args) { + // A double slash between repo and filename should not be an issue, so add it in any case + std::string fullPath = currentMountPrefix + std::string(repositoryPath) + "/" + + std::string(filename); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t FileSystemHandler::createDirectory(const char *repositoryPath, void *args) { + std::string fullPath = currentMountPrefix + std::string(repositoryPath); + if(std::filesystem::exists(fullPath)) { + return DIRECTORY_ALREADY_EXISTS; + } + if(std::filesystem::create_directory(fullPath)) { + return HasReturnvaluesIF::RETURN_OK; + } + sif::warning << "Creating directory " << fullPath << " failed" << std::endl; + return GENERIC_FILE_ERROR; +} + +ReturnValue_t FileSystemHandler::removeDirectory(const char *repositoryPath, + bool deleteRecurively, void *args) { + std::string fullPath = currentMountPrefix + std::string(repositoryPath); + if(not std::filesystem::exists(fullPath)) { + return DIRECTORY_DOES_NOT_EXIST; + } + std::error_code err; + if(not deleteRecurively) { + if(std::filesystem::remove(fullPath, err)) { + return HasReturnvaluesIF::RETURN_OK; + } + else { + // Check error code. Most probably denied permissions because folder is not empty + } + } + else { + if(std::filesystem::remove_all(fullPath, err)) { + return HasReturnvaluesIF::RETURN_OK; + } + else { + // Check error code + } + } + return HasReturnvaluesIF::RETURN_OK; } diff --git a/bsp_q7s/memory/FileSystemHandler.h b/bsp_q7s/memory/FileSystemHandler.h index 04cea3dc..886fa574 100644 --- a/bsp_q7s/memory/FileSystemHandler.h +++ b/bsp_q7s/memory/FileSystemHandler.h @@ -1,11 +1,16 @@ #ifndef BSP_Q7S_MEMORY_FILESYSTEMHANDLER_H_ #define BSP_Q7S_MEMORY_FILESYSTEMHANDLER_H_ +#include "SdCardManager.h" +#include "OBSWConfig.h" + #include "fsfw/ipc/MessageQueueIF.h" #include "fsfw/tasks/ExecutableObjectIF.h" #include "fsfw/objectmanager/SystemObject.h" #include "fsfw/memory/HasFileSystemIF.h" +#include + class FileSystemHandler: public SystemObject, public ExecutableObjectIF, public HasFileSystemIF { @@ -15,6 +20,8 @@ public: ReturnValue_t performOperation(uint8_t) override; + ReturnValue_t initialize() override; + /** * Function to get the MessageQueueId_t of the implementing object * @return MessageQueueId_t of the object @@ -23,6 +30,13 @@ public: private: MessageQueueIF* mq = nullptr; + std::string currentMountPrefix = SdCardManager::SD_0_MOUNT_POINT; + static constexpr uint32_t FS_MAX_QUEUE_SIZE = config::OBSW_FILESYSTEM_HANDLER_QUEUE_SIZE; + SdCardManager* sdcMan = nullptr; + uint8_t opCounter = 0; + + void fileSystemHandlerLoop(); + void fileSystemCheckup(); ReturnValue_t appendToFile(const char* repositoryPath, const char* filename, const uint8_t* data, size_t size, @@ -32,6 +46,9 @@ private: size_t size = 0, void* args = nullptr) override; ReturnValue_t deleteFile(const char* repositoryPath, const char* filename, void* args = nullptr) override; + ReturnValue_t createDirectory(const char* repositoryPath, void* args = nullptr) override; + ReturnValue_t removeDirectory(const char* repositoryPath, bool deleteRecurively = false, + void* args = nullptr) override; }; diff --git a/bsp_q7s/memory/SdCardAccess.cpp b/bsp_q7s/memory/SdCardAccess.cpp deleted file mode 100644 index 92837787..00000000 --- a/bsp_q7s/memory/SdCardAccess.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "SdCardAccess.h" - -#include "fsfw/ipc/MutexGuard.h" - -SdCardAccess::SdCardAccess(sd::SdCard sdCard) { - -} diff --git a/bsp_q7s/memory/SdCardAccess.h b/bsp_q7s/memory/SdCardAccess.h deleted file mode 100644 index 8128b122..00000000 --- a/bsp_q7s/memory/SdCardAccess.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef BSP_Q7S_MEMORY_SDCARDACCESS_H_ -#define BSP_Q7S_MEMORY_SDCARDACCESS_H_ - -#include "definitions.h" - -class SdCardAccess { -public: - SdCardAccess(sd::SdCard sdCard); -private: -}; - - -#endif /* BSP_Q7S_MEMORY_SDCARDACCESS_H_ */ diff --git a/bsp_q7s/memory/SdCardManager.cpp b/bsp_q7s/memory/SdCardManager.cpp index af963180..5a50665b 100644 --- a/bsp_q7s/memory/SdCardManager.cpp +++ b/bsp_q7s/memory/SdCardManager.cpp @@ -1,8 +1,13 @@ #include "SdCardManager.h" +#include "scratchApi.h" + +#include "linux/utility/utility.h" + #include "fsfw/ipc/MutexFactory.h" #include "fsfw/serviceinterface/ServiceInterface.h" #include +#include #include SdCardManager* SdCardManager::factoryInstance = nullptr; @@ -24,35 +29,99 @@ SdCardManager* SdCardManager::instance() { return SdCardManager::factoryInstance; } -ReturnValue_t SdCardManager::switchOnSdCard(sd::SdCard sdCard) { - std::pair active; - ReturnValue_t result = sdCardActive(active); - if(result != HasReturnvaluesIF::RETURN_OK) { +ReturnValue_t SdCardManager::switchOnSdCard(sd::SdCard sdCard, bool doMountSdCard, + SdStatusPair* statusPair) { + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + if(statusPair == nullptr) { + statusPair = std::make_unique().get(); + result = getSdCardActiveStatus(*statusPair); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + } + + // Not allowed, this function turns on one SD card + if(sdCard == sd::SdCard::BOTH) { + sif::warning << "SdCardManager::switchOffSdCard: API does not allow sd::SdStatus::BOTH" + << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + + sd::SdStatus targetStatus; + if(sdCard == sd::SdCard::SLOT_0) { + targetStatus = statusPair->first; + } + else if(sdCard == sd::SdCard::SLOT_1) { + targetStatus = statusPair->second; + } + + auto switchCall = [&]() { + if(targetStatus == sd::SdStatus::ON) { + if(not doMountSdCard) { + return ALREADY_ON; + } + else { + return mountSdCard(sdCard); + } + } + else if(targetStatus == sd::SdStatus::MOUNTED) { + return ALREADY_MOUNTED; + } + else if(targetStatus == sd::SdStatus::OFF) { + return setSdCardState(sdCard, true); + } + else { + return HasReturnvaluesIF::RETURN_FAILED; + } + }; + + result = switchCall(); + + if(result != HasReturnvaluesIF::RETURN_OK or not doMountSdCard) { return result; } - if((sdCard == sd::SdCard::SLOT_0 and active.first) or - (sdCard == sd::SdCard::SLOT_1 and active.second)) { - return ALREADY_ON; - } - return setSdCardState(sdCard, true); + + return mountSdCard(sdCard); } -ReturnValue_t SdCardManager::switchOffSdCard(sd::SdCard sdCard) { - std::pair active; - ReturnValue_t result = sdCardActive(active); +ReturnValue_t SdCardManager::switchOffSdCard(sd::SdCard sdCard, bool doUnmountSdCard, + SdStatusPair* statusPair) { + std::pair active; + ReturnValue_t result = getSdCardActiveStatus(active); if(result != HasReturnvaluesIF::RETURN_OK) { return result; } - if((sdCard == sd::SdCard::SLOT_0 and not active.first) or - (sdCard == sd::SdCard::SLOT_1 and not active.second)) { - return ALREADY_OFF; + // Not allowed, this function turns off one SD card + if(sdCard == sd::SdCard::BOTH) { + sif::warning << "SdCardManager::switchOffSdCard: API does not allow sd::SdStatus::BOTH" + << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; } + if(sdCard == sd::SdCard::SLOT_0) { + if(active.first == sd::SdStatus::OFF) { + return ALREADY_OFF; + } + } + else if(sdCard == sd::SdCard::SLOT_1) { + if(active.second == sd::SdStatus::OFF) { + return ALREADY_OFF; + } + } + + if(doUnmountSdCard) { + result = unmountSdCard(sdCard); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + } + return setSdCardState(sdCard, false); } ReturnValue_t SdCardManager::setSdCardState(sd::SdCard sdCard, bool on) { - std::string sdstring = ""; - std::string statestring = ""; + using namespace std; + string sdstring = ""; + string statestring = ""; if(sdCard == sd::SdCard::SLOT_0) { sdstring = "0"; } @@ -65,9 +134,9 @@ ReturnValue_t SdCardManager::setSdCardState(sd::SdCard sdCard, bool on) { else { statestring = "off"; } - std::ostringstream command; + ostringstream command; command << "q7hw sd set " << sdstring << " " << statestring; - int result = std::system(command.str().c_str()); + int result = system(command.str().c_str()); if(result == 0) { return HasReturnvaluesIF::RETURN_OK; } @@ -76,71 +145,185 @@ ReturnValue_t SdCardManager::setSdCardState(sd::SdCard sdCard, bool on) { return SYSTEM_CALL_ERROR; } -ReturnValue_t SdCardManager::sdCardActive(std::pair& active) { +ReturnValue_t SdCardManager::getSdCardActiveStatus(SdStatusPair& active) { using namespace std; - if(not filesystem::exists("/tmp/sd_status.txt")) { + if(not filesystem::exists(SD_STATE_FILE)) { return STATUS_FILE_NEXISTS; } // Now the file should exist in any case. Still check whether it exists. - fstream sdStatus("/tmp/sd_status.txt"); + fstream sdStatus(SD_STATE_FILE); if (not sdStatus.good()) { return STATUS_FILE_NEXISTS; } string line; uint8_t idx = 0; - + sd::SdCard currentSd = sd::SdCard::SLOT_0; + // Process status file line by line while (std::getline(sdStatus, line)) { - istringstream iss(line); - string word; - sd::SdCard currentSd = sd::SdCard::SLOT_0; - while(iss >> word) { + processSdStatusLine(active, line, idx, currentSd); + } + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t SdCardManager::mountSdCard(sd::SdCard sdCard) { + using namespace std; + if(sdCard == sd::SdCard::BOTH) { + sif::warning << "SdCardManager::mountSdCard: API does not allow sd::SdStatus::BOTH" + << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + string mountDev; + string mountPoint; + if(sdCard == sd::SdCard::SLOT_0) { + mountDev = SD_0_DEV_NAME; + mountPoint = SD_0_MOUNT_POINT; + } + else if(sdCard == sd::SdCard::SLOT_1) { + mountDev = SD_1_DEV_NAME; + mountPoint = SD_1_MOUNT_POINT; + } + if(not filesystem::exists(mountDev)) { + sif::warning << "SdCardManager::mountSdCard: Device file does not exists. Make sure to" + " turn on the SD card" << std::endl; + return MOUNT_ERROR; + } + + string sdMountCommand = "mount " + mountDev + " " + mountPoint; + int result = system(sdMountCommand.c_str()); + if (result != 0) { + utility::handleSystemError(result, "SdCardManager::mountSdCard"); + return SYSTEM_CALL_ERROR; + } + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t SdCardManager::unmountSdCard(sd::SdCard sdCard) { + using namespace std; + if(sdCard == sd::SdCard::BOTH) { + sif::warning << "SdCardManager::unmountSdCard: API does not allow sd::SdStatus::BOTH" + << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + string mountPoint; + if(sdCard == sd::SdCard::SLOT_0) { + mountPoint = SD_0_MOUNT_POINT; + } + else if(sdCard == sd::SdCard::SLOT_1) { + mountPoint = SD_1_MOUNT_POINT; + } + if(filesystem::is_empty(mountPoint)) { + // The mount point will always exist, but if it is empty, that is strong hint that + // the SD card was not mounted properly. Still proceed with operation. + sif::warning << "SdCardManager::unmountSdCard: Mount point is empty!" << std::endl; + } + string sdUnmountCommand = "umount " + mountPoint; + int result = system(sdUnmountCommand.c_str()); + if (result != 0) { + utility::handleSystemError(result, "SdCardManager::unmountSdCard"); + return SYSTEM_CALL_ERROR; + } + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t SdCardManager::sanitizeState(sd::SdCard* prefSdCard, SdStatusPair* statusPair) { + if(prefSdCard == nullptr) { + prefSdCard = std::make_unique(sd::SdCard::SLOT_0).get(); + getPreferredSdCard(*prefSdCard); + } + if(statusPair == nullptr) { + statusPair = std::make_unique().get(); + getSdCardActiveStatus(*statusPair); + } + + auto sanitizerFunc = [&](sd::SdCard prefSdCard) { + if(statusPair->first == sd::SdStatus::ON) { + return mountSdCard(prefSdCard); + } + else { + return switchOnSdCard(prefSdCard, true, statusPair); + } + }; + + return sanitizerFunc(*prefSdCard); +} + +void SdCardManager::processSdStatusLine(std::pair &active, + std::string& line, uint8_t& idx, sd::SdCard& currentSd) { + using namespace std; + istringstream iss(line); + string word; + bool slotLine = false; + bool mountLine = false; + while(iss >> word) { + if (word == "Slot") { + slotLine = true; + } + if(word == "Mounted") { + mountLine = true; + } + + if(slotLine) { if (word == "1:") { currentSd = sd::SdCard::SLOT_1; } if(word == "on") { if(currentSd == sd::SdCard::SLOT_0) { - active.first = true; + active.first = sd::SdStatus::ON; } else { - active.second = true; + active.second = sd::SdStatus::ON; } } else if (word == "off") { if(currentSd == sd::SdCard::SLOT_0) { - active.first = false; + active.first = sd::SdStatus::OFF; } else { - active.second = false; + active.second = sd::SdStatus::OFF; } } - else { - continue; - } + } - if(idx > 3) { - sif::warning << "SdCardManager::sdCardActive: Status file has more " - "than 4 lines!" << std::endl; - return STATUS_FILE_FORMAT_INVALID; + if(mountLine) { + if(currentSd == sd::SdCard::SLOT_0) { + active.first = sd::SdStatus::MOUNTED; + } + else { + active.second = sd::SdStatus::MOUNTED; } } - idx++; + + if(idx > 5) { + sif::warning << "SdCardManager::sdCardActive: /tmp/sd_status.txt has more than 6 " + "lines and might be invalid!" << std::endl; + } } + idx++; +} + +ReturnValue_t SdCardManager::getPreferredSdCard(sd::SdCard& sdCard) const { + uint8_t prefSdCard = 0; + ReturnValue_t result = scratch::readNumber(scratch::PREFERED_SDC_KEY, prefSdCard); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + sdCard = static_cast(prefSdCard); return HasReturnvaluesIF::RETURN_OK; } -sd::SdCard SdCardManager::getPreferredSdCard() const { - //int result = std::system("xsc_scratch read PREFSD > /tmp/pref_sd.txt"); - return preferredSdCard; -} - -void SdCardManager::setPreferredSdCard(sd::SdCard sdCard) { - preferredSdCard = sdCard; +ReturnValue_t SdCardManager::setPreferredSdCard(sd::SdCard sdCard) { + if(sdCard == sd::SdCard::BOTH) { + return HasReturnvaluesIF::RETURN_FAILED; + } + return scratch::writeNumber(scratch::PREFERED_SDC_KEY, static_cast(sdCard)); } ReturnValue_t SdCardManager::updateSdCardStateFile() { - int result = std::system("q7hw sd info all > /tmp/sd_status.txt"); + // Use q7hw utility and pipe the command output into the state file + std::string updateCmd = "q7hw sd info all > " + std::string(SD_STATE_FILE); + int result = std::system(updateCmd.c_str()); if(result == 0) { return HasReturnvaluesIF::RETURN_OK; } diff --git a/bsp_q7s/memory/SdCardManager.h b/bsp_q7s/memory/SdCardManager.h index 0c9c664d..966e6e2a 100644 --- a/bsp_q7s/memory/SdCardManager.h +++ b/bsp_q7s/memory/SdCardManager.h @@ -8,6 +8,7 @@ #include #include +#include class MutexIF; @@ -18,16 +19,33 @@ class MutexIF; class SdCardManager { friend class SdCardAccess; public: + using SdStatusPair = std::pair; + static constexpr uint8_t INTERFACE_ID = CLASS_ID::SD_CARD_MANAGER; - static constexpr ReturnValue_t ALREADY_ON = HasReturnvaluesIF::makeReturnCode(INTERFACE_ID, 0); - static constexpr ReturnValue_t ALREADY_OFF = HasReturnvaluesIF::makeReturnCode(INTERFACE_ID, 1); - static constexpr ReturnValue_t STATUS_FILE_NEXISTS = + static constexpr ReturnValue_t ALREADY_ON = + HasReturnvaluesIF::makeReturnCode(INTERFACE_ID, 0); + static constexpr ReturnValue_t ALREADY_MOUNTED = + HasReturnvaluesIF::makeReturnCode(INTERFACE_ID, 1); + static constexpr ReturnValue_t ALREADY_OFF = HasReturnvaluesIF::makeReturnCode(INTERFACE_ID, 2); + static constexpr ReturnValue_t STATUS_FILE_NEXISTS = + HasReturnvaluesIF::makeReturnCode(INTERFACE_ID, 10); static constexpr ReturnValue_t STATUS_FILE_FORMAT_INVALID = - HasReturnvaluesIF::makeReturnCode(INTERFACE_ID, 3); + HasReturnvaluesIF::makeReturnCode(INTERFACE_ID, 11); + static constexpr ReturnValue_t MOUNT_ERROR = + HasReturnvaluesIF::makeReturnCode(INTERFACE_ID, 12); + static constexpr ReturnValue_t UNMOUNT_ERROR = + HasReturnvaluesIF::makeReturnCode(INTERFACE_ID, 13); static constexpr ReturnValue_t SYSTEM_CALL_ERROR = - HasReturnvaluesIF::makeReturnCode(INTERFACE_ID, 4); + HasReturnvaluesIF::makeReturnCode(INTERFACE_ID, 14); + + // C++17 does not support constexpr std::string yet + static constexpr char SD_0_DEV_NAME[] = "/dev/mmcblk0p1"; + static constexpr char SD_1_DEV_NAME[] = "/dev/mmcblk1p1"; + static constexpr char SD_0_MOUNT_POINT[] = "/mnt/sd0"; + static constexpr char SD_1_MOUNT_POINT[] = "/mnt/sd1"; + static constexpr char SD_STATE_FILE[] = "/tmp/sd_status.txt"; virtual ~SdCardManager(); @@ -38,29 +56,50 @@ public: */ static SdCardManager* instance(); - void setPreferredSdCard(sd::SdCard sdCard); - - sd::SdCard getPreferredSdCard() const; + /** + * Set the preferred SD card which will determine which SD card will be used as the primary + * SD card in hot redundant and cold redundant mode. This function will not switch the + * SD cards which are currently on and mounted, this needs to be implemented by + * an upper layer by using #switchOffSdCard , #switchOnSdCard and #updateSdCardStateFile + * @param sdCard + * @return + */ + ReturnValue_t setPreferredSdCard(sd::SdCard sdCard); /** - * Switch on the specified SD card + * Get the currently configured preferred SD card * @param sdCard + * @return + */ + ReturnValue_t getPreferredSdCard(sd::SdCard& sdCard) const; + + /** + * Switch on the specified SD card. + * @param sdCard + * @param doMountSdCard Mount the SD card after switching it on, which is necessary + * to use it + * @param statusPair If the status pair is already available, it can be passed here * @return - RETURN_OK on success, ALREADY_ON if it is already on, * SYSTEM_CALL_ERROR on system error */ - ReturnValue_t switchOnSdCard(sd::SdCard sdCard); + ReturnValue_t switchOnSdCard(sd::SdCard sdCard, bool doMountSdCard = true, + SdStatusPair* statusPair = nullptr); /** - * Switch off the specified SD card + * Switch off the specified SD card. * @param sdCard + * @param doUnmountSdCard Unmount the SD card before switching the card off, which makes + * the operation safer + * @param statusPair If the status pair is already available, it can be passed here * @return - RETURN_OK on success, ALREADY_ON if it is already on, * SYSTEM_CALL_ERROR on system error */ - ReturnValue_t switchOffSdCard(sd::SdCard sdCard); + ReturnValue_t switchOffSdCard(sd::SdCard sdCard, bool doUnmountSdCard = true, + SdStatusPair* statusPair = nullptr); /** - * Updated the state file or creates one if it does not exist. You need to call this - * function before calling #sdCardActiv + * Update the state file or creates one if it does not exist. You need to call this + * function before calling #sdCardActive * @return - RETURN_OK if the state file was updated successfully * - SYSTEM_CALL_ERROR if the call to create the status file failed */ @@ -77,16 +116,42 @@ public: * should call #updateSdCardStateFile again in that case * - STATUS_FILE_NEXISTS if the status file does not exist */ - ReturnValue_t sdCardActive(std::pair& active); + ReturnValue_t getSdCardActiveStatus(SdStatusPair& active); - sd::SdCard getPreferedSdCard() const; + /** + * Mount the specified SD card. This is necessary to use it. + * @param sdCard + * @return + */ + ReturnValue_t mountSdCard(sd::SdCard sdCard); + /** + * Unmount the specified SD card. This is recommended before switching it off. The SD card + * can't be used after it has been unmounted. + * @param sdCard + * @return + */ + ReturnValue_t unmountSdCard(sd::SdCard sdCard); + + /** + * In case that there is a discrepancy between the preferred SD card and the currently + * mounted one, this function will sanitize the state by attempting to mount the + * currently preferred SD card. If the caller already has state information, it can be + * passed into the function. + * @param prefSdCard Preferred SD card captured with #getPreferredSdCard + * @param statusPair Current SD card status capture with #getSdCardActiveStatus + * @throws std::bad_alloc if one of the two arguments was a nullptr and an allocation failed + * @return + */ + ReturnValue_t sanitizeState(sd::SdCard* prefSdCard = nullptr, + SdStatusPair* statusPair = nullptr); private: SdCardManager(); ReturnValue_t setSdCardState(sd::SdCard sdCard, bool on); - sd::SdCard preferredSdCard = sd::SdCard::SLOT_0; + void processSdStatusLine(SdStatusPair& active, std::string& line, uint8_t& idx, + sd::SdCard& currentSd); static SdCardManager* factoryInstance; }; diff --git a/bsp_q7s/memory/definitions.h b/bsp_q7s/memory/definitions.h index 9a998dcc..546a6585 100644 --- a/bsp_q7s/memory/definitions.h +++ b/bsp_q7s/memory/definitions.h @@ -1,9 +1,18 @@ #ifndef BSP_Q7S_MEMORY_DEFINITIONS_H_ #define BSP_Q7S_MEMORY_DEFINITIONS_H_ +#include + namespace sd { -enum SdCard { +enum SdStatus: uint8_t { + OFF = 0, + ON = 1, + // A mounted SD card is on as well + MOUNTED = 2 +}; + +enum SdCard: uint8_t { SLOT_0, SLOT_1, BOTH diff --git a/bsp_q7s/memory/scratchApi.h b/bsp_q7s/memory/scratchApi.h index 09bf6bab..eceda11a 100644 --- a/bsp_q7s/memory/scratchApi.h +++ b/bsp_q7s/memory/scratchApi.h @@ -16,6 +16,8 @@ */ namespace scratch { +static constexpr char PREFERED_SDC_KEY[] = "PREFSD"; + namespace { static uint8_t counter = 0; } diff --git a/fsfw b/fsfw index eef2fd3b..da8a4470 160000 --- a/fsfw +++ b/fsfw @@ -1 +1 @@ -Subproject commit eef2fd3b7ac764f06ed5d9a3c97c894d92a515fa +Subproject commit da8a4470734808bed4d872e47c192af694382c41 diff --git a/linux/fsfwconfig/OBSWConfig.h.in b/linux/fsfwconfig/OBSWConfig.h.in index a5a439ab..ef6b419d 100644 --- a/linux/fsfwconfig/OBSWConfig.h.in +++ b/linux/fsfwconfig/OBSWConfig.h.in @@ -14,6 +14,8 @@ #include "commonConfig.h" #include "OBSWVersion.h" +#define OBSW_FILESYSTEM_HANDLER_QUEUE_SIZE 50 + /* These defines should be disabled for mission code but are useful for debugging. */ #define OBSW_VERBOSE_LEVEL 1