Merge "plat_stm32f4: exti: add chained ISRs" into mnc-dev
diff --git a/firmware/inc/platform/stm32f4xx/exti.h b/firmware/inc/platform/stm32f4xx/exti.h
index 849f1fa..c9873e4 100644
--- a/firmware/inc/platform/stm32f4xx/exti.h
+++ b/firmware/inc/platform/stm32f4xx/exti.h
@@ -1,7 +1,9 @@
 #ifndef _EXTI_H_
 #define _EXTI_H_
 
+#include <isr.h>
 #include <stdbool.h>
+#include <plat/inc/cmsis.h>
 #include <plat/inc/gpio.h>
 
 #ifdef __cplusplus
@@ -45,6 +47,9 @@
 bool extiIsPendingLine(const enum ExtiLine line);
 void extiClearPendingLine(const enum ExtiLine line);
 
+int extiChainIsr(IRQn_Type n, struct ChainedIsr *isr);
+int extiUnchainIsr(IRQn_Type n, struct ChainedIsr *isr);
+
 static inline void extiEnableIntGpio(const struct Gpio *__restrict gpio, enum ExtiTrigger trigger)
 {
     extiEnableIntLine(gpio->gpio & GPIO_PIN_MASK, trigger);
diff --git a/firmware/src/platform/stm32f4xx/exti.c b/firmware/src/platform/stm32f4xx/exti.c
index b997cc0..d63724d 100644
--- a/firmware/src/platform/stm32f4xx/exti.c
+++ b/firmware/src/platform/stm32f4xx/exti.c
@@ -1,3 +1,7 @@
+#include <errno.h>
+#include <isr.h>
+
+#include <plat/inc/cmsis.h>
 #include <plat/inc/exti.h>
 #include <plat/inc/pwr.h>
 
@@ -47,3 +51,90 @@
 {
     return (EXTI->PR & (1UL << line)) ? true : false;
 }
+
+struct ExtiInterrupt
+{
+    struct ChainedInterrupt base;
+    IRQn_Type irq;
+};
+
+static void extiInterruptEnable(struct ChainedInterrupt *irq)
+{
+    struct ExtiInterrupt *exti = container_of(irq, struct ExtiInterrupt, base);
+    NVIC_EnableIRQ(exti->irq);
+}
+
+static void extiInterruptDisable(struct ChainedInterrupt *irq)
+{
+    struct ExtiInterrupt *exti = container_of(irq, struct ExtiInterrupt, base);
+    NVIC_DisableIRQ(exti->irq);
+}
+
+#define DECLARE_SHARED_EXTI(i) {            \
+    .base = {                               \
+        .enable = extiInterruptEnable,      \
+        .disable = extiInterruptDisable,    \
+    },                                      \
+    .irq = i,                               \
+}
+
+static struct ExtiInterrupt gInterrupts[] = {
+    DECLARE_SHARED_EXTI(EXTI0_IRQn),
+    DECLARE_SHARED_EXTI(EXTI1_IRQn),
+    DECLARE_SHARED_EXTI(EXTI2_IRQn),
+    DECLARE_SHARED_EXTI(EXTI3_IRQn),
+    DECLARE_SHARED_EXTI(EXTI4_IRQn),
+    DECLARE_SHARED_EXTI(EXTI9_5_IRQn),
+    DECLARE_SHARED_EXTI(EXTI15_10_IRQn),
+};
+
+static inline struct ExtiInterrupt *extiForIrq(IRQn_Type n)
+{
+    if (n >= EXTI0_IRQn && n <= EXTI4_IRQn)
+        return &gInterrupts[n - EXTI0_IRQn];
+    if (n == EXTI9_5_IRQn)
+        return &gInterrupts[ARRAY_SIZE(gInterrupts) - 2];
+    if (n == EXTI15_10_IRQn)
+        return &gInterrupts[ARRAY_SIZE(gInterrupts) - 1];
+    return NULL;
+}
+
+static void extiIrqHandler(IRQn_Type n)
+{
+    struct ExtiInterrupt *exti = extiForIrq(n);
+    dispatchIsr(&exti->base);
+}
+
+#define DEFINE_SHARED_EXTI_ISR(i)           \
+    void EXTI##i##_IRQHandler(void);        \
+    void EXTI##i##_IRQHandler(void) {       \
+        extiIrqHandler(EXTI##i##_IRQn);     \
+    }                                       \
+
+DEFINE_SHARED_EXTI_ISR(0)
+DEFINE_SHARED_EXTI_ISR(1)
+DEFINE_SHARED_EXTI_ISR(2)
+DEFINE_SHARED_EXTI_ISR(3)
+DEFINE_SHARED_EXTI_ISR(4)
+DEFINE_SHARED_EXTI_ISR(9_5)
+DEFINE_SHARED_EXTI_ISR(15_10)
+
+int extiChainIsr(IRQn_Type n, struct ChainedIsr *isr)
+{
+    struct ExtiInterrupt *exti = extiForIrq(n);
+    if (!exti)
+        return -EINVAL;
+
+    chainIsr(&exti->base, isr);
+    return 0;
+}
+
+int extiUnchainIsr(IRQn_Type n, struct ChainedIsr *isr)
+{
+    struct ExtiInterrupt *exti = extiForIrq(n);
+    if (!exti)
+        return -EINVAL;
+
+    unchainIsr(&exti->base, isr);
+    return 0;
+}