From 2ff66440c37c9ec9c96670105d6082e456a975a3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 10:09:55 +0000 Subject: [PATCH 1/8] Initial plan From 45183bb35995dabecc686ec47e2e77eb48282b0c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 10:17:33 +0000 Subject: [PATCH 2/8] Implement device file structure and directory operations Co-authored-by: JohnAmadis <17320783+JohnAmadis@users.noreply.github.com> --- README.md | 32 +++++++++--- src/dmdevfs.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 151 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 7ed15b0..9a53558 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ cmake --build . ## Usage -The module can be loaded and mounted using DMVFS: +The module can be loaded and mounted using DMVFS. The module name is `dmdfs` and you must provide a path to a configuration directory containing `*.ini` files for drivers: ```c #include "dmvfs.h" @@ -80,20 +80,38 @@ The module can be loaded and mounted using DMVFS: // Initialize DMVFS dmvfs_init(16, 32); -// Mount the driver filesystem at /mnt -dmvfs_mount_fs("dmdevfs", "/mnt", NULL); +// Mount the driver filesystem at /dev +// The config parameter should point to a directory containing .ini files +dmvfs_mount_fs("dmdfs", "/dev", "/path/to/config"); -// Use standard file operations +// 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(); ``` +### Configuration Files + +The configuration directory should contain `.ini` files for each driver. Each file can specify the driver name in the `[main]` section, or the driver name will be derived from the filename (without `.ini` extension). + +Example configuration file (`/path/to/config/dmclk.ini`): +```ini +; Clock driver configuration +[main] +driver_name=dmclk + +; Additional driver-specific configuration +[settings] +frequency=100000 +``` + +If `driver_name` is not specified, the filename will be used (e.g., `dmclk.ini` → driver name `dmclk`). + ## API The module implements the full DMFSI interface: diff --git a/src/dmdevfs.c b/src/dmdevfs.c index 02b5936..3bfbb16 100644 --- a/src/dmdevfs.c +++ b/src/dmdevfs.c @@ -16,6 +16,7 @@ #include "dmini.h" #include "dmdrvi.h" #include +#include /** * @brief Magic number for DMDEVFS context validation @@ -29,8 +30,18 @@ typedef struct dmdrvi_dev_num_t dev_num; // Device number assigned to the driver bool was_loaded; // Indicates if the driver was loaded by dmdevfs bool was_enabled; // Indicates if the driver was enabled by dmdevfs + char driver_name[DMOD_MAX_MODULE_NAME_LENGTH]; // Driver name for device file naming } driver_node_t; +/** + * @brief Directory handle for directory operations + */ +typedef struct +{ + size_t current_index; // Current position in the driver list + bool is_open; // Whether the directory is open +} dir_handle_t; + /** * @brief File system context structure */ @@ -53,6 +64,7 @@ static void read_base_name(const char* path, char* base_name, size_t name_size); static dmini_context_t read_driver_for_config(const char* config_path, char* driver_name, size_t name_size, const char* default_driver); static Dmod_Context_t* prepare_driver_module(const char* driver_name, bool* was_loaded, bool* was_enabled); static void cleanup_driver_module(const char* driver_name, bool was_loaded, bool was_enabled); +static void build_device_filename(const driver_node_t* driver_node, char* filename, size_t size); // ============================================================================ // Module Interface Implementation @@ -351,8 +363,23 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _opendir, (dmfsi_context_t ct return DMFSI_ERR_INVALID; } - // TODO: Implement directory opening - return DMFSI_ERR_GENERAL; + // Only support opening root directory + if (path == NULL || strlen(path) == 0 || strcmp(path, "/") == 0) + { + dir_handle_t* dir = Dmod_Malloc(sizeof(dir_handle_t)); + if (dir == NULL) + { + DMOD_LOG_ERROR("dmdevfs: Failed to allocate directory handle\n"); + return DMFSI_ERR_GENERAL; + } + + dir->current_index = 0; + dir->is_open = true; + *dp = dir; + return DMFSI_OK; + } + + return DMFSI_ERR_NOT_FOUND; } /** @@ -366,8 +393,37 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _readdir, (dmfsi_context_t ct return DMFSI_ERR_INVALID; } - // TODO: Implement directory reading - return DMFSI_ERR_NOT_FOUND; + if (dp == NULL || entry == NULL) + { + return DMFSI_ERR_INVALID; + } + + dir_handle_t* dir = (dir_handle_t*)dp; + if (!dir->is_open) + { + return DMFSI_ERR_INVALID; + } + + size_t driver_count = dmlist_size(ctx->drivers); + if (dir->current_index >= driver_count) + { + return DMFSI_ERR_NOT_FOUND; // No more entries + } + + driver_node_t* driver_node = (driver_node_t*)dmlist_get(ctx->drivers, dir->current_index); + if (driver_node == NULL) + { + dir->current_index++; + return DMFSI_ERR_GENERAL; + } + + // Build device filename based on dev_num flags + build_device_filename(driver_node, entry->name, sizeof(entry->name)); + entry->is_directory = false; + entry->size = 0; + + dir->current_index++; + return DMFSI_OK; } /** @@ -381,7 +437,14 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _closedir, (dmfsi_context_t c return DMFSI_ERR_INVALID; } - // TODO: Implement directory closing + if (dp != NULL) + { + dir_handle_t* dir = (dir_handle_t*)dp; + dir->is_open = false; + Dmod_Free(dp); + return DMFSI_OK; + } + return DMFSI_ERR_GENERAL; } @@ -404,7 +467,12 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _direxists, (dmfsi_context_t return 0; } - // TODO: Implement directory existence check + // Only root directory exists + if (path == NULL || strlen(path) == 0 || strcmp(path, "/") == 0) + { + return 1; + } + return 0; } @@ -545,6 +613,9 @@ static driver_node_t* configure_driver(const char* driver_name, dmini_context_t driver_node->was_loaded = was_loaded; driver_node->was_enabled = was_enabled; driver_node->driver = driver; + strncpy(driver_node->driver_name, driver_name, DMOD_MAX_MODULE_NAME_LENGTH - 1); + driver_node->driver_name[DMOD_MAX_MODULE_NAME_LENGTH - 1] = '\0'; + driver_node->driver_context = dmdrvi_create(config_ctx, &driver_node->dev_num); if (driver_node->driver_context == NULL) { @@ -554,6 +625,22 @@ static driver_node_t* configure_driver(const char* driver_name, dmini_context_t return NULL; } + // Log device file information based on dev_num flags + if (driver_node->dev_num.flags == DMDRVI_NUM_NONE) + { + Dmod_Printf("dmdevfs: Device created: %s\n", driver_name); + } + else if (driver_node->dev_num.flags & DMDRVI_NUM_MINOR) + { + Dmod_Printf("dmdevfs: Device created: %s%d/%d (major/minor)\n", + driver_name, driver_node->dev_num.major, driver_node->dev_num.minor); + } + else if (driver_node->dev_num.flags & DMDRVI_NUM_MAJOR) + { + Dmod_Printf("dmdevfs: Device created: %s%d (major)\n", + driver_name, driver_node->dev_num.major); + } + return driver_node; } @@ -684,4 +771,37 @@ static void cleanup_driver_module(const char* driver_name, bool was_loaded, bool { Dmod_UnloadModule(driver_name, false); } +} + +/** + * @brief Build device filename based on dev_num flags + */ +static void build_device_filename(const driver_node_t* driver_node, char* filename, size_t size) +{ + if (driver_node->dev_num.flags == DMDRVI_NUM_NONE) + { + // Device file: /dev/dmclk -> dmclk (no /dev prefix needed) + snprintf(filename, size, "%s", driver_node->driver_name); + } + else if (driver_node->dev_num.flags & DMDRVI_NUM_MINOR) + { + // Device file: /dev/dmspi0/0 -> dmspi0/0 + // This creates a directory structure with major and minor + snprintf(filename, size, "%s%d/%d", + driver_node->driver_name, + driver_node->dev_num.major, + driver_node->dev_num.minor); + } + else if (driver_node->dev_num.flags & DMDRVI_NUM_MAJOR) + { + // Device file: /dev/dmuart0 -> dmuart0 + snprintf(filename, size, "%s%d", + driver_node->driver_name, + driver_node->dev_num.major); + } + else + { + // Fallback to driver name only + snprintf(filename, size, "%s", driver_node->driver_name); + } } \ No newline at end of file From 31e0fcfd0a351084a00aa667375391314b0a5367 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 10:21:29 +0000 Subject: [PATCH 3/8] Fix NULL pointer checks and add documentation for directory structure handling Co-authored-by: JohnAmadis <17320783+JohnAmadis@users.noreply.github.com> --- src/dmdevfs.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/dmdevfs.c b/src/dmdevfs.c index 3bfbb16..d4d5544 100644 --- a/src/dmdevfs.c +++ b/src/dmdevfs.c @@ -364,7 +364,7 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _opendir, (dmfsi_context_t ct } // Only support opening root directory - if (path == NULL || strlen(path) == 0 || strcmp(path, "/") == 0) + if (path == NULL || *path == '\0' || strcmp(path, "/") == 0) { dir_handle_t* dir = Dmod_Malloc(sizeof(dir_handle_t)); if (dir == NULL) @@ -468,7 +468,7 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _direxists, (dmfsi_context_t } // Only root directory exists - if (path == NULL || strlen(path) == 0 || strcmp(path, "/") == 0) + if (path == NULL || *path == '\0' || strcmp(path, "/") == 0) { return 1; } @@ -775,6 +775,11 @@ static void cleanup_driver_module(const char* driver_name, bool was_loaded, bool /** * @brief Build device filename based on dev_num flags + * + * Note: For DMDRVI_NUM_MINOR, this returns a path with forward slash (e.g., "dmspi0/0"). + * This matches the dmdrvi interface specification. Full directory hierarchy support + * would require implementing nested directory operations, which is left for future + * enhancement when file I/O operations are implemented. */ static void build_device_filename(const driver_node_t* driver_node, char* filename, size_t size) { @@ -786,7 +791,8 @@ static void build_device_filename(const driver_node_t* driver_node, char* filena else if (driver_node->dev_num.flags & DMDRVI_NUM_MINOR) { // Device file: /dev/dmspi0/0 -> dmspi0/0 - // This creates a directory structure with major and minor + // This represents a directory structure with major and minor numbers + // TODO: Implement proper nested directory support in opendir/readdir snprintf(filename, size, "%s%d/%d", driver_node->driver_name, driver_node->dev_num.major, From 35672a113f8340631613920811ed5bbb9ef262fa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 10:23:13 +0000 Subject: [PATCH 4/8] Fix NULL pointer safety in path validation logic Co-authored-by: JohnAmadis <17320783+JohnAmadis@users.noreply.github.com> --- src/dmdevfs.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/dmdevfs.c b/src/dmdevfs.c index d4d5544..a35a40e 100644 --- a/src/dmdevfs.c +++ b/src/dmdevfs.c @@ -17,6 +17,7 @@ #include "dmdrvi.h" #include #include +#include /** * @brief Magic number for DMDEVFS context validation @@ -363,8 +364,12 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _opendir, (dmfsi_context_t ct return DMFSI_ERR_INVALID; } - // Only support opening root directory - if (path == NULL || *path == '\0' || strcmp(path, "/") == 0) + // Only support opening root directory (NULL or empty path means root, or explicit "/") + bool is_root = (path == NULL) || + (path[0] == '\0') || + (strcmp(path, "/") == 0); + + if (is_root) { dir_handle_t* dir = Dmod_Malloc(sizeof(dir_handle_t)); if (dir == NULL) @@ -467,8 +472,12 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _direxists, (dmfsi_context_t return 0; } - // Only root directory exists - if (path == NULL || *path == '\0' || strcmp(path, "/") == 0) + // Only root directory exists (NULL or empty path means root, or explicit "/") + bool is_root = (path == NULL) || + (path[0] == '\0') || + (strcmp(path, "/") == 0); + + if (is_root) { return 1; } From 9c21c5d42bc0c6c0b613c77c60eea568dda2b540 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 10:24:56 +0000 Subject: [PATCH 5/8] Improve NULL safety with explicit checks and input validation Co-authored-by: JohnAmadis <17320783+JohnAmadis@users.noreply.github.com> --- src/dmdevfs.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/dmdevfs.c b/src/dmdevfs.c index a35a40e..1b10856 100644 --- a/src/dmdevfs.c +++ b/src/dmdevfs.c @@ -365,9 +365,15 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _opendir, (dmfsi_context_t ct } // Only support opening root directory (NULL or empty path means root, or explicit "/") - bool is_root = (path == NULL) || - (path[0] == '\0') || - (strcmp(path, "/") == 0); + bool is_root = false; + if (path == NULL) + { + is_root = true; + } + else if (path[0] == '\0' || strcmp(path, "/") == 0) + { + is_root = true; + } if (is_root) { @@ -473,9 +479,15 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _direxists, (dmfsi_context_t } // Only root directory exists (NULL or empty path means root, or explicit "/") - bool is_root = (path == NULL) || - (path[0] == '\0') || - (strcmp(path, "/") == 0); + bool is_root = false; + if (path == NULL) + { + is_root = true; + } + else if (path[0] == '\0' || strcmp(path, "/") == 0) + { + is_root = true; + } if (is_root) { @@ -789,9 +801,19 @@ static void cleanup_driver_module(const char* driver_name, bool was_loaded, bool * This matches the dmdrvi interface specification. Full directory hierarchy support * would require implementing nested directory operations, which is left for future * enhancement when file I/O operations are implemented. + * + * @param driver_node Pointer to driver node (must not be NULL) + * @param filename Output buffer for filename (must not be NULL) + * @param size Size of output buffer (must be > 0) */ static void build_device_filename(const driver_node_t* driver_node, char* filename, size_t size) { + // Validate input parameters + if (driver_node == NULL || filename == NULL || size == 0) + { + return; + } + if (driver_node->dev_num.flags == DMDRVI_NUM_NONE) { // Device file: /dev/dmclk -> dmclk (no /dev prefix needed) From 17e3422b9f21572cf6ae302cb46fa4a44e43f4e0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 10:27:40 +0000 Subject: [PATCH 6/8] Extract root path checking into reusable helper function Co-authored-by: JohnAmadis <17320783+JohnAmadis@users.noreply.github.com> --- src/dmdevfs.c | 56 +++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/src/dmdevfs.c b/src/dmdevfs.c index 1b10856..beffc41 100644 --- a/src/dmdevfs.c +++ b/src/dmdevfs.c @@ -66,6 +66,7 @@ static dmini_context_t read_driver_for_config(const char* config_path, char* dri static Dmod_Context_t* prepare_driver_module(const char* driver_name, bool* was_loaded, bool* was_enabled); static void cleanup_driver_module(const char* driver_name, bool was_loaded, bool was_enabled); static void build_device_filename(const driver_node_t* driver_node, char* filename, size_t size); +static bool is_root_path(const char* path); // ============================================================================ // Module Interface Implementation @@ -364,18 +365,8 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _opendir, (dmfsi_context_t ct return DMFSI_ERR_INVALID; } - // Only support opening root directory (NULL or empty path means root, or explicit "/") - bool is_root = false; - if (path == NULL) - { - is_root = true; - } - else if (path[0] == '\0' || strcmp(path, "/") == 0) - { - is_root = true; - } - - if (is_root) + // Only support opening root directory + if (is_root_path(path)) { dir_handle_t* dir = Dmod_Malloc(sizeof(dir_handle_t)); if (dir == NULL) @@ -478,23 +469,8 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _direxists, (dmfsi_context_t return 0; } - // Only root directory exists (NULL or empty path means root, or explicit "/") - bool is_root = false; - if (path == NULL) - { - is_root = true; - } - else if (path[0] == '\0' || strcmp(path, "/") == 0) - { - is_root = true; - } - - if (is_root) - { - return 1; - } - - return 0; + // Only root directory exists + return is_root_path(path) ? 1 : 0; } /** @@ -841,4 +817,26 @@ static void build_device_filename(const driver_node_t* driver_node, char* filena // Fallback to driver name only snprintf(filename, size, "%s", driver_node->driver_name); } +} + +/** + * @brief Check if path represents the root directory + * + * @param path Path to check (can be NULL) + * @return true if path is NULL, empty, or "/" + */ +static bool is_root_path(const char* path) +{ + if (path == NULL) + { + return true; + } + + if (path[0] == '\0' || strcmp(path, "/") == 0) + { + return true; + } + + return false; +} } \ No newline at end of file From 480b06d5d7558e42c32a8d33027aff1d209cc846 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 12:19:55 +0000 Subject: [PATCH 7/8] Address code review feedback: support subdirectories, simplify logging, use DMOD functions Co-authored-by: JohnAmadis <17320783+JohnAmadis@users.noreply.github.com> --- src/dmdevfs.c | 321 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 224 insertions(+), 97 deletions(-) diff --git a/src/dmdevfs.c b/src/dmdevfs.c index beffc41..d94b29d 100644 --- a/src/dmdevfs.c +++ b/src/dmdevfs.c @@ -16,7 +16,6 @@ #include "dmini.h" #include "dmdrvi.h" #include -#include #include /** @@ -41,6 +40,8 @@ typedef struct { size_t current_index; // Current position in the driver list bool is_open; // Whether the directory is open + char path[256]; // Path being listed + driver_node_t* parent_driver; // Parent driver for subdirectory listing (NULL for root) } dir_handle_t; /** @@ -67,6 +68,8 @@ static Dmod_Context_t* prepare_driver_module(const char* driver_name, bool* was_ static void cleanup_driver_module(const char* driver_name, bool was_loaded, bool was_enabled); static void build_device_filename(const driver_node_t* driver_node, char* filename, size_t size); static bool is_root_path(const char* path); +static driver_node_t* find_driver_by_subdir(dmfsi_context_t ctx, const char* path); +static bool path_is_device_subdir(dmfsi_context_t ctx, const char* path); // ============================================================================ // Module Interface Implementation @@ -109,20 +112,20 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, dmfsi_context_t, _init, (const cha { if(config == NULL) { - DMOD_LOG_ERROR("dmdevfs: Config path is NULL\n"); + DMOD_LOG_ERROR("Config path is NULL\n"); return NULL; } if(strlen(config) == 0) { - DMOD_LOG_ERROR("dmdevfs: Config path is empty\n"); + DMOD_LOG_ERROR("Config path is empty\n"); return NULL; } dmfsi_context_t ctx = Dmod_Malloc(sizeof(struct dmfsi_context)); if (ctx == NULL) { - DMOD_LOG_ERROR("dmdevfs: Failed to allocate memory for context\n"); + DMOD_LOG_ERROR("Failed to allocate memory for context\n"); return NULL; } @@ -133,7 +136,7 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, dmfsi_context_t, _init, (const cha int res = configure_drivers(ctx, NULL, ctx->config_path); if (res != DMFSI_OK) { - DMOD_LOG_ERROR("dmdevfs: Failed to configure drivers\n"); + DMOD_LOG_ERROR("Failed to configure drivers\n"); unconfigure_drivers(ctx); dmlist_destroy(ctx->drivers); Dmod_Free(ctx->config_path); @@ -159,7 +162,7 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _deinit, (dmfsi_context_t ctx { if (!dmfsi_dmdevfs_context_is_valid(ctx)) { - DMOD_LOG_ERROR("dmdevfs: Invalid context in deinit\n"); + DMOD_LOG_ERROR("Invalid context in deinit\n"); return DMFSI_ERR_INVALID; } @@ -177,12 +180,12 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _fopen, (dmfsi_context_t ctx, { if(dmfsi_dmdevfs_context_is_valid(ctx) == 0) { - DMOD_LOG_ERROR("dmdevfs: Invalid context in fopen\n"); + DMOD_LOG_ERROR("Invalid context in fopen\n"); return DMFSI_ERR_INVALID; } // TODO: Implement file opening through driver - DMOD_LOG_ERROR("dmdevfs: fopen not yet implemented\n"); + DMOD_LOG_ERROR("fopen not yet implemented\n"); return DMFSI_ERR_GENERAL; } @@ -193,12 +196,12 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _fclose, (dmfsi_context_t ctx { if(dmfsi_dmdevfs_context_is_valid(ctx) == 0) { - DMOD_LOG_ERROR("dmdevfs: Invalid context in fclose\n"); + DMOD_LOG_ERROR("Invalid context in fclose\n"); return DMFSI_ERR_INVALID; } // TODO: Implement file closing - DMOD_LOG_ERROR("dmdevfs: fclose not yet implemented\n"); + DMOD_LOG_ERROR("fclose not yet implemented\n"); return DMFSI_ERR_GENERAL; } @@ -209,7 +212,7 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _fread, (dmfsi_context_t ctx, { if(dmfsi_dmdevfs_context_is_valid(ctx) == 0) { - DMOD_LOG_ERROR("dmdevfs: Invalid context in fread\n"); + DMOD_LOG_ERROR("Invalid context in fread\n"); return DMFSI_ERR_INVALID; } @@ -225,7 +228,7 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _fwrite, (dmfsi_context_t ctx { if(dmfsi_dmdevfs_context_is_valid(ctx) == 0) { - DMOD_LOG_ERROR("dmdevfs: Invalid context in fwrite\n"); + DMOD_LOG_ERROR("Invalid context in fwrite\n"); return DMFSI_ERR_INVALID; } @@ -241,7 +244,7 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _lseek, (dmfsi_context_t ctx, { if(dmfsi_dmdevfs_context_is_valid(ctx) == 0) { - DMOD_LOG_ERROR("dmdevfs: Invalid context in lseek\n"); + DMOD_LOG_ERROR("Invalid context in lseek\n"); return DMFSI_ERR_INVALID; } @@ -256,7 +259,7 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, long, _tell, (dmfsi_context_t ctx, { if(dmfsi_dmdevfs_context_is_valid(ctx) == 0) { - DMOD_LOG_ERROR("dmdevfs: Invalid context in tell\n"); + DMOD_LOG_ERROR("Invalid context in tell\n"); return -1; } @@ -271,7 +274,7 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _eof, (dmfsi_context_t ctx, v { if(dmfsi_dmdevfs_context_is_valid(ctx) == 0) { - DMOD_LOG_ERROR("dmdevfs: Invalid context in eof\n"); + DMOD_LOG_ERROR("Invalid context in eof\n"); return 1; } @@ -286,7 +289,7 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, long, _size, (dmfsi_context_t ctx, { if(dmfsi_dmdevfs_context_is_valid(ctx) == 0) { - DMOD_LOG_ERROR("dmdevfs: Invalid context in size\n"); + DMOD_LOG_ERROR("Invalid context in size\n"); return -1; } @@ -301,7 +304,7 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _getc, (dmfsi_context_t ctx, { if(dmfsi_dmdevfs_context_is_valid(ctx) == 0) { - DMOD_LOG_ERROR("dmdevfs: Invalid context in getc\n"); + DMOD_LOG_ERROR("Invalid context in getc\n"); return -1; } @@ -316,7 +319,7 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _putc, (dmfsi_context_t ctx, { if(dmfsi_dmdevfs_context_is_valid(ctx) == 0) { - DMOD_LOG_ERROR("dmdevfs: Invalid context in putc\n"); + DMOD_LOG_ERROR("Invalid context in putc\n"); return -1; } @@ -331,7 +334,7 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _fflush, (dmfsi_context_t ctx { if(dmfsi_dmdevfs_context_is_valid(ctx) == 0) { - DMOD_LOG_ERROR("dmdevfs: Invalid context in fflush\n"); + DMOD_LOG_ERROR("Invalid context in fflush\n"); return DMFSI_ERR_INVALID; } @@ -346,7 +349,7 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _sync, (dmfsi_context_t ctx, { if(dmfsi_dmdevfs_context_is_valid(ctx) == 0) { - DMOD_LOG_ERROR("dmdevfs: Invalid context in sync\n"); + DMOD_LOG_ERROR("Invalid context in sync\n"); return DMFSI_ERR_INVALID; } @@ -361,26 +364,49 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _opendir, (dmfsi_context_t ct { if(dmfsi_dmdevfs_context_is_valid(ctx) == 0) { - DMOD_LOG_ERROR("dmdevfs: Invalid context in opendir\n"); + DMOD_LOG_ERROR("Invalid context in opendir\n"); return DMFSI_ERR_INVALID; } - // Only support opening root directory + dir_handle_t* dir = Dmod_Malloc(sizeof(dir_handle_t)); + if (dir == NULL) + { + DMOD_LOG_ERROR("Failed to allocate directory handle\n"); + return DMFSI_ERR_GENERAL; + } + + dir->current_index = 0; + dir->is_open = true; + dir->parent_driver = NULL; + + // Store the path + if (path != NULL) + { + Dmod_StrNCpy(dir->path, path, sizeof(dir->path) - 1); + dir->path[sizeof(dir->path) - 1] = '\0'; + } + else + { + dir->path[0] = '\0'; + } + + // Check if opening root directory if (is_root_path(path)) { - dir_handle_t* dir = Dmod_Malloc(sizeof(dir_handle_t)); - if (dir == NULL) - { - DMOD_LOG_ERROR("dmdevfs: Failed to allocate directory handle\n"); - return DMFSI_ERR_GENERAL; - } - - dir->current_index = 0; - dir->is_open = true; *dp = dir; return DMFSI_OK; } + // Check if opening a device subdirectory (e.g., /uart0/) + driver_node_t* parent = find_driver_by_subdir(ctx, path); + if (parent != NULL) + { + dir->parent_driver = parent; + *dp = dir; + return DMFSI_OK; + } + + Dmod_Free(dir); return DMFSI_ERR_NOT_FOUND; } @@ -391,7 +417,7 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _readdir, (dmfsi_context_t ct { if(dmfsi_dmdevfs_context_is_valid(ctx) == 0) { - DMOD_LOG_ERROR("dmdevfs: Invalid context in readdir\n"); + DMOD_LOG_ERROR("Invalid context in readdir\n"); return DMFSI_ERR_INVALID; } @@ -406,26 +432,69 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _readdir, (dmfsi_context_t ct return DMFSI_ERR_INVALID; } - size_t driver_count = dmlist_size(ctx->drivers); - if (dir->current_index >= driver_count) + // If listing a subdirectory (parent_driver is set), return minor device entries + if (dir->parent_driver != NULL) { - return DMFSI_ERR_NOT_FOUND; // No more entries + // For subdirectories, we only have one entry: the minor number + if (dir->current_index > 0) + { + return DMFSI_ERR_NOT_FOUND; // Only one entry + } + + // Return the minor number as filename + Dmod_SnPrintf(entry->name, sizeof(entry->name), "%d", dir->parent_driver->dev_num.minor); + entry->is_directory = false; + entry->size = 0; + dir->current_index++; + return DMFSI_OK; } - driver_node_t* driver_node = (driver_node_t*)dmlist_get(ctx->drivers, dir->current_index); - if (driver_node == NULL) + // Listing root directory - iterate through drivers + size_t driver_count = dmlist_size(ctx->drivers); + + while (dir->current_index < driver_count) { + driver_node_t* driver_node = (driver_node_t*)dmlist_get(ctx->drivers, dir->current_index); dir->current_index++; - return DMFSI_ERR_GENERAL; + + if (driver_node == NULL) + { + continue; + } + + // Build device name + if (driver_node->dev_num.flags == DMDRVI_NUM_NONE) + { + // Simple device file + Dmod_SnPrintf(entry->name, sizeof(entry->name), "%s", driver_node->driver_name); + entry->is_directory = false; + } + else if (driver_node->dev_num.flags & DMDRVI_NUM_MINOR) + { + // Directory with major number (contains minor device files) + Dmod_SnPrintf(entry->name, sizeof(entry->name), "%s%d", + driver_node->driver_name, driver_node->dev_num.major); + entry->is_directory = true; + } + else if (driver_node->dev_num.flags & DMDRVI_NUM_MAJOR) + { + // Device file with major number only + Dmod_SnPrintf(entry->name, sizeof(entry->name), "%s%d", + driver_node->driver_name, driver_node->dev_num.major); + entry->is_directory = false; + } + else + { + // Fallback + Dmod_SnPrintf(entry->name, sizeof(entry->name), "%s", driver_node->driver_name); + entry->is_directory = false; + } + + entry->size = 0; + return DMFSI_OK; } - // Build device filename based on dev_num flags - build_device_filename(driver_node, entry->name, sizeof(entry->name)); - entry->is_directory = false; - entry->size = 0; - - dir->current_index++; - return DMFSI_OK; + return DMFSI_ERR_NOT_FOUND; // No more entries } /** @@ -435,7 +504,7 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _closedir, (dmfsi_context_t c { if(dmfsi_dmdevfs_context_is_valid(ctx) == 0) { - DMOD_LOG_ERROR("dmdevfs: Invalid context in closedir\n"); + DMOD_LOG_ERROR("Invalid context in closedir\n"); return DMFSI_ERR_INVALID; } @@ -465,12 +534,18 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _direxists, (dmfsi_context_t { if(dmfsi_dmdevfs_context_is_valid(ctx) == 0) { - DMOD_LOG_ERROR("dmdevfs: Invalid context in direxists\n"); + DMOD_LOG_ERROR("Invalid context in direxists\n"); return 0; } - // Only root directory exists - return is_root_path(path) ? 1 : 0; + // Check if root directory + if (is_root_path(path)) + { + return 1; + } + + // Check if it's a device subdirectory + return path_is_device_subdir(ctx, path) ? 1 : 0; } /** @@ -480,7 +555,7 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _stat, (dmfsi_context_t ctx, { if(dmfsi_dmdevfs_context_is_valid(ctx) == 0) { - DMOD_LOG_ERROR("dmdevfs: Invalid context in stat\n"); + DMOD_LOG_ERROR("Invalid context in stat\n"); return DMFSI_ERR_INVALID; } @@ -495,7 +570,7 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _unlink, (dmfsi_context_t ctx { if(dmfsi_dmdevfs_context_is_valid(ctx) == 0) { - DMOD_LOG_ERROR("dmdevfs: Invalid context in unlink\n"); + DMOD_LOG_ERROR("Invalid context in unlink\n"); return DMFSI_ERR_INVALID; } @@ -510,7 +585,7 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, int, _rename, (dmfsi_context_t ctx { if(dmfsi_dmdevfs_context_is_valid(ctx) == 0) { - DMOD_LOG_ERROR("dmdevfs: Invalid context in rename\n"); + DMOD_LOG_ERROR("Invalid context in rename\n"); return DMFSI_ERR_INVALID; } @@ -531,7 +606,7 @@ static int configure_drivers(dmfsi_context_t ctx, const char* driver_name, const void* dir = Dmod_OpenDir(config_path); if (dir == NULL) { - DMOD_LOG_ERROR("dmdevfs: Failed to open config directory: %s\n", ctx->config_path); + DMOD_LOG_ERROR("Failed to open config directory: %s\n", ctx->config_path); return DMFSI_ERR_NOT_FOUND; } @@ -544,7 +619,7 @@ static int configure_drivers(dmfsi_context_t ctx, const char* driver_name, const dmini_context_t config_ctx = read_driver_for_config(entry, module_name, sizeof(module_name), driver_name); if (config_ctx == NULL) { - DMOD_LOG_ERROR("dmdevfs: Failed to read driver for config: %s\n", entry); + DMOD_LOG_ERROR("Failed to read driver for config: %s\n", entry); continue; } @@ -552,12 +627,12 @@ static int configure_drivers(dmfsi_context_t ctx, const char* driver_name, const dmini_destroy(config_ctx); if (driver_node == NULL) { - DMOD_LOG_ERROR("dmdevfs: Failed to configure driver: %s\n", module_name); + DMOD_LOG_ERROR("Failed to configure driver: %s\n", module_name); continue; } if(dmlist_push_back(ctx->drivers, driver_node) != 0) { - DMOD_LOG_ERROR("dmdevfs: Failed to add driver to list: %s\n", module_name); + DMOD_LOG_ERROR("Failed to add driver to list: %s\n", module_name); Dmod_Free(driver_node); continue; } @@ -570,7 +645,7 @@ static int configure_drivers(dmfsi_context_t ctx, const char* driver_name, const int res = configure_drivers(ctx, driver_name, entry); if (res != DMFSI_OK) { - DMOD_LOG_ERROR("dmdevfs: Failed to configure drivers in directory: %s\n", entry); + DMOD_LOG_ERROR("Failed to configure drivers in directory: %s\n", entry); } } } @@ -622,21 +697,10 @@ static driver_node_t* configure_driver(const char* driver_name, dmini_context_t return NULL; } - // Log device file information based on dev_num flags - if (driver_node->dev_num.flags == DMDRVI_NUM_NONE) - { - Dmod_Printf("dmdevfs: Device created: %s\n", driver_name); - } - else if (driver_node->dev_num.flags & DMDRVI_NUM_MINOR) - { - Dmod_Printf("dmdevfs: Device created: %s%d/%d (major/minor)\n", - driver_name, driver_node->dev_num.major, driver_node->dev_num.minor); - } - else if (driver_node->dev_num.flags & DMDRVI_NUM_MAJOR) - { - Dmod_Printf("dmdevfs: Device created: %s%d (major)\n", - driver_name, driver_node->dev_num.major); - } + // Log device file creation + char device_name[256]; + build_device_filename(driver_node, device_name, sizeof(device_name)); + DMOD_LOG_INFO("Device created: %s\n", device_name); return driver_node; } @@ -699,14 +763,14 @@ static dmini_context_t read_driver_for_config(const char* config_path, char* dri dmini_context_t ctx = dmini_create(); if (ctx == NULL) { - DMOD_LOG_ERROR("dmdevfs: Failed to create INI context\n"); + DMOD_LOG_ERROR("Failed to create INI context\n"); return NULL; } int res = dmini_parse_file(ctx, config_path); if (res != DMINI_OK) { - DMOD_LOG_ERROR("dmdevfs: Failed to parse INI file: %s\n", config_path); + DMOD_LOG_ERROR("Failed to parse INI file: %s\n", config_path); dmini_destroy(ctx); return NULL; } @@ -790,32 +854,21 @@ static void build_device_filename(const driver_node_t* driver_node, char* filena return; } - if (driver_node->dev_num.flags == DMDRVI_NUM_NONE) - { - // Device file: /dev/dmclk -> dmclk (no /dev prefix needed) - snprintf(filename, size, "%s", driver_node->driver_name); - } - else if (driver_node->dev_num.flags & DMDRVI_NUM_MINOR) - { - // Device file: /dev/dmspi0/0 -> dmspi0/0 - // This represents a directory structure with major and minor numbers - // TODO: Implement proper nested directory support in opendir/readdir - snprintf(filename, size, "%s%d/%d", - driver_node->driver_name, - driver_node->dev_num.major, - driver_node->dev_num.minor); - } - else if (driver_node->dev_num.flags & DMDRVI_NUM_MAJOR) + // Build device name - start with driver name + Dmod_SnPrintf(filename, size, "%s", driver_node->driver_name); + + // Append major number if present + if (driver_node->dev_num.flags & DMDRVI_NUM_MAJOR) { - // Device file: /dev/dmuart0 -> dmuart0 - snprintf(filename, size, "%s%d", - driver_node->driver_name, - driver_node->dev_num.major); + size_t len = Dmod_StrLen(filename); + Dmod_SnPrintf(filename + len, size - len, "%d", driver_node->dev_num.major); } - else + + // Append minor number if present (with directory separator) + if (driver_node->dev_num.flags & DMDRVI_NUM_MINOR) { - // Fallback to driver name only - snprintf(filename, size, "%s", driver_node->driver_name); + size_t len = Dmod_StrLen(filename); + Dmod_SnPrintf(filename + len, size - len, "/%d", driver_node->dev_num.minor); } } @@ -839,4 +892,78 @@ static bool is_root_path(const char* path) return false; } + +/** + * @brief Find a driver that has a subdirectory matching the given path + * + * @param ctx Filesystem context + * @param path Path to search for (e.g., "/uart0" or "uart0") + * @return Pointer to driver node if found, NULL otherwise + */ +static driver_node_t* find_driver_by_subdir(dmfsi_context_t ctx, const char* path) +{ + if (ctx == NULL || path == NULL) + { + return NULL; + } + + // Skip leading slash if present + const char* search_path = path; + if (path[0] == '/') + { + search_path = path + 1; + } + + size_t driver_count = dmlist_size(ctx->drivers); + for (size_t i = 0; i < driver_count; i++) + { + driver_node_t* driver_node = (driver_node_t*)dmlist_get(ctx->drivers, i); + if (driver_node == NULL) + { + continue; + } + + // Only drivers with MINOR flag create subdirectories + if (!(driver_node->dev_num.flags & DMDRVI_NUM_MINOR)) + { + continue; + } + + // Build expected directory name: drivername + major number + char dir_name[256]; + Dmod_SnPrintf(dir_name, sizeof(dir_name), "%s%d", + driver_node->driver_name, driver_node->dev_num.major); + + // Check if path matches (with or without trailing slash) + size_t dir_len = Dmod_StrLen(dir_name); + size_t path_len = Dmod_StrLen(search_path); + + if (path_len == dir_len && strcmp(search_path, dir_name) == 0) + { + return driver_node; + } + + // Also check with trailing slash + if (path_len == dir_len + 1 && + Dmod_StrNCmp(search_path, dir_name, dir_len) == 0 && + search_path[dir_len] == '/') + { + return driver_node; + } + } + + return NULL; +} + +/** + * @brief Check if a path corresponds to a device subdirectory + * + * @param ctx Filesystem context + * @param path Path to check + * @return true if path is a device subdirectory, false otherwise + */ +static bool path_is_device_subdir(dmfsi_context_t ctx, const char* path) +{ + return find_driver_by_subdir(ctx, path) != NULL; +} } \ No newline at end of file From 6268f9d943e74bbe5812ec11bd2310d36b5f5b35 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 29 Dec 2025 12:22:41 +0000 Subject: [PATCH 8/8] Use DMOD string functions consistently throughout the code Co-authored-by: JohnAmadis <17320783+JohnAmadis@users.noreply.github.com> --- src/dmdevfs.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/dmdevfs.c b/src/dmdevfs.c index d94b29d..5fa0a94 100644 --- a/src/dmdevfs.c +++ b/src/dmdevfs.c @@ -116,7 +116,7 @@ dmod_dmfsi_dif_api_declaration( 1.0, dmdevfs, dmfsi_context_t, _init, (const cha return NULL; } - if(strlen(config) == 0) + if(Dmod_StrLen(config) == 0) { DMOD_LOG_ERROR("Config path is empty\n"); return NULL; @@ -685,7 +685,7 @@ static driver_node_t* configure_driver(const char* driver_name, dmini_context_t driver_node->was_loaded = was_loaded; driver_node->was_enabled = was_enabled; driver_node->driver = driver; - strncpy(driver_node->driver_name, driver_name, DMOD_MAX_MODULE_NAME_LENGTH - 1); + Dmod_StrNCpy(driver_node->driver_name, driver_name, DMOD_MAX_MODULE_NAME_LENGTH - 1); driver_node->driver_name[DMOD_MAX_MODULE_NAME_LENGTH - 1] = '\0'; driver_node->driver_context = dmdrvi_create(config_ctx, &driver_node->dev_num); @@ -751,7 +751,7 @@ static void read_base_name(const char* path, char* base_name, size_t name_size) { const char* last_slash = strrchr(path, '/'); const char* name_start = (last_slash != NULL) ? last_slash + 1 : path; - strncpy(base_name, name_start, name_size); + Dmod_StrNCpy(base_name, name_start, name_size); base_name[name_size - 1] = '\0'; } @@ -778,7 +778,7 @@ static dmini_context_t read_driver_for_config(const char* config_path, char* dri const char* name = dmini_get_string(ctx, "main", "driver_name", default_driver); if(name != NULL) { - strncpy(driver_name, name, name_size); + Dmod_StrNCpy(driver_name, name, name_size); driver_name[name_size - 1] = '\0'; return ctx; } @@ -787,7 +787,7 @@ static dmini_context_t read_driver_for_config(const char* config_path, char* dri // cut the `.ini` extension if present char* ext = strrchr(driver_name, '.'); - if (ext != NULL && strcmp(ext, ".ini") == 0) + if (ext != NULL && Dmod_StrCmp(ext, ".ini") == 0) { *ext = '\0'; } @@ -885,7 +885,7 @@ static bool is_root_path(const char* path) return true; } - if (path[0] == '\0' || strcmp(path, "/") == 0) + if (path[0] == '\0' || Dmod_StrCmp(path, "/") == 0) { return true; } @@ -938,7 +938,7 @@ static driver_node_t* find_driver_by_subdir(dmfsi_context_t ctx, const char* pat size_t dir_len = Dmod_StrLen(dir_name); size_t path_len = Dmod_StrLen(search_path); - if (path_len == dir_len && strcmp(search_path, dir_name) == 0) + if (path_len == dir_len && Dmod_StrCmp(search_path, dir_name) == 0) { return driver_node; }