diff --git a/CMakeLists.txt b/CMakeLists.txt index b4606aa..03b9012 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,26 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(dmfsi) +# ====================================================================== +# Fetch DMDRVI (Driver Interface) +# ====================================================================== +FetchContent_Declare( + dmdrvi + GIT_REPOSITORY https://github.com/choco-technologies/dmdrvi.git + GIT_TAG main +) +FetchContent_MakeAvailable(dmdrvi) + +# ====================================================================== +# Fetch DMINI (INI Parser) +# ====================================================================== +FetchContent_Declare( + dmini + GIT_REPOSITORY https://github.com/choco-technologies/dmini.git + GIT_TAG main +) +FetchContent_MakeAvailable(dmini) + # ====================================================================== # DMDFS Module Configuration # ====================================================================== @@ -84,7 +104,15 @@ dmod_add_library(${DMOD_MODULE_NAME} ${DMOD_MODULE_VERSION} target_include_directories(${DMOD_MODULE_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include + ${dmdrvi_SOURCE_DIR}/include + ${dmdrvi_BINARY_DIR} + ${dmini_SOURCE_DIR}/include + ${dmini_BINARY_DIR} ) -# Link to DMFSI interface +# Link to DMFSI interface and dmdrvi, dmini modules +dmod_link_modules(${DMOD_MODULE_NAME} + dmini + dmdrvi +) target_link_libraries(${DMOD_MODULE_NAME} dmfsi_if) diff --git a/README.md b/README.md index f224991..4d9ef32 100644 --- a/README.md +++ b/README.md @@ -59,8 +59,49 @@ DMDFS is part of a modular embedded file system architecture built on DMOD: - **[DMOD](https://github.com/choco-technologies/dmod)**: The foundation providing dynamic module loading, inter-module communication, and resource management - **[DMFSI](https://github.com/choco-technologies/dmfsi)**: Defines the standard file system interface that DMDFS implements - **[DMVFS](https://github.com/choco-technologies/dmvfs)**: Virtual file system layer that can mount DMDFS at any path in a unified directory tree +- **[DMDRVI](https://github.com/choco-technologies/dmdrvi)**: Driver interface that all hardware drivers must implement +- **[DMINI](https://github.com/choco-technologies/dmini)**: INI file parser used for driver configuration - **DMDFS**: This project - implements DMFSI to provide access to driver-based storage +## How It Works + +DMDFS scans a configuration directory for `*.ini` files, where each file contains configuration for a specific driver. The module: + +1. **Scans Configuration Directory**: When mounted, DMDFS scans the provided directory for all `*.ini` files +2. **Parses Driver Configuration**: Each INI file should contain a `[main]` section with `driver_name` parameter: + ```ini + ; Configuration for clock driver + [main] + driver_name=dmclk + + ; Additional driver-specific settings + frequency=48000000 + ``` +3. **Determines Driver Name**: If `driver_name` is not specified, the filename (without .ini extension) is used +4. **Initializes Drivers**: Calls `dmdrvi_create()` for each driver with the parsed configuration +5. **Creates Device Files**: Based on the device numbering scheme returned by the driver: + - **No numbering** (DMDRVI_NUM_NONE): Creates `/dev/dmclk` + - **Major only** (DMDRVI_NUM_MAJOR): Creates `/dev/dmuart0`, `/dev/dmuart1`, etc. + - **Major + Minor** (DMDRVI_NUM_MAJOR | DMDRVI_NUM_MINOR): Creates `/dev/dmspi0/0`, `/dev/dmspi0/1`, etc. + +### Example Configuration + +Create INI files in your configuration directory: + +**dmclk.ini** (driver name from filename): +```ini +[main] +frequency=48000000 +``` + +**uart_config.ini** (explicit driver name): +```ini +[main] +driver_name=dmuart +port=0 +baudrate=115200 +``` + ## Building ```bash @@ -80,17 +121,18 @@ The module can be loaded and mounted using DMVFS: // Initialize DMVFS dmvfs_init(16, 32); -// Mount the driver filesystem at /mnt -dmvfs_mount_fs("dmdfs", "/mnt", NULL); +// Mount the driver filesystem at /dev with configuration directory +// The third parameter is the path to a directory containing *.ini driver configuration files +dmvfs_mount_fs("dmdfs", "/dev", "/path/to/config/directory"); -// Use standard file operations +// Use standard file operations to access device files void* fp; -dmvfs_fopen(&fp, "/mnt/file.txt", DMFSI_O_RDONLY, 0, 0); -// ... use file ... +dmvfs_fopen(&fp, "/dev/dmclk", DMFSI_O_RDWR, 0, 0); +// ... use device ... dmvfs_fclose(fp); // Unmount when done -dmvfs_unmount_fs("/mnt"); +dmvfs_unmount_fs("/dev"); dmvfs_deinit(); ``` diff --git a/src/dmdfs.c b/src/dmdfs.c index 66332e2..b5af186 100644 --- a/src/dmdfs.c +++ b/src/dmdfs.c @@ -12,20 +12,80 @@ #include "dmod.h" #include "dmdfs.h" #include "dmfsi.h" +#include "dmdrvi.h" +#include "dmini.h" #include +#include /** * @brief Magic number for DMDFS context validation */ #define DMDFS_CONTEXT_MAGIC 0x444D4446 // 'DMDF' +/** + * @brief Maximum number of drivers that can be loaded + */ +#define MAX_DRIVERS 16 + +/** + * @brief Maximum number of device files that can be created + */ +#define MAX_DEVICE_FILES 64 + +/** + * @brief Maximum path length + */ +#define MAX_PATH_LEN 256 + +/** + * @brief Device file structure + */ +typedef struct +{ + char path[MAX_PATH_LEN]; // Device file path (e.g., "dmclk" or "dmuart0" or "dmspi0/0") + dmdrvi_context_t driver_ctx; // Driver context + int driver_index; // Index into drivers array +} device_file_t; + +/** + * @brief Driver information structure + */ +typedef struct +{ + char name[64]; // Driver name + dmdrvi_context_t context; // Driver context + dmdrvi_dev_num_t dev_num; // Device numbers + Dmod_Context_t* module_ctx; // DMOD module context +} driver_info_t; + +/** + * @brief File handle structure + */ +typedef struct +{ + void* driver_handle; // Driver handle from dmdrvi_open + device_file_t* device; // Associated device file +} file_handle_t; + +/** + * @brief Directory handle structure + */ +typedef struct +{ + int current_index; // Current index in device files list +} dir_handle_t; + /** * @brief File system context structure */ struct dmfsi_context { uint32_t magic; - void* driver_context; // Driver-specific context + char config_path[MAX_PATH_LEN]; // Path to configuration directory + driver_info_t drivers[MAX_DRIVERS]; // Array of loaded drivers + int driver_count; // Number of loaded drivers + device_file_t devices[MAX_DEVICE_FILES]; // Array of device files + int device_count; // Number of device files }; // ============================================================================ @@ -58,6 +118,296 @@ int dmod_deinit(void) return 0; } +// ============================================================================ +// Helper Functions +// ============================================================================ + +/** + * @brief Simple integer to string conversion + */ +static int int_to_str(int value, char* buf, size_t buf_size) +{ + if (buf_size < 2) + return -1; + + if (value < 0) + { + *buf++ = '-'; + value = -value; + buf_size--; + } + + // Handle zero specially + if (value == 0) + { + *buf++ = '0'; + *buf = '\0'; + return 0; + } + + // Convert digits (reverse order) + char temp[32]; + int i = 0; + while (value > 0 && i < 31) + { + temp[i++] = '0' + (value % 10); + value /= 10; + } + + // Reverse into output buffer + int j = 0; + while (i > 0 && j < (int)buf_size - 1) + { + buf[j++] = temp[--i]; + } + buf[j] = '\0'; + + return 0; +} + +/** + * @brief Extract filename without extension + */ +static void extract_filename_without_ext(const char* filename, char* output, size_t output_size) +{ + const char* last_slash = strrchr(filename, '/'); + const char* base = last_slash ? last_slash + 1 : filename; + + const char* dot = strrchr(base, '.'); + size_t len = dot ? (size_t)(dot - base) : strlen(base); + + if (len >= output_size) + len = output_size - 1; + + memcpy(output, base, len); + output[len] = '\0'; +} + +/** + * @brief Create device files based on driver numbering scheme + */ +static int create_device_files(dmfsi_context_t ctx, int driver_index) +{ + driver_info_t* driver = &ctx->drivers[driver_index]; + + if (ctx->device_count >= MAX_DEVICE_FILES) + { + DMOD_LOG_ERROR("dmdfs: Maximum device files limit reached\n"); + return -1; + } + + device_file_t* dev = &ctx->devices[ctx->device_count]; + + // Create device file path based on numbering scheme + if (driver->dev_num.flags == DMDRVI_NUM_NONE) + { + // Device file: /dev/dmclk (no numbering) + strncpy(dev->path, driver->name, sizeof(dev->path) - 1); + dev->path[sizeof(dev->path) - 1] = '\0'; + } + else if (driver->dev_num.flags & DMDRVI_NUM_MINOR) + { + // Device file: /dev/dmspi0/0 (directory structure) + strncpy(dev->path, driver->name, sizeof(dev->path) - 1); + size_t len = strlen(dev->path); + char num_buf[16]; + int_to_str(driver->dev_num.major, num_buf, sizeof(num_buf)); + strncat(dev->path, num_buf, sizeof(dev->path) - len - 1); + len = strlen(dev->path); + strncat(dev->path, "/", sizeof(dev->path) - len - 1); + len = strlen(dev->path); + int_to_str(driver->dev_num.minor, num_buf, sizeof(num_buf)); + strncat(dev->path, num_buf, sizeof(dev->path) - len - 1); + } + else if (driver->dev_num.flags & DMDRVI_NUM_MAJOR) + { + // Device file: /dev/dmuart0 (major number only) + strncpy(dev->path, driver->name, sizeof(dev->path) - 1); + size_t len = strlen(dev->path); + char num_buf[16]; + int_to_str(driver->dev_num.major, num_buf, sizeof(num_buf)); + strncat(dev->path, num_buf, sizeof(dev->path) - len - 1); + } + + dev->driver_ctx = driver->context; + dev->driver_index = driver_index; + ctx->device_count++; + + DMOD_LOG_INFO("dmdfs: Created device file: %s\n", dev->path); + + return 0; +} + +/** + * @brief Load a driver from INI configuration file + */ +static int load_driver_from_ini(dmfsi_context_t ctx, const char* ini_path) +{ + if (ctx->driver_count >= MAX_DRIVERS) + { + DMOD_LOG_ERROR("dmdfs: Maximum driver limit reached\n"); + return -1; + } + + // Parse INI file + dmini_context_t ini_ctx = dmini_create(); + if (!ini_ctx) + { + DMOD_LOG_ERROR("dmdfs: Failed to create INI context\n"); + return -1; + } + + int result = dmini_parse_file(ini_ctx, ini_path); + if (result != DMINI_OK) + { + DMOD_LOG_ERROR("dmdfs: Failed to parse INI file: %s\n", ini_path); + dmini_destroy(ini_ctx); + return -1; + } + + // Get driver name from [main] section, or use filename if not present + const char* driver_name = dmini_get_string(ini_ctx, "main", "driver_name", NULL); + + char name_buf[64]; + if (driver_name == NULL) + { + // Use filename without extension as driver name + extract_filename_without_ext(ini_path, name_buf, sizeof(name_buf)); + driver_name = name_buf; + } + + DMOD_LOG_INFO("dmdfs: Loading driver: %s from %s\n", driver_name, ini_path); + + // Find the driver module that implements dmdrvi interface + Dmod_Context_t* driver_module_ctx = NULL; + Dmod_Context_t* iter_ctx = NULL; + + // Iterate through all modules implementing dmdrvi._create + while ((iter_ctx = Dmod_GetNextDifModule("dmdrvi._create", iter_ctx)) != NULL) + { + // Check if this is the driver we're looking for + const char* module_name = Dmod_ApiSignature_GetModule("dmdrvi._create"); + // TODO: Better way to match driver name with module + // For now, just use the first one found + driver_module_ctx = iter_ctx; + break; + } + + if (!driver_module_ctx) + { + DMOD_LOG_ERROR("dmdfs: Failed to find driver module implementing dmdrvi: %s\n", driver_name); + dmini_destroy(ini_ctx); + return -1; + } + + // Get the dmdrvi_create function from the driver + typedef dmdrvi_context_t (*dmdrvi_create_fn_t)(dmini_context_t, dmdrvi_dev_num_t*); + dmdrvi_create_fn_t create_fn = (dmdrvi_create_fn_t)Dmod_GetDifFunction(driver_module_ctx, "dmdrvi._create"); + + if (!create_fn) + { + DMOD_LOG_ERROR("dmdfs: Driver %s does not implement dmdrvi._create\n", driver_name); + dmini_destroy(ini_ctx); + return -1; + } + + // Create driver context + driver_info_t* driver = &ctx->drivers[ctx->driver_count]; + strncpy(driver->name, driver_name, sizeof(driver->name) - 1); + driver->name[sizeof(driver->name) - 1] = '\0'; + driver->module_ctx = driver_module_ctx; // Store module context + + // Call dmdrvi_create for the driver + driver->context = create_fn(ini_ctx, &driver->dev_num); + + if (!driver->context) + { + DMOD_LOG_ERROR("dmdfs: Failed to create driver context for: %s\n", driver_name); + dmini_destroy(ini_ctx); + return -1; + } + + int driver_index = ctx->driver_count; + ctx->driver_count++; + + // Create device files based on numbering scheme + create_device_files(ctx, driver_index); + + dmini_destroy(ini_ctx); + return 0; +} + +/** + * @brief Scan configuration directory for INI files and load drivers + */ +static int scan_and_load_drivers(dmfsi_context_t ctx) +{ + // Open configuration directory using SAL + void* dir = Dmod_OpenDir(ctx->config_path); + if (!dir) + { + DMOD_LOG_ERROR("dmdfs: Failed to open configuration directory: %s\n", ctx->config_path); + return -1; + } + + // Read directory entries + const char* entry_name; + + while ((entry_name = Dmod_ReadDir(dir)) != NULL) + { + // Skip . and .. + if (strcmp(entry_name, ".") == 0 || strcmp(entry_name, "..") == 0) + continue; + + // Check if file has .ini extension + size_t len = strlen(entry_name); + if (len > 4 && strcmp(entry_name + len - 4, ".ini") == 0) + { + // Build full path to INI file + char ini_path[MAX_PATH_LEN]; + strncpy(ini_path, ctx->config_path, sizeof(ini_path) - 1); + ini_path[sizeof(ini_path) - 1] = '\0'; + size_t path_len = strlen(ini_path); + if (path_len > 0 && ini_path[path_len - 1] != '/') + { + strncat(ini_path, "/", sizeof(ini_path) - path_len - 1); + } + strncat(ini_path, entry_name, sizeof(ini_path) - strlen(ini_path) - 1); + + // Load driver from this INI file + load_driver_from_ini(ctx, ini_path); + } + } + + Dmod_CloseDir(dir); + + DMOD_LOG_INFO("dmdfs: Loaded %d drivers with %d device files\n", + ctx->driver_count, ctx->device_count); + + return 0; +} + +/** + * @brief Find device file by path + */ +static device_file_t* find_device(dmfsi_context_t ctx, const char* path) +{ + // Remove leading slash if present + const char* search_path = path; + if (path[0] == '/') + search_path = path + 1; + + for (int i = 0; i < ctx->device_count; i++) + { + if (strcmp(ctx->devices[i].path, search_path) == 0) + { + return &ctx->devices[i]; + } + } + + return NULL; +} + // ============================================================================ // DMFSI Interface Implementation // ============================================================================ @@ -74,10 +424,27 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdfs, dmfsi_context_t, _init, (const char* return NULL; } + memset(ctx, 0, sizeof(struct dmfsi_context)); ctx->magic = DMDFS_CONTEXT_MAGIC; - ctx->driver_context = NULL; - // TODO: Initialize driver context based on config + // Store config path (path to directory containing INI files) + if (config) + { + strncpy(ctx->config_path, config, sizeof(ctx->config_path) - 1); + ctx->config_path[sizeof(ctx->config_path) - 1] = '\0'; + + // Scan directory and load all drivers + if (scan_and_load_drivers(ctx) != 0) + { + DMOD_LOG_ERROR("dmdfs: Failed to scan and load drivers\n"); + Dmod_Free(ctx); + return NULL; + } + } + else + { + DMOD_LOG_WARN("dmdfs: No config path provided, no drivers loaded\n"); + } return ctx; } @@ -89,7 +456,26 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdfs, int, _deinit, (dmfsi_context_t ctx) { if (ctx) { - // TODO: Cleanup driver context + // Free all driver contexts + for (int i = 0; i < ctx->driver_count; i++) + { + if (ctx->drivers[i].context) + { + // Get driver module context + Dmod_Context_t* driver_module_ctx = ctx->drivers[i].module_ctx; + if (driver_module_ctx) + { + // Get dmdrvi_free function + typedef void (*dmdrvi_free_fn_t)(dmdrvi_context_t); + dmdrvi_free_fn_t free_fn = (dmdrvi_free_fn_t)Dmod_GetDifFunction(driver_module_ctx, "dmdrvi._free"); + if (free_fn) + { + free_fn(ctx->drivers[i].context); + } + } + } + } + Dmod_Free(ctx); } return DMFSI_OK; @@ -114,9 +500,67 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdfs, int, _fopen, (dmfsi_context_t ctx, v return DMFSI_ERR_INVALID; } - // TODO: Implement file opening through driver - DMOD_LOG_ERROR("dmdfs: fopen not yet implemented\n"); - return DMFSI_ERR_GENERAL; + // Find the device file + device_file_t* device = find_device(ctx, path); + if (!device) + { + DMOD_LOG_ERROR("dmdfs: Device not found: %s\n", path); + return DMFSI_ERR_NOT_FOUND; + } + + // Convert DMFSI flags to DMDRVI flags + int drv_flags = 0; + if (mode & DMFSI_O_RDONLY) + drv_flags |= DMDRVI_O_RDONLY; + if (mode & DMFSI_O_WRONLY) + drv_flags |= DMDRVI_O_WRONLY; + if (mode & DMFSI_O_RDWR) + drv_flags |= DMDRVI_O_RDWR; + + // Get driver module context + Dmod_Context_t* driver_module_ctx = ctx->drivers[device->driver_index].module_ctx; + if (!driver_module_ctx) + { + DMOD_LOG_ERROR("dmdfs: Failed to get driver module context\n"); + return DMFSI_ERR_GENERAL; + } + + // Get dmdrvi_open function + typedef void* (*dmdrvi_open_fn_t)(dmdrvi_context_t, int); + dmdrvi_open_fn_t open_fn = (dmdrvi_open_fn_t)Dmod_GetDifFunction(driver_module_ctx, "dmdrvi._open"); + if (!open_fn) + { + DMOD_LOG_ERROR("dmdfs: Driver does not implement dmdrvi._open\n"); + return DMFSI_ERR_GENERAL; + } + + // Open device through driver + void* driver_handle = open_fn(device->driver_ctx, drv_flags); + if (!driver_handle) + { + DMOD_LOG_ERROR("dmdfs: Failed to open device: %s\n", path); + return DMFSI_ERR_GENERAL; + } + + // Create file handle + file_handle_t* handle = Dmod_Malloc(sizeof(file_handle_t)); + if (!handle) + { + // Get dmdrvi_close function + typedef void (*dmdrvi_close_fn_t)(dmdrvi_context_t, void*); + dmdrvi_close_fn_t close_fn = (dmdrvi_close_fn_t)Dmod_GetDifFunction(driver_module_ctx, "dmdrvi._close"); + if (close_fn) + { + close_fn(device->driver_ctx, driver_handle); + } + return DMFSI_ERR_GENERAL; + } + + handle->driver_handle = driver_handle; + handle->device = device; + + *fp = handle; + return DMFSI_OK; } /** @@ -130,9 +574,26 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdfs, int, _fclose, (dmfsi_context_t ctx, return DMFSI_ERR_INVALID; } - // TODO: Implement file closing - DMOD_LOG_ERROR("dmdfs: fclose not yet implemented\n"); - return DMFSI_ERR_GENERAL; + if (!fp) + return DMFSI_ERR_INVALID; + + file_handle_t* handle = (file_handle_t*)fp; + + // Get driver module context + Dmod_Context_t* driver_module_ctx = ctx->drivers[handle->device->driver_index].module_ctx; + if (driver_module_ctx) + { + // Get dmdrvi_close function + typedef void (*dmdrvi_close_fn_t)(dmdrvi_context_t, void*); + dmdrvi_close_fn_t close_fn = (dmdrvi_close_fn_t)Dmod_GetDifFunction(driver_module_ctx, "dmdrvi._close"); + if (close_fn) + { + close_fn(handle->device->driver_ctx, handle->driver_handle); + } + } + + Dmod_Free(handle); + return DMFSI_OK; } /** @@ -146,9 +607,33 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdfs, int, _fread, (dmfsi_context_t ctx, v return DMFSI_ERR_INVALID; } - // TODO: Implement file reading - if (read) *read = 0; - return DMFSI_ERR_GENERAL; + if (!fp || !buffer || !read) + return DMFSI_ERR_INVALID; + + file_handle_t* handle = (file_handle_t*)fp; + + // Get driver module context + Dmod_Context_t* driver_module_ctx = ctx->drivers[handle->device->driver_index].module_ctx; + if (!driver_module_ctx) + { + *read = 0; + return DMFSI_ERR_GENERAL; + } + + // Get dmdrvi_read function + typedef size_t (*dmdrvi_read_fn_t)(dmdrvi_context_t, void*, void*, size_t); + dmdrvi_read_fn_t read_fn = (dmdrvi_read_fn_t)Dmod_GetDifFunction(driver_module_ctx, "dmdrvi._read"); + if (!read_fn) + { + *read = 0; + return DMFSI_ERR_GENERAL; + } + + // Read from device through driver + size_t bytes_read = read_fn(handle->device->driver_ctx, handle->driver_handle, buffer, size); + + *read = bytes_read; + return DMFSI_OK; } /** @@ -162,9 +647,33 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdfs, int, _fwrite, (dmfsi_context_t ctx, return DMFSI_ERR_INVALID; } - // TODO: Implement file writing - if (written) *written = 0; - return DMFSI_ERR_GENERAL; + if (!fp || !buffer || !written) + return DMFSI_ERR_INVALID; + + file_handle_t* handle = (file_handle_t*)fp; + + // Get driver module context + Dmod_Context_t* driver_module_ctx = ctx->drivers[handle->device->driver_index].module_ctx; + if (!driver_module_ctx) + { + *written = 0; + return DMFSI_ERR_GENERAL; + } + + // Get dmdrvi_write function + typedef size_t (*dmdrvi_write_fn_t)(dmdrvi_context_t, void*, const void*, size_t); + dmdrvi_write_fn_t write_fn = (dmdrvi_write_fn_t)Dmod_GetDifFunction(driver_module_ctx, "dmdrvi._write"); + if (!write_fn) + { + *written = 0; + return DMFSI_ERR_GENERAL; + } + + // Write to device through driver + size_t bytes_written = write_fn(handle->device->driver_ctx, handle->driver_handle, buffer, size); + + *written = bytes_written; + return DMFSI_OK; } /** @@ -268,8 +777,30 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdfs, int, _fflush, (dmfsi_context_t ctx, return DMFSI_ERR_INVALID; } - // TODO: Implement buffer flushing - return DMFSI_OK; + if (!fp) + return DMFSI_ERR_INVALID; + + file_handle_t* handle = (file_handle_t*)fp; + + // Get driver module context + Dmod_Context_t* driver_module_ctx = ctx->drivers[handle->device->driver_index].module_ctx; + if (!driver_module_ctx) + { + return DMFSI_ERR_GENERAL; + } + + // Get dmdrvi_flush function + typedef int (*dmdrvi_flush_fn_t)(dmdrvi_context_t, void*); + dmdrvi_flush_fn_t flush_fn = (dmdrvi_flush_fn_t)Dmod_GetDifFunction(driver_module_ctx, "dmdrvi._flush"); + if (!flush_fn) + { + return DMFSI_OK; // Not all drivers may implement flush + } + + // Flush through driver + int result = flush_fn(handle->device->driver_ctx, handle->driver_handle); + + return (result == 0) ? DMFSI_OK : DMFSI_ERR_GENERAL; } /** @@ -283,8 +814,30 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdfs, int, _sync, (dmfsi_context_t ctx, vo return DMFSI_ERR_INVALID; } - // TODO: Implement sync - return DMFSI_OK; + if (!fp) + return DMFSI_ERR_INVALID; + + file_handle_t* handle = (file_handle_t*)fp; + + // Get driver module context + Dmod_Context_t* driver_module_ctx = ctx->drivers[handle->device->driver_index].module_ctx; + if (!driver_module_ctx) + { + return DMFSI_ERR_GENERAL; + } + + // Get dmdrvi_flush function (same as flush for devices) + typedef int (*dmdrvi_flush_fn_t)(dmdrvi_context_t, void*); + dmdrvi_flush_fn_t flush_fn = (dmdrvi_flush_fn_t)Dmod_GetDifFunction(driver_module_ctx, "dmdrvi._flush"); + if (!flush_fn) + { + return DMFSI_OK; // Not all drivers may implement flush + } + + // Sync through driver + int result = flush_fn(handle->device->driver_ctx, handle->driver_handle); + + return (result == 0) ? DMFSI_OK : DMFSI_ERR_GENERAL; } /** @@ -298,8 +851,24 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdfs, int, _opendir, (dmfsi_context_t ctx, return DMFSI_ERR_INVALID; } - // TODO: Implement directory opening - return DMFSI_ERR_GENERAL; + // Only root directory is supported + if (path && strcmp(path, "/") != 0 && strlen(path) > 0) + { + DMOD_LOG_ERROR("dmdfs: Only root directory is supported\n"); + return DMFSI_ERR_NOT_FOUND; + } + + // Create directory handle + dir_handle_t* handle = Dmod_Malloc(sizeof(dir_handle_t)); + if (!handle) + { + return DMFSI_ERR_GENERAL; + } + + handle->current_index = 0; + *dp = handle; + + return DMFSI_OK; } /** @@ -313,8 +882,28 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdfs, int, _readdir, (dmfsi_context_t ctx, return DMFSI_ERR_INVALID; } - // TODO: Implement directory reading - return DMFSI_ERR_NOT_FOUND; + if (!dp || !entry) + return DMFSI_ERR_INVALID; + + dir_handle_t* handle = (dir_handle_t*)dp; + + // Check if we've reached the end + if (handle->current_index >= ctx->device_count) + { + return DMFSI_ERR_NOT_FOUND; + } + + // Fill in entry information + device_file_t* device = &ctx->devices[handle->current_index]; + strncpy(entry->name, device->path, sizeof(entry->name) - 1); + entry->name[sizeof(entry->name) - 1] = '\0'; + entry->size = 0; // Device files don't have a fixed size + entry->attr = 0; + entry->time = 0; + + handle->current_index++; + + return DMFSI_OK; } /** @@ -328,8 +917,12 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdfs, int, _closedir, (dmfsi_context_t ctx return DMFSI_ERR_INVALID; } - // TODO: Implement directory closing - return DMFSI_ERR_GENERAL; + if (dp) + { + Dmod_Free(dp); + } + + return DMFSI_OK; } /** @@ -373,8 +966,34 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdfs, int, _stat, (dmfsi_context_t ctx, co return DMFSI_ERR_INVALID; } - // TODO: Implement stat - return DMFSI_ERR_GENERAL; + if (!stat) + return DMFSI_ERR_INVALID; + + // Check for root directory + if (!path || strcmp(path, "/") == 0 || strlen(path) == 0) + { + stat->size = 0; + stat->attr = 0; + stat->ctime = 0; + stat->mtime = 0; + stat->atime = 0; + return DMFSI_OK; + } + + // Find the device file + device_file_t* device = find_device(ctx, path); + if (!device) + { + return DMFSI_ERR_NOT_FOUND; + } + + stat->size = 0; // Device files don't have a predetermined size + stat->attr = 0; + stat->ctime = 0; + stat->mtime = 0; + stat->atime = 0; + + return DMFSI_OK; } /**