diff --git a/src/platforms/stm32/src/lib/CMakeLists.txt b/src/platforms/stm32/src/lib/CMakeLists.txt index f1846c070..26e068f7e 100644 --- a/src/platforms/stm32/src/lib/CMakeLists.txt +++ b/src/platforms/stm32/src/lib/CMakeLists.txt @@ -25,6 +25,7 @@ set(HEADER_FILES avm_devcfg.h avm_log.h gpio_driver.h + platform_defaultatoms.h stm_sys.h ../../../../libAtomVM/platform_nifs.h ../../../../libAtomVM/portnifloader.h @@ -33,6 +34,7 @@ set(HEADER_FILES set(SOURCE_FILES gpio_driver.c + platform_defaultatoms.c platform_nifs.c sys.c ../../../../libAtomVM/portnifloader.c diff --git a/src/platforms/stm32/src/lib/gpio_driver.c b/src/platforms/stm32/src/lib/gpio_driver.c index 27fcec4b3..053406075 100644 --- a/src/platforms/stm32/src/lib/gpio_driver.c +++ b/src/platforms/stm32/src/lib/gpio_driver.c @@ -44,6 +44,7 @@ #include "avm_log.h" #include "gpio_driver.h" +#include "platform_defaultatoms.h" #include "stm_sys.h" #define TAG "gpio_driver" @@ -54,26 +55,11 @@ #define GPIO_INVALID_MODE 0xE #define INVALID_GPIO_OSPEED 0xE -static const char *const high_atom = ATOM_STR("\x4", "high"); -static const char *const low_atom = ATOM_STR("\x3", "low"); -static const char *const invalid_bank_atom = ATOM_STR("\xC", "invalid_bank"); -static const char *const invalid_pin_atom = ATOM_STR("\xB", "invalid_pin"); -static const char *const invalid_mode_atom = ATOM_STR("\xC", "invalid_mode"); -static const char *const invalid_pull_atom = ATOM_STR("\xC", "invalid_pull"); -static const char *const invalid_rate_atom = ATOM_STR("\xC", "invalid_rate"); -static const char *const invalid_level_atom = ATOM_STR("\xD", "invalid_level"); -static const char *const invalid_irq_atom = ATOM_STR("\xB", "invalid_irq"); - // Port driver specific data structures and definitions #ifndef AVM_DISABLE_GPIO_PORT_DRIVER static NativeHandlerResult consume_gpio_mailbox(Context *ctx); -static const char *const gpio_atom = ATOM_STR("\x4", "gpio"); -static const char *const gpio_interrupt_atom = ATOM_STR("\xE", "gpio_interrupt"); -static const char *const invalid_trigger_atom = ATOM_STR("\xF", "invalid_trigger"); -static const char *const invalid_listener_atom = ATOM_STR("\x10", "invalid_listener"); - #define INVALID_EXTI_TRIGGER 0xEE struct GPIOListenerData @@ -222,60 +208,50 @@ void isr_handler(Context *ctx, uint32_t exti); void isr_error_handler(const char *isr_name); #endif /* NOT defined AVM_DISABLE_GPIO_PORT_DRIVER */ -static term create_pair(Context *ctx, term term1, term term2) -{ - term ret = term_alloc_tuple(2, &ctx->heap); - term_put_tuple_element(ret, 0, term1); - term_put_tuple_element(ret, 1, term2); - - return ret; -} - -static term error_tuple_maybe_gc(Context *ctx, term reason_atom) +static bool term_is_logic_level(GlobalContext *glb, term level) { - if (UNLIKELY(memory_ensure_free(ctx, TUPLE_SIZE(2)) != MEMORY_GC_OK)) { - return OUT_OF_MEMORY_ATOM; + if (level == globalcontext_existing_term_from_atom_string(glb, ATOM_STR("\x3", "low"))) { + return true; + } + if (level == globalcontext_existing_term_from_atom_string(glb, ATOM_STR("\x4", "high"))) { + return true; + } + if (term_is_integer(level)) { + switch (term_to_int32(level)) { + case GPIOPinLow: + return true; + case GPIOPinHigh: + return true; + default: + return false; + } } - return create_pair(ctx, ERROR_ATOM, reason_atom); -} - -static term error_tuple_str_maybe_gc(Context *ctx, AtomString reason_str) -{ - term reason = globalcontext_make_atom(ctx->global, reason_str); - return error_tuple_maybe_gc(ctx, reason); + return false; } -static inline term level_to_atom(Context *ctx, uint16_t level) +static inline term level_to_atom(uint16_t level) { term level_atom; - if (level != 0) { - level_atom = globalcontext_make_atom(ctx->global, high_atom); + if (level != GPIOPinLow) { + level_atom = HIGH_ATOM; } else { - level_atom = globalcontext_make_atom(ctx->global, low_atom); + level_atom = LOW_ATOM; } return level_atom; } -static term get_error_type(term error_tuple) -{ - if ((term_is_tuple(error_tuple)) && (term_get_tuple_element(error_tuple, 0) == ERROR_ATOM)) { - return term_get_tuple_element(error_tuple, 1); - } - return OK_ATOM; -} - // Common setup function used by nif and port driver static term setup_gpio_pin(Context *ctx, term gpio_pin_tuple, term mode_term) { if (UNLIKELY(!term_is_tuple(gpio_pin_tuple))) { AVM_LOGE(TAG, "Invalid GPIO Pin tuple, expect {Bank, Pin}."); - return error_tuple_maybe_gc(ctx, BADARG_ATOM); + return BADARG_ATOM; } term gpio_bank_atom = term_get_tuple_element(gpio_pin_tuple, 0); if (UNLIKELY(!term_is_atom(gpio_bank_atom))) { AVM_LOGE(TAG, "Bank parameter of pin tuple must be an atom! (a...h|k depending on board)"); - return error_tuple_str_maybe_gc(ctx, invalid_bank_atom); + return BADARG_ATOM; } uint32_t gpio_bank = ((uint32_t) interop_atom_term_select_int(gpio_bank_table, gpio_bank_atom, ctx->global)); @@ -283,7 +259,7 @@ static term setup_gpio_pin(Context *ctx, term gpio_pin_tuple, term mode_term) char *bank_string = interop_atom_to_string(ctx, gpio_bank_atom); AVM_LOGE(TAG, "Invalid GPIO Bank '%s' in pin tuple", bank_string); free(bank_string); - return error_tuple_str_maybe_gc(ctx, invalid_bank_atom); + return BADARG_ATOM; } term pin_term = term_get_tuple_element(gpio_pin_tuple, 1); @@ -291,18 +267,18 @@ static term setup_gpio_pin(Context *ctx, term gpio_pin_tuple, term mode_term) if (term_is_list(pin_term)) { if (UNLIKELY(!term_is_nonempty_list(pin_term))) { AVM_LOGE(TAG, "Pin list parameter contains no pin numbers!"); - return error_tuple_str_maybe_gc(ctx, invalid_pin_atom); + return BADARG_ATOM; } while (term_is_nonempty_list(pin_term)) { term gpio_pin_term = term_get_list_head(pin_term); if (UNLIKELY(!term_is_any_integer(gpio_pin_term))) { AVM_LOGE(TAG, "Pin numbers must be between 0 and 15!"); - return error_tuple_str_maybe_gc(ctx, invalid_pin_atom); + return BADARG_ATOM; } uint16_t gpio_pin_num = ((uint16_t) term_to_int32(gpio_pin_term)); if (UNLIKELY(gpio_pin_num > 15)) { AVM_LOGE(TAG, "Pin numbers must be between 0 and 15!"); - return error_tuple_str_maybe_gc(ctx, invalid_pin_atom); + return BADARG_ATOM; } gpio_pin_mask = 1U << gpio_pin_num | gpio_pin_mask; pin_term = term_get_list_tail(pin_term); @@ -311,17 +287,17 @@ static term setup_gpio_pin(Context *ctx, term gpio_pin_tuple, term mode_term) uint16_t gpio_pin_num = ((uint16_t) term_to_int32(pin_term)); if (UNLIKELY(gpio_pin_num > 15)) { AVM_LOGE(TAG, "Pin number must be between 0 and 15!"); - return error_tuple_str_maybe_gc(ctx, invalid_pin_atom); + return BADARG_ATOM; } else { gpio_pin_mask = 1U << gpio_pin_num | gpio_pin_mask; } } else if (term_is_atom(pin_term)) { if (pin_term != ALL_ATOM) { - return error_tuple_str_maybe_gc(ctx, invalid_pin_atom); + return BADARG_ATOM; } gpio_pin_mask = GPIO_ALL; } else { - return error_tuple_str_maybe_gc(ctx, invalid_pin_atom); + return BADARG_ATOM; } term mode_atom; @@ -336,14 +312,14 @@ static term setup_gpio_pin(Context *ctx, term gpio_pin_tuple, term mode_term) mode_atom = term_get_tuple_element(mode_term, 0); if (UNLIKELY(!term_is_atom(mode_atom))) { AVM_LOGE(TAG, "GPIO Mode must be an atom ('input', 'output', 'output_od')."); - return error_tuple_str_maybe_gc(ctx, invalid_mode_atom); + return BADARG_ATOM; } gpio_mode = ((uint8_t) interop_atom_term_select_int(pin_mode_table, mode_atom, ctx->global)); if (UNLIKELY(gpio_mode == GPIO_INVALID_MODE)) { char *mode_string = interop_atom_to_string(ctx, mode_atom); AVM_LOGE(TAG, "Invalid gpio mode: %s", mode_string); free(mode_string); - return error_tuple_str_maybe_gc(ctx, invalid_mode_atom); + return BADARG_ATOM; } if ((gpio_mode == GPIO_MODE_OUTPUT) || (gpio_mode == GPIO_MODE_OUTPUT_OD)) { if (gpio_mode == GPIO_MODE_OUTPUT_OD) { @@ -358,7 +334,7 @@ static term setup_gpio_pin(Context *ctx, term gpio_pin_tuple, term mode_term) pull_atom = term_get_tuple_element(mode_term, 1); if (UNLIKELY(!term_is_atom(pull_atom))) { AVM_LOGE(TAG, "GPIO pull direction must be one of the following atoms: up | down | floating"); - return error_tuple_str_maybe_gc(ctx, invalid_pull_atom); + return BADARG_ATOM; } pull_up_down = ((uint8_t) interop_atom_term_select_int(pull_mode_table, pull_atom, ctx->global)); @@ -366,7 +342,7 @@ static term setup_gpio_pin(Context *ctx, term gpio_pin_tuple, term mode_term) mhz_atom = term_get_tuple_element(mode_term, 2); if (UNLIKELY(!term_is_atom(mhz_atom))) { AVM_LOGE(TAG, "GPIO output speed must be one of the following atoms: mhz_2 | mhz_25 | mhz_50 | mhz_100"); - error_tuple_str_maybe_gc(ctx, invalid_rate_atom); + return BADARG_ATOM; } output_speed = (uint8_t) interop_atom_term_select_int(output_mhz_table, mhz_atom, ctx->global); @@ -384,14 +360,14 @@ static term setup_gpio_pin(Context *ctx, term gpio_pin_tuple, term mode_term) mode_atom = mode_term; if (UNLIKELY(!term_is_atom(mode_atom))) { AVM_LOGE(TAG, "GPIO Mode must be an atom ('input', 'output', 'output_od')."); - return error_tuple_str_maybe_gc(ctx, invalid_mode_atom); + return BADARG_ATOM; } gpio_mode = ((uint8_t) interop_atom_term_select_int(pin_mode_table, mode_atom, ctx->global)); if (UNLIKELY(gpio_mode == GPIO_INVALID_MODE)) { char *mode_string = interop_atom_to_string(ctx, mode_atom); AVM_LOGE(TAG, "Invalid gpio mode: %s", mode_string); free(mode_string); - return error_tuple_str_maybe_gc(ctx, invalid_mode_atom); + return BADARG_ATOM; } pull_up_down = GPIO_PUPD_NONE; if ((gpio_mode == GPIO_MODE_OUTPUT) || (gpio_mode == GPIO_MODE_OUTPUT_OD)) { @@ -421,12 +397,12 @@ static term gpio_digital_write(Context *ctx, term gpio_pin_tuple, term level_ter { if (UNLIKELY(!term_is_tuple(gpio_pin_tuple))) { AVM_LOGE(TAG, "Invalid GPIO Pin tuple, expect {Bank, Pin}."); - return error_tuple_maybe_gc(ctx, BADARG_ATOM); + return BADARG_ATOM; } term gpio_bank_atom = term_get_tuple_element(gpio_pin_tuple, 0); if (UNLIKELY(!term_is_atom(gpio_bank_atom))) { AVM_LOGE(TAG, "Bank parameter of pin tuple must be an atom!"); - return error_tuple_str_maybe_gc(ctx, invalid_bank_atom); + return BADARG_ATOM; } uint32_t gpio_bank = ((uint32_t) interop_atom_term_select_int(gpio_bank_table, gpio_bank_atom, ctx->global)); @@ -434,7 +410,7 @@ static term gpio_digital_write(Context *ctx, term gpio_pin_tuple, term level_ter char *bank_string = interop_atom_to_string(ctx, gpio_bank_atom); AVM_LOGE(TAG, "Invalid GPIO Bank '%s' in pin tuple", bank_string); free(bank_string); - return error_tuple_str_maybe_gc(ctx, invalid_bank_atom); + return BADARG_ATOM; } term pin_term = term_get_tuple_element(gpio_pin_tuple, 1); @@ -442,17 +418,17 @@ static term gpio_digital_write(Context *ctx, term gpio_pin_tuple, term level_ter if (term_is_list(pin_term)) { if (UNLIKELY(!term_is_nonempty_list(pin_term))) { AVM_LOGE(TAG, "Pin list parameter contains no pin numbers!"); - return error_tuple_str_maybe_gc(ctx, invalid_pin_atom); + return BADARG_ATOM; } while (term_is_nonempty_list(pin_term)) { term gpio_pin_term = term_get_list_head(pin_term); if (UNLIKELY(!term_is_integer(gpio_pin_term))) { - return error_tuple_str_maybe_gc(ctx, invalid_pin_atom); + return BADARG_ATOM; } uint16_t gpio_pin_num = ((uint16_t) term_to_int32(gpio_pin_term)); if (UNLIKELY(gpio_pin_num > 15)) { AVM_LOGE(TAG, "Pin numbers must be between 0 and 15!"); - return error_tuple_str_maybe_gc(ctx, invalid_pin_atom); + return BADARG_ATOM; } gpio_pin_mask = 1U << gpio_pin_num | gpio_pin_mask; pin_term = term_get_list_tail(pin_term); @@ -461,7 +437,7 @@ static term gpio_digital_write(Context *ctx, term gpio_pin_tuple, term level_ter uint16_t gpio_pin_num = ((uint16_t) term_to_int32(pin_term)); if (UNLIKELY(gpio_pin_num > 15)) { AVM_LOGE(TAG, "Pin number must be between 0 and 15!"); - return error_tuple_str_maybe_gc(ctx, invalid_pin_atom); + return BADARG_ATOM; } else { gpio_pin_mask = 1U << gpio_pin_num | gpio_pin_mask; } @@ -469,32 +445,25 @@ static term gpio_digital_write(Context *ctx, term gpio_pin_tuple, term level_ter if (UNLIKELY(pin_term != ALL_ATOM)) { AVM_LOGE(TAG, "Pin number must be between 0 and 15!"); - return error_tuple_str_maybe_gc(ctx, invalid_pin_atom); + return BADARG_ATOM; } gpio_pin_mask = GPIO_ALL; } else { - return error_tuple_str_maybe_gc(ctx, invalid_pin_atom); + return BADARG_ATOM; } int level; + if (UNLIKELY(!term_is_logic_level(ctx->global, level_term))) { + AVM_LOGE(TAG, "GPIO level must be 0 or 1, or an atom ('high' or 'low')."); + return BADARG_ATOM; + } if (term_is_integer(level_term)) { level = term_to_int(level_term); - if (UNLIKELY((level != 0) && (level != 1))) { - return error_tuple_str_maybe_gc(ctx, invalid_level_atom); - } } else { - if (UNLIKELY(!term_is_atom(level_term))) { - AVM_LOGE(TAG, "GPIO level must be 0 or 1, or an atom ('high' or 'low')."); - return error_tuple_str_maybe_gc(ctx, invalid_level_atom); - } level = interop_atom_term_select_int(pin_level_table, level_term, ctx->global); - if (UNLIKELY(level < 0)) { - AVM_LOGE(TAG, "GPIO level atom must be 'high' or 'low'."); - return error_tuple_str_maybe_gc(ctx, invalid_level_atom); - } } - if (level != 0) { + if (level != GPIOPinLow) { gpio_set(gpio_bank, gpio_pin_mask); } else { gpio_clear(gpio_bank, gpio_pin_mask); @@ -508,12 +477,12 @@ static term gpio_digital_read(Context *ctx, term gpio_pin_tuple) { if (UNLIKELY(!term_is_tuple(gpio_pin_tuple))) { AVM_LOGE(TAG, "Invalid GPIO Pin tuple, expect {Bank, Pin}."); - return error_tuple_maybe_gc(ctx, BADARG_ATOM); + return BADARG_ATOM; } term gpio_bank_atom = term_get_tuple_element(gpio_pin_tuple, 0); if (UNLIKELY(!term_is_atom(gpio_bank_atom))) { AVM_LOGE(TAG, "Bank parameter of pin tuple must be an atom!"); - return error_tuple_str_maybe_gc(ctx, invalid_bank_atom); + return BADARG_ATOM; } uint32_t gpio_bank = ((uint32_t) interop_atom_term_select_int(gpio_bank_table, gpio_bank_atom, ctx->global)); @@ -521,20 +490,20 @@ static term gpio_digital_read(Context *ctx, term gpio_pin_tuple) char *bank_string = interop_atom_to_string(ctx, gpio_bank_atom); AVM_LOGE(TAG, "Invalid GPIO Bank '%s' in pin tuple", bank_string); free(bank_string); - return error_tuple_str_maybe_gc(ctx, invalid_bank_atom); + return BADARG_ATOM; } // TODO: Add support for reading list, or all input pins on port? uint16_t gpio_pin_num = ((uint16_t) term_to_int32(term_get_tuple_element(gpio_pin_tuple, 1))); if (UNLIKELY(gpio_pin_num > 15)) { AVM_LOGE(TAG, "Pin number must be between 0 and 15!"); - return error_tuple_str_maybe_gc(ctx, invalid_pin_atom); + return BADARG_ATOM; } uint16_t pin_levels = gpio_get(gpio_bank, (1U << gpio_pin_num)); uint16_t level = (pin_levels >> gpio_pin_num); TRACE("Read: Bank 0x%08lX Pin %u. RESULT: %u\n", gpio_bank, gpio_pin_num, level); - return level_to_atom(ctx, level); + return level_to_atom(level); } #ifndef AVM_DISABLE_GPIO_PORT_DRIVER @@ -555,8 +524,7 @@ static Context *gpio_driver_create_port(GlobalContext *global, term opts) ctx->native_handler = consume_gpio_mailbox; ctx->platform_data = gpio_data; - term reg_name_term = globalcontext_make_atom(global, gpio_atom); - int atom_index = term_to_atom_index(reg_name_term); + int atom_index = term_to_atom_index(GPIO_ATOM); if (UNLIKELY(!globalcontext_register_process(ctx->global, atom_index, ctx->process_id))) { scheduler_terminate(ctx); @@ -570,11 +538,11 @@ static Context *gpio_driver_create_port(GlobalContext *global, term opts) static term gpiodriver_close(Context *ctx) { GlobalContext *glb = ctx->global; - term gpio_atom_term = globalcontext_make_atom(glb, gpio_atom); - int gpio_atom_index = term_to_atom_index(gpio_atom_term); + int gpio_atom_index = term_to_atom_index(GPIO_ATOM); if (UNLIKELY(!globalcontext_get_registered_process(glb, gpio_atom_index))) { AVM_LOGE(TAG, "No active GPIO driver can be found."); - return error_tuple_maybe_gc(ctx, NOPROC_ATOM); + port_ensure_available(ctx, TUPLE_SIZE(2)); + return port_create_error_tuple(ctx, NOPROC_ATOM); } struct GPIOData *gpio_data = ctx->platform_data; @@ -617,7 +585,7 @@ void gpio_interrupt_callback(Context *ctx, uint32_t exti) term int_msg = term_alloc_tuple(2, &heap); term gpio_tuple = term_alloc_tuple(2, &heap); - term_put_tuple_element(int_msg, 0, globalcontext_make_atom(ctx->global, gpio_interrupt_atom)); + term_put_tuple_element(int_msg, 0, GPIO_INTERRUPT_ATOM); term_put_tuple_element(gpio_tuple, 0, gpio_bank); term_put_tuple_element(gpio_tuple, 1, term_from_int32((int32_t) gpio_pin)); term_put_tuple_element(int_msg, 1, gpio_tuple); @@ -734,7 +702,13 @@ static term gpiodriver_set_level(Context *ctx, term cmd) term gpio_pin_tuple = term_get_tuple_element(cmd, 1); term level = term_get_tuple_element(cmd, 2); - return gpio_digital_write(ctx, gpio_pin_tuple, level); + term result = gpio_digital_write(ctx, gpio_pin_tuple, level); + if (UNLIKELY(result != OK_ATOM)) { + port_ensure_available(ctx, TUPLE_SIZE(2)); + return port_create_error_tuple(ctx, result); + } + + return result; } static term gpiodriver_set_direction(Context *ctx, term cmd) @@ -742,13 +716,26 @@ static term gpiodriver_set_direction(Context *ctx, term cmd) term gpio_tuple = term_get_tuple_element(cmd, 1); term direction = term_get_tuple_element(cmd, 2); - return setup_gpio_pin(ctx, gpio_tuple, direction); + term result = setup_gpio_pin(ctx, gpio_tuple, direction); + if (UNLIKELY(result != OK_ATOM)) { + port_ensure_available(ctx, TUPLE_SIZE(2)); + return port_create_error_tuple(ctx, result); + } + + return result; } static term gpiodriver_read(Context *ctx, term cmd) { term gpio_pin_tuple = term_get_tuple_element(cmd, 1); - return gpio_digital_read(ctx, gpio_pin_tuple); + + term result = gpio_digital_read(ctx, gpio_pin_tuple); + if (UNLIKELY(!term_is_logic_level(ctx->global, result))) { + port_ensure_available(ctx, TUPLE_SIZE(2)); + return port_create_error_tuple(ctx, result); + } + + return result; } static bool gpiodriver_is_gpio_attached(struct GPIOData *gpio_data, term gpio_bank_atom, uint16_t pin) @@ -770,19 +757,22 @@ static term gpiodriver_set_int(Context *ctx, int32_t target_pid, term cmd) term gpio_tuple = term_get_tuple_element(cmd, 1); if (UNLIKELY(!term_is_tuple(gpio_tuple))) { AVM_LOGE(TAG, "Invalid GPIO Pin tuple, expect {Bank, Pin}."); - return error_tuple_maybe_gc(ctx, BADARG_ATOM); + port_ensure_available(ctx, TUPLE_SIZE(2)); + return port_create_error_tuple(ctx, BADARG_ATOM); } term gpio_bank_atom = term_get_tuple_element(gpio_tuple, 0); if (UNLIKELY(!term_is_atom(gpio_bank_atom))) { AVM_LOGE(TAG, "Bank parameter of pin tuple must be an atom!"); - return error_tuple_str_maybe_gc(ctx, invalid_bank_atom); + port_ensure_available(ctx, TUPLE_SIZE(2)); + return port_create_error_tuple(ctx, BADARG_ATOM); } uint32_t gpio_bank = (uint32_t) interop_atom_term_select_int(gpio_bank_table, gpio_bank_atom, ctx->global); if (UNLIKELY(gpio_bank == GPIOInvalidBank)) { char *bank_string = interop_atom_to_string(ctx, gpio_bank_atom); AVM_LOGE(TAG, "Invalid GPIO bank '%s' in pin tuple", bank_string); free(bank_string); - return error_tuple_str_maybe_gc(ctx, invalid_bank_atom); + port_ensure_available(ctx, TUPLE_SIZE(2)); + return port_create_error_tuple(ctx, BADARG_ATOM); } uint16_t gpio_pin = (uint16_t) term_to_int32(term_get_tuple_element(gpio_tuple, 1)); @@ -798,21 +788,24 @@ static term gpiodriver_set_int(Context *ctx, int32_t target_pid, term cmd) term trigger = term_get_tuple_element(cmd, 2); if (UNLIKELY(!term_is_atom(trigger))) { AVM_LOGE(TAG, "GPIO interrupt trigger must be an atom ('rising', 'falling', or 'both')."); - return error_tuple_str_maybe_gc(ctx, invalid_trigger_atom); + port_ensure_available(ctx, TUPLE_SIZE(2)); + return port_create_error_tuple(ctx, BADARG_ATOM); } - enum exti_trigger_type interrupt_type = interop_atom_term_select_int(exti_trigger_table, trigger, ctx->global); + int interrupt_type = interop_atom_term_select_int(exti_trigger_table, trigger, ctx->global); if (UNLIKELY(interrupt_type == INVALID_EXTI_TRIGGER)) { char *trigger_string = interop_atom_to_string(ctx, trigger); AVM_LOGE(TAG, "Interrupt type %s not supported on stm32 platform.", trigger_string); free(trigger_string); - return error_tuple_str_maybe_gc(ctx, invalid_trigger_atom); + port_ensure_available(ctx, TUPLE_SIZE(2)); + return port_create_error_tuple(ctx, BADARG_ATOM); } if (term_get_tuple_arity(cmd) == 4) { term pid = term_get_tuple_element(cmd, 3); if (UNLIKELY(!term_is_pid(pid) && !term_is_atom(pid))) { AVM_LOGE(TAG, "Invalid listener parameter, must be a pid() or registered process!"); - return create_pair(ctx, ERROR_ATOM, globalcontext_make_atom(ctx->global, invalid_listener_atom)); + port_ensure_available(ctx, TUPLE_SIZE(2)); + return port_create_error_tuple(ctx, BADARG_ATOM); } if (term_is_pid(pid)) { target_local_pid = term_to_local_process_id(pid); @@ -821,7 +814,8 @@ static term gpiodriver_set_int(Context *ctx, int32_t target_pid, term cmd) int32_t registered_process = (int32_t) globalcontext_get_registered_process(ctx->global, pid_atom_index); if (UNLIKELY(registered_process == 0)) { AVM_LOGE(TAG, "Invalid listener parameter, atom() is not a registered process name!"); - return create_pair(ctx, ERROR_ATOM, NOPROC_ATOM); + port_ensure_available(ctx, TUPLE_SIZE(2)); + return port_create_error_tuple(ctx, NOPROC_ATOM); } target_local_pid = registered_process; } @@ -839,7 +833,8 @@ static term gpiodriver_set_int(Context *ctx, int32_t target_pid, term cmd) char *bank_string = interop_atom_to_string(ctx, gpio_bank_atom); AVM_LOGE(TAG, "Cannot set interrupt for pin %s%u, exti%u device already in use!", bank_string, gpio_pin, gpio_pin); free(bank_string); - return error_tuple_str_maybe_gc(ctx, invalid_irq_atom); + port_ensure_available(ctx, TUPLE_SIZE(2)); + return port_create_error_tuple(ctx, BADARG_ATOM); } } } @@ -847,7 +842,8 @@ static term gpiodriver_set_int(Context *ctx, int32_t target_pid, term cmd) uint8_t exti_irq = pin_num_to_exti_irq(gpio_pin); if (UNLIKELY(exti_irq == 0)) { AVM_LOGE(TAG, "BUG: No valid exti irq found!"); - return error_tuple_str_maybe_gc(ctx, invalid_irq_atom); + port_ensure_available(ctx, TUPLE_SIZE(2)); + return port_create_error_tuple(ctx, BADARG_ATOM); } struct GPIOListenerData *data = malloc(sizeof(struct GPIOListenerData)); @@ -883,19 +879,22 @@ static term gpiodriver_remove_int(Context *ctx, term cmd) term gpio_tuple = term_get_tuple_element(cmd, 1); if (UNLIKELY(!term_is_tuple(gpio_tuple))) { AVM_LOGE(TAG, "Invalid GPIO Pin tuple, expect {Bank, Pin}."); - return error_tuple_maybe_gc(ctx, BADARG_ATOM); + port_ensure_available(ctx, TUPLE_SIZE(2)); + return port_create_error_tuple(ctx, BADARG_ATOM); } term target_bank_atom = term_get_tuple_element(gpio_tuple, 0); if (UNLIKELY(!term_is_atom(target_bank_atom))) { AVM_LOGE(TAG, "Bank parameter of pin tuple must be an atom!"); - return error_tuple_str_maybe_gc(ctx, invalid_bank_atom); + port_ensure_available(ctx, TUPLE_SIZE(2)); + return port_create_error_tuple(ctx, BADARG_ATOM); } uint32_t target_bank = (uint32_t) interop_atom_term_select_int(gpio_bank_table, target_bank_atom, ctx->global); if (UNLIKELY(target_bank == GPIOInvalidBank)) { char *bank_string = interop_atom_to_string(ctx, target_bank_atom); AVM_LOGE(TAG, "Invalid GPIO bank %s in pin tuple", bank_string); free(bank_string); - return error_tuple_str_maybe_gc(ctx, invalid_bank_atom); + port_ensure_available(ctx, TUPLE_SIZE(2)); + return port_create_error_tuple(ctx, BADARG_ATOM); } uint16_t target_num = (uint16_t) term_to_int32(term_get_tuple_element(gpio_tuple, 1)); uint8_t target_irq = pin_num_to_exti_irq(target_num); @@ -981,7 +980,8 @@ static NativeHandlerResult consume_gpio_mailbox(Context *ctx) char *invalid_name = interop_atom_to_string(ctx, cmd_term); AVM_LOGE(TAG, "Invalid command: %s", invalid_name); free(invalid_name); - ret = error_tuple_maybe_gc(ctx, UNDEFINED_ATOM); + port_ensure_available(ctx, TUPLE_SIZE(2)); + ret = port_create_error_tuple(ctx, UNDEFINED_ATOM); break; } @@ -989,7 +989,8 @@ static NativeHandlerResult consume_gpio_mailbox(Context *ctx) char *cmd_name = interop_atom_to_string(ctx, cmd_term); AVM_LOGE(TAG, "Unhandled error processing command: %s", cmd_name); free(cmd_name); - ret = error_tuple_maybe_gc(ctx, BADMATCH_ATOM); + port_ensure_available(ctx, TUPLE_SIZE(2)); + ret = port_create_error_tuple(ctx, BADMATCH_ATOM); } } @@ -997,7 +998,7 @@ static NativeHandlerResult consume_gpio_mailbox(Context *ctx) if (UNLIKELY(memory_ensure_free_with_roots(ctx, TUPLE_SIZE(2), 1, &ret, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { ret_msg = OUT_OF_MEMORY_ATOM; } else { - ret_msg = create_pair(ctx, gen_message.ref, ret); + ret_msg = port_create_tuple2(ctx, gen_message.ref, ret); } globalcontext_send_message(ctx->global, local_process_id, ret_msg); @@ -1020,14 +1021,10 @@ static term nif_gpio_set_pin_mode(Context *ctx, int argc, term argv[]) { UNUSED(argc); term ret = setup_gpio_pin(ctx, argv[0], argv[1]); - term error = get_error_type(ret); - if (UNLIKELY(error != OK_ATOM)) { - RAISE_ERROR(error); - } else { - if (UNLIKELY(ret == OUT_OF_MEMORY_ATOM)) { - RAISE_ERROR(ret); - } + if (UNLIKELY(ret != OK_ATOM)) { + RAISE_ERROR(ret); } + return ret; } @@ -1037,21 +1034,17 @@ static term nif_gpio_set_pin_pull(Context *ctx, int argc, term argv[]) UNUSED(argc); UNUSED(argv); AVM_LOGW(TAG, "Pull mode must be set using `gpio:set_pin_mode/2` arg #2 i.e. {Mode,PullMode}"); - return UNDEF_ATOM; + RAISE_ERROR(UNSUPPORTED_ATOM); } static term nif_gpio_digital_write(Context *ctx, int argc, term argv[]) { UNUSED(argc); term ret = gpio_digital_write(ctx, argv[0], argv[1]); - term error = get_error_type(ret); - if (UNLIKELY(error != OK_ATOM)) { - RAISE_ERROR(error); - } else { - if (UNLIKELY(ret == OUT_OF_MEMORY_ATOM)) { - RAISE_ERROR(ret); - } + if (UNLIKELY(ret != OK_ATOM)) { + RAISE_ERROR(ret); } + return ret; } @@ -1059,14 +1052,10 @@ static term nif_gpio_digital_read(Context *ctx, int argc, term argv[]) { UNUSED(argc); term ret = gpio_digital_read(ctx, argv[0]); - term error = get_error_type(ret); - if (UNLIKELY(error != OK_ATOM)) { - RAISE_ERROR(error); - } else { - if (UNLIKELY(ret == OUT_OF_MEMORY_ATOM)) { - RAISE_ERROR(ret); - } + if (UNLIKELY(!term_is_logic_level(ctx->global, ret))) { + RAISE_ERROR(ret); } + return ret; } diff --git a/src/platforms/stm32/src/lib/platform_defaultatoms.c b/src/platforms/stm32/src/lib/platform_defaultatoms.c new file mode 100644 index 000000000..412d03b64 --- /dev/null +++ b/src/platforms/stm32/src/lib/platform_defaultatoms.c @@ -0,0 +1,49 @@ +/* + * This file is part of AtomVM. + * + * Copyright 2019 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later + */ + +#include "platform_defaultatoms.h" + +#include +#include + +void platform_defaultatoms_init(GlobalContext *glb) +{ +// About X macro: https://en.wikipedia.org/wiki/X_macro +#define X(name, lenstr, str) \ + lenstr str, + + static const char *const atoms[] = { +#include "platform_defaultatoms.def" + + // dummy value + NULL + }; +#undef X + + for (int i = 0; i < ATOM_FIRST_AVAIL_INDEX - PLATFORM_ATOMS_BASE_INDEX; i++) { + if (UNLIKELY((size_t) atoms[i][0] != strlen(atoms[i] + 1))) { + AVM_ABORT(); + } + + if (UNLIKELY(globalcontext_insert_atom(glb, atoms[i]) != i + PLATFORM_ATOMS_BASE_INDEX)) { + AVM_ABORT(); + } + } +} diff --git a/src/platforms/stm32/src/lib/platform_defaultatoms.def b/src/platforms/stm32/src/lib/platform_defaultatoms.def new file mode 100644 index 000000000..65756c2d7 --- /dev/null +++ b/src/platforms/stm32/src/lib/platform_defaultatoms.def @@ -0,0 +1,30 @@ +/* + * This file is part of AtomVM. + * + * Copyright 2019-2024 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License")) + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later + */ + +X(STM32_ATOM, "\x5", "stm32") + +#if !(defined(AVM_DISABLE_GPIO_PORT_DRIVER) && defined(AVM_DISABLE_GPIO_NIFS)) +X(HIGH_ATOM, "\x4", "high") +X(LOW_ATOM, "\x3", "low") +#if !(defined(AVM_DISABLE_GPIO_PORT_DRIVER)) +X(GPIO_ATOM, "\x4", "gpio") +X(GPIO_INTERRUPT_ATOM, "\xE", "gpio_interrupt") +#endif +#endif diff --git a/src/platforms/stm32/src/lib/platform_defaultatoms.h b/src/platforms/stm32/src/lib/platform_defaultatoms.h new file mode 100644 index 000000000..794f55006 --- /dev/null +++ b/src/platforms/stm32/src/lib/platform_defaultatoms.h @@ -0,0 +1,58 @@ +/* + * This file is part of AtomVM. + * + * Copyright 2019-2024 Davide Bettio + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later + */ + +#ifndef _PLATFORM_DEFAULTATOMS_H_ +#define _PLATFORM_DEFAULTATOMS_H_ + +#include "defaultatoms.h" + +// About X macro: https://en.wikipedia.org/wiki/X_macro + +#define X(name, lenstr, str) \ + name##_INDEX, + +enum +{ + PLATFORM_ATOMS_BASE_INDEX_MINUS_ONE = PLATFORM_ATOMS_BASE_INDEX - 1, + +#include "platform_defaultatoms.def" + + ATOM_FIRST_AVAIL_INDEX +}; + +#undef X + +_Static_assert((int) ATOM_FIRST_AVAIL_INDEX > (int) PLATFORM_ATOMS_BASE_INDEX, + "default atoms and platform ones are overlapping"); + +#define X(name, lenstr, str) \ + name = TERM_FROM_ATOM_INDEX(name##_INDEX), + +enum +{ +#include "platform_defaultatoms.def" + + // dummy last item + ATOM_FIRST_AVAIL_DUMMY = TERM_FROM_ATOM_INDEX(ATOM_FIRST_AVAIL_INDEX) +}; + +#undef X + +#endif diff --git a/src/platforms/stm32/src/lib/platform_nifs.c b/src/platforms/stm32/src/lib/platform_nifs.c index 2a5e84383..a525aeed6 100644 --- a/src/platforms/stm32/src/lib/platform_nifs.c +++ b/src/platforms/stm32/src/lib/platform_nifs.c @@ -26,6 +26,7 @@ //#define ENABLE_TRACE #include +#include "platform_defaultatoms.h" #include "stm_sys.h" static term nif_atomvm_platform(Context *ctx, int argc, term argv[]) diff --git a/src/platforms/stm32/src/lib/stm_sys.h b/src/platforms/stm32/src/lib/stm_sys.h index 75f7bdc49..2bebdf0a5 100644 --- a/src/platforms/stm32/src/lib/stm_sys.h +++ b/src/platforms/stm32/src/lib/stm_sys.h @@ -24,8 +24,6 @@ #include #include -#define STM32_ATOM globalcontext_make_atom(ctx->global, ATOM_STR("\x5", "stm32")) - /* Only on ARMv7EM and above * TODO: These definitions are back-ported from libopencm3 `master`, if they ever release a new version this section should * be removed, along with the included headers and replaced with only the following inclusion: diff --git a/src/platforms/stm32/src/lib/sys.c b/src/platforms/stm32/src/lib/sys.c index c65a39cab..8b881caf6 100644 --- a/src/platforms/stm32/src/lib/sys.c +++ b/src/platforms/stm32/src/lib/sys.c @@ -98,15 +98,6 @@ static inline void sys_clock_gettime(struct timespec *t) t->tv_nsec = ((int32_t) now % 1000) * 1000000; } -/* TODO: Needed because `defaultatoms_init` in libAtomVM/defaultatoms.c calls this function. - * We should be able to remove this after `platform_defaulatoms.{c,h}` are removed on all platforms - * and `defaultatoms_init` is no longer called. - */ -void platform_defaultatoms_init(GlobalContext *glb) -{ - UNUSED(glb); -} - void sys_enable_core_periph_clocks() { uint32_t list[] = GPIO_CLOCK_LIST;