From 2da4c8c184ab6b7bf6bc4dfb2a8d44526a915520 Mon Sep 17 00:00:00 2001 From: Stephen Berard Date: Wed, 19 Mar 2025 20:00:18 +0100 Subject: [PATCH 1/6] Multi container fixes plus initial GPIO support (#27) * Fix multi container issues * Added memory check feature * Initial GPIO support * Fix for container ID in thread create * Fix for container update * Clean up mechanism for timers * Fix for ocre sensors * Fix for app updates --------- Co-authored-by: ArturSir Co-authored-by: SorinOlari --- CMakeLists.txt | 18 +- Kconfig | 55 +- boards/native_sim.overlay | 4 + prj.conf | 6 +- src/ocre/api/ocre_api.c | 21 +- .../components/container_supervisor/cs_sm.c | 10 +- .../container_supervisor/cs_sm_impl.c | 136 +++- src/ocre/container_messaging/messaging.c | 228 +++++++ src/ocre/container_messaging/messaging.h | 49 ++ src/ocre/ocre.h | 10 +- .../ocre_container_runtime.c | 1 + src/ocre/ocre_gpio/ocre_gpio.c | 598 ++++++++++++++++++ src/ocre/ocre_gpio/ocre_gpio.h | 147 +++++ src/ocre/ocre_sensors/ocre_sensors.c | 33 +- src/ocre/ocre_sensors/rng_sensor.c | 6 +- 15 files changed, 1282 insertions(+), 40 deletions(-) create mode 100644 src/ocre/container_messaging/messaging.c create mode 100644 src/ocre/container_messaging/messaging.h create mode 100644 src/ocre/ocre_gpio/ocre_gpio.c create mode 100644 src/ocre/ocre_gpio/ocre_gpio.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f250888..b1a9c34d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,13 +114,25 @@ set(lib_sources ) # Compile in sensors framework if enabled. -if(DEFINED CONFIG_SENSOR) +if(CONFIG_OCRE_SENSORS) + set(ocre_sources ${ocre_sources} ${OCRE_ROOT_DIR}/src/ocre/ocre_sensors/ocre_sensors.c) +endif() + +# Compile random sensor if enabled. +if(CONFIG_RNG_SENSOR) + set(ocre_sources ${ocre_sources} ${OCRE_ROOT_DIR}/src/ocre/ocre_sensors/rng_sensor.c) +endif() + +if(DEFINED CONFIG_OCRE_GPIO) set(lib_sources ${lib_sources} - src/ocre/ocre_sensors/ocre_sensors.c - src/ocre/ocre_sensors/rng_sensor.c + src/ocre/ocre_gpio/ocre_gpio.c ) endif() +# Compile container messaging if enabled. +if(CONFIG_OCRE_CONTAINER_MESSAGING) + set(lib_sources ${lib_sources} src/ocre/container_messaging/messaging.c) +endif() set(component_sources # Component support diff --git a/Kconfig b/Kconfig index 6b584e5e..9424966e 100644 --- a/Kconfig +++ b/Kconfig @@ -65,10 +65,17 @@ source "subsys/logging/Kconfig.template.log_config" endif +config OCRE_SENSORS + bool "Enable OCRE Sensors support" + default n + depends on SENSOR + help + Enable support for OCRE sensors + config RNG_SENSOR bool "RNG Sensor" default n - depends on SENSOR + depends on OCRE_SENSORS help Enable support for the custom RNG sensor. @@ -109,6 +116,50 @@ config MAX_CHANNELS_PER_SENSOR help Defines the maximum number of channels that each sensor can have. +config OCRE_MEMORY_CHECK_ENABLED + bool "Enable memory availability checking for containers" + default y + help + Enable runtime memory checks before creating containers + + +config OCRE_GPIO + bool "OCRE GPIO Driver" + default y + help + Enable the OCRE GPIO driver that provides a portable API layer + for GPIO operations across different hardware platforms. + +config OCRE_GPIO_MAX_PINS + int "Maximum number of GPIO pins" + default 32 + help + Maximum number of GPIO pins that can be managed by the OCRE GPIO driver. + +config OCRE_GPIO_MAX_PORTS + int "Maximum number of GPIO ports" + default 4 + help + Maximum number of GPIO port devices that can be used by the OCRE GPIO driver. + +config OCRE_GPIO_PINS_PER_PORT + int "Number of pins per GPIO port" + default 32 + help + Number of pins available on each GPIO port. This is used to map the + logical pin numbers to physical port and pin numbers. + +config OCRE_CONTAINER_MESSAGING + bool "Enable OCRE Container Messaging support" + default n + help + Enable support for OCRE Container Messaging + +config MESSAGING_MAX_SUBSCRIPTIONS + int "Number of maximum subscriptions for Container Messaging" + default 10 + depends on OCRE_CONTAINER_MESSAGING + help + Number of maximum subscriptions for Container Messaging - endmenu \ No newline at end of file diff --git a/boards/native_sim.overlay b/boards/native_sim.overlay index 4b31cbb3..8bf90af4 100644 --- a/boards/native_sim.overlay +++ b/boards/native_sim.overlay @@ -5,6 +5,10 @@ zephyr,uart-mcumgr = &uart0; }; + aliases { + rng0 = &rng_device; + }; + devices{ rng_device: rng_0 { compatible = "custom,rng-sensor"; diff --git a/prj.conf b/prj.conf index 55346357..822707c5 100644 --- a/prj.conf +++ b/prj.conf @@ -73,6 +73,9 @@ CONFIG_LOG_TRACE_SHORT_TIMESTAMP=y CONFIG_SENSOR=y CONFIG_RNG_SENSOR=y +CONFIG_OCRE_CONTAINER_MESSAGING=y +CONFIG_MESSAGING_MAX_SUBSCRIPTIONS=10 + CONFIG_DEVICE_DT_METADATA=y CONFIG_DEVICE_DEPS=y @@ -81,5 +84,6 @@ CONFIG_MAX_TIMERS=5 CONFIG_MAX_SENSORS=10 CONFIG_MAX_CHANNELS_PER_SENSOR=5 - +CONFIG_OCRE_MEMORY_CHECK_ENABLED=n +CONFIG_GPIO=y diff --git a/src/ocre/api/ocre_api.c b/src/ocre/api/ocre_api.c index 5a5ef3d6..3dde01e7 100644 --- a/src/ocre/api/ocre_api.c +++ b/src/ocre/api/ocre_api.c @@ -19,6 +19,8 @@ #include "ocre_api.h" #include "../ocre_timers/ocre_timer.h" #include "../ocre_sensors/ocre_sensors.h" +#include "../ocre_gpio/ocre_gpio.h" +#include "../container_messaging/messaging.h" int _ocre_posix_uname(wasm_exec_env_t exec_env, struct _ocre_posix_utsname *name) { struct utsname info; @@ -82,6 +84,15 @@ NativeSymbol ocre_api_table[] = { {"ocre_sleep", ocre_sleep, "(i)i", NULL}, +// Container Messaging API +#ifdef CONFIG_OCRE_CONTAINER_MESSAGING + {"ocre_msg_system_init", ocre_msg_system_init, "()", NULL}, + {"ocre_publish_message", ocre_publish_message, "(***i)i", NULL}, + {"ocre_subscribe_message", ocre_subscribe_message, "(**)i", NULL}, +#endif + +// Sensor API +#ifdef CONFIG_OCRE_SENSORS // Sensor API {"ocre_sensors_init", ocre_sensors_init, "()i", NULL}, {"ocre_sensors_discover", ocre_sensors_discover, "()i", NULL}, @@ -90,7 +101,7 @@ NativeSymbol ocre_api_table[] = { {"ocre_sensors_get_channel_count", ocre_sensors_get_channel_count, "(i)i", NULL}, {"ocre_sensors_get_channel_type", ocre_sensors_get_channel_type, "(ii)i", NULL}, {"ocre_sensors_read", ocre_sensors_read, "(ii)i", NULL}, - +#endif // Timer API {"ocre_timer_create", ocre_timer_create, "(i)i", NULL}, {"ocre_timer_start", ocre_timer_start, "(iii)i", NULL}, @@ -99,6 +110,14 @@ NativeSymbol ocre_api_table[] = { {"ocre_timer_get_remaining", ocre_timer_get_remaining, "(i)i", NULL}, {"ocre_timer_set_dispatcher", ocre_timer_set_dispatcher, "(i)v", NULL}, + // GPIO API + {"ocre_gpio_init", ocre_gpio_wasm_init, "()i", NULL}, + {"ocre_gpio_configure", ocre_gpio_wasm_configure, "(iii)i", NULL}, + {"ocre_gpio_set", ocre_gpio_wasm_set, "(iii)i", NULL}, + {"ocre_gpio_get", ocre_gpio_wasm_get, "(ii)i", NULL}, + {"ocre_gpio_toggle", ocre_gpio_wasm_toggle, "(ii)i", NULL}, + {"ocre_gpio_register_callback", ocre_gpio_wasm_register_callback, "(ii)i", NULL}, + {"ocre_gpio_unregister_callback", ocre_gpio_wasm_unregister_callback, "(ii)i", NULL}, }; int ocre_api_table_size = sizeof(ocre_api_table) / sizeof(NativeSymbol); \ No newline at end of file diff --git a/src/ocre/components/container_supervisor/cs_sm.c b/src/ocre/components/container_supervisor/cs_sm.c index 7020a337..e78a705a 100644 --- a/src/ocre/components/container_supervisor/cs_sm.c +++ b/src/ocre/components/container_supervisor/cs_sm.c @@ -36,6 +36,7 @@ static void runtime_uninitialized_run(void *o) { switch (msg->event) { case EVENT_CS_INITIALIZE: + LOG_INF("Transitioning from state STATE_RUNTIME_UNINITIALIZED_RUN to state STATE_RUNTIME_RUNNING"); sm_transition(&ocre_cs_state_machine, STATE_RUNTIME_RUNNING); break; @@ -47,10 +48,6 @@ static void runtime_uninitialized_run(void *o) { SM_MARK_EVENT_HANDLED(o); } -// void callbackFcn(void) { -// LOG_INF("CALLBACK CALLED"); -// } - static void runtime_running_entry(void *o) { #if OCRE_CS_DEBUG_ON LOG_INF("HELLO runtime_running_entry"); @@ -75,6 +72,7 @@ static void runtime_running_run(void *o) { break; } case EVENT_RUN_CONTAINER: { + LOG_INF("EVENT_RUN_CONTAINER"); if (CS_run_container(ctx, &msg->containerId) == CONTAINER_STATUS_RUNNING) { LOG_INF("Started container in slot:%d", msg->containerId); } else { @@ -83,18 +81,22 @@ static void runtime_running_run(void *o) { break; } case EVENT_STOP_CONTAINER: { + LOG_INF("EVENT_STOP_CONTAINER"); CS_stop_container(ctx, msg->containerId, callback); break; } case EVENT_DESTROY_CONTAINER: { + LOG_INF("EVENT_DESTROY_CONTAINER"); CS_destroy_container(ctx, msg->containerId, callback); break; } case EVENT_RESTART_CONTAINER: { + LOG_INF("EVENT_RESTART_CONTAINER"); CS_restart_container(ctx, msg->containerId, callback); break; } case EVENT_CS_DESTROY: + LOG_INF("EVENT_CS_DESTROY"); sm_transition(&ocre_cs_state_machine, STATE_RUNTIME_UNINITIALIZED); break; default: diff --git a/src/ocre/components/container_supervisor/cs_sm_impl.c b/src/ocre/components/container_supervisor/cs_sm_impl.c index afa1fc02..1f12302d 100644 --- a/src/ocre/components/container_supervisor/cs_sm_impl.c +++ b/src/ocre/components/container_supervisor/cs_sm_impl.c @@ -13,12 +13,13 @@ LOG_MODULE_DECLARE(ocre_cs_component, OCRE_LOG_LEVEL); #include #include "../../../../../wasm-micro-runtime/core/iwasm/include/lib_export.h" +#include "bh_log.h" #include "cs_sm.h" #include "cs_sm_impl.h" #include "../../ocre_timers/ocre_timer.h" +#include "../../container_messaging/messaging.h" -// Internal Data structures for runtime static char filepath[FILE_PATH_MAX]; static char wamr_heap_buf[CONFIG_OCRE_WAMR_HEAP_BUFFER_SIZE] = {0}; @@ -29,6 +30,24 @@ static struct k_thread container_threads[MAX_CONTAINERS]; static k_tid_t container_thread_ids[MAX_CONTAINERS]; static bool container_thread_active[MAX_CONTAINERS] = {false}; +#ifdef CONFIG_OCRE_MEMORY_CHECK_ENABLED +static size_t ocre_get_available_memory(void) { +#ifdef CONFIG_HEAP_MEM_POOL_SIZE + struct k_heap_stats stats; + k_heap_sys_get_stats(&stats); + return stats.free_bytes; +#else + extern char *z_malloc_mem_pool_area_start; + extern char *z_malloc_mem_pool_area_end; + extern struct sys_mem_pool_base z_malloc_mem_pool; + + size_t total_size = z_malloc_mem_pool_area_end - z_malloc_mem_pool_area_start; + size_t used_size = sys_mem_pool_get_used_size(&z_malloc_mem_pool); + return total_size - used_size; +#endif +} +#endif + // Thread entry point for container execution static void container_thread_entry(void *container_id_ptr, void *ctx_ptr, void *unused) { int container_id = *((int *)container_id_ptr); @@ -51,6 +70,7 @@ static void container_thread_entry(void *container_id_ptr, void *ctx_ptr, void * if (ctx->containers[container_id].container_runtime_status == CONTAINER_STATUS_RUNNING) { ctx->containers[container_id].container_runtime_status = CONTAINER_STATUS_STOPPED; + LOG_INF("Container %d marked as STOPPED after main exit", container_id); } } @@ -68,6 +88,7 @@ static int load_binary_to_buffer_fs(ocre_cs_ctx *ctx, int container_id, ocre_con } ctx->containers[container_id].ocre_runtime_arguments.size = entry.size; + LOG_INF("Allocating memory for container %d", container_id); ctx->containers[container_id].ocre_runtime_arguments.buffer = (char *)malloc(entry.size * sizeof(char)); if (ctx->containers[container_id].ocre_runtime_arguments.buffer == NULL) { LOG_ERR("Failed to allocate memory for container binary."); @@ -116,8 +137,6 @@ int CS_ctx_init(ocre_cs_ctx *ctx) { return 0; } -// Supervisor functions - ocre_container_runtime_status_t CS_runtime_init(ocre_cs_ctx *ctx, ocre_container_init_arguments_t *args) { RuntimeInitArgs init_args; memset(&init_args, 0, sizeof(RuntimeInitArgs)); @@ -133,11 +152,15 @@ ocre_container_runtime_status_t CS_runtime_init(ocre_cs_ctx *ctx, ocre_container return RUNTIME_STATUS_ERROR; } + // Configure WAMR logging + bh_log_set_verbose_level(BH_LOG_LEVEL_WARNING); + if (!wasm_runtime_register_natives("env", ocre_api_table, ocre_api_table_size)) { LOG_ERR("Failed to register the API's"); return RUNTIME_STATUS_ERROR; } ocre_timer_init(); + ocre_msg_system_init(); return RUNTIME_STATUS_INITIALIZED; } @@ -152,18 +175,49 @@ ocre_container_runtime_status_t CS_runtime_destroy(void) { return RUNTIME_STATUS_DESTROYED; } -// Container functions - ocre_container_status_t CS_create_container(ocre_cs_ctx *ctx, int container_id) { + if (ctx->containers[container_id].container_runtime_status == CONTAINER_STATUS_STOPPED) { + LOG_WRN("Container %d is in STOPPED state, destroying it before reuse", container_id); + CS_destroy_container(ctx, container_id, NULL); + } + if (ctx->containers[container_id].container_runtime_status == CONTAINER_STATUS_DESTROYED || ctx->containers[container_id].container_runtime_status == CONTAINER_STATUS_UNKNOWN) { +#ifdef CONFIG_OCRE_MEMORY_CHECK_ENABLED + size_t requested_heap = ctx->containers[container_id].ocre_container_data.heap_size; + size_t requested_stack = ctx->containers[container_id].ocre_container_data.stack_size; + + if (requested_heap == 0 || requested_heap > CONFIG_OCRE_CONTAINER_DEFAULT_HEAP_SIZE) { + LOG_ERR("Invalid heap size requested: %zu bytes", requested_heap); + return CONTAINER_STATUS_ERROR; + } + + if (requested_stack == 0 || requested_stack > CONFIG_OCRE_CONTAINER_DEFAULT_STACK_SIZE) { + LOG_ERR("Invalid stack size requested: %zu bytes", requested_stack); + return CONTAINER_STATUS_ERROR; + } + + size_t available_memory = ocre_get_available_memory(); + size_t required_memory = requested_heap + requested_stack + sizeof(WASMExecEnv); + + if (available_memory < required_memory) { + LOG_ERR("Insufficient memory for container %d: required %zu bytes, available %zu bytes", container_id, + required_memory, available_memory); + return CONTAINER_STATUS_ERROR; + } + LOG_INF("Memory check passed: %zu bytes required, %zu bytes available", required_memory, available_memory); +#endif + ctx->containers[container_id].ocre_runtime_arguments.stack_size = ctx->containers[container_id].ocre_container_data.stack_size; ctx->containers[container_id].ocre_runtime_arguments.heap_size = ctx->containers[container_id].ocre_container_data.heap_size; +#ifndef CONFIG_OCRE_MEMORY_CHECK_ENABLED ctx->containers[container_id].ocre_runtime_arguments.stack_size = CONFIG_OCRE_CONTAINER_DEFAULT_STACK_SIZE; ctx->containers[container_id].ocre_runtime_arguments.heap_size = CONFIG_OCRE_CONTAINER_DEFAULT_HEAP_SIZE; +#endif + int ret = load_binary_to_buffer_fs(ctx, container_id, &ctx->containers[container_id].ocre_container_data); if (ret < 0) { LOG_ERR("Failed to load binary to buffer: %d", ret); @@ -184,17 +238,23 @@ ocre_container_status_t CS_create_container(ocre_cs_ctx *ctx, int container_id) LOG_ERR("Failed to load WASM module: %s", ctx->containers[container_id].ocre_runtime_arguments.error_buf); return CONTAINER_STATUS_ERROR; } - ctx->containers[container_id].ocre_runtime_arguments.module_inst = - wasm_runtime_instantiate(ctx->containers[container_id].ocre_runtime_arguments.module, - ctx->containers[container_id].ocre_runtime_arguments.stack_size, - ctx->containers[container_id].ocre_runtime_arguments.heap_size, - ctx->containers[container_id].ocre_runtime_arguments.error_buf, - sizeof(ctx->containers[container_id].ocre_runtime_arguments.error_buf)); - if (!ctx->containers[container_id].ocre_runtime_arguments.module_inst) { - LOG_ERR("Failed to instantiate WASM module: %s, for containerID= %d", - ctx->containers[container_id].ocre_runtime_arguments.error_buf, container_id); - return CONTAINER_STATUS_ERROR; + if (ctx->containers[container_id].ocre_runtime_arguments.module_inst) { + LOG_INF("WASM runtime allready instantiated for container:%d", container_id); + } else { + LOG_INF("instantiating WASM runtime for container:%d", container_id); + ctx->containers[container_id].ocre_runtime_arguments.module_inst = + wasm_runtime_instantiate(ctx->containers[container_id].ocre_runtime_arguments.module, + ctx->containers[container_id].ocre_runtime_arguments.stack_size, + ctx->containers[container_id].ocre_runtime_arguments.heap_size, + ctx->containers[container_id].ocre_runtime_arguments.error_buf, + sizeof(ctx->containers[container_id].ocre_runtime_arguments.error_buf)); + if (!ctx->containers[container_id].ocre_runtime_arguments.module_inst) { + LOG_ERR("Failed to instantiate WASM module: %s, for containerID= %d", + ctx->containers[container_id].ocre_runtime_arguments.error_buf, container_id); + return CONTAINER_STATUS_ERROR; + } } + ctx->containers[container_id].container_runtime_status = CONTAINER_STATUS_CREATED; LOG_WRN("Created container:%d", container_id); return ctx->containers[container_id].container_runtime_status; @@ -205,9 +265,23 @@ ocre_container_status_t CS_create_container(ocre_cs_ctx *ctx, int container_id) } ocre_container_status_t CS_run_container(ocre_cs_ctx *ctx, int *container_id) { + char current_sha256[70 + 1]; + + if (ctx->containers[*container_id].container_runtime_status == CONTAINER_STATUS_STOPPED) { + strcpy(current_sha256, ctx->containers[*container_id].ocre_container_data.sha256); + + if (strcmp(current_sha256, ctx->containers[*container_id].ocre_container_data.sha256) != 0) { + LOG_WRN("Running different app in container %d, performing full cleanup", *container_id); + CS_destroy_container(ctx, *container_id, NULL); + CS_create_container(ctx, *container_id); + } + } + if (ctx->containers[*container_id].container_runtime_status == CONTAINER_STATUS_CREATED || ctx->containers[*container_id].container_runtime_status == CONTAINER_STATUS_STOPPED) { ocre_timer_set_module_inst(ctx->containers[*container_id].ocre_runtime_arguments.module_inst); + ocre_gpio_set_module_inst(ctx->containers[*container_id].ocre_runtime_arguments.module_inst); + ctx->containers[*container_id].ocre_runtime_arguments.exec_env = wasm_runtime_create_exec_env(ctx->containers[*container_id].ocre_runtime_arguments.module_inst, ctx->containers[*container_id].ocre_runtime_arguments.stack_size); @@ -258,8 +332,6 @@ ocre_container_status_t CS_stop_container(ocre_cs_ctx *ctx, int container_id, oc } if (ctx->containers[container_id].container_runtime_status == CONTAINER_STATUS_RUNNING) { - ocre_timer_cleanup_container(ctx->containers[container_id].ocre_runtime_arguments.module_inst); - if (container_thread_active[container_id] && container_thread_ids[container_id] != NULL) { LOG_INF("Aborting thread for container %d", container_id); k_thread_abort(container_thread_ids[container_id]); @@ -294,6 +366,8 @@ ocre_container_status_t CS_destroy_container(ocre_cs_ctx *ctx, int container_id, return CONTAINER_STATUS_ERROR; } + ocre_timer_cleanup_container(ctx->containers[container_id].ocre_runtime_arguments.module_inst); + if (ctx->containers[container_id].container_runtime_status == CONTAINER_STATUS_UNKNOWN) { LOG_ERR("Cannot destroy container %d: It was never created.", container_id); return CONTAINER_STATUS_ERROR; @@ -310,6 +384,34 @@ ocre_container_status_t CS_destroy_container(ocre_cs_ctx *ctx, int container_id, container_thread_ids[container_id] = NULL; } + if (ctx->containers[container_id].ocre_runtime_arguments.exec_env) { + wasm_runtime_destroy_exec_env(ctx->containers[container_id].ocre_runtime_arguments.exec_env); + ctx->containers[container_id].ocre_runtime_arguments.exec_env = NULL; + LOG_INF("Destroyed exec environment for container %d", container_id); + } + + if (ctx->containers[container_id].ocre_runtime_arguments.module_inst) { + wasm_runtime_deinstantiate(ctx->containers[container_id].ocre_runtime_arguments.module_inst); + ctx->containers[container_id].ocre_runtime_arguments.module_inst = NULL; + LOG_INF("Deinstantiated WASM module for container %d", container_id); + } + + if (ctx->containers[container_id].ocre_runtime_arguments.module) { + wasm_runtime_unload(ctx->containers[container_id].ocre_runtime_arguments.module); + ctx->containers[container_id].ocre_runtime_arguments.module = NULL; + } + if (ctx->containers[container_id].ocre_runtime_arguments.buffer) { + free(ctx->containers[container_id].ocre_runtime_arguments.buffer); + LOG_INF("Freed buffer for container %d", container_id); + ctx->containers[container_id].ocre_runtime_arguments.buffer = NULL; + } + memset(ctx->containers[container_id].ocre_container_data.name, 0, + sizeof(ctx->containers[container_id].ocre_container_data.name)); + memset(ctx->containers[container_id].ocre_container_data.sha256, 0, + sizeof(ctx->containers[container_id].ocre_container_data.sha256)); + ctx->containers[container_id].ocre_container_data.timers = 0; + ctx->containers[container_id].ocre_container_data.watchdog_interval = 0; + wasm_runtime_unload(ctx->containers[container_id].ocre_runtime_arguments.module); free(ctx->containers[container_id].ocre_runtime_arguments.buffer); ctx->containers[container_id].ocre_runtime_arguments.buffer = NULL; diff --git a/src/ocre/container_messaging/messaging.c b/src/ocre/container_messaging/messaging.c new file mode 100644 index 00000000..cf6d8dc9 --- /dev/null +++ b/src/ocre/container_messaging/messaging.c @@ -0,0 +1,228 @@ + +#include +#include +#include +#include +#include "messaging.h" + +LOG_MODULE_DECLARE(ocre_container_messaging, OCRE_LOG_LEVEL); + +#define QUEUE_SIZE 100 +#define STACK_SIZE 1024 +#define PRIORITY 5 +#define WASM_STACK_SIZE (8 * 1024) + +K_MSGQ_DEFINE(ocre_msg_queue, sizeof(ocre_msg_t), QUEUE_SIZE, 4); + +// Structure to hold the subscription information +typedef struct { + char *topic; + wasm_function_inst_t handler; + wasm_module_inst_t module_inst; + wasm_exec_env_t exec_env; +} ocre_subscription_t; + +static ocre_subscription_t subscriptions[CONFIG_MESSAGING_MAX_SUBSCRIPTIONS]; +static int subscription_count = 0; +static bool msg_system_is_init = false; + +static uint32_t allocate_wasm_memory(wasm_module_inst_t module_inst, const void *src, size_t size) { + void *native_addr = NULL; + uint64_t wasm_ptr = wasm_runtime_module_malloc(module_inst, size, &native_addr); + if (!wasm_ptr || !native_addr) { + LOG_ERR("Failed to allocate memory in WASM"); + return 0; + } + if (src) { + memcpy(native_addr, src, size); + } + return (uint32_t)wasm_ptr; +} + +static void free_wasm_message(wasm_module_inst_t module_inst, uint32_t *ptr_array, uint16_t count) { + for (uint16_t i = 0; i < count; i++) { + if (ptr_array[i]) { + wasm_runtime_module_free(module_inst, ptr_array[i]); + } + } +} + +void subscriber_thread(void *arg1, void *arg2, void *arg3) { + ocre_msg_t msg; + while (1) { + if (k_msgq_get(&ocre_msg_queue, &msg, K_FOREVER) == 0) { + for (int i = 0; i < subscription_count; i++) { + if (strcmp(subscriptions[i].topic, msg.topic) == 0) { + + wasm_module_inst_t module_inst = subscriptions[i].module_inst; + if (!module_inst) { + LOG_ERR("Invalid module instance"); + continue; + } + + uint32_t allocated_pointers[4] = {0}; + // Allocate memory for ocre_msg_t + allocated_pointers[0] = allocate_wasm_memory(module_inst, NULL, sizeof(ocre_msg_t)); + if (allocated_pointers[0] == 0) { + LOG_ERR("Failed to allocate memory for message structure in WASM"); + continue; + } + + // Allocate memory for topic + allocated_pointers[1] = allocate_wasm_memory(module_inst, msg.topic, strlen(msg.topic) + 1); + if (allocated_pointers[1] == 0) { + LOG_ERR("Failed to allocate memory for topic in WASM"); + free_wasm_message(module_inst, allocated_pointers, 1); + continue; + } + + // Allocate memory for content_type + allocated_pointers[2] = + allocate_wasm_memory(module_inst, msg.content_type, strlen(msg.content_type) + 1); + if (allocated_pointers[2] == 0) { + LOG_ERR("Failed to allocate memory for content_type in WASM"); + free_wasm_message(module_inst, allocated_pointers, 2); + continue; + } + + // Allocate memory for payload + allocated_pointers[3] = allocate_wasm_memory(module_inst, msg.payload, msg.payload_len); + if (allocated_pointers[3] == 0) { + LOG_ERR("Failed to allocate memory for payload in WASM"); + free_wasm_message(module_inst, allocated_pointers, 3); + continue; + } + + // Populate the WASM message structure + ocre_msg_t *wasm_msg = + (ocre_msg_t *)wasm_runtime_addr_app_to_native(module_inst, allocated_pointers[0]); + wasm_msg->mid = msg.mid; + wasm_msg->topic = (char *)allocated_pointers[1]; + wasm_msg->content_type = (char *)allocated_pointers[2]; + wasm_msg->payload = (void *)allocated_pointers[3]; + wasm_msg->payload_len = msg.payload_len; + + // Call the WASM function + uint32_t args[1] = {allocated_pointers[0]}; + if (!wasm_runtime_call_wasm(subscriptions[i].exec_env, subscriptions[i].handler, 1, args)) { + const char *error = wasm_runtime_get_exception(module_inst); + LOG_ERR("Failed to call WASM function: %s", error ? error : "Unknown error"); + } else { + LOG_INF("Function executed successfully"); + } + + // Free allocated WASM memory + free_wasm_message(module_inst, allocated_pointers, 4); + } + } + } + } +} + +// Define the stack area for the subscriber thread +K_THREAD_STACK_DEFINE(subscriber_stack_area, STACK_SIZE); +struct k_thread subscriber_thread_data; + +// Initialize the message system +void ocre_msg_system_init() { + if (msg_system_is_init) { + LOG_WRN("Messaging System is already initialized"); + return; + } + + k_tid_t tid = k_thread_create(&subscriber_thread_data, subscriber_stack_area, + K_THREAD_STACK_SIZEOF(subscriber_stack_area), subscriber_thread, NULL, NULL, NULL, + PRIORITY, 0, K_NO_WAIT); + + if (!tid) { + LOG_ERR("Failed to create container messaging thread"); + return; + } + k_thread_name_set(tid, "container_messaging"); + msg_system_is_init = true; +} + +int ocre_publish_message(wasm_exec_env_t exec_env, char *topic, char *content_type, void *payload, int payload_len) { + LOG_INF("---------------_ocre_publish_message: topic: %s [%zu], content_type: %s, payload_len: %u ", topic, + strlen(topic), content_type, payload_len); + + if (!msg_system_is_init) { + LOG_ERR("Messaging system not initialized"); + return -1; + } + + if (!topic || (topic[0] == '\0')) { + LOG_ERR("topic is NULL or empty, please check function parameters!"); + return -1; + } + + if (!content_type || (content_type[0] == '\0')) { + LOG_ERR("content_type is NULL or empty, please check function parameters!"); + return -1; + } + + if (!payload || payload_len == 0) { + LOG_ERR("payload is NULL or payload_len is 0, please check function parameters!"); + return -1; + } + + static uint64_t message_id = 0; + ocre_msg_t msg = {.mid = message_id, + .topic = topic, + .content_type = content_type, + .payload = payload, + .payload_len = payload_len}; + + message_id++; + + if (k_msgq_put(&ocre_msg_queue, &msg, K_NO_WAIT) != 0) { + // Handle message queue full scenario + LOG_ERR("Message queue is full, could not publish message\n"); + return -1; + } + + return 0; +} + +int ocre_subscribe_message(wasm_exec_env_t exec_env, char *topic, char *handler_name) { + LOG_INF("---------------_ocre_subscribe_message---------------"); + + if (!msg_system_is_init) { + LOG_ERR("Messaging system not initialized"); + return -1; + } + + if (subscription_count < CONFIG_MESSAGING_MAX_SUBSCRIPTIONS) { + + if (!topic || (topic[0] == '\0')) { + LOG_ERR("topic is NULL or empty, please check function parameters!"); + return -1; + } + + if (!handler_name || (handler_name[0] == '\0')) { + LOG_ERR("handler_name is NULL or empty, please check function parameters!"); + return -1; + } + + wasm_module_inst_t current_module_inst = wasm_runtime_get_module_inst(exec_env); + wasm_function_inst_t handler_function = wasm_runtime_lookup_function(current_module_inst, handler_name); + if (!handler_function) { + LOG_ERR("Failed to find %s in WASM module", handler_name); + return -1; + } + + subscriptions[subscription_count].topic = topic; + subscriptions[subscription_count].handler = handler_function; + subscriptions[subscription_count].exec_env = exec_env; + subscriptions[subscription_count].module_inst = current_module_inst; + + LOG_INF("WASM messaging callback function set successfully"); + + subscription_count++; + } else { + LOG_ERR("Maximum subscriptions reached, could not subscribe to topic"); + return -1; + } + + return 0; +} diff --git a/src/ocre/container_messaging/messaging.h b/src/ocre/container_messaging/messaging.h new file mode 100644 index 00000000..b3bfe66d --- /dev/null +++ b/src/ocre/container_messaging/messaging.h @@ -0,0 +1,49 @@ + +#include "wasm_export.h" + +/** + * @typedef ocre_msg + * + * Structure of ocre messages + * + */ +typedef struct ocre_msg { + // message id - increments on each message + uint64_t mid; + + // url of the request + char *topic; + + // payload format (MIME type??) + char *content_type; + + // payload of the request, currently only support attr_container_t type + void *payload; + + // length in bytes of the payload + int payload_len; +} ocre_msg_t; + +/** + * @brief Initialize OCRE Messaging System. + * + */ +void ocre_msg_system_init(); + +/** + * @brief Publish a message to the specified target. + * + * @param topic the name of the topic on which to publish the message + * @param content_type the content type of the message; it is recommended to use a MIME type + * @param payload a buffer containing the message contents + * @param payload_len the length of the payload buffer + */ +int ocre_publish_message(wasm_exec_env_t exec_env, char *topic, char *content_type, void *payload, int payload_len); + +/** + * @brief Subscribe to messages on the specified topic. + * + * @param topic the name of the topic on which to subscribe + * @param handler_name name of callback function that will be called when a message is received on this topic + */ +int ocre_subscribe_message(wasm_exec_env_t exec_env, char *topic, char *handler_name); diff --git a/src/ocre/ocre.h b/src/ocre/ocre.h index af470906..b48af1bf 100644 --- a/src/ocre/ocre.h +++ b/src/ocre/ocre.h @@ -10,12 +10,16 @@ #include -#define CONFIG_OCRE_LOG_DEBUG 1 - #if CONFIG_OCRE_LOG_DEBUG #define OCRE_LOG_LEVEL LOG_LEVEL_DBG -#else +#elif CONFIG_OCRE_LOG_ERR +#define OCRE_LOG_LEVEL LOG_LEVEL_ERR +#elif CONFIG_OCRE_LOG_WARN #define OCRE_LOG_LEVEL LOG_LEVEL_WRN +#elif CONFIG_OCRE_LOG_INF +#define OCRE_LOG_LEVEL LOG_LEVEL_INF +#else +#define OCRE_LOG_LEVEL LOG_LEVEL_NONE #endif #endif \ No newline at end of file diff --git a/src/ocre/ocre_container_runtime/ocre_container_runtime.c b/src/ocre/ocre_container_runtime/ocre_container_runtime.c index 349e21ed..9cb0d687 100644 --- a/src/ocre/ocre_container_runtime/ocre_container_runtime.c +++ b/src/ocre/ocre_container_runtime/ocre_container_runtime.c @@ -64,6 +64,7 @@ ocre_container_status_t ocre_container_runtime_create_container(ocre_cs_ctx *ctx break; } } + if (validity_flag == false) { LOG_ERR("No available slots, unable to create container"); return CONTAINER_STATUS_ERROR; diff --git a/src/ocre/ocre_gpio/ocre_gpio.c b/src/ocre/ocre_gpio/ocre_gpio.c new file mode 100644 index 00000000..d37b3436 --- /dev/null +++ b/src/ocre/ocre_gpio/ocre_gpio.c @@ -0,0 +1,598 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include "ocre_gpio.h" +#include "wasm_export.h" +#include +#include +#include + +LOG_MODULE_DECLARE(ocre_cs_component, OCRE_LOG_LEVEL); + +#define GPIO_STACK_SIZE 2048 +#define GPIO_THREAD_PRIORITY 5 +#define WASM_STACK_SIZE (8 * 1024) + +typedef struct { + bool in_use; + uint32_t id; + const struct device *port; + gpio_pin_t pin_number; + ocre_gpio_direction_t direction; + struct gpio_callback gpio_cb_data; + ocre_gpio_callback_t user_callback; +} gpio_pin_ocre; + +static K_THREAD_STACK_DEFINE(gpio_thread_stack, GPIO_STACK_SIZE); +static struct k_thread gpio_thread; +static k_tid_t gpio_thread_id; + +K_MSGQ_DEFINE(gpio_msgq, sizeof(uint32_t) * 2, 10, 4); + +// GPIO pin management +static gpio_pin_ocre gpio_pins[CONFIG_OCRE_GPIO_MAX_PINS] = {0}; +static wasm_function_inst_t gpio_dispatcher_func = NULL; +static bool gpio_system_initialized = false; +static wasm_module_inst_t current_module_inst = NULL; +static wasm_exec_env_t shared_exec_env = NULL; + +// Define GPIO port devices structure - will be populated at runtime +static const struct device *gpio_ports[CONFIG_OCRE_GPIO_MAX_PORTS] = {NULL}; + +// Board-specific GPIO port device definitions +#if defined(CONFIG_BOARD_B_U585I_IOT02A) +// STM32U5 board configuration +#define GPIO_PORT_A DT_NODELABEL(gpioa) +#define GPIO_PORT_B DT_NODELABEL(gpiob) +#define GPIO_PORT_C DT_NODELABEL(gpioc) +#define GPIO_PORT_D DT_NODELABEL(gpiod) +#define GPIO_PORT_E DT_NODELABEL(gpioe) +#define GPIO_PORT_F DT_NODELABEL(gpiof) +#define GPIO_PORT_G DT_NODELABEL(gpiog) +#define GPIO_PORT_H DT_NODELABEL(gpioh) +static const char *gpio_port_names[CONFIG_OCRE_GPIO_MAX_PORTS] = {"GPIOA", "GPIOB", "GPIOC", "GPIOD", + "GPIOE", "GPIOF", "GPIOG", "GPIOH"}; +#elif defined(CONFIG_BOARD_ESP32C3_DEVKITM) +// ESP32C3 board configuration +#define GPIO_PORT_0 DT_NODELABEL(gpio0) +static const char *gpio_port_names[CONFIG_OCRE_GPIO_MAX_PORTS] = {"GPIO0"}; +#else +// Generic fallback configuration +#if DT_NODE_EXISTS(DT_NODELABEL(gpio0)) +#define GPIO_PORT_0 DT_NODELABEL(gpio0) +#endif +#if DT_NODE_EXISTS(DT_NODELABEL(gpioa)) +#define GPIO_PORT_A DT_NODELABEL(gpioa) +#endif +#if DT_NODE_EXISTS(DT_NODELABEL(gpio)) +#define GPIO_PORT_G DT_NODELABEL(gpio) +#endif +static const char *gpio_port_names[CONFIG_OCRE_GPIO_MAX_PORTS] = {"GPIO"}; +#endif + +void ocre_gpio_cleanup_container(wasm_module_inst_t module_inst) { + if (!gpio_system_initialized || !module_inst) { + LOG_ERR("GPIO system not properly initialized"); + return; + } + + if (module_inst != current_module_inst) { + LOG_WRN("Cleanup requested for non-active module instance"); + return; + } + + int cleaned_count = 0; + for (int i = 0; i < CONFIG_OCRE_GPIO_MAX_PINS; i++) { + if (gpio_pins[i].in_use) { + if (gpio_pins[i].direction == OCRE_GPIO_DIR_INPUT && gpio_pins[i].user_callback) { + gpio_pin_interrupt_configure(gpio_pins[i].port, gpio_pins[i].pin_number, GPIO_INT_DISABLE); + gpio_remove_callback(gpio_pins[i].port, &gpio_pins[i].gpio_cb_data); + } + gpio_pins[i].in_use = false; + gpio_pins[i].id = 0; + cleaned_count++; + } + } + + LOG_INF("Cleaned up %d GPIO pins for container", cleaned_count); +} + +static void gpio_thread_fn(void *arg1, void *arg2, void *arg3) { + uint32_t msg_data[2]; // [0] = pin_id, [1] = state + + while (1) { + if (k_msgq_get(&gpio_msgq, &msg_data, K_FOREVER) == 0) { + if (!gpio_dispatcher_func || !current_module_inst || !shared_exec_env) { + LOG_ERR("GPIO system not properly initialized"); + continue; + } + + uint32_t pin_id = msg_data[0]; + uint32_t state = msg_data[1]; + + LOG_DBG("Processing GPIO pin event: %d, state: %d", pin_id, state); + uint32_t args[2] = {pin_id, state}; + + bool call_success = wasm_runtime_call_wasm(shared_exec_env, gpio_dispatcher_func, 2, args); + + if (!call_success) { + const char *error = wasm_runtime_get_exception(current_module_inst); + LOG_ERR("Failed to call WASM function: %s", error ? error : "Unknown error"); + } else { + LOG_INF("Successfully called WASM function for GPIO pin %d", pin_id); + } + } + } +} + +static void gpio_callback_handler(const struct device *port, struct gpio_callback *cb, gpio_port_pins_t pins) { + for (int i = 0; i < CONFIG_OCRE_GPIO_MAX_PINS; i++) { + if (gpio_pins[i].in_use && &gpio_pins[i].gpio_cb_data == cb && gpio_pins[i].port == port) { + if (pins & BIT(gpio_pins[i].pin_number)) { + int state = gpio_pin_get(port, gpio_pins[i].pin_number); + if (state >= 0) { + uint32_t msg_data[2] = {gpio_pins[i].id, (uint32_t)state}; + if (k_msgq_put(&gpio_msgq, &msg_data, K_NO_WAIT) != 0) { + LOG_ERR("Failed to queue GPIO callback for ID: %d", gpio_pins[i].id); + } + } + } + } + } +} + +void ocre_gpio_set_module_inst(wasm_module_inst_t module_inst) { + current_module_inst = module_inst; + if (shared_exec_env) { + wasm_runtime_destroy_exec_env(shared_exec_env); + } + shared_exec_env = wasm_runtime_create_exec_env(module_inst, WASM_STACK_SIZE); +} + +void ocre_gpio_set_dispatcher(wasm_exec_env_t exec_env) { + if (!current_module_inst) { + LOG_ERR("No active WASM module instance"); + return; + } + + wasm_function_inst_t func = wasm_runtime_lookup_function(current_module_inst, "gpio_callback"); + if (!func) { + LOG_ERR("Failed to find 'gpio_callback' in WASM module"); + return; + } + + gpio_dispatcher_func = func; + LOG_INF("WASM GPIO dispatcher function set successfully"); +} + +static gpio_pin_ocre *get_gpio_from_id(int id) { + if (id < 0 || id >= CONFIG_OCRE_GPIO_MAX_PINS) { + return NULL; + } + return &gpio_pins[id]; +} + +int ocre_gpio_init(void) { + if (gpio_system_initialized) { + LOG_INF("GPIO already initialized"); + return 0; + } + + // Initialize GPIO ports array based on board + int port_count = 0; + +#if defined(CONFIG_BOARD_B_U585I_IOT02A) + // STM32U5 board initialization + if (DT_NODE_EXISTS(GPIO_PORT_A)) { + gpio_ports[0] = DEVICE_DT_GET(GPIO_PORT_A); + if (!device_is_ready(gpio_ports[0])) { + LOG_ERR("GPIOA not ready"); + } else { + LOG_INF("GPIOA initialized"); + port_count++; + } + } + + if (DT_NODE_EXISTS(GPIO_PORT_B)) { + gpio_ports[1] = DEVICE_DT_GET(GPIO_PORT_B); + if (!device_is_ready(gpio_ports[1])) { + LOG_ERR("GPIOB not ready"); + } else { + LOG_INF("GPIOB initialized"); + port_count++; + } + } + + if (DT_NODE_EXISTS(GPIO_PORT_C)) { + gpio_ports[2] = DEVICE_DT_GET(GPIO_PORT_C); + if (!device_is_ready(gpio_ports[2])) { + LOG_ERR("GPIOC not ready"); + } else { + LOG_INF("GPIOC initialized"); + port_count++; + } + } + + if (DT_NODE_EXISTS(GPIO_PORT_D)) { + gpio_ports[3] = DEVICE_DT_GET(GPIO_PORT_D); + if (!device_is_ready(gpio_ports[3])) { + LOG_ERR("GPIOD not ready"); + } else { + LOG_INF("GPIOD initialized"); + port_count++; + } + } + + if (DT_NODE_EXISTS(GPIO_PORT_E)) { + gpio_ports[4] = DEVICE_DT_GET(GPIO_PORT_E); + if (!device_is_ready(gpio_ports[4])) { + LOG_ERR("GPIOE not ready"); + } else { + LOG_INF("GPIOE initialized"); + port_count++; + } + } + + if (DT_NODE_EXISTS(GPIO_PORT_F)) { + gpio_ports[5] = DEVICE_DT_GET(GPIO_PORT_F); + if (!device_is_ready(gpio_ports[5])) { + LOG_ERR("GPIOF not ready"); + } else { + LOG_INF("GPIOF initialized"); + port_count++; + } + } + + if (DT_NODE_EXISTS(GPIO_PORT_G)) { + gpio_ports[6] = DEVICE_DT_GET(GPIO_PORT_G); + if (!device_is_ready(gpio_ports[6])) { + LOG_ERR("GPIOG not ready"); + } else { + LOG_INF("GPIOG initialized"); + port_count++; + } + } + + if (DT_NODE_EXISTS(GPIO_PORT_H)) { + gpio_ports[7] = DEVICE_DT_GET(GPIO_PORT_H); + if (!device_is_ready(gpio_ports[7])) { + LOG_ERR("GPIOH not ready"); + } else { + LOG_INF("GPIOH initialized"); + port_count++; + } + } +#elif defined(CONFIG_BOARD_ESP32C3_DEVKITM) + // ESP32C3 board initialization + if (DT_NODE_EXISTS(GPIO_PORT_0)) { + gpio_ports[0] = DEVICE_DT_GET(GPIO_PORT_0); + if (!device_is_ready(gpio_ports[0])) { + LOG_ERR("GPIO0 not ready"); + } else { + LOG_INF("GPIO0 initialized"); + port_count++; + } + } +/* ADD BOARDS HERE */ +#else + // Generic fallback initialization + // #if defined(GPIO_PORT_0) + // #if DT_NODE_EXISTS(GPIO_PORT_0) + // gpio_ports[0] = DEVICE_DT_GET(GPIO_PORT_0); + // LOG_INF("No Board Choosen Generic fallback initialization %s\n", gpio_ports[0]); + // port_count = 1; + // #endif + // #endif + +#if defined(GPIO_PORT_A) +#if DT_NODE_EXISTS(GPIO_PORT_A) + gpio_ports[0] = DEVICE_DT_GET(GPIO_PORT_A); + LOG_INF("No Board Choosen Generic fallback initialization %s\n", gpio_ports[0]); + port_count = 1; +#endif +#endif + +#if defined(GPIO_PORT_G) +#if DT_NODE_EXISTS(GPIO_PORT_G) + gpio_ports[0] = DEVICE_DT_GET(GPIO_PORT_G); + LOG_INF("No Board Choosen Generic fallback initialization %s\n", gpio_ports[0]); + port_count = 1; +#endif +#endif +#endif + + if (port_count == 0) { + LOG_ERR("No GPIO ports were initialized\n"); + return -ENODEV; + } + + // Start GPIO thread for callbacks + gpio_thread_id = k_thread_create(&gpio_thread, gpio_thread_stack, K_THREAD_STACK_SIZEOF(gpio_thread_stack), + gpio_thread_fn, NULL, NULL, NULL, GPIO_THREAD_PRIORITY, 0, K_NO_WAIT); + if (!gpio_thread_id) { + LOG_ERR("Failed to create GPIO thread"); + return -ENOMEM; + } + + k_thread_name_set(gpio_thread_id, "gpio_thread"); + LOG_INF("GPIO system initialized with %d ports", port_count); + gpio_system_initialized = true; + return 0; +} + +int ocre_gpio_configure(const ocre_gpio_config_t *config) { + if (!gpio_system_initialized) { + LOG_ERR("GPIO not initialized"); + return -ENODEV; + } + + if (config == NULL) { + LOG_ERR("NULL config pointer"); + return -EINVAL; + } + + if (config->pin < 0 || config->pin >= CONFIG_OCRE_GPIO_MAX_PINS) { + LOG_ERR("Invalid GPIO pin number: %d", config->pin); + return -EINVAL; + } + + // Map logical pin to physical port/pin + int port_idx = config->pin / CONFIG_OCRE_GPIO_PINS_PER_PORT; + gpio_pin_t pin_number = config->pin % CONFIG_OCRE_GPIO_PINS_PER_PORT; + + if (port_idx >= CONFIG_OCRE_GPIO_MAX_PORTS || gpio_ports[port_idx] == NULL) { + LOG_ERR("Invalid GPIO port index: %d", port_idx); + return -EINVAL; + } + + // Configure Zephyr GPIO + gpio_flags_t flags = 0; + + if (config->direction == OCRE_GPIO_DIR_INPUT) { + flags = GPIO_INPUT; + } else if (config->direction == OCRE_GPIO_DIR_OUTPUT) { + flags = GPIO_OUTPUT; + } else { + LOG_ERR("Invalid GPIO direction"); + return -EINVAL; + } + + int ret = gpio_pin_configure(gpio_ports[port_idx], pin_number, flags); + if (ret != 0) { + LOG_ERR("Failed to configure GPIO pin: %d", ret); + return ret; + } + + // Update our tracking information + gpio_pins[config->pin].in_use = true; + gpio_pins[config->pin].id = config->pin; + gpio_pins[config->pin].port = gpio_ports[port_idx]; + gpio_pins[config->pin].pin_number = pin_number; + gpio_pins[config->pin].direction = config->direction; + gpio_pins[config->pin].user_callback = NULL; + + LOG_DBG("Configured GPIO pin %d: port=%s, pin=%d, direction=%d", config->pin, gpio_port_names[port_idx], pin_number, + config->direction); + + return 0; +} + +int ocre_gpio_pin_set(int pin, ocre_gpio_pin_state_t state) { + gpio_pin_ocre *gpio = get_gpio_from_id(pin); + + if (!gpio_system_initialized) { + LOG_ERR("GPIO system not initialized when trying to set pin %d", pin); + return -ENODEV; + } + + if (!gpio || !gpio->in_use) { + LOG_ERR("Invalid or unconfigured GPIO pin: %d", pin); + return -EINVAL; + } + + if (gpio->direction != OCRE_GPIO_DIR_OUTPUT) { + LOG_ERR("Cannot set state of input pin: %d", pin); + return -EINVAL; + } + + LOG_INF("Setting GPIO pin %d (port=%p, pin=%d) to state %d", pin, gpio->port, gpio->pin_number, state); + + int ret = gpio_pin_set(gpio->port, gpio->pin_number, state); + if (ret != 0) { + LOG_ERR("Failed to set GPIO pin %d: %d", pin, ret); + return ret; + } + + LOG_INF("Successfully set GPIO pin %d to state %d", pin, state); + return 0; +} + +ocre_gpio_pin_state_t ocre_gpio_pin_get(int pin) { + gpio_pin_ocre *gpio = get_gpio_from_id(pin); + + if (!gpio_system_initialized) { + return -ENODEV; + } + + if (!gpio || !gpio->in_use) { + LOG_ERR("Invalid or unconfigured GPIO pin: %d", pin); + return -EINVAL; + } + + int value = gpio_pin_get(gpio->port, gpio->pin_number); + if (value < 0) { + LOG_ERR("Failed to get GPIO pin %d state: %d", pin, value); + return value; + } + + return value ? OCRE_GPIO_PIN_SET : OCRE_GPIO_PIN_RESET; +} + +int ocre_gpio_pin_toggle(int pin) { + gpio_pin_ocre *gpio = get_gpio_from_id(pin); + + if (!gpio_system_initialized) { + return -ENODEV; + } + + if (!gpio || !gpio->in_use) { + LOG_ERR("Invalid or unconfigured GPIO pin: %d", pin); + return -EINVAL; + } + + if (gpio->direction != OCRE_GPIO_DIR_OUTPUT) { + LOG_ERR("Cannot toggle state of input pin: %d", pin); + return -EINVAL; + } + + int ret = gpio_pin_toggle(gpio->port, gpio->pin_number); + if (ret != 0) { + LOG_ERR("Failed to toggle GPIO pin %d: %d", pin, ret); + return ret; + } + + return 0; +} + +int ocre_gpio_register_callback(int pin, ocre_gpio_callback_t callback) { + gpio_pin_ocre *gpio = get_gpio_from_id(pin); + + if (!gpio_system_initialized) { + return -ENODEV; + } + + if (!gpio || !gpio->in_use) { + LOG_ERR("Invalid or unconfigured GPIO pin: %d", pin); + return -EINVAL; + } + + if (gpio->direction != OCRE_GPIO_DIR_INPUT) { + LOG_ERR("Cannot register callback on output pin: %d", pin); + return -EINVAL; + } + + // Configure the interrupt + int ret = gpio_pin_interrupt_configure(gpio->port, gpio->pin_number, GPIO_INT_EDGE_BOTH); + if (ret != 0) { + LOG_ERR("Failed to configure GPIO interrupt: %d", ret); + return ret; + } + + // Initialize the callback structure + gpio_init_callback(&gpio->gpio_cb_data, gpio_callback_handler, BIT(gpio->pin_number)); + ret = gpio_add_callback(gpio->port, &gpio->gpio_cb_data); + if (ret != 0) { + LOG_ERR("Failed to add GPIO callback: %d", ret); + return ret; + } + + // Store the user callback + gpio->user_callback = callback; + LOG_DBG("Registered callback for GPIO pin %d", pin); + + return 0; +} + +int ocre_gpio_unregister_callback(int pin) { + gpio_pin_ocre *gpio = get_gpio_from_id(pin); + + if (!gpio_system_initialized) { + return -ENODEV; + } + + if (!gpio || !gpio->in_use) { + LOG_ERR("Invalid or unconfigured GPIO pin: %d", pin); + return -EINVAL; + } + + if (!gpio->user_callback) { + LOG_WRN("No callback registered for pin %d", pin); + return 0; + } + + // Disable interrupt and remove callback + int ret = gpio_pin_interrupt_configure(gpio->port, gpio->pin_number, GPIO_INT_DISABLE); + if (ret != 0) { + LOG_ERR("Failed to disable GPIO interrupt: %d", ret); + return ret; + } + + ret = gpio_remove_callback(gpio->port, &gpio->gpio_cb_data); + if (ret != 0) { + LOG_ERR("Failed to remove GPIO callback: %d", ret); + return ret; + } + + gpio->user_callback = NULL; + LOG_DBG("Unregistered callback for GPIO pin %d", pin); + + return 0; +} + +int get_pin_id(int port, int pin) { + // Ensure the pin is valid within the specified port + if (pin < 0 || pin >= CONFIG_OCRE_GPIO_PINS_PER_PORT) { + printf("Invalid pin number\n"); + return -1; // Return -1 for invalid pin number + } + + // Calculate the pin ID + int pin_id = port * CONFIG_OCRE_GPIO_PINS_PER_PORT + pin; + return pin_id; +} + +// WASM-exposed functions for GPIO control +int ocre_gpio_wasm_init(wasm_exec_env_t exec_env) { + return ocre_gpio_init(); +} + +int ocre_gpio_wasm_configure(wasm_exec_env_t exec_env, int port, int P_pin, int direction) { + if (!gpio_system_initialized) { + LOG_ERR("GPIO system not initialized"); + return -ENODEV; + } + int pin = get_pin_id(port, P_pin); + ocre_gpio_config_t config; + config.pin = pin; + config.direction = direction; + + return ocre_gpio_configure(&config); +} + +int ocre_gpio_wasm_set(wasm_exec_env_t exec_env, int port, int P_pin, int state) { + int pin = get_pin_id(port, P_pin); + return ocre_gpio_pin_set(pin, state ? OCRE_GPIO_PIN_SET : OCRE_GPIO_PIN_RESET); +} + +int ocre_gpio_wasm_get(wasm_exec_env_t exec_env, int port, int P_pin) { + int pin = get_pin_id(port, P_pin); + return ocre_gpio_pin_get(pin); +} + +int ocre_gpio_wasm_toggle(wasm_exec_env_t exec_env, int port, int P_pin) { + int pin = get_pin_id(port, P_pin); + return ocre_gpio_pin_toggle(pin); +} + +int ocre_gpio_wasm_register_callback(wasm_exec_env_t exec_env, int port, int P_pin) { + int pin = get_pin_id(port, P_pin); + ocre_gpio_set_dispatcher(exec_env); + + // For WASM, we don't need an actual callback function pointer + // as we'll use the dispatcher to call the appropriate WASM function + return ocre_gpio_register_callback(pin, NULL); +} + +int ocre_gpio_wasm_unregister_callback(wasm_exec_env_t exec_env, int port, int P_pin) { + int pin = get_pin_id(port, P_pin); + return ocre_gpio_unregister_callback(pin); +} diff --git a/src/ocre/ocre_gpio/ocre_gpio.h b/src/ocre/ocre_gpio/ocre_gpio.h new file mode 100644 index 00000000..39788616 --- /dev/null +++ b/src/ocre/ocre_gpio/ocre_gpio.h @@ -0,0 +1,147 @@ +/** + * @copyright Copyright © contributors to Project Ocre, + * which has been established as Project Ocre a Series of LF Projects, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef OCRE_GPIO_H +#define OCRE_GPIO_H + +#include +#include +#include "wasm_export.h" + +#define CONFIG_BOARD_B_U585I_IOT02A +// #define CONFIG_BOARD_ESP32C3_DEVKITM + +// Default configuration values if not provided by Kconfig +#ifndef CONFIG_OCRE_GPIO_MAX_PINS +#define CONFIG_OCRE_GPIO_MAX_PINS 32 +#endif + +#ifndef CONFIG_OCRE_GPIO_MAX_PORTS +#define CONFIG_OCRE_GPIO_MAX_PORTS 8 +#endif + +#ifndef CONFIG_OCRE_GPIO_PINS_PER_PORT +#define CONFIG_OCRE_GPIO_PINS_PER_PORT 16 +#endif + +/** + * GPIO pin state + */ +typedef enum { + OCRE_GPIO_PIN_RESET = 0, + OCRE_GPIO_PIN_SET = 1 +} ocre_gpio_pin_state_t; + +/** + * GPIO pin direction + */ +typedef enum { + OCRE_GPIO_DIR_INPUT = 0, + OCRE_GPIO_DIR_OUTPUT = 1 +} ocre_gpio_direction_t; + +/** + * GPIO configuration structure + */ +typedef struct { + int pin; /**< GPIO pin number (logical) */ + ocre_gpio_direction_t direction; /**< Pin direction */ +} ocre_gpio_config_t; + +/** + * GPIO callback function type + */ +typedef void (*ocre_gpio_callback_t)(int pin, ocre_gpio_pin_state_t state); + +/** + * Initialize GPIO subsystem. + * + * @return 0 on success, negative error code on failure + */ +int ocre_gpio_init(void); + +/** + * Configure a GPIO pin. + * + * @param config GPIO pin configuration + * @return 0 on success, negative error code on failure + */ +int ocre_gpio_configure(const ocre_gpio_config_t *config); + +/** + * Set GPIO pin state. + * + * @param pin GPIO pin identifier + * @param state Desired pin state + * @return 0 on success, negative error code on failure + */ +int ocre_gpio_pin_set(int pin, ocre_gpio_pin_state_t state); + +/** + * Get GPIO pin state. + * + * @param pin GPIO pin identifier + * @return Pin state or negative error code + */ +ocre_gpio_pin_state_t ocre_gpio_pin_get(int pin); + +/** + * Toggle GPIO pin state. + * + * @param pin GPIO pin identifier + * @return 0 on success, negative error code on failure + */ +int ocre_gpio_pin_toggle(int pin); + +/** + * Register callback for GPIO pin state changes. + * + * @param pin GPIO pin identifier + * @param callback Callback function + * @return 0 on success, negative error code on failure + */ +int ocre_gpio_register_callback(int pin, ocre_gpio_callback_t callback); + +/** + * Unregister GPIO pin callback. + * + * @param pin GPIO pin identifier + * @return 0 on success, negative error code on failure + */ +int ocre_gpio_unregister_callback(int pin); + +/** + * Clean up GPIO resources for a WASM container. + * + * @param module_inst WASM module instance + */ +void ocre_gpio_cleanup_container(wasm_module_inst_t module_inst); + +/** + * Set the active WASM module instance for GPIO operations. + * + * @param module_inst WASM module instance + */ +void ocre_gpio_set_module_inst(wasm_module_inst_t module_inst); + +/** + * Set the WASM function that will handle GPIO callbacks. + * + * @param exec_env WASM execution environment + */ +void ocre_gpio_set_dispatcher(wasm_exec_env_t exec_env); + +// WASM-exposed GPIO functions +int ocre_gpio_wasm_init(wasm_exec_env_t exec_env); +int ocre_gpio_wasm_configure(wasm_exec_env_t exec_env, int port, int P_pin, int direction); +int ocre_gpio_wasm_set(wasm_exec_env_t exec_env, int port, int P_pin, int state); +int ocre_gpio_wasm_get(wasm_exec_env_t exec_env, int port, int P_pin); +int ocre_gpio_wasm_toggle(wasm_exec_env_t exec_env, int port, int P_pin); +int ocre_gpio_wasm_register_callback(wasm_exec_env_t exec_env, int port, int P_pin); +int ocre_gpio_wasm_unregister_callback(wasm_exec_env_t exec_env, int port, int P_pin); + +#endif /* OCRE_GPIO_H */ \ No newline at end of file diff --git a/src/ocre/ocre_sensors/ocre_sensors.c b/src/ocre/ocre_sensors/ocre_sensors.c index 7a463e26..00bc39de 100644 --- a/src/ocre/ocre_sensors/ocre_sensors.c +++ b/src/ocre/ocre_sensors/ocre_sensors.c @@ -13,9 +13,24 @@ LOG_MODULE_DECLARE(ocre_sensors, OCRE_LOG_LEVEL); #include "ocre_sensors.h" -#define DEVICE_NODE DT_PATH(devices) -#define DEVICE_NODES_LIST(node_id) \ - DT_NODE_HAS_PROP(node_id, label) ? DT_PROP_OR(node_id, label, "undefined") : "undefined" +/* + * Build a list of device nodes from the devicetree if: + * 1. OCRE_SENSORS is enabled + * 2. The 'devices' node exists in the devicetree + * 3. The 'devices' node has at least one child + * 4. The 'devices' node has property label + */ +#define DEVICE_NODE DT_PATH(devices) +#define HAS_DEVICE_NODES DT_NODE_EXISTS(DEVICE_NODE) && DT_CHILD_NUM(DEVICE_NODE) > 0 + +#if (CONFIG_OCRE_SENSORS) && (HAS_DEVICE_NODES) +#define DEVICE_NODES_LIST(node_id) COND_CODE_1(DT_NODE_HAS_PROP(node_id, label), (DT_PROP(node_id, label), ), ()) +static const char *device_nodes[] = {DT_FOREACH_CHILD(DEVICE_NODE, DEVICE_NODES_LIST)}; +#define DEVICE_NODES_COUNT (sizeof(device_nodes) / sizeof(device_nodes[0])) +#else +static const char *device_nodes[] = {}; +#define DEVICE_NODES_COUNT 0 +#endif static const char *device_nodes[] = {DT_FOREACH_CHILD(DEVICE_NODE, DEVICE_NODES_LIST)}; typedef struct { @@ -34,7 +49,7 @@ static bool is_custom_device(const struct device *dev) { return false; } - for (int i = 0; i < ARRAY_SIZE(device_nodes); i++) { + for (int i = 0; i < DEVICE_NODES_COUNT; i++) { if (strcmp(dev->name, device_nodes[i]) == 0) { return true; } @@ -91,11 +106,16 @@ int ocre_sensors_discover(wasm_exec_env_t exec_env) { } if (device_count == 0) { - LOG_ERR("No devices found"); + LOG_ERR("No static devices found"); return -1; } - LOG_INF("Total devices found: %zu", device_count); + LOG_INF("Total static devices found: %zu", device_count); + + if (!device_nodes[0]) { + LOG_ERR("No devices found in DeviceTree!"); + return -1; + } for (size_t i = 0; i < device_count && sensor_count < CONFIG_MAX_SENSORS; i++) { if (!dev[i].name) { @@ -123,7 +143,6 @@ int ocre_sensors_discover(wasm_exec_env_t exec_env) { ocre_sensor_internal_t *sensor = &sensors[sensor_count]; sensor->device = &dev[i]; sensor->in_use = true; - sensor->info.handle = sensor_count; strncpy(sensor_names[sensor_count], dev[i].name, CONFIG_MAX_SENSOR_NAME_LENGTH - 1); diff --git a/src/ocre/ocre_sensors/rng_sensor.c b/src/ocre/ocre_sensors/rng_sensor.c index 0297bf29..c172b59e 100644 --- a/src/ocre/ocre_sensors/rng_sensor.c +++ b/src/ocre/ocre_sensors/rng_sensor.c @@ -63,8 +63,10 @@ int rng_sensor_init(const struct device *dev) { return 0; } -/* Define the sensor device */ +// /* Define the sensor device */ +#if DT_NODE_EXISTS(DT_DRV_INST(0)) static struct rng_sensor_data rng_sensor_data; DEVICE_DT_DEFINE(DT_DRV_INST(0), rng_sensor_init, NULL, &rng_sensor_data, NULL, POST_KERNEL, - CONFIG_SENSOR_INIT_PRIORITY, &rng_sensor_api); \ No newline at end of file + CONFIG_SENSOR_INIT_PRIORITY, &rng_sensor_api); +#endif From df948bf3011a4b455a559aafa6df65917826bc97 Mon Sep 17 00:00:00 2001 From: Stephen Berard Date: Wed, 19 Mar 2025 20:16:23 +0100 Subject: [PATCH 2/6] Fixed GPIO configuration parameter Signed-off-by: Stephen Berard --- boards/b_u585i_iot02a.conf | 1 + prj.conf | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/boards/b_u585i_iot02a.conf b/boards/b_u585i_iot02a.conf index 2dbfe829..7f3fafa4 100644 --- a/boards/b_u585i_iot02a.conf +++ b/boards/b_u585i_iot02a.conf @@ -11,3 +11,4 @@ CONFIG_NET_L2_ETHERNET=y CONFIG_I2C=y CONFIG_SENSOR=y CONFIG_CBPRINTF_FP_SUPPORT=y +CONFIG_GPIO=y \ No newline at end of file diff --git a/prj.conf b/prj.conf index 822707c5..5feb4cfa 100644 --- a/prj.conf +++ b/prj.conf @@ -85,5 +85,5 @@ CONFIG_MAX_SENSORS=10 CONFIG_MAX_CHANNELS_PER_SENSOR=5 CONFIG_OCRE_MEMORY_CHECK_ENABLED=n -CONFIG_GPIO=y +CONFIG_GPIO=n From 86dd8a3cde4ecf5daf1da7c21afcd5dd0a447526 Mon Sep 17 00:00:00 2001 From: Stephen Berard Date: Wed, 19 Mar 2025 20:28:05 +0100 Subject: [PATCH 3/6] Fixed config issue Signed-off-by: Stephen Berard --- boards/b_u585i_iot02a.conf | 3 +-- prj.conf | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/boards/b_u585i_iot02a.conf b/boards/b_u585i_iot02a.conf index 7f3fafa4..58b0fae9 100644 --- a/boards/b_u585i_iot02a.conf +++ b/boards/b_u585i_iot02a.conf @@ -10,5 +10,4 @@ CONFIG_NET_L2_ETHERNET=y # hts221 sensor config CONFIG_I2C=y CONFIG_SENSOR=y -CONFIG_CBPRINTF_FP_SUPPORT=y -CONFIG_GPIO=y \ No newline at end of file +CONFIG_CBPRINTF_FP_SUPPORT=y \ No newline at end of file diff --git a/prj.conf b/prj.conf index 5feb4cfa..ce657741 100644 --- a/prj.conf +++ b/prj.conf @@ -85,5 +85,5 @@ CONFIG_MAX_SENSORS=10 CONFIG_MAX_CHANNELS_PER_SENSOR=5 CONFIG_OCRE_MEMORY_CHECK_ENABLED=n -CONFIG_GPIO=n +CONFIG_OCRE_GPIO=n From 3d2255667fea47c7af53eefd7ea07229fabee408 Mon Sep 17 00:00:00 2001 From: Stephen Berard Date: Wed, 19 Mar 2025 20:36:28 +0100 Subject: [PATCH 4/6] Fix Cmake issue with regard to GPIO Signed-off-by: Stephen Berard --- src/ocre/api/ocre_api.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ocre/api/ocre_api.c b/src/ocre/api/ocre_api.c index 3dde01e7..2794ef36 100644 --- a/src/ocre/api/ocre_api.c +++ b/src/ocre/api/ocre_api.c @@ -110,6 +110,7 @@ NativeSymbol ocre_api_table[] = { {"ocre_timer_get_remaining", ocre_timer_get_remaining, "(i)i", NULL}, {"ocre_timer_set_dispatcher", ocre_timer_set_dispatcher, "(i)v", NULL}, +#ifdef CONFIG_OCRE_GPIO // GPIO API {"ocre_gpio_init", ocre_gpio_wasm_init, "()i", NULL}, {"ocre_gpio_configure", ocre_gpio_wasm_configure, "(iii)i", NULL}, @@ -118,6 +119,7 @@ NativeSymbol ocre_api_table[] = { {"ocre_gpio_toggle", ocre_gpio_wasm_toggle, "(ii)i", NULL}, {"ocre_gpio_register_callback", ocre_gpio_wasm_register_callback, "(ii)i", NULL}, {"ocre_gpio_unregister_callback", ocre_gpio_wasm_unregister_callback, "(ii)i", NULL}, +#endif }; int ocre_api_table_size = sizeof(ocre_api_table) / sizeof(NativeSymbol); \ No newline at end of file From 65a2b45f1da4ff36e2e39788e51b659900b90af3 Mon Sep 17 00:00:00 2001 From: Stephen Berard Date: Wed, 19 Mar 2025 20:50:07 +0100 Subject: [PATCH 5/6] Fixed conditional compile issue with GPIO Signed-off-by: Stephen Berard --- src/ocre/components/container_supervisor/cs_sm_impl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ocre/components/container_supervisor/cs_sm_impl.c b/src/ocre/components/container_supervisor/cs_sm_impl.c index 1f12302d..b61aaaa5 100644 --- a/src/ocre/components/container_supervisor/cs_sm_impl.c +++ b/src/ocre/components/container_supervisor/cs_sm_impl.c @@ -280,8 +280,9 @@ ocre_container_status_t CS_run_container(ocre_cs_ctx *ctx, int *container_id) { if (ctx->containers[*container_id].container_runtime_status == CONTAINER_STATUS_CREATED || ctx->containers[*container_id].container_runtime_status == CONTAINER_STATUS_STOPPED) { ocre_timer_set_module_inst(ctx->containers[*container_id].ocre_runtime_arguments.module_inst); +#ifdef #ifdef CONFIG_OCRE_GPIO ocre_gpio_set_module_inst(ctx->containers[*container_id].ocre_runtime_arguments.module_inst); - +#endif ctx->containers[*container_id].ocre_runtime_arguments.exec_env = wasm_runtime_create_exec_env(ctx->containers[*container_id].ocre_runtime_arguments.module_inst, ctx->containers[*container_id].ocre_runtime_arguments.stack_size); From 67284bae336e6591388e6463332c37dba14dc314 Mon Sep 17 00:00:00 2001 From: Stephen Berard Date: Wed, 19 Mar 2025 20:53:12 +0100 Subject: [PATCH 6/6] Fixed syntax issue Signed-off-by: Stephen Berard --- src/ocre/components/container_supervisor/cs_sm_impl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ocre/components/container_supervisor/cs_sm_impl.c b/src/ocre/components/container_supervisor/cs_sm_impl.c index b61aaaa5..eb2cdedd 100644 --- a/src/ocre/components/container_supervisor/cs_sm_impl.c +++ b/src/ocre/components/container_supervisor/cs_sm_impl.c @@ -280,7 +280,7 @@ ocre_container_status_t CS_run_container(ocre_cs_ctx *ctx, int *container_id) { if (ctx->containers[*container_id].container_runtime_status == CONTAINER_STATUS_CREATED || ctx->containers[*container_id].container_runtime_status == CONTAINER_STATUS_STOPPED) { ocre_timer_set_module_inst(ctx->containers[*container_id].ocre_runtime_arguments.module_inst); -#ifdef #ifdef CONFIG_OCRE_GPIO +#ifdef CONFIG_OCRE_GPIO ocre_gpio_set_module_inst(ctx->containers[*container_id].ocre_runtime_arguments.module_inst); #endif ctx->containers[*container_id].ocre_runtime_arguments.exec_env =