diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7777469..429104e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,6 +25,9 @@ jobs: matrix: board: [stm32wb55xx_nucleo, pic32cz_curiosity_ultra, stm32h563zi_nucleo, stm32f411_blackpill, stm32c031_nucleo] extra_cflags: ["", "-DWHAL_CFG_NO_TIMEOUT"] + include: + - board: stm32wb55xx_nucleo + extra_cflags: "-DBOARD_DMA" steps: - uses: actions/checkout@v4 diff --git a/.gitignore b/.gitignore index ef44b24..3a0c8ce 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,5 @@ dkms.conf compile_commands.json tests/core/test_core + +*.pdf diff --git a/boards/README.md b/boards/README.md index e2cb445..14718b5 100644 --- a/boards/README.md +++ b/boards/README.md @@ -15,7 +15,10 @@ build configuration. | Board | Platform | CPU | Directory | |-------|----------|-----|-----------| | Microchip PIC32CZ CA Curiosity Ultra | PIC32CZ | Cortex-M7 | `pic32cz_curiosity_ultra/` | -| ST STM32WB55 Nucleo | STM32WB | Cortex-M4 | `stm32wb55xx_nucleo/` | +| ST NUCLEO-C031C6 | STM32C0 | Cortex-M0+ | `stm32c031_nucleo/` | +| WeAct BlackPill STM32F411 | STM32F4 | Cortex-M4 | `stm32f411_blackpill/` | +| ST NUCLEO-H563ZI | STM32H5 | Cortex-M33 | `stm32h563zi_nucleo/` | +| ST NUCLEO-WB55RG | STM32WB | Cortex-M4 | `stm32wb55xx_nucleo/` | ## Board Directory Contents diff --git a/boards/stm32wb55xx_nucleo/Makefile.inc b/boards/stm32wb55xx_nucleo/Makefile.inc index 955f1b0..0aeceb0 100644 --- a/boards/stm32wb55xx_nucleo/Makefile.inc +++ b/boards/stm32wb55xx_nucleo/Makefile.inc @@ -9,7 +9,8 @@ OBJCOPY = $(GCC_PATH)arm-none-eabi-objcopy CFLAGS += -Wall -Werror $(INCLUDE) -g3 \ -ffreestanding -nostdlib -mcpu=cortex-m4 \ - -DPLATFORM_STM32WB -MMD -MP + -DPLATFORM_STM32WB -MMD -MP \ + $(if $(DMA),-DBOARD_DMA) LDFLAGS = --omagic -static LINKER_SCRIPT ?= $(_BOARD_DIR)/linker.ld @@ -29,6 +30,9 @@ BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/spi.c) BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/rng.c) BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/crypto.c) BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/block.c) +BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/dma.c) +BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/irq.c) +BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/irq/cortex_m4_nvic.c) BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/stm32wb_*.c) BOARD_SOURCE += $(wildcard $(WHAL_DIR)/src/*/systick.c) diff --git a/boards/stm32wb55xx_nucleo/board.c b/boards/stm32wb55xx_nucleo/board.c index 6adf75d..7062bce 100644 --- a/boards/stm32wb55xx_nucleo/board.c +++ b/boards/stm32wb55xx_nucleo/board.c @@ -6,6 +6,7 @@ #include #include "peripheral.h" + /* SysTick timing */ volatile uint32_t g_tick = 0; volatile uint8_t g_waiting = 0; @@ -30,6 +31,11 @@ whal_Timeout g_whalTimeout = { .GetTick = Board_GetTick, }; +/* IRQ */ +whal_Irq g_whalIrq = { + WHAL_CORTEX_M4_NVIC_DEVICE, +}; + /* Clock */ whal_Clock g_whalClock = { WHAL_STM32WB55_RCC_PLL_DEVICE, @@ -155,7 +161,48 @@ whal_Timer g_whalTimer = { }, }; +/* DMA */ +#ifdef BOARD_DMA + +whal_Dma g_whalDma1 = { + WHAL_STM32WB55_DMA1_DEVICE, + .cfg = &(whal_Stm32wbDma_Cfg){WHAL_STM32WB55_DMA1_CFG}, +}; + +static const whal_Stm32wbRcc_Clk g_dmaClock = {WHAL_STM32WB55_DMA1_CLOCK}; +static const whal_Stm32wbRcc_Clk g_dmamuxClock = {WHAL_STM32WB55_DMAMUX1_CLOCK}; + +void DMA1_Channel4_IRQHandler(void) +{ + whal_Stm32wbDma_IRQHandler(&g_whalDma1, 3, + whal_Stm32wbUartDma_TxCallback, g_whalUart.cfg); +} + +void DMA1_Channel5_IRQHandler(void) +{ + whal_Stm32wbDma_IRQHandler(&g_whalDma1, 4, + whal_Stm32wbUartDma_RxCallback, g_whalUart.cfg); +} +#endif + /* UART */ +#ifdef BOARD_DMA +whal_Uart g_whalUart = { + WHAL_STM32WB55_UART1_DEVICE, + .driver = &whal_Stm32wbUartDma_Driver, + .cfg = &(whal_Stm32wbUartDma_Cfg) { + .base = { + .brr = WHAL_STM32WB_UART_BRR(64000000, 115200), + .timeout = &g_whalTimeout, + }, + .dma = &g_whalDma1, + .txCh = 3, + .rxCh = 4, + .txChCfg = &(whal_Stm32wbDma_ChCfg){WHAL_STM32WB55_UART1_TX_DMA_CFG}, + .rxChCfg = &(whal_Stm32wbDma_ChCfg){WHAL_STM32WB55_UART1_RX_DMA_CFG}, + }, +}; +#else whal_Uart g_whalUart = { WHAL_STM32WB55_UART1_DEVICE, @@ -165,6 +212,7 @@ whal_Uart g_whalUart = { .brr = WHAL_STM32WB_UART_BRR(64000000, 115200), }, }; +#endif /* Flash */ whal_Flash g_whalFlash = { @@ -174,7 +222,7 @@ whal_Flash g_whalFlash = { .timeout = &g_whalTimeout, .startAddr = 0x08000000, - .size = 0x100000, + .size = 0x80000, /* 512 KB (upper half reserved for BLE stack) */ }, }; @@ -260,6 +308,30 @@ whal_Error Board_Init(void) return err; } + err = whal_Irq_Init(&g_whalIrq); + if (err) + return err; + +#ifdef BOARD_DMA + err = whal_Clock_Enable(&g_whalClock, &g_dmaClock); + if (err) + return err; + err = whal_Clock_Enable(&g_whalClock, &g_dmamuxClock); + if (err) + return err; + err = whal_Dma_Init(&g_whalDma1); + if (err) + return err; + + /* Enable NVIC interrupts for DMA1 channel 4 (IRQ 14) and channel 5 (IRQ 15) */ + err = whal_Irq_Enable(&g_whalIrq, 14, NULL); + if (err) + return err; + err = whal_Irq_Enable(&g_whalIrq, 15, NULL); + if (err) + return err; +#endif + err = whal_Gpio_Init(&g_whalGpio); if (err) { return err; @@ -357,6 +429,25 @@ whal_Error Board_Deinit(void) return err; } +#ifdef BOARD_DMA + whal_Irq_Disable(&g_whalIrq, 14); + whal_Irq_Disable(&g_whalIrq, 15); + + err = whal_Dma_Deinit(&g_whalDma1); + if (err) + return err; + err = whal_Clock_Disable(&g_whalClock, &g_dmamuxClock); + if (err) + return err; + err = whal_Clock_Disable(&g_whalClock, &g_dmaClock); + if (err) + return err; +#endif + + err = whal_Irq_Deinit(&g_whalIrq); + if (err) + return err; + /* Disable clocks */ for (size_t i = 0; i < CLOCK_COUNT; i++) { err = whal_Clock_Disable(&g_whalClock, &g_clocks[i]); diff --git a/boards/stm32wb55xx_nucleo/board.h b/boards/stm32wb55xx_nucleo/board.h index 92deefb..1075024 100644 --- a/boards/stm32wb55xx_nucleo/board.h +++ b/boards/stm32wb55xx_nucleo/board.h @@ -13,6 +13,7 @@ extern whal_Spi g_whalSpi; extern whal_Flash g_whalFlash; extern whal_Rng g_whalRng; extern whal_Crypto g_whalCrypto; +extern whal_Irq g_whalIrq; extern whal_Timeout g_whalTimeout; extern volatile uint32_t g_tick; @@ -32,8 +33,8 @@ enum { #define BOARD_LED_PORT_OFFSET 0x400 /* GPIOB */ #define BOARD_LED_PIN_NUM 5 #define BOARD_FLASH_START_ADDR 0x08000000 -#define BOARD_FLASH_SIZE 0x100000 -#define BOARD_FLASH_TEST_ADDR 0x08080000 +#define BOARD_FLASH_SIZE 0x80000 /* 512 KB (upper half reserved for BLE stack) */ +#define BOARD_FLASH_TEST_ADDR 0x0807F000 #define BOARD_FLASH_SECTOR_SZ 0x1000 enum { diff --git a/docs/writing_a_driver.md b/docs/writing_a_driver.md index 03cc257..9135d4c 100644 --- a/docs/writing_a_driver.md +++ b/docs/writing_a_driver.md @@ -437,6 +437,51 @@ Receive `dataSz` bytes into the provided buffer. For each byte: is available 2. Read the byte from the receive data register and store it in the buffer +### SendAsync + +Start a non-blocking transmit. Returns immediately after initiating the +transfer. The buffer must remain valid until the transfer completes. The +driver signals completion through a platform-specific mechanism. + +Drivers that do not support async should set SendAsync to NULL in the vtable. +The dispatch layer returns WHAL_EINVAL when the caller tries to use a NULL +async function. + +### RecvAsync + +Start a non-blocking receive. Returns immediately after initiating the +transfer. The buffer must remain valid until the transfer completes. + +The async variants are optional — a driver vtable only needs to populate +them if the platform supports non-blocking transfers. Polled-only drivers +leave these NULL. + +--- + +## IRQ + +Header: `wolfHAL/irq/irq.h` + +The IRQ driver controls an interrupt controller. It provides a +platform-independent way to enable and disable individual interrupt lines. + +### Init + +Initialize the interrupt controller. + +### Deinit + +Shut down the interrupt controller. + +### Enable + +Enable an interrupt line. The `irqCfg` parameter is platform-specific and +can contain settings such as priority. Pass NULL for defaults. + +### Disable + +Disable an interrupt line. + --- ## SPI @@ -857,6 +902,66 @@ issue the write command and poll for completion with a timeout. --- +## DMA + +Header: `wolfHAL/dma/dma.h` + +The DMA driver controls a DMA controller. A single device instance represents +one controller, and individual channels are identified by index. Channel +configuration is platform-specific and passed as an opaque pointer. + +DMA is a service peripheral — peripheral drivers (UART, SPI) consume it +internally. The application never calls the DMA API directly. Peripheral +drivers receive a `whal_Dma` pointer and channel number through their +configuration struct and use them to set up transfers. + +### Init + +Initialize the DMA controller. Clear any pending interrupt flags and reset +controller state. The board must enable the DMA controller clock before calling +Init. + +### Deinit + +Shut down the DMA controller. + +### Configure + +Configure a DMA channel for transfers. The `chCfg` parameter is a +platform-specific struct containing: + +- Transfer direction (memory-to-peripheral, peripheral-to-memory, etc.) +- Source and destination addresses +- Transfer width (8, 16, 32 bit) +- Buffer address and length +- Burst size (if supported) +- Peripheral request mapping (e.g., DMAMUX request ID) + +The DMA driver does not store callbacks. Instead, the board defines ISR +entries in the vector table and calls the driver's IRQ handler (e.g., +`whal_Stm32wbDma_IRQHandler()`), passing a callback and context pointer. +The IRQ handler checks and clears the interrupt flags, then invokes the +callback. Peripheral drivers expose their completion callbacks for the +board to wire up (e.g., `whal_Stm32wbUartDma_TxCallback`). + +Configure sets up all channel registers but does not start the transfer. +Call Start to begin. A channel can be reconfigured between transfers (e.g., +to change the buffer address and length) by calling Configure again. + +### Start + +Start a previously configured DMA channel. This enables the channel, +beginning the transfer. The channel must have been configured via Configure +before calling Start. + +### Stop + +Stop a DMA channel. This aborts any in-progress transfer and disables the +channel. The peripheral driver should call Stop in its cleanup path or when +a transfer needs to be cancelled. + +--- + ## EthPhy Header: `wolfHAL/eth_phy/eth_phy.h` diff --git a/src/dma/dma.c b/src/dma/dma.c new file mode 100644 index 0000000..28a7a9f --- /dev/null +++ b/src/dma/dma.c @@ -0,0 +1,47 @@ +#include + +inline whal_Error whal_Dma_Init(whal_Dma *dmaDev) +{ + if (!dmaDev || !dmaDev->driver || !dmaDev->driver->Init) { + return WHAL_EINVAL; + } + + return dmaDev->driver->Init(dmaDev); +} + +inline whal_Error whal_Dma_Deinit(whal_Dma *dmaDev) +{ + if (!dmaDev || !dmaDev->driver || !dmaDev->driver->Deinit) { + return WHAL_EINVAL; + } + + return dmaDev->driver->Deinit(dmaDev); +} + +inline whal_Error whal_Dma_Configure(whal_Dma *dmaDev, size_t ch, + const void *chCfg) +{ + if (!dmaDev || !dmaDev->driver || !dmaDev->driver->Configure || !chCfg) { + return WHAL_EINVAL; + } + + return dmaDev->driver->Configure(dmaDev, ch, chCfg); +} + +inline whal_Error whal_Dma_Start(whal_Dma *dmaDev, size_t ch) +{ + if (!dmaDev || !dmaDev->driver || !dmaDev->driver->Start) { + return WHAL_EINVAL; + } + + return dmaDev->driver->Start(dmaDev, ch); +} + +inline whal_Error whal_Dma_Stop(whal_Dma *dmaDev, size_t ch) +{ + if (!dmaDev || !dmaDev->driver || !dmaDev->driver->Stop) { + return WHAL_EINVAL; + } + + return dmaDev->driver->Stop(dmaDev, ch); +} diff --git a/src/dma/stm32wb_dma.c b/src/dma/stm32wb_dma.c new file mode 100644 index 0000000..8f43609 --- /dev/null +++ b/src/dma/stm32wb_dma.c @@ -0,0 +1,356 @@ +#include +#include +#include +#include +#include +#include + +/* + * STM32WB DMA Register Definitions + * + * DMA1 has 7 channels, DMA2 has 5 channels. Each channel has a set of + * registers at offset 0x08 + 0x14 * (ch - 1) from the DMA base, where + * ch is the 1-based hardware channel number. + */ + +/* DMA interrupt status register (read-only) */ +#define DMA_ISR_REG 0x00 + +/* DMA interrupt flag clear register (write-only) */ +#define DMA_IFCR_REG 0x04 + +/* Per-channel registers. hw_ch is 1-based hardware channel number. */ +#define DMA_CCR_REG(hw_ch) (0x08 + 0x14 * ((hw_ch) - 1)) +#define DMA_CNDTR_REG(hw_ch) (0x0C + 0x14 * ((hw_ch) - 1)) +#define DMA_CPAR_REG(hw_ch) (0x10 + 0x14 * ((hw_ch) - 1)) +#define DMA_CMAR_REG(hw_ch) (0x14 + 0x14 * ((hw_ch) - 1)) + +/* CCR bit definitions */ +#define DMA_CCR_EN_Pos 0 +#define DMA_CCR_EN_Msk (1UL << DMA_CCR_EN_Pos) + +#define DMA_CCR_TCIE_Pos 1 +#define DMA_CCR_TCIE_Msk (1UL << DMA_CCR_TCIE_Pos) + +#define DMA_CCR_HTIE_Pos 2 +#define DMA_CCR_HTIE_Msk (1UL << DMA_CCR_HTIE_Pos) + +#define DMA_CCR_TEIE_Pos 3 +#define DMA_CCR_TEIE_Msk (1UL << DMA_CCR_TEIE_Pos) + +#define DMA_CCR_DIR_Pos 4 +#define DMA_CCR_DIR_Msk (1UL << DMA_CCR_DIR_Pos) + +#define DMA_CCR_CIRC_Pos 5 +#define DMA_CCR_CIRC_Msk (1UL << DMA_CCR_CIRC_Pos) + +#define DMA_CCR_PINC_Pos 6 +#define DMA_CCR_PINC_Msk (1UL << DMA_CCR_PINC_Pos) + +#define DMA_CCR_MINC_Pos 7 +#define DMA_CCR_MINC_Msk (1UL << DMA_CCR_MINC_Pos) + +#define DMA_CCR_PSIZE_Pos 8 +#define DMA_CCR_PSIZE_Msk (WHAL_BITMASK(2) << DMA_CCR_PSIZE_Pos) + +#define DMA_CCR_MSIZE_Pos 10 +#define DMA_CCR_MSIZE_Msk (WHAL_BITMASK(2) << DMA_CCR_MSIZE_Pos) + +#define DMA_CCR_PL_Pos 12 +#define DMA_CCR_PL_Msk (WHAL_BITMASK(2) << DMA_CCR_PL_Pos) + +#define DMA_CCR_MEM2MEM_Pos 14 +#define DMA_CCR_MEM2MEM_Msk (1UL << DMA_CCR_MEM2MEM_Pos) + +/* + * ISR/IFCR flag positions. Each channel uses 4 bits: + * bit 0: GIF (global interrupt flag) + * bit 1: TCIF (transfer complete) + * bit 2: HTIF (half transfer) + * bit 3: TEIF (transfer error) + * Channel 1 starts at bit 0, channel 2 at bit 4, etc. + */ +#define DMA_ISR_GIF_Pos(hw_ch) (((hw_ch) - 1) * 4) +#define DMA_ISR_TCIF_Pos(hw_ch) (((hw_ch) - 1) * 4 + 1) +#define DMA_ISR_TEIF_Pos(hw_ch) (((hw_ch) - 1) * 4 + 3) + +#define DMA_IFCR_CGIF(hw_ch) (1UL << (((hw_ch) - 1) * 4)) +#define DMA_IFCR_CTCIF(hw_ch) (1UL << (((hw_ch) - 1) * 4 + 1)) +#define DMA_IFCR_CHTIF(hw_ch) (1UL << (((hw_ch) - 1) * 4 + 2)) +#define DMA_IFCR_CTEIF(hw_ch) (1UL << (((hw_ch) - 1) * 4 + 3)) + +/* Clear all flags for a given hardware channel */ +#define DMA_IFCR_CALL(hw_ch) (0xFUL << (((hw_ch) - 1) * 4)) + +/* DMAMUX channel configuration register */ +#define DMAMUX_CxCR_REG(mux_ch) (0x000 + 0x04 * (mux_ch)) + +#define DMAMUX_CxCR_DMAREQ_ID_Pos 0 +#define DMAMUX_CxCR_DMAREQ_ID_Msk (WHAL_BITMASK(6) << DMAMUX_CxCR_DMAREQ_ID_Pos) + +static whal_Error whal_Stm32wbDma_Init(whal_Dma *dmaDev) +{ + whal_Stm32wbDma_Cfg *cfg; + size_t base; + + if (!dmaDev || !dmaDev->cfg) { + return WHAL_EINVAL; + } + + cfg = (whal_Stm32wbDma_Cfg *)dmaDev->cfg; + base = dmaDev->regmap.base; + + /* Clear all interrupt flags for all channels */ + { + size_t clearAll = 0; + for (size_t i = 1; i <= cfg->numChannels; ++i) { + clearAll |= DMA_IFCR_CALL(i); + } + whal_Reg_Write(base, DMA_IFCR_REG, clearAll); + } + + return WHAL_SUCCESS; +} + +static whal_Error whal_Stm32wbDma_Deinit(whal_Dma *dmaDev) +{ + whal_Stm32wbDma_Cfg *cfg; + size_t base; + + if (!dmaDev || !dmaDev->cfg) { + return WHAL_EINVAL; + } + + cfg = (whal_Stm32wbDma_Cfg *)dmaDev->cfg; + base = dmaDev->regmap.base; + + /* Disable all channels and clear all interrupt flags */ + for (size_t i = 1; i <= cfg->numChannels; ++i) { + whal_Reg_Update(base, DMA_CCR_REG(i), + DMA_CCR_EN_Msk, + whal_SetBits(DMA_CCR_EN_Msk, DMA_CCR_EN_Pos, 0)); + } + + { + size_t clearAll = 0; + for (size_t i = 1; i <= cfg->numChannels; ++i) { + clearAll |= DMA_IFCR_CALL(i); + } + whal_Reg_Write(base, DMA_IFCR_REG, clearAll); + } + + return WHAL_SUCCESS; +} + +static whal_Error whal_Stm32wbDma_Configure(whal_Dma *dmaDev, size_t ch, + const void *chCfg) +{ + whal_Stm32wbDma_Cfg *cfg; + const whal_Stm32wbDma_ChCfg *dmaChCfg; + size_t base; + size_t hw_ch; + size_t ccr; + uint32_t periphAddr; + uint32_t memAddr; + size_t periphInc; + size_t memInc; + size_t periphSize; + size_t memSize; + + if (!dmaDev || !dmaDev->cfg || !chCfg) { + return WHAL_EINVAL; + } + + cfg = (whal_Stm32wbDma_Cfg *)dmaDev->cfg; + dmaChCfg = (const whal_Stm32wbDma_ChCfg *)chCfg; + base = dmaDev->regmap.base; + + if (ch >= cfg->numChannels) { + return WHAL_EINVAL; + } + + /* Hardware channels are 1-based */ + hw_ch = ch + 1; + + /* + * Map the direction, src/dst addresses, and increment modes to the + * hardware's CPAR/CMAR/DIR/PINC/MINC/PSIZE/MSIZE model. + * + * DIR=0: peripheral (CPAR) -> memory (CMAR) [read from peripheral] + * DIR=1: memory (CMAR) -> peripheral (CPAR) [read from memory] + * MEM2MEM=1: memory (CPAR with DIR=0 is src) -> memory (CMAR is dst) + */ + switch (dmaChCfg->dir) { + case WHAL_STM32WB_DMA_DIR_PERIPH_TO_MEM: + periphAddr = dmaChCfg->srcAddr; + memAddr = dmaChCfg->dstAddr; + periphInc = dmaChCfg->srcInc; + memInc = dmaChCfg->dstInc; + periphSize = dmaChCfg->width; + memSize = dmaChCfg->width; + break; + case WHAL_STM32WB_DMA_DIR_MEM_TO_PERIPH: + periphAddr = dmaChCfg->dstAddr; + memAddr = dmaChCfg->srcAddr; + periphInc = dmaChCfg->dstInc; + memInc = dmaChCfg->srcInc; + periphSize = dmaChCfg->width; + memSize = dmaChCfg->width; + break; + case WHAL_STM32WB_DMA_DIR_MEM_TO_MEM: + /* MEM2MEM: CPAR=source, CMAR=destination, DIR=0 */ + periphAddr = dmaChCfg->srcAddr; + memAddr = dmaChCfg->dstAddr; + periphInc = dmaChCfg->srcInc; + memInc = dmaChCfg->dstInc; + periphSize = dmaChCfg->width; + memSize = dmaChCfg->width; + break; + default: + return WHAL_EINVAL; + } + + /* Build the CCR value */ + ccr = 0; + + /* DIR bit: 0 for periph-to-mem and mem-to-mem, 1 for mem-to-periph */ + if (dmaChCfg->dir == WHAL_STM32WB_DMA_DIR_MEM_TO_PERIPH) { + ccr |= whal_SetBits(DMA_CCR_DIR_Msk, DMA_CCR_DIR_Pos, 1); + } + + /* MEM2MEM bit */ + if (dmaChCfg->dir == WHAL_STM32WB_DMA_DIR_MEM_TO_MEM) { + ccr |= whal_SetBits(DMA_CCR_MEM2MEM_Msk, DMA_CCR_MEM2MEM_Pos, 1); + } + + /* Circular mode */ + if (dmaChCfg->circular) { + ccr |= whal_SetBits(DMA_CCR_CIRC_Msk, DMA_CCR_CIRC_Pos, 1); + } + + /* Peripheral increment */ + ccr |= whal_SetBits(DMA_CCR_PINC_Msk, DMA_CCR_PINC_Pos, periphInc); + + /* Memory increment */ + ccr |= whal_SetBits(DMA_CCR_MINC_Msk, DMA_CCR_MINC_Pos, memInc); + + /* Peripheral size */ + ccr |= whal_SetBits(DMA_CCR_PSIZE_Msk, DMA_CCR_PSIZE_Pos, periphSize); + + /* Memory size */ + ccr |= whal_SetBits(DMA_CCR_MSIZE_Msk, DMA_CCR_MSIZE_Pos, memSize); + + /* Enable transfer complete and transfer error interrupts */ + ccr |= whal_SetBits(DMA_CCR_TCIE_Msk, DMA_CCR_TCIE_Pos, 1); + ccr |= whal_SetBits(DMA_CCR_TEIE_Msk, DMA_CCR_TEIE_Pos, 1); + + /* Write channel registers. EN must be 0 before writing CCR fields. */ + whal_Reg_Write(base, DMA_CCR_REG(hw_ch), 0); + whal_Reg_Write(base, DMA_CNDTR_REG(hw_ch), dmaChCfg->length); + whal_Reg_Write(base, DMA_CPAR_REG(hw_ch), periphAddr); + whal_Reg_Write(base, DMA_CMAR_REG(hw_ch), memAddr); + whal_Reg_Write(base, DMA_CCR_REG(hw_ch), ccr); + + /* Configure DMAMUX request mapping */ + size_t muxCh = cfg->dmamuxChOffset + ch; + whal_Reg_Update(cfg->dmamuxBase, DMAMUX_CxCR_REG(muxCh), + DMAMUX_CxCR_DMAREQ_ID_Msk, + whal_SetBits(DMAMUX_CxCR_DMAREQ_ID_Msk, + DMAMUX_CxCR_DMAREQ_ID_Pos, + dmaChCfg->dmamuxReqId)); + + return WHAL_SUCCESS; +} + +static whal_Error whal_Stm32wbDma_Start(whal_Dma *dmaDev, size_t ch) +{ + whal_Stm32wbDma_Cfg *cfg; + size_t base; + size_t hw_ch; + + if (!dmaDev || !dmaDev->cfg) { + return WHAL_EINVAL; + } + + cfg = (whal_Stm32wbDma_Cfg *)dmaDev->cfg; + base = dmaDev->regmap.base; + + if (ch >= cfg->numChannels) { + return WHAL_EINVAL; + } + + hw_ch = ch + 1; + + /* Clear stale interrupt flags before enabling (TRM requires this) */ + whal_Reg_Write(base, DMA_IFCR_REG, DMA_IFCR_CALL(hw_ch)); + + /* Enable the channel */ + whal_Reg_Update(base, DMA_CCR_REG(hw_ch), + DMA_CCR_EN_Msk, + whal_SetBits(DMA_CCR_EN_Msk, DMA_CCR_EN_Pos, 1)); + + return WHAL_SUCCESS; +} + +static whal_Error whal_Stm32wbDma_Stop(whal_Dma *dmaDev, size_t ch) +{ + whal_Stm32wbDma_Cfg *cfg; + size_t base; + size_t hw_ch; + + if (!dmaDev || !dmaDev->cfg) { + return WHAL_EINVAL; + } + + cfg = (whal_Stm32wbDma_Cfg *)dmaDev->cfg; + base = dmaDev->regmap.base; + + if (ch >= cfg->numChannels) { + return WHAL_EINVAL; + } + + hw_ch = ch + 1; + + /* Disable the channel */ + whal_Reg_Update(base, DMA_CCR_REG(hw_ch), + DMA_CCR_EN_Msk, + whal_SetBits(DMA_CCR_EN_Msk, DMA_CCR_EN_Pos, 0)); + + /* Clear channel interrupt flags */ + whal_Reg_Write(base, DMA_IFCR_REG, DMA_IFCR_CALL(hw_ch)); + + return WHAL_SUCCESS; +} + +void whal_Stm32wbDma_IRQHandler(whal_Dma *dmaDev, size_t ch, + whal_Stm32wbDma_Callback cb, void *ctx) +{ + size_t base; + size_t hw_ch; + size_t isr; + + base = dmaDev->regmap.base; + hw_ch = ch + 1; + + isr = whal_Reg_Read(base, DMA_ISR_REG); + + if (isr & (1UL << DMA_ISR_TCIF_Pos(hw_ch))) { + whal_Reg_Write(base, DMA_IFCR_REG, DMA_IFCR_CTCIF(hw_ch)); + if (cb) + cb(ctx, WHAL_SUCCESS); + } + + if (isr & (1UL << DMA_ISR_TEIF_Pos(hw_ch))) { + whal_Reg_Write(base, DMA_IFCR_REG, DMA_IFCR_CTEIF(hw_ch)); + if (cb) + cb(ctx, WHAL_EHARDWARE); + } +} + +const whal_DmaDriver whal_Stm32wbDma_Driver = { + .Init = whal_Stm32wbDma_Init, + .Deinit = whal_Stm32wbDma_Deinit, + .Configure = whal_Stm32wbDma_Configure, + .Start = whal_Stm32wbDma_Start, + .Stop = whal_Stm32wbDma_Stop, +}; diff --git a/src/irq/cortex_m4_nvic.c b/src/irq/cortex_m4_nvic.c new file mode 100644 index 0000000..9685f80 --- /dev/null +++ b/src/irq/cortex_m4_nvic.c @@ -0,0 +1,77 @@ +#include +#include +#include + +/* + * ARM Cortex-M4 NVIC register offsets (relative to 0xE000E100). + * + * ISER: Interrupt Set-Enable Registers (0x000-0x01C, 32 IRQs per register) + * ICER: Interrupt Clear-Enable Registers (0x080-0x09C) + * IPR: Interrupt Priority Registers (0x300-0x37F, 4 IRQs per register) + */ +#define NVIC_ISER_REG(irq) (0x000 + (((irq) >> 5) << 2)) +#define NVIC_ICER_REG(irq) (0x080 + (((irq) >> 5) << 2)) +#define NVIC_IPR_REG(irq) (0x300 + (((irq) >> 2) << 2)) +#define NVIC_IPR_SHIFT(irq) (((irq) & 0x3) << 3) + +static whal_Error whal_Nvic_Init(whal_Irq *irqDev) +{ + if (!irqDev) { + return WHAL_EINVAL; + } + + return WHAL_SUCCESS; +} + +static whal_Error whal_Nvic_Deinit(whal_Irq *irqDev) +{ + if (!irqDev) { + return WHAL_EINVAL; + } + + return WHAL_SUCCESS; +} + +static whal_Error whal_Nvic_Enable(whal_Irq *irqDev, size_t irq, + const void *irqCfg) +{ + if (!irqDev) { + return WHAL_EINVAL; + } + + size_t base = irqDev->regmap.base; + + /* Set priority if config provided */ + if (irqCfg) { + const whal_Nvic_Cfg *cfg = (const whal_Nvic_Cfg *)irqCfg; + size_t shift = NVIC_IPR_SHIFT(irq); + size_t mask = (0xFFUL << shift); + whal_Reg_Update(base, NVIC_IPR_REG(irq), mask, + (size_t)(cfg->priority << 4) << shift); + } + + /* Enable the interrupt */ + whal_Reg_Write(base, NVIC_ISER_REG(irq), (1UL << (irq & 0x1F))); + + return WHAL_SUCCESS; +} + +static whal_Error whal_Nvic_Disable(whal_Irq *irqDev, size_t irq) +{ + if (!irqDev) { + return WHAL_EINVAL; + } + + size_t base = irqDev->regmap.base; + + whal_Reg_Write(base, NVIC_ICER_REG(irq), (1UL << (irq & 0x1F))); + + return WHAL_SUCCESS; +} + +const whal_IrqDriver whal_Nvic_Driver = { + .Init = whal_Nvic_Init, + .Deinit = whal_Nvic_Deinit, + .Enable = whal_Nvic_Enable, + .Disable = whal_Nvic_Disable, +}; diff --git a/src/irq/irq.c b/src/irq/irq.c new file mode 100644 index 0000000..c4244f8 --- /dev/null +++ b/src/irq/irq.c @@ -0,0 +1,38 @@ +#include + +inline whal_Error whal_Irq_Init(whal_Irq *irqDev) +{ + if (!irqDev || !irqDev->driver || !irqDev->driver->Init) { + return WHAL_EINVAL; + } + + return irqDev->driver->Init(irqDev); +} + +inline whal_Error whal_Irq_Deinit(whal_Irq *irqDev) +{ + if (!irqDev || !irqDev->driver || !irqDev->driver->Deinit) { + return WHAL_EINVAL; + } + + return irqDev->driver->Deinit(irqDev); +} + +inline whal_Error whal_Irq_Enable(whal_Irq *irqDev, size_t irq, + const void *irqCfg) +{ + if (!irqDev || !irqDev->driver || !irqDev->driver->Enable) { + return WHAL_EINVAL; + } + + return irqDev->driver->Enable(irqDev, irq, irqCfg); +} + +inline whal_Error whal_Irq_Disable(whal_Irq *irqDev, size_t irq) +{ + if (!irqDev || !irqDev->driver || !irqDev->driver->Disable) { + return WHAL_EINVAL; + } + + return irqDev->driver->Disable(irqDev, irq); +} diff --git a/src/uart/stm32wb_uart_dma.c b/src/uart/stm32wb_uart_dma.c new file mode 100644 index 0000000..c6a4bdb --- /dev/null +++ b/src/uart/stm32wb_uart_dma.c @@ -0,0 +1,200 @@ +#include +#include +#include +#include +#include +#include +#include + +#define UART_CR3_REG 0x08 + +#define UART_CR3_DMAR_Pos 6 +#define UART_CR3_DMAR_Msk (1UL << UART_CR3_DMAR_Pos) + +#define UART_CR3_DMAT_Pos 7 +#define UART_CR3_DMAT_Msk (1UL << UART_CR3_DMAT_Pos) + +#define UART_ISR_REG 0x1C +#define UART_ISR_TC_Pos 6 +#define UART_ISR_TC_Msk (1UL << UART_ISR_TC_Pos) + +#define UART_RDR_REG 0x24 + +#define UART_TDR_REG 0x28 + +whal_Error whal_Stm32wbUartDma_SendAsync(whal_Uart *uartDev, const void *data, + size_t dataSz) +{ + whal_Stm32wbUartDma_Cfg *cfg; + const whal_Regmap *reg; + whal_Error err; + + if (!uartDev || !uartDev->cfg || !data || dataSz > UINT16_MAX) + return WHAL_EINVAL; + + if (dataSz == 0) + return WHAL_SUCCESS; + + cfg = (whal_Stm32wbUartDma_Cfg *)uartDev->cfg; + + if (cfg->txResult == WHAL_ENOTREADY) + return WHAL_ENOTREADY; + + reg = &uartDev->regmap; + + cfg->txChCfg->srcAddr = (uint32_t)(uintptr_t)data; + cfg->txChCfg->dstAddr = (uint32_t)(reg->base + UART_TDR_REG); + cfg->txChCfg->length = dataSz; + + cfg->txResult = WHAL_ENOTREADY; + + err = whal_Dma_Configure(cfg->dma, cfg->txCh, cfg->txChCfg); + if (err) { + cfg->txResult = err; + return err; + } + + whal_Reg_Update(reg->base, UART_CR3_REG, UART_CR3_DMAT_Msk, + UART_CR3_DMAT_Msk); + + err = whal_Dma_Start(cfg->dma, cfg->txCh); + if (err) { + whal_Reg_Update(reg->base, UART_CR3_REG, UART_CR3_DMAT_Msk, 0); + cfg->txResult = err; + return err; + } + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wbUartDma_RecvAsync(whal_Uart *uartDev, void *data, + size_t dataSz) +{ + whal_Stm32wbUartDma_Cfg *cfg; + const whal_Regmap *reg; + whal_Error err; + + if (!uartDev || !uartDev->cfg || !data || dataSz > UINT16_MAX) + return WHAL_EINVAL; + + if (dataSz == 0) + return WHAL_SUCCESS; + + cfg = (whal_Stm32wbUartDma_Cfg *)uartDev->cfg; + + if (cfg->rxResult == WHAL_ENOTREADY) + return WHAL_ENOTREADY; + + reg = &uartDev->regmap; + + cfg->rxChCfg->srcAddr = (uint32_t)(reg->base + UART_RDR_REG); + cfg->rxChCfg->dstAddr = (uint32_t)(uintptr_t)data; + cfg->rxChCfg->length = dataSz; + + cfg->rxResult = WHAL_ENOTREADY; + + err = whal_Dma_Configure(cfg->dma, cfg->rxCh, cfg->rxChCfg); + if (err) { + cfg->rxResult = err; + return err; + } + + whal_Reg_Update(reg->base, UART_CR3_REG, UART_CR3_DMAR_Msk, + UART_CR3_DMAR_Msk); + + err = whal_Dma_Start(cfg->dma, cfg->rxCh); + if (err) { + whal_Reg_Update(reg->base, UART_CR3_REG, UART_CR3_DMAR_Msk, 0); + cfg->rxResult = err; + return err; + } + + return WHAL_SUCCESS; +} + +whal_Error whal_Stm32wbUartDma_Send(whal_Uart *uartDev, const void *data, + size_t dataSz) +{ + whal_Stm32wbUartDma_Cfg *cfg; + whal_Error err; + + err = whal_Stm32wbUartDma_SendAsync(uartDev, data, dataSz); + if (err) + return err; + + cfg = (whal_Stm32wbUartDma_Cfg *)uartDev->cfg; + + WHAL_TIMEOUT_START(cfg->base.timeout); + while (cfg->txResult == WHAL_ENOTREADY) { + if (WHAL_TIMEOUT_EXPIRED(cfg->base.timeout)) { + err = WHAL_ETIMEOUT; + goto cleanup; + } + } + + if (cfg->txResult != WHAL_SUCCESS) { + err = cfg->txResult; + goto cleanup; + } + + err = whal_Reg_ReadPoll(uartDev->regmap.base, UART_ISR_REG, + UART_ISR_TC_Msk, UART_ISR_TC_Msk, cfg->base.timeout); + +cleanup: + whal_Dma_Stop(cfg->dma, cfg->txCh); + whal_Reg_Update(uartDev->regmap.base, UART_CR3_REG, UART_CR3_DMAT_Msk, 0); + cfg->txResult = err; + + return err; +} + +whal_Error whal_Stm32wbUartDma_Recv(whal_Uart *uartDev, void *data, + size_t dataSz) +{ + whal_Stm32wbUartDma_Cfg *cfg; + whal_Error err; + + err = whal_Stm32wbUartDma_RecvAsync(uartDev, data, dataSz); + if (err) + return err; + + cfg = (whal_Stm32wbUartDma_Cfg *)uartDev->cfg; + + WHAL_TIMEOUT_START(cfg->base.timeout); + while (cfg->rxResult == WHAL_ENOTREADY) { + if (WHAL_TIMEOUT_EXPIRED(cfg->base.timeout)) { + err = WHAL_ETIMEOUT; + goto cleanup; + } + } + + err = cfg->rxResult; + +cleanup: + whal_Dma_Stop(cfg->dma, cfg->rxCh); + whal_Reg_Update(uartDev->regmap.base, UART_CR3_REG, UART_CR3_DMAR_Msk, 0); + cfg->rxResult = err; + + return err; +} + +void whal_Stm32wbUartDma_TxCallback(void *ctx, whal_Error err) +{ + whal_Stm32wbUartDma_Cfg *cfg = (whal_Stm32wbUartDma_Cfg *)ctx; + cfg->txResult = err; +} + +void whal_Stm32wbUartDma_RxCallback(void *ctx, whal_Error err) +{ + whal_Stm32wbUartDma_Cfg *cfg = (whal_Stm32wbUartDma_Cfg *)ctx; + cfg->rxResult = err; +} + +const whal_UartDriver whal_Stm32wbUartDma_Driver = { + .Init = whal_Stm32wbUart_Init, + .Deinit = whal_Stm32wbUart_Deinit, + .Send = whal_Stm32wbUartDma_Send, + .Recv = whal_Stm32wbUartDma_Recv, + .SendAsync = whal_Stm32wbUartDma_SendAsync, + .RecvAsync = whal_Stm32wbUartDma_RecvAsync, +}; diff --git a/src/uart/uart.c b/src/uart/uart.c index 6c0b23f..a227459 100644 --- a/src/uart/uart.c +++ b/src/uart/uart.c @@ -38,3 +38,21 @@ inline whal_Error whal_Uart_Recv(whal_Uart *uartDev, void *data, size_t dataSz) return uartDev->driver->Recv(uartDev, data, dataSz); } +inline whal_Error whal_Uart_SendAsync(whal_Uart *uartDev, const void *data, size_t dataSz) +{ + if (!uartDev || !uartDev->driver || !uartDev->driver->SendAsync || !data) { + return WHAL_EINVAL; + } + + return uartDev->driver->SendAsync(uartDev, data, dataSz); +} + +inline whal_Error whal_Uart_RecvAsync(whal_Uart *uartDev, void *data, size_t dataSz) +{ + if (!uartDev || !uartDev->driver || !uartDev->driver->RecvAsync || !data) { + return WHAL_EINVAL; + } + + return uartDev->driver->RecvAsync(uartDev, data, dataSz); +} + diff --git a/tests/core/test_dispatch.c b/tests/core/test_dispatch.c index c8dfb32..b46455c 100644 --- a/tests/core/test_dispatch.c +++ b/tests/core/test_dispatch.c @@ -40,6 +40,9 @@ static whal_Error MockUartDeinit(whal_Uart *d) { (void)d; return WHAL_SUCCESS; } static whal_Error MockUartSend(whal_Uart *d, const void *data, size_t sz) { (void)d; (void)data; (void)sz; return WHAL_SUCCESS; } static whal_Error MockUartRecv(whal_Uart *d, void *data, size_t sz) { (void)d; (void)data; (void)sz; return WHAL_SUCCESS; } +static whal_Error MockUartSendAsync(whal_Uart *d, const void *data, size_t sz) { (void)d; (void)data; (void)sz; return WHAL_SUCCESS; } +static whal_Error MockUartRecvAsync(whal_Uart *d, void *data, size_t sz) { (void)d; (void)data; (void)sz; return WHAL_SUCCESS; } + static const whal_UartDriver mockUartDriver = { .Init = MockUartInit, .Deinit = MockUartDeinit, @@ -47,6 +50,15 @@ static const whal_UartDriver mockUartDriver = { .Recv = MockUartRecv, }; +static const whal_UartDriver mockUartAsyncDriver = { + .Init = MockUartInit, + .Deinit = MockUartDeinit, + .Send = MockUartSend, + .Recv = MockUartRecv, + .SendAsync = MockUartSendAsync, + .RecvAsync = MockUartRecvAsync, +}; + static whal_Error MockFlashInit(whal_Flash *d) { (void)d; return WHAL_SUCCESS; } static whal_Error MockFlashDeinit(whal_Flash *d) { (void)d; return WHAL_SUCCESS; } static whal_Error MockFlashLock(whal_Flash *d, size_t a, size_t l) { (void)d; (void)a; (void)l; return WHAL_SUCCESS; } @@ -160,6 +172,8 @@ static void Test_Uart_NullDev(void) uint8_t buf[1]; WHAL_ASSERT_EQ(whal_Uart_Send(NULL, buf, 1), WHAL_EINVAL); WHAL_ASSERT_EQ(whal_Uart_Recv(NULL, buf, 1), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Uart_SendAsync(NULL, buf, 1), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Uart_RecvAsync(NULL, buf, 1), WHAL_EINVAL); } static void Test_Uart_NullDriver(void) @@ -168,6 +182,14 @@ static void Test_Uart_NullDriver(void) WHAL_ASSERT_EQ(whal_Uart_Init(&dev), WHAL_EINVAL); } +static void Test_Uart_NullAsyncVtable(void) +{ + whal_Uart dev = { .driver = &mockUartDriver }; + uint8_t buf[1] = {0}; + WHAL_ASSERT_EQ(whal_Uart_SendAsync(&dev, buf, 1), WHAL_EINVAL); + WHAL_ASSERT_EQ(whal_Uart_RecvAsync(&dev, buf, 1), WHAL_EINVAL); +} + static void Test_Uart_ValidDispatch(void) { whal_Uart dev = { .driver = &mockUartDriver }; @@ -175,6 +197,10 @@ static void Test_Uart_ValidDispatch(void) uint8_t buf[4] = {0}; WHAL_ASSERT_EQ(whal_Uart_Send(&dev, buf, sizeof(buf)), WHAL_SUCCESS); WHAL_ASSERT_EQ(whal_Uart_Recv(&dev, buf, sizeof(buf)), WHAL_SUCCESS); + + whal_Uart asyncDev = { .driver = &mockUartAsyncDriver }; + WHAL_ASSERT_EQ(whal_Uart_SendAsync(&asyncDev, buf, sizeof(buf)), WHAL_SUCCESS); + WHAL_ASSERT_EQ(whal_Uart_RecvAsync(&asyncDev, buf, sizeof(buf)), WHAL_SUCCESS); } /* --- Flash dispatch tests --- */ @@ -358,6 +384,7 @@ void whal_Test_Dispatch(void) WHAL_TEST(Test_Gpio_ValidDispatch); WHAL_TEST(Test_Uart_NullDev); WHAL_TEST(Test_Uart_NullDriver); + WHAL_TEST(Test_Uart_NullAsyncVtable); WHAL_TEST(Test_Uart_ValidDispatch); WHAL_TEST(Test_Flash_NullDev); WHAL_TEST(Test_Flash_NullDriver); diff --git a/wolfHAL/dma/dma.h b/wolfHAL/dma/dma.h new file mode 100644 index 0000000..4d4fe7e --- /dev/null +++ b/wolfHAL/dma/dma.h @@ -0,0 +1,112 @@ +#ifndef WHAL_DMA_H +#define WHAL_DMA_H + +#include +#include +#include + +/* + * @file dma.h + * @brief Generic DMA abstraction and driver interface. + * + * A DMA device represents one DMA controller. Individual channels are + * identified by index. Channel configuration is platform-specific and + * passed as an opaque pointer to Configure. + * + * Typical usage from a peripheral driver: + * 1. whal_Dma_Init() — enable the DMA controller + * 2. whal_Dma_Configure() — set up a channel (direction, width, src/dst, + * buffer, length, callback, etc.) + * 3. whal_Dma_Start() — kick off the transfer + * 4. whal_Dma_Stop() — abort or clean up after completion + * + * The completion callback (provided in the platform-specific channel config) + * runs in ISR context. + */ + +typedef struct whal_Dma whal_Dma; + +/* + * @brief Driver vtable for DMA controllers. + */ +typedef struct { + /* Initialize the DMA controller. */ + whal_Error (*Init)(whal_Dma *dmaDev); + /* Deinitialize the DMA controller. */ + whal_Error (*Deinit)(whal_Dma *dmaDev); + /* Configure a DMA channel. chCfg is platform-specific. */ + whal_Error (*Configure)(whal_Dma *dmaDev, size_t ch, const void *chCfg); + /* Start a previously configured DMA channel. */ + whal_Error (*Start)(whal_Dma *dmaDev, size_t ch); + /* Stop a DMA channel. */ + whal_Error (*Stop)(whal_Dma *dmaDev, size_t ch); +} whal_DmaDriver; + +/* + * @brief DMA device instance containing driver and configuration data. + */ +struct whal_Dma { + const whal_Regmap regmap; + const whal_DmaDriver *driver; + const void *cfg; +}; + +#ifdef WHAL_CFG_DIRECT_CALLBACKS +#define whal_Dma_Init(dmaDev) ((dmaDev)->driver->Init((dmaDev))) +#define whal_Dma_Deinit(dmaDev) ((dmaDev)->driver->Deinit((dmaDev))) +#define whal_Dma_Configure(dmaDev, ch, chCfg) ((dmaDev)->driver->Configure((dmaDev), (ch), (chCfg))) +#define whal_Dma_Start(dmaDev, ch) ((dmaDev)->driver->Start((dmaDev), (ch))) +#define whal_Dma_Stop(dmaDev, ch) ((dmaDev)->driver->Stop((dmaDev), (ch))) +#else +/* + * @brief Initialize a DMA controller. + * + * @param dmaDev DMA instance to initialize. + * + * @retval WHAL_SUCCESS Init completed. + * @retval WHAL_EINVAL Null pointer or missing driver function. + */ +whal_Error whal_Dma_Init(whal_Dma *dmaDev); +/* + * @brief Deinitialize a DMA controller. + * + * @param dmaDev DMA instance to deinitialize. + * + * @retval WHAL_SUCCESS Deinit completed. + * @retval WHAL_EINVAL Null pointer or missing driver function. + */ +whal_Error whal_Dma_Deinit(whal_Dma *dmaDev); +/* + * @brief Configure a DMA channel. + * + * @param dmaDev DMA controller instance. + * @param ch Channel index. + * @param chCfg Platform-specific channel configuration. + * + * @retval WHAL_SUCCESS Channel configured. + * @retval WHAL_EINVAL Null pointer, missing driver function, or bad config. + */ +whal_Error whal_Dma_Configure(whal_Dma *dmaDev, size_t ch, const void *chCfg); +/* + * @brief Start a previously configured DMA channel. + * + * @param dmaDev DMA controller instance. + * @param ch Channel index. + * + * @retval WHAL_SUCCESS Transfer started. + * @retval WHAL_EINVAL Null pointer or missing driver function. + */ +whal_Error whal_Dma_Start(whal_Dma *dmaDev, size_t ch); +/* + * @brief Stop a DMA channel. + * + * @param dmaDev DMA controller instance. + * @param ch Channel index. + * + * @retval WHAL_SUCCESS Channel stopped. + * @retval WHAL_EINVAL Null pointer or missing driver function. + */ +whal_Error whal_Dma_Stop(whal_Dma *dmaDev, size_t ch); +#endif + +#endif /* WHAL_DMA_H */ diff --git a/wolfHAL/dma/stm32wb_dma.h b/wolfHAL/dma/stm32wb_dma.h new file mode 100644 index 0000000..11e6188 --- /dev/null +++ b/wolfHAL/dma/stm32wb_dma.h @@ -0,0 +1,108 @@ +#ifndef WHAL_STM32WB_DMA_H +#define WHAL_STM32WB_DMA_H + +#include +#include +#include +#include + +/* + * @file stm32wb_dma.h + * @brief STM32WB DMA driver configuration types. + * + * The STM32WB has two DMA controllers: + * - DMA1 with 7 channels + * - DMA2 with 5 channels + * + * Each controller is represented by a single whal_Dma instance. Channels are + * identified by a 0-based index (0-6 for DMA1, 0-4 for DMA2). + * + * A DMAMUX peripheral maps peripheral request lines to DMA channels. + * DMAMUX channels 0-6 correspond to DMA1 channels 1-7. DMAMUX channels 7-11 + * correspond to DMA2 channels 1-5. The driver handles this mapping internally; + * the caller only needs to provide the DMAREQ_ID for the desired peripheral. + */ + +/* + * @brief Transfer direction. + */ +typedef enum { + WHAL_STM32WB_DMA_DIR_PERIPH_TO_MEM, /* Peripheral to memory */ + WHAL_STM32WB_DMA_DIR_MEM_TO_PERIPH, /* Memory to peripheral */ + WHAL_STM32WB_DMA_DIR_MEM_TO_MEM, /* Memory to memory */ +} whal_Stm32wbDma_Dir; + +/* + * @brief Data width for transfers. + */ +typedef enum { + WHAL_STM32WB_DMA_WIDTH_8BIT, /* 8-bit (byte) */ + WHAL_STM32WB_DMA_WIDTH_16BIT, /* 16-bit (half-word) */ + WHAL_STM32WB_DMA_WIDTH_32BIT, /* 32-bit (word) */ +} whal_Stm32wbDma_Width; + +/* + * @brief Address increment mode. + */ +typedef enum { + WHAL_STM32WB_DMA_INC_DISABLE, /* Fixed address */ + WHAL_STM32WB_DMA_INC_ENABLE, /* Increment address after each transfer */ +} whal_Stm32wbDma_Inc; + +/* + * @brief Per-channel DMA configuration. + */ +typedef struct { + whal_Stm32wbDma_Dir dir; /* Transfer direction */ + uint32_t srcAddr; /* Source address */ + uint32_t dstAddr; /* Destination address */ + uint16_t length; /* Number of data items to transfer */ + whal_Stm32wbDma_Width width; /* Data width (8, 16, or 32 bit) */ + whal_Stm32wbDma_Inc srcInc; /* Source address increment mode */ + whal_Stm32wbDma_Inc dstInc; /* Destination address increment mode */ + uint8_t dmamuxReqId; /* DMAMUX request ID for the peripheral */ + uint8_t circular; /* Non-zero to enable circular mode */ +} whal_Stm32wbDma_ChCfg; + +/* + * @brief Controller-level DMA configuration. + * + * Contains the DMAMUX base address and the DMAMUX channel offset for this + * controller. DMA1 uses DMAMUX channels starting at 0; DMA2 starts at 7. + */ +typedef struct { + size_t dmamuxBase; /* DMAMUX1 register base address */ + uint8_t dmamuxChOffset; /* First DMAMUX channel index for this controller */ + uint8_t numChannels; /* Number of channels (7 for DMA1, 5 for DMA2) */ +} whal_Stm32wbDma_Cfg; + +/* + * @brief Driver instance for STM32WB DMA. + */ +extern const whal_DmaDriver whal_Stm32wbDma_Driver; + +/* + * @brief DMA callback type. + * + * Called from ISR context when a transfer completes or an error occurs. + * + * @param ctx User context pointer. + * @param err WHAL_SUCCESS on transfer complete, WHAL_EHARDWARE on error. + */ +typedef void (*whal_Stm32wbDma_Callback)(void *ctx, whal_Error err); + +/* + * @brief Handle a DMA channel interrupt. + * + * Checks TCIF/TEIF flags, clears them, and invokes the callback. + * Should be called from the board's ISR entry point. + * + * @param dmaDev DMA controller instance. + * @param ch Channel index (0-based). + * @param cb Callback to invoke (or NULL to just clear flags). + * @param ctx Context pointer passed to callback. + */ +void whal_Stm32wbDma_IRQHandler(whal_Dma *dmaDev, size_t ch, + whal_Stm32wbDma_Callback cb, void *ctx); + +#endif /* WHAL_STM32WB_DMA_H */ diff --git a/wolfHAL/error.h b/wolfHAL/error.h index c55a31c..5e8e03a 100644 --- a/wolfHAL/error.h +++ b/wolfHAL/error.h @@ -15,6 +15,7 @@ enum { WHAL_SUCCESS = 0, /* Invalid argument or unsupported operation. */ WHAL_EINVAL = -4000, + /* Resource not ready or busy. */ WHAL_ENOTREADY = -4001, /* Hardware device error. */ WHAL_EHARDWARE = -4002, diff --git a/wolfHAL/irq/cortex_m4_nvic.h b/wolfHAL/irq/cortex_m4_nvic.h new file mode 100644 index 0000000..43c8f80 --- /dev/null +++ b/wolfHAL/irq/cortex_m4_nvic.h @@ -0,0 +1,29 @@ +#ifndef WHAL_CORTEX_M4_NVIC_H +#define WHAL_CORTEX_M4_NVIC_H + +#include +#include + +/* + * @file cortex_m4_nvic.h + * @brief ARM Cortex-M4 NVIC driver. + * + * Uses a 4-bit priority field in bits[7:4] of each IPR byte. + * The regmap base should be 0xE000E100 (NVIC_ISER0). + */ + +/* + * @brief Optional per-interrupt configuration. + * + * Pass to whal_Irq_Enable() to set priority, or pass NULL for default (0). + */ +typedef struct { + uint8_t priority; +} whal_Nvic_Cfg; + +/* + * @brief Driver instance for Cortex-M4 NVIC. + */ +extern const whal_IrqDriver whal_Nvic_Driver; + +#endif /* WHAL_CORTEX_M4_NVIC_H */ diff --git a/wolfHAL/irq/irq.h b/wolfHAL/irq/irq.h new file mode 100644 index 0000000..d1e8a66 --- /dev/null +++ b/wolfHAL/irq/irq.h @@ -0,0 +1,90 @@ +#ifndef WHAL_IRQ_H +#define WHAL_IRQ_H + +#include +#include +#include + +/* + * @file irq.h + * @brief Generic interrupt controller abstraction and driver interface. + * + * A whal_Irq device represents an interrupt controller (e.g., ARM Cortex-M + * NVIC). The API allows enabling and disabling individual interrupt lines, + * with optional platform-specific configuration (e.g., priority) passed + * through the Enable call. + */ + +typedef struct whal_Irq whal_Irq; + +/* + * @brief Driver vtable for interrupt controllers. + */ +typedef struct { + /* Initialize the interrupt controller. */ + whal_Error (*Init)(whal_Irq *irqDev); + /* Deinitialize the interrupt controller. */ + whal_Error (*Deinit)(whal_Irq *irqDev); + /* Enable an interrupt line. irqCfg is platform-specific (or NULL). */ + whal_Error (*Enable)(whal_Irq *irqDev, size_t irq, const void *irqCfg); + /* Disable an interrupt line. */ + whal_Error (*Disable)(whal_Irq *irqDev, size_t irq); +} whal_IrqDriver; + +/* + * @brief Interrupt controller device instance. + */ +struct whal_Irq { + const whal_Regmap regmap; + const whal_IrqDriver *driver; + const void *cfg; +}; + +#ifdef WHAL_CFG_DIRECT_CALLBACKS +#define whal_Irq_Init(irqDev) ((irqDev)->driver->Init((irqDev))) +#define whal_Irq_Deinit(irqDev) ((irqDev)->driver->Deinit((irqDev))) +#define whal_Irq_Enable(irqDev, irq, irqCfg) ((irqDev)->driver->Enable((irqDev), (irq), (irqCfg))) +#define whal_Irq_Disable(irqDev, irq) ((irqDev)->driver->Disable((irqDev), (irq))) +#else +/* + * @brief Initialize the interrupt controller. + * + * @param irqDev Interrupt controller instance. + * + * @retval WHAL_SUCCESS Init completed. + * @retval WHAL_EINVAL Null pointer or missing driver function. + */ +whal_Error whal_Irq_Init(whal_Irq *irqDev); +/* + * @brief Deinitialize the interrupt controller. + * + * @param irqDev Interrupt controller instance. + * + * @retval WHAL_SUCCESS Deinit completed. + * @retval WHAL_EINVAL Null pointer or missing driver function. + */ +whal_Error whal_Irq_Deinit(whal_Irq *irqDev); +/* + * @brief Enable an interrupt line. + * + * @param irqDev Interrupt controller instance. + * @param irq Interrupt number. + * @param irqCfg Platform-specific config (e.g., priority), or NULL for defaults. + * + * @retval WHAL_SUCCESS Interrupt enabled. + * @retval WHAL_EINVAL Null pointer or missing driver function. + */ +whal_Error whal_Irq_Enable(whal_Irq *irqDev, size_t irq, const void *irqCfg); +/* + * @brief Disable an interrupt line. + * + * @param irqDev Interrupt controller instance. + * @param irq Interrupt number. + * + * @retval WHAL_SUCCESS Interrupt disabled. + * @retval WHAL_EINVAL Null pointer or missing driver function. + */ +whal_Error whal_Irq_Disable(whal_Irq *irqDev, size_t irq); +#endif + +#endif /* WHAL_IRQ_H */ diff --git a/wolfHAL/platform/arm/cortex_m4.h b/wolfHAL/platform/arm/cortex_m4.h index 5a256ec..e90d266 100644 --- a/wolfHAL/platform/arm/cortex_m4.h +++ b/wolfHAL/platform/arm/cortex_m4.h @@ -2,6 +2,7 @@ #define WHAL_CORTEX_M4_H #include +#include #define WHAL_CORTEX_M4_SYSTICK_DEVICE \ .regmap = { \ @@ -10,4 +11,11 @@ }, \ .driver = &whal_SysTick_Driver +#define WHAL_CORTEX_M4_NVIC_DEVICE \ + .regmap = { \ + .base = 0xE000E100, \ + .size = 0x400, \ + }, \ + .driver = &whal_Nvic_Driver + #endif /* WHAL_CORTEX_M4_H */ diff --git a/wolfHAL/platform/st/stm32wb55xx.h b/wolfHAL/platform/st/stm32wb55xx.h index 1692e1b..f783ea5 100644 --- a/wolfHAL/platform/st/stm32wb55xx.h +++ b/wolfHAL/platform/st/stm32wb55xx.h @@ -6,10 +6,12 @@ #include #include #include +#include #include #include #include #include +#include /* * @file stm32wb55xx.h @@ -79,6 +81,30 @@ }, \ .driver = &whal_Stm32wbFlash_Driver +#define WHAL_STM32WB55_DMA1_DEVICE \ + .regmap = { \ + .base = 0x40020000, \ + .size = 0x400, \ + }, \ + .driver = &whal_Stm32wbDma_Driver + +#define WHAL_STM32WB55_DMA2_DEVICE \ + .regmap = { \ + .base = 0x40020400, \ + .size = 0x400, \ + }, \ + .driver = &whal_Stm32wbDma_Driver + +#define WHAL_STM32WB55_DMA1_CFG \ + .dmamuxBase = 0x40020800, \ + .dmamuxChOffset = 0, \ + .numChannels = 7 + +#define WHAL_STM32WB55_DMA2_CFG \ + .dmamuxBase = 0x40020800, \ + .dmamuxChOffset = 7, \ + .numChannels = 5 + #define WHAL_STM32WB55_PLL_CLOCK \ .regOffset = 0x00, \ @@ -125,5 +151,34 @@ .enableMask = (1UL << 12), \ .enablePos = 12 +#define WHAL_STM32WB55_DMA1_CLOCK \ + .regOffset = 0x48, \ + .enableMask = (1UL << 0), \ + .enablePos = 0 + +#define WHAL_STM32WB55_DMA2_CLOCK \ + .regOffset = 0x48, \ + .enableMask = (1UL << 1), \ + .enablePos = 1 + +#define WHAL_STM32WB55_UART1_TX_DMA_CFG \ + .dir = WHAL_STM32WB_DMA_DIR_MEM_TO_PERIPH, \ + .width = WHAL_STM32WB_DMA_WIDTH_8BIT, \ + .srcInc = WHAL_STM32WB_DMA_INC_ENABLE, \ + .dstInc = WHAL_STM32WB_DMA_INC_DISABLE, \ + .dmamuxReqId = 15 + +#define WHAL_STM32WB55_UART1_RX_DMA_CFG \ + .dir = WHAL_STM32WB_DMA_DIR_PERIPH_TO_MEM, \ + .width = WHAL_STM32WB_DMA_WIDTH_8BIT, \ + .srcInc = WHAL_STM32WB_DMA_INC_DISABLE, \ + .dstInc = WHAL_STM32WB_DMA_INC_ENABLE, \ + .dmamuxReqId = 14 + +#define WHAL_STM32WB55_DMAMUX1_CLOCK \ + .regOffset = 0x48, \ + .enableMask = (1UL << 2), \ + .enablePos = 2 + #endif /* WHAL_STM32WB55XX_H */ diff --git a/wolfHAL/uart/stm32wb_uart.h b/wolfHAL/uart/stm32wb_uart.h index 5157b7b..637836e 100644 --- a/wolfHAL/uart/stm32wb_uart.h +++ b/wolfHAL/uart/stm32wb_uart.h @@ -2,13 +2,12 @@ #define WHAL_STM32WB_UART_H #include +#include #include #include -#include - /* * @file stm32wb_uart.h - * @brief STM32 UART driver configuration. + * @brief STM32WB UART driver — polled variant. */ /* @@ -21,8 +20,10 @@ */ #define WHAL_STM32WB_LPUART_BRR(clk, baud) ((uint32_t)(((uint64_t)(clk) * 256) / (baud))) +/* Polled UART */ + /* - * @brief STM32 UART configuration parameters. + * @brief Polled UART configuration. */ typedef struct whal_Stm32wbUart_Cfg { uint32_t brr; @@ -30,47 +31,50 @@ typedef struct whal_Stm32wbUart_Cfg { } whal_Stm32wbUart_Cfg; /* - * @brief Driver instance for STM32 UART peripheral. + * @brief Polled UART driver. Implements Init, Deinit, Send, Recv. */ extern const whal_UartDriver whal_Stm32wbUart_Driver; /* - * @brief Initialize the STM32 UART peripheral. + * @brief Initialize the STM32WB UART peripheral. * - * @param uartDev UART device instance to initialize. + * @param uartDev UART device instance. * * @retval WHAL_SUCCESS Initialization completed. * @retval WHAL_EINVAL Invalid arguments. */ whal_Error whal_Stm32wbUart_Init(whal_Uart *uartDev); + /* - * @brief Deinitialize the STM32 UART peripheral. + * @brief Deinitialize the STM32WB UART peripheral. * - * @param uartDev UART device instance to deinitialize. + * @param uartDev UART device instance. * * @retval WHAL_SUCCESS Deinit completed. * @retval WHAL_EINVAL Invalid arguments. */ whal_Error whal_Stm32wbUart_Deinit(whal_Uart *uartDev); + /* - * @brief Transmit a buffer over UART. + * @brief Transmit a buffer over UART (blocking, polled). * * @param uartDev UART device instance. * @param data Buffer to transmit. * @param dataSz Number of bytes to transmit. * - * @retval WHAL_SUCCESS Transfer completed. + * @retval WHAL_SUCCESS All bytes transmitted. * @retval WHAL_EINVAL Invalid arguments. */ whal_Error whal_Stm32wbUart_Send(whal_Uart *uartDev, const void *data, size_t dataSz); + /* - * @brief Receive a buffer over UART. + * @brief Receive a buffer over UART (blocking, polled). * * @param uartDev UART device instance. * @param data Receive buffer. * @param dataSz Number of bytes to receive. * - * @retval WHAL_SUCCESS Transfer completed. + * @retval WHAL_SUCCESS All bytes received. * @retval WHAL_EINVAL Invalid arguments. */ whal_Error whal_Stm32wbUart_Recv(whal_Uart *uartDev, void *data, size_t dataSz); diff --git a/wolfHAL/uart/stm32wb_uart_dma.h b/wolfHAL/uart/stm32wb_uart_dma.h new file mode 100644 index 0000000..e883e41 --- /dev/null +++ b/wolfHAL/uart/stm32wb_uart_dma.h @@ -0,0 +1,118 @@ +#ifndef WHAL_STM32WB_UART_DMA_H +#define WHAL_STM32WB_UART_DMA_H + +#include +#include +#include +#include +#include + +/* + * @file stm32wb_uart_dma.h + * @brief STM32WB UART driver — DMA-backed variant. + */ + +/* + * @brief DMA-backed UART configuration. + * + * Used with whal_Stm32wbUartDma_Driver. Init/Deinit reuse the polled driver. + * Send/Recv block using DMA. SendAsync/RecvAsync return immediately. + */ +typedef struct { + whal_Stm32wbUart_Cfg base; + whal_Dma *dma; + size_t txCh; + size_t rxCh; + whal_Stm32wbDma_ChCfg *txChCfg; + whal_Stm32wbDma_ChCfg *rxChCfg; + volatile whal_Error txResult; + volatile whal_Error rxResult; +} whal_Stm32wbUartDma_Cfg; + +/* + * @brief DMA-backed UART driver. Implements Init, Deinit, Send, Recv, + * SendAsync, RecvAsync. + */ +extern const whal_UartDriver whal_Stm32wbUartDma_Driver; + +/* + * @brief Transmit a buffer over UART using DMA (blocking). + * + * Starts a DMA transfer and waits for completion with timeout. + * + * @param uartDev UART device instance. + * @param data Buffer to transmit. + * @param dataSz Number of bytes to transmit. + * + * @retval WHAL_SUCCESS All bytes transmitted. + * @retval WHAL_ETIMEOUT DMA transfer or UART TC timed out. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wbUartDma_Send(whal_Uart *uartDev, const void *data, + size_t dataSz); + +/* + * @brief Receive a buffer over UART using DMA (blocking). + * + * Starts a DMA transfer and waits for completion with timeout. + * + * @param uartDev UART device instance. + * @param data Receive buffer. + * @param dataSz Number of bytes to receive. + * + * @retval WHAL_SUCCESS All bytes received. + * @retval WHAL_ETIMEOUT DMA transfer timed out. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wbUartDma_Recv(whal_Uart *uartDev, void *data, + size_t dataSz); + +/* + * @brief Start an asynchronous DMA transmit. + * + * Returns immediately after starting the DMA transfer. The buffer must + * remain valid until cfg->txResult is no longer WHAL_ENOTREADY. + * + * @param uartDev UART device instance. + * @param data Buffer to transmit. Must remain valid until completion. + * @param dataSz Number of bytes to transmit. + * + * @retval WHAL_SUCCESS Transfer started. + * @retval WHAL_ENOTREADY A transfer is already in progress. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wbUartDma_SendAsync(whal_Uart *uartDev, const void *data, + size_t dataSz); + +/* + * @brief Start an asynchronous DMA receive. + * + * Returns immediately after starting the DMA transfer. The buffer must + * remain valid until cfg->rxResult is no longer WHAL_ENOTREADY. + * + * @param uartDev UART device instance. + * @param data Receive buffer. Must remain valid until completion. + * @param dataSz Number of bytes to receive. + * + * @retval WHAL_SUCCESS Receive started. + * @retval WHAL_ENOTREADY A transfer is already in progress. + * @retval WHAL_EINVAL Invalid arguments. + */ +whal_Error whal_Stm32wbUartDma_RecvAsync(whal_Uart *uartDev, void *data, + size_t dataSz); + +/* + * @brief DMA TX completion callback for use with whal_Stm32wbDma_IRQHandler. + * + * Sets cfg->txResult from the DMA error code. Pass the UART DMA cfg as ctx. + */ +void whal_Stm32wbUartDma_TxCallback(void *ctx, whal_Error err); + +/* + * @brief DMA RX completion callback for use with whal_Stm32wbDma_IRQHandler. + * + * Sets cfg->rxResult from the DMA error code. Pass the UART DMA cfg as ctx. + */ +void whal_Stm32wbUartDma_RxCallback(void *ctx, whal_Error err); + +#endif /* WHAL_STM32WB_UART_DMA_H */ diff --git a/wolfHAL/uart/uart.h b/wolfHAL/uart/uart.h index 5a37c93..36b8a85 100644 --- a/wolfHAL/uart/uart.h +++ b/wolfHAL/uart/uart.h @@ -25,6 +25,10 @@ typedef struct { whal_Error (*Send)(whal_Uart *uartDev, const void *data, size_t dataSz); /* Receive into a buffer. */ whal_Error (*Recv)(whal_Uart *uartDev, void *data, size_t dataSz); + /* Start an asynchronous transmit. Returns immediately. */ + whal_Error (*SendAsync)(whal_Uart *uartDev, const void *data, size_t dataSz); + /* Start an asynchronous receive. Returns immediately. */ + whal_Error (*RecvAsync)(whal_Uart *uartDev, void *data, size_t dataSz); } whal_UartDriver; /* @@ -41,6 +45,8 @@ struct whal_Uart { #define whal_Uart_Deinit(uartDev) ((uartDev)->driver->Deinit((uartDev))) #define whal_Uart_Send(uartDev, data, dataSz) ((uartDev)->driver->Send((uartDev), (data), (dataSz))) #define whal_Uart_Recv(uartDev, data, dataSz) ((uartDev)->driver->Recv((uartDev), (data), (dataSz))) +#define whal_Uart_SendAsync(uartDev, data, dataSz) ((uartDev)->driver->SendAsync((uartDev), (data), (dataSz))) +#define whal_Uart_RecvAsync(uartDev, data, dataSz) ((uartDev)->driver->RecvAsync((uartDev), (data), (dataSz))) #else /* * @brief Initializes a UART device and its driver. @@ -85,6 +91,40 @@ whal_Error whal_Uart_Send(whal_Uart *uartDev, const void *data, size_t dataSz); * @retval WHAL_EINVAL Null pointer or driver failed to receive. */ whal_Error whal_Uart_Recv(whal_Uart *uartDev, void *data, size_t dataSz); + +/* + * @brief Start an asynchronous transmit. + * + * Returns immediately. The buffer must remain valid until the transfer + * completes. Completion signaling is driver-specific (e.g., a status + * field in the driver config). Drivers that do not support async return + * WHAL_EINVAL. + * + * @param uartDev Pointer to the UART instance. + * @param data Buffer to transmit. Must remain valid until completion. + * @param dataSz Number of bytes to send. + * + * @retval WHAL_SUCCESS Transfer started. + * @retval WHAL_EINVAL Null pointer or async not supported. + */ +whal_Error whal_Uart_SendAsync(whal_Uart *uartDev, const void *data, size_t dataSz); + +/* + * @brief Start an asynchronous receive. + * + * Returns immediately. The buffer must remain valid until the transfer + * completes. Completion signaling is driver-specific (e.g., a status + * field in the driver config). Drivers that do not support async return + * WHAL_EINVAL. + * + * @param uartDev Pointer to the UART instance. + * @param data Receive buffer. Must remain valid until completion. + * @param dataSz Number of bytes to receive. + * + * @retval WHAL_SUCCESS Receive started. + * @retval WHAL_EINVAL Null pointer or async not supported. + */ +whal_Error whal_Uart_RecvAsync(whal_Uart *uartDev, void *data, size_t dataSz); #endif #endif /* WHAL_UART_H */ diff --git a/wolfHAL/wolfHAL.h b/wolfHAL/wolfHAL.h index 25836bc..2440672 100644 --- a/wolfHAL/wolfHAL.h +++ b/wolfHAL/wolfHAL.h @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include