diff --git a/epicardium/api/interrupt-sender.c b/epicardium/api/interrupt-sender.c index 53568df9c839b019d33e126cb42166a2b5b78566..fad7ed674ef85f7e0553abbfc86ed1cca6af37d5 100644 --- a/epicardium/api/interrupt-sender.c +++ b/epicardium/api/interrupt-sender.c @@ -3,49 +3,20 @@ #include "tmr_utils.h" #include <assert.h> -static bool int_enabled[EPIC_INT_NUM]; - -void api_interrupt_trigger(api_int_id_t id) -{ - assert(id < EPIC_INT_NUM); - - if (int_enabled[id]) { - while (API_CALL_MEM->int_id != (api_int_id_t)(-1)) - ; - - API_CALL_MEM->int_id = id; - TMR_TO_Start(MXC_TMR5, 1, 0); - } -} - void api_interrupt_init(void) { API_CALL_MEM->int_id = (-1); - - for (int i = 0; i < EPIC_INT_NUM; i++) { - int_enabled[i] = false; - } - - /* Reset interrupt is always enabled */ - int_enabled[EPIC_INT_RESET] = true; } -int epic_interrupt_enable(api_int_id_t int_id) +bool api_interrupt_is_ready(void) { - if (int_id >= EPIC_INT_NUM) { - return -EINVAL; - } - - int_enabled[int_id] = true; - return 0; + return API_CALL_MEM->int_id == (api_int_id_t)(-1); } -int epic_interrupt_disable(api_int_id_t int_id) +void api_interrupt_trigger(api_int_id_t id) { - if (int_id >= EPIC_INT_NUM || int_id == EPIC_INT_RESET) { - return -EINVAL; - } + assert(API_CALL_MEM->int_id == (api_int_id_t)(-1)); - int_enabled[int_id] = false; - return 0; + API_CALL_MEM->int_id = id; + TMR_TO_Start(MXC_TMR5, 1, 0); } diff --git a/epicardium/api/interrupt-sender.h b/epicardium/api/interrupt-sender.h index 84fe4428fdbaf00381fa757392f911ebc48cd961..d4924c0995b2ed46474f64155db67db9415e7843 100644 --- a/epicardium/api/interrupt-sender.h +++ b/epicardium/api/interrupt-sender.h @@ -2,4 +2,5 @@ #include "api/common.h" void api_interrupt_init(void); +bool api_interrupt_is_ready(void); void api_interrupt_trigger(api_int_id_t id); diff --git a/epicardium/modules/hardware.c b/epicardium/modules/hardware.c index 358154334f1b7f3a2d508e7348e70999aa67257c..6d118fe73ef59007c4e3cb915afdba004e678d56 100644 --- a/epicardium/modules/hardware.c +++ b/epicardium/modules/hardware.c @@ -1,7 +1,6 @@ #include "epicardium.h" #include "api/dispatcher.h" -#include "api/interrupt-sender.h" #include "usb/epc_usb.h" #include "modules/filesystem.h" #include "modules/log.h" @@ -171,7 +170,7 @@ int hardware_early_init(void) /* * API Dispatcher & API Interrupts */ - api_interrupt_init(); + interrupt_init(); api_dispatcher_init(); /* @@ -237,7 +236,7 @@ int hardware_reset(void) /* * API Dispatcher & API Interrupts */ - api_interrupt_init(); + interrupt_init(); api_dispatcher_init(); /* diff --git a/epicardium/modules/interrupts.c b/epicardium/modules/interrupts.c new file mode 100644 index 0000000000000000000000000000000000000000..e33e35769d887fbcc6eafdf79186e7015f88106c --- /dev/null +++ b/epicardium/modules/interrupts.c @@ -0,0 +1,99 @@ +#include "modules/mutex.h" +#include "epicardium.h" +#include "api/interrupt-sender.h" +#include <assert.h> + +struct interrupt_priv { + /* Whether this interrupt can be triggered */ + bool int_enabled[EPIC_INT_NUM]; + /* Whether this interrupt is waiting to be delivered */ + bool int_pending[EPIC_INT_NUM]; + /* Whether any interrupts are currently waiting to be triggered */ + bool has_pending; +}; + +static struct interrupt_priv interrupt_data; +static struct mutex interrupt_mutex; + +void interrupt_trigger(api_int_id_t id) +{ + assert(id < EPIC_INT_NUM); + + mutex_lock(&interrupt_mutex); + if (!interrupt_data.int_enabled[id]) + goto out; + + while (!api_interrupt_is_ready()) + ; + + api_interrupt_trigger(id); +out: + mutex_unlock(&interrupt_mutex); +} + +/* + * This function solely exists because of that one use of interrupts that breaks + * the rules: The RTC ALARM interrupt is triggered from a hardware ISR where + * interrupt_trigger_sync() won't work because it needs to lock a mutex. + * + * DO NOT USE THIS FUNCTION IN ANY NEW CODE. + */ +void __attribute__((deprecated)) interrupt_trigger_unsafe(api_int_id_t id) +{ + assert(id < EPIC_INT_NUM); + + if (!interrupt_data.int_enabled[id]) + return; + + while (!api_interrupt_is_ready()) + ; + + api_interrupt_trigger(id); +} + +static void interrupt_set_enabled(api_int_id_t id, bool enabled) +{ + assert(id < EPIC_INT_NUM); + + mutex_lock(&interrupt_mutex); + interrupt_data.int_enabled[id] = enabled; + mutex_unlock(&interrupt_mutex); +} + +void interrupt_init(void) +{ + if (interrupt_mutex.name == NULL) + mutex_create(&interrupt_mutex); + + api_interrupt_init(); + + /* Reset all irqs to disabled */ + for (size_t i = 0; i < EPIC_INT_NUM; i++) { + interrupt_set_enabled(i, false); + } + + /* Reset interrupt is always enabled */ + interrupt_set_enabled(EPIC_INT_RESET, true); +} + +/* Epic-calls {{{ */ +int epic_interrupt_enable(api_int_id_t int_id) +{ + if (int_id >= EPIC_INT_NUM) { + return -EINVAL; + } + + interrupt_set_enabled(int_id, true); + return 0; +} + +int epic_interrupt_disable(api_int_id_t int_id) +{ + if (int_id >= EPIC_INT_NUM || int_id == EPIC_INT_RESET) { + return -EINVAL; + } + + interrupt_set_enabled(int_id, false); + return 0; +} +/* }}} */ diff --git a/epicardium/modules/meson.build b/epicardium/modules/meson.build index 474b32930f9b4970fdde4cf65c6891f5eead8a1f..548d8563ea1e7ac2943843931e1733af5fc07dd2 100644 --- a/epicardium/modules/meson.build +++ b/epicardium/modules/meson.build @@ -9,6 +9,7 @@ module_sources = files( 'gpio.c', 'hardware.c', 'hw-lock.c', + 'interrupts.c', 'leds.c', 'lifecycle.c', 'light_sensor.c', diff --git a/epicardium/modules/modules.h b/epicardium/modules/modules.h index 67557ad4f226aa6ad9a116aa0b940b09889ed2a2..534901c5ccb6d82a69b7b314e8f3e85da4916616 100644 --- a/epicardium/modules/modules.h +++ b/epicardium/modules/modules.h @@ -4,6 +4,7 @@ #include "FreeRTOS.h" #include "gpio.h" #include "modules/mutex.h" +#include "epicardium.h" #include <stdint.h> #include <stdbool.h> @@ -27,6 +28,13 @@ int hardware_reset(void); void vLifecycleTask(void *pvParameters); void return_to_menu(void); +/* ---------- Interrupts --------------------------------------------------- */ +void interrupt_init(void); +void interrupt_trigger(api_int_id_t id); +void interrupt_trigger_unsafe(api_int_id_t id) __attribute__((deprecated( + "interrupt_trigger_unsafe() is racy and only exists for legacy code." +))); + /* ---------- Serial ------------------------------------------------------- */ #define SERIAL_READ_BUFFER_SIZE 128 #define SERIAL_WRITE_STREAM_BUFFER_SIZE 512