LittleFs_SDCard/src/AzureSphere/SDCard_HighLevelApp/main.c (228 lines of code) (raw):
/* Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License. */
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <assert.h>
#include <sys/ioctl.h>
#include <applibs/log.h>
#include <applibs/gpio.h>
#include "eventloop_timer_utilities.h"
#include "lfs.h"
#include "lfs_util.h"
#include "SDCardViaRtCore.h"
#include "hw/mt3620_rdb.h"
/// <summary>
/// Exit codes for this application. These are used for the
/// application exit code. They must all be between zero and 255,
/// where zero is reserved for successful termination.
/// </summary>
typedef enum {
ExitCode_Success = 0,
ExitCode_TermHandler_SigTerm = 1,
ExitCode_Init_EventLoop = 2,
ExitCode_Init_Connection = 3,
ExitCode_Init_SetSockOpt = 4,
ExitCode_Main_EventLoopFail = 5,
ExitCode_ButtonTimer_Consume = 6,
ExitCode_Init_Button = 7,
ExitCode_Init_ButtonPollTimer = 8,
ExitCode_ButtonTimer_GetButtonState = 9
} ExitCode;
static EventLoop* eventLoop = NULL;
static int buttonA_fd = -1;
static EventLoopTimer* buttonPollTimer = NULL;
static GPIO_Value_Type buttonState = GPIO_Value_High;
static void ButtonTimerEventHandler(EventLoopTimer* timer);
static volatile sig_atomic_t exitCode = ExitCode_Success;
static void TerminationHandler(int signalNumber);
static void InitSigterm(void);
static ExitCode InitHandlers(void);
static void CloseHandlers(void);
// SD Card uses 512 Byte blocks
// 4MB Card size = 4,194,304 bytes - 8192 blocks
// 2GB Card size = 2,147,483,648 bytes = 4194304 total blocks
//
// The Project is configured for 4MB Storage (8192 blocks)
#define BLOCK_SIZE 512
#define TOTAL_BLOCKS 8192 // TODO: Modify TOTAL_BLOCKS to match your SD Card configuration (total bytes/512)
char* writeMessage = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua\r\n";
static int storage_read(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, void* buffer, lfs_size_t size);
static int storage_program(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, const void* buffer, lfs_size_t size);
static int storage_erase(const struct lfs_config* c, lfs_block_t block);
static int storage_sync(const struct lfs_config* c);
static lfs_t lfs;
const struct lfs_config g_littlefs_config = {
.read = storage_read,
.prog = storage_program,
.erase = storage_erase,
.sync = storage_sync,
.read_size = BLOCK_SIZE,
.prog_size = BLOCK_SIZE,
.block_size = BLOCK_SIZE,
.block_count = TOTAL_BLOCKS,
.block_cycles = 1000,
.cache_size = BLOCK_SIZE,
.lookahead_size = BLOCK_SIZE,
.name_max = 255
};
/// <summary>
/// Function to exercise LittleFs
/// </summary>
static void DoLittleFswork(void)
{
Log_Debug("Button A Handler - Do LittleFs work\n");
if (lfs_mount(&lfs, &g_littlefs_config) != LFS_ERR_OK) {
Log_Debug("Format and Mount\n");
assert(lfs_format(&lfs, &g_littlefs_config) == LFS_ERR_OK);
assert(lfs_mount(&lfs, &g_littlefs_config) == LFS_ERR_OK);
}
lfs_file_t datafile;
// Create a directory, create a file in the directory, write, then read data from the file.
size_t dataLength = strlen(writeMessage);
// Create the directory
Log_Debug("Create Directory '/data'\n");
assert(lfs_mkdir(&lfs, "/data") == LFS_ERR_OK);
// Create a file
Log_Debug("Create File /data/lorem.txt\n");
assert(lfs_file_open(&lfs, &datafile, "/data/lorem.txt", LFS_O_RDWR | LFS_O_CREAT) == LFS_ERR_OK);
// Write 'Lorem' data to the file
Log_Debug("Write to file: %s\n", writeMessage);
assert(lfs_file_write(&lfs, &datafile, writeMessage, dataLength) == dataLength);
// Rewind to the start of the file
Log_Debug("Rewind file pointer\n");
assert(lfs_file_seek(&lfs, &datafile, 0, LFS_SEEK_SET) == 0);
// Read from the file
char buffer[dataLength + 1];
memset(buffer, 0x00, dataLength + 1);
Log_Debug("Read from /data/lorem.txt\n");
assert(lfs_file_read(&lfs, &datafile, buffer, dataLength) == dataLength);
// Log the data that was read
Log_Debug("Read data: %s\n", buffer);
// Close the file
Log_Debug("Close file\n");
assert(lfs_file_close(&lfs, &datafile) == LFS_ERR_OK);
// Get the file size of the directory file.
struct lfs_info lfsInfo;
lfs_stat(&lfs, "/data/lorem.txt", &lfsInfo);
Log_Debug("/data/lorem.txt size (bytes): %u\n", lfsInfo.size);
// clean up
Log_Debug("Clean up\n");
// remove the file first.
Log_Debug("Delete file\n");
assert(lfs_remove(&lfs, "/data/lorem.txt") == LFS_ERR_OK);
// remove the directory.
Log_Debug("Delete directory\n");
assert(lfs_remove(&lfs, "/data") == LFS_ERR_OK);
}
/// <summary>
/// Littlefs callback function to handle reads from storage
/// </summary>
static int storage_read(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, void* buffer, lfs_size_t size)
{
// just need to read the block number/data over SD/Intercore.
#ifdef SHOW_DEBUG_INFO
Log_Debug("Read Block %d\n", block);
#endif
if (SDCard_ReadBlock(block, buffer, size) != LFS_ERR_OK)
return LFS_ERR_IO;
return LFS_ERR_OK;
}
/// <summary>
/// Littlefs callback function to handle writes to storage
/// </summary>
static int storage_program(const struct lfs_config* c, lfs_block_t block, lfs_off_t off, const void* buffer, lfs_size_t size)
{
if (SDCard_WriteBlock(block, buffer, size) != LFS_ERR_OK)
return LFS_ERR_IO;
return LFS_ERR_OK;
}
/// <summary>
/// Littlefs callback function to erase a storage block
/// </summary>
static int storage_erase(const struct lfs_config* c, lfs_block_t block)
{
#ifdef SHOW_DEBUG_INFO
Log_Debug("Erase Block %d\n", block);
#endif
uint8_t block_data[c->block_size];
memset(&block_data, 0x00, c->block_size);
return storage_program(c, block, 0x00, &block_data, c->block_size);
}
/// <summary>
/// Littlefs callback function to sync storage (not used)
/// </summary>
static int storage_sync(const struct lfs_config* c)
{
return LFS_ERR_OK;
}
/// <summary>
/// Timer callback to check for 'Button A' press
/// </summary>
static void ButtonTimerEventHandler(EventLoopTimer* timer)
{
if (ConsumeEventLoopTimerEvent(timer) != 0) {
exitCode = ExitCode_ButtonTimer_Consume;
return;
}
// Check for a button press
GPIO_Value_Type newButtonState;
int result = GPIO_GetValue(buttonA_fd, &newButtonState);
if (result != 0) {
Log_Debug("ERROR: Could not read button GPIO: %s (%d).\n", strerror(errno), errno);
exitCode = ExitCode_ButtonTimer_GetButtonState;
return;
}
// If the button has just been pressed, change the LED blink interval
// The button has GPIO_Value_Low when pressed and GPIO_Value_High when released
if (newButtonState != buttonState) {
if (newButtonState == GPIO_Value_Low) {
DoLittleFswork();
}
buttonState = newButtonState;
}
}
/// <summary>
/// Signal handler for termination requests. This handler must be async-signal-safe.
/// </summary>
static void TerminationHandler(int signalNumber)
{
// Don't use Log_Debug here, as it is not guaranteed to be async-signal-safe.
exitCode = ExitCode_TermHandler_SigTerm;
}
/// <summary>
/// Set up SIGTERM termination handler and event handlers for send timer
/// </summary>
static void InitSigterm(void)
{
struct sigaction action;
memset(&action, 0, sizeof(struct sigaction));
action.sa_handler = TerminationHandler;
sigaction(SIGTERM, &action, NULL);
}
/// <summary>
/// Set up SIGTERM termination handler and event handlers for send timer
/// and to receive data from real-time capable application.
/// </summary>
/// <returns>
/// ExitCode_Success if all resources were allocated successfully; otherwise another
/// ExitCode value which indicates the specific failure.
/// </returns>
static ExitCode InitHandlers(void)
{
eventLoop = EventLoop_Create();
if (eventLoop == NULL) {
Log_Debug("Could not create event loop.\n");
return ExitCode_Init_EventLoop;
}
Log_Debug("Opening Button A as input.\n");
buttonA_fd = GPIO_OpenAsInput(MT3620_RDB_BUTTON_A);
if (buttonA_fd == -1) {
Log_Debug("ERROR: Could not open Button A: %s (%d).\n", strerror(errno), errno);
return ExitCode_Init_Button;
}
struct timespec buttonPressCheckPeriod = { .tv_sec = 0, .tv_nsec = 1000000 };
buttonPollTimer = CreateEventLoopPeriodicTimer(eventLoop, &ButtonTimerEventHandler, &buttonPressCheckPeriod);
if (buttonPollTimer == NULL) {
return ExitCode_Init_ButtonPollTimer;
}
if (SDCard_Init() == -1)
{
Log_Debug("ERROR: Failed to initialize intercore connection\n");
return ExitCode_Init_Connection;
}
return ExitCode_Success;
}
/// <summary>
/// Closes a file descriptor and prints an error on failure.
/// </summary>
/// <param name="fd">File descriptor to close</param>
/// <param name="fdName">File descriptor name to use in error message</param>
static void CloseFdAndPrintError(int fd, const char *fdName)
{
if (fd >= 0) {
int result = close(fd);
if (result != 0) {
Log_Debug("ERROR: Could not close fd %s: %s (%d).\n", fdName, strerror(errno), errno);
}
}
}
/// <summary>
/// Write 0x00 to the first two SD Card blocks
/// </summary>
void FormatCard(void)
{
uint8_t formatBuffer[BLOCK_SIZE];
memset(&formatBuffer[0], 0x00, BLOCK_SIZE);
Log_Debug("Formating blocks 0 and 1\n");
for (uint32_t x = 0; x < 2; x++)
{
if (SDCard_WriteBlock(x, &formatBuffer[0], BLOCK_SIZE) != LFS_ERR_OK)
{
Log_Debug("\nFailed to write block %d\n", x);
break;
}
}
}
/// <summary>
/// Clean up the resources previously allocated.
/// </summary>
static void CloseHandlers(void)
{
DisposeEventLoopTimer(buttonPollTimer);
EventLoop_Close(eventLoop);
SDCard_Cleanup();
Log_Debug("Closing file descriptors.\n");
CloseFdAndPrintError(buttonA_fd, "Button");
}
int main(void)
{
Log_Debug("Littlefs SD Card project\n");
InitSigterm();
exitCode = InitHandlers();
// WARNING: FormatCard Writes 0x00 to the first two SD Card blocks.
// useful to test initialization of LittleFs
// comment the FormatCard line to leave the SD Card intact on the next run of the application
FormatCard();
Log_Debug("Press 'Button A' to do LittleFs work\n");
while (exitCode == ExitCode_Success) {
EventLoop_Run_Result result = EventLoop_Run(eventLoop, -1, true);
// Continue if interrupted by signal, e.g. due to breakpoint being set.
if (result == EventLoop_Run_Failed && errno != EINTR) {
exitCode = ExitCode_Main_EventLoopFail;
}
}
CloseHandlers();
Log_Debug("Application exiting.\n");
return exitCode;
}