aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2021-10-26 00:10:06 -0600
committerJosh Rahm <joshuarahm@gmail.com>2021-10-26 00:10:06 -0600
commitf9b12f748b557994b958115c04fd1591b322248e (patch)
tree7d80680edadf5b0018944966af64f8773bfa8f1a
parentdbbe83bd8882fe18e26f6305a1f425145bfea8db (diff)
parent90eb3a0b79bfef67c70dc545b49c48928eea05f4 (diff)
downloadstm32l4-f9b12f748b557994b958115c04fd1591b322248e.tar.gz
stm32l4-f9b12f748b557994b958115c04fd1591b322248e.tar.bz2
stm32l4-f9b12f748b557994b958115c04fd1591b322248e.zip
Merge branch 'christmas' into HEAD
-rw-r--r--Makefile.preamble2
-rw-r--r--include/arch/arm/arch.h5
-rw-r--r--include/arch/stm32l4xxx/peripherals/exti.h35
-rw-r--r--include/arch/stm32l4xxx/peripherals/syscfg.h87
-rw-r--r--include/arch/stm32l4xxx/peripherals/tim.h175
-rw-r--r--include/arch/x86_64/arch.h6
-rw-r--r--include/drv/ir/control.h14
-rw-r--r--include/drv/ir/ir.h34
-rw-r--r--include/drv/ir/lg_remote_codes.h17
-rw-r--r--include/drv/ws2812B/ws2812b.h3
-rw-r--r--include/kern/common.h1
-rw-r--r--include/kern/exti/exti_manager.h32
-rw-r--r--include/kern/gpio/gpio_manager.h56
-rw-r--r--include/kern/mem.h2
-rw-r--r--include/shared/array_list.h105
-rw-r--r--include/shared/avl_tree.h270
-rw-r--r--include/shared/avl_tree.h.gchbin0 -> 11451 bytes
-rw-r--r--include/shared/linked_list.h277
-rw-r--r--include/shared/map.h90
-rw-r--r--include/shared/math.h19
-rw-r--r--include/shared/stdmacro.h7
-rw-r--r--src/drv/ir/control.c43
-rw-r--r--src/drv/ir/ir.c160
-rw-r--r--src/drv/ws2812B/ws2812b.c26
-rw-r--r--src/kern/gpio/gpio_manager.c9
-rw-r--r--src/kern/lib.c5
-rw-r--r--src/kern/main.c236
-rw-r--r--src/kern/mem.c17
-rw-r--r--src/kern/stdlibrepl.c36
-rw-r--r--src/shared/math.c38
-rw-r--r--test_harness/fake_env.c5
-rw-r--r--test_harness/fake_env.h3
-rw-r--r--tests/test_array_list.c65
-rw-r--r--tests/test_avl_tree.c149
-rw-r--r--tests/test_linked_list.c22
-rw-r--r--tests/test_map.c113
-rw-r--r--tests/test_mpu.c27
37 files changed, 1962 insertions, 229 deletions
diff --git a/Makefile.preamble b/Makefile.preamble
index 62b221a..75f3afb 100644
--- a/Makefile.preamble
+++ b/Makefile.preamble
@@ -6,7 +6,7 @@ CFLAGS?=$(OPT) -mcpu=cortex-m4 -mthumb -g -lgcc -static -nostartfiles -Iinclude
LD_FLAGS?=-T linker/linker_script.ld -nostdlib --cref -Map linker/main.map -static
TEST_PREFIX=x86_64-pc-linux-gnu-
-TEST_CFLAGS=-Iinclude -Iinclude/arch/x86_64 -Itest_harness -g3 -ggdb -DFOR_TESTING -Wall
+TEST_CFLAGS=-Iinclude -Iinclude/arch/x86_64 -Itest_harness -g3 -ggdb -gdwarf-2 -DFOR_TESTING -Wall
all: _$(PREFIX)_obs/main.elf
diff --git a/include/arch/arm/arch.h b/include/arch/arm/arch.h
index e4ebb8d..7dfc343 100644
--- a/include/arch/arm/arch.h
+++ b/include/arch/arm/arch.h
@@ -44,6 +44,11 @@
#define STACK_TOP (0x2000c000)
#define MPU_BASE (0xE000ED90)
+#define SYSCFG_BASE (0x40010000)
+#define EXTI_BASE (0x40010400)
+
+#define TIM2_BASE (0x40000000)
+
#include <stdint.h>
#ifndef DRY_RUN
_Static_assert(sizeof(void*) == sizeof(uint32_t), "Pointers must be 32 bits");
diff --git a/include/arch/stm32l4xxx/peripherals/exti.h b/include/arch/stm32l4xxx/peripherals/exti.h
new file mode 100644
index 0000000..6776d10
--- /dev/null
+++ b/include/arch/stm32l4xxx/peripherals/exti.h
@@ -0,0 +1,35 @@
+#ifndef PERIPHERALS_EXTI_H_
+#define PERIPHERALS_EXTI_H_
+
+#include "arch.h"
+
+#define EXTI (*(exti_regs_t*)(EXTI_BASE))
+
+typedef struct {
+#define exti_im_n(n) (1 << (n))
+ uint32_t im_r1; /* Interrupt mask register 1. */
+
+#define exti_em_n(n) (1 << (n))
+ uint32_t em_r1; /* Event mask register 1. */
+
+#define exti_rt_n(n) (1 << (n))
+ uint32_t rts_r1; /* Rising trigger selection register 1. */
+
+#define exti_ft_n(n) (1 << (n))
+ uint32_t fts_r1; /* Falling trigger selection register 1. */
+
+#define exti_swi_n(n) (1 << (n))
+ uint32_t swie_r1; /* Software interrupt event register 1. */
+
+#define exti_pif_n(n) (1 << (n))
+ uint32_t p_r1; /* Pending register 1. */
+
+ uint32_t im_r2; /* Interrupt mask register 2. */
+ uint32_t em_r2; /* Event mask register 2. */
+ uint32_t rts_r2; /* Rising trigger selection register 2. */
+ uint32_t fts_r2; /* Falling trigger selection register 2. */
+ uint32_t swie_r2; /* Software interrupt event register 2. */
+ uint32_t p_r2; /* Pending register 2. */
+} exti_regs_t;
+
+#endif /* PERIPHERALS_EXTI_H_ */
diff --git a/include/arch/stm32l4xxx/peripherals/syscfg.h b/include/arch/stm32l4xxx/peripherals/syscfg.h
new file mode 100644
index 0000000..65290a0
--- /dev/null
+++ b/include/arch/stm32l4xxx/peripherals/syscfg.h
@@ -0,0 +1,87 @@
+#ifndef PERIPHERALS_SYSCFG_H_
+#define PERIPHERALS_SYSCFG_H_
+
+#include "arch.h"
+#include "kern/common.h"
+
+#define SYSCFG (*(syscfg_t*)SYSCFG_BASE)
+
+typedef __IO struct {
+#define syscfg_memmode (7 << 0) /* Memory mapping selection. */
+#define syscfg_fbmode (1 << 8) /* Flash bank mode selection. */
+ uint32_t memrmp_r; /* Configuration on memory map. */
+
+#define syscfg_fwdis (1 << 0) /* Disable firewall. */
+#define syscfg_boosten (1 << 8) /* Enable analog boost. */
+#define syscfg_i2c4_fmp \
+ (1 << 23) /* Fast-mode Plus driving capability activation (this bit is only \
+ available on */
+#define syscfg_i2c3_fmp \
+ (1 << 22) /* I2C3 Fast-mode Plus driving capability activation */
+#define syscfg_i2c2_fmp \
+ (1 << 21) /* I2C2 Fast-mode Plus driving capability activation */
+#define syscfg_i2c1_fmp \
+ (1 << 20) /* I2C1 Fast-mode Plus driving capability activation */
+#define syscfg_i2c_pb9_fmp \
+ (1 << 19) /* Fast-mode Plus (Fm+) driving capability activation on PB9 */
+#define syscfg_i2c_pb8_fmp \
+ (1 << 18) /* Fast-mode Plus (Fm+) driving capability activation on PB8 */
+#define syscfg_i2c_pb7_fmp \
+ (1 << 17) /* Fast-mode Plus (Fm+) driving capability activation on PB7 */
+#define syscfg_i2c_pb6_fmp \
+ (1 << 16) /* Fast-mode Plus (Fm+) driving capability activation on PB6 */
+#define syscfg_fpe_ie (0x3f << 26) /* Floating point interrupts enabled. */
+ uint32_t cfg_r1; /* Configuration register 1. */
+
+#define syscfg_exti0 (0xf << 0)
+#define syscfg_exti1 (0xf << 4)
+#define syscfg_exti2 (0xf << 8)
+#define syscfg_exti3 (0xf << 12)
+ uint32_t extic_r1; /* External interrupt configuration register 1. */
+
+#define syscfg_exti4 (0xf << 0)
+#define syscfg_exti5 (0xf << 4)
+#define syscfg_exti6 (0xf << 8)
+#define syscfg_exti7 (0xf << 12)
+ uint32_t extic_r2; /* External interrupt configuration register 2. */
+
+#define syscfg_exti8 (0xf << 0)
+#define syscfg_exti9 (0xf << 4)
+#define syscfg_exti10 (0xf << 8)
+#define syscfg_exti11 (0xf << 12)
+ uint32_t extic_r3; /* External interrupt configuration register 3. */
+
+#define syscfg_exti12 (0xf << 0)
+#define syscfg_exti13 (0xf << 4)
+#define syscfg_exti14 (0xf << 8)
+#define syscfg_exti15 (0xf << 12)
+ uint32_t extic_r4; /* External interrupt configuration register 4. */
+
+#define syscfg_sram2er (1 << 0)
+#define syscfg_sram2bsy (1 << 1)
+ uint32_t scs_r; /* SRAM2 control & status register. */
+
+#define syscfg_spf (1 << 8) /* SRAM2 parity error flag */
+#define syscfg_eccl (1 << 3) /* ECC Lock */
+#define syscfg_pvdl (1 << 2) /* PVD lock enable bit */
+#define syscfg_spl (1 << 1) /* SRAM2 parity lock bit */
+#define syscfg_cll (1 << 0) /* Cortex-M4 LOCKUP (Hardfault) output enable bit */
+ uint32_t cfg_r2; /* Configuration register 2. */
+
+#define syscfg_pnwp(n) (1 << (n)) /* Page n write protection on sram2. */
+ uint32_t swp_r1; /* SRAM2 software protection register 1. */
+
+ union {
+ uint8_t sk_r; /* SRAM2 write protection key for software erase. */
+ uint32_t reserved;
+ };
+
+ uint32_t swp_r2; /* SRAM2 software protection register 2. */
+
+
+} syscfg_t;
+
+static_assert(
+ offsetof(syscfg_t, swp_r2) == 0x28, "Offset check failed");
+
+#endif /* PERIPHERALS_SYSCFG_H_ */
diff --git a/include/arch/stm32l4xxx/peripherals/tim.h b/include/arch/stm32l4xxx/peripherals/tim.h
new file mode 100644
index 0000000..145622a
--- /dev/null
+++ b/include/arch/stm32l4xxx/peripherals/tim.h
@@ -0,0 +1,175 @@
+#ifndef ARCH_STM32L4XXX_PERIPHERALS_TIM_H_
+#define ARCH_STM32L4XXX_PERIPHERALS_TIM_H_
+
+#include "arch.h"
+#include "kern/common.h"
+
+#define TIM2 (*((tim_regs_t*)TIM2_BASE))
+
+typedef __IO struct {
+#define tim_uifremap (1 << 11) /* UIF status bit remapping */
+#define tim_ckd (3 << 8) /* Clock Divisor */
+#define tim_arpe (1 << 7) /* Auto-reload preload enable */
+#define tim_cms (3 << 5) /* Center align mode selection */
+#define tim_dir (1 << 4) /* Direction */
+#define tim_opm (1 << 3) /* One-pulse mode */
+#define tim_urs (1 << 2) /* Update request source */
+#define tim_udis (1 << 1) /* Update disable */
+#define tim_cen (1 << 0) /* Counter enable */
+ uint32_t c_r1;
+
+#define tim_ti1s (1 << 7)
+#define tim_mms (7 << 4)
+#define tim_ccds (1 << 3)
+ uint32_t c_r2;
+
+#define tim_sms_3 (1 << 16)
+#define tim_etp (1 << 15)
+#define tim_ece (1 << 14)
+#define tim_etps (3 << 12)
+#define tim_etf (0xF << 8)
+#define tim_msm (1 << 7)
+#define tim_ts (7 << 4)
+#define tim_occs (1 << 3)
+#define tim_sms_0_2 (7 << 0)
+ uint32_t smc_r;
+
+#define tim_tde (1 << 14) /* Trigger DMA request enable */
+#define tim_cc4de (1 << 12) /* Capture/Compare 4 DMA request enable */
+#define tim_cc3de (1 << 11) /* Capture/Compare 3 DMA request enable */
+#define tim_cc2de (1 << 10) /* Capture/Compare 2 DMA request enable */
+#define tim_cc1de (1 << 9) /* Capture/Compare 1 DMA request enable */
+#define tim_ude (1 << 8) /* Update DMA request enable */
+#define tim_tie (1 << 6) /* Trigger interrupt enable */
+#define tim_cc4ie (1 << 4) /* Capture/Compare 4 interrupt enable */
+#define tim_cc3ie (1 << 3) /* Capture/Compare 3 interrupt enable */
+#define tim_cc2ie (1 << 2) /* Capture/Compare 2 interrupt enable */
+#define tim_cc1ie (1 << 1) /* Capture/Compare 1 interrupt enable */
+#define tim_uie (1 << 0) /* Update interrupt enable */
+ uint32_t die_r; /* Dma interrupt/enable register */
+
+#define tim_cc4of (1 << 12) /* Capture/Compare 4 overcapture flag */
+#define tim_cc3of (1 << 11) /* Capture/Compare 3 overcapture flag */
+#define tim_cc2of (1 << 10) /* Capture/compare 2 overcapture flag */
+#define tim_cc1of (1 << 9) /* Capture/Compare 1 overcapture flag */
+#define tim_tif (1 << 6) /* Trigger interrupt flag */
+#define tim_cc4if (1 << 4) /* Capture/Compare 4 interrupt flag */
+#define tim_cc3if (1 << 3) /* Capture/Compare 3 interrupt flag */
+#define tim_cc2if (1 << 2) /* Capture/Compare 2 interrupt flag */
+#define tim_cc1if (1 << 1) /* Capture/compare 1 interrupt flag */
+#define tim_uif (1 << 0) /* Update interrupt flag */
+ uint32_t s_r; /* Status register */
+
+#define tim_tg (1 << 6) /* Trigger generation */
+#define tim_cc4g (1 << 4) /* Capture/compare 4 generation */
+#define tim_cc3g (1 << 3) /* Capture/compare 3 generation */
+#define tim_cc2g (1 << 2) /* Capture/compare 2 generation */
+#define tim_cc1g (1 << 1) /* Capture/compare 1 generation */
+#define tim_ug (1 << 0) /* Update generation */
+ uint32_t eg_r; /* Event generation register. */
+
+#define tim_ic2f (15 << 12) /* Input capture 2 filter */
+#define tim_ic2psc (3 << 10) /* Input capture 2 prescaler */
+#define tim_cc2s (3 << 8) /* Capture/compare 2 selection */
+#define tim_ic1f (15 << 4) /* Input capture 1 filter */
+#define tim_ic1psc (3 << 2) /* Input capture 1 prescaler */
+#define tim_cc1s (3 << 0) /* Capture/Compare 1 selection */
+ /* Alternate */
+#define tim_oc2ce (1 << 15) /* Output compare 2 clear enable */
+#define tim_oc2m_3 (1 << 24) /* Output compare 2 mode, bit 3. */
+#define tim_oc2m_0_2 (7 << 12) /* Output compare 2 mode, bits 0-2 */
+#define tim_oc2pe (1 << 11) /* Output compare 2 preload enable */
+#define tim_oc2fe (1 << 10) /* Output compare 2 fast enable */
+#define tim_cc2s (3 << 8) /* Capture/Compare 2 selection */
+#define tim_oc1ce (1 << 7) /* Output compare 1 clear enable */
+#define tim_oc1m_3 (1 << 16) /* Output compare 1 mode, bit 3. */
+#define tim_oc1m_0_2 (7 << 4) /* Output compare 1 mode, bits 0-2. */
+#define tim_oc1pe (1 << 3) /* Output compare 1 preload enable */
+#define tim_oc1fe (1 << 2) /* Output compare 1 fast enable */
+#define tim_cc1s (3 << 0) /* Capture/Compare 1 selection */
+ uint32_t ccm_r1; /* Capture/compare mode register 1. */
+
+#define tim_ic4f (15 << 12) /* Input capture 4 filter */
+#define tim_ic4psc (3 << 10) /* Input capture 4 prescaler */
+#define tim_cc4s (3 << 8) /* Capture/Compare 4 selection */
+#define tim_ic3f (15 << 4) /* Input capture 3 filter */
+#define tim_ic3psc (3 << 2) /* Input capture 3 prescaler */
+#define tim_cc3s (3 << 0) /* Capture/Compare 3 selection */
+ /* Alternate */
+#define tim_oc4ce (1 << 15) /* Output compare 4 clear enable */
+#define tim_oc4m_3 (1 << 24) /* Output compare 4 mode, bit 3. */
+#define tim_oc4m_0_2 (7 << 12) /* Output compare 4 mode, bits 0-2 */
+#define tim_oc4pe (1 << 11) /* Output compare 4 preload enable */
+#define tim_oc4fe (1 << 10) /* Output compare 4 fast enable */
+#define tim_cc4s (3 << 8) /* Capture/Compare 4 selection */
+#define tim_oc3ce (1 << 7) /* Output compare 3 clear enable */
+#define tim_oc3m_3 (1 << 16) /* Output compare 3 mode, bit 3. */
+#define tim_oc3m_0_2 (7 << 4) /* Output compare 3 mode, bits 0-2. */
+#define tim_oc3pe (1 << 3) /* Output compare 3 preload enable */
+#define tim_oc3fe (1 << 2) /* Output compare 3 fast enable */
+#define tim_cc3s (3 << 0) /* Capture/Compare 3 selection */
+ uint32_t ccm_r2; /* Capture/compare mode register 2. */
+
+#define tim_cc4np (1 << 15) /* Capture/Compare 4 output Polarity. */
+#define tim_cc4p (1 << 13) /* Capture/Compare 4 output Polarity. */
+#define tim_cc4e (1 << 12) /* Capture/Compare 4 output enable. */
+#define tim_cc3np (1 << 11) /* Capture/Compare 3 output Polarity. */
+#define tim_cc3p (1 << 9) /* Capture/Compare 3 output Polarity. */
+#define tim_cc3e (1 << 8) /* Capture/Compare 3 output enable. */
+#define tim_cc2np (1 << 7) /* Capture/Compare 2 output Polarity. */
+#define tim_cc2p (1 << 5) /* Capture/Compare 2 output Polarity. */
+#define tim_cc2e (1 << 4) /* Capture/Compare 2 output enable. */
+#define tim_cc1np (1 << 3) /* Capture/Compare 1 output Polarity. */
+#define tim_cc1p (1 << 1) /* Capture/Compare 1 output Polarity. */
+#define tim_cc1e (1 << 0) /* Capture/Compare 1 output enable. */
+ uint32_t cce_r; /* Copture/compare enable register. */
+
+ /* Mapping depends on UIFREMAP. */
+#define tim_cnt (0x7fffffff << 0) /* Counter value */
+#define tim_uifcpy (1 << 31)
+ uint32_t cnt;
+
+ uint16_t psc; /* Prescaler. */
+ uint16_t reserved0;
+
+ uint32_t ar_r; /* Auto reload register. */
+
+ uint32_t reserved1;
+
+ uint32_t cc_r1; /* Capture compare register 1. */
+ uint32_t cc_r2; /* Capture compare register 2. */
+ uint32_t cc_r3; /* Capture compare register 3. */
+ uint32_t cc_r4; /* Capture compare register 4. */
+
+ uint32_t reserved2;
+
+#define tim_dbl (31 << 8) /* DMA burst length */
+#define tim_dba (31 << 0) /* DMA base address */
+ uint32_t dc_r; /* dma control register. */
+
+ uint16_t dma_r; /* dma address for full transfer. */
+ uint16_t reserved3; /* dma address for full transfer. */
+
+ union {
+#define tim_ti4_rmp (3 << 2) /* Input Capture 4 remap */
+#define tim_etr_rmp (1 << 1) /* External trigger remap */
+#define tim_itr1_rmp (1 << 0) /* Internal trigger 1 remap */
+ uint32_t tim2_o_r1;
+#define tim_ti1_rmp (3 << 0)
+ uint32_t tim3_o_r1;
+ };
+
+ uint32_t reserved4[3];
+
+#define tim_etrsel (7 << 14) /* ETR source selection */
+ union {
+ uint32_t tim2_o_r2;
+ uint32_t tim3_o_r2;
+ };
+} tim_regs_t;
+
+static_assert(offsetof(tim_regs_t, ar_r) == 0x2c, "Offset check failed");
+static_assert(offsetof(tim_regs_t, dc_r) == 0x48, "Offset check failed");
+static_assert(offsetof(tim_regs_t, tim3_o_r2) == 0x60, "Offset check failed");
+
+#endif /* ARCH_STM32L4XXX_PERIPHERALS_TIM_H_ */
diff --git a/include/arch/x86_64/arch.h b/include/arch/x86_64/arch.h
index ab26d1e..8bd3468 100644
--- a/include/arch/x86_64/arch.h
+++ b/include/arch/x86_64/arch.h
@@ -34,11 +34,15 @@
#define SPI3_BASE (load_fake_spi3__())
#define MPU_BASE (load_fake_mpu__())
+#define SYSCFG_BASE (load_fake_syscfg__())
+#define EXTI_BASE (load_fake_exti__())
+
+#define TIM2_BASE (load_fake_exti__())
// Pretend there's a data segement at the start of SRAM1 for more accurate
// testing.
#define GHOST_DATA_SEGMENT_SIZE 1200
#define HEAP_START (*(((unsigned char*)SRAM1_BASE) + GHOST_DATA_SEGMENT_SIZE))
-#define HEAP_STOP (*(&HEAP_START + 16384 - GHOST_DATA_SEGMENT_SIZE))
+#define HEAP_STOP (*(&HEAP_START + 49152 - GHOST_DATA_SEGMENT_SIZE))
#endif /* ARCH_H_ */
diff --git a/include/drv/ir/control.h b/include/drv/ir/control.h
new file mode 100644
index 0000000..11e3604
--- /dev/null
+++ b/include/drv/ir/control.h
@@ -0,0 +1,14 @@
+#ifndef _DRV_IR_CONTROL_H_
+#define _DRV_IR_CONTROL_H_
+
+#include "kern/common.h"
+
+#define add_ir_code_callback(code, fn, closure) \
+ add_ir_code_callback_(code, (void (*)(uint32_t, void*))(fn), closure)
+
+void add_ir_code_callback_(
+ uint32_t code, void (*fn)(uint32_t code, void* closure), void* closure);
+
+void enable_ir_control();
+
+#endif /* _DRV_IR_CONTROL_H_ */
diff --git a/include/drv/ir/ir.h b/include/drv/ir/ir.h
new file mode 100644
index 0000000..0a747e8
--- /dev/null
+++ b/include/drv/ir/ir.h
@@ -0,0 +1,34 @@
+#ifndef _DRV_IR_IR_H_
+#define _DRV_IR_IR_H_
+
+#include "kern/common.h"
+
+typedef struct {
+ uint16_t ts[64]; /* Timestamps. Microseconds. */
+ uint8_t n;
+} ir_code_t;
+
+typedef struct {
+ uint32_t c;
+} decoded_ir;
+
+/*
+ * Attempt to decode the provided ir code.
+ *
+ * The generic ir is:
+ *
+ * The frame starts out as really-long, half(ish) as long, short. If
+ * this basic frame header does not exist decoding cannot continue.
+ *
+ * A short code followed by a short code = 0
+ * A long code followed by a short code = 1
+ *
+ * returns non-zero on successful decode.
+ */
+int ir_generic_decode(const ir_code_t* code, uint32_t* out);
+
+void add_ir_callback(void(*callback)(const ir_code_t*));
+
+void ir_begin_listen();
+
+#endif /* _DRV_IR_IR_H_ */
diff --git a/include/drv/ir/lg_remote_codes.h b/include/drv/ir/lg_remote_codes.h
new file mode 100644
index 0000000..77cbb45
--- /dev/null
+++ b/include/drv/ir/lg_remote_codes.h
@@ -0,0 +1,17 @@
+#ifndef _DRV_IR_LG_REMOTE_CODES_H_
+#define _DRV_IR_LG_REMOTE_CODES_H_
+
+#define RC_HIGH 0x08e76800
+#define RC_TEMP_UP 0x08e7a800
+#define RC_DRY 0x08e72800
+#define RC_LOW 0x08e77000
+#define RC_TEMP_DOWN 0x08e7b000
+#define RC_COOL 0x08e73000
+#define RC_CONTINUOUS 0x08e75000
+#define RC_FAN 0x08e71000
+#define RC_SLEEP 0x08e76000
+#define RC_UNITS 0x08e72000
+#define RC_TIMER 0x08e78000
+#define RC_POWER 0x08e70080
+
+#endif /* _DRV_IR_LG_REMOTE_CODES_H_ */
diff --git a/include/drv/ws2812B/ws2812b.h b/include/drv/ws2812B/ws2812b.h
index 3189907..d7137a1 100644
--- a/include/drv/ws2812B/ws2812b.h
+++ b/include/drv/ws2812B/ws2812b.h
@@ -30,7 +30,4 @@ void ws2812b_write_rgb_arr_sync(ws2812b_t* drv, rgb_t* arr, size_t arr_len);
void ws2812b_delete(ws2812b_t* drv);
-/* Returns round(sin(2πn / 256) * 127.5 + 127.5). */
-uint8_t byte_sin(uint8_t n);
-
#endif /* WS2812B_H_ */
diff --git a/include/kern/common.h b/include/kern/common.h
index 021229b..0acc9d3 100644
--- a/include/kern/common.h
+++ b/include/kern/common.h
@@ -39,6 +39,7 @@ typedef enum { ENDIANNESS_LITTLE, ENDIANNESS_BIG } endianness_t;
#define ptr2reg(ptr) ((uint32_t)(ptrdiff_t)(ptr))
typedef __IO uint32_t bits_t;
+typedef uint16_t msize_t;
#define regset(reg, mask, val) ((reg) = ((reg) & ~mask) | (val << CTZ(mask)))
diff --git a/include/kern/exti/exti_manager.h b/include/kern/exti/exti_manager.h
new file mode 100644
index 0000000..aa39b4f
--- /dev/null
+++ b/include/kern/exti/exti_manager.h
@@ -0,0 +1,32 @@
+#ifndef _KERN_EXTI_EXTI_MANAGER_H_
+#define _KERN_EXTI_EXTI_MANAGER_H_
+
+#include "kern/common.h"
+#include "kern/gpio/gpio_manager.h"
+#include "shared/linked_list.h"
+
+#define EXTI_ERROR_ALREADY_IN_USE 1
+
+struct EXTI_HANDLE;
+
+
+typedef struct {
+ void (*fn)(struct EXTI_HANDLE, void*);
+ void* closure;
+} exti_callback_t;
+
+LINKED_LIST_DECL(exti_callback_t);
+typedef struct EXTI_HANDLE {
+ uint8_t id;
+ linked_list_t(exti_callback_t)* callbacks;
+} exti_handle_t;
+
+exti_handle_t* enable_exti_for_gpio(gpio_pin_t gpio_pin, int* ec);
+
+exti_handle_t* exti_add_callback(exti_handle_t*, exti_callback_t* callback);
+
+gpio_pin_t exti_gpio_pin(exti_handle_t* handle);
+
+
+
+#endif /* _KERN_EXTI_EXTI_MANAGER_H_ */
diff --git a/include/kern/gpio/gpio_manager.h b/include/kern/gpio/gpio_manager.h
index 922a423..cb61059 100644
--- a/include/kern/gpio/gpio_manager.h
+++ b/include/kern/gpio/gpio_manager.h
@@ -1,8 +1,8 @@
#ifndef KERN_GPIO_GPIO_MANAGE_H_
#define KERN_GPIO_GPIO_MANAGE_H_
-#include "kern/common.h"
#include "arch/stm32l4xxx/peripherals/gpio.h"
+#include "kern/common.h"
#define GPIO_ERROR_IN_USE 1
#define GPIO_ERROR_INVALID_PIN_FOR_ALTERNATE_FUNCTION 2
@@ -10,8 +10,7 @@
typedef enum {
/* Creates vaules GPIO_PIN_<port><num> i.e. GPIO_PIN_A0 */
-#define PORT(p, pn) \
- GPIO_PIN_P ## p ## pn,
+#define PORT(p, pn) GPIO_PIN_P##p##pn,
#include "arch/stm32l4xxx/peripherals/tables/stm32l432xx/gpio/port_table.inc"
#undef PORT
@@ -20,16 +19,14 @@ typedef enum {
/* Alternate functions. */
typedef enum {
-#define AFN(fn, ...) \
- GPIO_ALTERNATE_FUNCTION_ ## fn,
+#define AFN(fn, ...) GPIO_ALTERNATE_FUNCTION_##fn,
#include "arch/stm32l4xxx/peripherals/tables/stm32l432xx/gpio/afn_table.inc"
#undef AFN
GPIO_ALTERNATE_FUNCTION_EVENTOUT,
} gpio_alternate_function_t;
#define gpio_pin_for_alternate_function(af) ((af) / 16)
-#define gpio_pin_out_of_range(pin) \
- ((pin) < 0 || (pin) >= N_GPIO_PINS)
+#define gpio_pin_out_of_range(pin) ((pin) < 0 || (pin) >= N_GPIO_PINS)
typedef enum {
GPIO_PORT_A,
@@ -83,41 +80,43 @@ typedef enum {
inline static gpio_port_t get_port_for_pin(gpio_pin_t pin)
{
switch (pin) {
-#define PORT(p, pn) \
- case GPIO_PIN_P ## p ## pn: return GPIO_PORT_ ## p;
+#define PORT(p, pn) \
+ case GPIO_PIN_P##p##pn: \
+ return GPIO_PORT_##p;
#include "arch/stm32l4xxx/peripherals/tables/stm32l432xx/gpio/port_table.inc"
#undef PORT
- case N_GPIO_PINS: return N_GPIO_PORTS;
+ case N_GPIO_PINS:
+ return N_GPIO_PORTS;
}
/* Should be unreachable. */
}
-#define DEFAULT_GPIO_OPTS_OUTPUT \
- (gpio_pin_opts_t) { \
- .mode = GPIO_MODE_OUTPUT, \
- .pull_dir = GPIO_PULL_DIR_DOWN, \
- .output_opts.speed = GPIO_OUTPUT_SPEED_MEDIUM, \
- .output_opts.type = GPIO_OUTPUT_TYPE_PUSH_PULL, \
+#define DEFAULT_GPIO_OPTS_OUTPUT \
+ (gpio_pin_opts_t) \
+ { \
+ .mode = GPIO_MODE_OUTPUT, .pull_dir = GPIO_PULL_DIR_DOWN, \
+ .output_opts.speed = GPIO_OUTPUT_SPEED_MEDIUM, \
+ .output_opts.type = GPIO_OUTPUT_TYPE_PUSH_PULL, \
}
-#define DEFAULT_GPIO_OPTS_INPUT \
- (gpio_pin_opts_t) { \
- .mode = GPIO_MODE_OUTPUT, \
- .pull_dir = GPIO_PULL_DIR_DOWN, \
+#define DEFAULT_GPIO_OPTS_INPUT \
+ (gpio_pin_opts_t) \
+ { \
+ .mode = GPIO_MODE_INPUT, .pull_dir = GPIO_PULL_DIR_DOWN, \
}
typedef struct {
- gpio_mode_t mode;
- gpio_pull_dir_t pull_dir;
+ gpio_mode_t mode;
+ gpio_pull_dir_t pull_dir;
union {
struct {
} input_opts;
struct {
- gpio_output_speed_t speed;
- gpio_output_type_t type;
+ gpio_output_speed_t speed;
+ gpio_output_type_t type;
} output_opts;
struct {
@@ -171,9 +170,7 @@ gpio_reserved_pin_t reserve_gpio_pin(
*
*/
gpio_reserved_pin_t gpio_enable_alternate_function(
- gpio_alternate_function_t fn,
- gpio_pin_t hint,
- int* error_out);
+ gpio_alternate_function_t fn, gpio_pin_t hint, int* error_out);
/*
* Releases the GPIO pin so it can be reserved again in the future.
@@ -184,4 +181,9 @@ gpio_reserved_pin_t gpio_enable_alternate_function(
*/
void release_gpio_pin(gpio_reserved_pin_t gpio_pin);
+/*
+ * Returns whether the GPIO pin has high or low input.
+ */
+bool get_gpio_pin_input_state(gpio_reserved_pin_t pin);
+
#endif /* KERN_GPIO_GPIO_MANAGE_H_ */
diff --git a/include/kern/mem.h b/include/kern/mem.h
index 20b09bb..bc6c4e2 100644
--- a/include/kern/mem.h
+++ b/include/kern/mem.h
@@ -22,6 +22,8 @@ int debug_kalloc_assert_consistency(char* error, size_t len);
void debug_print_blocks();
+int debug_is_heap_empty();
+
#endif
diff --git a/include/shared/array_list.h b/include/shared/array_list.h
new file mode 100644
index 0000000..8b942ad
--- /dev/null
+++ b/include/shared/array_list.h
@@ -0,0 +1,105 @@
+#ifndef SHARED_ARRAY_LIST_H_
+#define SHARED_ARRAY_LIST_H_
+
+#include "kern/common.h"
+#include "kern/mem.h"
+
+#define ALLOC kalloc
+#define FREE kfree
+
+#define array_list_t(type) array_list_##type##_t
+#define array_list_new(type) array_list_##type##_new
+#define array_list_length(type) array_list_##type##_length
+#define array_list_reserve(type) array_list_##type##_reserve
+#define array_list_add(type) array_list_##type##_add
+#define array_list_remove(type) array_list_##type##remove
+#define array_list_ref(type) array_list_##type##_get
+#define array_list_free(type) array_list_##type##_free
+
+#define array_list_foreach(ar, val) \
+ typeof((ar)->elements[0]) val; \
+ if ((ar)->size > 0) val = (ar)->elements[0]; \
+ for (size_t _idx_ = 0; _idx_ < (ar)->size; \
+ val = (++_idx_) >= (ar)->size ? val : (ar)->elements[_idx_])
+
+#define ARRAY_LIST_DECL(type) \
+ typedef struct { \
+ size_t reserved; \
+ size_t size; \
+ type* elements; \
+ } array_list_t(type); \
+ size_t array_list_length(type)(const array_list_t(type)*); \
+ array_list_t(type) * array_list_new(type)(); \
+ bool array_list_reserve(type)(array_list_t(type)*, size_t len); \
+ bool array_list_add(type)(array_list_t(type)*, type val); \
+ bool array_list_remove(type)(array_list_t(type)*, size_t idx, type * out); \
+ type* array_list_ref(type)(array_list_t(type)*, size_t idx); \
+ void array_list_free(type)(array_list_t(type)*);
+
+#define ARRAY_LIST_IMPL(type) \
+ array_list_t(type) * array_list_new(type)() \
+ { \
+ array_list_t(type)* ret = ALLOC(sizeof(array_list_t(type))); \
+ ret->size = 0; \
+ ret->reserved = 0; \
+ ret->elements = NULL; \
+ return ret; \
+ } \
+ size_t array_list_length(type)(const array_list_t(type) * lst) \
+ { \
+ return lst->size; \
+ } \
+ bool array_list_reserve(type)(array_list_t(type) * lst, size_t newlen) \
+ { \
+ if (lst->reserved < newlen) { \
+ type* new_arr = ALLOC(sizeof(type) * newlen); \
+ if (!new_arr) { \
+ return 0; \
+ } \
+ for (size_t i = 0; i < lst->size; ++i) { \
+ new_arr[i] = lst->elements[i]; \
+ } \
+ FREE(lst->elements); \
+ lst->elements = new_arr; \
+ lst->reserved = newlen; \
+ } \
+ return 1; \
+ } \
+ bool array_list_add(type)(array_list_t(type) * lst, type v) \
+ { \
+ if (lst->size == lst->reserved) { \
+ if (!array_list_reserve(type)( \
+ lst, lst->reserved == 0 ? 4 : lst->reserved * 2)) { \
+ return 0; \
+ } \
+ } \
+ lst->elements[lst->size++] = v; \
+ return 1; \
+ } \
+ bool array_list_remove(type)( \
+ array_list_t(type) * lst, size_t idx, type * out) \
+ { \
+ if (idx >= lst->size) { \
+ return 0; \
+ } \
+ if (out) *out = lst->elements[idx]; \
+ for (size_t i = idx; i < lst->size - 1; ++i) { \
+ lst->elements[i] = lst->elements[i + 1]; \
+ } \
+ lst->size--; \
+ return 1; \
+ } \
+ type* array_list_ref(type)(array_list_t(type) * lst, size_t idx) \
+ { \
+ if (idx >= lst->size) { \
+ return NULL; \
+ } \
+ return &lst->elements[idx]; \
+ } \
+ void array_list_free(type)(array_list_t(type) * lst) \
+ { \
+ FREE(lst->elements); \
+ FREE(lst); \
+ }
+
+#endif /* SHARED_ARRAY_LIST_H_ */
diff --git a/include/shared/avl_tree.h b/include/shared/avl_tree.h
new file mode 100644
index 0000000..2b174d3
--- /dev/null
+++ b/include/shared/avl_tree.h
@@ -0,0 +1,270 @@
+#ifndef _SHARED_AVL_TREE_H_
+#define _SHARED_AVL_TREE_H_
+
+#define ALLOC kalloc
+#define FREE kfree
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "kern/common.h"
+#include "kern/mem.h"
+#include "shared/stdmacro.h"
+
+#define get_height(n) ((n) == NULL ? 0 : (n)->height)
+#define max(a, b) ((a) < (b) ? (b) : (a))
+#define reset_height(node) \
+ (node)->height = \
+ (max(get_height((node)->right), get_height((node)->left)) + 1)
+
+#define avl_tree_t(T) CONCAT(avl_tree__, T)
+#define avl_tree_node_t(T) CONCAT(avl_tree_node__, T)
+
+#define avl_tree_new(T) CONCAT(avl_tree_new__, T)
+#define avl_tree_free(T) CONCAT(avl_tree_free__, T)
+#define avl_tree_size(T) CONCAT(avl_tree_size__, T)
+#define avl_tree_insert(T) CONCAT(avl_tree_insert__, T)
+#define avl_tree_find(T) CONCAT(avl_tree_find__, T)
+#define avl_tree_erase(T) CONCAT(avl_tree_erase__, T)
+#define avl_tree_height(T) CONCAT(avl_tree_height__, T)
+
+#define null_dtor(a)
+
+#define AVL_TREE_DECL(T) \
+ typedef struct CONCAT(AVL_TREE_NODE_, T) { \
+ T value; \
+ size_t height; \
+ struct CONCAT(AVL_TREE_NODE_, T) * left; \
+ struct CONCAT(AVL_TREE_NODE_, T) * right; \
+ } avl_tree_node_t(T); \
+ \
+ typedef struct { \
+ avl_tree_node_t(T) * root; \
+ } avl_tree_t(T); \
+ \
+ size_t avl_tree_height(T)(avl_tree_t(T) * tree); \
+ avl_tree_t(T) * avl_tree_new(T)(); \
+ void avl_tree_free(T)(avl_tree_t(T) * tree); \
+ size_t avl_tree_size(T)(const avl_tree_t(T) * avl_tree); \
+ T* avl_tree_insert(T)(avl_tree_t(T) * avl_tree, T value); \
+ T* avl_tree_find(T)(const avl_tree_t(T)*, T val); \
+ bool avl_tree_erase(T)(avl_tree_t(T) * tree, T val, T * out);
+
+#define AVL_TREE_IMPL(T, CMP, DTOR) \
+ avl_tree_t(T) * avl_tree_new(T)() \
+ { \
+ avl_tree_t(T)* ret = ALLOC(sizeof(avl_tree_t(T))); \
+ ret->root = NULL; \
+ return ret; \
+ } \
+ static void CONCAT(avl_loose_free__, T)(avl_tree_node_t(T) * node) \
+ { \
+ if (!node) return; \
+ CONCAT(avl_loose_free__, T)(node->right); \
+ CONCAT(avl_loose_free__, T)(node->left); \
+ DTOR(node->value); \
+ FREE(node); \
+ } \
+ void avl_tree_free(T)(avl_tree_t(T) * tree) \
+ { \
+ CONCAT(avl_loose_free__, T)(tree->root); \
+ FREE(tree); \
+ } \
+ static inline size_t CONCAT( \
+ loose_size__, T)(const avl_tree_node_t(T) * node) \
+ { \
+ if (!node) return 0; \
+ return 1 + CONCAT(loose_size__, T)(node->left) + \
+ CONCAT(loose_size__, T)(node->right); \
+ } \
+ size_t avl_tree_size(T)(const avl_tree_t(T) * tree) \
+ { \
+ return CONCAT(loose_size__, T)(tree->root); \
+ } \
+ static int CONCAT(balance_factor, T)(avl_tree_node_t(T) * node) \
+ { \
+ return get_height(node->left) - get_height(node->right); \
+ } \
+ static avl_tree_node_t(T) * CONCAT(ll_rotate, T)(avl_tree_node_t(T) * node) \
+ { \
+ avl_tree_node_t(T)* child = node->left; \
+ node->left = child->right; \
+ reset_height(node); \
+ child->right = node; \
+ reset_height(child); \
+ return child; \
+ } \
+ static avl_tree_node_t(T) * CONCAT(rr_rotate, T)(avl_tree_node_t(T) * node) \
+ { \
+ avl_tree_node_t(T)* child = node->right; \
+ node->right = child->left; \
+ reset_height(node); \
+ child->left = node; \
+ reset_height(child); \
+ return child; \
+ } \
+ static avl_tree_node_t(T) * CONCAT(rl_rotate, T)(avl_tree_node_t(T) * node) \
+ { \
+ avl_tree_node_t(T)* child = node->right; \
+ node->right = CONCAT(ll_rotate, T)(child); \
+ reset_height(node); \
+ return CONCAT(rr_rotate, T)(node); \
+ } \
+ static avl_tree_node_t(T) * CONCAT(lr_rotate, T)(avl_tree_node_t(T) * node) \
+ { \
+ avl_tree_node_t(T)* child = node->left; \
+ node->left = CONCAT(rr_rotate, T)(child); \
+ reset_height(node); \
+ return CONCAT(ll_rotate, T)(node); \
+ } \
+ static avl_tree_node_t(T) * \
+ CONCAT(avl_tree_balance_, T)(avl_tree_node_t(T) * node) \
+ { \
+ int d = CONCAT(balance_factor, T)(node); \
+ if (d > 1) { \
+ if (CONCAT(balance_factor, T)(node->left) > 0) { \
+ return CONCAT(ll_rotate, T)(node); \
+ } else { \
+ return CONCAT(lr_rotate, T)(node); \
+ } \
+ } else if (d < -1) { \
+ if (CONCAT(balance_factor, T)(node->right) > 0) { \
+ return CONCAT(rl_rotate, T)(node); \
+ } else { \
+ return CONCAT(rr_rotate, T)(node); \
+ } \
+ } \
+ \
+ return node; \
+ } \
+ static avl_tree_node_t(T) * \
+ CONCAT(avl_tree_loose_insert_, T)( \
+ avl_tree_node_t(T) * node, T value, T * *ptr_out) \
+ { \
+ if (!node) { \
+ node = ALLOC(sizeof(avl_tree_node_t(T))); \
+ assert(node); \
+ node->left = NULL; \
+ node->right = NULL; \
+ node->value = value; \
+ node->height = 1; \
+ *ptr_out = &node->value; \
+ } else { \
+ typeof(CMP(node->value, value)) cmp = CMP(node->value, value); \
+ if (cmp < 0) { \
+ node->left = \
+ CONCAT(avl_tree_loose_insert_, T)(node->left, value, ptr_out); \
+ reset_height(node); \
+ node = CONCAT(avl_tree_balance_, T)(node); \
+ } else if (cmp > 0) { \
+ node->right = \
+ CONCAT(avl_tree_loose_insert_, T)(node->right, value, ptr_out); \
+ reset_height(node); \
+ node = CONCAT(avl_tree_balance_, T)(node); \
+ } \
+ } \
+ return node; \
+ } \
+ T* avl_tree_insert(T)(avl_tree_t(T) * tree, T value) \
+ { \
+ T* ret; \
+ tree->root = CONCAT(avl_tree_loose_insert_, T)(tree->root, value, &ret); \
+ return ret; \
+ } \
+ size_t avl_tree_height(T)(avl_tree_t(T) * tree) \
+ { \
+ if (!tree) return 0; \
+ return get_height(tree->root); \
+ } \
+ static T* CONCAT(loose_avl_tree_find, T)(avl_tree_node_t(T) * node, T value) \
+ { \
+ if (!node) return NULL; \
+ \
+ typeof(CMP(node->value, value)) cmp = CMP(node->value, value); \
+ if (cmp > 0) { \
+ return CONCAT(loose_avl_tree_find, T)(node->right, value); \
+ } else if (cmp < 0) { \
+ return CONCAT(loose_avl_tree_find, T)(node->left, value); \
+ } \
+ return &node->value; \
+ } \
+ T* avl_tree_find(T)(const avl_tree_t(T) * tree, T val) \
+ { \
+ if (!tree) return NULL; \
+ return CONCAT(loose_avl_tree_find, T)(tree->root, val); \
+ } \
+ static avl_tree_node_t(T) * \
+ CONCAT(pluck_left, T)(avl_tree_node_t(T) * node, T * into) \
+ { \
+ if (node->left) { \
+ node->left = CONCAT(pluck_left, T)(node->left, into); \
+ reset_height(node); \
+ return CONCAT(avl_tree_balance_, T)(node); \
+ } else { \
+ *into = node->value; \
+ FREE(node); \
+ return node->right; \
+ } \
+ } \
+ static avl_tree_node_t(T) * \
+ CONCAT(pluck_right, T)(avl_tree_node_t(T) * node, T * into) \
+ { \
+ if (node->right) { \
+ node->right = CONCAT(pluck_right, T)(node->right, into); \
+ reset_height(node); \
+ return CONCAT(avl_tree_balance_, T)(node); \
+ } else { \
+ *into = node->value; \
+ FREE(node); \
+ return node->left; \
+ } \
+ } \
+ avl_tree_node_t(T) * \
+ CONCAT(loose_avl_tree_erase, T)( \
+ avl_tree_node_t(T) * node, T value, bool* out, T* erased) \
+ { \
+ if (!node) { \
+ *out = 0; \
+ return NULL; \
+ } \
+ typeof(CMP(node->value, value)) cmp = CMP(node->value, value); \
+ if (cmp == 0) { \
+ if (erased) *erased = node->value; \
+ *out = 1; \
+ if (!node->right && !node->left) { \
+ FREE(node); \
+ return NULL; \
+ } \
+ if (get_height(node->right) > get_height(node->left)) { \
+ node->right = CONCAT(pluck_left, T)(node->right, &node->value); \
+ reset_height(node); \
+ node = CONCAT(avl_tree_balance_, T)(node); \
+ return node; \
+ } \
+ node->left = CONCAT(pluck_right, T)(node->left, &node->value); \
+ reset_height(node); \
+ node = CONCAT(avl_tree_balance_, T)(node); \
+ return node; \
+ } else if (cmp < 0) { \
+ node->left = \
+ CONCAT(loose_avl_tree_erase, T)(node->left, value, out, erased); \
+ reset_height(node); \
+ node = CONCAT(avl_tree_balance_, T)(node); \
+ } else { \
+ node->right = \
+ CONCAT(loose_avl_tree_erase, T)(node->right, value, out, erased); \
+ reset_height(node); \
+ node = CONCAT(avl_tree_balance_, T)(node); \
+ } \
+ return node; \
+ } \
+ bool avl_tree_erase(T)(avl_tree_t(T) * tree, T val, T * erased) \
+ { \
+ if (!tree) return 0; \
+ bool out; \
+ tree->root = \
+ CONCAT(loose_avl_tree_erase, T)(tree->root, val, &out, erased); \
+ return out; \
+ }
+
+#endif /* _SHARED_AVL_TREE_H_ */
diff --git a/include/shared/avl_tree.h.gch b/include/shared/avl_tree.h.gch
new file mode 100644
index 0000000..78fc043
--- /dev/null
+++ b/include/shared/avl_tree.h.gch
Binary files differ
diff --git a/include/shared/linked_list.h b/include/shared/linked_list.h
index 3f8e075..ec72377 100644
--- a/include/shared/linked_list.h
+++ b/include/shared/linked_list.h
@@ -1,15 +1,21 @@
#ifndef SHARED_LINKED_LIST_H_
#define SHARED_LINKED_LIST_H_
-#define linked_list_t(type) linked_list_##type##_t
-#define linked_list_node_t(type) linked_list_node_##type##_t
-#define linked_list_push_back(type) linked_list_push_back_##type
-#define linked_list_push_front(type) linked_list_push_front_##type
-#define linked_list_pop_front(type) linked_list_pop_front_##type
-#define linked_list_pop_back(type) linked_list_pop_back_##type
-#define linked_list_front(type) linked_list_front_##type
-#define linked_list_back(type) linked_list_back_##type
-#define linked_list_length(type) linked_list_length_##type
+#include "kern/common.h"
+
+#define linked_list_t(T) linked_list___##T##___t
+#define linked_list_node_t(T) linked_list_node___##T##___t
+#define linked_list_itr_t(T) linked_list_node_itr___##T##___t
+#define linked_list_push_back(T) linked_list_push_back___##T
+#define linked_list_push_front(T) linked_list_push_front___##T
+#define linked_list_pop_front(T) linked_list_pop_front___##T
+#define linked_list_pop_back(T) linked_list_pop_back___##T
+#define linked_list_front(T) linked_list_front___##T
+#define linked_list_back(T) linked_list_back___##T
+#define linked_list_length(T) linked_list_length___##T
+#define linked_list_find_match(T) linked_list_find_match___##T
+#define linked_list_remove_first_match(T) linked_list_remove_first_match___##T
+#define linked_list_remove(T) linked_list_remove___##T
#define LINKED_LIST_INIT \
{ \
@@ -24,108 +30,171 @@
#define NO_ATTRIBUTE
-#define LINKED_LIST_DECL(type) LINKED_LIST_INTERNAL_DECL(type, NO_ATTRIBUTE)
+#define LINKED_LIST_DECL(T) LINKED_LIST_INTERNAL_DECL(T, NO_ATTRIBUTE)
-#define LINKED_LIST_STATIC_DECL(type) LINKED_LIST_INTERNAL_DECL(type, static)
+#define LINKED_LIST_STATIC_DECL(T) LINKED_LIST_INTERNAL_DECL(T, static inline)
-#define LINKED_LIST_INTERNAL_DECL(type, attr) \
- typedef struct LINKED_LIST_NODE_##type { \
- type value; \
- struct LINKED_LIST_NODE_##type* next; \
- } linked_list_node_t(type); \
+#define LINKED_LIST_INTERNAL_DECL(T, attr) \
+ typedef struct LINKED_LIST_NODE_##T { \
+ T value; \
+ struct LINKED_LIST_NODE_##T* next; \
+ } linked_list_node_t(T); \
+ \
+ typedef struct { \
+ linked_list_node_t(T) * prev; \
+ linked_list_node_t(T) * cur; \
+ } linked_list_itr_t(T); \
\
typedef struct { \
- linked_list_node_t(type) * head; \
- } linked_list_t(type); \
+ linked_list_node_t(T) * head; \
+ } linked_list_t(T); \
\
- attr void linked_list_push_back(type)(linked_list_t(type) * ll, type value); \
- attr void linked_list_push_front(type)( \
- linked_list_t(type) * ll, type value); \
- attr void linked_list_pop_back(type)(linked_list_t(type) * ll); \
- attr void linked_list_pop_front(type)(linked_list_t(type) * ll); \
- attr type* linked_list_front(type)(linked_list_t(type) * ll); \
- attr type* linked_list_back(type)(linked_list_t(type) * ll);
+ attr void linked_list_push_back(T)(linked_list_t(T) * ll, T value); \
+ attr void linked_list_push_front(T)(linked_list_t(T) * ll, T value); \
+ attr void linked_list_pop_back(T)(linked_list_t(T) * ll); \
+ attr void linked_list_pop_front(T)(linked_list_t(T) * ll); \
+ attr size_t linked_list_length(T)(const linked_list_t(T) * ll); \
+ attr T* linked_list_front(T)(const linked_list_t(T) * ll); \
+ attr bool linked_list_find_match(T)( \
+ const linked_list_t(T) * ll, \
+ linked_list_itr_t(T) * itr_out, \
+ bool (*match)(T, void*), \
+ void* closure); \
+ attr bool linked_list_remove_first_match(T)( \
+ linked_list_t(T) * ll, T * out, bool (*match)(T, void*), void* closure); \
+ attr T* linked_list_back(T)(const linked_list_t(T) * ll);
-#define LINKED_LIST_IMPL(type) LINKED_LIST_INTERNAL_IMPL(type, NO_ATTRIBUTE)
+#define LINKED_LIST_IMPL(T) LINKED_LIST_INTERNAL_IMPL(T, NO_ATTRIBUTE)
-#define LINKED_LIST_STATIC_IMPL(type) LINKED_LIST_INTERNAL_IMPL(type, static)
+#define LINKED_LIST_STATIC_IMPL(T) LINKED_LIST_INTERNAL_IMPL(T, static inline)
-#define LINKED_LIST_INTERNAL_IMPL(type, attr) \
- attr void linked_list_push_back(type)(linked_list_t(type) * ll, type value) \
- { \
- if (!ll->head) { \
- ll->head = kalloc(sizeof(linked_list_node_t(type))); \
- ll->head->value = value; \
- ll->head->next = NULL; \
- } else { \
- linked_list_node_t(type)* c = ll->head; \
- while (c->next) c = c->next; \
- c->next = kalloc(sizeof(linked_list_node_t(type))); \
- c->next->next = NULL; \
- c->next->value = value; \
- } \
- } \
- attr void linked_list_push_front(type)(linked_list_t(type) * ll, type value) \
- { \
- if (!ll->head) { \
- ll->head = kalloc(sizeof(linked_list_node_t(type))); \
- ll->head->value = value; \
- ll->head->next = NULL; \
- } else { \
- linked_list_node_t(type)* node = \
- kalloc(sizeof(linked_list_node_t(type))); \
- node->next = ll->head; \
- node->value = value; \
- ll->head = node; \
- } \
- } \
- attr void linked_list_pop_back(type)(linked_list_t(type) * ll) \
- { \
- if (!ll->head) { \
- return; \
- } \
- if (!ll->head->next) { \
- kfree(ll->head); \
- ll->head = NULL; \
- return; \
- } \
- linked_list_node_t(type)* c = ll->head; \
- while (c->next->next) c = c->next; \
- kfree(c->next); \
- c->next = NULL; \
- return; \
- } \
- attr void linked_list_pop_front(type)(linked_list_t(type) * ll) \
- { \
- if (!ll->head) { \
- return; \
- } \
- linked_list_node_t(type)* node = ll->head; \
- ll->head = node->next; \
- kfree(node); \
- } \
- attr type* linked_list_front(type)(linked_list_t(type) * ll) \
- { \
- if (!ll->head) { \
- return NULL; \
- } \
- return &ll->head->value; \
- } \
- attr type* linked_list_back(type)(linked_list_t(type) * ll) \
- { \
- if (!ll->head) { \
- return NULL; \
- } \
- linked_list_node_t(type)* cur = ll->head; \
- while (cur->next) cur = cur->next; \
- return &cur->value; \
- } \
- attr size_t linked_list_length(type)(linked_list_t(type) * ll) \
- { \
- linked_list_node_t(type) * cur; \
- size_t ret = 0; \
- for (cur = ll->head; cur; cur = cur->next) ++ret; \
- return ret; \
- }
+#define PRAGMA(x) _Pragma(#x)
+#define LINKED_LIST_INTERNAL_IMPL(T, attr) \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-Wunused-function\"") \
+ attr void linked_list_push_back(T)(linked_list_t(T) * ll, T value) \
+ { \
+ if (!ll->head) { \
+ ll->head = kalloc(sizeof(linked_list_node_t(T))); \
+ ll->head->value = value; \
+ ll->head->next = NULL; \
+ } else { \
+ linked_list_node_t(T)* c = ll->head; \
+ while (c->next) c = c->next; \
+ c->next = kalloc(sizeof(linked_list_node_t(T))); \
+ c->next->next = NULL; \
+ c->next->value = value; \
+ } \
+ } \
+ attr void linked_list_push_front(T)(linked_list_t(T) * ll, T value) \
+ { \
+ if (!ll->head) { \
+ ll->head = kalloc(sizeof(linked_list_node_t(T))); \
+ ll->head->value = value; \
+ ll->head->next = NULL; \
+ } else { \
+ linked_list_node_t(T)* node = kalloc(sizeof(linked_list_node_t(T))); \
+ node->next = ll->head; \
+ node->value = value; \
+ ll->head = node; \
+ } \
+ } \
+ attr void linked_list_pop_back(T)(linked_list_t(T) * ll) \
+ { \
+ if (!ll->head) { \
+ return; \
+ } \
+ if (!ll->head->next) { \
+ kfree(ll->head); \
+ ll->head = NULL; \
+ return; \
+ } \
+ linked_list_node_t(T)* c = ll->head; \
+ while (c->next->next) c = c->next; \
+ kfree(c->next); \
+ c->next = NULL; \
+ return; \
+ } \
+ attr void linked_list_pop_front(T)(linked_list_t(T) * ll) \
+ { \
+ if (!ll->head) { \
+ return; \
+ } \
+ linked_list_node_t(T)* node = ll->head; \
+ ll->head = node->next; \
+ kfree(node); \
+ } \
+ attr T* linked_list_front(T)(const linked_list_t(T) * ll) \
+ { \
+ if (!ll->head) { \
+ return NULL; \
+ } \
+ return &ll->head->value; \
+ } \
+ attr T* linked_list_back(T)(const linked_list_t(T) * ll) \
+ { \
+ if (!ll->head) { \
+ return NULL; \
+ } \
+ linked_list_node_t(T)* cur = ll->head; \
+ while (cur->next) cur = cur->next; \
+ return &cur->value; \
+ } \
+ attr size_t linked_list_length(T)(const linked_list_t(T) * ll) \
+ { \
+ linked_list_node_t(T) * cur; \
+ size_t ret = 0; \
+ for (cur = ll->head; cur; cur = cur->next) ++ret; \
+ return ret; \
+ } \
+ attr bool linked_list_find_match(T)( \
+ const linked_list_t(T) * ll, \
+ linked_list_itr_t(T) * itr_out, \
+ bool (*match)(T, void*), \
+ void* closure) \
+ { \
+ linked_list_node_t(T)* prev = NULL; \
+ linked_list_node_t(T)* cur = NULL; \
+ if (!ll || !ll->head) { \
+ return 0; \
+ } \
+ for (cur = ll->head; cur != NULL; cur = (prev = cur)->next) { \
+ if (match(cur->value, closure)) { \
+ itr_out->prev = prev; \
+ itr_out->cur = cur; \
+ return 1; \
+ } \
+ } \
+ return 0; \
+ } \
+ attr bool linked_list_remove_first_match(T)( \
+ linked_list_t(T) * ll, T * out, bool (*match)(T, void*), void* closure) \
+ { \
+ linked_list_itr_t(T) ctx; \
+ if (!linked_list_find_match(T)(ll, &ctx, match, closure)) { \
+ return 0; \
+ } \
+ if (out) *out = ctx.cur->value; \
+ if (!ctx.prev) { \
+ ll->head = ctx.cur->next; \
+ } else { \
+ ctx.prev->next = ctx.cur->next; \
+ } \
+ kfree(ctx.cur); \
+ return 1; \
+ } \
+ int memcmp(const void* s1, const void* s2, size_t n); \
+ static inline bool linked_list___##T##___eq(T elem, void* other) \
+ { \
+ return memcmp(&elem, other, sizeof(T)) == 0; \
+ } \
+ attr bool linked_list_remove(T)(linked_list_t(T) * ll, T val) \
+ { \
+ bool (*fn)(T, void*) = &linked_list___##T##___eq; \
+ bool ret = 0; \
+ while (linked_list_remove_first_match(T)(ll, NULL, fn, &val)) ret = 1; \
+ return ret; \
+ } \
+ _Pragma("GCC diagnostic pop")
#endif /* SHARED_LINKED_LIST_H_ */
diff --git a/include/shared/map.h b/include/shared/map.h
new file mode 100644
index 0000000..c9ee6b9
--- /dev/null
+++ b/include/shared/map.h
@@ -0,0 +1,90 @@
+#ifndef _SHARED_MAP_H_
+#define _SHARED_MAP_H_
+
+#include "shared/avl_tree.h"
+#include "shared/stdmacro.h"
+
+#define MAP_PASTE_KV(K, V) CONCAT(CONCAT(K, ___), V)
+#define map_entry_t(K, V) CONCAT(map_entry_t__, MAP_PASTE_KV(K, V))
+#define map_t(K, V) CONCAT(map_t__, MAP_PASTE_KV(K, V))
+
+#define map_new(K, V) CONCAT(map_new__, MAP_PASTE_KV(K, V))
+#define map_free(K, V) CONCAT(map_free__, MAP_PASTE_KV(K, V))
+#define map_put(K, V) CONCAT(map_put__, MAP_PASTE_KV(K, V))
+#define map_get(K, V) CONCAT(map_get__, MAP_PASTE_KV(K, V))
+#define map_erase(K, V) CONCAT(map_erase__, MAP_PASTE_KV(K, V))
+#define _map_compare_(K, V) CONCAT(_map_compare__, MAP_PASTE_KV(K, V))
+#define _map_dtor_(K, V) CONCAT(_map_dtor__, MAP_PASTE_KV(K, V))
+
+#define MAP_DECL(K, V) \
+ typedef struct { \
+ K key; \
+ V value; \
+ } map_entry_t(K, V); \
+ AVL_TREE_DECL(map_entry_t(K, V)); \
+ typedef struct { \
+ avl_tree_t(map_entry_t(K, V)) * tree; \
+ } map_t(K, V); \
+ map_t(K, V) * map_new(K, V)(); \
+ void map_free(K, V)(map_t(K, V) * map); \
+ V* map_get(K, V)(map_t(K, V) * map, K key); \
+ V* map_put(K, V)(map_t(K, V) * map, K key, V value); \
+ bool map_erase(K, V)(map_t(K, V) * map, K key, V * out);
+
+#define MAP_IMPL(K, V, CMP, DTOR) \
+ static inline int _map_compare_(K, V)( \
+ map_entry_t(K, V) e1, map_entry_t(K, V) e2) \
+ { \
+ return CMP(e1.key, e2.key); \
+ } \
+ static inline void _map_dtor_(K, V)(map_entry_t(K, V) e1) \
+ { \
+ DTOR(e1.value); \
+ } \
+ AVL_TREE_IMPL(map_entry_t(K, V), _map_compare_(K, V), _map_dtor_(K, V)) \
+ map_t(K, V) * map_new(K, V)() \
+ { \
+ map_t(K, V)* ret = ALLOC(sizeof(map_t(K, V))); \
+ ret->tree = avl_tree_new(map_entry_t(K, V))(); \
+ return ret; \
+ } \
+ void map_free(K, V)(map_t(K, V) * map) { \
+ avl_tree_free(map_entry_t(K, V))(map->tree); \
+ FREE(map); \
+ }\
+ V* map_get(K, V)(map_t(K, V) * map, K key) \
+ { \
+ map_entry_t(K, V) key_; \
+ key_.key = key; \
+ map_entry_t(K, V)* entry = \
+ avl_tree_find(map_entry_t(K, V))(map->tree, key_); \
+ \
+ if (!entry) { \
+ return NULL; \
+ } \
+ \
+ return &entry->value; \
+ } \
+ V* map_put(K, V)(map_t(K, V) * map, K key, V val) \
+ { \
+ map_entry_t(K, V) entry; \
+ entry.key = key; \
+ entry.value = val; \
+ map_entry_t(K, V)* ref = \
+ avl_tree_insert(map_entry_t(K, V))(map->tree, entry); \
+ return &ref->value; \
+ } \
+ bool map_erase(K, V)(map_t(K, V) * map, K key, V * out) \
+ { \
+ map_entry_t(K, V) key_; \
+ key_.key = key; \
+ \
+ map_entry_t(K, V) entry_out; \
+ bool ret = avl_tree_erase(map_entry_t(K, V))(map->tree, key_, &entry_out); \
+ if (ret) { \
+ *out = entry_out.value; \
+ } \
+ return ret; \
+ }
+
+#endif /* _SHARED_MAP_H_ */
diff --git a/include/shared/math.h b/include/shared/math.h
new file mode 100644
index 0000000..6aec0e7
--- /dev/null
+++ b/include/shared/math.h
@@ -0,0 +1,19 @@
+#ifndef _SHARED_MATH_H_
+#define _SHARED_MATH_H_
+
+#include "kern/common.h"
+
+/* Returns round(sin(2πn / 256) * 127.5 + 127.5). */
+uint8_t byte_sin(uint8_t n);
+
+static inline uint8_t byte_scale(uint8_t n, uint8_t sc) {
+ return n * sc / 255;
+}
+
+#define min(a, b) (a) < (b) ? (a) : (b)
+#define max(a, b) (a) > (b) ? (a) : (b)
+
+/* returns (in / 256)^n * 256. */
+uint8_t amp(uint8_t in, uint8_t n);
+
+#endif /* _SHARED_MATH_H_ */
diff --git a/include/shared/stdmacro.h b/include/shared/stdmacro.h
new file mode 100644
index 0000000..6010982
--- /dev/null
+++ b/include/shared/stdmacro.h
@@ -0,0 +1,7 @@
+#ifndef _SHARED_STDMACRO_H_
+#define _SHARED_STDMACRO_H_
+
+#define CONCAT_(a, b) a ## b
+#define CONCAT(a, b) CONCAT_(a, b)
+
+#endif /* _SHARED_STDMACRO_H_ */
diff --git a/src/drv/ir/control.c b/src/drv/ir/control.c
new file mode 100644
index 0000000..6be2cf1
--- /dev/null
+++ b/src/drv/ir/control.c
@@ -0,0 +1,43 @@
+#include "drv/ir/control.h"
+
+#include "drv/ir/ir.h"
+#include "shared/map.h"
+
+typedef struct {
+ void* closure;
+ void (*fn)(uint32_t code, void* closure);
+} ir_code_listener_t;
+
+#define integer_cmp_(a, b) (a - b)
+MAP_DECL(uint32_t, ir_code_listener_t);
+MAP_IMPL(uint32_t, ir_code_listener_t, integer_cmp_, null_dtor);
+
+static map_t(uint32_t, ir_code_listener_t) * listeners;
+
+static void ir_callback(const ir_code_t* ir)
+{
+ uint32_t code;
+ if (ir_generic_decode(ir, &code)) {
+ ir_code_listener_t* l =
+ map_get(uint32_t, ir_code_listener_t)(listeners, code);
+
+ if (l) {
+ l->fn(code, l->closure);
+ }
+ }
+}
+
+void add_ir_code_callback_(
+ uint32_t code, void (*fn)(uint32_t, void*), void* closure)
+{
+ ir_code_listener_t l;
+ l.fn = fn;
+ l.closure = closure;
+ map_put(uint32_t, ir_code_listener_t)(listeners, code, l);
+}
+
+void enable_ir_control()
+{
+ listeners = map_new(uint32_t, ir_code_listener_t)();
+ add_ir_callback(ir_callback);
+}
diff --git a/src/drv/ir/ir.c b/src/drv/ir/ir.c
new file mode 100644
index 0000000..c84417d
--- /dev/null
+++ b/src/drv/ir/ir.c
@@ -0,0 +1,160 @@
+#include "drv/ir/ir.h"
+
+#include "arch/stm32l4xxx/peripherals/exti.h"
+#include "arch/stm32l4xxx/peripherals/irq.h"
+#include "arch/stm32l4xxx/peripherals/rcc.h"
+#include "arch/stm32l4xxx/peripherals/syscfg.h"
+#include "arch/stm32l4xxx/peripherals/tim.h"
+#include "kern/gpio/gpio_manager.h"
+#include "kern/mem.h"
+#include "kern/log.h"
+#include "kern/panic.h"
+#include "shared/linked_list.h"
+
+typedef void (*ir_callback_t)(const ir_code_t*);
+
+LINKED_LIST_DECL(ir_callback_t);
+LINKED_LIST_IMPL(ir_callback_t);
+
+static linked_list_t(ir_callback_t) callbacks;
+static ir_code_t read_into;
+
+void on_exti9_5()
+{
+ EXTI.p_r1 |= 1 << 6;
+ /* The IR pin has been activated. */
+
+ if (read_into.n == 0) {
+ /* Starting fresh, start the timer. */
+ regset(TIM2.c_r1, tim_cen, 1);
+ read_into.ts[read_into.n ++] = 0;
+ } else {
+ uint32_t ts = TIM2.cnt;
+ if (read_into.n < 64) {
+ read_into.ts[read_into.n++] = ts;
+ }
+ }
+}
+
+typedef enum {
+ PARITY_LOW = 0,
+ PARITY_HIGH = 1
+} ir_parity_t;
+
+static ir_parity_t ir_discover_parity(const ir_code_t* code)
+{
+ uint32_t max_even = 0;
+ uint32_t min_even = (uint32_t) -1;
+
+ uint32_t max_odd = 0;
+ uint32_t min_odd = (uint32_t) -1;
+
+ for (int i = 3; i < code->n; ++ i) {
+ uint32_t n = code->ts[i] - code->ts[i-1];
+ if (i % 2 == 0) {
+ if (n < min_even) {
+ min_even = n;
+ } else if (n > max_even) {
+ max_even = n;
+ }
+ } else {
+ if (n < min_odd) {
+ min_odd = n;
+ } else if (n > max_odd) {
+ max_odd = n;
+ }
+ }
+ }
+
+ if (max_even - min_even > max_odd - min_odd) {
+ return PARITY_LOW;
+ } else {
+ return PARITY_HIGH;
+ }
+}
+
+int ir_generic_decode(
+ const ir_code_t* code, uint32_t* out_)
+{
+ if (code->n < 5) {
+ return 0;
+ }
+
+ uint32_t min = (uint32_t) -1;
+ uint32_t max = 0;
+
+ uint32_t start = 4 + ir_discover_parity(code);
+
+ for (int i = start; i < code->n; i += 2) {
+ uint32_t n = code->ts[i] - code->ts[i-1];
+
+ if (n > max) {
+ max = n;
+ } else if (n < min) {
+ min = n;
+ }
+ }
+
+ uint32_t mid = (max + min) / 2;
+
+ uint32_t out = 0;
+ uint32_t mask = 0x80000000;
+
+ for (int i = start; i < code->n; i += 2) {
+ if ((code->ts[i] - code->ts[i-1]) >= mid) {
+ out |= mask;
+ }
+ mask >>= 1;
+ }
+
+ *out_ = out;
+ return 1;
+}
+
+void on_tim2()
+{
+ if (regget(TIM2.s_r, tim_uif)) {
+ regset(TIM2.s_r, tim_uif, 0);
+ }
+
+ /* The timer has run out. */
+ linked_list_foreach(callbacks, cb) { cb(&read_into); }
+
+ read_into.n = 0;
+}
+
+void add_ir_callback(ir_callback_t callback)
+{
+ linked_list_push_front(ir_callback_t)(&callbacks, callback);
+}
+
+void ir_begin_listen()
+{
+ int ec;
+
+ gpio_pin_opts_t opts = DEFAULT_GPIO_OPTS_INPUT;
+ opts.pull_dir = GPIO_PULL_DIR_NONE;
+ gpio_reserved_pin_t pb6 = reserve_gpio_pin(GPIO_PIN_PB6, &opts, &ec);
+
+ regset(RCC.apb1en1_r, rcc_tim2en, 1);
+ regset(RCC.apb2en_r, rcc_syscfgen, 1);
+
+ enable_interrupt(IRQ_TIM2);
+ enable_interrupt(IRQ_EXTI9_5);
+
+ /* Configure the timer. */
+ TIM2.psc = 80; /* Counts every microsecond. */
+ TIM2.ar_r = 50000; /* Stop after 50ms. */
+ regset(TIM2.die_r, tim_uie, 1); /* Enable interrupts. */
+ regset(TIM2.c_r1, tim_opm, 1); /* Set one-pulse-mode. */
+
+ /* Configure the external interrupts. */
+ regset(SYSCFG.extic_r2, syscfg_exti6, 1 /* Port B. */);
+ regset(EXTI.im_r1, exti_im_n(6), 1); /* Enable the EXTI interrupt. */
+ regset(EXTI.fts_r1, exti_ft_n(6), 1); /* Enable for falling edge. */
+ regset(EXTI.rts_r1, exti_rt_n(6), 1); /* Enable for rising edge. */
+
+ if (ec) {
+ panic("Unable to reserve GPIO pin ec=%d\n", ec);
+ }
+}
diff --git a/src/drv/ws2812B/ws2812b.c b/src/drv/ws2812B/ws2812b.c
index 3cf3570..aeb3784 100644
--- a/src/drv/ws2812B/ws2812b.c
+++ b/src/drv/ws2812B/ws2812b.c
@@ -4,32 +4,6 @@
#include "kern/mem.h"
#include "kern/panic.h"
-uint8_t sintable[256] = {
- 128, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 162, 165, 167, 170,
- 173, 176, 179, 182, 185, 188, 190, 193, 196, 198, 201, 203, 206, 208, 211,
- 213, 215, 218, 220, 222, 224, 226, 228, 230, 232, 234, 235, 237, 238, 240,
- 241, 243, 244, 245, 246, 248, 249, 250, 250, 251, 252, 253, 253, 254, 254,
- 254, 255, 255, 255, 255, 255, 255, 255, 254, 254, 254, 253, 253, 252, 251,
- 250, 250, 249, 248, 246, 245, 244, 243, 241, 240, 238, 237, 235, 234, 232,
- 230, 228, 226, 224, 222, 220, 218, 215, 213, 211, 208, 206, 203, 201, 198,
- 196, 193, 190, 188, 185, 182, 179, 176, 173, 170, 167, 165, 162, 158, 155,
- 152, 149, 146, 143, 140, 137, 134, 131, 128, 124, 121, 118, 115, 112, 109,
- 106, 103, 100, 97, 93, 90, 88, 85, 82, 79, 76, 73, 70, 67, 65,
- 62, 59, 57, 54, 52, 49, 47, 44, 42, 40, 37, 35, 33, 31, 29,
- 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11, 10, 9, 7, 6,
- 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0,
- 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, 10, 11,
- 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35, 37,
- 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76,
- 79, 82, 85, 88, 90, 93, 97, 100, 103, 106, 109, 112, 115, 118, 121,
- 124,
-};
-
-uint8_t byte_sin(uint8_t n)
-{
- return sintable[n];
-}
-
ws2812b_t* ws2812b_new(spi_select_t spi_select, int* ec)
{
spi_opts_t spi_opts = DEFAULT_SPI_OPTS;
diff --git a/src/kern/gpio/gpio_manager.c b/src/kern/gpio/gpio_manager.c
index 749ae79..ea720e9 100644
--- a/src/kern/gpio/gpio_manager.c
+++ b/src/kern/gpio/gpio_manager.c
@@ -388,3 +388,12 @@ void set_gpio_pin_low(gpio_reserved_pin_t pin)
regset(portcfg->od_r, (1 << off), 0);
}
+
+bool get_gpio_pin_input_state(gpio_reserved_pin_t pin)
+{
+ int off;
+ gpio_port_config_t* portcfg;
+ get_gpio_pin_port_off(pin.v_, &portcfg, &off);
+
+ return !!regget(portcfg->id_r, (1 << off));
+}
diff --git a/src/kern/lib.c b/src/kern/lib.c
index 24e1658..e0d3e23 100644
--- a/src/kern/lib.c
+++ b/src/kern/lib.c
@@ -37,6 +37,11 @@ void decimalify(int v, char* into)
*into = 0;
return;
} else {
+ if (v < 0) {
+ v = -v;
+ *(into++) = '-';
+ }
+
while (v > 0) {
*(into++) = 0x30 + (v % 10);
v /= 10;
diff --git a/src/kern/main.c b/src/kern/main.c
index fab2eb9..cdaa2b1 100644
--- a/src/kern/main.c
+++ b/src/kern/main.c
@@ -1,11 +1,19 @@
+#include <assert.h>
+
#include "arch.h"
#include "arch/arm/cortex-m4/mpu.h"
#include "arch/stm32l4xxx/peripherals/clock.h"
#include "arch/stm32l4xxx/peripherals/dma.h"
+#include "arch/stm32l4xxx/peripherals/exti.h"
#include "arch/stm32l4xxx/peripherals/irq.h"
#include "arch/stm32l4xxx/peripherals/rcc.h"
#include "arch/stm32l4xxx/peripherals/spi.h"
+#include "arch/stm32l4xxx/peripherals/syscfg.h"
#include "arch/stm32l4xxx/peripherals/system.h"
+#include "arch/stm32l4xxx/peripherals/tim.h"
+#include "drv/ir/control.h"
+#include "drv/ir/ir.h"
+#include "drv/ir/lg_remote_codes.h"
#include "drv/ws2812B/ws2812b.h"
#include "kern/delay.h"
#include "kern/dma/dma_manager.h"
@@ -19,8 +27,23 @@
#include "kern/priv.h"
#include "kern/spi/spi_manager.h"
#include "kern/systick/systick_manager.h"
+#include "shared/math.h"
#include "user/syscall.h"
+volatile struct {
+ uint32_t time;
+ signed int timetick;
+ ws2812b_t* drv;
+ uint8_t brightness;
+ uint8_t n_leds;
+ uint8_t off;
+ uint8_t n_snow;
+ uint8_t n_red;
+ bool sleep;
+ bool power;
+ bool cool;
+} state;
+
void on_hard_fault()
{
panic("Hard fault encountered!\n");
@@ -49,100 +72,189 @@ void configure_gpio()
}
}
-static uint8_t* compiled;
-static size_t compiled_len;
+void printit(uint32_t code, const char* str)
+{
+ (void)code;
+ klogf("%s\n", str);
+}
+
+volatile bool do_redraw;
+static void on_systick()
+{
+ if (!state.sleep) state.time += state.timetick;
+}
+
+void redraw()
+{
+ uint32_t red = 0, green = 0, blue = 0;
+
+ for (int i = 0; i < state.n_leds; ++i) {
+ if (!state.power) {
+ ws2812b_write_rgb_sync(state.drv, 0, 0, 0);
+ continue;
+ }
+
+ red = byte_scale(byte_sin(state.time / 1000 + i * 4), 255 - state.n_red) +
+ state.n_red;
+ green = 255 - red;
+
+ if (state.cool) {
+ uint32_t tmp = green;
+ green = blue;
+ blue = tmp;
+ }
-extern uint8_t sintable[256];
+ /* Add a little white flair that comes around every once in a while. */
-static uint32_t time;
+ uint32_t whitesum = 0;
+ if (state.n_snow) {
+ uint32_t white[] = {
+ /* Parallax "snow" */
+ state.time / 179 + i * 8,
+ state.time / 193 + i * 12,
+ state.time / 211 + i * 16,
+ (state.time) / 233 + i * 8,
+ // (state.time + 128) / 233 + i * 8,
+ };
-static void on_systick(void* nil)
+ for (int i = 0; i < sizeof(white) / sizeof(uint32_t); ++i) {
+ if ((white[i] / 256) % state.n_snow == 0) {
+ white[i] = amp(byte_sin(white[i]), 20);
+ } else {
+ white[i] = 0;
+ }
+ whitesum += white[i];
+ }
+ }
+
+ ws2812b_write_rgb_sync(
+ state.drv,
+ byte_scale(min(red + whitesum, 255), state.brightness),
+ byte_scale(min(green + whitesum, 255), state.brightness),
+ byte_scale(min(blue + whitesum, 255), state.brightness));
+ }
+
+ ws2812b_latch(state.drv);
+
+ delay(100000);
+}
+
+void increment_it_u8(uint32_t unused, uint8_t* v)
{
- ++time;
+ (*v)++;
}
-#define min(a, b) (a) < (b) ? (a) : (b)
+void decrement_it_u8(uint32_t unused, uint8_t* v)
+{
+ (*v)--;
+}
-static uint8_t amp(uint8_t in)
+void timetick_up(uint32_t unused, void* nil)
{
- uint32_t out = in;
+ state.timetick += 5;
+}
- for (int i = 0; i < 20; ++i) {
- out = (out * in) / 256;
+void timetick_down(uint32_t unused, void* nil)
+{
+ state.timetick -= 5;
+}
+
+void toggle_brightness(uint32_t unused, void* nil)
+{
+ klogf("Toggle %d\n", state.brightness);
+ if (state.brightness == 255) {
+ state.brightness = 32;
+ } else {
+ state.brightness = 255;
}
+}
+
+static void toggle_sleep(uint32_t unused, void* nil)
+{
+ state.sleep = !state.sleep;
+}
+
+static void toggle_power(uint32_t unused, void* nil)
+{
+ state.power = !state.power;
+}
+
+static void toggle_cool(uint32_t unused, void* nil)
+{
+ state.cool = !state.cool;
+}
+
+static void set_snow()
+{
+ state.n_snow = (state.n_snow + 1) % 10;
+}
- return min(out, 255);
+static void set_red()
+{
+ state.n_red += 50;
+ state.n_red %= 250;
}
-static uint32_t bytescale(uint32_t n, uint32_t sc)
+static void reset_state()
{
- return n * sc / 255;
+ ws2812b_t* tmp = state.drv;
+ memset((void*) &state, 0, sizeof(state));
+ state.drv = tmp;
+ state.brightness = 255;
+ state.n_leds = 250;
+ state.off = 8;
+ state.timetick = 10;
+ state.power = 1;
+ state.n_snow = 2;
}
/* Main function. This gets executed from the interrupt vector defined above. */
int main()
{
- klogf("Entering main\n");
- gpio_reserved_pin_t sysled = get_sysled();
+ klogf("Entering Main\n");
systick_add_callback(on_systick, NULL);
- enable_systick(1000);
+ enable_systick(10000);
configure_gpio();
+ ir_begin_listen();
+ enable_ir_control();
+
+ add_ir_code_callback(RC_HIGH, printit, "RC_HIGH");
+ add_ir_code_callback(RC_TEMP_UP, timetick_up, NULL);
+ add_ir_code_callback(RC_DRY, set_red, NULL);
+ add_ir_code_callback(RC_LOW, printit, "RC_LOW");
+ add_ir_code_callback(RC_TEMP_DOWN, timetick_down, NULL);
+ add_ir_code_callback(RC_COOL, toggle_cool, NULL);
+ add_ir_code_callback(RC_CONTINUOUS, set_snow, "RC_CONTINUOUS");
+ add_ir_code_callback(RC_FAN, toggle_brightness, NULL);
+ add_ir_code_callback(RC_SLEEP, toggle_sleep, NULL);
+ add_ir_code_callback(RC_UNITS, printit, "RC_UNITS");
+ add_ir_code_callback(RC_TIMER, reset_state, NULL);
+ add_ir_code_callback(RC_POWER, toggle_power, NULL);
+
int ec;
- ws2812b_t* drv = ws2812b_new(SPI_SELECT_SPI1, &ec);
+ state.drv = ws2812b_new(SPI_SELECT_SPI1, &ec);
- if (ec || !drv) {
+ if (ec || !state.drv) {
panic("Unable to create WS2812b driver :( (%d)\n", ec);
}
-#define SIZE 256
- rgb_t rgb[SIZE];
- for (int i = 0; i < SIZE; ++i) {
- rgb[i].g = 0xff;
- rgb[i].r = 0xff;
- rgb[i].b = 0xff;
- }
-
- uint32_t red = 0x40;
- uint32_t green = 0x40;
- uint32_t brightness = 255;
+ reset_state();
- for (int i = 0; i < 100; ++i) {
- ws2812b_write_rgb_sync(drv, 0, 0, 0);
+ for (int i = 0; i < state.n_leds; ++i) {
+ /* Clear the LED strip. */
+ disable_all_interrupts();
+ ws2812b_write_rgb_sync(state.drv, 0, 0, 0);
+ enable_all_interrupts();
}
-
- ws2812b_latch(drv);
+ ws2812b_latch(state.drv);
for (;;) {
- set_gpio_pin_high(sysled);
- ws2812b_latch(drv);
-
- int i;
- for (i = 0; i < SIZE; ++i) {
- red = byte_sin(time / 1000 + i * 4);
- green = 255 - red;
-
- brightness = 3 * byte_sin(time / 5000) / 4 + 63;
-
- /* Add a little white flair that comes around every once in a while. */
- uint32_t white = time / 137 + i * 4;
- if ((white / 256) % 8 == 0) {
- white = amp(byte_sin(white));
- } else {
- white = 0;
- }
-
- ws2812b_write_rgb_sync(
- drv,
- bytescale(min(red + white, 255), brightness),
- bytescale(min(green + white, 255), brightness),
- bytescale(white, brightness));
- }
-
- set_gpio_pin_low(sysled);
-
- ws2812b_latch(drv);
+ // while (!do_redraw)
+ // ;
+ // do_redraw = 0;
+ if (!state.sleep) redraw();
}
}
diff --git a/src/kern/mem.c b/src/kern/mem.c
index aa221ff..d7f7dc3 100644
--- a/src/kern/mem.c
+++ b/src/kern/mem.c
@@ -50,7 +50,7 @@ typedef uint64_t ptrint_t;
typedef uint32_t ptrint_t;
#endif
-#define CANARY ((uint32_t) 0xdeadbeee)
+#define CANARY ((uint32_t)0xdeadbeee)
#define kalloc_node_in_use(node) ((node)->used_and_canary & 1)
#define kalloc_node_get_canary(node) ((node)->used_and_canary & (~1))
#define WORD_SIZE (sizeof(uint32_t))
@@ -153,7 +153,7 @@ static void coalesce(kalloc_node_t* cur)
/* Find the next used block. */
cur = orig;
- while (!kalloc_node_in_use(cur) && !kalloc_node_out_of_range(cur)) {
+ while (!kalloc_node_out_of_range(cur) && !kalloc_node_in_use(cur)) {
cur = kalloc_node_next(cur);
}
@@ -212,6 +212,8 @@ void debug_print_blocks()
printf("---------------------------\n");
kalloc_node_t* cur = kalloc_node_at_off(0);
+ int total_words = 0;
+ int total_blocks = 0;
while (!kalloc_node_out_of_range(cur)) {
printf(
"header (%04x)@%p {used=%d, size=%5d, prev=%04x, canary=%04x}\n",
@@ -221,8 +223,13 @@ void debug_print_blocks()
cur->size_words,
cur->prev,
kalloc_node_get_canary(cur));
+ total_words += cur->size_words;
+ total_blocks ++;
cur = kalloc_node_next(cur);
}
+
+ printf("Total words allocated: %d\n", total_words);
+ printf("Total blocks allocated: %d\n", total_blocks);
}
/* Tests that we can walk up and down the allocated blocks and that they
@@ -305,4 +312,10 @@ int debug_kalloc_assert_consistency(char* error, size_t len)
return 1;
}
+int debug_is_heap_empty()
+{
+ return (void*)((uint8_t*)kalloc_start + kalloc_start->size_words * sizeof(uint32_t) + sizeof(kalloc_node_t)) ==
+ (void*)&HEAP_STOP;
+}
+
#endif
diff --git a/src/kern/stdlibrepl.c b/src/kern/stdlibrepl.c
index 588191b..676b820 100644
--- a/src/kern/stdlibrepl.c
+++ b/src/kern/stdlibrepl.c
@@ -1,4 +1,5 @@
#include "arch.h"
+#include "kern/panic.h"
/*
* Replacement for common stdlib functions that don't exist
* on the ARM bare-metal compilation environment.
@@ -15,14 +16,43 @@ size_t strlen(char* ch)
#ifdef ARCH_STM32L4
-void memcpy(void* dest, void* src, size_t size)
+void* memcpy(void* dest, const void* src, size_t size)
{
uint8_t* dest_ = dest;
- uint8_t* src_ = src;
+ const uint8_t* src_ = src;
- while(size --) {
+ while (size--) {
*(dest_++) = *(src_++);
}
+
+ return dest;
+}
+
+int memcmp(const void* s1_, const void* s2_, size_t size)
+{
+ const uint8_t* s1 = s1_;
+ const uint8_t* s2 = s2_;
+
+ while (size--) {
+ if (*s1 != *s2) {
+ if (*s1 > *s2) {
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+
+ ++s1;
+ ++s2;
+ }
+
+ return 0;
+}
+
+void __assert_func(
+ const char* file, int line, const char* func, const char* failedexpr)
+{
+ panic("Assertion failed: %s:%d in %s at %s\n", file, line, func, failedexpr);
}
#endif
diff --git a/src/shared/math.c b/src/shared/math.c
new file mode 100644
index 0000000..900cced
--- /dev/null
+++ b/src/shared/math.c
@@ -0,0 +1,38 @@
+#include "shared/math.h"
+
+uint8_t sintable[256] = {
+ 128, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 162, 165, 167, 170,
+ 173, 176, 179, 182, 185, 188, 190, 193, 196, 198, 201, 203, 206, 208, 211,
+ 213, 215, 218, 220, 222, 224, 226, 228, 230, 232, 234, 235, 237, 238, 240,
+ 241, 243, 244, 245, 246, 248, 249, 250, 250, 251, 252, 253, 253, 254, 254,
+ 254, 255, 255, 255, 255, 255, 255, 255, 254, 254, 254, 253, 253, 252, 251,
+ 250, 250, 249, 248, 246, 245, 244, 243, 241, 240, 238, 237, 235, 234, 232,
+ 230, 228, 226, 224, 222, 220, 218, 215, 213, 211, 208, 206, 203, 201, 198,
+ 196, 193, 190, 188, 185, 182, 179, 176, 173, 170, 167, 165, 162, 158, 155,
+ 152, 149, 146, 143, 140, 137, 134, 131, 128, 124, 121, 118, 115, 112, 109,
+ 106, 103, 100, 97, 93, 90, 88, 85, 82, 79, 76, 73, 70, 67, 65,
+ 62, 59, 57, 54, 52, 49, 47, 44, 42, 40, 37, 35, 33, 31, 29,
+ 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11, 10, 9, 7, 6,
+ 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, 10, 11,
+ 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35, 37,
+ 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76,
+ 79, 82, 85, 88, 90, 93, 97, 100, 103, 106, 109, 112, 115, 118, 121,
+ 124,
+};
+
+uint8_t byte_sin(uint8_t n)
+{
+ return sintable[n];
+}
+
+uint8_t amp(uint8_t in, uint8_t n)
+{
+ uint32_t out = in;
+
+ for (int i = 0; i < n; ++i) {
+ out = (out * in) / 256;
+ }
+
+ return min(out, 255);
+}
diff --git a/test_harness/fake_env.c b/test_harness/fake_env.c
index 261e9e2..bae6298 100644
--- a/test_harness/fake_env.c
+++ b/test_harness/fake_env.c
@@ -62,3 +62,8 @@ DEFINE_MEMORY_SEGMENT(mpu, 0xE000ED90, 0xE000EDA4)
/* Serial Peripheral Interface */
DEFINE_MEMORY_SEGMENT(spi1, 0x40013000, 0x400133FF)
DEFINE_MEMORY_SEGMENT(spi3, 0x40003C00, 0x40003FFF)
+
+DEFINE_MEMORY_SEGMENT(syscfg, 0x40010000, 0x4001002F)
+DEFINE_MEMORY_SEGMENT(exti, 0x40010400, 0x40010800)
+
+DEFINE_MEMORY_SEGMENT(tim2, 0x40000000, 0x400003FF)
diff --git a/test_harness/fake_env.h b/test_harness/fake_env.h
index b4451df..83ac702 100644
--- a/test_harness/fake_env.h
+++ b/test_harness/fake_env.h
@@ -15,6 +15,9 @@ void* load_fake_rcc__();
void* load_fake_spi1__();
void* load_fake_spi3__();
void* load_fake_mpu__();
+void* load_fake_syscfg__();
+void* load_fake_exti__();
+void* load_fake_tim2__();
void wipeout_fake_env();
diff --git a/tests/test_array_list.c b/tests/test_array_list.c
new file mode 100644
index 0000000..71ffa55
--- /dev/null
+++ b/tests/test_array_list.c
@@ -0,0 +1,65 @@
+#include "kern/mem.h"
+#include "shared/array_list.h"
+#include "test_harness.h"
+
+ARRAY_LIST_DECL(int);
+ARRAY_LIST_IMPL(int);
+
+TEST(array_list, smell)
+{
+ array_list_t(int)* list = array_list_new(int)();
+
+ array_list_add(int)(list, 1);
+ array_list_add(int)(list, 2);
+ array_list_add(int)(list, 3);
+ array_list_add(int)(list, 4);
+ array_list_add(int)(list, 5);
+
+ int expected[5] = {1, 2, 3, 4, 5};
+
+ for (size_t i = 0; i < 5; ++i) {
+ ASSERT_EQ(*array_list_ref(int)(list, i), expected[i]);
+ }
+
+ ASSERT_EQ(array_list_length(int)(list), 5);
+ *array_list_ref(int)(list, 3) = 8;
+ expected[3] = 8;
+
+ for (size_t i = 0; i < 5; ++i) {
+ ASSERT_EQ(*array_list_ref(int)(list, i), expected[i]);
+ }
+
+ int out;
+ array_list_remove(int)(list, 2, &out);
+ ASSERT_EQ(out, 3);
+ ASSERT_EQ(array_list_length(int)(list), 4);
+
+ int expected2[4] = {1, 2, 8, 5};
+ for (size_t i = 0; i < 4; ++i) {
+ ASSERT_EQ(*array_list_ref(int)(list, i), expected2[i]);
+ }
+
+ return 0;
+}
+
+TEST(array_list, foreach)
+{
+ array_list_t(int)* arl = array_list_new(int)();
+ array_list_add(int)(arl, 3);
+ array_list_add(int)(arl, 2);
+ array_list_add(int)(arl, 1);
+
+ int i = 0;
+ int values[3];
+ array_list_foreach(arl, val) {
+ values[i] = val;
+ ++ i;
+ }
+
+ ASSERT_EQ(values[0], 3);
+ ASSERT_EQ(values[1], 2);
+ ASSERT_EQ(values[2], 1);
+
+ return 0;
+
+}
diff --git a/tests/test_avl_tree.c b/tests/test_avl_tree.c
new file mode 100644
index 0000000..2a7260f
--- /dev/null
+++ b/tests/test_avl_tree.c
@@ -0,0 +1,149 @@
+#include "shared/avl_tree.h"
+#include "test_harness.h"
+
+#define integer_cmp_(a, b) (a - b)
+AVL_TREE_DECL(int);
+AVL_TREE_IMPL(int, integer_cmp_, null_dtor);
+
+static inline void avl_tree_debug_print(avl_tree_node_t(int) * node, int tab)
+{
+ if (node) {
+ avl_tree_debug_print(node->right, tab + 1);
+ }
+ for (int i = 0; i < tab; ++i) {
+ printf(" ");
+ }
+ if (!node) {
+ printf("(nil)\n");
+ return;
+ }
+ printf("%d\n", node->value);
+ avl_tree_debug_print(node->left, tab + 1);
+}
+
+TEST(avl_tree, insert)
+{
+ avl_tree_t(int)* tree = avl_tree_new(int)();
+ avl_tree_insert(int)(tree, 5);
+
+ ASSERT_EQ(avl_tree_size(int)(tree), 1);
+
+ avl_tree_insert(int)(tree, 4);
+ avl_tree_insert(int)(tree, 6);
+
+ ASSERT_EQ(avl_tree_size(int)(tree), 3);
+ ASSERT_EQ(avl_tree_height(int)(tree), 2);
+
+ return 0;
+}
+
+TEST(avl_tree, insert_rotate_asc)
+{
+ avl_tree_t(int)* tree = avl_tree_new(int)();
+
+ avl_tree_insert(int)(tree, 1);
+ avl_tree_insert(int)(tree, 2);
+ avl_tree_insert(int)(tree, 3);
+ avl_tree_insert(int)(tree, 4);
+
+ ASSERT_EQ(avl_tree_size(int)(tree), 4);
+ ASSERT_EQ(avl_tree_height(int)(tree), 3);
+
+ return 0;
+}
+
+TEST(avl_tree, insert_rotate_desc)
+{
+ avl_tree_t(int)* tree = avl_tree_new(int)();
+
+ avl_tree_insert(int)(tree, 4);
+ avl_tree_insert(int)(tree, 3);
+ avl_tree_insert(int)(tree, 2);
+ avl_tree_insert(int)(tree, 1);
+
+ ASSERT_EQ(avl_tree_size(int)(tree), 4);
+ ASSERT_EQ(avl_tree_height(int)(tree), 3);
+
+ return 0;
+}
+
+TEST(avl_tree_erase, erase)
+{
+ avl_tree_t(int)* tree = avl_tree_new(int)();
+
+ avl_tree_insert(int)(tree, 4);
+ avl_tree_insert(int)(tree, 3);
+ avl_tree_insert(int)(tree, 2);
+ avl_tree_insert(int)(tree, 1);
+
+ ASSERT_EQ(avl_tree_size(int)(tree), 4);
+ ASSERT_EQ(avl_tree_height(int)(tree), 3);
+
+ int out;
+ bool b = avl_tree_erase(int)(tree, 3, &out);
+
+ ASSERT_EQ(b, 1);
+ ASSERT_EQ(out, 3);
+ ASSERT_EQ(avl_tree_size(int)(tree), 3);
+ ASSERT_EQ(avl_tree_height(int)(tree), 2);
+
+ b = avl_tree_erase(int)(tree, 1, &out);
+
+ ASSERT_EQ(b, 1);
+ ASSERT_EQ(out, 1);
+ ASSERT_EQ(avl_tree_size(int)(tree), 2);
+ ASSERT_EQ(avl_tree_height(int)(tree), 2);
+
+ return 0;
+}
+
+TEST(avl_tree, erase_onesided)
+{
+ avl_tree_t(int)* tree = avl_tree_new(int)();
+ for (int i = 0; i < 16; ++i) {
+ avl_tree_insert(int)(tree, i);
+ }
+
+ ASSERT_EQ(avl_tree_erase(int)(tree, 0, NULL), 1);
+ ASSERT_EQ(avl_tree_erase(int)(tree, 1, NULL), 1);
+ ASSERT_EQ(avl_tree_erase(int)(tree, 2, NULL), 1);
+ ASSERT_EQ(avl_tree_erase(int)(tree, 3, NULL), 1);
+ ASSERT_EQ(avl_tree_erase(int)(tree, 4, NULL), 1);
+ ASSERT_EQ(avl_tree_erase(int)(tree, 5, NULL), 1);
+ ASSERT_EQ(avl_tree_erase(int)(tree, 6, NULL), 1);
+
+ ASSERT_EQ(avl_tree_height(int)(tree), 4);
+ ASSERT_EQ(avl_tree_size(int)(tree), 9);
+
+ avl_tree_free(int)(tree);
+ ASSERT_TRUE(debug_is_heap_empty());
+
+ return 0;
+}
+
+TEST(avl_tree, find)
+{
+ avl_tree_t(int)* tree = avl_tree_new(int)();
+
+ for (int i = 0; i < 16; ++ i) {
+ avl_tree_insert(int)(tree, i);
+ }
+
+ ASSERT_EQ(*avl_tree_find(int)(tree, 4), 4);
+ ASSERT_EQ(*avl_tree_find(int)(tree, 3), 3);
+ ASSERT_EQ(*avl_tree_find(int)(tree, 15), 15);
+
+ ASSERT_EQ(avl_tree_find(int)(tree, 100), NULL);
+
+ return 0;
+}
+
+TEST (avl_tree, stress)
+{
+ avl_tree_t(int)* tree = avl_tree_new(int)();
+ for (int i = 0; i < 512; ++ i) {
+ avl_tree_insert(int)(tree, i);
+ }
+
+ return 0;
+}
diff --git a/tests/test_linked_list.c b/tests/test_linked_list.c
index 7ec96b5..2c51625 100644
--- a/tests/test_linked_list.c
+++ b/tests/test_linked_list.c
@@ -60,3 +60,25 @@ TEST(linked_list, foreach)
return 0;
}
+
+TEST(linked_list, remove)
+{
+ linked_list_t(int) ll = LINKED_LIST_INIT;
+ linked_list_push_front(int)(&ll, 1);
+ linked_list_push_front(int)(&ll, 2);
+ linked_list_push_front(int)(&ll, 3);
+ linked_list_push_front(int)(&ll, 4);
+ linked_list_push_front(int)(&ll, 4);
+
+ int ec = linked_list_remove(int)(&ll, 4);
+ ASSERT_EQ(!!ec, 1);
+ ec = linked_list_remove(int)(&ll, 2);
+ ASSERT_EQ(!!ec, 1);
+ ec = linked_list_remove(int)(&ll, 1);
+ ASSERT_EQ(!!ec, 1);
+
+ ASSERT_EQ(linked_list_length(int)(&ll), 1);
+ ASSERT_EQ(*linked_list_front(int)(&ll), 3);
+
+ return 0;
+}
diff --git a/tests/test_map.c b/tests/test_map.c
new file mode 100644
index 0000000..29704f0
--- /dev/null
+++ b/tests/test_map.c
@@ -0,0 +1,113 @@
+#include <stdlib.h>
+
+#include "shared/map.h"
+#include "test_harness.h"
+
+typedef const char* const_char_ptr;
+
+#define integer_cmp_(a, b) (a - b)
+MAP_DECL(int, const_char_ptr);
+MAP_IMPL(int, const_char_ptr, integer_cmp_, null_dtor);
+
+TEST(map, smoke)
+{
+ map_t(int, const_char_ptr)* map = map_new(int, const_char_ptr)();
+
+ map_put(int, const_char_ptr)(map, 6, "string1");
+ map_put(int, const_char_ptr)(map, 8, "string2");
+ map_put(int, const_char_ptr)(map, 9, "string3");
+
+ const char* str = *map_get(int, const_char_ptr)(map, 8);
+ ASSERT_EQ_STR(str, "string2");
+
+ str = *map_get(int, const_char_ptr)(map, 9);
+ ASSERT_EQ_STR(str, "string3");
+ ASSERT_EQ(map_get(int, const_char_ptr)(map, 20), NULL);
+
+ map_free(int, const_char_ptr)(map);
+ ASSERT_TRUE(debug_is_heap_empty());
+ return 0;
+}
+
+typedef uint32_t* int_ptr;
+MAP_DECL(int, int_ptr);
+MAP_IMPL(int, int_ptr, integer_cmp_, kfree);
+
+static inline void loose_map_print(
+ avl_tree_node_t(map_entry_t(int, int_ptr))* node,
+ int tab) {
+ if (node) {
+ loose_map_print(node->right, tab + 1);
+ }
+ for (int i = 0; i < tab; ++i) {
+ printf(" ");
+ }
+ if (!node) {
+ printf("(nil)\n");
+ return;
+ }
+ printf("(%d => %d)\n", node->value.key, *node->value.value);
+ loose_map_print(node->left, tab + 1);
+}
+static inline void map_debug_print(
+ map_t(int, int_ptr) * map)
+{
+ printf("------\n");
+ if (map->tree) {
+ loose_map_print(map->tree->root, 0);
+ }
+ printf("------\n");
+}
+TEST(map, stress)
+{
+#define N 256
+ uint32_t* arr[N];
+ memset(arr, 0, sizeof(arr));
+ map_t(int, int_ptr)* map = map_new(int, int_ptr)();
+
+ for (int i = 0; i < N; ++i) {
+ uint32_t* a = kalloc(sizeof(uint32_t));
+ *a = rand();
+ arr[i] = a;
+ }
+
+ for (int i = 0; i < N; ++i) {
+ map_put(int, int_ptr)(map, i, arr[i]);
+ }
+
+ for (int i = 0; i < N; ++i) {
+ uint32_t** ptr = map_get(int, int_ptr)(map, i);
+ ASSERT_TRUE(ptr != NULL);
+ ASSERT_TRUE(*ptr != NULL);
+ ASSERT_EQ(**ptr, *arr[i]);
+ }
+
+ int erased[N];
+ memset(erased, 0, sizeof(erased));
+
+ for (int i = 0; i < (N / 2); ++i) {
+ int_ptr out;
+ int e = rand() % N;
+ if (map_erase(int, int_ptr)(map, e, &out)) {
+ ASSERT_EQ(*out, *arr[e]);
+ arr[e] = NULL;
+ kfree(out);
+ }
+ erased[e] = 1;
+ }
+
+ for (int i = 0; i < N; ++ i) {
+ if (erased[i]) {
+ continue;
+ }
+ uint32_t** ptr = map_get(int, int_ptr)(map, i);
+ ASSERT_TRUE(ptr != NULL);
+ ASSERT_TRUE(*ptr != NULL);
+ ASSERT_EQ(**ptr, *arr[i]);
+ }
+
+ map_free(int, int_ptr)(map);
+ ASSERT_TRUE(debug_is_heap_empty());
+
+ return 0;
+}
diff --git a/tests/test_mpu.c b/tests/test_mpu.c
new file mode 100644
index 0000000..551e079
--- /dev/null
+++ b/tests/test_mpu.c
@@ -0,0 +1,27 @@
+#include "test_harness.c"
+
+#include "arch/arm/cortex-m4/mpu.h"
+#include "kern/mpu/mpu_manager.h"
+
+TEST(mpu, smell)
+{
+ memory_region_opts_t memopts = { 0 };
+ memopts.region = (void*) 0;
+ memopts.bufferable = 0;
+ memopts.cacheable = 1;
+ memopts.sharable = 1;
+ memopts.tex = 0;
+ memopts.size = REGION_SIZE_4Gb;
+ memopts.perms = ACCESS_PERMS_FULL;
+ memopts.subregion_disable = 0;
+ memopts.executable = 1;
+ memopts.enable = 1;
+
+ mpu_configure_region(0, &memopts);
+ mpu_set_enabled(1);
+
+ ASSERT_EQ(MPU.rba_r, 1 << 4);
+ ASSERT_EQ(MPU.ras_r, 0x0306003F);
+
+ return 0;
+}