diff --git a/examples/nxp/rt1020-make-baremetal/Makefile b/examples/nxp/rt1020-evk-make-baremetal-builtin/Makefile similarity index 62% rename from examples/nxp/rt1020-make-baremetal/Makefile rename to examples/nxp/rt1020-evk-make-baremetal-builtin/Makefile index 1b5533fe32..3807cff296 100644 --- a/examples/nxp/rt1020-make-baremetal/Makefile +++ b/examples/nxp/rt1020-evk-make-baremetal-builtin/Makefile @@ -2,22 +2,22 @@ CFLAGS = -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion CFLAGS += -Wformat-truncation -fno-common -Wconversion -Wno-sign-conversion CFLAGS += -g3 -Os -ffunction-sections -fdata-sections CFLAGS += -I. -Icmsis_core/CMSIS/Core/Include -Icmsis_mcu/devices/MIMXRT1021 #-DCPU_MIMXRT1021DAG5A -CFLAGS += -mcpu=cortex-m7 -mthumb -mfloat-abi=hard -mfpu=fpv5-sp-d16 -LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map -# cmsis_mcu/devices/MIMXRT1021/gcc/MIMXRT1021xxxxx_ram.ld -# cmsis_mcu/devices/MIMXRT1021/system_MIMXRT1021.c +CFLAGS += -mcpu=cortex-m7 -mthumb -mfloat-abi=hard -mfpu=fpv5-d16 +LDFLAGS ?= -Tlink_ram.ld -nostdlib -nostartfiles --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map SOURCES = main.c syscalls.c sysinit.c SOURCES += cmsis_mcu/devices/MIMXRT1021/gcc/startup_MIMXRT1021.S +CFLAGS += -D__ATOLLIC__ -D__STARTUP_CLEAR_BSS # Make startup code work as expected # Mongoose-specific. See https://mongoose.ws/documentation/#build-options -SOURCES += mongoose.c +SOURCES += mongoose.c #net.c packed_fs.c CFLAGS += -DMG_ENABLE_TCPIP=1 -DMG_ARCH=MG_ARCH_NEWLIB -DMG_ENABLE_CUSTOM_MILLIS=1 -CFLAGS += -DMG_ENABLE_CUSTOM_RANDOM=1 #-DMG_ENABLE_PACKED_FS=1 +CFLAGS += -DMG_ENABLE_CUSTOM_RANDOM=1 -DMG_ENABLE_PACKED_FS=1 -DMG_ENABLE_DRIVER_IMXRT1020=1 + CFLAGS += $(CFLAGS_EXTRA) # Example specific build options. See README.md -#CFLAGS += -DHTTP_URL=\"http://0.0.0.0/\" -DHTTPS_URL=\"https://0.0.0.0/\" +CFLAGS += -DHTTP_URL=\"http://0.0.0.0/\" -DHTTPS_URL=\"https://0.0.0.0/\" ifeq ($(OS),Windows_NT) RM = cmd /C del /Q /F /S @@ -32,6 +32,7 @@ firmware.bin: firmware.elf firmware.elf: cmsis_core cmsis_mcu $(SOURCES) hal.h link.ld Makefile arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@ + arm-none-eabi-size $@ flash: firmware.bin st-flash --reset write $< 0x8000000 @@ -56,8 +57,16 @@ endif DEVICE_URL ?= https://dash.vcon.io/api/v3/devices/4 update: firmware.bin curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/ota --data-binary @$< + curl -su :$(VCON_API_KEY) https://dash.vcon.io/api/v3/devices/4/rpc/swd.exec -d '{"req":"init"}' + curl -su :$(VCON_API_KEY) https://dash.vcon.io/api/v3/devices/4/rpc/swd.exec -d '{"req":"wm,e000edf0,a05f0003 wm,e000edfc,1 wm,e000ed0c,5fa0004"}' + curl -su :$(VCON_API_KEY) https://dash.vcon.io/api/v3/devices/4/rpc/swd.exec -d '{"req":"init"}' + PC=`curl -su :$(VCON_API_KEY) $(DEVICE_URL)/rpc/swd.exec -d '{"req":"rm,4"}' | jq -r .resp[5:]` && \ + SP=`curl -su :$(VCON_API_KEY) $(DEVICE_URL)/rpc/swd.exec -d '{"req":"rm,0"}' | jq -r .resp[5:]` && \ + REQ="wm,e000ed08,0 wr,d,$$SP wr,f,$$PC" && \ + curl -su :$(VCON_API_KEY) $(DEVICE_URL)/rpc/swd.exec -d '{"req":"'"$$REQ"'"}' + curl -su :$(VCON_API_KEY) $(DEVICE_URL)/rpc/swd.exec -d '{"req":"wm,e000edf0,a05f0001"}' -test update: CFLAGS += -DUART_DEBUG=USART1 +test update: CFLAGS += -DUART_DEBUG=LPUART2 test: update curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=5 | tee /tmp/output.txt grep 'READY, IP:' /tmp/output.txt # Check for network init diff --git a/examples/nxp/rt1020-evk-make-baremetal-builtin/hal.h b/examples/nxp/rt1020-evk-make-baremetal-builtin/hal.h new file mode 100644 index 0000000000..eb9dffa5e3 --- /dev/null +++ b/examples/nxp/rt1020-evk-make-baremetal-builtin/hal.h @@ -0,0 +1,288 @@ +// Copyright (c) 2023 Cesanta Software Limited +// All rights reserved +// https://www.nxp.com/webapp/Download?colCode=IMXRT1020RM +// https://cache.nxp.com/secured/assets/documents/en/user-guide/MIMXRT1020EVKHUG.pdf + +#pragma once + +#include "MIMXRT1021.h" + +#include +#include +#include +#include + +#define BIT(x) (1UL << (x)) +#define SETBITS(R, CLEARMASK, SETMASK) (R) = ((R) & ~(CLEARMASK)) | (SETMASK) +#define PIN(bank, num) ((((bank) - '0') << 8) | (num)) +#define PINNO(pin) (pin & 255) +#define PINBANK(pin) (pin >> 8) + +// Use LED for blinking, GPIO_AD_B0_05. GPIO1.5 (schematics) +#define LED PIN('1', 5) + +#ifndef UART_DEBUG +#define UART_DEBUG LPUART1 +#endif + +// No settable constants, see sysinit.c +#define SYS_FREQUENCY 500000000UL + +static inline void spin(volatile uint32_t count) { + while (count--) (void) 0; +} + +enum { CLOCK_OFF = 0U, CLOCK_ON_RUN = 1U, CLOCK_ON_RUN_WAIT = 3U }; +static inline void clock_periph(uint32_t index, uint32_t shift, uint32_t val) { + volatile uint32_t *r = &CCM->CCGR0; + SETBITS(r[index], 3UL << shift, val << shift); +} + +// which peripheral feeds the pin +static inline void gpio_mux_config(uint16_t index, uint8_t af) { + IOMUXC->SW_MUX_CTL_PAD[index] = af; +} + +// which pin feeds the peripheral (2nd stage) +static inline void periph_mux_config(uint16_t index, uint8_t in) { + IOMUXC->SELECT_INPUT[index] = in; +} + +enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT }; +enum { GPIO_OTYPE_PUSH_PULL, GPIO_OTYPE_OPEN_DRAIN }; +enum { GPIO_SPEED_LOW, GPIO_SPEED_MEDIUM, GPIO_SPEED_MEDIUM_, GPIO_SPEED_HIGH }; +enum { GPIO_PULL_NONE, GPIO_PULL_DOWN, GPIO_PULL_UP }; +static inline GPIO_Type *gpio_bank(uint16_t pin) { + static const GPIO_Type *g[] = {NULL, GPIO1, GPIO2, GPIO3, NULL, GPIO5}; + return (GPIO_Type *) g[PINBANK(pin)]; +} + +// pin driver/pull-up/down configuration (ignore "keeper") +static inline void gpio_pad_config(uint16_t index, uint8_t type, uint8_t speed, + uint8_t pull) { + bool dopull = pull > 0; + if (dopull) --pull; + IOMUXC->SW_PAD_CTL_PAD[index] = + IOMUXC_SW_PAD_CTL_PAD_SPEED(speed) | IOMUXC_SW_PAD_CTL_PAD_ODE(type) | + IOMUXC_SW_PAD_CTL_PAD_SRE(speed >= GPIO_SPEED_HIGH) | + IOMUXC_SW_PAD_CTL_PAD_PUE(1) | IOMUXC_SW_PAD_CTL_PAD_PKE(dopull) | + IOMUXC_SW_PAD_CTL_PAD_PUS(pull) | IOMUXC_SW_PAD_CTL_PAD_DSE(7); +} + +static inline void gpio_init(uint16_t pin, uint8_t mode, uint8_t type, + uint8_t speed, uint8_t pull) { + GPIO_Type *gpio = gpio_bank(pin); + uint8_t bit = (uint8_t) PINNO(pin); + uint32_t mask = (uint32_t) BIT(PINNO(pin)); + + clock_periph(4, CCM_CCGR4_CG1_SHIFT, CLOCK_ON_RUN_WAIT); // iomuxc_ipg_clk_s + switch (PINBANK(pin)) { + case 1: + gpio_mux_config(kIOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B0_00 + bit, 5); + gpio_pad_config(kIOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B0_00 + bit, type, speed, + pull); + clock_periph(1, CCM_CCGR1_CG13_SHIFT, CLOCK_ON_RUN_WAIT); + break; + case 2: + gpio_mux_config(kIOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_00 + bit, 5); + gpio_pad_config(kIOMUXC_SW_PAD_CTL_PAD_GPIO_EMC_00 + bit, type, speed, + pull); + clock_periph(0, CCM_CCGR0_CG15_SHIFT, CLOCK_ON_RUN_WAIT); + break; + case 3: + gpio_mux_config(bit < 13 + ? kIOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_32 + bit + : kIOMUXC_SW_MUX_CTL_PAD_GPIO_SD_B0_00 + bit - 13, + 5); + gpio_pad_config(bit < 13 + ? kIOMUXC_SW_PAD_CTL_PAD_GPIO_EMC_32 + bit + : kIOMUXC_SW_PAD_CTL_PAD_GPIO_SD_B0_00 + bit - 13, + type, speed, pull); + clock_periph(2, CCM_CCGR2_CG13_SHIFT, CLOCK_ON_RUN_WAIT); + break; + case 5: + // TODO(): support sw_mux + clock_periph(1, CCM_CCGR1_CG15_SHIFT, CLOCK_ON_RUN_WAIT); + break; + default: + break; + } + + gpio->IMR &= ~mask; + if (mode == GPIO_MODE_INPUT) { + gpio->GDIR &= ~mask; + } else { + gpio->GDIR |= mask; + } +} +static inline void gpio_input(uint16_t pin) { + gpio_init(pin, GPIO_MODE_INPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_MEDIUM, + GPIO_PULL_NONE); +} +static inline void gpio_output(uint16_t pin) { + gpio_init(pin, GPIO_MODE_OUTPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_MEDIUM, + GPIO_PULL_NONE); +} + +static inline bool gpio_read(uint16_t pin) { + GPIO_Type *gpio = gpio_bank(pin); + uint32_t mask = (uint32_t) BIT(PINNO(pin)); + return gpio->DR & mask; +} +static inline void gpio_write(uint16_t pin, bool value) { + GPIO_Type *gpio = gpio_bank(pin); + uint32_t mask = (uint32_t) BIT(PINNO(pin)); + if (value) { + gpio->DR |= mask; + } else { + gpio->DR &= ~mask; + } +} +static inline void gpio_toggle(uint16_t pin) { + gpio_write(pin, !gpio_read(pin)); +} + +// 14.5 Table 14-4: uart_clk_root +// see sysinit.c for clocks, (14.7.9: defaults to PLL3/6/1 = 80MHz) +static inline void uart_init(LPUART_Type *uart, unsigned long baud) { + uint8_t af = 2; // Alternate function + uint16_t mr = 0, pr = 0, mt = 0, pt = 0; // pins + uint32_t freq = 80000000; // uart_clk_root frequency + if (uart == LPUART1) + mt = kIOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B0_06, + pt = kIOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B0_06, + mr = kIOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B0_07, + pr = kIOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B0_07; + if (uart == LPUART2) + mt = kIOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_08, + pt = kIOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_08, + mr = kIOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B1_09, + pr = kIOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B1_09; + + if (uart == LPUART1) clock_periph(5, CCM_CCGR5_CG12_SHIFT, CLOCK_ON_RUN_WAIT); + if (uart == LPUART2) clock_periph(0, CCM_CCGR0_CG14_SHIFT, CLOCK_ON_RUN_WAIT); + clock_periph(4, CCM_CCGR4_CG1_SHIFT, CLOCK_ON_RUN_WAIT); // iomuxc_ipg_clk_s + gpio_mux_config(mt, af); + gpio_pad_config(pt, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_MEDIUM, GPIO_PULL_UP); + gpio_mux_config(mr, af); + gpio_pad_config(pr, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_MEDIUM, GPIO_PULL_UP); + + uart->GLOBAL |= LPUART_GLOBAL_RST_MASK; // reset, CTRL = 0, defaults + uart->GLOBAL &= ~LPUART_GLOBAL_RST_MASK; + SETBITS(uart->BAUD, + LPUART_BAUD_OSR_MASK | LPUART_BAUD_SBR_MASK | LPUART_BAUD_SBNS_MASK, + LPUART_BAUD_OSR(16 - 1) | + LPUART_BAUD_SBR(freq / (16 * baud))); // Rx sample at 16x + SETBITS(uart->CTRL, + LPUART_CTRL_PE_MASK | LPUART_CTRL_M_MASK | LPUART_CTRL_ILT_MASK | + LPUART_CTRL_IDLECFG_MASK, + LPUART_CTRL_IDLECFG(1) | LPUART_CTRL_ILT(1) | + LPUART_BAUD_SBNS(0)); // no parity, idle 2 chars after 1 stop bit + uart->CTRL |= LPUART_CTRL_TE_MASK | LPUART_CTRL_RE_MASK; +} + +static inline void uart_write_byte(LPUART_Type *uart, uint8_t byte) { + uart->DATA = byte; + while ((uart->STAT & LPUART_STAT_TDRE_MASK) == 0) spin(1); +} +static inline void uart_write_buf(LPUART_Type *uart, char *buf, size_t len) { + while (len-- > 0) uart_write_byte(uart, *(uint8_t *) buf++); +} +static inline int uart_read_ready(LPUART_Type *uart) { + (void) uart; + return uart->STAT & LPUART_STAT_RDRF_MASK; +} +static inline uint8_t uart_read_byte(LPUART_Type *uart) { + return (uint8_t) (uart->DATA & 255); +} + +// TODO(NXP): get RNG usage specs +static inline void rng_init(void) { +} +#include +static inline uint32_t rng_read(void) { + return (uint32_t) rand(); +} + +// - PHY has no xtal, XI driven from ENET_REF_CLK1 (labeled as ENET_TX_CLK +// (GPIO_AD_B0_08)), generated by the MCU +// - PHY RST connected to GPIO1.4 (GPIO_AD_B0_04); INTRP/NAND_TREE connected to +// GPIO1.22 (GPIO_AD_B1_06) +// - 37.4 REF_CLK1 is RMII mode reference clock for Rx, Tx, and SMI; it is I/O +// - 11.4.2 IOMUXC_GPR_GPR1 bit 17: ENET_REF_CLK_DIR --> 1 ENET_REF_CLK is +// output driven by ref_enetpll0 +// - 14.6.1.3.4 Ethernet PLL (PLL6) +static inline void ethernet_init(void) { + gpio_init(PIN('1', 4), GPIO_MODE_OUTPUT, GPIO_OTYPE_PUSH_PULL, + GPIO_SPEED_MEDIUM, GPIO_PULL_UP); // set GPIO1.4 as GPIO (PHY \RST) + gpio_write(PIN('1', 4), 0); // reset PHY + + gpio_mux_config(kIOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B0_08, + 4); // set for ENET_REF_CLK1 + IOMUXC->SW_MUX_CTL_PAD[kIOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B0_08] |= + IOMUXC_SW_MUX_CTL_PAD_SION(1); // loop signal back from pin + periph_mux_config(kIOMUXC_ENET_RMII_SELECT_INPUT, + 1); // drive peripheral from B0_08, so RMII clock is taken + // from ENET_REF_CLK1 + gpio_pad_config(kIOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B0_08, GPIO_OTYPE_PUSH_PULL, + GPIO_SPEED_HIGH, GPIO_PULL_UP); + gpio_mux_config(kIOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B0_09, 0); // set for RXDATA1 + periph_mux_config(kIOMUXC_ENET_RX_DATA1_SELECT_INPUT, + 1); // drive peripheral from B0_09 + gpio_pad_config(kIOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B0_09, GPIO_OTYPE_PUSH_PULL, + GPIO_SPEED_HIGH, GPIO_PULL_UP); + gpio_mux_config(kIOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B0_10, 0); // set for RXDATA0 + periph_mux_config(kIOMUXC_ENET_RX_DATA0_SELECT_INPUT, + 1); // drive peripheral from B0_10 + gpio_pad_config(kIOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B0_10, GPIO_OTYPE_PUSH_PULL, + GPIO_SPEED_HIGH, GPIO_PULL_UP); + gpio_mux_config(kIOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B0_11, 0); // set for CRS + periph_mux_config(kIOMUXC_ENET_RX_EN_SELECT_INPUT, + 1); // drive peripheral from B0_11 + gpio_pad_config(kIOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B0_11, GPIO_OTYPE_PUSH_PULL, + GPIO_SPEED_HIGH, GPIO_PULL_UP); + gpio_mux_config(kIOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B0_12, 0); // set for RXERR + periph_mux_config(kIOMUXC_ENET_RX_ERR_SELECT_INPUT, + 1); // drive peripheral from B0_12 + gpio_pad_config(kIOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B0_12, GPIO_OTYPE_PUSH_PULL, + GPIO_SPEED_HIGH, GPIO_PULL_UP); + gpio_mux_config(kIOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B0_13, 0); // set for TXEN + gpio_pad_config(kIOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B0_13, GPIO_OTYPE_PUSH_PULL, + GPIO_SPEED_HIGH, GPIO_PULL_UP); + gpio_mux_config(kIOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B0_14, 0); // set for TXDATA0 + gpio_pad_config(kIOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B0_14, GPIO_OTYPE_PUSH_PULL, + GPIO_SPEED_HIGH, GPIO_PULL_UP); + gpio_mux_config(kIOMUXC_SW_MUX_CTL_PAD_GPIO_AD_B0_15, 0); // set for TXDATA1 + gpio_pad_config(kIOMUXC_SW_PAD_CTL_PAD_GPIO_AD_B0_15, GPIO_OTYPE_PUSH_PULL, + GPIO_SPEED_HIGH, GPIO_PULL_UP); + gpio_mux_config(kIOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_40, 4); // set for MDIO + periph_mux_config(kIOMUXC_ENET_MDIO_SELECT_INPUT, + 2); // drive peripheral from EMC_40 + gpio_pad_config(kIOMUXC_SW_PAD_CTL_PAD_GPIO_EMC_40, GPIO_OTYPE_PUSH_PULL, + GPIO_SPEED_MEDIUM, GPIO_PULL_UP); + gpio_mux_config(kIOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_41, 4); // set for MDC + gpio_pad_config(kIOMUXC_SW_PAD_CTL_PAD_GPIO_EMC_41, GPIO_OTYPE_PUSH_PULL, + GPIO_SPEED_MEDIUM, GPIO_PULL_UP); + + gpio_init(PIN('1', 22), GPIO_MODE_OUTPUT, GPIO_OTYPE_PUSH_PULL, + GPIO_SPEED_MEDIUM, + GPIO_PULL_UP); // set GPIO1.22 as GPIO (PHY INTRP/NAND_TREE) + gpio_write(PIN('1', 22), 1); // prevent NAND_TREE + // 14.8.9 Use 500MHz reference and generate 50MHz. This is done at sysinit.c, + // as we use this source to clock the core + spin(10000); // keep PHY RST low for a while + gpio_write(PIN('1', 4), 1); // deassert RST + gpio_init(PIN('1', 22), GPIO_MODE_INPUT, 0, GPIO_SPEED_MEDIUM, + GPIO_PULL_UP); // setup IRQ (pulled-up)(not used) + + IOMUXC_GPR->GPR1 |= + IOMUXC_GPR_GPR1_ENET_REF_CLK_DIR(1); // Set ENET_REF_CLK1 as output + clock_periph(1, CCM_CCGR1_CG5_SHIFT, CLOCK_ON_RUN_WAIT); // enet_ipg_clk + NVIC_EnableIRQ(ENET_IRQn); // Setup Ethernet IRQ handler +} + +// Helper macro for MAC generation +#define GENERATE_LOCALLY_ADMINISTERED_MAC() \ + { 2, 0, 2, 5, 7, 91 } +// *((uint32_t *)(uintptr_t)&uid[0]) = OCOTP->CFG0; +// *((uint32_t *)(uintptr_t)&uid[4]) = OCOTP->CFG1; diff --git a/examples/nxp/rt1020-make-baremetal/link.ld b/examples/nxp/rt1020-evk-make-baremetal-builtin/link.ld similarity index 100% rename from examples/nxp/rt1020-make-baremetal/link.ld rename to examples/nxp/rt1020-evk-make-baremetal-builtin/link.ld diff --git a/examples/nxp/rt1020-make-baremetal/link2.ld b/examples/nxp/rt1020-evk-make-baremetal-builtin/link2.ld similarity index 100% rename from examples/nxp/rt1020-make-baremetal/link2.ld rename to examples/nxp/rt1020-evk-make-baremetal-builtin/link2.ld diff --git a/examples/nxp/rt1020-evk-make-baremetal-builtin/link_ram.ld b/examples/nxp/rt1020-evk-make-baremetal-builtin/link_ram.ld new file mode 100644 index 0000000000..1240f1d27e --- /dev/null +++ b/examples/nxp/rt1020-evk-make-baremetal-builtin/link_ram.ld @@ -0,0 +1,16 @@ +ENTRY(Reset_Handler); +MEMORY { + itcram(rx) : ORIGIN = 0x00000000, LENGTH = 64k + dtcram(rw) : ORIGIN = 0x20000000, LENGTH = 64k + ocram(rw) : ORIGIN = 0x20200000, LENGTH = 128k # This is cached ! +} +__StackTop = ORIGIN(dtcram) + LENGTH(dtcram); + +SECTIONS { + .irq : { KEEP(*(.isr_vector)) } > itcram + .text : { *(.text* .text.*) *(.rodata*) ; } > itcram + .data : { __data_start__ = .; *(.data SORT(.data.*)) __data_end__ = .; } > dtcram AT > itcram + __etext = LOADADDR(.data); + .bss : { __bss_start__ = .; *(.bss SORT(.bss.*) COMMON) __bss_end__ = .; } > dtcram + _end = .; +} diff --git a/examples/nxp/rt1020-make-baremetal/main.c b/examples/nxp/rt1020-evk-make-baremetal-builtin/main.c similarity index 73% rename from examples/nxp/rt1020-make-baremetal/main.c rename to examples/nxp/rt1020-evk-make-baremetal-builtin/main.c index c7d12673ca..9f60d034bf 100644 --- a/examples/nxp/rt1020-make-baremetal/main.c +++ b/examples/nxp/rt1020-evk-make-baremetal-builtin/main.c @@ -3,7 +3,7 @@ #include "hal.h" #include "mongoose.h" -// #include "net.h" +#include "net.h" #define BLINK_PERIOD_MS 1000 // LED blinking period in millis @@ -22,26 +22,17 @@ void mg_random(void *buf, size_t len) { // Use on-board RNG memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r)); } } - static void timer_fn(void *arg) { - //gpio_toggle(LED); // Blink LED - //struct mg_tcpip_if *ifp = arg; // And show - //const char *names[] = {"down", "up", "req", "ready"}; // network stats - //MG_INFO(("Ethernet: %s, IP: %M, rx:%u, tx:%u, dr:%u, er:%u", - // names[ifp->state], mg_print_ip4, &ifp->ip, ifp->nrecv, ifp->nsent, - // ifp->ndrop, ifp->nerr)); - MG_INFO(("%p", arg)); + gpio_toggle(LED); // Blink LED + struct mg_tcpip_if *ifp = arg; // And show + const char *names[] = {"down", "up", "req", "ready"}; // network stats + MG_INFO(("Ethernet: %s, IP: %M, rx:%u, tx:%u, dr:%u, er:%u", + names[ifp->state], mg_print_ip4, &ifp->ip, ifp->nrecv, ifp->nsent, + ifp->ndrop, ifp->nerr)); } int main(void) { gpio_output(LED); // Setup blue LED - - for (;;) { - gpio_toggle(LED); - spin(99999); - } - - uart_init(UART_DEBUG, 115200); // Initialise debug printf ethernet_init(); // Initialise ethernet pins MG_INFO(("Starting, CPU freq %g MHz", (double) SystemCoreClock / 1000000)); @@ -49,17 +40,15 @@ int main(void) { struct mg_mgr mgr; // Initialise mg_mgr_init(&mgr); // Mongoose event manager mg_log_set(MG_LL_DEBUG); // Set log level - mg_timer_add(&mgr, BLINK_PERIOD_MS, MG_TIMER_REPEAT, timer_fn, NULL); -#if 0 // Initialise Mongoose network stack - struct mg_tcpip_driver_stm32_data driver_data = {.mdc_cr = 4}; + struct mg_tcpip_driver_imxrt1020_data driver_data = {.mdc_cr = 4}; struct mg_tcpip_if mif = {.mac = GENERATE_LOCALLY_ADMINISTERED_MAC(), // Uncomment below for static configuration: // .ip = mg_htonl(MG_U32(192, 168, 0, 223)), // .mask = mg_htonl(MG_U32(255, 255, 255, 0)), // .gw = mg_htonl(MG_U32(192, 168, 0, 1)), - .driver = &mg_tcpip_driver_stm32, + .driver = &mg_tcpip_driver_imxrt1020, .driver_data = &driver_data}; mg_tcpip_init(&mgr, &mif); mg_timer_add(&mgr, BLINK_PERIOD_MS, MG_TIMER_REPEAT, timer_fn, &mif); @@ -70,8 +59,7 @@ int main(void) { } MG_INFO(("Initialising application...")); - web_init(&mgr); -#endif +// web_init(&mgr); MG_INFO(("Starting event loop")); for (;;) { diff --git a/examples/nxp/rt1020-make-baremetal/mongoose.c b/examples/nxp/rt1020-evk-make-baremetal-builtin/mongoose.c similarity index 100% rename from examples/nxp/rt1020-make-baremetal/mongoose.c rename to examples/nxp/rt1020-evk-make-baremetal-builtin/mongoose.c diff --git a/examples/nxp/rt1020-make-baremetal/mongoose.h b/examples/nxp/rt1020-evk-make-baremetal-builtin/mongoose.h similarity index 100% rename from examples/nxp/rt1020-make-baremetal/mongoose.h rename to examples/nxp/rt1020-evk-make-baremetal-builtin/mongoose.h diff --git a/examples/nxp/rt1020-evk-make-baremetal-builtin/net.c b/examples/nxp/rt1020-evk-make-baremetal-builtin/net.c new file mode 120000 index 0000000000..fe0e6f06e7 --- /dev/null +++ b/examples/nxp/rt1020-evk-make-baremetal-builtin/net.c @@ -0,0 +1 @@ +../../device-dashboard/net.c \ No newline at end of file diff --git a/examples/nxp/rt1020-evk-make-baremetal-builtin/net.h b/examples/nxp/rt1020-evk-make-baremetal-builtin/net.h new file mode 120000 index 0000000000..9de896ef4e --- /dev/null +++ b/examples/nxp/rt1020-evk-make-baremetal-builtin/net.h @@ -0,0 +1 @@ +../../device-dashboard/net.h \ No newline at end of file diff --git a/examples/nxp/rt1020-evk-make-baremetal-builtin/packed_fs.c b/examples/nxp/rt1020-evk-make-baremetal-builtin/packed_fs.c new file mode 120000 index 0000000000..e06bf09258 --- /dev/null +++ b/examples/nxp/rt1020-evk-make-baremetal-builtin/packed_fs.c @@ -0,0 +1 @@ +../../device-dashboard/packed_fs.c \ No newline at end of file diff --git a/examples/nxp/rt1020-make-baremetal/syscalls.c b/examples/nxp/rt1020-evk-make-baremetal-builtin/syscalls.c similarity index 95% rename from examples/nxp/rt1020-make-baremetal/syscalls.c rename to examples/nxp/rt1020-evk-make-baremetal-builtin/syscalls.c index a7c8d622c6..6fef1007c4 100644 --- a/examples/nxp/rt1020-make-baremetal/syscalls.c +++ b/examples/nxp/rt1020-evk-make-baremetal-builtin/syscalls.c @@ -9,12 +9,12 @@ int _fstat(int fd, struct stat *st) { } void *_sbrk(int incr) { - extern char __end__; + extern char _end; static unsigned char *heap = NULL; unsigned char *prev_heap; unsigned char x = 0, *heap_end = (unsigned char *)((size_t) &x - 512); (void) x; - if (heap == NULL) heap = (unsigned char *) &__end__; + if (heap == NULL) heap = (unsigned char *) &_end; prev_heap = heap; if (heap + incr > heap_end) return (void *) -1; heap += incr; diff --git a/examples/nxp/rt1020-evk-make-baremetal-builtin/sysinit.c b/examples/nxp/rt1020-evk-make-baremetal-builtin/sysinit.c new file mode 100644 index 0000000000..d60a5254e2 --- /dev/null +++ b/examples/nxp/rt1020-evk-make-baremetal-builtin/sysinit.c @@ -0,0 +1,106 @@ +// Copyright (c) 2023 Cesanta Software Limited +// All rights reserved +// +// This file contains essentials required by the CMSIS: +// uint32_t SystemCoreClock - holds the system core clock value +// SystemInit() - initialises the system, e.g. sets up clocks + +#include "hal.h" + +uint32_t SystemCoreClock = SYS_FREQUENCY; + +// - 14.4, Figure 14-2: clock tree +// - 14.7.4: ARM_PODF defaults to /1 +// - 14.7.5: AHB_PODF defaults to /1; PERIPH_CLK_SEL defaults to derive clock +// from pre_periph_clk_sel +// - 14.7.6: PRE_PERIPH_CLK_SEL defaults to derive clock from PLL2 PFD3. +// - (For 528MHz operation, we need to set it to derive clock from PLL2, but +// this chip max is 500 MHz). +// - 14.6.1.3.1 System PLL (PLL2); 13.3.2.2 PLLs; 14.6.1.4 Phase Fractional +// Dividers (PFD) +// - 14.8.2: PLL2 is powered off and bypassed to 24MHz +// - 14.8.11: PFD defaults to 18/16 but Figure 14-2 shows half the value +// ("divider") +// - For 500MHz operation, we need to set PRE_PERIPH_CLK_SEL to derive clock +// from divided PLL6 +// - 14.8.9: configure PLL6 to generate a 500MHz clock +// - Datasheet 4.1.3: System frequency/Bus frequency max 500/125MHz respectively +// (AHB/IPG) +// - MCUXpresso: IPG_CLK_ROOT <= 125MHz; PERCLK_CLK_ROOT <= 62.5MHz +// - Datasheet 4.8.4.1.1/2: the processor clock frequency must exceed twice the +// ENET_RX_CLK/ENET_TX_CLK frequency. +// - Datasheet 4.8.4.2: no details for RMII (above is for MII), assumed 50MHz +// min processor clock +// - Datasheet 4.1.3, Table 11: "Overdrive" run mode requires 1.25V core voltage +// minimum +void SystemInit(void) { // Called automatically by startup code (ints masked) + SCB->CPACR |= ((3UL << 10 * 2) | (3UL << 11 * 2)); // Enable FPU + asm("DSB"); + asm("ISB"); + // 53.4.2: Disable watchdog after reset (unlocked) + RTWDOG->CS &= ~RTWDOG_CS_EN_MASK; + RTWDOG->TOVAL = 0xFFFF; + while (RTWDOG->CS & RTWDOG_CS_ULK_MASK) spin(1); // wait for lock + while ((RTWDOG->CS & RTWDOG_CS_RCS_MASK) == 0) + spin(1); // wait for new config + // Set VDD_SOC to 1.25V + SETBITS(DCDC->REG3, DCDC_REG3_TRG_MASK, DCDC_REG3_TRG(0x12)); + while ((DCDC->REG0 & DCDC_REG0_STS_DC_OK_MASK) == 0) + spin(1); // Wait for DCDC_STS_DC_OK + // 14.8.9 Init 500MHz reference, clock the M7 core with it, generate 50MHz for + // ENET and RMII. + SETBITS(CCM_ANALOG->PLL_ENET, CCM_ANALOG_PLL_ENET_BYPASS_CLK_SRC_MASK, + CCM_ANALOG_PLL_ENET_BYPASS_MASK | + CCM_ANALOG_PLL_ENET_BYPASS_CLK_SRC(0)); // bypass to 24MHz osc + SETBITS( + CCM_ANALOG->PLL_ENET, + CCM_ANALOG_PLL_ENET_DIV_SELECT_MASK | CCM_ANALOG_PLL_ENET_POWERDOWN_MASK, + CCM_ANALOG_PLL_ENET_DIV_SELECT(1) | CCM_ANALOG_PLL_ENET_ENABLE_MASK | + CCM_ANALOG_PLL_ENET_ENET_500M_REF_EN_MASK); // setup PLL + while ((CCM_ANALOG->PLL_ENET & CCM_ANALOG_PLL_ENET_LOCK_MASK) == 0) + spin(1); // wait until it is stable + CCM_ANALOG->PLL_ENET &= + ~CCM_ANALOG_PLL_ENET_BYPASS_MASK; // Disable Bypass (switch to PLL) + SETBITS(CCM->CBCDR, CCM_CBCDR_IPG_PODF_MASK, + CCM_CBCDR_IPG_PODF(3)); // Set IPG divider /4 (125MHz) + SETBITS(CCM->CSCMR1, CCM_CSCMR1_PERCLK_PODF_MASK, + CCM_CSCMR1_PERCLK_PODF(1)); // Set PERCLK divider /2 (62.5MHz) + SETBITS(CCM->CBCMR, CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK, + CCM_CBCMR_PRE_PERIPH_CLK_SEL(3)); // run from 500MHz clock + // 14.5 Table 14-4: uart_clk_root + // 14.4: uart_clk_root = PLL3/6 or OSC; CCM_CSCDR1 (14.7.9) defaults to + // PLL3/6/1 + CCM_ANALOG->PLL_USB1 |= CCM_ANALOG_PLL_USB1_POWER_MASK; // Power PLL on + while ((CCM_ANALOG->PLL_USB1 & CCM_ANALOG_PLL_USB1_LOCK_MASK) == 0) + spin(1); // wait until it is stable + CCM_ANALOG->PLL_USB1 &= + ~CCM_ANALOG_PLL_USB1_BYPASS_MASK; // Disable Bypass (switch to PLL) + rng_init(); // Initialise random number generator + // NXP startup code calls SystemInit BEFORE initializing RAM... + SysTick_Config(SYS_FREQUENCY / 1000); // Sys tick every 1ms +} + +#if 0 +__attribute__((section(".cfg"), used)) uint32_t __cfg[] = {0x1234abcd}; + +extern uint32_t __isr_vector[]; +extern uint32_t __ivt_boot_data[]; + +__attribute__((section(".ivt"), used)) uint32_t __ivt[8] = { + 0x412000d1, // header: 41 - version, 2000 size, d1 tag + (uint32_t) __isr_vector, // entry + 0, // reserved + 0, // dcd + (uint32_t) __ivt_boot_data, // boot data + (uint32_t) __ivt, // this is us - ivt absolute address + 0, // csf absolute address + 0, // reserved for HAB +}; + +__attribute__((section(".ivt"), used)) uint32_t __ivt_boot_data[] = { + 0, // boot start location + 64 * 1024, // size + 0, // Plugin flag + 0Xffffffff // empty - extra data word +}; +#endif diff --git a/examples/nxp/rt1020-make-baremetal/sysinit2.c b/examples/nxp/rt1020-evk-make-baremetal-builtin/sysinit2.c similarity index 100% rename from examples/nxp/rt1020-make-baremetal/sysinit2.c rename to examples/nxp/rt1020-evk-make-baremetal-builtin/sysinit2.c diff --git a/examples/nxp/rt1020-make-baremetal/hal.h b/examples/nxp/rt1020-make-baremetal/hal.h deleted file mode 100644 index 7ce32e1057..0000000000 --- a/examples/nxp/rt1020-make-baremetal/hal.h +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (c) 2023 Cesanta Software Limited -// All rights reserved -// https://www.nxp.com/webapp/Download?colCode=IMXRT1020RM -// https://cache.nxp.com/secured/assets/documents/en/user-guide/MIMXRT1020EVKHUG.pdf - -#pragma once - -#include "MIMXRT1021.h" -// #include "drivers/fsl_clock.h" - -#include -#include -#include -#include - -#define BIT(x) (1UL << (x)) -#define SETBITS(R, CLEARMASK, SETMASK) (R) = ((R) & ~(CLEARMASK)) | (SETMASK) -#define PIN(bank, num) ((((bank) - 'A') << 8) | (num)) -#define PINNO(pin) (pin & 255) -#define PINBANK(pin) (pin >> 8) - -#define LED PIN('A', 6) // Use LED for blinking, GPIO_AD_B0_06. RM tbl 9-1 - -#ifndef UART_DEBUG -#define UART_DEBUG LPUART1 -#endif - -#define SYS_FREQUENCY 16000000 - -static inline void spin(volatile uint32_t count) { - while (count--) (void) 0; -} - -static inline GPIO_Type *gpio_bank(uint16_t pin) { - switch (PINBANK(pin)) { - case 1: return (GPIO_Type *) GPIO1_BASE; - case 2: return (GPIO_Type *) GPIO2_BASE; - case 3: return (GPIO_Type *) GPIO3_BASE; - case 5: return (GPIO_Type *) GPIO5_BASE; - default: return NULL; - } -} - -enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT, GPIO_MODE_AF, GPIO_MODE_ANALOG }; -// enum { GPIO_OTYPE_PUSH_PULL, GPIO_OTYPE_OPEN_DRAIN }; -// enum { GPIO_PULL_NONE, GPIO_PULL_UP, GPIO_PULL_DOWN }; - -#if 0 -static inline void CLOCK_ControlGate(clock_ip_name_t name, - clock_gate_value_t value) { - uint32_t index = ((uint32_t) name) >> 8U; - uint32_t shift = ((uint32_t) name) & 0x1FU; - volatile uint32_t *reg; - assert(index <= 6UL); - reg = (volatile uint32_t *) ((uint32_t) ((volatile uint32_t *) &CCM->CCGR0) + - sizeof(volatile uint32_t *) * index); - SDK_ATOMIC_LOCAL_CLEAR_AND_SET(reg, (3UL << shift), - (((uint32_t) value) << shift)); -} - -static inline void CLOCK_EnableClock(clock_ip_name_t name) { - CLOCK_ControlGate(name, kCLOCK_ClockNeededRunWait); -} -#endif - -enum { CLOCK_OFF = 0U, CLOCK_ON_RUN = 1U, CLOCK_ON_RUN_WAIT = 3U }; -static inline void clock_periph(uint32_t index, uint32_t shift, uint32_t val) { - volatile uint32_t *r = &CCM->CCGR0; - SETBITS(r[index], 3UL << shift, val << shift); -} - -static inline void gpio_init(uint16_t pin, uint8_t mode) { - GPIO_Type *gpio = gpio_bank(pin); - uint32_t mask = (uint32_t) BIT(PINNO(pin)); - - // Enable clock - switch (PINBANK(pin)) { - case 1: clock_periph(1, CCM_CCGR1_CG13_SHIFT, CLOCK_ON_RUN_WAIT); break; - case 2: clock_periph(0, CCM_CCGR0_CG15_SHIFT, CLOCK_ON_RUN_WAIT); break; - case 3: clock_periph(2, CCM_CCGR2_CG13_SHIFT, CLOCK_ON_RUN_WAIT); break; - case 5: clock_periph(1, CCM_CCGR1_CG15_SHIFT, CLOCK_ON_RUN_WAIT); break; - default: break; - } - - gpio->IMR &= ~mask; - if (mode == GPIO_MODE_INPUT) { - gpio->GDIR &= ~mask; - } else { - gpio->GDIR |= mask; - } -} -static inline void gpio_input(uint16_t pin) { - gpio_init(pin, GPIO_MODE_INPUT); -} -static inline void gpio_output(uint16_t pin) { - gpio_init(pin, GPIO_MODE_OUTPUT); -} -static inline bool gpio_read(uint16_t pin) { - GPIO_Type *gpio = gpio_bank(pin); - uint32_t mask = (uint32_t) BIT(PINNO(pin)); - return gpio->DR & mask; -} -static inline void gpio_write(uint16_t pin, bool value) { - GPIO_Type *gpio = gpio_bank(pin); - uint32_t mask = (uint32_t) BIT(PINNO(pin)); - if (value) { - gpio->DR |= mask; - } else { - gpio->DR &= ~mask; - } -} -static inline void gpio_toggle(uint16_t pin) { - gpio_write(pin, !gpio_read(pin)); -} - -static inline void uart_init(LPUART_Type *uart, unsigned long baud) { - (void) uart, (void) baud; -} -#if 0 - -static inline void uart_init(LPUART_Type *uart, unsigned long baud) { - uint8_t af = 7; // Alternate function - uint16_t rx = 0, tx = 0; // pins - uint32_t freq = 0; // Bus frequency. UART1 is on APB2, rest on APB1 - - if (uart == USART1) freq = APB2_FREQUENCY, RCC->APB2ENR |= BIT(4); - if (uart == USART2) freq = APB1_FREQUENCY, RCC->APB1ENR |= BIT(17); - if (uart == USART3) freq = APB1_FREQUENCY, RCC->APB1ENR |= BIT(18); - - if (uart == USART1) tx = PIN('A', 9), rx = PIN('A', 10); - if (uart == USART2) tx = PIN('A', 2), rx = PIN('A', 3); - if (uart == USART3) tx = PIN('D', 8), rx = PIN('D', 9); - - gpio_init(tx, GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, 0, af); - gpio_init(rx, GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, 0, af); - uart->CR1 = 0; // Disable this UART - uart->BRR = freq / baud; // Set baud rate - uart->CR1 |= BIT(0) | BIT(2) | BIT(3); // Set UE, RE, TE -} -#endif -static inline void uart_write_byte(LPUART_Type *uart, uint8_t byte) { - // uart->TDR = byte; - // while ((uart->ISR & BIT(7)) == 0) spin(1); - (void) uart, (void) byte; -} -static inline void uart_write_buf(LPUART_Type *uart, char *buf, size_t len) { - while (len-- > 0) uart_write_byte(uart, *(uint8_t *) buf++); -} -static inline int uart_read_ready(LPUART_Type *uart) { - (void) uart; - // return uart->ISR & BIT(5); // If RXNE bit is set, data is ready - return 0; -} -static inline uint8_t uart_read_byte(LPUART_Type *uart) { - (void) uart; - // return (uint8_t) (uart->RDR & 255); - return 0; -} - -static inline void rng_init(void) { -} -static inline uint32_t rng_read(void) { - return 42; -} -static inline void ethernet_init(void) { -} diff --git a/examples/nxp/rt1020-make-baremetal/sysinit.c b/examples/nxp/rt1020-make-baremetal/sysinit.c deleted file mode 100644 index b065eef455..0000000000 --- a/examples/nxp/rt1020-make-baremetal/sysinit.c +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2023 Cesanta Software Limited -// All rights reserved -// -// This file contains essentials required by the CMSIS: -// uint32_t SystemCoreClock - holds the system core clock value -// SystemInit() - initialises the system, e.g. sets up clocks - -#include "hal.h" - -uint32_t SystemCoreClock = SYS_FREQUENCY; - -void SystemInit(void) { // Called automatically by startup code - rng_init(); // Initialise random number generator - SysTick_Config(SystemCoreClock / 1000); // Sys tick every 1ms -} - -void _start(void) { - extern void main(void); - main(); - for (;;) (void) 0; -} - -__attribute__((section(".cfg"), used)) uint32_t __cfg[] = {0x1234abcd}; - -extern uint32_t __isr_vector[]; -extern uint32_t __ivt_boot_data[]; - -__attribute__((section(".ivt"), used)) uint32_t __ivt[8] = { - 0x412000d1, // header: 41 - version, 2000 size, d1 tag - (uint32_t) __isr_vector, // entry - 0, // reserved - 0, // dcd - (uint32_t) __ivt_boot_data, // boot data - (uint32_t) __ivt, // this is us - ivt absolute address - 0, // csf absolute address - 0, // reserved for HAB -}; - -__attribute__((section(".ivt"), used)) uint32_t __ivt_boot_data[] = { - 0, // boot start location - 64 * 1024, // size - 0, // Plugin flag - 0Xffffffff // empty - extra data word -}; diff --git a/mongoose.c b/mongoose.c index 20db1ee34c..09089d1118 100644 --- a/mongoose.c +++ b/mongoose.c @@ -4017,273 +4017,6 @@ struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url, return c; } -#ifdef MG_ENABLE_LINES -#line 1 "src/net.c" -#endif - - - - - - - - -size_t mg_vprintf(struct mg_connection *c, const char *fmt, va_list *ap) { - size_t old = c->send.len; - mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap); - return c->send.len - old; -} - -size_t mg_printf(struct mg_connection *c, const char *fmt, ...) { - size_t len = 0; - va_list ap; - va_start(ap, fmt); - len = mg_vprintf(c, fmt, &ap); - va_end(ap); - return len; -} - -static bool mg_atonl(struct mg_str str, struct mg_addr *addr) { - uint32_t localhost = mg_htonl(0x7f000001); - if (mg_vcasecmp(&str, "localhost") != 0) return false; - memcpy(addr->ip, &localhost, sizeof(uint32_t)); - addr->is_ip6 = false; - return true; -} - -static bool mg_atone(struct mg_str str, struct mg_addr *addr) { - if (str.len > 0) return false; - memset(addr->ip, 0, sizeof(addr->ip)); - addr->is_ip6 = false; - return true; -} - -static bool mg_aton4(struct mg_str str, struct mg_addr *addr) { - uint8_t data[4] = {0, 0, 0, 0}; - size_t i, num_dots = 0; - for (i = 0; i < str.len; i++) { - if (str.ptr[i] >= '0' && str.ptr[i] <= '9') { - int octet = data[num_dots] * 10 + (str.ptr[i] - '0'); - if (octet > 255) return false; - data[num_dots] = (uint8_t) octet; - } else if (str.ptr[i] == '.') { - if (num_dots >= 3 || i == 0 || str.ptr[i - 1] == '.') return false; - num_dots++; - } else { - return false; - } - } - if (num_dots != 3 || str.ptr[i - 1] == '.') return false; - memcpy(&addr->ip, data, sizeof(data)); - addr->is_ip6 = false; - return true; -} - -static bool mg_v4mapped(struct mg_str str, struct mg_addr *addr) { - int i; - uint32_t ipv4; - if (str.len < 14) return false; - if (str.ptr[0] != ':' || str.ptr[1] != ':' || str.ptr[6] != ':') return false; - for (i = 2; i < 6; i++) { - if (str.ptr[i] != 'f' && str.ptr[i] != 'F') return false; - } - // struct mg_str s = mg_str_n(&str.ptr[7], str.len - 7); - if (!mg_aton4(mg_str_n(&str.ptr[7], str.len - 7), addr)) return false; - memcpy(&ipv4, addr->ip, sizeof(ipv4)); - memset(addr->ip, 0, sizeof(addr->ip)); - addr->ip[10] = addr->ip[11] = 255; - memcpy(&addr->ip[12], &ipv4, 4); - addr->is_ip6 = true; - return true; -} - -static bool mg_aton6(struct mg_str str, struct mg_addr *addr) { - size_t i, j = 0, n = 0, dc = 42; - addr->scope_id = 0; - if (str.len > 2 && str.ptr[0] == '[') str.ptr++, str.len -= 2; - if (mg_v4mapped(str, addr)) return true; - for (i = 0; i < str.len; i++) { - if ((str.ptr[i] >= '0' && str.ptr[i] <= '9') || - (str.ptr[i] >= 'a' && str.ptr[i] <= 'f') || - (str.ptr[i] >= 'A' && str.ptr[i] <= 'F')) { - unsigned long val; - if (i > j + 3) return false; - // MG_DEBUG(("%lu %lu [%.*s]", i, j, (int) (i - j + 1), &str.ptr[j])); - val = mg_unhexn(&str.ptr[j], i - j + 1); - addr->ip[n] = (uint8_t) ((val >> 8) & 255); - addr->ip[n + 1] = (uint8_t) (val & 255); - } else if (str.ptr[i] == ':') { - j = i + 1; - if (i > 0 && str.ptr[i - 1] == ':') { - dc = n; // Double colon - if (i > 1 && str.ptr[i - 2] == ':') return false; - } else if (i > 0) { - n += 2; - } - if (n > 14) return false; - addr->ip[n] = addr->ip[n + 1] = 0; // For trailing :: - } else if (str.ptr[i] == '%') { // Scope ID - for (i = i + 1; i < str.len; i++) { - if (str.ptr[i] < '0' || str.ptr[i] > '9') return false; - addr->scope_id *= 10, addr->scope_id += (uint8_t) (str.ptr[i] - '0'); - } - } else { - return false; - } - } - if (n < 14 && dc == 42) return false; - if (n < 14) { - memmove(&addr->ip[dc + (14 - n)], &addr->ip[dc], n - dc + 2); - memset(&addr->ip[dc], 0, 14 - n); - } - - addr->is_ip6 = true; - return true; -} - -bool mg_aton(struct mg_str str, struct mg_addr *addr) { - // MG_INFO(("[%.*s]", (int) str.len, str.ptr)); - return mg_atone(str, addr) || mg_atonl(str, addr) || mg_aton4(str, addr) || - mg_aton6(str, addr); -} - -struct mg_connection *mg_alloc_conn(struct mg_mgr *mgr) { - struct mg_connection *c = - (struct mg_connection *) calloc(1, sizeof(*c) + mgr->extraconnsize); - if (c != NULL) { - c->mgr = mgr; - c->send.align = c->recv.align = MG_IO_SIZE; - c->id = ++mgr->nextid; - } - return c; -} - -void mg_close_conn(struct mg_connection *c) { - mg_resolve_cancel(c); // Close any pending DNS query - LIST_DELETE(struct mg_connection, &c->mgr->conns, c); - if (c == c->mgr->dns4.c) c->mgr->dns4.c = NULL; - if (c == c->mgr->dns6.c) c->mgr->dns6.c = NULL; - // Order of operations is important. `MG_EV_CLOSE` event must be fired - // before we deallocate received data, see #1331 - mg_call(c, MG_EV_CLOSE, NULL); - MG_DEBUG(("%lu %ld closed", c->id, c->fd)); - - mg_tls_free(c); - mg_iobuf_free(&c->recv); - mg_iobuf_free(&c->send); - mg_bzero((unsigned char *) c, sizeof(*c)); - free(c); -} - -struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *url, - mg_event_handler_t fn, void *fn_data) { - struct mg_connection *c = NULL; - if (url == NULL || url[0] == '\0') { - MG_ERROR(("null url")); - } else if ((c = mg_alloc_conn(mgr)) == NULL) { - MG_ERROR(("OOM")); - } else { - LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); - c->is_udp = (strncmp(url, "udp:", 4) == 0); - c->fd = (void *) (size_t) MG_INVALID_SOCKET; - c->fn = fn; - c->is_client = true; - c->fn_data = fn_data; - MG_DEBUG(("%lu %ld %s", c->id, c->fd, url)); - mg_call(c, MG_EV_OPEN, (void *) url); - mg_resolve(c, url); - } - return c; -} - -struct mg_connection *mg_listen(struct mg_mgr *mgr, const char *url, - mg_event_handler_t fn, void *fn_data) { - struct mg_connection *c = NULL; - if ((c = mg_alloc_conn(mgr)) == NULL) { - MG_ERROR(("OOM %s", url)); - } else if (!mg_open_listener(c, url)) { - MG_ERROR(("Failed: %s, errno %d", url, errno)); - free(c); - c = NULL; - } else { - c->is_listening = 1; - c->is_udp = strncmp(url, "udp:", 4) == 0; - LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); - c->fn = fn; - c->fn_data = fn_data; - mg_call(c, MG_EV_OPEN, NULL); - if (mg_url_is_ssl(url)) c->is_tls = 1; // Accepted connection must - MG_DEBUG(("%lu %ld %s", c->id, c->fd, url)); - } - return c; -} - -struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd, - mg_event_handler_t fn, void *fn_data) { - struct mg_connection *c = mg_alloc_conn(mgr); - if (c != NULL) { - c->fd = (void *) (size_t) fd; - c->fn = fn; - c->fn_data = fn_data; - MG_EPOLL_ADD(c); - mg_call(c, MG_EV_OPEN, NULL); - LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); - } - return c; -} - -struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds, - unsigned flags, void (*fn)(void *), void *arg) { - struct mg_timer *t = (struct mg_timer *) calloc(1, sizeof(*t)); - if (t != NULL) { - mg_timer_init(&mgr->timers, t, milliseconds, flags, fn, arg); - t->id = mgr->timerid++; - } - return t; -} - -void mg_mgr_free(struct mg_mgr *mgr) { - struct mg_connection *c; - struct mg_timer *tmp, *t = mgr->timers; - while (t != NULL) tmp = t->next, free(t), t = tmp; - mgr->timers = NULL; // Important. Next call to poll won't touch timers - for (c = mgr->conns; c != NULL; c = c->next) c->is_closing = 1; - mg_mgr_poll(mgr, 0); -#if MG_ENABLE_FREERTOS_TCP - FreeRTOS_DeleteSocketSet(mgr->ss); -#endif - MG_DEBUG(("All connections closed")); -#if MG_ENABLE_EPOLL - if (mgr->epoll_fd >= 0) close(mgr->epoll_fd), mgr->epoll_fd = -1; -#endif - mg_tls_ctx_free(mgr); -} - -void mg_mgr_init(struct mg_mgr *mgr) { - memset(mgr, 0, sizeof(*mgr)); -#if MG_ENABLE_EPOLL - if ((mgr->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) - MG_ERROR(("epoll_create1 errno %d", errno)); -#else - mgr->epoll_fd = -1; -#endif -#if MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK - // clang-format off - { WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); } - // clang-format on -#elif MG_ENABLE_FREERTOS_TCP - mgr->ss = FreeRTOS_CreateSocketSet(); -#elif defined(__unix) || defined(__unix__) || defined(__APPLE__) - // Ignore SIGPIPE signal, so if client cancels the request, it - // won't kill the whole process. - signal(SIGPIPE, SIG_IGN); -#endif - mgr->dnstimeout = 3000; - mgr->dns4.url = "udp://8.8.8.8:53"; - mgr->dns6.url = "udp://[2001:4860:4860::8888]:53"; - mg_tls_ctx_init(mgr); -} - #ifdef MG_ENABLE_LINES #line 1 "src/net_builtin.c" #endif @@ -5263,147 +4996,414 @@ void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) { // MG_EPHEMERAL_PORT_BASE to 65535 if (ifp->tx.ptr == NULL || ifp->recv_queue.buf == NULL) MG_ERROR(("OOM")); } -} - -void mg_tcpip_free(struct mg_tcpip_if *ifp) { - free(ifp->recv_queue.buf); - free((char *) ifp->tx.ptr); -} +} + +void mg_tcpip_free(struct mg_tcpip_if *ifp) { + free(ifp->recv_queue.buf); + free((char *) ifp->tx.ptr); +} + +int mg_mkpipe(struct mg_mgr *m, mg_event_handler_t fn, void *d, bool udp) { + (void) m, (void) fn, (void) d, (void) udp; + MG_ERROR(("Not implemented")); + return -1; +} + +static void send_syn(struct mg_connection *c) { + struct connstate *s = (struct connstate *) (c + 1); + uint32_t isn = mg_htonl((uint32_t) mg_ntohs(c->loc.port)); + struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv; + uint32_t rem_ip; + memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); + tx_tcp(ifp, s->mac, rem_ip, TH_SYN, c->loc.port, c->rem.port, isn, 0, NULL, + 0); +} + +void mg_connect_resolved(struct mg_connection *c) { + struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv; + uint32_t rem_ip; + memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); + c->is_resolving = 0; + if (ifp->eport < MG_EPHEMERAL_PORT_BASE) ifp->eport = MG_EPHEMERAL_PORT_BASE; + memcpy(c->loc.ip, &ifp->ip, sizeof(uint32_t)); + c->loc.port = mg_htons(ifp->eport++); + MG_DEBUG(("%lu %M -> %M", c->id, mg_print_ip_port, &c->loc, mg_print_ip_port, + &c->rem)); + mg_call(c, MG_EV_RESOLVE, NULL); + if (c->is_udp && (rem_ip == 0xffffffff || rem_ip == (ifp->ip | ~ifp->mask))) { + struct connstate *s = (struct connstate *) (c + 1); + memset(s->mac, 0xFF, sizeof(s->mac)); // global or local broadcast + } else if (((rem_ip & ifp->mask) == (ifp->ip & ifp->mask))) { + // If we're in the same LAN, fire an ARP lookup. + MG_DEBUG(("%lu ARP lookup...", c->id)); + arp_ask(ifp, rem_ip); + settmout(c, MIP_TTYPE_ARP); + c->is_arplooking = 1; + c->is_connecting = 1; + } else if ((*((uint8_t *) &rem_ip) & 0xE0) == 0xE0) { + struct connstate *s = (struct connstate *) (c + 1); // 224 to 239, E0 to EF + uint8_t mcastp[3] = {0x01, 0x00, 0x5E}; // multicast group + memcpy(s->mac, mcastp, 3); + memcpy(s->mac + 3, ((uint8_t *) &rem_ip) + 1, 3); // 23 LSb + s->mac[3] &= 0x7F; + } else { + struct connstate *s = (struct connstate *) (c + 1); + memcpy(s->mac, ifp->gwmac, sizeof(ifp->gwmac)); + if (c->is_udp) { + mg_call(c, MG_EV_CONNECT, NULL); + } else { + send_syn(c); + settmout(c, MIP_TTYPE_SYN); + c->is_connecting = 1; + } + } +} + +bool mg_open_listener(struct mg_connection *c, const char *url) { + c->loc.port = mg_htons(mg_url_port(url)); + return true; +} + +static void write_conn(struct mg_connection *c) { + long len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len) + : mg_io_send(c, c->send.buf, c->send.len); + if (len > 0) { + mg_iobuf_del(&c->send, 0, (size_t) len); + mg_call(c, MG_EV_WRITE, &len); + } +} + +static void init_closure(struct mg_connection *c) { + struct connstate *s = (struct connstate *) (c + 1); + if (c->is_udp == false && c->is_listening == false && + c->is_connecting == false) { // For TCP conns, + struct mg_tcpip_if *ifp = + (struct mg_tcpip_if *) c->mgr->priv; // send TCP FIN + uint32_t rem_ip; + memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); + tx_tcp(ifp, s->mac, rem_ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port, + mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); + settmout(c, MIP_TTYPE_FIN); + } +} + +static void close_conn(struct mg_connection *c) { + struct connstate *s = (struct connstate *) (c + 1); + mg_iobuf_free(&s->raw); // For TLS connections, release raw data + mg_close_conn(c); +} + +static bool can_write(struct mg_connection *c) { + return c->is_connecting == 0 && c->is_resolving == 0 && c->send.len > 0 && + c->is_tls_hs == 0 && c->is_arplooking == 0; +} + +void mg_mgr_poll(struct mg_mgr *mgr, int ms) { + struct mg_connection *c, *tmp; + uint64_t now = mg_millis(); + mg_tcpip_poll((struct mg_tcpip_if *) mgr->priv, now); + mg_timer_poll(&mgr->timers, now); + for (c = mgr->conns; c != NULL; c = tmp) { + tmp = c->next; + struct connstate *s = (struct connstate *) (c + 1); + mg_call(c, MG_EV_POLL, &now); + MG_VERBOSE(("%lu .. %c%c%c%c%c", c->id, c->is_tls ? 'T' : 't', + c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h', + c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c')); + if (c->is_tls_hs) mg_tls_handshake(c); + if (can_write(c)) write_conn(c); + if (c->is_draining && c->send.len == 0 && s->ttype != MIP_TTYPE_FIN) + init_closure(c); + if (c->is_closing) close_conn(c); + } + (void) ms; +} + +bool mg_send(struct mg_connection *c, const void *buf, size_t len) { + struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv; + bool res = false; + uint32_t rem_ip; + memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); + if (ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY) { + mg_error(c, "net down"); + } else if (c->is_udp) { + struct connstate *s = (struct connstate *) (c + 1); + len = trim_len(c, len); // Trimming length if necessary + tx_udp(ifp, s->mac, ifp->ip, c->loc.port, rem_ip, c->rem.port, buf, len); + res = true; + } else { + res = mg_iobuf_add(&c->send, c->send.len, buf, len); + } + return res; +} +#endif // MG_ENABLE_TCPIP + +#ifdef MG_ENABLE_LINES +#line 1 "src/net.c" +#endif + + + + + + + + +size_t mg_vprintf(struct mg_connection *c, const char *fmt, va_list *ap) { + size_t old = c->send.len; + mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap); + return c->send.len - old; +} + +size_t mg_printf(struct mg_connection *c, const char *fmt, ...) { + size_t len = 0; + va_list ap; + va_start(ap, fmt); + len = mg_vprintf(c, fmt, &ap); + va_end(ap); + return len; +} + +static bool mg_atonl(struct mg_str str, struct mg_addr *addr) { + uint32_t localhost = mg_htonl(0x7f000001); + if (mg_vcasecmp(&str, "localhost") != 0) return false; + memcpy(addr->ip, &localhost, sizeof(uint32_t)); + addr->is_ip6 = false; + return true; +} + +static bool mg_atone(struct mg_str str, struct mg_addr *addr) { + if (str.len > 0) return false; + memset(addr->ip, 0, sizeof(addr->ip)); + addr->is_ip6 = false; + return true; +} + +static bool mg_aton4(struct mg_str str, struct mg_addr *addr) { + uint8_t data[4] = {0, 0, 0, 0}; + size_t i, num_dots = 0; + for (i = 0; i < str.len; i++) { + if (str.ptr[i] >= '0' && str.ptr[i] <= '9') { + int octet = data[num_dots] * 10 + (str.ptr[i] - '0'); + if (octet > 255) return false; + data[num_dots] = (uint8_t) octet; + } else if (str.ptr[i] == '.') { + if (num_dots >= 3 || i == 0 || str.ptr[i - 1] == '.') return false; + num_dots++; + } else { + return false; + } + } + if (num_dots != 3 || str.ptr[i - 1] == '.') return false; + memcpy(&addr->ip, data, sizeof(data)); + addr->is_ip6 = false; + return true; +} + +static bool mg_v4mapped(struct mg_str str, struct mg_addr *addr) { + int i; + uint32_t ipv4; + if (str.len < 14) return false; + if (str.ptr[0] != ':' || str.ptr[1] != ':' || str.ptr[6] != ':') return false; + for (i = 2; i < 6; i++) { + if (str.ptr[i] != 'f' && str.ptr[i] != 'F') return false; + } + // struct mg_str s = mg_str_n(&str.ptr[7], str.len - 7); + if (!mg_aton4(mg_str_n(&str.ptr[7], str.len - 7), addr)) return false; + memcpy(&ipv4, addr->ip, sizeof(ipv4)); + memset(addr->ip, 0, sizeof(addr->ip)); + addr->ip[10] = addr->ip[11] = 255; + memcpy(&addr->ip[12], &ipv4, 4); + addr->is_ip6 = true; + return true; +} + +static bool mg_aton6(struct mg_str str, struct mg_addr *addr) { + size_t i, j = 0, n = 0, dc = 42; + addr->scope_id = 0; + if (str.len > 2 && str.ptr[0] == '[') str.ptr++, str.len -= 2; + if (mg_v4mapped(str, addr)) return true; + for (i = 0; i < str.len; i++) { + if ((str.ptr[i] >= '0' && str.ptr[i] <= '9') || + (str.ptr[i] >= 'a' && str.ptr[i] <= 'f') || + (str.ptr[i] >= 'A' && str.ptr[i] <= 'F')) { + unsigned long val; + if (i > j + 3) return false; + // MG_DEBUG(("%lu %lu [%.*s]", i, j, (int) (i - j + 1), &str.ptr[j])); + val = mg_unhexn(&str.ptr[j], i - j + 1); + addr->ip[n] = (uint8_t) ((val >> 8) & 255); + addr->ip[n + 1] = (uint8_t) (val & 255); + } else if (str.ptr[i] == ':') { + j = i + 1; + if (i > 0 && str.ptr[i - 1] == ':') { + dc = n; // Double colon + if (i > 1 && str.ptr[i - 2] == ':') return false; + } else if (i > 0) { + n += 2; + } + if (n > 14) return false; + addr->ip[n] = addr->ip[n + 1] = 0; // For trailing :: + } else if (str.ptr[i] == '%') { // Scope ID + for (i = i + 1; i < str.len; i++) { + if (str.ptr[i] < '0' || str.ptr[i] > '9') return false; + addr->scope_id *= 10, addr->scope_id += (uint8_t) (str.ptr[i] - '0'); + } + } else { + return false; + } + } + if (n < 14 && dc == 42) return false; + if (n < 14) { + memmove(&addr->ip[dc + (14 - n)], &addr->ip[dc], n - dc + 2); + memset(&addr->ip[dc], 0, 14 - n); + } -int mg_mkpipe(struct mg_mgr *m, mg_event_handler_t fn, void *d, bool udp) { - (void) m, (void) fn, (void) d, (void) udp; - MG_ERROR(("Not implemented")); - return -1; + addr->is_ip6 = true; + return true; } -static void send_syn(struct mg_connection *c) { - struct connstate *s = (struct connstate *) (c + 1); - uint32_t isn = mg_htonl((uint32_t) mg_ntohs(c->loc.port)); - struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv; - uint32_t rem_ip; - memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); - tx_tcp(ifp, s->mac, rem_ip, TH_SYN, c->loc.port, c->rem.port, isn, 0, NULL, - 0); +bool mg_aton(struct mg_str str, struct mg_addr *addr) { + // MG_INFO(("[%.*s]", (int) str.len, str.ptr)); + return mg_atone(str, addr) || mg_atonl(str, addr) || mg_aton4(str, addr) || + mg_aton6(str, addr); } -void mg_connect_resolved(struct mg_connection *c) { - struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv; - uint32_t rem_ip; - memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); - c->is_resolving = 0; - if (ifp->eport < MG_EPHEMERAL_PORT_BASE) ifp->eport = MG_EPHEMERAL_PORT_BASE; - memcpy(c->loc.ip, &ifp->ip, sizeof(uint32_t)); - c->loc.port = mg_htons(ifp->eport++); - MG_DEBUG(("%lu %M -> %M", c->id, mg_print_ip_port, &c->loc, mg_print_ip_port, - &c->rem)); - mg_call(c, MG_EV_RESOLVE, NULL); - if (c->is_udp && (rem_ip == 0xffffffff || rem_ip == (ifp->ip | ~ifp->mask))) { - struct connstate *s = (struct connstate *) (c + 1); - memset(s->mac, 0xFF, sizeof(s->mac)); // global or local broadcast - } else if (((rem_ip & ifp->mask) == (ifp->ip & ifp->mask))) { - // If we're in the same LAN, fire an ARP lookup. - MG_DEBUG(("%lu ARP lookup...", c->id)); - arp_ask(ifp, rem_ip); - settmout(c, MIP_TTYPE_ARP); - c->is_arplooking = 1; - c->is_connecting = 1; - } else if ((*((uint8_t *) &rem_ip) & 0xE0) == 0xE0) { - struct connstate *s = (struct connstate *) (c + 1); // 224 to 239, E0 to EF - uint8_t mcastp[3] = {0x01, 0x00, 0x5E}; // multicast group - memcpy(s->mac, mcastp, 3); - memcpy(s->mac + 3, ((uint8_t *) &rem_ip) + 1, 3); // 23 LSb - s->mac[3] &= 0x7F; - } else { - struct connstate *s = (struct connstate *) (c + 1); - memcpy(s->mac, ifp->gwmac, sizeof(ifp->gwmac)); - if (c->is_udp) { - mg_call(c, MG_EV_CONNECT, NULL); - } else { - send_syn(c); - settmout(c, MIP_TTYPE_SYN); - c->is_connecting = 1; - } +struct mg_connection *mg_alloc_conn(struct mg_mgr *mgr) { + struct mg_connection *c = + (struct mg_connection *) calloc(1, sizeof(*c) + mgr->extraconnsize); + if (c != NULL) { + c->mgr = mgr; + c->send.align = c->recv.align = MG_IO_SIZE; + c->id = ++mgr->nextid; } + return c; } -bool mg_open_listener(struct mg_connection *c, const char *url) { - c->loc.port = mg_htons(mg_url_port(url)); - return true; +void mg_close_conn(struct mg_connection *c) { + mg_resolve_cancel(c); // Close any pending DNS query + LIST_DELETE(struct mg_connection, &c->mgr->conns, c); + if (c == c->mgr->dns4.c) c->mgr->dns4.c = NULL; + if (c == c->mgr->dns6.c) c->mgr->dns6.c = NULL; + // Order of operations is important. `MG_EV_CLOSE` event must be fired + // before we deallocate received data, see #1331 + mg_call(c, MG_EV_CLOSE, NULL); + MG_DEBUG(("%lu %ld closed", c->id, c->fd)); + + mg_tls_free(c); + mg_iobuf_free(&c->recv); + mg_iobuf_free(&c->send); + mg_bzero((unsigned char *) c, sizeof(*c)); + free(c); } -static void write_conn(struct mg_connection *c) { - long len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len) - : mg_io_send(c, c->send.buf, c->send.len); - if (len > 0) { - mg_iobuf_del(&c->send, 0, (size_t) len); - mg_call(c, MG_EV_WRITE, &len); +struct mg_connection *mg_connect(struct mg_mgr *mgr, const char *url, + mg_event_handler_t fn, void *fn_data) { + struct mg_connection *c = NULL; + if (url == NULL || url[0] == '\0') { + MG_ERROR(("null url")); + } else if ((c = mg_alloc_conn(mgr)) == NULL) { + MG_ERROR(("OOM")); + } else { + LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); + c->is_udp = (strncmp(url, "udp:", 4) == 0); + c->fd = (void *) (size_t) MG_INVALID_SOCKET; + c->fn = fn; + c->is_client = true; + c->fn_data = fn_data; + MG_DEBUG(("%lu %ld %s", c->id, c->fd, url)); + mg_call(c, MG_EV_OPEN, (void *) url); + mg_resolve(c, url); } + return c; } -static void init_closure(struct mg_connection *c) { - struct connstate *s = (struct connstate *) (c + 1); - if (c->is_udp == false && c->is_listening == false && - c->is_connecting == false) { // For TCP conns, - struct mg_tcpip_if *ifp = - (struct mg_tcpip_if *) c->mgr->priv; // send TCP FIN - uint32_t rem_ip; - memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); - tx_tcp(ifp, s->mac, rem_ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port, - mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); - settmout(c, MIP_TTYPE_FIN); +struct mg_connection *mg_listen(struct mg_mgr *mgr, const char *url, + mg_event_handler_t fn, void *fn_data) { + struct mg_connection *c = NULL; + if ((c = mg_alloc_conn(mgr)) == NULL) { + MG_ERROR(("OOM %s", url)); + } else if (!mg_open_listener(c, url)) { + MG_ERROR(("Failed: %s, errno %d", url, errno)); + free(c); + c = NULL; + } else { + c->is_listening = 1; + c->is_udp = strncmp(url, "udp:", 4) == 0; + LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); + c->fn = fn; + c->fn_data = fn_data; + mg_call(c, MG_EV_OPEN, NULL); + if (mg_url_is_ssl(url)) c->is_tls = 1; // Accepted connection must + MG_DEBUG(("%lu %ld %s", c->id, c->fd, url)); } + return c; } -static void close_conn(struct mg_connection *c) { - struct connstate *s = (struct connstate *) (c + 1); - mg_iobuf_free(&s->raw); // For TLS connections, release raw data - mg_close_conn(c); +struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd, + mg_event_handler_t fn, void *fn_data) { + struct mg_connection *c = mg_alloc_conn(mgr); + if (c != NULL) { + c->fd = (void *) (size_t) fd; + c->fn = fn; + c->fn_data = fn_data; + MG_EPOLL_ADD(c); + mg_call(c, MG_EV_OPEN, NULL); + LIST_ADD_HEAD(struct mg_connection, &mgr->conns, c); + } + return c; } -static bool can_write(struct mg_connection *c) { - return c->is_connecting == 0 && c->is_resolving == 0 && c->send.len > 0 && - c->is_tls_hs == 0 && c->is_arplooking == 0; +struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds, + unsigned flags, void (*fn)(void *), void *arg) { + struct mg_timer *t = (struct mg_timer *) calloc(1, sizeof(*t)); + if (t != NULL) { + mg_timer_init(&mgr->timers, t, milliseconds, flags, fn, arg); + t->id = mgr->timerid++; + } + return t; } -void mg_mgr_poll(struct mg_mgr *mgr, int ms) { - struct mg_connection *c, *tmp; - uint64_t now = mg_millis(); - mg_tcpip_poll((struct mg_tcpip_if *) mgr->priv, now); - mg_timer_poll(&mgr->timers, now); - for (c = mgr->conns; c != NULL; c = tmp) { - tmp = c->next; - struct connstate *s = (struct connstate *) (c + 1); - mg_call(c, MG_EV_POLL, &now); - MG_VERBOSE(("%lu .. %c%c%c%c%c", c->id, c->is_tls ? 'T' : 't', - c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h', - c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c')); - if (c->is_tls_hs) mg_tls_handshake(c); - if (can_write(c)) write_conn(c); - if (c->is_draining && c->send.len == 0 && s->ttype != MIP_TTYPE_FIN) - init_closure(c); - if (c->is_closing) close_conn(c); - } - (void) ms; +void mg_mgr_free(struct mg_mgr *mgr) { + struct mg_connection *c; + struct mg_timer *tmp, *t = mgr->timers; + while (t != NULL) tmp = t->next, free(t), t = tmp; + mgr->timers = NULL; // Important. Next call to poll won't touch timers + for (c = mgr->conns; c != NULL; c = c->next) c->is_closing = 1; + mg_mgr_poll(mgr, 0); +#if MG_ENABLE_FREERTOS_TCP + FreeRTOS_DeleteSocketSet(mgr->ss); +#endif + MG_DEBUG(("All connections closed")); +#if MG_ENABLE_EPOLL + if (mgr->epoll_fd >= 0) close(mgr->epoll_fd), mgr->epoll_fd = -1; +#endif + mg_tls_ctx_free(mgr); } -bool mg_send(struct mg_connection *c, const void *buf, size_t len) { - struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv; - bool res = false; - uint32_t rem_ip; - memcpy(&rem_ip, c->rem.ip, sizeof(uint32_t)); - if (ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY) { - mg_error(c, "net down"); - } else if (c->is_udp) { - struct connstate *s = (struct connstate *) (c + 1); - len = trim_len(c, len); // Trimming length if necessary - tx_udp(ifp, s->mac, ifp->ip, c->loc.port, rem_ip, c->rem.port, buf, len); - res = true; - } else { - res = mg_iobuf_add(&c->send, c->send.len, buf, len); - } - return res; +void mg_mgr_init(struct mg_mgr *mgr) { + memset(mgr, 0, sizeof(*mgr)); +#if MG_ENABLE_EPOLL + if ((mgr->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) + MG_ERROR(("epoll_create1 errno %d", errno)); +#else + mgr->epoll_fd = -1; +#endif +#if MG_ARCH == MG_ARCH_WIN32 && MG_ENABLE_WINSOCK + // clang-format off + { WSADATA data; WSAStartup(MAKEWORD(2, 2), &data); } + // clang-format on +#elif MG_ENABLE_FREERTOS_TCP + mgr->ss = FreeRTOS_CreateSocketSet(); +#elif defined(__unix) || defined(__unix__) || defined(__APPLE__) + // Ignore SIGPIPE signal, so if client cancels the request, it + // won't kill the whole process. + signal(SIGPIPE, SIG_IGN); +#endif + mgr->dnstimeout = 3000; + mgr->dns4.url = "udp://8.8.8.8:53"; + mgr->dns6.url = "udp://[2001:4860:4860::8888]:53"; + mg_tls_ctx_init(mgr); } -#endif // MG_ENABLE_TCPIP #ifdef MG_ENABLE_LINES #line 1 "src/ota_dummy.c" @@ -8488,9 +8488,9 @@ typedef struct enet_bd_struct_def } enet_bd_struct_t; // Descriptor and buffer globals, in non-cached area, 64 bits aligned. - -__attribute__((section("NonCacheable,\"aw\",%nobits @"))) enet_bd_struct_t rx_buffer_descriptor[(ENET_RXBD_NUM)] __attribute__((aligned((64U)))); -__attribute__((section("NonCacheable,\"aw\",%nobits @"))) enet_bd_struct_t tx_buffer_descriptor[(ENET_TXBD_NUM)] __attribute__((aligned((64U)))); +// TODO(): handle these in a portable compiler-independent CMSIS-friendly way +/*__attribute__((section("NonCacheable,\"aw\",%nobits @")))*/ enet_bd_struct_t rx_buffer_descriptor[(ENET_RXBD_NUM)] __attribute__((aligned((64U)))); +/*__attribute__((section("NonCacheable,\"aw\",%nobits @")))*/ enet_bd_struct_t tx_buffer_descriptor[(ENET_TXBD_NUM)] __attribute__((aligned((64U)))); uint8_t rx_data_buffer[(ENET_RXBD_NUM)][((unsigned int)(((ENET_RXBUFF_SIZE)) + (((64U))-1U)) & (unsigned int)(~(unsigned int)(((64U))-1U)))] __attribute__((aligned((64U)))); uint8_t tx_data_buffer[(ENET_TXBD_NUM)][((unsigned int)(((ENET_TXBUFF_SIZE)) + (((64U))-1U)) & (unsigned int)(~(unsigned int)(((64U))-1U)))] __attribute__((aligned((64U)))); @@ -8500,7 +8500,7 @@ uint8_t tx_data_buffer[(ENET_TXBD_NUM)][((unsigned int)(((ENET_TXBUFF_SIZE)) + ( // static bool mg_tcpip_driver_imxrt1020_init(uint8_t *mac, void *data) { // VO static bool mg_tcpip_driver_imxrt1020_init(struct mg_tcpip_if *ifp) { - struct mg_tcpip_driver_imxrt1020_data *d = (struct mg_tcpip_driver_imxrt1020_data *) ifp->driver_data; +// TODO(scaprile): struct mg_tcpip_driver_imxrt1020_data *d = (struct mg_tcpip_driver_imxrt1020_data *) ifp->driver_data; s_ifp = ifp; // ENET Reset, wait complete @@ -8531,6 +8531,7 @@ static bool mg_tcpip_driver_imxrt1020_init(struct mg_tcpip_if *ifp) { // Configure ENET ENET->RCR = 0x05ee0104; // #CRCFWD=0 (CRC kept in frame) + RMII + MII Enable + //ENET->RCR |= BIT(3); // Receive all ENET->TCR = BIT(8) | BIT(2); // Addins (MAC address from PAUR+PALR) + Full duplex enable //ENET->TFWR = BIT(8); // Store And Forward Enable, 64 bytes (minimize tx latency) diff --git a/mongoose.h b/mongoose.h index 9cb4d30628..ab251ffb57 100644 --- a/mongoose.h +++ b/mongoose.h @@ -1784,7 +1784,7 @@ extern struct mg_tcpip_driver mg_tcpip_driver_stm32; extern struct mg_tcpip_driver mg_tcpip_driver_w5500; extern struct mg_tcpip_driver mg_tcpip_driver_tm4c; extern struct mg_tcpip_driver mg_tcpip_driver_stm32h; -extern struct mg_tcpip_driver mg_tcpip_driver_imxrt; +extern struct mg_tcpip_driver mg_tcpip_driver_imxrt1020; extern struct mg_tcpip_driver mg_tcpip_driver_same54; // Drivers that require SPI, can use this SPI abstraction diff --git a/src/drivers/rt1020.c b/src/drivers/rt1020.c index 7eaec4a525..363fd912f1 100644 --- a/src/drivers/rt1020.c +++ b/src/drivers/rt1020.c @@ -97,9 +97,9 @@ typedef struct enet_bd_struct_def } enet_bd_struct_t; // Descriptor and buffer globals, in non-cached area, 64 bits aligned. - -__attribute__((section("NonCacheable,\"aw\",%nobits @"))) enet_bd_struct_t rx_buffer_descriptor[(ENET_RXBD_NUM)] __attribute__((aligned((64U)))); -__attribute__((section("NonCacheable,\"aw\",%nobits @"))) enet_bd_struct_t tx_buffer_descriptor[(ENET_TXBD_NUM)] __attribute__((aligned((64U)))); +// TODO(): handle these in a portable compiler-independent CMSIS-friendly way +/*__attribute__((section("NonCacheable,\"aw\",%nobits @")))*/ enet_bd_struct_t rx_buffer_descriptor[(ENET_RXBD_NUM)] __attribute__((aligned((64U)))); +/*__attribute__((section("NonCacheable,\"aw\",%nobits @")))*/ enet_bd_struct_t tx_buffer_descriptor[(ENET_TXBD_NUM)] __attribute__((aligned((64U)))); uint8_t rx_data_buffer[(ENET_RXBD_NUM)][((unsigned int)(((ENET_RXBUFF_SIZE)) + (((64U))-1U)) & (unsigned int)(~(unsigned int)(((64U))-1U)))] __attribute__((aligned((64U)))); uint8_t tx_data_buffer[(ENET_TXBD_NUM)][((unsigned int)(((ENET_TXBUFF_SIZE)) + (((64U))-1U)) & (unsigned int)(~(unsigned int)(((64U))-1U)))] __attribute__((aligned((64U)))); @@ -109,7 +109,7 @@ uint8_t tx_data_buffer[(ENET_TXBD_NUM)][((unsigned int)(((ENET_TXBUFF_SIZE)) + ( // static bool mg_tcpip_driver_imxrt1020_init(uint8_t *mac, void *data) { // VO static bool mg_tcpip_driver_imxrt1020_init(struct mg_tcpip_if *ifp) { - struct mg_tcpip_driver_imxrt1020_data *d = (struct mg_tcpip_driver_imxrt1020_data *) ifp->driver_data; +// TODO(scaprile): struct mg_tcpip_driver_imxrt1020_data *d = (struct mg_tcpip_driver_imxrt1020_data *) ifp->driver_data; s_ifp = ifp; // ENET Reset, wait complete @@ -140,6 +140,7 @@ static bool mg_tcpip_driver_imxrt1020_init(struct mg_tcpip_if *ifp) { // Configure ENET ENET->RCR = 0x05ee0104; // #CRCFWD=0 (CRC kept in frame) + RMII + MII Enable + //ENET->RCR |= BIT(3); // Receive all ENET->TCR = BIT(8) | BIT(2); // Addins (MAC address from PAUR+PALR) + Full duplex enable //ENET->TFWR = BIT(8); // Store And Forward Enable, 64 bytes (minimize tx latency) diff --git a/src/net_builtin.h b/src/net_builtin.h index 5cc412df4a..4ea2346dbe 100644 --- a/src/net_builtin.h +++ b/src/net_builtin.h @@ -55,7 +55,7 @@ extern struct mg_tcpip_driver mg_tcpip_driver_stm32; extern struct mg_tcpip_driver mg_tcpip_driver_w5500; extern struct mg_tcpip_driver mg_tcpip_driver_tm4c; extern struct mg_tcpip_driver mg_tcpip_driver_stm32h; -extern struct mg_tcpip_driver mg_tcpip_driver_imxrt; +extern struct mg_tcpip_driver mg_tcpip_driver_imxrt1020; extern struct mg_tcpip_driver mg_tcpip_driver_same54; // Drivers that require SPI, can use this SPI abstraction