Files
mongoose/mongoose.h
T

5823 lines
216 KiB
C++

// Copyright (c) 2004-2013 Sergey Lyubka
// Copyright (c) 2013-2025 Cesanta Software Limited
// All rights reserved
//
// This software is dual-licensed: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation. For the terms of this
// license, see http://www.gnu.org/licenses/
//
// You are free to use this software under the terms of the GNU General
// Public License, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// Alternatively, you can license this software under a commercial
// license, as set out in https://www.mongoose.ws/licensing/
//
// SPDX-License-Identifier: GPL-2.0-only or commercial
#ifndef MONGOOSE_H
#define MONGOOSE_H
#define MG_VERSION "7.23"
#ifdef __cplusplus
extern "C" {
#endif
#define MG_ARCH_CUSTOM 0 // User creates its own mongoose_config.h
#define MG_ARCH_UNIX 1 // Linux, BSD, Mac, ...
#define MG_ARCH_WIN32 2 // Windows
#define MG_ARCH_ESP32 3 // ESP32
#define MG_ARCH_ESP8266 4 // ESP8266
#define MG_ARCH_FREERTOS 5 // FreeRTOS <-- DEPRECATED !!!
#define MG_ARCH_THREADX 6 // Eclipse ThreadX (former MS Azure RTOS)
#define MG_ARCH_ZEPHYR 7 // Zephyr RTOS
#define MG_ARCH_ARMGCC 8 // Plain ARM GCC
#define MG_ARCH_CMSIS_RTOS1 9 // CMSIS-RTOS API v1 (Keil RTX)
#define MG_ARCH_TIRTOS 10 // Texas Semi TI-RTOS
#define MG_ARCH_PICOSDK 11 // Raspberry Pi Pico-SDK (RP2040, RP2350)
#define MG_ARCH_ARMCC 12 // Keil MDK-Core with Configuration Wizard
#define MG_ARCH_CMSIS_RTOS2 13 // CMSIS-RTOS API v2 (Keil RTX5, FreeRTOS)
#define MG_ARCH_RTTHREAD 14 // RT-Thread RTOS
#define MG_ARCH_ARMCGT 15 // Texas Semi ARM-CGT
#define MG_ARCH_CUBE 16 // STM32Cube environment
#define MG_ARCH_MCUXPRESSO 17 // MCUXpresso environment
#define MG_ARCH_NEWLIB MG_ARCH_ARMGCC // Alias, deprecate in 2025
#if !defined(MG_ARCH)
#if defined(__unix__) || defined(__APPLE__)
#define MG_ARCH MG_ARCH_UNIX
#elif defined(_WIN32)
#define MG_ARCH MG_ARCH_WIN32
#endif
#endif // !defined(MG_ARCH)
#if !defined(MG_ARCH) || (MG_ARCH == MG_ARCH_CUSTOM)
#include "mongoose_config.h" // keep this include
#endif
#if !defined(MG_ARCH)
#error "MG_ARCH is not specified and we couldn't guess it. Define MG_ARCH=... in mongoose_config.h"
#elif MG_ARCH == MG_ARCH_FREERTOS
#error "MG_ARCH_FREERTOS has been deprecated, set MG_ARCH=your_build_environment and MG_ENABLE_FREERTOS=1 instead"
#undef MG_ARCH // avoid errors piling up, provide a clean environment so the error above is seen
#define MG_ARCH MG_ARCH_ARMGCC
#define MG_ENABLE_FREERTOS 1
#endif
// http://esr.ibiblio.org/?p=5095
#define MG_BIG_ENDIAN (*(uint16_t *) "\0\xff" < 0x100)
#if MG_ARCH == MG_ARCH_ARMCGT
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#ifndef MG_PATH_MAX
#define MG_PATH_MAX 100
#endif
#endif
#if MG_ARCH == MG_ARCH_ARMGCC
#define _POSIX_TIMERS
#include <ctype.h>
#if !defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP
#include <errno.h>
#endif
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#ifndef MG_PATH_MAX
#define MG_PATH_MAX 100
#endif
#endif
#if MG_ARCH == MG_ARCH_CUBE
#include <ctype.h>
#if !defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP
#include <errno.h>
#endif
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
// Cube-generated header, includes ST Cube HAL
// NOTE: use angle brackets to prevent amalgamator ditching it
#include <main.h>
#ifndef MG_PATH_MAX
#define MG_PATH_MAX 100
#endif
#ifndef MG_ENABLE_TCPIP
#define MG_ENABLE_TCPIP 1 // Enable built-in TCP/IP stack
#endif
#if MG_ENABLE_TCPIP && !defined(MG_ENABLE_DRIVER_STM32F) && \
!defined(MG_ENABLE_DRIVER_STM32H) && !defined(MG_ENABLE_DRIVER_STM32N) && \
!defined(MG_ENABLE_DRIVER_CYW) && !defined(MG_ENABLE_DRIVER_CYW_SDIO) && \
!defined(MG_ENABLE_DRIVER_ST67W6)
#if defined(STM32F1) || defined(STM32F2) || defined(STM32F4) || defined(STM32F7)
#define MG_ENABLE_DRIVER_STM32F 1
#elif defined(STM32H5) || defined(STM32H7)
#define MG_ENABLE_DRIVER_STM32H 1
#elif defined(STM32N6)
#define MG_ENABLE_DRIVER_STM32N 1
#else
#error Select a driver in mongoose_config.h
#endif
#endif
#ifndef MG_TLS
#define MG_TLS MG_TLS_BUILTIN
#endif
#if !defined(MG_OTA) && defined(STM32F1) || defined(STM32F2) || \
defined(STM32F4) || defined(STM32F7)
#define MG_OTA MG_OTA_STM32F
#elif !defined(MG_OTA) && defined(STM32H5)
#define MG_OTA MG_OTA_STM32H5
#elif !defined(MG_OTA) && defined(STM32H7)
#define MG_OTA MG_OTA_STM32H7
#endif
// OTA rollback timer via IWDG. Fixed 10 s hang-detection window (prescaler
// /1024, LSI ~32 kHz: reload = 10 * 32000 / 1024 = 312). mg_ota_poll() feeds
// the watchdog every 500 ms while in MG_OTA_TESTING state; if the event loop
// hangs for 10 s, the IWDG fires and the device rolls back.
// STM32H7 names the peripheral IWDG1; all others use IWDG.
// The total evaluation period is set by MG_OTA_ROLLBACK_TIMEOUT_SECONDS.
// STM32H7 names the watchdog peripheral IWDG1; all other families use IWDG.
#if defined(IWDG1) && !defined(IWDG)
#define MG_IWDG IWDG1
#else
#define MG_IWDG IWDG
#endif
// PR=6 → /256 prescaler, RLR=1250 → ~10 s at LSI 32 kHz.
// Works on F4/F7/H5/H7; uses direct register writes so it is safe before HAL_Init().
#ifndef MG_OTA_ROLLBACK_TIMER_START
#define MG_OTA_ROLLBACK_TIMER_START() \
do { \
MG_IWDG->KR = 0xCCCCU; /* start + LSI */ \
MG_IWDG->KR = 0x5555U; /* unlock PR/RLR */ \
MG_IWDG->PR = 6U; /* /256 */ \
MG_IWDG->RLR = 1250U; /* 10 s */ \
while (MG_IWDG->SR & (IWDG_SR_PVU | IWDG_SR_RVU)) (void) 0; \
MG_IWDG->KR = 0xAAAAU; /* reload */ \
} while (0)
#endif
// Feed (reload) the IWDG. Called from mg_ota_poll() every 500 ms.
#ifndef MG_OTA_ROLLBACK_TIMER_FEED
#define MG_OTA_ROLLBACK_TIMER_FEED() (MG_IWDG->KR = 0xAAAAU)
#endif
// OTA state in a backup register that survives warm resets but clears on POR.
// STATE_GET reads work without any setup. STATE_SET enables the RTC APB clock
// and unlocks backup domain write access via direct register writes, so it is
// safe to call from any context (before or after HAL_Init).
// H5 (TAMP->BKP0R), H7 (RTC->BKP0R), F4/F7 (RTC->BKP0R).
#ifndef MG_OTA_STATE_GET
#if defined(PWR_DBPCR_DBP)
#define MG_OTA_DBP_ENABLE() (PWR->DBPCR |= PWR_DBPCR_DBP)
#elif defined(PWR_CR1_DBP)
#define MG_OTA_DBP_ENABLE() (PWR->CR1 |= PWR_CR1_DBP)
#else
#define MG_OTA_DBP_ENABLE() (PWR->CR |= PWR_CR_DBP)
#endif
#if defined(TAMP)
#define MG_OTA_STATE_GET() \
(RCC->APB3ENR |= RCC_APB3ENR_RTCAPBEN, TAMP->BKP0R)
#define MG_OTA_STATE_SET(v) \
(RCC->APB3ENR |= RCC_APB3ENR_RTCAPBEN, MG_OTA_DBP_ENABLE(), \
(TAMP->BKP0R = (uint32_t) (v)), TAMP->BKP0R)
#elif defined(RCC_APB4ENR_RTCAPBEN)
#define MG_OTA_STATE_GET() (RTC->BKP0R)
#define MG_OTA_STATE_SET(v) \
(RCC->APB4ENR |= RCC_APB4ENR_RTCAPBEN, MG_OTA_DBP_ENABLE(), \
(RTC->BKP0R = (uint32_t) (v)))
#else
#define MG_OTA_STATE_GET() (RTC->BKP0R)
#define MG_OTA_STATE_SET(v) \
(RCC->APB1ENR |= RCC_APB1ENR_PWREN, MG_OTA_DBP_ENABLE(), \
(RTC->BKP0R = (uint32_t) (v)))
#endif
#endif
// use HAL-defined execute-in-ram section
#ifndef MG_IRAM
#define MG_IRAM __attribute__((section(".RamFunc")))
#endif
#ifndef MG_ETH_RAM
#define MG_ETH_RAM __attribute__((section(".eth_ram")))
#endif
#ifndef HAL_ICACHE_MODULE_ENABLED
#define HAL_ICACHE_IsEnabled() 0
#define HAL_ICACHE_Enable() (void) 0
#define HAL_ICACHE_Disable() (void) 0
#endif
#ifndef MG_SET_MAC_ADDRESS
// Construct MAC address from UUID
#define MGUID ((uint32_t *) UID_BASE) // Unique 96-bit chip ID
#define MG_SET_MAC_ADDRESS(mac) \
do { \
int icache_enabled_ = HAL_ICACHE_IsEnabled(); \
if (icache_enabled_) HAL_ICACHE_Disable(); \
mac[0] = 42; \
mac[1] = ((MGUID[0] >> 0) & 255) ^ ((MGUID[2] >> 19) & 255); \
mac[2] = ((MGUID[0] >> 10) & 255) ^ ((MGUID[1] >> 10) & 255); \
mac[3] = (MGUID[0] >> 19) & 255; \
mac[4] = ((MGUID[1] >> 0) & 255) ^ ((MGUID[2] >> 10) & 255); \
mac[5] = ((MGUID[2] >> 0) & 255) ^ ((MGUID[1] >> 19) & 255); \
if (icache_enabled_) HAL_ICACHE_Enable(); \
} while (0)
#endif
#endif
#if MG_ARCH == MG_ARCH_ESP32
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <netdb.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <esp_ota_ops.h> // Use angle brackets to avoid
#include <esp_timer.h> // amalgamation ditching them
#include <esp_system.h>
#include <esp_mac.h>
#include <esp_idf_version.h>
// #include <esp_memory_utils.h> // For health record coredumps
// #include <esp_core_dump.h> // For health record coredumps
#define MG_PATH_MAX 128
#ifndef MG_ENABLE_POSIX_FS
#define MG_ENABLE_POSIX_FS 1
#endif
#ifndef MG_HEALTH_DATA_SIZE
#define MG_HEALTH_DATA_SIZE 20
#endif
#endif
#if MG_ARCH == MG_ARCH_ESP8266
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <netdb.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <esp_system.h>
#define MG_PATH_MAX 128
#endif
#if MG_ARCH == MG_ARCH_MCUXPRESSO
#define _POSIX_TIMERS
#include <ctype.h>
#if !defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP
#include <errno.h>
#endif
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
// MCUXpresso-generated header, includes the SoC peripheral definitions.
// NOTE: use angle brackets to prevent amalgamator ditching it
#include <fsl_device_registers.h>
#ifndef MG_PATH_MAX
#define MG_PATH_MAX 100
#endif
#ifndef MG_ENABLE_TCPIP
#define MG_ENABLE_TCPIP 1 // Enable built-in TCP/IP stack
#endif
#if MG_ENABLE_TCPIP && !defined(MG_ENABLE_DRIVER_IMXRT10) && \
!defined(MG_ENABLE_DRIVER_IMXRT11) && !defined(MG_ENABLE_DRIVER_NETC)
#if defined(CPU_MIMXRT1021DAG5A) || defined(CPU_MIMXRT1024DAG5A) || \
defined(CPU_MIMXRT1042XJM5B) || defined(CPU_MIMXRT1052DVL6B) || \
defined(CPU_MIMXRT1062DVL6B) || defined(CPU_MIMXRT1064DVL6B) || \
defined(CPU_MIMXRT1062DVL6A) || defined(CPU_MIMXRT1064DVL6A) || \
defined(CPU_MIMXRT1062DVMAA_cm7) || defined(CPU_MIMXRT1062DVL6A_cm7) || \
defined(CPU_MIMXRT1062DVL6B_cm7) || defined(_MIMXRT1021_H_) || \
defined(_MIMXRT1024_H_) || defined(_MIMXRT1042_H_) || \
defined(_MIMXRT1052_H_) || defined(_MIMXRT1062_H_) || \
defined(_MIMXRT1064_H_)
#define MG_ENABLE_DRIVER_IMXRT10 1
#elif defined(CPU_MIMXRT1176DVMAA_cm7) || defined(CPU_MIMXRT1176DVMAA_cm4) || \
defined(_MIMXRT1176_cm7_H_) || defined(_MIMXRT1176_cm4_H_) || \
defined(_MIMXRT1176_H_)
#define MG_ENABLE_DRIVER_IMXRT11 1
#elif defined(CPU_MIMXRT1186CVJ8C_cm33) || defined(MIMXRT1186_cm33_H_)
#define MG_ENABLE_DRIVER_NETC 1
#else
#error Select an Ethernet driver in mongoose_config.h
#endif
#endif
#ifndef MG_TLS
#define MG_TLS MG_TLS_BUILTIN
#endif
#if !defined(MG_OTA) && \
(defined(CPU_MIMXRT1021DAG5A) || defined(CPU_MIMXRT1024DAG5A) || \
defined(_MIMXRT1021_H_) || defined(_MIMXRT1024_H_))
#define MG_OTA MG_OTA_RT1020
#elif !defined(MG_OTA) && \
(defined(CPU_MIMXRT1052DVL6B) || defined(_MIMXRT1052_H_))
#define MG_OTA MG_OTA_RT1050
#elif !defined(MG_OTA) && \
(defined(CPU_MIMXRT1062DVL6B) || defined(CPU_MIMXRT1062DVL6A) || \
defined(CPU_MIMXRT1042XJM5B) || defined(_MIMXRT1042_H_) || \
defined(CPU_MIMXRT1062DVMAA_cm7) || defined(CPU_MIMXRT1062DVL6A_cm7) || \
defined(CPU_MIMXRT1062DVL6B_cm7) || defined(_MIMXRT1062_H_))
#define MG_OTA MG_OTA_RT1060
#elif !defined(MG_OTA) && \
(defined(CPU_MIMXRT1064DVL6B) || defined(CPU_MIMXRT1064DVL6A) || \
defined(_MIMXRT1064_H_))
#define MG_OTA MG_OTA_RT1064
#elif !defined(MG_OTA) && \
(defined(CPU_MIMXRT1176DVMAA_cm7) || defined(CPU_MIMXRT1176DVMAA_cm4) || \
defined(_MIMXRT1176_cm7_H_) || defined(_MIMXRT1176_cm4_H_) || \
defined(_MIMXRT1176_H_))
#define MG_OTA MG_OTA_RT1170
#elif !defined(MG_OTA) && \
(defined(CPU_MIMXRT1186CVJ8C_cm33) || defined(MIMXRT1186_cm33_H_))
#define MG_OTA MG_OTA_RT1180
#endif
#ifndef MG_IRAM
#if defined(CPU_MIMXRT1186CVJ8C_cm33) || defined(MIMXRT1186_cm33_H_)
#define MG_IRAM \
__attribute__((noinline, section(".ramfunc.$SRAM_ITC_cm33")))
#else
#define MG_IRAM __attribute__((noinline, section(".data_RAM2")))
#endif
#endif
#ifndef MG_ETH_RAM
#if defined(CPU_MIMXRT1186CVJ8C_cm33) || defined(MIMXRT1186_cm33_H_)
#define MG_ETH_RAM __attribute__((section(".bss.$ETH_RAM")))
#endif
#endif
#ifndef MG_SET_MAC_ADDRESS
#if defined(MG_ENABLE_DRIVER_NETC) && MG_ENABLE_DRIVER_NETC
// No OCOTP_FUSES present on RT1186 to generate the UUID
#define MG_SET_MAC_ADDRESS(mac) (void) (mac)
#else
#if defined(MG_ENABLE_DRIVER_IMXRT11) && MG_ENABLE_DRIVER_IMXRT11
#define MG_OCOTP_FUSES ((volatile uint32_t *) 0x40cac900)
#else
#define MG_OCOTP_FUSES ((volatile uint32_t *) 0x401f4410)
#endif
#define MG_SET_MAC_ADDRESS(mac) \
do { \
mac[0] = 2; \
mac[1] = (MG_OCOTP_FUSES[0] >> 0) & 255; \
mac[2] = (MG_OCOTP_FUSES[0] >> 10) & 255; \
mac[3] = ((MG_OCOTP_FUSES[0] >> 19) ^ (MG_OCOTP_FUSES[4] >> 19)) & 255; \
mac[4] = (MG_OCOTP_FUSES[4] >> 10) & 255; \
mac[5] = (MG_OCOTP_FUSES[4] >> 0) & 255; \
} while (0)
#endif
#endif
#ifndef MG_IMXRT_WDOG1_TIMEOUT_MS
#define MG_IMXRT_WDOG1_TIMEOUT_MS 10000U
#endif
#if defined(CPU_MIMXRT1186CVJ8C_cm33) || defined(MIMXRT1186_cm33_H_)
#define MG_IMXRT_RTWDOG1_UNLOCK() \
do { \
if ((RTWDOG1->CS & RTWDOG_CS_CMD32EN_MASK) != 0U) { \
RTWDOG1->CNT = RTWDOG_UPDATE_KEY; \
} else { \
RTWDOG1->CNT = RTWDOG_UPDATE_KEY & 0xffffU; \
RTWDOG1->CNT = RTWDOG_UPDATE_KEY >> 16; \
} \
while ((RTWDOG1->CS & RTWDOG_CS_ULK_MASK) == 0U) (void) 0; \
} while (0)
#define MG_IMXRT_RTWDOG1_FEED() \
do { \
uint32_t primask_ = __get_PRIMASK(); \
__disable_irq(); \
if ((RTWDOG1->CS & RTWDOG_CS_CMD32EN_MASK) != 0U) { \
RTWDOG1->CNT = RTWDOG_REFRESH_KEY; \
} else { \
RTWDOG1->CNT = RTWDOG_REFRESH_KEY & 0xffffU; \
RTWDOG1->CNT = RTWDOG_REFRESH_KEY >> 16; \
} \
if (primask_ == 0U) __enable_irq(); \
} while (0)
#ifndef MG_OTA_ROLLBACK_TIMER_START
#define MG_OTA_ROLLBACK_TIMER_START() \
do { \
uint32_t primask_ = __get_PRIMASK(); \
SRC_GENERAL_REG->SRMASK &= ~SRC_GENERAL_SRMASK_WDOG1_MASK_MASK; \
__disable_irq(); \
MG_IMXRT_RTWDOG1_UNLOCK(); \
RTWDOG1->WIN = 0U; \
RTWDOG1->TOVAL = (MG_IMXRT_WDOG1_TIMEOUT_MS + 7U) / 8U; \
RTWDOG1->CS = RTWDOG_CS_EN_MASK | RTWDOG_CS_UPDATE_MASK | \
RTWDOG_CS_CLK(1U) | RTWDOG_CS_PRES_MASK | \
RTWDOG_CS_CMD32EN_MASK; \
while ((RTWDOG1->CS & RTWDOG_CS_RCS_MASK) == 0U) (void) 0; \
if (primask_ == 0U) __enable_irq(); \
} while (0)
#endif
#ifndef MG_OTA_ROLLBACK_TIMER_FEED
#define MG_OTA_ROLLBACK_TIMER_FEED() MG_IMXRT_RTWDOG1_FEED()
#endif
// GPR5 survives RTWDOG global resets and is not reserved by the boot ROM.
#ifndef MG_OTA_STATE_GET
#define MG_OTA_STATE_GET() SRC_GENERAL_REG->GPR[5]
#endif
#ifndef MG_OTA_STATE_SET
#define MG_OTA_STATE_SET(v) (SRC_GENERAL_REG->GPR[5] = (uint32_t) (v))
#endif
#else
#define MG_IMXRT_WDOG1_FEED() \
do { \
WDOG1->WSR = 0x5555; \
WDOG1->WSR = 0xaaaa; \
} while (0)
#ifndef MG_OTA_ROLLBACK_TIMER_START
#define MG_OTA_ROLLBACK_TIMER_START() \
do { \
uint16_t wt_ = (uint16_t) ((MG_IMXRT_WDOG1_TIMEOUT_MS / 500) - 1); \
if ((WDOG1->WCR & WDOG_WCR_WDE_MASK) == 0) { \
WDOG1->WMCR = 0; \
WDOG1->WCR = (uint16_t) (WDOG_WCR_WDZST_MASK | WDOG_WCR_WDBG_MASK | \
WDOG_WCR_WDE_MASK | WDOG_WCR_WDT_MASK | \
WDOG_WCR_SRS_MASK | WDOG_WCR_WDW_MASK | \
WDOG_WCR_WT(wt_)); \
} \
MG_IMXRT_WDOG1_FEED(); \
} while (0)
#endif
#ifndef MG_OTA_ROLLBACK_TIMER_FEED
#define MG_OTA_ROLLBACK_TIMER_FEED() MG_IMXRT_WDOG1_FEED()
#endif
#ifndef MG_OTA_STATE_GET
#define MG_OTA_STATE_GET() SNVS->LPGPR[0]
#endif
#ifndef MG_OTA_STATE_SET
#define MG_OTA_STATE_SET(v) \
do { \
if (SNVS->LPSR & (1u << 3)) { \
SNVS->LPLVDR = 0x41736166u; \
SNVS->LPSR = 1u << 3; \
} \
SNVS->LPGPR[0] = (uint32_t) (v); \
} while (0)
#endif
#endif
#endif
#if MG_ARCH == MG_ARCH_PICOSDK
#if !defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP
#include <errno.h>
#endif
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <pico/stdlib.h>
#include <pico/rand.h>
#include <pico/unique_id.h>
int mkdir(const char *, mode_t);
#if MG_OTA == MG_OTA_PICOSDK
#include <hardware/flash.h>
#include <hardware/watchdog.h>
#include <hardware/structs/watchdog.h>
#include <pico/bootrom.h>
#ifndef MG_OTA_WATCHDOG_MS
#if PICO_RP2040
#define MG_OTA_WATCHDOG_MS 8000U // max delay on RP2040 is 8.3 seconds
#else
#define MG_OTA_WATCHDOG_MS 10000U // max delay on RP2350 is 16 seconds
#endif
#endif
#ifndef MG_OTA_ROLLBACK_TIMER_START
#define MG_OTA_ROLLBACK_TIMER_START() watchdog_enable(MG_OTA_WATCHDOG_MS, false)
#endif
#ifndef MG_OTA_ROLLBACK_TIMER_FEED
#define MG_OTA_ROLLBACK_TIMER_FEED() watchdog_update()
#endif
#define MG_OTA_STATE_GET() ((uint32_t) watchdog_hw->scratch[0])
#define MG_OTA_STATE_SET(v) \
(watchdog_hw->scratch[0] = (uint32_t) (v), (uint32_t) watchdog_hw->scratch[0])
#if PICO_RP2040
#define MG_OTA_ROLLBACK() \
do { \
mg_flash->swap_fn(); \
*(volatile unsigned long *) 0xe000ed0c = 0x5fa0004; \
} while (0)
#else
#define MG_OTA_ROLLBACK() \
do { \
mg_flash->swap_fn(); \
((rom_reboot_fn) rom_func_lookup(ROM_FUNC_REBOOT))( \
BOOT_TYPE_NORMAL | 0x100, 1, 0, 0); \
} while (0)
#endif
#endif
#endif
#if MG_ARCH == MG_ARCH_RTTHREAD
#include <rtthread.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#ifndef MG_IO_SIZE
#define MG_IO_SIZE 1460
#endif
#endif // MG_ARCH == MG_ARCH_RTTHREAD
#if MG_ARCH == MG_ARCH_ARMCC || MG_ARCH == MG_ARCH_CMSIS_RTOS1 || \
MG_ARCH == MG_ARCH_CMSIS_RTOS2
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <alloca.h>
#include <string.h>
#include <time.h>
#if MG_ARCH == MG_ARCH_CMSIS_RTOS1
#include "cmsis_os.h" // keep this include
// https://developer.arm.com/documentation/ka003821/latest
extern uint32_t rt_time_get(void);
#elif MG_ARCH == MG_ARCH_CMSIS_RTOS2
#include "cmsis_os2.h" // keep this include
#endif
#define strdup(s) ((char *) mg_strdup(mg_str(s)).buf)
#if defined(__ARMCC_VERSION)
#define mode_t size_t
#if !defined(MG_ENABLE_POSIX_FS) || !MG_ENABLE_POSIX_FS
#else
#define mkdir(a, b) mg_mkdir(a, b)
static inline int mg_mkdir(const char *path, mode_t mode) {
(void) path, (void) mode;
return -1;
}
#endif
#endif
#if (MG_ARCH == MG_ARCH_CMSIS_RTOS1 || MG_ARCH == MG_ARCH_CMSIS_RTOS2) && \
!defined MG_ENABLE_RL && (!defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP) && \
(!defined(MG_ENABLE_TCPIP) || !MG_ENABLE_TCPIP)
#define MG_ENABLE_RL 1
#ifndef MG_SOCK_LISTEN_BACKLOG_SIZE
#define MG_SOCK_LISTEN_BACKLOG_SIZE 3
#endif
#endif
#endif
#if MG_ARCH == MG_ARCH_THREADX
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
// Do not include time.h and stdlib.h, since they conflict with nxd_bsd.h
// extern time_t time(time_t *);
#include <nxd_bsd.h>
#define MG_DIRSEP '\\'
#undef FOPEN_MAX
#ifndef MG_PATH_MAX
#define MG_PATH_MAX 32
#endif
#ifndef MG_SOCK_LISTEN_BACKLOG_SIZE
#define MG_SOCK_LISTEN_BACKLOG_SIZE 3
#endif
#ifndef MG_ENABLE_IPV6
#define MG_ENABLE_IPV6 0
#endif
#define socklen_t int
#define closesocket(x) soc_close(x)
// In order to enable BSD support in NetxDuo, do the following (assuming Cube):
// 1. Add nxd_bsd.h and nxd_bsd.c to the repo:
// https://github.com/eclipse-threadx/netxduo/blob/v6.1.12_rel/addons/BSD/nxd_bsd.c
// https://github.com/eclipse-threadx/netxduo/blob/v6.1.12_rel/addons/BSD/nxd_bsd.h
// 2. Add to tx_user.h
// #define TX_THREAD_USER_EXTENSION int bsd_errno;
// 3. Add to nx_user.h
// #define NX_ENABLE_EXTENDED_NOTIFY_SUPPORT
// 4. Add __CCRX__ build preprocessor constant
// Project -> Properties -> C/C++ -> Settings -> MCU Compiler -> Preprocessor
#endif
#if MG_ARCH == MG_ARCH_TIRTOS
#include <stdlib.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <serrno.h>
#include <sys/socket.h>
#include <ti/sysbios/knl/Clock.h>
#endif
#if MG_ARCH == MG_ARCH_UNIX
#define _DARWIN_UNLIMITED_SELECT 1 // No limit on file descriptors
#if defined(__APPLE__)
#include <mach/mach_time.h>
#endif
#if !defined(MG_ENABLE_EPOLL) && defined(__linux__)
#define MG_ENABLE_EPOLL 1
#elif !defined(MG_ENABLE_POLL)
#define MG_ENABLE_POLL 1
#endif
#include <arpa/inet.h>
#include <ctype.h>
#include <dirent.h>
#if !defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP
#include <errno.h>
#endif
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(MG_ENABLE_EPOLL) && MG_ENABLE_EPOLL
#include <sys/epoll.h>
#elif defined(MG_ENABLE_POLL) && MG_ENABLE_POLL
#include <poll.h>
#else
#include <sys/select.h>
#endif
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#ifndef MG_PATH_MAX
#define MG_PATH_MAX FILENAME_MAX
#endif
#ifndef MG_ENABLE_POSIX_FS
#define MG_ENABLE_POSIX_FS 1
#endif
#ifndef MG_IO_SIZE
#define MG_IO_SIZE 16384
#endif
#endif
#if MG_ARCH == MG_ARCH_WIN32
// Avoid name clashing; (macro expansion producing 'defined' has undefined
// behaviour). See config.h for user options
#ifndef MG_ENABLE_WINSOCK
#if (!defined(MG_ENABLE_TCPIP) || !MG_ENABLE_TCPIP) && \
(!defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP) && \
(!defined(MG_ENABLE_FREERTOS_TCP) || !MG_ENABLE_FREERTOS_TCP)
#define MG_ENABLE_WINSOCK 1
#else
#define MG_ENABLE_WINSOCK 0
#endif
#endif
#ifndef _CRT_RAND_S
#define _CRT_RAND_S
#endif
#ifndef _WIN32_WINNT
#if defined(_MSC_VER) && _MSC_VER < 1700
#define _WIN32_WINNT 0x0400 // Let vc98 pick up wincrypt.h
#else
#define _WIN32_WINNT 0x0600
#endif
#endif
#ifndef WINVER
#define WINVER _WIN32_WINNT
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#endif
#include <ctype.h>
#include <direct.h>
#if !defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP
#include <errno.h>
#endif
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <winsock2.h> // fix missing macros and types
#if defined(_MSC_VER) && _MSC_VER < 1700
#define __func__ ""
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
typedef unsigned char uint8_t;
typedef char int8_t;
typedef unsigned short uint16_t;
typedef short int16_t;
typedef unsigned int uint32_t;
typedef int int32_t;
typedef enum { false = 0, true = 1 } bool;
#define inline __inline
#else
#include <stdbool.h>
#include <stdint.h>
#if MG_ENABLE_WINSOCK
#include <ws2tcpip.h>
#endif
#endif
#include <process.h>
#include <winerror.h>
// For mg_random()
#if defined(_MSC_VER) && _MSC_VER < 1700
#include <wincrypt.h>
#pragma comment(lib, "advapi32.lib")
#endif
#if defined(_MSC_VER) && _MSC_VER <= 1200
#ifndef IPPROTO_IP
#define IPPROTO_IP 0
#endif
#ifndef IP_ADD_MEMBERSHIP
struct ip_mreq {
struct in_addr imr_multiaddr;
struct in_addr imr_interface;
};
#define IP_ADD_MEMBERSHIP 12
#endif
#endif
// Protect from calls like std::snprintf in app code
// See https://github.com/cesanta/mongoose/issues/1047
#ifndef __cplusplus
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#ifndef strdup // For MSVC with _DEBUG, see #1359
#define strdup(x) _strdup(x)
#endif
#endif
#if defined(MG_ENABLE_POLL) && MG_ENABLE_POLL && (!defined(MG_ENABLE_LWIP) || !MG_ENABLE_LWIP)
typedef unsigned long nfds_t; // see #3388
#endif
#if defined(_MSC_VER)
#if MG_ENABLE_WINSOCK
#pragma comment(lib, "ws2_32.lib")
#endif
#ifndef alloca
#define alloca(a) _alloca(a)
#endif
#endif
#define MG_DIRSEP '\\'
#ifndef MG_PATH_MAX
#define MG_PATH_MAX FILENAME_MAX
#endif
#if MG_ENABLE_WINSOCK
#define MG_INVALID_SOCKET INVALID_SOCKET
#define MG_SOCKET_TYPE SOCKET
#define poll(a, b, c) WSAPoll((a), (b), (c))
#define closesocket(x) closesocket(x)
typedef int socklen_t;
#ifndef SO_EXCLUSIVEADDRUSE
#define SO_EXCLUSIVEADDRUSE ((int) (~SO_REUSEADDR))
#endif
#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? WSAGetLastError() : 0)
#define MG_SOCK_PENDING(errcode) \
(((errcode) < 0) && \
(WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEINPROGRESS || \
WSAGetLastError() == WSAEWOULDBLOCK))
#define MG_SOCK_RESET(errcode) \
(((errcode) < 0) && (WSAGetLastError() == WSAECONNRESET))
#endif // MG_ENABLE_WINSOCK
#define realpath(a, b) _fullpath((b), (a), MG_PATH_MAX)
#define sleep(x) Sleep((x) *1000)
#define mkdir(a, b) _mkdir(a)
#define rmdir(a) _rmdir(a)
#define timegm(x) _mkgmtime(x)
#ifndef S_ISDIR
#define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR)
#endif
#ifndef SIGPIPE
#define SIGPIPE 0
#endif
#ifndef MG_ENABLE_POSIX_FS
#define MG_ENABLE_POSIX_FS 1
#endif
#ifndef MG_IO_SIZE
#define MG_IO_SIZE 16384
#endif
#endif
#if MG_ARCH == MG_ARCH_ZEPHYR
#include <zephyr/kernel.h>
#include <zephyr/net/socket.h>
// #include <zephyr/posix/dirent.h>
#include <zephyr/posix/fcntl.h>
#include <zephyr/posix/sys/select.h>
#include <zephyr/random/random.h>
#include <zephyr/version.h>
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#define MG_PUTCHAR(x) printk("%c", x)
#ifndef strdup
#define strdup(s) ((char *) mg_strdup(mg_str(s)).buf)
#endif
#define strerror(x) zsock_gai_strerror(x)
#ifndef FD_CLOEXEC
#define FD_CLOEXEC 0
#endif
#ifndef F_SETFD
#define F_SETFD 0
#endif
#define MG_ENABLE_SSI 0
int rand(void);
int sscanf(const char *, const char *, ...);
#endif
#if defined(MG_ENABLE_FREERTOS) && MG_ENABLE_FREERTOS
#include <FreeRTOS.h>
#include <task.h>
#define MG_ENABLE_CUSTOM_CALLOC 1
static inline void mg_free(void *ptr) {
vPortFree(ptr);
}
// Re-route calloc/free to FreeRTOS functions, don't use stdlib
static inline void *mg_calloc(size_t cnt, size_t size) {
void *p = pvPortMalloc(cnt * size);
if (p != NULL) memset(p, 0, size * cnt);
return p;
}
#endif // MG_ENABLE_FREERTOS
#if defined(MG_ENABLE_FREERTOS_TCP) && MG_ENABLE_FREERTOS_TCP
#include <limits.h>
#include <list.h>
#include <FreeRTOS_IP.h>
#include <FreeRTOS_Sockets.h>
#define MG_SOCKET_TYPE Socket_t
#define MG_INVALID_SOCKET FREERTOS_INVALID_SOCKET
// Why FreeRTOS-TCP did not implement a clean BSD API, but its own thing
// with FreeRTOS_ prefix, is beyond me
#define IPPROTO_TCP FREERTOS_IPPROTO_TCP
#define IPPROTO_UDP FREERTOS_IPPROTO_UDP
#define AF_INET FREERTOS_AF_INET
#define SOCK_STREAM FREERTOS_SOCK_STREAM
#define SOCK_DGRAM FREERTOS_SOCK_DGRAM
#define SO_BROADCAST 0
#define SO_ERROR 0
#define SOL_SOCKET 0
#define SO_REUSEADDR 0
#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? (errcode) : 0)
#define MG_SOCK_PENDING(errcode) \
((errcode) == -pdFREERTOS_ERRNO_EWOULDBLOCK || \
(errcode) == -pdFREERTOS_ERRNO_EISCONN || \
(errcode) == -pdFREERTOS_ERRNO_EINPROGRESS || \
(errcode) == -pdFREERTOS_ERRNO_EAGAIN || \
(errcode) == -pdFREERTOS_ERRNO_ENOSPC)
#define MG_SOCK_RESET(errcode) ((errcode) == -pdFREERTOS_ERRNO_ENOTCONN)
// actually only if optional timeout is enabled
#define MG_SOCK_INTR(fd) (fd == NULL)
#define sockaddr_in freertos_sockaddr
#define sockaddr freertos_sockaddr
#if ipFR_TCP_VERSION_MAJOR >= 4
#define sin_addr sin_address.ulIP_IPv4
#endif
#define accept(a, b, c) FreeRTOS_accept((a), (b), (c))
#define connect(a, b, c) FreeRTOS_connect((a), (b), (c))
#define bind(a, b, c) FreeRTOS_bind((a), (b), (c))
#define listen(a, b) FreeRTOS_listen((a), (b))
#define socket(a, b, c) FreeRTOS_socket((a), (b), (c))
#define send(a, b, c, d) FreeRTOS_send((a), (b), (c), (d))
#define recv(a, b, c, d) FreeRTOS_recv((a), (b), (c), (d))
#define setsockopt(a, b, c, d, e) FreeRTOS_setsockopt((a), (b), (c), (d), (e))
#define sendto(a, b, c, d, e, f) FreeRTOS_sendto((a), (b), (c), (d), (e), (f))
#define recvfrom(a, b, c, d, e, f) \
FreeRTOS_recvfrom((a), (b), (c), (d), (e), (f))
#define closesocket(x) FreeRTOS_closesocket(x)
#define gethostbyname(x) FreeRTOS_gethostbyname(x)
#define getsockname(a, b, c) mg_getsockname((a), (b), (c))
#define getpeername(a, b, c) mg_getpeername((a), (b), (c))
static inline int mg_getsockname(MG_SOCKET_TYPE fd, void *buf, socklen_t *len) {
(void) fd, (void) buf, (void) len;
return -1;
}
static inline int mg_getpeername(MG_SOCKET_TYPE fd, void *buf, socklen_t *len) {
(void) fd, (void) buf, (void) len;
return 0;
}
#endif
#if defined(MG_ENABLE_LWIP) && MG_ENABLE_LWIP
#if defined(__GNUC__) && !defined(__ARMCC_VERSION)
#include <sys/stat.h>
#endif
struct timeval;
#include <lwip/sockets.h>
#if !LWIP_TIMEVAL_PRIVATE
#if defined(__GNUC__) && !defined(__ARMCC_VERSION) // armclang sets both
#include <sys/time.h>
#else
struct timeval {
time_t tv_sec;
long tv_usec;
};
#endif
#endif
#if LWIP_SOCKET != 1
// Sockets support disabled in LWIP by default
#error Set LWIP_SOCKET variable to 1 (in lwipopts.h)
#endif
#endif
#if defined(MG_ENABLE_RL) && MG_ENABLE_RL
#include <rl_net.h>
#define closesocket(x) closesocket(x)
#define TCP_NODELAY SO_KEEPALIVE
#define MG_SOCK_ERR(errcode) ((errcode) < 0 ? (errcode) : 0)
#define MG_SOCK_PENDING(errcode) \
((errcode) == BSD_EWOULDBLOCK || (errcode) == BSD_EALREADY || \
(errcode) == BSD_EINPROGRESS)
#define MG_SOCK_RESET(errcode) \
((errcode) == BSD_ECONNABORTED || (errcode) == BSD_ECONNRESET)
// In blocking mode, which is enabled by default, accept() waits for a
// connection request. In non blocking mode, you must call accept()
// again if the error code BSD_EWOULDBLOCK is returned.
#define MG_SOCK_INTR(fd) (fd == BSD_EWOULDBLOCK)
#define socklen_t int
#endif
#ifndef MG_ENABLE_LOG
#define MG_ENABLE_LOG 1
#endif
#ifndef MG_ENABLE_CUSTOM_CALLOC
#define MG_ENABLE_CUSTOM_CALLOC 0
#endif
#ifndef MG_ENABLE_CUSTOM_LOG
#define MG_ENABLE_CUSTOM_LOG 0 // Let user define their own MG_LOG
#endif
#ifndef MG_ENABLE_FREERTOS
#define MG_ENABLE_FREERTOS 0 // FreeRTOS RTOS
#endif
#ifndef MG_ENABLE_TCPIP
#define MG_ENABLE_TCPIP 0 // Mongoose built-in network stack
#endif
#ifndef MG_ENABLE_BSD_SOCKETS
#define MG_ENABLE_BSD_SOCKETS 0 // BSD API support for built-in stack
#endif
#ifndef MG_ENABLE_BSD_PROTOTYPES
#define MG_ENABLE_BSD_PROTOTYPES 1
#endif
#ifndef MG_ENABLE_LWIP
#define MG_ENABLE_LWIP 0 // lwIP network stack
#endif
#ifndef MG_ENABLE_FREERTOS_TCP
#define MG_ENABLE_FREERTOS_TCP 0 // Amazon FreeRTOS-TCP network stack
#endif
#ifndef MG_ENABLE_RL
#define MG_ENABLE_RL 0 // ARM MDK network stack
#endif
#ifndef MG_ENABLE_SOCKET
#define MG_ENABLE_SOCKET !MG_ENABLE_TCPIP
#endif
#ifndef MG_ENABLE_POLL
#define MG_ENABLE_POLL 0
#endif
#ifndef MG_ENABLE_EPOLL
#define MG_ENABLE_EPOLL 0
#endif
#ifndef MG_ENABLE_FATFS
#define MG_ENABLE_FATFS 0
#endif
#ifndef MG_ENABLE_LFS
#define MG_ENABLE_LFS 0
#endif
#ifndef MG_ENABLE_SSI
#define MG_ENABLE_SSI 0
#endif
#ifndef MG_ENABLE_IPV6
#define MG_ENABLE_IPV6 0
#endif
#ifndef MG_IPV6_V6ONLY
#define MG_IPV6_V6ONLY 0 // IPv6 socket binds only to V6, not V4 address
#endif
#ifndef MG_ENABLE_MD5
#define MG_ENABLE_MD5 1
#endif
// Set MG_ENABLE_WINSOCK=0 for Win32 builds with other external IP stack not
// mentioned in arch_win32.h
#ifndef MG_ENABLE_WINSOCK
#define MG_ENABLE_WINSOCK 1
#endif
#ifndef MG_ENABLE_CUSTOM_RANDOM
#define MG_ENABLE_CUSTOM_RANDOM 0
#endif
#ifndef MG_ENABLE_CUSTOM_MILLIS
#define MG_ENABLE_CUSTOM_MILLIS 0
#endif
#ifndef MG_ENABLE_ASSERT
#define MG_ENABLE_ASSERT 0
#endif
#ifndef MG_IO_SIZE
#define MG_IO_SIZE 512 // Granularity of the send/recv IO buffer growth
#endif
#ifndef MG_MAX_RECV_SIZE
#define MG_MAX_RECV_SIZE (3UL * 1024UL * 1024UL) // Maximum recv IO buffer size
#endif
#ifndef MG_DATA_SIZE
#define MG_DATA_SIZE 32 // struct mg_connection :: data size
#endif
#ifndef MG_MAX_HTTP_HEADERS
#define MG_MAX_HTTP_HEADERS 30
#endif
#ifndef MG_HTTP_INDEX
#define MG_HTTP_INDEX "index.html"
#endif
#ifndef MG_PATH_MAX
#ifdef PATH_MAX
#define MG_PATH_MAX PATH_MAX
#else
#define MG_PATH_MAX 128
#endif
#endif
#ifndef MG_SOCK_LISTEN_BACKLOG_SIZE
#define MG_SOCK_LISTEN_BACKLOG_SIZE 128
#endif
#ifndef MG_DIRSEP
#define MG_DIRSEP '/'
#endif
#ifndef MG_ENABLE_POSIX_FS
#define MG_ENABLE_POSIX_FS MG_ENABLE_LFS
#endif
#ifndef MG_INVALID_SOCKET
#define MG_INVALID_SOCKET (-1)
#endif
#ifndef MG_SOCKET_TYPE
#define MG_SOCKET_TYPE int
#endif
#ifndef MG_SOCKET_ERRNO
#define MG_SOCKET_ERRNO errno
#endif
#if MG_ENABLE_EPOLL
#define MG_EPOLL_ADD(c) \
do { \
struct epoll_event ev = {EPOLLIN | EPOLLERR | EPOLLHUP, {c}}; \
epoll_ctl(c->mgr->epoll_fd, EPOLL_CTL_ADD, (int) (size_t) c->fd, &ev); \
} while (0)
#define MG_EPOLL_MOD(c, wr) \
do { \
struct epoll_event ev = {EPOLLIN | EPOLLERR | EPOLLHUP, {c}}; \
if (wr) ev.events |= EPOLLOUT; \
epoll_ctl(c->mgr->epoll_fd, EPOLL_CTL_MOD, (int) (size_t) c->fd, &ev); \
} while (0)
#else
#define MG_EPOLL_ADD(c)
#define MG_EPOLL_MOD(c, wr)
#endif
#ifndef MG_ENABLE_PROFILE
#define MG_ENABLE_PROFILE 0
#endif
#ifndef MG_ENABLE_TCPIP_DRIVER_INIT // mg_mgr_init() will also initialize
#define MG_ENABLE_TCPIP_DRIVER_INIT 1 // enabled built-in driver for
#endif // Mongoose built-in network stack
#ifndef MG_ENABLE_TCPIP_TCPRTX
#define MG_ENABLE_TCPIP_TCPRTX 0 // TCP retransmission queue
#endif
#ifndef MG_TCPIP_DHCPNAME_SIZE
#define MG_TCPIP_DHCPNAME_SIZE 18 // struct mg_tcpip_if :: dhcp_name size
#endif
#ifndef MG_TCPIP_IP // e.g. MG_IPV4(192, 168, 0, 223)
#define MG_TCPIP_IP MG_IPV4(0, 0, 0, 0) // Default is 0.0.0.0 (DHCP)
#endif
#ifndef MG_TCPIP_MASK
#define MG_TCPIP_MASK MG_IPV4(0, 0, 0, 0) // Default is 0.0.0.0 (DHCP)
#endif
#ifndef MG_TCPIP_GW
#define MG_TCPIP_GW MG_IPV4(0, 0, 0, 0) // Default is 0.0.0.0 (DHCP)
#endif
#if MG_ENABLE_IPV6
#ifndef MG_TCPIP_GLOBAL
#define MG_TCPIP_GLOBAL MG_IPV6(0, 0, 0, 0, 0, 0, 0, 0)
#endif
#ifndef MG_TCPIP_IPV6_LINKLOCAL
#define MG_TCPIP_IPV6_LINKLOCAL MG_IPV6(0, 0, 0, 0, 0, 0, 0, 0)
#endif
#ifndef MG_TCPIP_PREFIX_LEN
#define MG_TCPIP_PREFIX_LEN 0
#endif
#ifndef MG_TCPIP_GW6
#define MG_TCPIP_GW6 MG_IPV6(0, 0, 0, 0, 0, 0, 0, 0)
#endif
#endif
#ifndef MG_SET_MAC_ADDRESS
#define MG_SET_MAC_ADDRESS(mac)
#endif
#ifndef MG_SET_WIFI_CONFIG
#define MG_SET_WIFI_CONFIG(ifp, driver_data)
#endif
#ifndef MG_ENABLE_TCPIP_PRINT_DEBUG_STATS
#define MG_ENABLE_TCPIP_PRINT_DEBUG_STATS 0
#endif
#ifndef MG_TLS_RECORD_SIZE
#define MG_TLS_RECORD_SIZE 16384 // Max plaintext per sent TLS record, builtin
#endif
#ifndef MG_ENABLE_CHACHA20
#define MG_ENABLE_CHACHA20 1 // When set to 0, GCM is used. For MG_TLS_BUILTIN
#endif
// P-256 implementation, for MG_TLS_BUILTIN
#define MG_P256_LENNGREN 1 // Cortex-M4/M7/M33 asm, see tls_p256_cortex.c
#define MG_P256_BEAR 3 // Portable C, BearSSL, see tls_p256.c
#ifndef MG_P256
#if defined(__GNUC__) && defined(__ARM_ARCH_PROFILE) && \
__ARM_ARCH_PROFILE == 'M' && defined(__ARM_FEATURE_DSP) && \
!defined(__ARM_BIG_ENDIAN)
#define MG_P256 MG_P256_LENNGREN // Cortex-M with DSP: M4, M7, M33, M55, M85
#else
#define MG_P256 MG_P256_BEAR
#endif
#endif
#ifndef MG_ENABLE_HEALTH
#define MG_ENABLE_HEALTH 0
#endif
// Macros to record timestamped events that happens with a connection.
// They are saved into a c->prof IO buffer, each event is a name and a 32-bit
// timestamp in milliseconds since connection init time.
//
// Test (run in two separate terminals):
// make -C tutorials/http/http-server/ CFLAGS_EXTRA=-DMG_ENABLE_PROFILE=1
// curl localhost:8000
// Output:
// 1ea1f1e7 2 net.c:150:mg_close_conn 3 profile:
// 1ea1f1e8 2 net.c:150:mg_close_conn 1ea1f1e6 init
// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_OPEN
// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_ACCEPT
// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_READ
// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_HTTP_MSG
// 1ea1f1e8 2 net.c:150:mg_close_conn 0 EV_WRITE
// 1ea1f1e8 2 net.c:150:mg_close_conn 1 EV_CLOSE
//
// Usage:
// Enable profiling by setting MG_ENABLE_PROFILE=1
// Invoke MG_PROF_ADD(c, "MY_EVENT_1") in the places you'd like to measure
#if MG_ENABLE_PROFILE
struct mg_profitem {
const char *name; // Event name
uint32_t timestamp; // Milliseconds since connection creation (MG_EV_OPEN)
};
#define MG_PROFILE_ALLOC_GRANULARITY 256 // Can save 32 items wih to realloc
// Adding a profile item to the c->prof. Must be as fast as possible.
// Reallocation of the c->prof iobuf is not desirable here, that's why we
// pre-allocate c->prof with MG_PROFILE_ALLOC_GRANULARITY.
// This macro just inits and copies 8 bytes, and calls mg_millis(),
// which should be fast enough.
#define MG_PROF_ADD(c, name_) \
do { \
struct mg_iobuf *io = &c->prof; \
uint32_t inittime = ((struct mg_profitem *) io->buf)->timestamp; \
struct mg_profitem item = {name_, (uint32_t) mg_millis() - inittime}; \
mg_iobuf_add(io, io->len, &item, sizeof(item)); \
} while (0)
// Initialising profile for a new connection. Not time sensitive
#define MG_PROF_INIT(c) \
do { \
struct mg_profitem first = {"init", (uint32_t) mg_millis()}; \
mg_iobuf_init(&(c)->prof, 0, MG_PROFILE_ALLOC_GRANULARITY); \
mg_iobuf_add(&c->prof, c->prof.len, &first, sizeof(first)); \
} while (0)
#define MG_PROF_FREE(c) mg_iobuf_free(&(c)->prof)
// Dumping the profile. Not time sensitive
#define MG_PROF_DUMP(c) \
do { \
struct mg_iobuf *io = &c->prof; \
struct mg_profitem *p = (struct mg_profitem *) io->buf; \
struct mg_profitem *e = &p[io->len / sizeof(*p)]; \
MG_INFO(("%lu profile:", c->id)); \
while (p < e) { \
MG_INFO(("%5lx %s", (unsigned long) p->timestamp, p->name)); \
p++; \
} \
} while (0)
#else
#define MG_PROF_INIT(c)
#define MG_PROF_FREE(c)
#define MG_PROF_ADD(c, name)
#define MG_PROF_DUMP(c)
#endif
// A non-owning view of an arbitrary byte buffer. Not NUL-terminated in general.
// Use mg_str() or mg_str_n() to construct; use mg_strdup() for an owned copy.
struct mg_str {
char *buf; // Pointer to the data (not necessarily NUL-terminated)
size_t len; // Number of bytes
};
// Using macro to avoid shadowing the C++ struct constructor, see #1298
#define mg_str(s) mg_str_s(s)
// Creates an mg_str from a NUL-terminated C string. Computes length via
// strlen. Returns {NULL, 0} when s is NULL.
struct mg_str mg_str(const char *s);
// Creates an mg_str from a pointer and explicit byte count. s need not be
// NUL-terminated. Does not copy the data.
struct mg_str mg_str_n(const char *s, size_t n);
// Case-insensitive strcmp for NUL-terminated C strings.
// Returns 0 if equal, <0 if s1 < s2, >0 if s1 > s2.
int mg_casecmp(const char *s1, const char *s2);
// Compares two mg_str by content and length. Returns 0 if identical,
// -1 if str1 < str2, 1 if str1 > str2. Length is used as a tiebreaker
// when one string is a prefix of the other.
int mg_strcmp(const struct mg_str str1, const struct mg_str str2);
// Case-insensitive mg_strcmp.
int mg_strcasecmp(const struct mg_str str1, const struct mg_str str2);
// Returns a heap-allocated, NUL-terminated copy of s.
// Returns {NULL, 0} on OOM or if s.buf is NULL or s.len is 0.
// Caller must mg_free(result.buf) when done.
struct mg_str mg_strdup(const struct mg_str s);
// Matches a string against a glob pattern.
//
// Returns:
// True if the whole str matches pattern.
// Example:
// if (mg_match(hm->uri, mg_str("/api/#"), NULL)) { ... }
// Full examples:
// tutorials/http/http-server, tutorials/http/link-checker,
// tutorials/websocket/websocket-server
// Related APIs:
// mg_str(), mg_str_n(), mg_span(), mg_strcmp(), mg_strcasecmp()
// Notes:
// Pattern wildcards: ? matches one character, * matches any sequence except
// '/', and # matches any sequence including '/'. If caps is not NULL, each
// wildcard capture is stored as a zero-copy mg_str slice into str. Pass
// caps=NULL when captures are not needed. The caps array size should be
// at least the number of wildcards in a pattern plus one. The last cap
// is initialised to an empty string.
// Attention: the pattern must consume the whole str in order to match.
bool mg_match(struct mg_str str, struct mg_str pattern, struct mg_str *caps);
// Splits s at the first occurrence of sep. Sets *a to the part before sep
// and *b to the part after it (sep is not included in either). Both slices
// are zero-copy views into s. a or b may be NULL if that half is not needed.
// If sep is not found, *a is the whole string and *b is empty.
// Returns false if s is empty or s.buf is NULL.
// Example - scan comma-separated key=value pairs:
// ```c
// struct mg_str entry, key, val;
// struct mg_str s = mg_str("a=333,b=777,hello");
//
// while (mg_span(s, &entry, &s, ',')) {
// mg_span(entry, &key, &val, '=');
// printf("key: %.*s, val: %.*s\n", (int) key.len, key.buf, (int) val.len, val.buf);
// }
// ```
bool mg_span(struct mg_str s, struct mg_str *a, struct mg_str *b, char sep);
// Parses str as an unsigned integer into the val_len-byte value at *val.
// val_len must be 1, 2, 4, or 8 (for uint8_t, uint16_t, uint32_t, uint64_t).
// base must be 2, 10, or 16; pass 0 to auto-detect from a "0b" or "0x" prefix.
// The entire string must consist of valid digits with no trailing characters.
// Returns false on overflow, invalid input, unsupported base, or unsupported val_len.
bool mg_str_to_num(struct mg_str str, int base, void *val, size_t val_len);
// Lock-free, single-producer / single-consumer (SPSC) message queue.
// Safe to write from an ISR and read from the main loop without locking,
// as long as only one context writes and one context reads at a time.
// Each message is stored with an internal 32-bit length prefix; the buf
// pointers returned by mg_queue_book() and mg_queue_next() already skip it.
//
// Producer pattern:
// char buf[1024];
// if (mg_queue_book(&q, &buf, len) >= len) {
// memcpy(buf, data, len);
// mg_queue_add(&q, len);
// }
//
// Consumer pattern:
// char *buf;
// size_t len = mg_queue_next(&q, &buf);
// if (len > 0) { process(buf, len); mg_queue_del(&q, len); }
struct mg_queue {
char *buf; // Caller-supplied backing buffer
size_t size; // Size of buf in bytes
volatile size_t tail; // Read offset; updated by the consumer
volatile size_t head; // Write offset; updated by the producer
};
// Initialises q to use the caller-supplied buf/size. buf must remain valid
// for the lifetime of the queue.
void mg_queue_init(struct mg_queue *q, char *buf, size_t size);
// Reserves space for a message of len bytes. Sets *buf to the write location.
// Returns the number of bytes available (>= len means the reservation
// succeeded). Does NOT commit the message; call mg_queue_add() after writing.
size_t mg_queue_book(struct mg_queue *q, char **buf, size_t len);
// Commits the most recently reserved message of len bytes. Must be called
// with the same len passed to the preceding mg_queue_book() call, after
// the data has been written into the buffer it returned.
void mg_queue_add(struct mg_queue *q, size_t len);
// Returns the length of the oldest unconsumed message and sets *buf to its
// start. Returns 0 if the queue is empty. Does NOT remove the message;
// call mg_queue_del() after processing to advance the read position.
size_t mg_queue_next(struct mg_queue *q, char **buf);
// Removes the oldest message of len bytes. Must be called with the same len
// returned by the preceding mg_queue_next() call.
void mg_queue_del(struct mg_queue *q, size_t len);
// Character output function. Called once per output character by mg_xprintf/mg_vxprintf.
// arg is an arbitrary user pointer passed through from the caller.
typedef void (*mg_pfn_t)(char, void *arg);
// Custom %M/%m printer. Reads its own arguments from ap and writes via fn.
// Returns the number of characters printed (excluding the quotes added by %m).
typedef size_t (*mg_pm_t)(mg_pfn_t fn, void *arg, va_list *);
// Formats fmt with printf-like specifiers, calling fn for each output character.
// Returns the total number of characters printed. Supported specifiers:
// - %c - expect char
// - %f, %g - expect double
// - %hhd, %hd, %d, %ld, %lld - for char, short, int, long, int64_t
// - %hhu, %hu, %u, %lu, %llu - same but for unsigned variants
// - %hhx, %hx, %x, %lx, %llx - same, unsigned and hex output
// - %p - expects any pointer, prints 0x..... hex value
// - %s - expects char *
// - %% - prints the % character itself
// - %X.Y - optional width and precision modifiers (e.g.: %1.2d)
// - %.* - optional precision modifier, expected as int argument (e.g.: %.*d)
// - %M - (EXTENSION) expects a pointer to a custom print function and its arguments
// - %m - (EXTENSION) exactly like %M but double-quotes the output
// - %M/%m (custom mg_pm_t printer; %m additionally wraps the output in quotes).
//
// There are higher level wrappers around these functions that use
// predefined output functions, and can print to char buffer, or connection
// send iobuffer, etc etc. - for example, mg_snrptinf(). The %M/%m non-standard
// specifier allows to use custom formtter. There are several pre-defined
// formatters:
// - mg_print_base64 - prints a buffer as a base64-encoded string
// - mg_print_esc - prints a JSON-escaped string
// - mg_print_hex - prints a buffer as a hex string
// - mg_print_ip - prints an IP address in a struct mg_addr
// - mg_print_ip_port - prints IP address and port in a struct mg_addr
// - mg_print_ip4 - prints an IPv4 address
// - mg_print_ip6 - prints an IPv6 address
// - mg_print_mac - prints a MAC address
//
// Usage example:
//
// ```c
// struct a { int a, b; };
//
// size_t print_a(mg_pfn_t fn, void *arg, va_list *ap) {
// struct a *p = va_arg(*ap, struct a *);
// return mg_xprintf(fn, arg, "{%m:%d}", MG_ESC("a"), p->a);
// }
//
// struct a temp = { 42, 43 };
// char buf[100];
//
// mg_snprintf(buf, sizeof(buf), "%M", print_a, &temp); // {"a":42}
// ```
size_t mg_vxprintf(mg_pfn_t fn, void *arg, const char *fmt, va_list *);
size_t mg_xprintf(mg_pfn_t fn, void *arg, const char *fmt, ...);
// Formats data into a caller-supplied buffer.
//
// Returns:
// Number of bytes that would be written if buf were large enough, like
// snprintf().
// Example:
// char buf[20];
// mg_snprintf(buf, sizeof(buf), "{%m:%d}", MG_ESC("status"), 1);
// Full examples:
// tutorials/http/link-checker, tutorials/http/redirect-to-https,
// tutorials/mqtt/mqtt-dashboard/device
// Related APIs:
// mg_xprintf(), mg_printf(), mg_mprintf(), MG_ESC
// Notes:
// These functions are just wrappers around mg_xprintf().
// Call with buf=NULL and len=0 to measure. NUL-terminates if the result fits
// (return value < len). Supports mg_xprintf specifiers, including custom
// %M/%m printers. Use MG_ESC when printing JSON strings.
size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list *ap);
size_t mg_snprintf(char *buf, size_t len, const char *fmt, ...);
// Formats into a heap-allocated NUL-terminated string. Caller must mg_free() it.
char *mg_vmprintf(const char *fmt, va_list *ap);
char *mg_mprintf(const char *fmt, ...);
// Formats into a mg_queue slot. Returns bytes written, or 0 if no space.
size_t mg_queue_printf(struct mg_queue *, const char *fmt, ...);
// Built-in %M/%m printer functions. Each reads its argument(s) from ap.
size_t mg_print_base64(mg_pfn_t, void *arg, va_list *ap); // expects: int len, uint8_t *buf
size_t mg_print_esc(mg_pfn_t, void *arg, va_list *ap); // expects: int len, const char *str -- use MG_ESC()
size_t mg_print_hex(mg_pfn_t, void *arg, va_list *ap); // expects: int len, uint8_t *buf
size_t mg_print_ip(mg_pfn_t, void *arg, va_list *ap); // expects: const struct mg_addr *
size_t mg_print_ip_port(mg_pfn_t, void *arg, va_list *ap); // expects: const struct mg_addr *
size_t mg_print_ip4(mg_pfn_t, void *arg, va_list *ap); // expects: uint32_t *ipv4
size_t mg_print_ip6(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t[16] ipv6
size_t mg_print_mac(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t[6] mac
size_t mg_print_ieee64(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t[8] ieee64
size_t mg_print_l2addr(mg_pfn_t, void *arg, va_list *ap); // expects: uint8_t l2, uint8_t[n] n-byte l2-dependent address
size_t mg_print_html_esc(mg_pfn_t, void *arg, va_list *ap); // expects: int len, const char *str -- use MG_ESC()
// Output functions for use as the fn argument to mg_xprintf/mg_vxprintf.
void mg_pfn_iobuf(char ch, void *param); // param: struct mg_iobuf * (resizes as needed)
void mg_pfn_iobuf_noresize(char ch, void *param); // param: struct mg_iobuf * (fixed buffer, no resize)
void mg_pfn_stdout(char c, void *param); // param: ignored, writes to stdout
// Expands to the three arguments for %m to JSON-escape a string literal.
// Example: mg_snprintf(buf, len, "%m", MG_ESC("hello")) -> "\"hello\""
#define MG_ESC(str) mg_print_esc, 0, (str)
// Logging
//
// Mongoose provides a set of functions and macros for logging. The application can
// use these functions for its own purposes as well as the rest of Mongoose API.
// Log levels
enum { MG_LL_NONE, MG_LL_ERROR, MG_LL_INFO, MG_LL_DEBUG, MG_LL_VERBOSE };
// Set Mongoose logging level. Example: `mg_log_set(MG_LL_INFO);`
extern int mg_log_level; // Current log level, one of MG_LL_*
#define mg_log_set(level_) mg_log_level = (level_)
void mg_log(const char *fmt, ...);
void mg_log_prefix(int ll, const char *file, int line, const char *fname);
// Log a hex dump of binary data `buf`, `len`.
void mg_hexdump(const void *buf, size_t len);
// Set log printer function which prints one byte. Example:
//
// ```c
// static void print_char(char ch, void *param) {
// hal_uart_write_char(param, ch);
// }
// ...
// mg_log_set_fn(print_char, USART3);
// ```
void mg_log_set_fn(mg_pfn_t fn, void *param);
#if MG_ENABLE_LOG
#if !defined(_MSC_VER) && \
(!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L)
#define MG___FUNC__ ""
#else
#define MG___FUNC__ __func__ // introduced in C99
#endif
#define MG_LOG(level, args) \
do { \
if ((level) <= mg_log_level) { \
mg_log_prefix((level), __FILE__, __LINE__, MG___FUNC__); \
mg_log args; \
} \
} while (0)
#else
#define MG_LOG(level, args) \
do { \
if (0) mg_log args; \
} while (0)
#endif
// Logging macros. Note: the argument is exactly like for mg_snprintf(),
// and it should be enclosed in double parenthesis. Example:
// ```c
// MG_INFO(("Conn %lu, recv buf: %.*s", c->id, c->recv.len, c->recv.buf));
// ```
#define MG_ERROR(args) MG_LOG(MG_LL_ERROR, args)
#define MG_INFO(args) MG_LOG(MG_LL_INFO, args)
#define MG_DEBUG(args) MG_LOG(MG_LL_DEBUG, args)
#define MG_VERBOSE(args) MG_LOG(MG_LL_VERBOSE, args)
struct mg_timer {
uint64_t period_ms; // Timer period in milliseconds
uint64_t expire; // Expiration timestamp in milliseconds
unsigned flags; // Possible flags values below
#define MG_TIMER_ONCE 0 // Call function once
#define MG_TIMER_REPEAT 1 // Call function periodically
#define MG_TIMER_RUN_NOW 2 // Call immediately when timer is set
#define MG_TIMER_CALLED 4 // Timer function was called at least once
#define MG_TIMER_AUTODELETE 8 // mg_free() timer when done
void (*fn)(void *); // Function to call
void *arg; // Function argument
struct mg_timer *next; // Linkage
};
void mg_timer_init(struct mg_timer **head, struct mg_timer *timer,
uint64_t milliseconds, unsigned flags, void (*fn)(void *),
void *arg);
void mg_timer_free(struct mg_timer **head, struct mg_timer *);
void mg_timer_poll(struct mg_timer **head, uint64_t new_ms);
// Flags returned by mg_fs.st() and passed to mg_fs.open().
enum { MG_FS_READ = 1, MG_FS_WRITE = 2, MG_FS_DIR = 4, MG_FS_EXCL = 8 };
// Filesystem abstraction. Implement all function pointers to plug in a custom
// filesystem. Short UNIX-style names are used deliberately to avoid conflicts
// with libraries that define macros for stat(), read(), write(), etc.
struct mg_fs {
int (*st)(const char *path, size_t *size, time_t *mtime); // Stat: return MG_FS_* flags; size/mtime may be NULL
void (*ls)(const char *path, void (*fn)(const char *, void *), void *); // List dir: call fn(name, arg) per entry
void *(*op)(const char *path, int flags); // Open file; return opaque fd, or NULL on error
void (*cl)(void *fd); // Close file
size_t (*rd)(void *fd, void *buf, size_t len); // Read up to len bytes; return bytes read
size_t (*wr)(void *fd, const void *buf, size_t len); // Write len bytes; return bytes written
size_t (*sk)(void *fd, size_t offset); // Seek to offset from start; return new position
bool (*mv)(const char *from, const char *to); // Rename/move file
bool (*rm)(const char *path); // Delete file
bool (*mkd)(const char *path); // Create directory
};
extern struct mg_fs mg_fs_posix; // POSIX filesystem (open/close/read/write/seek)
extern struct mg_fs mg_fs_packed; // Read-only packed filesystem (see tutorials/core/embedded-filesystem)
extern struct mg_fs mg_fs_fat; // FAT filesystem
// Open file descriptor returned by mg_fs_open(). Bundles the raw fd with its fs.
struct mg_fd {
void *fd; // Opaque file handle from fs->op()
struct mg_fs *fs; // Filesystem that owns fd
};
// Opens path with the given MG_FS_* flags. Returns NULL on error.
struct mg_fd *mg_fs_open(struct mg_fs *fs, const char *path, int flags);
// Closes fd and frees the mg_fd struct. Safe to call with NULL.
void mg_fs_close(struct mg_fd *fd);
// Sequential directory iterator. Call repeatedly with the same buf/len;
// each call fills buf with the next entry name. Returns false when done.
// Note: make sure buf[0] == 0 before the first call
bool mg_fs_ls(struct mg_fs *fs, const char *path, char *buf, size_t len);
// Reads the entire file into a heap-allocated buffer. The returned mg_str is
// NUL-terminated. Caller must mg_free(result.buf). Returns {NULL,0} on error.
struct mg_str mg_file_read(struct mg_fs *fs, const char *path);
// Atomically writes buf/len to path (via a temp file + rename). Returns false on error.
bool mg_file_write(struct mg_fs *fs, const char *path, const void *buf, size_t len);
// Formats a string and atomically writes it to path. Returns false on error.
bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...);
// A file stored in memory (flash or RAM), used by mg_fs_packed.
struct mg_mem_file {
const char *path; // Virtual file path, e.g. "/web_root/index.html"
const unsigned char *data; // Pointer to file content
size_t size; // Content size in bytes
time_t mtime; // Modification time (may be 0)
};
// Pointer to a NULL-path-terminated array of in-memory files for mg_fs_packed.
// Override to serve a custom set of files:
// static const struct mg_mem_file my_files[] = {
// {"/index.html", data, sizeof(data), 0},
// {NULL, NULL, 0, 0},
// };
// mg_mem_files = my_files;
extern const struct mg_mem_file *mg_mem_files;
// Looks up path in the packed file array. Returns an mg_str pointing directly
// into the stored data (zero-copy). Returns {NULL,0} if not found.
struct mg_str mg_unpacked(const char *path);
// Array of packed files generated by the "pack" utility into packed_fs.c.
// Activate by assigning: mg_mem_files = mg_packed_files;
extern const struct mg_mem_file mg_packed_files[];
#if MG_ENABLE_LFS
#include <fcntl.h>
#include <lfs.h>
#ifndef DT_DIR
#define DT_DIR 4
#endif
#ifndef DT_REG
#define DT_REG 8
#endif
struct dirent {
char d_name[LFS_NAME_MAX + 1];
unsigned char d_type;
};
typedef struct mg_lfs_fd DIR;
#endif
#if MG_ENABLE_ASSERT
#include <assert.h>
#elif !defined(assert)
#define assert(x)
#endif
// Wrappers around calloc/free. Override by defining MG_ENABLE_CUSTOM_CALLOC=1
// and providing your own implementations.
void *mg_calloc(size_t count, size_t size);
void mg_free(void *ptr);
// Zeroes len bytes at buf using a volatile write loop that the compiler cannot
// elide. Safe to call with buf=NULL. Use instead of memset() for clearing
// sensitive data (keys, passwords).
void mg_bzero(volatile unsigned char *buf, size_t len);
// Fixed-length constant-time byte equality. Use for MACs, tags, signatures.
bool mg_memeq(const void *a, const void *b, size_t n);
// Fills buf with len cryptographically random bytes. Uses the best available
// hardware or OS source (hardware RNG, /dev/urandom, CryptGenRandom, etc.).
// Falls back to rand() with an error log if no strong source is available.
// Returns true when a strong source was used, false on fallback to rand().
// Override by defining MG_ENABLE_CUSTOM_RANDOM=1 and providing your own impl.
bool mg_random(void *buf, size_t len);
// Fills buf with len-1 random alphanumeric characters ([a-zA-Z0-9]) and
// NUL-terminates. Returns buf.
char *mg_random_str(char *buf, size_t len);
// Computes CRC32 (polynomial 0xEDB88320) over buf/len. Pass crc=0 to start
// a new checksum; pass the result of a prior call to extend over more data.
uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len);
// Computes CRC16 (HDLC, polynomial 0x8408) over buf/len. Pass crc=0 to start
// a new checksum; pass the result of a prior call to extend over more data.
uint16_t mg_crc16(uint16_t crc, const char *buf, size_t len);
// Returns true if path is safe to serve from the filesystem. Rejects paths
// that start with '~' or '..', or contain a '/../' component, to prevent
// directory traversal attacks.
bool mg_path_is_sane(const struct mg_str path);
// Busy-waits for at least ms milliseconds using mg_millis(). Blocks the
// calling context; avoid in event handlers.
void mg_delayms(unsigned int ms);
uint64_t mg_timegm(unsigned int year, unsigned int month, unsigned int day,
unsigned int hour, unsigned int min, unsigned int sec);
// Packs four byte values into a uint32_t in big-endian order.
// MG_U32(1, 2, 3, 4) == 0x01020304
#define MG_U32(a, b, c, d) \
(((uint32_t) ((a) &255) << 24) | ((uint32_t) ((b) &255) << 16) | \
((uint32_t) ((c) &255) << 8) | (uint32_t) ((d) &255))
// Constructs an IPv4 address in network byte order from four decimal octets.
// Usage: uint32_t ip = MG_IPV4(192, 168, 1, 1);
#define MG_IPV4(a, b, c, d) mg_htonl(MG_U32(a, b, c, d))
// Expands to a brace-enclosed byte initialiser for a 16-byte IPv6 address.
// Arguments are the eight 16-bit groups in the address, e.g.:
// uint8_t ip6[16] = MG_IPV6(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1);
#define MG_IPV6(a, b, c, d, e, f, g ,h) \
{ (uint8_t)((a)>>8),(uint8_t)(a), \
(uint8_t)((b)>>8),(uint8_t)(b), \
(uint8_t)((c)>>8),(uint8_t)(c), \
(uint8_t)((d)>>8),(uint8_t)(d), \
(uint8_t)((e)>>8),(uint8_t)(e), \
(uint8_t)((f)>>8),(uint8_t)(f), \
(uint8_t)((g)>>8),(uint8_t)(g), \
(uint8_t)((h)>>8),(uint8_t)(h) }
// For printing IPv4 addresses: printf("%d.%d.%d.%d\n", MG_IPADDR_PARTS(&ip))
#define MG_U8P(ADDR) ((uint8_t *) (ADDR))
#define MG_IPADDR_PARTS(ADDR) \
MG_U8P(ADDR)[0], MG_U8P(ADDR)[1], MG_U8P(ADDR)[2], MG_U8P(ADDR)[3]
// Read an unaligned big-endian value from byte pointer p into a native integer.
// Safe on architectures that forbid unaligned access (e.g. Cortex-M0).
#define MG_LOAD_BE16(p) \
((uint16_t) (((uint16_t) MG_U8P(p)[0] << 8U) | MG_U8P(p)[1]))
#define MG_LOAD_BE24(p) \
((uint32_t) (((uint32_t) MG_U8P(p)[0] << 16U) | \
((uint32_t) MG_U8P(p)[1] << 8U) | MG_U8P(p)[2]))
#define MG_LOAD_BE32(p) \
((uint32_t) (((uint32_t) MG_U8P(p)[0] << 24U) | \
((uint32_t) MG_U8P(p)[1] << 16U) | \
((uint32_t) MG_U8P(p)[2] << 8U) | MG_U8P(p)[3]))
#define MG_LOAD_BE64(p) \
((uint64_t) (((uint64_t) MG_U8P(p)[0] << 56U) | \
((uint64_t) MG_U8P(p)[1] << 48U) | \
((uint64_t) MG_U8P(p)[2] << 40U) | \
((uint64_t) MG_U8P(p)[3] << 32U) | \
((uint64_t) MG_U8P(p)[4] << 24U) | \
((uint64_t) MG_U8P(p)[5] << 16U) | \
((uint64_t) MG_U8P(p)[6] << 8U) | MG_U8P(p)[7]))
// Write a native integer to byte pointer p in big-endian byte order.
// Safe on architectures that forbid unaligned access.
#define MG_STORE_BE16(p, n) \
do { \
MG_U8P(p)[0] = (uint8_t) ((n) >> 8U); \
MG_U8P(p)[1] = (uint8_t) (n); \
} while (0)
#define MG_STORE_BE24(p, n) \
do { \
MG_U8P(p)[0] = (uint8_t) ((n) >> 16U); \
MG_U8P(p)[1] = (uint8_t) ((n) >> 8U); \
MG_U8P(p)[2] = (uint8_t) (n); \
} while (0)
#define MG_STORE_BE32(p, n) \
do { \
MG_U8P(p)[0] = (uint8_t) ((n) >> 24U); \
MG_U8P(p)[1] = (uint8_t) ((n) >> 16U); \
MG_U8P(p)[2] = (uint8_t) ((n) >> 8U); \
MG_U8P(p)[3] = (uint8_t) (n); \
} while (0)
#define MG_STORE_BE64(p, n) \
do { \
MG_U8P(p)[0] = (uint8_t) ((n) >> 56U); \
MG_U8P(p)[1] = (uint8_t) ((n) >> 48U); \
MG_U8P(p)[2] = (uint8_t) ((n) >> 40U); \
MG_U8P(p)[3] = (uint8_t) ((n) >> 32U); \
MG_U8P(p)[4] = (uint8_t) ((n) >> 24U); \
MG_U8P(p)[5] = (uint8_t) ((n) >> 16U); \
MG_U8P(p)[6] = (uint8_t) ((n) >> 8U); \
MG_U8P(p)[7] = (uint8_t) (n); \
} while (0)
// Network / host byte-order conversion (big-endian <-> native).
// mg_htons/mg_htonl/mg_htonll are aliases for the same operation (symmetric).
uint16_t mg_ntohs(uint16_t net);
uint32_t mg_ntohl(uint32_t net);
uint64_t mg_ntohll(uint64_t net);
#define mg_htons(x) mg_ntohs(x)
#define mg_htonl(x) mg_ntohl(x)
#define mg_htonll(x) mg_ntohll(x)
// Memory-mapped register access: reads/writes a volatile uint32_t at address x.
#define MG_REG(x) ((volatile uint32_t *) (x))[0]
// Produces a uint32_t with bit x set. x must be 0-31.
#define MG_BIT(x) (((uint32_t) 1U) << (x))
// Clears bits in CLRMASK and sets bits in SETMASK in register R atomically
// (read-modify-write). Example: MG_SET_BITS(MG_REG(addr), 0xF, 0x3);
#define MG_SET_BITS(R, CLRMASK, SETMASK) (R) = ((R) & ~(CLRMASK)) | (SETMASK)
// Round x up / down to the nearest multiple of a. Returns x when a is 0.
#define MG_ROUND_UP(x, a) ((a) == 0 ? (x) : ((((x) + (a) -1) / (a)) * (a)))
#define MG_ROUND_DOWN(x, a) ((a) == 0 ? (x) : (((x) / (a)) * (a)))
#if defined(__GNUC__) && defined(__arm__)
#ifdef __ZEPHYR__
#define MG_ARM_DISABLE_IRQ() __asm__ __volatile__("cpsid i" : : : "memory")
#define MG_ARM_ENABLE_IRQ() __asm__ __volatile__("cpsie i" : : : "memory")
#else
#define MG_ARM_DISABLE_IRQ() asm volatile("cpsid i" : : : "memory")
#define MG_ARM_ENABLE_IRQ() asm volatile("cpsie i" : : : "memory")
#endif // !ZEPHYR
#elif defined(__CCRH__)
#define MG_RH850_DISABLE_IRQ() __DI()
#define MG_RH850_ENABLE_IRQ() __EI()
#else
#define MG_ARM_DISABLE_IRQ()
#define MG_ARM_ENABLE_IRQ()
#endif
#if defined(__CC_ARM)
#define MG_DSB() __dsb(0xf)
#elif defined(__ARMCC_VERSION)
#define MG_DSB() __builtin_arm_dsb(0xf)
#elif defined(__GNUC__) && defined(__arm__) && defined(__thumb__)
#ifdef __ZEPHYR__
#define MG_DSB() __asm__("DSB 0xf")
#else
#define MG_DSB() asm("DSB 0xf")
#endif // !ZEPHYR
#elif defined(__ICCARM__)
#define MG_DSB() __iar_builtin_DSB()
#else
#define MG_DSB()
#endif
struct mg_addr;
int mg_check_ip_acl(struct mg_str acl, struct mg_addr *remote_ip);
// Linked list management macros
#define LIST_ADD_HEAD(type_, head_, elem_) \
do { \
(elem_)->next = (*head_); \
*(head_) = (elem_); \
} while (0)
#define LIST_ADD_TAIL(type_, head_, elem_) \
do { \
type_ **h = head_; \
while (*h != NULL) h = &(*h)->next; \
*h = (elem_); \
} while (0)
#define LIST_DELETE(type_, head_, elem_) \
do { \
type_ **h = head_; \
while (*h != (elem_)) h = &(*h)->next; \
*h = (elem_)->next; \
} while (0)
// Returns the port from url. Uses scheme defaults when no port is explicit:
// http/ws=80, https/wss=443, mqtt=1883, mqtts=8883. Returns 0 if unknown.
unsigned short mg_url_port(const char *url);
// Returns true if url uses a TLS scheme (https, wss, mqtts, ssl, tls, tcps).
bool mg_url_is_ssl(const char *url);
// Returns the hostname from url as mg_str, without port or brackets.
// E.g. "http://foo.com:8080/x" -> "foo.com". Not NUL-terminated.
struct mg_str mg_url_host(const char *url);
// Returns the username from url (user:pass@host form) as mg_str.
// Returns an empty mg_str if no credentials are present. Not NUL-terminated.
struct mg_str mg_url_user(const char *url);
// Returns the password from url (user:pass@host form) as mg_str.
// Returns an empty mg_str if no credentials are present. Not NUL-terminated.
struct mg_str mg_url_pass(const char *url);
// Returns a pointer to the URI path in url (e.g. "/api/v1").
// Returns "/" if the url has no path. Points into the original url string.
const char *mg_url_uri(const char *url);
// Dynamically-sized I/O buffer. len <= size always holds.
struct mg_iobuf {
unsigned char *buf; // Heap-allocated data buffer
size_t size; // Allocated capacity (rounded up to align)
size_t len; // Number of bytes currently stored
size_t align; // Allocation granularity; capacity is always a multiple of this
};
// Initializes io to empty, then allocates size bytes with the given alignment.
// Returns false on allocation failure.
bool mg_iobuf_init(struct mg_iobuf *io, size_t size, size_t align);
// Resizes the buffer to new_size (rounded up to io->align). new_size=0 frees
// the buffer. Uses mg_calloc+mg_free (not realloc) so old memory is zeroed on release.
// Returns false on allocation failure.
bool mg_iobuf_resize(struct mg_iobuf *io, size_t new_size);
// Frees the buffer and zeroes the struct. Equivalent to mg_iobuf_resize(io, 0).
void mg_iobuf_free(struct mg_iobuf *io);
// Inserts len bytes from buf at offset ofs, shifting existing data right.
// Pass buf=NULL to reserve space without writing. Returns bytes inserted, 0 on OOM.
size_t mg_iobuf_add(struct mg_iobuf *io, size_t ofs, const void *buf, size_t len);
// Removes len bytes at ofs, shifting remaining data left. Clamps to available data.
// Returns bytes actually removed.
size_t mg_iobuf_del(struct mg_iobuf *io, size_t ofs, size_t len);
size_t mg_base64_update(unsigned char input_byte, char *buf, size_t len);
size_t mg_base64_final(char *buf, size_t len);
size_t mg_base64_encode(const unsigned char *p, size_t n, char *buf, size_t);
size_t mg_base64_decode(const char *src, size_t n, char *dst, size_t);
size_t mg_base64url_encode(const unsigned char *p, size_t n, char *buf,
size_t);
size_t mg_base64url_decode(const char *src, size_t n, char *dst, size_t);
typedef struct {
uint32_t buf[4];
uint32_t bits[2];
unsigned char in[64];
} mg_md5_ctx;
void mg_md5_init(mg_md5_ctx *c);
void mg_md5_update(mg_md5_ctx *c, const unsigned char *data, size_t len);
void mg_md5_final(mg_md5_ctx *c, unsigned char[16]);
typedef struct {
uint32_t state[5];
uint32_t count[2];
unsigned char buffer[64];
} mg_sha1_ctx;
void mg_sha1_init(mg_sha1_ctx *);
void mg_sha1_update(mg_sha1_ctx *, const unsigned char *data, size_t len);
void mg_sha1_final(unsigned char digest[20], mg_sha1_ctx *);
// https://github.com/B-Con/crypto-algorithms
// Author: Brad Conte (brad AT bradconte.com)
// Disclaimer: This code is presented "as is" without any guarantees.
// Details: Defines the API for the corresponding SHA1 implementation.
// Copyright: public domain
typedef struct {
uint32_t state[8];
uint64_t bits;
uint32_t len;
unsigned char buffer[64];
} mg_sha256_ctx;
void mg_sha256_init(mg_sha256_ctx *);
void mg_sha256_update(mg_sha256_ctx *, const unsigned char *data, size_t len);
void mg_sha256_final(unsigned char digest[32], mg_sha256_ctx *);
void mg_sha256(uint8_t dst[32], uint8_t *data, size_t datasz);
void mg_hmac_sha256(uint8_t dst[32], uint8_t *key, size_t keysz, uint8_t *data,
size_t datasz);
typedef struct {
uint64_t state[8];
uint8_t buffer[128];
uint64_t bitlen[2];
uint32_t datalen;
} mg_sha384_ctx;
void mg_sha384_init(mg_sha384_ctx *ctx);
void mg_sha384_update(mg_sha384_ctx *ctx, const uint8_t *data, size_t len);
void mg_sha384_final(uint8_t digest[48], mg_sha384_ctx *ctx);
void mg_sha384(uint8_t dst[48], uint8_t *data, size_t datasz);
struct mg_connection;
// User-supplied event handler. ev is enum mg_event; ev_data type depends on
// the event (see enum below). c->fn_data is the user pointer passed to
// mg_listen(), mg_connect(), mg_*_listen(), mg_*_connect().
typedef void (*mg_event_handler_t)(struct mg_connection *, int ev,
void *ev_data);
// Fires ev on connection c, invoking c->fn and c->pfn with ev_data.
void mg_call(struct mg_connection *c, int ev, void *ev_data);
// Sets c->is_closing and fires MG_EV_ERROR with a printf-formatted message.
void mg_error(struct mg_connection *c, const char *fmt, ...);
// Event codes passed to mg_event_handler_t. Each entry shows the type of
// ev_data cast to use inside the handler, e.g.: char *msg = (char *) ev_data;
enum mg_event {
// Event Meaning ev_data type
MG_EV_ERROR, // Error char *error_message
MG_EV_OPEN, // Connection created NULL
MG_EV_POLL, // mg_mgr_poll iteration uint64_t *uptime_millis
MG_EV_RESOLVE, // Host name is resolved NULL
MG_EV_CONNECT, // Connection established NULL
MG_EV_ACCEPT, // Connection accepted NULL
MG_EV_TLS_HS, // TLS handshake succeeded NULL
MG_EV_READ, // Data received from socket long *bytes_read
MG_EV_WRITE, // Data written to socket long *bytes_written
MG_EV_CLOSE, // Connection closed NULL
MG_EV_HTTP_HDRS, // HTTP headers struct mg_http_message *
MG_EV_HTTP_MSG, // Full HTTP request/response struct mg_http_message *
MG_EV_WS_OPEN, // Websocket handshake done struct mg_http_message *
MG_EV_WS_MSG, // Websocket msg, text or bin struct mg_ws_message *
MG_EV_WS_CTL, // Websocket control msg struct mg_ws_message *
MG_EV_MQTT_CMD, // MQTT low-level command struct mg_mqtt_message *
MG_EV_MQTT_MSG, // MQTT PUBLISH received struct mg_mqtt_message *
MG_EV_MQTT_OPEN, // MQTT CONNACK received int *connack_status_code
MG_EV_SNTP_TIME, // SNTP time received uint64_t *epoch_millis
MG_EV_WAKEUP, // mg_wakeup() data received struct mg_str *data
MG_EV_MDNS_REQ, // mDNS request struct mg_mdns_req *
MG_EV_MDNS_RESP, // mDNS response struct mg_mdns_resp *
MG_EV_MODBUS_REQ, // Modbus TCP request struct mg_modbus_req *
MG_EV_USER // Starting ID for user events
};
// DNS server configuration used by struct mg_mgr.
struct mg_dns {
const char *url; // DNS server URL, e.g. "udp://8.8.8.8:53"
struct mg_connection *c; // Active DNS connection; NULL when idle
};
// Network address: IPv4 or IPv6 address plus port. All values in network byte order.
struct mg_addr {
union { // IP address bytes in network byte order
uint8_t ip[16]; // Raw 16-byte buffer; IPv4 uses the first 4 bytes
uint32_t ip4; // IPv4 address alias (same storage as ip[0..3])
uint64_t ip6[2]; // IPv6 address as two 64-bit words
} addr;
uint16_t port; // TCP or UDP port in network byte order
uint8_t scope_id; // IPv6 scope ID (from the %N suffix in the address)
bool is_ip6; // True when this address holds an IPv6 address
};
union mg_pipe {
MG_SOCKET_TYPE fd;
void *q;
};
// Central event manager. Zero-initialise with mg_mgr_init() before use.
struct mg_mgr {
struct mg_connection *conns; // Linked list of all open connections
struct mg_dns dns4; // IPv4 DNS server (default: 8.8.8.8:53)
struct mg_dns dns6; // IPv6 DNS server (default: [2001:4860:4860::8888]:53)
struct mg_connection *mdns; // mDNS listener connection, or NULL
int dnstimeout; // DNS resolve timeout in ms (default: 3000)
bool use_dns6; // If true, prefer DNS6 for hostname resolution
unsigned long nextid; // Auto-incrementing counter for connection IDs
void *userdata; // Arbitrary user pointer
void *tls_ctx; // Shared TLS context for all TLS connections
uint16_t mqtt_id; // Packet ID counter for MQTT pub/sub
void *active_dns_requests; // Pending DNS queries (internal)
void *active_mdns_requests; // Pending mDNS resolver queries (internal)
struct mg_timer *timers; // Linked list of active timers
int epoll_fd; // epoll file descriptor; -1 when unused (MG_EPOLL_ENABLE=1)
struct mg_tcpip_if *ifp; // Builtin TCP/IP stack: network interface pointer
size_t extraconnsize; // Builtin TCP/IP stack: extra bytes allocated per connection
union mg_pipe pipe; // Socketpair write-end / queue, used by mg_wakeup()
struct mg_rpc *rpcs; // Linked list of registered RPC handlers
#if MG_ENABLE_FREERTOS_TCP
SocketSet_t ss; // FreeRTOS-TCP socket set
#endif
};
// A single network connection. Do not allocate directly; use mg_connect() or
// mg_listen(). Each connection has an event handler fn called for every event.
struct mg_connection {
struct mg_connection *next; // Next connection in mgr->conns linked list
struct mg_mgr *mgr; // Manager that owns this connection
struct mg_addr loc; // Local address (IP + port)
struct mg_addr rem; // Remote address (IP + port)
void *fd; // OS socket handle or LWIP PCB pointer
unsigned long id; // Unique, auto-incrementing connection ID
struct mg_iobuf recv; // Received data; consume in MG_EV_READ handler
struct mg_iobuf send; // Data to send; append with mg_send() / mg_printf()
struct mg_iobuf prof; // Profiling data (MG_ENABLE_PROFILE only)
struct mg_iobuf rtls; // TLS only: encrypted data buffered before decryption
mg_event_handler_t fn; // User event handler
void *fn_data; // User-supplied argument passed to fn
mg_event_handler_t pfn; // Protocol-level handler (set by protocol modules)
void *pfn_data; // Protocol-level handler argument
char data[MG_DATA_SIZE]; // Scratch space for protocol state; freely readable
void *tls; // TLS state (internal)
unsigned dscp : 6; // Differentiated Services Code Point
#define MG_DSCP_CS(class_) ((class_) << 3)
#define MG_DSCP_AF(class_, drop_) (((class_) << 3) | ((drop_) << 1))
#define MG_DSCP_EF 46
unsigned is_listening : 1; // Listening connection; accepts inbound connections
unsigned is_client : 1; // Outbound connection created by mg_connect()
unsigned is_accepted : 1; // Inbound connection accepted from a listener
unsigned is_resolving : 1; // DNS resolution is in progress
unsigned is_arplooking : 1; // ARP resolution is in progress (builtin TCP/IP)
unsigned is_connecting : 1; // Non-blocking TCP connect is in progress
unsigned is_tls : 1; // TLS connection
unsigned is_tls_hs : 1; // TLS handshake is in progress
unsigned is_udp : 1; // UDP connection (not TCP)
unsigned is_websocket : 1; // WebSocket connection
unsigned is_mqtt5 : 1; // MQTT5 connection (set by mg_mqtt_login)
unsigned is_hexdumping : 1; // Log hex dump of all in/out traffic
unsigned is_draining : 1; // Flush send buffer, then close
unsigned is_closing : 1; // Request Close and free on next poll
unsigned is_full : 1; // Pause incoming reads until cleared
unsigned is_tls_throttled : 1; // TLS write was throttled; retry pending
unsigned is_readable : 1; // Socket is ready to read (epoll/select)
unsigned is_writable : 1; // Socket is ready to write (epoll/select)
unsigned is_bsd : 1; // Built-in TCP/IP stack BSD socket
};
// Runs one iteration of the event loop.
//
// Example:
// while (keep_running) mg_mgr_poll(&mgr, 50);
// Full examples:
// tutorials/http/http-server, tutorials/mqtt/mqtt-client,
// tutorials/websocket/websocket-server, tutorials/core/timers
// Related APIs:
// mg_mgr_init(), mg_mgr_free(), mg_timer_add(), mg_wakeup()
// Notes:
// Waits up to ms milliseconds for I/O events; use ms=0 to return
// immediately. Calls event handlers for ready connections and fires expired
// timers. Call repeatedly from the main loop or a dedicated network task.
void mg_mgr_poll(struct mg_mgr *, int ms);
// Initialises an event manager before use.
//
// Example:
// struct mg_mgr mgr;
// mg_mgr_init(&mgr);
// Full examples:
// tutorials/http/http-server, tutorials/mqtt/mqtt-client,
// tutorials/websocket/websocket-server, tutorials/core/embedded-filesystem
// Related APIs:
// mg_mgr_poll(), mg_mgr_free(), mg_http_listen(), mg_connect()
// Notes:
// Sets safe defaults such as DNS servers, DNS timeout, epoll/SIGPIPE setup,
// and TLS context. Call before creating connections, listeners, or timers.
// Set mgr.userdata after this call if your application needs it.
// On embedded systems, if the built-in TCP/IP stack is enabled
// (MG_ENABLE_TCPIP=1) and one built-in driver is selected, e.g.
// MG_ENABLE_DRIVER_STM32H=1, mg_mgr_init() also calls mg_tcpip_init().
// If no driver is selected, automatic driver init is disabled, or multiple
// interfaces are intended, call mg_tcpip_init() separately.
void mg_mgr_init(struct mg_mgr *);
// Closes all connections, frees all timers, and releases the TLS context.
// Safe to call on a partially initialised mgr.
void mg_mgr_free(struct mg_mgr *);
// Creates a listening connection on url (e.g. "tcp://0.0.0.0:8080").
// Fires MG_EV_OPEN on the listener, then MG_EV_ACCEPT for each new client.
// Returns NULL on error.
struct mg_connection *mg_listen(struct mg_mgr *, const char *url,
mg_event_handler_t fn, void *fn_data);
// Opens an outbound connection to url (e.g. "tcp://example.com:80").
// DNS resolution is asynchronous. Fires MG_EV_RESOLVE, then MG_EV_CONNECT
// when the connection is established (or MG_EV_ERROR on failure).
// Returns NULL on error (OOM, network down, null url).
struct mg_connection *mg_connect(struct mg_mgr *, const char *url,
mg_event_handler_t fn, void *fn_data);
// Wraps an existing OS file descriptor in a Mongoose connection.
// The connection is added to mgr and fires MG_EV_OPEN immediately.
// Useful for integrating pre-opened sockets (e.g. stdin, accept() fd).
struct mg_connection *mg_wrapfd(struct mg_mgr *mgr, int fd,
mg_event_handler_t fn, void *fn_data);
// Sets the 6-bit Differentiated Services Code Point for outgoing datagrams.
// Call on MG_EV_OPEN for UDP listeners, MG_EV_CONNECT for clients, or
// MG_EV_ACCEPT for TCP servers, before sending application data.
bool mg_dscp(struct mg_connection *, uint8_t dscp);
// Called internally after DNS resolution completes to create the OS socket
// and initiate the TCP/IP connect. Normally not called by user code.
void mg_connect_resolved(struct mg_connection *);
// Appends buf/len to c->send. Returns true on success, false on OOM
// (which also fires MG_EV_ERROR and schedules the connection for close).
// Data is sent asynchronously by the next mg_mgr_poll() call.
bool mg_send(struct mg_connection *, const void *, size_t);
// Formats data and appends it to a connection send buffer.
//
// Returns:
// Number of bytes appended, or 0 on OOM.
// Example:
// mg_printf(c, "GET / HTTP/1.0\r\nHost: %.*s\r\n\r\n",
// (int) host.len, host.buf);
// Full examples:
// tutorials/http/http-client, tutorials/http/http-proxy-client,
// tutorials/http/http-restful-server
// Related APIs:
// mg_send(), mg_snprintf(), mg_http_reply(), mg_ws_printf()
// Notes:
// Data is sent asynchronously by a later mg_mgr_poll() call. Supports
// mg_xprintf specifiers, including custom %M/%m printers. See src/fmt.h for
// the full specifier list.
size_t mg_printf(struct mg_connection *, const char *fmt, ...);
size_t mg_vprintf(struct mg_connection *, const char *fmt, va_list *ap);
// Parses an IP address string str into addr. Handles "localhost", plain IPv4
// (e.g. "1.2.3.4"), IPv6 with optional brackets and scope ID, and
// IPv4-mapped IPv6 (::ffff:1.2.3.4). Does NOT parse the port.
// Returns true on success; addr->is_ip6 and addr->addr are set.
bool mg_aton(struct mg_str str, struct mg_addr *addr);
// Low-level functions for integrating with custom network stacks.
// Allocates a new connection struct with mgr->extraconnsize extra bytes.
// Returns NULL on OOM.
struct mg_connection *mg_alloc_conn(struct mg_mgr *);
// Fires MG_EV_CLOSE, frees all buffers (recv/send/rtls), removes c from
// mgr->conns, and calls mg_free(c). Do not use c after this call.
void mg_close_conn(struct mg_connection *c);
// Creates and binds the OS socket for a listening connection c at url.
// Called internally by mg_listen(); expose for custom network stack use.
// Returns false on error.
bool mg_open_listener(struct mg_connection *c, const char *url);
// Thread-safe wakeup: fires MG_EV_WAKEUP on the connection whose id matches,
// with ev_data pointing to an mg_str containing buf/len.
// Requires mg_wakeup_init() to have been called first.
// Returns false if the pipe is not initialised or conn_id is 0.
// Safe to call from any thread or interrupt context.
bool mg_wakeup(struct mg_mgr *, unsigned long id, const void *buf, size_t len);
// Initialises the internal socketpair used by mg_wakeup(). Call once after
// mg_mgr_init() if you intend to wake the event loop from another thread.
// Returns false on failure (e.g. socketpair creation failed).
bool mg_wakeup_init(struct mg_mgr *);
// Deprecated API, do not expose. Use mg_timer_expired instead
struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t milliseconds,
unsigned flags, void (*fn)(void *), void *arg);
// Like mg_connect() but also sets a protocol-level handler pfn/pfn_data.
// Used internally by protocol modules (MQTT, WebSocket, etc.).
struct mg_connection *mg_connect_svc(struct mg_mgr *mgr, const char *url,
mg_event_handler_t fn, void *fn_data,
mg_event_handler_t pfn, void *pfn_data);
// Restores c->rem from a saved address buffer written by the caller, then
// maps the IP back through the builtin TCP/IP stack. Used after a UDP
// multicast send to restore the connection's original remote address.
void mg_multicast_restore(struct mg_connection *c, uint8_t *from);
// A single HTTP header name/value pair.
struct mg_http_header {
struct mg_str name; // Header name, e.g. "Content-Type"
struct mg_str value; // Header value, e.g. "text/html"
};
// Parsed HTTP request or response.
// Passed as ev_data in MG_EV_HTTP_MSG and MG_EV_HTTP_HDRS.
// For requests: method="GET", uri="/path", proto="a=1&b=2", proto="HTTP/1.1"
// For responses: method="HTTP/1.0", uri="200", query="", proto="OK"
struct mg_http_message {
struct mg_str method, uri, query, proto; // Request/response line
struct mg_http_header headers[MG_MAX_HTTP_HEADERS]; // Parsed headers array
struct mg_str body; // Request or response body
struct mg_str head; // Raw bytes: request/status line + headers, no body
struct mg_str message; // Raw bytes: head + body
};
// Options for mg_http_serve_dir() and mg_http_serve_file().
struct mg_http_serve_opts {
const char *root_dir; // Root directory for serving files, e.g. "/web"
const char *ssi_pattern; // Pattern for Server-Side Include files, e.g. "#.shtml". NULL disables SSI
const char *extra_headers; // Extra response headers, e.g. "A: b\r\nC: d\r\n". NULL for none
const char *mime_types; // Additional MIME types: "ext1=type1,ext2=type2". NULL for defaults only
const char *page404; // Path to a custom 404 page, e.g. "/404.html". NULL for built-in
struct mg_fs *fs; // Filesystem to use. NULL defaults to POSIX
bool enable_delete; // Allow to DELETE files
bool enable_upload; // Allow to POST/PUT files
bool enable_dir_listing; // Enable directory listing
};
// A single part of a multipart/form-data body, filled by mg_http_next_multipart().
struct mg_http_part {
struct mg_str name; // Form field name, from Content-Disposition: name="..."
struct mg_str filename; // Original filename for file uploads, empty for plain fields
struct mg_str body; // Part contents, points into the original message buffer
};
// Parses HTTP request or response in buffer s/len into hm.
// Returns number of bytes consumed, 0 if incomplete, -1 on error.
int mg_http_parse(const char *s, size_t len, struct mg_http_message *);
// Returns the byte length of HTTP headers in buf (i.e. offset of the body).
// Returns 0 if headers are incomplete, -1 on error.
int mg_http_get_request_len(const unsigned char *buf, size_t buf_len);
// Sends one chunk in HTTP chunked transfer encoding using printf-style fmt.
// Call with empty fmt ("") to send the terminating zero-length chunk.
void mg_http_printf_chunk(struct mg_connection *cnn, const char *fmt, ...);
// Sends one chunk in HTTP chunked transfer encoding from a raw buffer.
// Call with len=0 to send the terminating zero-length chunk.
void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len);
// Creates an HTTP server on url, e.g. "http://0.0.0.0:8000".
//
// Returns:
// Listening connection, or NULL on error.
// Example:
// mg_http_listen(&mgr, "http://0.0.0.0:8000", fn, NULL);
// Full examples:
// tutorials/http/http-server, tutorials/http/*
// Related APIs:
// mg_http_reply(), mg_http_serve_dir(), mg_match()
// Notes:
// Call mg_mgr_poll() in the main loop. The user-supplied fn event handler
// receives normal connection events. It also receives MG_EV_HTTP_HDRS when
// headers are received and MG_EV_HTTP_MSG when the full request is received.
// ev_data for both HTTP events is struct mg_http_message *.
struct mg_connection *mg_http_listen(struct mg_mgr *, const char *url,
mg_event_handler_t fn, void *fn_data);
// Opens an HTTP client connection to url, e.g. "http://example.org".
//
// Returns:
// Client connection, or NULL on error.
// Example:
// mg_http_connect(&mgr, "http://example.org", fn, NULL);
// Full examples:
// tutorials/http/http-client, tutorials/http/huge-response
// Related APIs:
// mg_printf(), mg_http_status(), mg_http_get_header()
// Notes:
// In the user-supplied fn event handler, send the HTTP request on
// MG_EV_CONNECT. The handler receives normal connection events. It also
// receives MG_EV_HTTP_HDRS when headers are received and MG_EV_HTTP_MSG when
// the full response is received. ev_data for both HTTP events is struct
// mg_http_message *.
struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url,
mg_event_handler_t fn, void *fn_data);
// Serves static files from a directory.
//
// Example:
// struct mg_http_serve_opts o = {.root_dir = "web_root", .fs = &mg_fs_posix};
// mg_http_serve_dir(c, hm, &o);
// Full examples:
// tutorials/http/http-server, tutorials/core/embedded-filesystem
// Related APIs:
// mg_http_listen(), mg_http_serve_file(), mg_http_reply(), mg_match()
// Notes:
// Call from an MG_EV_HTTP_MSG handler. The uri in hm is mapped under
// opts->root_dir. opts->enable_dir_listing enables directory listing; SSI
// uses opts->ssi_pattern when configured. opts->enable_delete enables
// DELETE; opts->enable_upload enables POST/PUT, writing the whole body to
// a file - see mg_http_serve_upload() to also stream big uploads without
// buffering.
void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm,
const struct mg_http_serve_opts *);
// Serves a single file at path. Call from an MG_EV_HTTP_MSG handler.
void mg_http_serve_file(struct mg_connection *, struct mg_http_message *hm,
const char *path, const struct mg_http_serve_opts *);
// Streams a big POST/PUT upload straight to a file, instead of buffering the
// whole body. Optional companion to mg_http_serve_dir(): call this from
// MG_EV_HTTP_HDRS with the same opts, and keep calling mg_http_serve_dir()
// from MG_EV_HTTP_MSG as usual - it still handles uploads this function skips.
// Does nothing unless opts->enable_upload is set, the method is POST or PUT,
// and Content-Length is known and big enough.
void mg_http_serve_upload(struct mg_connection *, struct mg_http_message *hm,
const struct mg_http_serve_opts *);
// Sends a complete HTTP response with Content-Length.
//
// Example:
// mg_http_reply(c, 200, "Content-Type: application/json\r\n",
// "{%m:%d}", MG_ESC("temperature"), 123);
// Full examples:
// tutorials/http/http-server, tutorials/http/http-restful-server,
// tutorials/http/device-dashboard
// Related APIs:
// mg_http_listen(), mg_printf(), mg_http_printf_chunk(), MG_ESC
// Notes:
// headers must end with "\r\n"; pass "" or NULL for no extra headers.
// body_fmt is printf-style and supports %M/%m custom printers. Use MG_ESC
// when printing JSON strings.
void mg_http_reply(struct mg_connection *, int status_code, const char *headers,
const char *body_fmt, ...);
// Looks up a request header by name (case-insensitive).
// Returns a pointer to the mg_str value, or NULL if not found.
struct mg_str *mg_http_get_header(struct mg_http_message *, const char *name);
// Extracts a named variable from a query string or form-encoded body (buf).
// Returns the raw (URL-encoded) value as mg_str, or an empty mg_str if not found.
// Not NUL-terminated. Use mg_url_decode() to get a decoded string.
struct mg_str mg_http_var(struct mg_str buf, struct mg_str name);
// URL-decodes variable name from query string or form body into a NUL-terminated buffer.
// Returns the length of the decoded value, or negative if not found / buffer too small.
int mg_http_get_var(const struct mg_str *, const char *name, char *, size_t);
// URL-decodes s/n into to/to_len. Set form=1 to also decode '+' as space.
// Returns number of bytes written, or -1 if to buffer is too small.
int mg_url_decode(const char *s, size_t n, char *to, size_t to_len, int form);
// URL-encodes s/n into buf/len. Returns number of bytes written.
size_t mg_url_encode(const char *s, size_t n, char *buf, size_t len);
// Extracts HTTP credentials into caller-supplied buffers.
// For Basic auth: fills user (buf1) and password (buf2).
// For Bearer auth: fills buf1 with "" and buf2 with the token.
void mg_http_creds(struct mg_http_message *, char *, size_t, char *, size_t);
// Deprecated API, do not expose
long mg_http_upload(struct mg_connection *c, struct mg_http_message *hm,
struct mg_fs *fs, const char *dir, size_t max_size);
// Sends a 401 Unauthorized response with a Basic Auth WWW-Authenticate challenge.
void mg_http_bauth(struct mg_connection *, const char *user, const char *pass);
// Extracts a named parameter from a header value string s.
// E.g. for s="multipart/form-data; boundary=abc", v="boundary" returns "abc".
struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v);
// Iterates over parts of a multipart/form-data body.
//
// Returns:
// Offset for the next call, or 0 when there are no more parts.
// Example:
// size_t ofs = 0;
// struct mg_http_part part;
// while ((ofs = mg_http_next_multipart(hm->body, ofs, &part)) > 0) { ... }
// Full examples:
// tutorials/http/file-upload-html-form, tutorials/http/http-server
// Related APIs:
// mg_http_listen(), mg_http_stream_body()
// Notes:
// Call from an MG_EV_HTTP_MSG handler after the full request body is
// received. part.name, part.filename, and part.body are zero-copy slices into
// hm->body and are not NUL-terminated.
size_t mg_http_next_multipart(struct mg_str, size_t, struct mg_http_part *);
// Returns the HTTP status code from a parsed response message (e.g. 200, 404).
int mg_http_status(const struct mg_http_message *hm);
// Streams a POST/PUT body to a callback as it arrives, without buffering it.
// Call it from an MG_EV_HTTP_HDRS handler, after matching the request, e.g.
// with mg_match(). Returns true if it took over the connection, false if the
// request is not POST/PUT, is chunked, or has no Content-Length.
//
// Callback upload_fn(hm, data, user_data) is called:
// - at start: hm is set. Open the file, set *user_data.
// - per chunk: data is set, 512 byte aligned except the last. Not called
// again after a failure.
// - at end: both NULL. Always called once, also when the connection drops
// early, so release resources here. A truncated body looks like a complete
// one: validate before committing.
// Return true on success, false on error.
// Example:
// if (ev == MG_EV_HTTP_HDRS && mg_match(hm->uri, mg_str("/fs/*"), NULL) {
// mg_http_stream_body(c, hm, upload, NULL));
// }
// Full example:
// tutorials/http/file-upload-single-post
// Related APIs:
// mg_http_listen(), mg_http_next_multipart()
// Notes:
// After it returns true, this function hijacks the connection. Existing event
// handler not called, hm is invalid, c->data and c->fn_data are modified
bool mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
bool (*upload_fn)(struct mg_http_message *,
struct mg_str *, void **user_data),
void *user_data);
void mg_http_serve_ssi(struct mg_connection *c, const char *root,
const char *fullpath);
// TLS / Security
// Available TLS backend libraries
#define MG_TLS_NONE 0 // No TLS support
#define MG_TLS_MBED 1 // mbedTLS
#define MG_TLS_OPENSSL 2 // OpenSSL
#define MG_TLS_WOLFSSL 5 // WolfSSL (based on OpenSSL)
#define MG_TLS_BUILTIN 3 // Built-in
#define MG_TLS_CUSTOM 4 // Custom implementation
#ifndef MG_TLS
// mongoose_config.h setting. Set MG_TLS to one of the MG_TLS_* values above to
// select a TLS backend. Defaults to MG_TLS_NONE (no TLS).
#define MG_TLS MG_TLS_NONE
#endif
// TLS options structure passed to mg_tls_init(). All cert/key fields accept
// PEM strings or DER binary.
//
// One-way TLS:
// server sets `cert` + `key`,
// client sets `ca` + optionally `name` for hostname verification.
//
// Two-way (mutual) TLS: both sides set `ca` + `cert` + `key`.
//
// - `ca`: CA certificate. Verifies the peer's certificate.
// Set on clients to authenticate the server. Set on servers to require
// and verify a client certificate. If empty, peer is not verified.
// - `cert`: Our certificate. Required on servers. Also set on clients for
// mutual TLS.
// - `key`: Our private key. May equal `cert` when PEM bundles both.
// - `name`: Server name for SNI and hostname verification. Set on clients.
// Empty disables hostname verification.
// - `skip_verification`: Skip certificate and hostname verification.
// Useful during development; do not use in production.
struct mg_tls_opts {
struct mg_str ca; // CA certificate, PEM or DER
struct mg_str cert; // Our certificate, PEM or DER
struct mg_str key; // Our private key, PEM or DER
struct mg_str name; // Server name for SNI + hostname verification
bool skip_verification; // Skip certificate and hostname verification
};
// Initialises TLS on a connection.
//
// Example:
// ```c
// // Server: one-way TLS
// if (ev == MG_EV_ACCEPT) {
// struct mg_tls_opts opts = {.cert = mg_str(s_tls_cert),
// .key = mg_str(s_tls_key)};
// mg_tls_init(c, &opts);
// }
//
// // Client: verify server certificate and hostname
// if (ev == MG_EV_CONNECT) {
// struct mg_tls_opts opts = {.ca = mg_str(s_tls_ca),
// .name = mg_str("hostname")};
// mg_tls_init(c, &opts);
// }
// ```
// Full examples:
// tutorials/http/http-server, tutorials/http/http-client,
// tutorials/mqtt/mqtt-client-aws-iot, tutorials/websocket/websocket-client
// Related APIs:
// mg_http_listen(), mg_http_connect(), mg_ws_connect(), mg_mqtt_connect()
// Notes:
// Call from the user-supplied event handler on MG_EV_ACCEPT for servers or
// MG_EV_CONNECT for clients, before application data is sent. Servers usually
// set cert and key. Clients usually set ca and name; name enables SNI and
// hostname verification.
void mg_tls_init(struct mg_connection *c, const struct mg_tls_opts *opts);
// Private API, do not expose
void mg_tls_free(struct mg_connection *);
long mg_tls_send(struct mg_connection *, const void *buf, size_t len);
long mg_tls_recv(struct mg_connection *, void *buf, size_t len);
size_t mg_tls_pending(struct mg_connection *);
long mg_tls_flush(struct mg_connection *);
void mg_tls_handshake(struct mg_connection *);
void mg_tls_ctx_init(struct mg_mgr *);
void mg_tls_ctx_free(struct mg_mgr *);
#define MG_IS_DER(buf) (((uint8_t *) (buf))[0] == 0x30) // DER begins with 0x30
#if MG_TLS == MG_TLS_BUILTIN
// Extracts a 32-byte P-256 private scalar from EC PRIVATE KEY or PKCS#8 PRIVATE
// KEY input. Accepts PEM or DER. Returns key size, or 0 on error.
size_t mg_p256_parse_private_key(struct mg_str key, uint8_t *buf, size_t len);
#endif
// Low-level IO primitives used by TLS layer
enum { MG_IO_ERR = -1, MG_IO_WAIT = -2 };
long mg_io_send(struct mg_connection *c, const void *buf, size_t len);
long mg_io_recv(struct mg_connection *c, void *buf, size_t len);
/******************************************************************************
*
* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
*
* This is a simple and straightforward implementation of AES-GCM authenticated
* encryption. The focus of this work was correctness & accuracy. It is written
* in straight 'C' without any particular focus upon optimization or speed. It
* should be endian (memory byte order) neutral since the few places that care
* are handled explicitly.
*
* This implementation of AES-GCM was created by Steven M. Gibson of GRC.com.
*
* It is intended for general purpose use, but was written in support of GRC's
* reference implementation of the SQRL (Secure Quick Reliable Login) client.
*
* See: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
* http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/ \
* gcm/gcm-revised-spec.pdf
*
* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
*
*******************************************************************************/
#ifndef TLS_AES128_H
#define TLS_AES128_H
/******************************************************************************
* AES_CONTEXT : cipher context / holds inter-call data
******************************************************************************/
typedef struct {
int mode; // 1 for Encryption, 0 for Decryption
int rounds; // keysize-based rounds count
uint32_t *rk; // pointer to current round key
uint32_t buf[68]; // key expansion buffer
} aes_context;
#define GCM_AUTH_FAILURE 0x55555555 // authentication failure
/******************************************************************************
* GCM_CONTEXT : MUST be called once before ANY use of this library
******************************************************************************/
int mg_gcm_initialize(void);
//
// aes-gcm.h
// MKo
//
// Created by Markus Kosmal on 20/11/14.
//
//
int mg_aes_gcm_encrypt(unsigned char *output, const unsigned char *input,
size_t input_length, const unsigned char *key,
const size_t key_len, const unsigned char *iv,
const size_t iv_len, unsigned char *aead,
size_t aead_len, unsigned char *tag,
const size_t tag_len);
int mg_aes_gcm_decrypt(unsigned char *output, const unsigned char *input,
size_t input_length, const unsigned char *key,
const size_t key_len, const unsigned char *iv,
const size_t iv_len, unsigned char *aead,
size_t aead_len, const unsigned char *tag,
const size_t tag_len);
#endif /* TLS_AES128_H */
// End of aes128 PD
// P-256 API, mg_p256_*(), used by built-in TLS, JWT and OTA. Implemented by
// BearSSL code below (MG_P256=MG_P256_BEAR) or tls_p256_cortex.c (LENNGREN)
// BearSSL code: https://bearssl.org commit 7bea48e. Shared bignum and ECDSA
// code is always compiled, it is also used by P-384, see tls_p384.c
//
// Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
// Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Mongoose changes: br_ / BR_ names are prefixed with mg_ / MG_, as are the
// constant-time helpers from inner.h (NOT, MUX, EQ, ...). Structs are not
// typedef-ed. ECDSA signing takes the caller's k instead of HMAC-DRBG, k is
// computed by mg_p256_sign() (RFC 6979). ECDSA functions take the curve id and
// public key directly
#if MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY)
#define MG_BR_EC_secp256r1 23 // Curve IDs, same as TLS named groups
#define MG_BR_EC_secp384r1 24
#define MG_BR_MAX_EC_SIZE 384 // Largest supported curve, bits
// From BearSSL inc/bearssl_ec.h: an EC implementation
struct mg_br_ec_impl {
uint32_t supported_curves; // Bit mask of curve IDs
const unsigned char *(*generator)(int curve, size_t *len);
const unsigned char *(*order)(int curve, size_t *len);
size_t (*xoff)(int curve, size_t *len);
uint32_t (*mul)(unsigned char *G, size_t Glen, const unsigned char *x,
size_t xlen, int curve);
size_t (*mulgen)(unsigned char *R, const unsigned char *x, size_t xlen,
int curve);
uint32_t (*muladd)(unsigned char *A, const unsigned char *B, size_t len,
const unsigned char *x, size_t xlen,
const unsigned char *y, size_t ylen, int curve);
};
// From BearSSL src/inner.h: constant-time helpers
static inline uint32_t MG_BR_NOT(uint32_t ctl) {
return ctl ^ 1;
}
static inline uint32_t MG_BR_MUX(uint32_t ctl, uint32_t x, uint32_t y) {
return y ^ (-ctl & (x ^ y));
}
static inline uint32_t MG_BR_EQ(uint32_t x, uint32_t y) {
uint32_t q = x ^ y;
return MG_BR_NOT((q | -q) >> 31);
}
static inline uint32_t MG_BR_NEQ(uint32_t x, uint32_t y) {
uint32_t q = x ^ y;
return (q | -q) >> 31;
}
static inline uint32_t MG_BR_GT(uint32_t x, uint32_t y) {
uint32_t z = y - x;
return (z ^ ((x ^ y) & (x ^ z))) >> 31;
}
#define MG_BR_GE(x, y) MG_BR_NOT(MG_BR_GT(y, x))
#define MG_BR_LT(x, y) MG_BR_GT(y, x)
static inline int32_t MG_BR_CMP(uint32_t x, uint32_t y) {
return (int32_t) MG_BR_GT(x, y) | -(int32_t) MG_BR_GT(y, x);
}
static inline uint32_t MG_BR_BIT_LENGTH(uint32_t x) {
uint32_t k, c;
k = MG_BR_NEQ(x, 0);
c = MG_BR_GT(x, 0xFFFF), x = MG_BR_MUX(c, x >> 16, x), k += c << 4;
c = MG_BR_GT(x, 0x00FF), x = MG_BR_MUX(c, x >> 8, x), k += c << 3;
c = MG_BR_GT(x, 0x000F), x = MG_BR_MUX(c, x >> 4, x), k += c << 2;
c = MG_BR_GT(x, 0x0003), x = MG_BR_MUX(c, x >> 2, x), k += c << 1;
k += MG_BR_GT(x, 0x0001);
return k;
}
#define MG_BR_MUL31(x, y) ((uint64_t) (x) * (uint64_t) (y))
#define MG_BR_MUL31_lo(x, y) \
(((uint32_t) (x) * (uint32_t) (y)) & (uint32_t) 0x7FFFFFFF)
#define MG_BR_CCOPY mg_br_ccopy
void mg_br_ccopy(uint32_t ctl, void *dst, const void *src, size_t len);
uint32_t mg_br_divrem(uint32_t hi, uint32_t lo, uint32_t d, uint32_t *r);
static inline uint32_t mg_br_rem(uint32_t hi, uint32_t lo, uint32_t d) {
uint32_t r;
mg_br_divrem(hi, lo, d, &r);
return r;
}
static inline uint32_t mg_br_div(uint32_t hi, uint32_t lo, uint32_t d) {
uint32_t r;
return mg_br_divrem(hi, lo, d, &r);
}
static inline void mg_br_enc32be(void *dst, uint32_t x) {
unsigned char *buf = (unsigned char *) dst;
buf[0] = (unsigned char) (x >> 24), buf[1] = (unsigned char) (x >> 16);
buf[2] = (unsigned char) (x >> 8), buf[3] = (unsigned char) x;
}
static inline void mg_br_i31_zero(uint32_t *x, uint32_t bit_len) {
*x++ = bit_len;
memset(x, 0, ((bit_len + 31) >> 5) * sizeof *x);
}
// Big integers, "i31" format, see BearSSL src/inner.h
uint32_t mg_br_i31_add(uint32_t *a, const uint32_t *b, uint32_t ctl);
uint32_t mg_br_i31_sub(uint32_t *a, const uint32_t *b, uint32_t ctl);
uint32_t mg_br_i31_bit_length(uint32_t *x, size_t xlen);
uint32_t mg_br_i31_decode_mod(uint32_t *x, const void *src, size_t len,
const uint32_t *m);
void mg_br_i31_decode(uint32_t *x, const void *src, size_t len);
void mg_br_i31_encode(void *dst, size_t len, const uint32_t *x);
void mg_br_i31_from_monty(uint32_t *x, const uint32_t *m, uint32_t m0i);
void mg_br_i31_to_monty(uint32_t *x, const uint32_t *m);
uint32_t mg_br_i31_iszero(const uint32_t *x);
void mg_br_i31_modpow(uint32_t *x, const unsigned char *e, size_t elen,
const uint32_t *m, uint32_t m0i, uint32_t *t1,
uint32_t *t2);
void mg_br_i31_montymul(uint32_t *d, const uint32_t *x, const uint32_t *y,
const uint32_t *m, uint32_t m0i);
void mg_br_i31_muladd_small(uint32_t *x, uint32_t z, const uint32_t *m);
uint32_t mg_br_i31_ninv31(uint32_t x);
void mg_br_i31_rshift(uint32_t *x, int count);
// ECDSA, see BearSSL src/ec/ecdsa_i31_*.c. q is an uncompressed point
void mg_br_ecdsa_i31_bits2int(uint32_t *x, const void *src, size_t len,
uint32_t ebitlen);
uint32_t mg_br_ecdsa_i31_vrfy_raw(const struct mg_br_ec_impl *impl, int curve,
const void *hash, size_t hash_len,
const unsigned char *q, size_t qlen,
const void *sig, size_t sig_len);
size_t mg_br_ecdsa_i31_sign_raw(const struct mg_br_ec_impl *impl, int curve,
const void *hash, size_t hash_len,
const unsigned char *x, size_t xlen,
const unsigned char *k, void *sig);
// P-256 (secp256r1) API. Keys and signatures are big endian byte strings,
// a public key is X || Y, a signature is r || s. Functions return 1 on
// success, 0 on failure
// Generate a random key pair, using mg_random()
int mg_p256_make_key(uint8_t public_key[64], uint8_t private_key[32]);
// Compute public key from a private key. Return 0 if private key is invalid
int mg_p256_compute_public_key(const uint8_t private_key[32],
uint8_t public_key[64]);
// ECDH. Validates the peer public key, returns 0 if it is not on the curve
int mg_p256_shared_secret(const uint8_t public_key[64],
const uint8_t private_key[32], uint8_t secret[32]);
// ECDSA sign with a deterministic k, RFC 6979 with HMAC-SHA256
int mg_p256_sign(const uint8_t private_key[32], const uint8_t *hash,
size_t hash_size, uint8_t signature[64]);
// ECDSA verify. Validates the public key
int mg_p256_verify(const uint8_t public_key[64], const uint8_t *hash,
size_t hash_size, const uint8_t signature[64]);
// Internal: ECDSA sign with a given k, 32 bytes big endian, in [1, n-1].
// Provided by the selected implementation, used by mg_p256_sign()
int mg_p256_sign_with_k(const uint8_t private_key[32], const uint8_t *hash,
size_t hash_size, const uint8_t k[32],
uint8_t signature[64]);
#endif // MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY)
// P-384 from BearSSL, https://bearssl.org commit 7bea48e
// P-384 signature verification, used by built-in TLS for certificates and
// CertificateVerify. Shared bignum and ECDSA code is in tls_p256.c
//
// Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Mongoose changes: br_ / BR_ names are prefixed with mg_ / MG_, static
// functions with p384_. Trimmed to P-384. Structs are not typedef-ed
#if MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY)
// Verify ECDSA P-384 signature r || s over hash. Public key is X || Y.
// Return 1 on success, 0 on failure
int mg_p384_verify(const uint8_t public_key[96], const uint8_t *hash,
size_t hash_size, const uint8_t signature[96]);
#endif // MG_TLS == MG_TLS_BUILTIN || defined(MG_OTA_PUBLIC_KEY)
// portable8439 v1.0.1
// Source: https://github.com/DavyLandman/portable8439
// Licensed under CC0-1.0
// Contains poly1305-donna e6ad6e091d30d7f4ec2d4f978be1fcfcbce72781 (Public
// Domain)
#if MG_TLS == MG_TLS_BUILTIN && MG_ENABLE_CHACHA20
#ifndef __PORTABLE_8439_H
#define __PORTABLE_8439_H
#if defined(__cplusplus)
extern "C" {
#endif
// provide your own decl specificier like -DPORTABLE_8439_DECL=ICACHE_RAM_ATTR
#ifndef PORTABLE_8439_DECL
#define PORTABLE_8439_DECL
#endif
/*
This library implements RFC 8439 a.k.a. ChaCha20-Poly1305 AEAD
You can use this library to avoid attackers mutating or reusing your
encrypted messages. This does assume you never reuse a nonce+key pair and,
if possible, carefully pick your associated data.
*/
/* Make sure we are either nested in C++ or running in a C99+ compiler
#if !defined(__cplusplus) && !defined(_MSC_VER) && \
(!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L)
#error "C99 or newer required"
#endif */
// #if CHAR_BIT > 8
// # error "Systems without native octals not suppoted"
// #endif
#if defined(_MSC_VER) || defined(__cplusplus)
// add restrict support is possible
#if (defined(_MSC_VER) && _MSC_VER >= 1900) || defined(__clang__) || \
defined(__GNUC__)
#define restrict __restrict
#else
#define restrict
#endif
#endif
#define RFC_8439_TAG_SIZE (16)
#define RFC_8439_KEY_SIZE (32)
#define RFC_8439_NONCE_SIZE (12)
/*
Encrypt/Seal plain text bytes into a cipher text that can only be
decrypted by knowing the key, nonce and associated data.
input:
- key: RFC_8439_KEY_SIZE bytes that all parties have agreed
upon beforehand
- nonce: RFC_8439_NONCE_SIZE bytes that should never be repeated
for the same key. A counter or a pseudo-random value are fine.
- ad: associated data to include with calculating the tag of the
cipher text. Can be null for empty.
- plain_text: data to be encrypted, pointer + size should not overlap
with cipher_text pointer
output:
- cipher_text: encrypted plain_text with a tag appended. Make sure to
allocate at least plain_text_size + RFC_8439_TAG_SIZE
returns:
- size of bytes written to cipher_text, can be -1 if overlapping
pointers are passed for plain_text and cipher_text
*/
PORTABLE_8439_DECL size_t mg_chacha20_poly1305_encrypt(
uint8_t *restrict cipher_text, const uint8_t key[RFC_8439_KEY_SIZE],
const uint8_t nonce[RFC_8439_NONCE_SIZE], const uint8_t *restrict ad,
size_t ad_size, const uint8_t *restrict plain_text, size_t plain_text_size);
/*
Decrypt/unseal cipher text given the right key, nonce, and additional data.
input:
- key: RFC_8439_KEY_SIZE bytes that all parties have agreed
upon beforehand
- nonce: RFC_8439_NONCE_SIZE bytes that should never be repeated for
the same key. A counter or a pseudo-random value are fine.
- ad: associated data to include with calculating the tag of the
cipher text. Can be null for empty.
- cipher_text: encrypted message.
output:
- plain_text: data to be encrypted, pointer + size should not overlap
with cipher_text pointer, leave at least enough room for
cipher_text_size - RFC_8439_TAG_SIZE
returns:
- size of bytes written to plain_text, -1 signals either:
- incorrect key/nonce/ad
- corrupted cipher_text
- overlapping pointers are passed for plain_text and cipher_text
*/
PORTABLE_8439_DECL size_t mg_chacha20_poly1305_decrypt(
uint8_t *restrict plain_text, const uint8_t key[RFC_8439_KEY_SIZE],
const uint8_t nonce[RFC_8439_NONCE_SIZE],
const uint8_t *restrict ad, size_t ad_size,
const uint8_t *restrict cipher_text, size_t cipher_text_size);
#if defined(__cplusplus)
}
#endif
#endif
#endif
#ifndef TLS_RSA_H
#define TLS_RSA_H
int mg_rsa_mod_pow(const uint8_t *mod, size_t modsz, const uint8_t *exp, size_t expsz, const uint8_t *msg, size_t msgsz, uint8_t *out, size_t outsz);
int mg_rsa_crt_sign(const uint8_t *em, size_t em_len,
const uint8_t *dP, size_t dP_len,
const uint8_t *dQ, size_t dQ_len,
const uint8_t *p, size_t p_len,
const uint8_t *q, size_t q_len,
const uint8_t *qInv, size_t qInv_len,
uint8_t *signature, size_t sig_len);
bool mg_rsa_pkcs_verify(const uint8_t *em, size_t nlen, const uint8_t *hash,
size_t hashlen);
bool mg_rsa_verify(const uint8_t *em, size_t nlen, const uint8_t *mhash);
#endif // TLS_RSA_H
#if MG_TLS == MG_TLS_MBED
#include <mbedtls/debug.h>
#include <mbedtls/error.h>
#include <mbedtls/net_sockets.h>
#include <mbedtls/ssl.h>
#include <mbedtls/ssl_ticket.h>
struct mg_tls_ctx {
int dummy;
#ifdef MBEDTLS_SSL_SESSION_TICKETS
mbedtls_ssl_ticket_context tickets;
#endif
};
struct mg_tls {
mbedtls_x509_crt ca; // Parsed CA certificate
mbedtls_x509_crt cert; // Parsed certificate
mbedtls_pk_context pk; // Private key context
mbedtls_ssl_context ssl; // SSL/TLS context
mbedtls_ssl_config conf; // SSL/TLS config
#ifdef MBEDTLS_SSL_SESSION_TICKETS
mbedtls_ssl_ticket_context ticket; // Session tickets context
#endif
// https://github.com/Mbed-TLS/mbedtls/blob/3b3c652d/include/mbedtls/ssl.h#L5071C18-L5076C29
unsigned char *throttled_buf; // see #3074
size_t throttled_len;
bool check_name; // set when hostname was set, but no CA certificate given
};
#endif
#if MG_TLS == MG_TLS_OPENSSL || MG_TLS == MG_TLS_WOLFSSL
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
struct mg_tls {
BIO_METHOD *bm;
SSL_CTX *ctx;
SSL *ssl;
char *name; // matching hostname
bool check_name; // set when hostname was set, but no CA certificate given
};
#endif
// WebSocket frame opcodes, used in mg_ws_send() op parameter and
// in the low 4 bits of mg_ws_message.flags.
#define WEBSOCKET_OP_CONTINUE 0 // Continuation frame (fragmented message)
#define WEBSOCKET_OP_TEXT 1 // Text (UTF-8) data frame
#define WEBSOCKET_OP_BINARY 2 // Binary data frame
#define WEBSOCKET_OP_CLOSE 8 // Connection close control frame
#define WEBSOCKET_OP_PING 9 // Ping control frame (auto-replied with PONG)
#define WEBSOCKET_OP_PONG 10 // Pong control frame
// WebSocket message delivered as ev_data in MG_EV_WS_MSG and MG_EV_WS_CTL.
// data is a zero-copy slice into the receive buffer; valid until the next
// mg_mgr_poll() call.
struct mg_ws_message {
struct mg_str data; // Message payload
uint8_t flags; // First byte of the WS frame header: FIN (bit7) | opcode (bits3:0)
};
// Opens an outbound WebSocket connection.
//
// Returns:
// Client connection, or NULL on error.
// Example:
// mg_ws_connect(&mgr, "ws://localhost:8000/websocket", fn, NULL, NULL);
// Full examples:
// tutorials/websocket/websocket-client, tutorials/mqtt/mqtt-over-ws-client
// Related APIs:
// mg_ws_send(), mg_ws_printf(), mg_tls_init(), mg_ws_upgrade()
// Notes:
// url may use ws:// or wss://. Sends the HTTP Upgrade request immediately.
// fmt is a printf-style string for extra HTTP request headers; each header
// must end with "\r\n". The user-supplied fn event handler receives
// MG_EV_WS_OPEN on handshake success, MG_EV_WS_MSG for messages, and
// MG_EV_WS_CTL for control frames.
struct mg_connection *mg_ws_connect(struct mg_mgr *, const char *url,
mg_event_handler_t fn, void *fn_data,
const char *fmt, ...);
// Upgrades a server-side HTTP connection to WebSocket.
//
// Example:
// if (mg_match(hm->uri, mg_str("/websocket"), NULL)) {
// mg_ws_upgrade(c, hm, NULL);
// }
// Full examples:
// tutorials/websocket/websocket-server,
// tutorials/websocket/json-rpc-over-websocket, tutorials/core/timers
// Related APIs:
// mg_http_listen(), mg_match(), mg_ws_send(), mg_ws_printf()
// Notes:
// Call from an MG_EV_HTTP_MSG handler and pass that event's hm. fmt is a
// printf-style string for extra response headers; each header must end with
// "\r\n". Fires MG_EV_WS_OPEN immediately on success. Sends HTTP 426 and
// drains the connection if the request lacks Sec-WebSocket-Key.
void mg_ws_upgrade(struct mg_connection *, struct mg_http_message *,
const char *fmt, ...);
// Sends one WebSocket frame.
//
// Returns:
// Number of bytes appended to c->send; 0 if the frame header cannot be
// appended.
// Example:
// mg_ws_send(c, "hello", 5, WEBSOCKET_OP_TEXT);
// Full examples:
// tutorials/websocket/websocket-server, tutorials/websocket/websocket-client,
// tutorials/core/timers
// Related APIs:
// mg_ws_connect(), mg_ws_upgrade(), mg_ws_printf(), mg_ws_wrap()
// Notes:
// op is one of WEBSOCKET_OP_*. Client connections are automatically masked
// per RFC 6455. On OOM, the return value can be smaller than header + len.
// Data is appended to c->send and sent by a later mg_mgr_poll() call.
size_t mg_ws_send(struct mg_connection *, const void *buf, size_t len, int op);
// Wraps the last len bytes already in c->send with a WebSocket frame header
// and opcode op. Used internally by mg_ws_printf(); call it directly when you
// have written data into c->send manually and need to frame it.
// Returns c->send.len (total buffer size after the operation).
size_t mg_ws_wrap(struct mg_connection *, size_t len, int op);
// Formats a WebSocket message using printf-style fmt and sends it with
// opcode op (WEBSOCKET_OP_TEXT or WEBSOCKET_OP_BINARY).
// Returns the number of payload bytes written, or 0 on OOM.
size_t mg_ws_printf(struct mg_connection *c, int op, const char *fmt, ...);
size_t mg_ws_vprintf(struct mg_connection *c, int op, const char *fmt,
va_list *);
// Time / SNTP functions
// Return milliseconds since boot (uptime). Never goes backwards.
// On RTOS targets (FreeRTOS, ThreadX, Zephyr, etc.) uses the RTOS tick count.
// On STM32/Cube uses HAL_GetTick(). On Arduino uses millis().
// On Linux/macOS uses CLOCK_MONOTONIC_RAW or CLOCK_MONOTONIC; falls back to
// CLOCK_REALTIME if neither is available.
uint64_t mg_millis(void);
// Boot timestamp in ms since Epoch. 0 until the first successful SNTP sync;
// updated automatically by mg_sntp_connect() on each successful response.
extern uint64_t mg_boot_timestamp_ms;
// Return milliseconds since Epoch: mg_millis() + mg_boot_timestamp_ms.
// Until a successful SNTP request completes, this is identical to mg_millis().
uint64_t mg_now(void);
// Checks whether a periodic deadline has expired.
//
// Returns:
// True when the timer has fired; false otherwise.
// Example:
// uint64_t timer = 0;
// if (mg_timer_expired(&timer, 1000, mg_millis())) {
// MG_INFO(("every second"));
// }
// Full examples:
// tutorials/mqtt/mqtt-client
// Related APIs:
// mg_millis(), mg_now()
// Notes:
// Initialise *expiration to 0 before first use. On expiry, this function
// advances *expiration by period and handles time wrap-around.
bool mg_timer_expired(uint64_t *expiration, uint64_t period, uint64_t now);
// Connect to an SNTP server and send a time request.
// `url` defaults to "udp://time.google.com:123" when NULL.
// On success the internal boot timestamp is updated so that mg_now() returns
// the correct wall-clock time, and MG_EV_SNTP_TIME is fired on `fn` (if not
// NULL) with ev_data pointing to a uint64_t containing the epoch in milliseconds.
// Pass fn == NULL and fn_data == NULL for a fire-and-forget sync that only
// updates mg_boot_timestamp_ms. Typical polling usage:
//
// ```c
// uint64_t timer = 0;
// for (;;) {
// uint64_t period = mg_boot_timestamp_ms == 0 ? 1000 : 3600 * 1000;
// if (mg_timer_expired(&timer, period, mg_millis())) {
// mg_sntp_connect(&mgr, NULL, NULL, NULL);
// }
// mg_mgr_poll(&mgr, 1000);
// }
// ```
struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url,
mg_event_handler_t fn, void *fn_data);
// Private API, do not expose
void mg_sntp_request(struct mg_connection *c);
int64_t mg_sntp_parse(const unsigned char *buf, size_t len);
// MQTT packet type codes. The received command is in mg_mqtt_message.cmd.
#define MQTT_CMD_CONNECT 1
#define MQTT_CMD_CONNACK 2
#define MQTT_CMD_PUBLISH 3
#define MQTT_CMD_PUBACK 4
#define MQTT_CMD_PUBREC 5
#define MQTT_CMD_PUBREL 6
#define MQTT_CMD_PUBCOMP 7
#define MQTT_CMD_SUBSCRIBE 8
#define MQTT_CMD_SUBACK 9
#define MQTT_CMD_UNSUBSCRIBE 10
#define MQTT_CMD_UNSUBACK 11
#define MQTT_CMD_PINGREQ 12
#define MQTT_CMD_PINGRESP 13
#define MQTT_CMD_DISCONNECT 14
#define MQTT_CMD_AUTH 15
// MQTT5 property IDs. Used in mg_mqtt_prop.id.
#define MQTT_PROP_PAYLOAD_FORMAT_INDICATOR 0x01
#define MQTT_PROP_MESSAGE_EXPIRY_INTERVAL 0x02
#define MQTT_PROP_CONTENT_TYPE 0x03
#define MQTT_PROP_RESPONSE_TOPIC 0x08
#define MQTT_PROP_CORRELATION_DATA 0x09
#define MQTT_PROP_SUBSCRIPTION_IDENTIFIER 0x0B
#define MQTT_PROP_SESSION_EXPIRY_INTERVAL 0x11
#define MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER 0x12
#define MQTT_PROP_SERVER_KEEP_ALIVE 0x13
#define MQTT_PROP_AUTHENTICATION_METHOD 0x15
#define MQTT_PROP_AUTHENTICATION_DATA 0x16
#define MQTT_PROP_REQUEST_PROBLEM_INFORMATION 0x17
#define MQTT_PROP_WILL_DELAY_INTERVAL 0x18
#define MQTT_PROP_REQUEST_RESPONSE_INFORMATION 0x19
#define MQTT_PROP_RESPONSE_INFORMATION 0x1A
#define MQTT_PROP_SERVER_REFERENCE 0x1C
#define MQTT_PROP_REASON_STRING 0x1F
#define MQTT_PROP_RECEIVE_MAXIMUM 0x21
#define MQTT_PROP_TOPIC_ALIAS_MAXIMUM 0x22
#define MQTT_PROP_TOPIC_ALIAS 0x23
#define MQTT_PROP_MAXIMUM_QOS 0x24
#define MQTT_PROP_RETAIN_AVAILABLE 0x25
#define MQTT_PROP_USER_PROPERTY 0x26
#define MQTT_PROP_MAXIMUM_PACKET_SIZE 0x27
#define MQTT_PROP_WILDCARD_SUBSCRIPTION_AVAILABLE 0x28
#define MQTT_PROP_SUBSCRIPTION_IDENTIFIER_AVAILABLE 0x29
#define MQTT_PROP_SHARED_SUBSCRIPTION_AVAILABLE 0x2A
// Property value types used internally to encode/decode MQTT5 properties.
enum {
MQTT_PROP_TYPE_BYTE,
MQTT_PROP_TYPE_STRING,
MQTT_PROP_TYPE_STRING_PAIR,
MQTT_PROP_TYPE_BINARY_DATA,
MQTT_PROP_TYPE_VARIABLE_INT,
MQTT_PROP_TYPE_INT,
MQTT_PROP_TYPE_SHORT
};
// Return codes from mg_mqtt_parse().
enum { MQTT_OK, MQTT_INCOMPLETE, MQTT_MALFORMED };
// A single MQTT5 property. Use in mg_mqtt_opts.props / mg_mqtt_opts.will_props
// arrays, and iterate received properties with mg_mqtt_next_prop().
struct mg_mqtt_prop {
uint8_t id; // Property ID, one of MQTT_PROP_*
uint32_t iv; // Integer value for BYTE, SHORT, INT, VARIABLE_INT types
struct mg_str key; // Property key string; only set for USER_PROPERTY pairs
struct mg_str val; // String/binary value; set for STRING, BINARY_DATA, and USER_PROPERTY
};
// Options passed to mg_mqtt_connect(), mg_mqtt_pub(), mg_mqtt_sub(),
// mg_mqtt_unsub(), and mg_mqtt_disconnect(). Zero-initialise and set only
// the fields relevant to the operation being called.
struct mg_mqtt_opts {
struct mg_str user; // CONNECT: username; empty = omit
struct mg_str pass; // CONNECT: password; empty = omit
struct mg_str client_id; // CONNECT: client ID; empty = auto-generate
struct mg_str topic; // CONNECT: will topic; PUBLISH/SUBSCRIBE/UNSUBSCRIBE: topic
struct mg_str message; // CONNECT: will payload; PUBLISH: message payload
uint8_t qos; // QoS level: 0, 1, or 2
uint8_t version; // MQTT version: 4 (3.1.1) or 5; 0 defaults to 4
uint16_t keepalive; // CONNECT: keepalive interval in seconds; 0 = no timeout
uint16_t retransmit_id; // PUBLISH: packet ID for retransmit; init to 0 for new message
bool retain; // PUBLISH/CONNECT will: set the retain flag
bool clean; // CONNECT: start a clean session
struct mg_mqtt_prop *props; // MQTT5: properties array; NULL if none
size_t num_props; // MQTT5: number of entries in props
struct mg_mqtt_prop *will_props; // MQTT5 CONNECT: will properties; NULL if none
size_t num_will_props; // MQTT5: number of entries in will_props
};
// Received MQTT message. Passed as ev_data for MG_EV_MQTT_MSG (PUBLISH),
// MG_EV_MQTT_CMD (all packets), and MG_EV_MQTT_OPEN (CONNACK).
// topic, data, and dgram are zero-copy slices into the receive buffer; they
// are only valid until the next mg_mgr_poll() call.
struct mg_mqtt_message {
struct mg_str topic; // PUBLISH: message topic; slice into dgram
struct mg_str data; // PUBLISH: message payload; slice into dgram
struct mg_str dgram; // Complete raw MQTT packet, including fixed header
uint16_t id; // Packet ID; set for QoS>0 PUBLISH, PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, UNSUBACK
uint8_t cmd; // Packet type, one of MQTT_CMD_*
uint8_t qos; // QoS level extracted from the PUBLISH fixed header
uint8_t ack; // CONNACK return code; 0 = success
size_t props_start; // MQTT5: byte offset from dgram.buf to start of properties
size_t props_size; // MQTT5: byte length of the properties section
};
// Opens an MQTT client connection and sends a CONNECT packet.
//
// Returns:
// Client connection, or NULL on error.
// Example:
// struct mg_mqtt_opts opts = {.client_id = mg_str("device1")};
// mg_mqtt_connect(&mgr, "mqtt://broker:1883", &opts, fn, NULL);
// Full examples:
// tutorials/mqtt/mqtt-client, tutorials/mqtt/mqtt-client-aws-iot,
// tutorials/mqtt/ota-over-mqtt
// Related APIs:
// mg_mqtt_pub(), mg_mqtt_sub(), mg_tls_init(), mg_mgr_poll()
// Notes:
// opts may be NULL to use MQTT 3.1.1 defaults with no auth and an auto client
// ID. The user-supplied fn event handler receives normal connection events,
// MG_EV_MQTT_OPEN when CONNACK is received, MG_EV_MQTT_MSG for incoming
// PUBLISH packets, and MG_EV_MQTT_CMD for every received MQTT packet.
struct mg_connection *mg_mqtt_connect(struct mg_mgr *, const char *url,
const struct mg_mqtt_opts *opts,
mg_event_handler_t fn, void *fn_data);
// Creates an MQTT server listener on url.
// Fires MG_EV_MQTT_CMD for each complete packet received (ev_data: struct mg_mqtt_message *).
// Returns NULL on error.
struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url,
mg_event_handler_t fn, void *fn_data);
// Sends a CONNECT packet on an already-open connection.
// Called automatically by mg_mqtt_connect(); exposed for advanced use such
// as re-authenticating on an existing connection.
void mg_mqtt_login(struct mg_connection *c, const struct mg_mqtt_opts *opts);
// Sends an MQTT PUBLISH packet.
//
// Returns:
// 0 for QoS 0; assigned packet ID for QoS 1 or 2.
// Example:
// struct mg_mqtt_opts opts = {
// .topic = mg_str("device/status"),
// .message = mg_str("{\"ok\":true}"),
// .qos = 1,
// };
// uint16_t id = mg_mqtt_pub(c, &opts);
// Full examples:
// tutorials/mqtt/mqtt-client, tutorials/mqtt/mqtt-client-aws-iot,
// tutorials/mqtt/ota-over-mqtt
// Related APIs:
// mg_mqtt_connect(), mg_mqtt_sub(), mg_mqtt_send_header()
// Notes:
// On success, opts.topic and opts.message are copied into c->send before the
// function returns. To retransmit a QoS message, set opts.retransmit_id to the
// packet ID returned by the previous call; use 0 for a new message.
uint16_t mg_mqtt_pub(struct mg_connection *c, const struct mg_mqtt_opts *opts);
// Sends an MQTT SUBSCRIBE packet.
//
// Example:
// struct mg_mqtt_opts opts = {.topic = mg_str("device/rx"), .qos = 1};
// mg_mqtt_sub(c, &opts);
// Full examples:
// tutorials/mqtt/mqtt-client, tutorials/mqtt/mqtt-client-aws-iot,
// tutorials/mqtt/ota-over-mqtt
// Related APIs:
// mg_mqtt_connect(), mg_mqtt_pub(), mg_mqtt_unsub()
// Notes:
// Send after MG_EV_MQTT_OPEN reports a successful CONNACK. Incoming PUBLISH
// messages are delivered to the user-supplied fn event handler as
// MG_EV_MQTT_MSG with ev_data pointing to struct mg_mqtt_message.
void mg_mqtt_sub(struct mg_connection *, const struct mg_mqtt_opts *opts);
// Sends an UNSUBSCRIBE packet for opts.topic.
void mg_mqtt_unsub(struct mg_connection *c, const struct mg_mqtt_opts *opts);
// Parses a raw MQTT packet in buf/len. version must be 4 (MQTT 3.1.1) or 5.
// On success, fills *m; topic, data, and dgram slice into buf (zero-copy).
// Returns MQTT_OK, MQTT_INCOMPLETE (need more data), or MQTT_MALFORMED.
int mg_mqtt_parse(const uint8_t *, size_t, uint8_t, struct mg_mqtt_message *);
// Low-level helper: sends the MQTT fixed header (command byte and
// variable-length remaining-length field). Call before writing the PDU body.
// cmd: MQTT_CMD_*; flags: low 4 bits of the first byte; len: remaining length.
void mg_mqtt_send_header(struct mg_connection *, uint8_t cmd, uint8_t flags,
uint32_t len);
// Sends a PINGREQ packet.
void mg_mqtt_ping(struct mg_connection *);
// Sends a PINGRESP packet.
void mg_mqtt_pong(struct mg_connection *);
// Sends a DISCONNECT packet. For MQTT5, opts.props can carry reason
// properties. opts may be NULL for a plain disconnect with no properties.
void mg_mqtt_disconnect(struct mg_connection *, const struct mg_mqtt_opts *);
// Iterates MQTT5 properties in a received message. Start with ofs=0; pass
// the returned value back on each successive call. Returns 0 when done.
// Fills *prop with each property; key and val are zero-copy slices into msg->dgram.
size_t mg_mqtt_next_prop(struct mg_mqtt_message *, struct mg_mqtt_prop *,
size_t ofs);
// DNS record types, used in mg_dns_rr.atype and mg_mdns_query().
#define MG_DNS_RTYPE_A 1 // IPv4 address (4 bytes)
#define MG_DNS_RTYPE_PTR 12 // Domain name pointer, used for reverse lookups and service discovery
#define MG_DNS_RTYPE_TXT 16 // Arbitrary text, used by DNS-SD for service metadata
#define MG_DNS_RTYPE_AAAA 28 // IPv6 address (16 bytes)
#define MG_DNS_RTYPE_SRV 33 // Service location: priority, weight, port, target hostname
// Mongoose sends DNS queries that contain only one question:
// either A (IPv4) or AAAA (IPv6) address lookup.
// Therefore, we expect zero or one answer.
// If `resolved` is true, then `addr` contains resolved IPv4 or IPV6 address.
struct mg_dns_message {
uint16_t txnid; // Transaction ID
bool resolved; // Resolve successful, addr is set
struct mg_addr addr; // Resolved address
char name[256]; // Host name
};
// DNS wire-format header (RFC 1035). All fields are in network byte order;
// use mg_ntohs() to read them.
struct mg_dns_header {
uint16_t txnid; // Transaction ID, matched between query and response
uint16_t flags; // Flags: bit15=QR (0=query,1=response), bit10=AA, bit8=RD
uint16_t num_questions; // Number of question records following the header
uint16_t num_answers; // Number of answer records in the response
uint16_t num_authority_prs; // Number of authority records
uint16_t num_other_prs; // Number of additional records
};
// Parsed DNS resource record fields, extracted from the wire format.
struct mg_dns_rr {
uint16_t nlen; // Byte length of the encoded name in the wire format
uint16_t atype; // Record type: MG_DNS_RTYPE_A=1, AAAA=28, PTR=12, SRV=33, TXT=16
uint16_t aclass; // Record class: 1=INET; mDNS queries: bit15=QU (unicast request)
uint16_t alen; // Byte length of the RDATA section (e.g. 4 for A, 16 for AAAA)
};
// DNS-SD service description. Used by the user to describe a service to advertise.
struct mg_dnssd_record {
struct mg_str srvcproto; // Service and protocol label, e.g. "_http._tcp"
struct mg_str txt; // TXT record contents, verbatim
uint16_t port; // Port number for the SRV record
};
// ev_data for MG_EV_MDNS_REQ. The handler must set is_resp=true and fill r
// (and optionally respname/addr) to send a response; leave is_resp=false to ignore.
struct mg_mdns_req {
struct mg_dns_rr *rr; // Parsed resource record from the incoming query
struct mg_dnssd_record *r; // User-supplied service record to include in the response
struct mg_str reqname; // Queried hostname, without the .local suffix
struct mg_str respname; // Hostname to use in response; defaults to fn_data if empty
struct mg_addr *addr; // IP address for A record; uses local interface if NULL
bool is_listing; // True if this is a service-discovery listing (_services._dns-sd._udp)
bool is_resp; // Set to true in the handler to trigger a response
bool is_unicast; // True if the client requested a unicast (QU) response
};
// ev_data for MG_EV_MDNS_RESP, carrying address/data from a mDNS response
struct mg_mdns_resp {
struct mg_dns_rr *rr; // Resource record from the response (1st in chain)
struct mg_str name; // Resolved hostname, without the .local suffix
struct mg_addr addr; // Resolved IP address
struct mg_dnssd_record sd; // Service Discovery data
};
// Parses a DNS query or response from buf/len into dm. Returns true on success.
// On a response, sets dm->resolved=true and fills dm->addr if an A/AAAA record
// is found. Always fills dm->txnid and dm->name. Rejects packets larger than 512 bytes.
bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm);
// Parses one DNS record at offset ofs in buf/len into rr. Set is_question=true
// for question records (no RDATA), false for answer records. Returns the number
// of bytes consumed (use to advance ofs), or 0 on error.
size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs,
bool is_question, struct mg_dns_rr *rr);
// Creates an mDNS listener on UDP multicast 224.0.0.251:5353. Registers it as
// the manager's mDNS resolver (mgr->mdns). Fires MG_EV_MDNS_REQ for incoming
// queries and MG_EV_MDNS_RESP for incoming responses. Returns NULL on error.
// fn_data is a NUL-terminated server name, or NULL to pass all requests to fn.
struct mg_connection *mg_mdns_listen(struct mg_mgr *mgr, mg_event_handler_t fn,
void *fn_data);
// Sends an mDNS query on connection c (from mg_mdns_listen()) for name, with
// record type rtype (e.g. MG_DNS_RTYPE_A). Returns false on send error.
bool mg_mdns_query(struct mg_connection *c, const char *name, unsigned int rtype);
// Private API, do not expose or call in the user app
void mg_resolve(struct mg_connection *, const char *url);
void mg_resolve_cancel(struct mg_connection *);
// Modbus TCP function codes passed in mg_modbus_req.func.
#define MG_MODBUS_FUNC_READ_COILS 1 // Read 1-2000 discrete output coils
#define MG_MODBUS_FUNC_READ_DISCRETE_INPUTS 2 // Read 1-2000 discrete input coils
#define MG_MODBUS_FUNC_READ_HOLDING_REGISTERS 3 // Read 1-125 holding registers
#define MG_MODBUS_FUNC_READ_INPUT_REGISTERS 4 // Read 1-125 input registers
#define MG_MODBUS_FUNC_WRITE_SINGLE_COIL 5 // Write one output coil
#define MG_MODBUS_FUNC_WRITE_SINGLE_REGISTER 6 // Write one holding register
#define MG_MODBUS_FUNC_WRITE_MULTIPLE_COILS 15 // Write 1-1968 output coils
#define MG_MODBUS_FUNC_WRITE_MULTIPLE_REGISTERS 16 // Write 1-123 holding registers
// Standard Modbus exception codes. Set mg_modbus_req.error to one of these
// in the MG_EV_MODBUS_REQ handler to return an exception response to the client.
#define MG_MODBUS_ERR_NONE 0 // No error; send a normal response
#define MG_MODBUS_ERR_ILLEGAL_FUNCTION 1 // Function code not supported
#define MG_MODBUS_ERR_ILLEGAL_ADDRESS 2 // Starting address or range out of bounds
#define MG_MODBUS_ERR_ILLEGAL_VALUE 3 // Data value not accepted
#define MG_MODBUS_ERR_DEVICE_FAILURE 4 // Unrecoverable device error
// Modbus request passed as ev_data to the MG_EV_MODBUS_REQ event handler.
//
// For READ functions (READ_COILS, READ_DISCRETE_INPUTS, READ_HOLDING_REGISTERS,
// READ_INPUT_REGISTERS): u.bits / u.regs points to a zeroed, heap-allocated
// array of len entries. The handler fills in the values to return.
//
// For WRITE functions: u.bits / u.regs is pre-populated from the client
// request. len=1 for single-write functions.
//
// Do NOT free u.bits or u.regs - the library frees them after the handler
// returns and the response has been sent.
//
// Set error to a non-zero MG_MODBUS_ERR_* code to send an exception response
// instead of a data response. The response is sent automatically on return.
struct mg_modbus_req {
uint8_t func; // Function code, one of MG_MODBUS_FUNC_*
uint8_t error; // Exception code; set to MG_MODBUS_ERR_* to return an error
uint16_t addr; // Starting coil/register address from the request
union {
bool *bits; // Coil values; used by coil function codes
uint16_t *regs; // Register values; used by register function codes
} u;
uint16_t len; // Number of entries in u.bits or u.regs
};
// Creates a Modbus TCP listener on url. Fires MG_EV_MODBUS_REQ for each
// complete request; ev_data is struct mg_modbus_req *. Returns the listening
// connection, or NULL on error.
struct mg_connection *mg_modbus_listen(struct mg_mgr *mgr, const char *url,
mg_event_handler_t fn, void *fn_data);
// Maximum JSON nesting depth. mg_json_get() returns MG_JSON_TOO_DEEP if
// the path traverses more than this many nested objects/arrays.
#ifndef MG_JSON_MAX_DEPTH
#define MG_JSON_MAX_DEPTH 30
#endif
// Error codes returned by mg_json_get(). Successful returns are >= 0.
enum { MG_JSON_TOO_DEEP = -1, MG_JSON_INVALID = -2, MG_JSON_NOT_FOUND = -3 };
// Low-level token locator. Finds the JSON token at path inside json and
// returns its byte offset in json.buf. Sets *toklen to the token length.
// toklen may be NULL if only a presence check is needed.
// path must start with "$" (root). Use ".key" for object fields and "[N]"
// for array indices. Example: "$.items[0].name"
// Returns MG_JSON_NOT_FOUND, MG_JSON_INVALID, or MG_JSON_TOO_DEEP on error.
int mg_json_get(struct mg_str json, const char *path, int *toklen);
// Returns the raw JSON token at path.
//
// Returns:
// Zero-copy mg_str slice into json.buf, or {NULL, 0} if not found or invalid.
// Example:
// struct mg_str tok = mg_json_get_tok(body, "$.device.name");
// Full examples:
// src/dash.c
// Related APIs:
// mg_json_get(), mg_json_get_str(), mg_json_unescape()
// Notes:
// The returned token is not NUL-terminated. String tokens include the
// surrounding double quotes and escape sequences; use mg_json_unescape() or
// mg_json_get_str() when you need decoded string content.
struct mg_str mg_json_get_tok(struct mg_str json, const char *path);
// Parses a numeric JSON value at path into *v.
// Returns true and sets *v on success. Returns false if not found or not a
// number. v may be NULL to check existence only.
bool mg_json_get_num(struct mg_str json, const char *path, double *v);
// Parses a boolean JSON value at path into *v.
// Returns true and sets *v on success. Returns false if not found or not a
// boolean literal. v may be NULL to check existence only.
bool mg_json_get_bool(struct mg_str json, const char *path, bool *v);
// Returns a JSON number at path as a long, or dflt if not found or not a
// number. Use mg_json_get_num() when you need to distinguish missing from 0.
long mg_json_get_long(struct mg_str json, const char *path, long dflt);
// Returns a heap-allocated, NUL-terminated, JSON-unescaped copy of the string
// at path. Caller must mg_free() the result. Returns NULL if not found or the
// token is not a JSON string.
char *mg_json_get_str(struct mg_str json, const char *path);
// Decodes a hex-encoded JSON string at path into a heap-allocated byte array.
// Sets *len to the decoded byte count. Caller must mg_free() the result.
// Returns NULL if not found, not a string, or not valid hexadecimal.
char *mg_json_get_hex(struct mg_str json, const char *path, int *len);
// Decodes a base64-encoded JSON string at path into a heap-allocated byte
// array. Sets *len to the decoded byte count. Caller must mg_free() the result.
// Returns NULL if not found or not a string.
char *mg_json_get_b64(struct mg_str json, const char *path, int *len);
// Writes a JSON-unescaped string value at path into a caller-supplied buffer.
//
// Returns:
// Number of bytes written excluding the NUL, or 0 on error, not found,
// non-string token, too-small buffer, or an empty string.
// Example:
// char name[32];
// mg_json_unescape(body, "$.device.name", name, sizeof(name));
// Full examples:
// src/dash.c, src/ota.c
// Related APIs:
// mg_json_get_tok(), mg_json_get_str(), mg_json_get_num()
// Notes:
// On success, NUL-terminates when n > 0. The destination buffer is owned by
// the caller. Only string tokens are unescaped; other token types return 0.
size_t mg_json_unescape(struct mg_str json, const char *path, char *, size_t);
// Sequential iterator over a JSON object or array. Start with ofs=0.
// Each call fills *key and *val with the next entry and returns the new ofs
// to pass on the next call. Returns 0 when there are no more entries.
// For arrays, *key is set to {NULL, 0}. For objects, *key is the raw
// quoted key (e.g. "\"name\""). *val is the raw unparsed token.
size_t mg_json_next(struct mg_str obj, size_t ofs, struct mg_str *key,
struct mg_str *val);
struct mg_jwt_opts {
struct mg_str claims; // JSON payload
struct mg_str header; // Extra header members, no braces; alg/typ are set
struct mg_str kid; // Key ID protected header
struct mg_str secret; // HS256 secret
const uint8_t *private_key; // ES256 private key (MG_TLS_BUILTIN only)
const uint8_t *public_key; // ES256 public key (MG_TLS_BUILTIN only)
};
// Signs JSON claims as a compact JWT. Returns JWT length, or 0 on error.
size_t mg_jwt_sign_hs256(const struct mg_jwt_opts *opts, char *buf,
size_t len);
// Verifies JWT and decodes JSON claims into buf. Returns claims length, or 0.
size_t mg_jwt_verify_hs256(struct mg_str jwt, const struct mg_jwt_opts *opts,
char *buf, size_t len);
size_t mg_jwt_sign_es256(const struct mg_jwt_opts *opts, char *buf,
size_t len);
size_t mg_jwt_verify_es256(struct mg_str jwt, const struct mg_jwt_opts *opts,
char *buf, size_t len);
// In-flight JSON-RPC request. Populate and pass to mg_rpc_process() to
// dispatch, or receive one pre-filled in a handler registered with mg_rpc_add().
struct mg_rpc_req {
struct mg_rpc **head; // Head of the handler list; allows handlers to call other RPCs
struct mg_rpc *rpc; // Handler that matched this request; set by mg_rpc_process()
mg_pfn_t pfn; // Output function for the response (e.g. mg_pfn_iobuf)
void *pfn_data; // Argument passed to pfn (e.g. pointer to an mg_iobuf)
void *req_data; // Arbitrary caller data, not touched by the RPC layer
struct mg_str frame; // Raw JSON-RPC frame, e.g. {"id":1,"method":"add","params":[1,2]}
};
// A registered JSON-RPC method handler. Managed by mg_rpc_add/del; do not
// allocate directly. method is a glob pattern matched via mg_match().
struct mg_rpc {
struct mg_rpc *next; // Next handler in the linked list
struct mg_str method; // Method name glob pattern (heap-allocated copy)
void (*fn)(struct mg_rpc_req *); // Handler function called on a match
void *fn_data; // Passed to fn as r->rpc->fn_data
};
// Allocates a handler entry and prepends it to *head. method_pattern is
// copied (mg_strdup). Glob patterns are supported, e.g. "get/*".
// To receive JSON-RPC responses ($.result / $.error), register with method=""
void mg_rpc_add(struct mg_rpc **head, struct mg_str method_pattern,
void (*handler)(struct mg_rpc_req *), void *handler_data);
// Removes and frees all handlers in *head whose fn matches handler.
// Pass handler=NULL to remove every handler in the list.
void mg_rpc_del(struct mg_rpc **head, void (*handler)(struct mg_rpc_req *));
// Dispatches r->frame to the matching handler. Looks up "$.method" and calls
// the first handler whose pattern matches. If "$.result" or "$.error" is found
// instead (i.e. r->frame is a JSON-RPC response), dispatches to the "" handler.
// Sends a -32601 "not found" error if no handler matches, or -32700 on parse failure.
void mg_rpc_process(struct mg_rpc_req *);
// Sends a JSON-RPC success response: {"id":<id>,"result":<fmt>}.
// fmt is a printf-style format string written to r->pfn; NULL sends "null".
// Does nothing if the request frame has no "id" field (notification).
void mg_rpc_ok(struct mg_rpc_req *, const char *fmt, ...);
void mg_rpc_vok(struct mg_rpc_req *, const char *fmt, va_list *ap);
// Sends a JSON-RPC error response: {"id":<id>,"error":{"code":<code>,"message":<fmt>}}.
// fmt is a printf-style format string; NULL sends "null" as the message.
// The "id" field is omitted if the request had none; the error is always sent.
void mg_rpc_err(struct mg_rpc_req *, int code, const char *fmt, ...);
void mg_rpc_verr(struct mg_rpc_req *, int code, const char *fmt, va_list *);
// Built-in handler: responds with a JSON array of all registered method names.
// Register it as a method handler, e.g. mg_rpc_add(&head, mg_str("rpc.list"), mg_rpc_list, NULL);
void mg_rpc_list(struct mg_rpc_req *r);
#ifndef MG_DASH_MAX_USER_NAME
#define MG_DASH_MAX_USER_NAME 32
#endif
#ifndef MG_HTTP_ADDR
#if MG_ARCH == MG_ARCH_UNIX || MG_ARCH == MG_ARCH_WIN32
#define MG_HTTP_ADDR "http://0.0.0.0:8000"
#define MG_HTTPS_ADDR "https://0.0.0.0:8443"
#define MG_MODBUS_ADDR "tcp://0.0.0.0:8502"
#else
#define MG_HTTP_ADDR "http://0.0.0.0:80"
#define MG_HTTPS_ADDR "http://0.0.0.0:443"
#define MG_MODBUS_ADDR "tcp://0.0.0.0:502"
#endif
#endif
#ifndef MG_DASH_MAX_SEND_BUF_SIZE
#define MG_DASH_MAX_SEND_BUF_SIZE (MG_IO_SIZE * 5)
#endif
// C type of a value bound to a dashboard field, see struct mg_field
enum mg_val_type {
MG_VAL_INT, // int
MG_VAL_UINT64, // uint64_t
MG_VAL_BOOL, // bool
MG_VAL_DBL, // double
MG_VAL_STR, // Zero-terminated string
MG_VAL_RAW, // Pre-formatted JSON value, copied verbatim
};
// Dashboard field: binds a JSON key to a C variable for read and/or write
struct mg_field {
const char *name; // JSON key
enum mg_val_type type; // C type of the value, see enum mg_val_type
void *value; // Pointer to the bound variable
size_t value_size; // sizeof(*value) for type checking; 0 makes it read-only
};
// Operation passed to a struct mg_field_set read/write/delete/add callback.
// MG_DASH_ADD: fields hold the values of a new element to append; *index is
// not set. Return true to accept and persist it, false to reject (e.g. a cap
// was reached)
enum mg_dash_op { MG_DASH_READ, MG_DASH_WRITE, MG_DASH_DELETE, MG_DASH_ADD };
struct mg_dash;
// Authenticated (or guest) dashboard user
struct mg_dash_user {
struct mg_dash_user *next; // Next user in the list
char name[MG_DASH_MAX_USER_NAME]; // Username
char token[21]; // Session token, sent to the client as a cookie
int level; // Access level, see struct mg_dash::authenticate
uint64_t expire; // Session expiration time, see mg_millis()
struct mg_dash *dash; // Dashboard this user belongs to
};
// Named group of related dashboard fields, e.g. "settings" or "metrics"
struct mg_field_set {
const char *name; // Set name, used as a JSON key
struct mg_field *fields; // Zero-terminated array of fields
bool (*fn)(enum mg_dash_op op,
struct mg_dash_user *u); // Read/write/delete/add callback
int *index; // Non-NULL: array set. *index is set before fn(READ) call;
// fn sets *index = -1 to signal end of iteration.
// Size query: framework sets *index = -1 before fn(READ); fn
// sets *index = total size
struct mg_field_set *next; // Next set in the list
};
// Custom URI handler, registered with MG_DASH_REGISTER_CUSTOM_HANDLER()
struct mg_dash_custom_handler {
struct mg_dash_custom_handler *next; // Next handler in the list
struct mg_str uri_pattern; // URI to match, see mg_match()
mg_event_handler_t handler; // Event handler function
void *handler_data; // Opaque data for the handler
};
// Dashboard instance: fields, users and handlers served over HTTP and WS
struct mg_dash {
struct mg_field_set *sets; // Field sets, see MG_DASH_ADD_FIELD_SET()
struct mg_dash_custom_handler *custom_handlers; // Custom URI handlers
// Validates pass, fills in user, returns access level (<= 0: failure).
// NULL: no login required, all clients get guest access
int (*authenticate)(char *user, size_t userlen, const char *pass);
int session_auto_expiration_seconds; // Session lifetime, 0: 1 hour default
struct mg_dash_user *guest; // Used when authenticate == NULL
struct mg_fs *upload_fs; // FS for uploads/listing, default &mg_fs_posix
// Resolves the built-in file manager's root dir for user u, e.g. per-user
// upload directories. NULL: file manager disabled
bool (*files_dir)(const struct mg_dash_user *u, char *buf, size_t len);
};
#define MG_DASH_ADD_FIELD_SET(dash_, set_) \
do { \
(set_)->next = (dash_)->sets; \
(dash_)->sets = (set_); \
} while (0)
#define MG_DASH_REGISTER_CUSTOM_HANDLER(dash_, uri_, fn_, data_) \
do { \
static struct mg_dash_custom_handler ch_; \
ch_.next = (dash_)->custom_handlers; \
ch_.uri_pattern = mg_str(uri_); \
ch_.handler = (fn_); \
ch_.handler_data = (data_); \
(dash_)->custom_handlers = &ch_; \
} while (0)
// Dashboard event handler. Pass to mg_http_listen() as the fn argument
void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data);
// Notify WebSocket clients that a field set changed: broadcasts the new
// array size if *set->index < 0, or a single element update otherwise
void mg_dash_send_change(struct mg_mgr *mgr, struct mg_field_set *);
// Helper forward declarations for Mongoose CMSIS pack modules
extern struct mg_mgr g_mgr;
extern void mg_dash_init(struct mg_mgr *);
extern void mg_dash_poll(struct mg_mgr *);
extern void mg_mqtt_init(struct mg_mgr *);
extern void mg_mqtt_poll(struct mg_mgr *);
// Firmware OTA updates
//
// This is a low-level OTA API, not intended to be called directly by users.
// Users are provided with a higher-level API:
// - defining MG_OTA_URL enables HTTP or HTTPS pull-based updates
// - mg_http_stream_body() enables push-based updates
//
// However, it is possible to use the mg_ota_* API directly. Below is the
// example code of a function that is called repeatedly. It expects the
// last chink of size 0, which marks the end of the OTA process
//
// ```c
// static void ota(size_t total, size_t offset, void *buf, size_t len) {
// bool ok = false;
// if (ofs == 0 && (ok = mg_ota_begin(total)) == false) {
// mg_ota_end();
// } else if (len > 0 && (ok = mg_ota_write(buf, len)) == false) {
// mg_ota_end();
// } else if (len == 0) {}
// ok = mg_ota_end();
// }
// return ok;
// }
// ```
// Supported OTA targets
#define MG_OTA_NONE 0 // No OTA support
#define MG_OTA_STM32H5 1 // STM32 H5
#define MG_OTA_STM32H7 2 // STM32 H7
#define MG_OTA_STM32H7_DUAL_CORE 3 // STM32 H7 dual core
#define MG_OTA_STM32F 4 // STM32 F7/F4/F2
#define MG_OTA_CH32V307 100 // WCH CH32V307
#define MG_OTA_U2A 200 // Renesas U2A16, U2A8, U2A6
#define MG_OTA_RT1020 300 // IMXRT1020
#define MG_OTA_RT1050 301 // IMXRT1050
#define MG_OTA_RT1060 302 // IMXRT1060
#define MG_OTA_RT1064 303 // IMXRT1064
#define MG_OTA_RT1170 304 // IMXRT1170
#define MG_OTA_RT1180 305 // IMXRT1180
#define MG_OTA_MCXN 310 // MCXN947
#define MG_OTA_RW612 320 // FRDM-RW612
#define MG_OTA_FLASH 900 // OTA via internal flash
#define MG_OTA_ESP32 910 // ESP32 OTA implementation
#define MG_OTA_PICOSDK 920 // RP2040/2350 using Pico-SDK hardware_flash
#define MG_OTA_CUSTOM 1000 // Custom implementation
// OTA target. Settable in mongoose_config.h
#ifndef MG_OTA
#define MG_OTA MG_OTA_NONE
#else
#ifndef MG_IRAM
#if defined(__GNUC__)
// Places code block in .iram ELF section. A linker script can place it in RAM
#define MG_IRAM __attribute__((noinline, section(".iram")))
#else
#define MG_IRAM
#endif // compiler
#endif // IRAM
#endif // OTA
// Starts the OTA process. Called once at the beginning of a firmware update.
bool mg_ota_begin(size_t new_firmware_size);
// Writes the next firmware chunk. Called repeatedly until all data is written.
bool mg_ota_write(const void *buf, size_t len);
// Ends the OTA process and commits the written firmware.
bool mg_ota_end(void);
// MG_OTA_ROLLBACK: swap firmware banks and reset. On flash-based OTA, the
// default calls swap_fn() (no-op on single-bank) then resets. Override in
// mongoose_config.h for platforms without mg_flash (ESP32, PicoSDK, etc.).
#ifndef MG_OTA_ROLLBACK
#define MG_OTA_ROLLBACK() \
do { \
mg_flash->swap_fn(); \
NVIC_SystemReset(); \
} while (0)
#endif
// Arm a hardware watchdog. mg_ota_poll() feeds it while in MG_OTA_TESTING state;
// if the event loop hangs, it fires, resets the device, and MG_OTA_FAILED
// triggers MG_OTA_ROLLBACK() on next boot. Timeout is arch-defined.
#ifndef MG_OTA_ROLLBACK_TIMER_START
#define MG_OTA_ROLLBACK_TIMER_START()
#endif
#ifndef MG_OTA_ROLLBACK_TIMER_FEED
#define MG_OTA_ROLLBACK_TIMER_FEED()
#endif
// OTA state values for MG_OTA_STATE_GET / MG_OTA_STATE_SET.
// MG_OTA_CONFIRMED: firmware is committed, normal operation.
// MG_OTA_TESTING: new firmware booted for the first time; IWDG is armed.
// If the device reboots while in this state, it transitions
// to MG_OTA_FAILED and rolls back on the next boot.
// MG_OTA_FAILED: previous boot did not commit in time; rollback on next boot.
// MG_OTA_ROLLEDBACK: device rolled back to previous firmware. Sent to the OTA
// server as boot=3 on every poll until a new OTA succeeds,
// which overwrites this state with MG_OTA_TESTING.
enum {
MG_OTA_CONFIRMED = 0,
MG_OTA_TESTING = 1,
MG_OTA_FAILED = 2,
MG_OTA_ROLLEDBACK = 3
};
// Persistent OTA state storage. Override in mongoose_config.h to persist state across resets.
#ifndef MG_OTA_STATE_GET
#define MG_OTA_STATE_GET() 0
#endif
#ifndef MG_OTA_STATE_SET
#define MG_OTA_STATE_SET(val) (void) (val)
#endif
// Evaluation window for new firmware in seconds. mg_ota_poll() feeds the
// rollback watchdog while in MG_OTA_TESTING state and within this window;
// when it expires, auto-commits if the OTA server confirmed the same version,
// otherwise stops feeding and the watchdog triggers a rollback.
#ifndef MG_OTA_ROLLBACK_TIMEOUT_SECONDS
#define MG_OTA_ROLLBACK_TIMEOUT_SECONDS 120
#endif
// Call once at boot, after clock and backup-domain init (e.g. after HAL_Init()
// + SystemClock_Config() on CubeMX projects, or after hal_clock_init() on
// Mongoose tutorials). The backup domain must be write-accessible so the state
// transition TESTING → FAILED persists across resets.
// Arms the rollback watchdog if new firmware is in TESTING state, rolls back
// immediately if FAILED. mg_ota_poll() feeds the watchdog and auto-commits
// when the server confirms the same version within the window.
// boot (TESTING) → set FAILED → arm IWDG → run firmware
// ├── server ok + window expires → set CONFIRMED → IWDG resets cleanly
// └── no server ok + window expires → stop feeding → IWDG fires → FAILED → rollback
#define MG_OTA_BOOT_CHECK() \
do { \
if (MG_OTA_STATE_GET() == MG_OTA_FAILED) { \
MG_OTA_STATE_SET(MG_OTA_ROLLEDBACK); \
MG_INFO(("Commit deadline expired, rolling back")); \
MG_OTA_ROLLBACK(); \
} else if (MG_OTA_STATE_GET() == MG_OTA_TESTING) { \
MG_OTA_STATE_SET(MG_OTA_FAILED); \
MG_INFO(("New firmware: commit within %u sec or rolls back", \
(unsigned) MG_OTA_ROLLBACK_TIMEOUT_SECONDS)); \
MG_OTA_ROLLBACK_TIMER_START(); \
} \
} while (0)
// Pull-based OTA over HTTP or HTTPS. If it's HTTPS, MG_OTA_TLS_CA
// needs to be defined. Fetches metadata_url, which must
// return a JSON object like:
// { "version": "1.2.3", "url": "FIRMWARE_URL", "size": 324645 }
// If the server version differs from MG_FIRMWARE_VERSION, downloads
// FIRMWARE_URL and performs the OTA update. fn is called on every outcome:
// NULL on successful flash, "Same version" when already up to date, or an
// error string on failure. Pass NULL to use the default handler which logs
// failures via MG_ERROR.
void mg_ota_url_check(struct mg_mgr *mgr, const char *metadata_url,
void (*fn)(const char *error_message));
// Firmware info URL for mg_ota_poll() which calls mg_ota_url_check().
// Example: "http://mongoose.ws/ota/u/0/ota.json". See http://mongoose.ws/ota/
// Settable in mongoose_config.h
#ifndef MG_OTA_URL
#define MG_OTA_URL NULL
#endif
// PEM or DER CA certificate used to authenticate an HTTPS OTA server. Set to
// NULL to use HTTP. Set in mongoose_config.h.
#ifndef MG_OTA_TLS_CA
#define MG_OTA_TLS_CA NULL
#endif
// OTA status callback function for mg_ota_poll()
#ifndef MG_OTA_STATUS_FN
#define MG_OTA_STATUS_FN NULL
#endif
// Firmware version for mg_ota_poll(). Settable in mongoose_config.h
#ifndef MG_FIRMWARE_VERSION
#define MG_FIRMWARE_VERSION "1.0.0"
#endif
// Maximum version string length
#ifndef MG_OTA_MAX_VERSION_LEN
#define MG_OTA_MAX_VERSION_LEN 64
#endif
// How often mg_ota_poll() checks for a firmware update, in seconds
#ifndef MG_OTA_PULL_INTERVAL_SECONDS
#define MG_OTA_PULL_INTERVAL_SECONDS 60
#endif
// Set to 1 to define a custom mg_ota_device_id(). Settable in mongoose_config.h
#ifndef MG_ENABLE_CUSTOM_DEVICE_ID
#define MG_ENABLE_CUSTOM_DEVICE_ID 0
#endif
// Populates buf with a unique device ID string.
// E.g. on STM32, uses the 96-bit MCU UID converted to a hex string.
void mg_ota_device_id(char *buf, size_t len);
// Checks for a firmware update over HTTP or HTTPS. Called automatically by
// mg_mgr_poll() when MG_OTA_URL is set in mongoose_config.h. Do not call
// directly.
void mg_ota_poll(struct mg_mgr *);
// Device health monitoring
#ifndef MG_HEALTH_MAGIC
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
#endif
#if !defined(MG_HEALTH_RAM) && (defined(STM32H7_DEV_ID) || defined(STM32H5))
#define MG_HEALTH_RAM __attribute__((section(".mg_health")))
#endif
#if !defined(MG_HEALTH_DATA_SIZE) && (defined(STM32H7_DEV_ID) || defined(STM32H5))
#define MG_HEALTH_DATA_SIZE 250
#endif
#ifndef MG_HEALTH_DATA_SIZE
#define MG_HEALTH_DATA_SIZE 10
#endif
// Places the record in RAM that survives a reset. Define in mongoose_config.h
// to match the linker script, e.g. __attribute__((section(".mg_health")))
#ifndef MG_HEALTH_RAM
#define MG_HEALTH_RAM
#endif
// Why the device restarted. Hardware usually reports several flags at once,
// so these are ordered by how much they tell you: the most specific cause a
// device reports wins
enum mg_health_reason {
MG_HEALTH_RESET_UNKNOWN = 0, // Nothing reported, or no masks configured
MG_HEALTH_RESET_POWER, // Power-on reset, RAM contents are lost
MG_HEALTH_RESET_BROWNOUT, // Supply dipped below the brown-out level
MG_HEALTH_RESET_PIN, // External reset pin, e.g. the reset button
MG_HEALTH_RESET_SOFTWARE, // Firmware asked for it, e.g. NVIC_SystemReset
MG_HEALTH_RESET_LOWPOWER, // Illegal or failed low-power transition
MG_HEALTH_RESET_WATCHDOG, // Watchdog fired: the application stopped
MG_HEALTH_RESET_FAULT // Fault handler ran, see regs[] and saved_stack
};
struct mg_health {
char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data
uint32_t reset_reason; // enum mg_health_reason that started this boot
uint32_t cpuid; // ARM CPU ID
uint32_t data[MG_HEALTH_DATA_SIZE];
};
extern struct mg_health mg_health_record; // Defined in health.c
// Return true if the record holds valid data, i.e. it survived the reset and
// was written by this firmware layout
static inline bool mg_health_valid(void) {
static const char magic[] = MG_HEALTH_MAGIC;
return memcmp(mg_health_record.magic, magic,
sizeof(mg_health_record.magic)) == 0;
}
static inline int mg_health_reason(void) {
return mg_health_valid() ? (int) mg_health_record.reset_reason
: MG_HEALTH_RESET_UNKNOWN;
}
static inline const char *mg_health_reason_str(int reason) {
switch (reason) {
case MG_HEALTH_RESET_POWER: return "power";
case MG_HEALTH_RESET_BROWNOUT: return "brownout";
case MG_HEALTH_RESET_PIN: return "pin";
case MG_HEALTH_RESET_SOFTWARE: return "software";
case MG_HEALTH_RESET_LOWPOWER: return "lowpower";
case MG_HEALTH_RESET_WATCHDOG: return "watchdog";
case MG_HEALTH_RESET_FAULT: return "fault";
default: return "unknown";
}
}
#define MG_HEALTH_INIT() mg_health_init()
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
extern void mg_health_init(void);
// Cloud connector API to https://mdash.net - device management cloud
#ifndef MG_MDASH_URL
#define MG_MDASH_URL "wss://mdash.net/api/v2/rpc"
#endif
#ifndef MG_MDASH_KEY
#define MG_MDASH_KEY ""
#endif
#ifndef MG_ENABLE_MDASH
#define MG_ENABLE_MDASH 0
#endif
void mg_mdash_init(struct mg_mgr *);
void mg_mdash_poll(struct mg_mgr *);
void mg_mdash_free(struct mg_mgr *);
#if (MG_OTA != MG_OTA_NONE && MG_OTA != MG_OTA_CUSTOM) || MG_ENABLE_LFS
struct mg_flash {
void *start; // Address at which flash starts
size_t size; // Flash size
size_t secsz; // Sector size
size_t align; // Write alignment
bool (*write_fn)(void *, const void *, size_t); // Write function
bool (*swap_fn)(void); // Swap partitions
};
extern struct mg_flash *mg_flash;
bool mg_lfs_init(size_t size);
bool mg_ota_flash_begin(size_t new_firmware_size, struct mg_flash *flash);
bool mg_ota_flash_write(const void *buf, size_t len, struct mg_flash *flash);
bool mg_ota_flash_end(struct mg_flash *flash);
#endif
struct mg_wifi_data {
char *ssid, *pass; // STA mode, SSID to connect to
char *apssid, *appass; // AP mode, our SSID
uint32_t apip, apmask; // AP mode, our IP address and mask
uint8_t security; // STA mode, TBD
uint8_t apsecurity; // AP mode, TBD
uint8_t apchannel; // AP mode, channel to use
bool apmode; // start in AP mode; 'false' -> connect to 'ssid' != NULL
};
struct mg_wifi_scan_bss_data {
struct mg_str SSID;
char *BSSID;
int16_t RSSI;
uint8_t security;
#define MG_WIFI_SECURITY_OPEN 0
#define MG_WIFI_SECURITY_WEP MG_BIT(0)
#define MG_WIFI_SECURITY_WPA MG_BIT(1)
#define MG_WIFI_SECURITY_WPA2 MG_BIT(2)
#define MG_WIFI_SECURITY_WPA3 MG_BIT(3)
#define MG_WIFI_SECURITY_WPA_ENTERPRISE MG_BIT(4)
#define MG_WIFI_SECURITY_WPA2_ENTERPRISE MG_BIT(5)
#define MG_WIFI_SECURITY_WPA3_ENTERPRISE MG_BIT(6)
uint8_t channel;
unsigned band : 2;
#define MG_WIFI_BAND_2G 0
#define MG_WIFI_BAND_5G 1
unsigned has_n : 1;
unsigned has_ac : 1;
unsigned has_ax : 1;
};
bool mg_wifi_scan(void);
bool mg_wifi_connect(struct mg_wifi_data *);
bool mg_wifi_disconnect(void);
bool mg_wifi_ap_start(struct mg_wifi_data *);
bool mg_wifi_ap_stop(void);
#if MG_ENABLE_TCPIP
// no config defaults to 0 => Ethernet
enum mg_l2type { MG_TCPIP_L2_ETH = 0, MG_TCPIP_L2_PPP, MG_TCPIP_L2_PPPoE, MG_TCPIP_L2_USER };
#if defined(__DCC__)
#pragma pack(1)
#else
#pragma pack(push, 1)
#endif
struct mg_l2addr {
union {
uint8_t mac[6]; // ieee48
uint64_t ieee64; // PPP ifcid
uint16_t ieee16;
} addr;
};
// L2 using L2 (PPPoE) must inherit the lower layer first, so they overlap
struct eth_data {
uint16_t vlan_id;
};
struct pppoe_data { // (struct eth_data *) &pppoe_data = pppoedata.eth
struct eth_data eth;
};
union mg_l2data {
struct eth_data eth;
struct pppoe_data pppoe;
};
#if defined(__DCC__)
#pragma pack(0)
#else
#pragma pack(pop)
#endif
enum mg_l2proto {
MG_TCPIP_L2PROTO_IPV4 = 0,
MG_TCPIP_L2PROTO_IPV6,
MG_TCPIP_L2PROTO_ARP,
MG_TCPIP_L2PROTO_PPPoE_DISC,
MG_TCPIP_L2PROTO_PPPoE_SESS
};
enum mg_l2addrtype {
MG_TCPIP_L2ADDR_BCAST,
MG_TCPIP_L2ADDR_MCAST,
MG_TCPIP_L2ADDR_MCAST6
};
#endif
#if MG_ENABLE_TCPIP
// Mongoose TCP/IP network interface
struct mg_tcpip_if;
// Hardware driver interface. Implement all function pointers to plug in a
// new network device. Polling drivers set rx; interrupt/DMA drivers set
// rx=NULL and push frames via mg_tcpip_qwrite() from their ISR instead.
struct mg_tcpip_driver {
bool (*init)(struct mg_tcpip_if *); // One-time hardware init; return false on failure
size_t (*tx)(const void *, size_t, struct mg_tcpip_if *); // Transmit one raw frame; return bytes sent
size_t (*rx)(void *buf, size_t len, struct mg_tcpip_if *); // Read one raw frame; return frame size, 0 if none. NULL for interrupt-driven drivers
bool (*poll)(struct mg_tcpip_if *, bool); // Check link status; second arg is true once per second. Return true=up, false=down
};
// Event handler called by the TCP/IP stack for network-level events.
// ev is one of MG_TCPIP_EV_*; ev_data type is documented per event below.
typedef void (*mg_tcpip_event_handler_t)(struct mg_tcpip_if *ifp, int ev,
void *ev_data);
// clang-format off
// Events delivered to mg_tcpip_if.fn (user) and mg_tcpip_if.pfn (driver).
// Each entry shows the ev_data type to cast inside the handler.
enum {
MG_TCPIP_EV_STATE_CHANGE, // IPv4 link state changed uint8_t *(&ifp->state)
MG_TCPIP_EV_DHCP_DNS, // DHCP assigned a DNS server uint32_t *ipaddr
MG_TCPIP_EV_DHCP_SNTP, // DHCP assigned an SNTP server uint32_t *ipaddr
MG_TCPIP_EV_ARP, // Raw ARP frame received struct mg_str *
MG_TCPIP_EV_TIMER_1S, // 1-second periodic tick NULL
MG_TCPIP_EV_WIFI_SCAN_RESULT, // Wi-Fi BSS scan entry struct mg_wifi_scan_bss_data *
MG_TCPIP_EV_WIFI_SCAN_END, // Wi-Fi scan finished NULL
MG_TCPIP_EV_WIFI_CONNECT_ERR, // Wi-Fi connection failed driver and chip specific
MG_TCPIP_EV_DRIVER, // Driver-defined event driver specific
MG_TCPIP_EV_STATE6_CHANGE, // IPv6 link state changed uint8_t *(&ifp->state6)
MG_TCPIP_EV_USER // Starting ID for user-defined events
};
// clang-format on
// Network interface. Zero-initialise, then set the fields marked "user-set"
// before calling mg_tcpip_init(). Fields in the "internal state" section
// below are managed by the stack; read them freely but do not modify them.
struct mg_tcpip_if {
// User-set configuration
uint8_t mac[sizeof(struct mg_l2addr)]; // MAC address; must be set to a valid address
uint32_t ip, mask, gw; // Static IPv4 address, netmask, gateway; 0 = use DHCP
struct mg_str tx; // TX frame buffer (managed by the stack)
bool enable_dhcp_client; // Enable DHCP client; auto-set if ip==0
bool enable_dhcp_server; // Enable DHCP server
bool enable_get_gateway; // DHCP server offers itself as the default gateway
bool enable_req_dns; // DHCP client requests a DNS server address
bool enable_req_sntp; // DHCP client requests an SNTP server address
bool enable_fcs_check; // Verify and strip FCS from received frames
bool enable_mac_check; // Drop frames not addressed to this MAC
bool enable_tcp_retransmit; // Enable optional TCP retransmission queue
bool update_mac_hash_table; // Signal driver to refresh MAC multicast hash table
bool is_ip_changed; // Set by stack when IP changes; triggers connection restart
struct mg_tcpip_driver *driver; // Hardware driver; must be set before mg_tcpip_init()
void *driver_data; // Passed to all driver functions as ifp->driver_data
mg_tcpip_event_handler_t pfn; // Driver-level event handler; called before fn
mg_tcpip_event_handler_t fn; // User event handler for MG_TCPIP_EV_* events
struct mg_mgr *mgr; // Mongoose event manager; set by mg_tcpip_init()
struct mg_queue recv_queue; // Incoming frame queue; pre-size by setting recv_queue.size
char dhcp_name[MG_TCPIP_DHCPNAME_SIZE]; // Hostname sent in DHCP requests; defaults to "mip"
uint16_t mtu; // IP MTU (max payload size at the IP layer)
uint16_t framesize; // Maximum L2 frame size in bytes
uint16_t l2mtu; // L2 frame payload, default net MTU
#if MG_ENABLE_IPV6
uint64_t ip6ll[2], ip6[2]; // IPv6 link-local and global addresses
uint8_t prefix[8]; // Global prefix bytes
uint8_t prefix_len; // Global prefix length in bits
uint64_t gw6[2]; // IPv6 default gateway
bool enable_slaac; // Enable SLAAC (stateless address autoconfiguration)
bool enable_dhcp6_client; // Enable DHCPv6 client (not yet fully implemented)
bool is_ip6_changed; // Set by stack when IPv6 address changes
#endif
// Internal state - read-only for users, managed by the stack
uint8_t gwmac[sizeof(struct mg_l2addr)]; // Gateway hardware address (resolved via ARP)
enum mg_l2type l2type; // Layer-2 type: Ethernet, PPP, etc. (see l2.h)
union mg_l2data l2data; // Layer-2 config and state (see l2.h)
char *dns4_url; // Active DNS server URL (set from DHCP or config)
uint64_t now; // Current time in ms (updated each mg_mgr_poll)
uint64_t timer_1000ms; // Timestamp of last 1-second tick
uint64_t lease_expire; // DHCP lease expiry time in ms
uint16_t eport; // Next ephemeral port to assign
volatile uint32_t ndrop; // Frames received but dropped (queue full)
volatile uint32_t nrecv; // Total frames received
volatile uint32_t nsent; // Total frames transmitted
volatile uint32_t nerr; // Driver-reported transmit errors
uint8_t state; // Current IPv4 link/IP state (MG_TCPIP_STATE_*)
#define MG_TCPIP_STATE_DOWN 0 // Physical link is down
#define MG_TCPIP_STATE_LINK_UP 1 // Driver reports physical link is up
#define MG_TCPIP_STATE_UP 2 // L2 is ready (e.g. PPP negotiated)
#define MG_TCPIP_STATE_REQ 3 // DHCP REQUESTING: waiting for DHCP offer
#define MG_TCPIP_STATE_IP 4 // IP address assigned; resolving gateway MAC
#define MG_TCPIP_STATE_READY 5 // Fully operational; gateway MAC resolved
bool gw_ready; // Gateway MAC address has been resolved via ARP
#if MG_ENABLE_IPV6
uint8_t gw6mac[sizeof(struct mg_l2addr)]; // IPv6 gateway hardware address
uint8_t state6; // Current IPv6 link/IP state (MG_TCPIP_STATE_*)
bool gw6_ready; // IPv6 gateway MAC address has been resolved
#endif
};
// Initialises the TCP/IP stack. Calls driver->init(), allocates the TX frame
// buffer (ifp->framesize bytes) and the RX queue. If ifp->ip == 0 and the
// L2 type is not PPP, DHCP client is enabled automatically.
// Must be called once after populating ifp->driver, ifp->mac, and any
// static IP/mask/gw values.
void mg_tcpip_init(struct mg_mgr *, struct mg_tcpip_if *);
// Frees the TX buffer and RX queue allocated by mg_tcpip_init().
void mg_tcpip_free(struct mg_tcpip_if *);
// Pushes a received raw frame into ifp->recv_queue. Used by interrupt-driven
// or DMA-based drivers (those with rx=NULL) to hand frames to the stack from
// an ISR or DMA completion callback. Drops the frame and increments ndrop if
// the queue is full.
void mg_tcpip_qwrite(void *buf, size_t len, struct mg_tcpip_if *ifp);
// Sends a broadcast ARP who-has request for ip. mac may be NULL for a plain
// broadcast, or a non-NULL hint to set the target MAC field in the request.
void mg_tcpip_arp_request(struct mg_tcpip_if *ifp, uint32_t ip, uint8_t *mac);
// Built-in hardware drivers. Assign one to mg_tcpip_if.driver before calling
// mg_tcpip_init(). Each driver also requires a corresponding driver_data
// struct; see the matching driver header for details.
extern struct mg_tcpip_driver mg_tcpip_driver_stm32f;
extern struct mg_tcpip_driver mg_tcpip_driver_w5500;
extern struct mg_tcpip_driver mg_tcpip_driver_w5100;
extern struct mg_tcpip_driver mg_tcpip_driver_tm4c;
extern struct mg_tcpip_driver mg_tcpip_driver_tms570;
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_same54;
extern struct mg_tcpip_driver mg_tcpip_driver_cmsis;
extern struct mg_tcpip_driver mg_tcpip_driver_ra;
extern struct mg_tcpip_driver mg_tcpip_driver_xmc;
extern struct mg_tcpip_driver mg_tcpip_driver_xmc7;
extern struct mg_tcpip_driver mg_tcpip_driver_pico_w;
extern struct mg_tcpip_driver mg_tcpip_driver_rw612;
extern struct mg_tcpip_driver mg_tcpip_driver_cyw;
extern struct mg_tcpip_driver mg_tcpip_driver_nxp_wifi;
extern struct mg_tcpip_driver mg_tcpip_driver_st67w6;
extern struct mg_tcpip_driver mg_tcpip_driver_atcmd;
extern struct mg_tcpip_driver mg_tcpip_driver_netc;
// SPI bus abstraction for SPI-attached network chips (e.g. W5500).
// Populate and assign to the driver's driver_data field.
struct mg_tcpip_spi {
void *spi; // Opaque SPI bus handle passed to all callbacks
void (*begin)(void *); // Assert chip-select (start of transaction)
void (*end)(void *); // Deassert chip-select (end of transaction)
void (*txn)(void *, uint8_t *write, uint8_t *read, size_t); // Full-duplex transfer: write and read len bytes simultaneously
};
// Alignment attributes for DMA-capable Ethernet descriptor buffers.
// Use these on driver RX/TX descriptor arrays where the hardware requires
// specific alignment.
#ifndef MG_8BYTE_ALIGNED
#if defined(__GNUC__)
#define MG_8BYTE_ALIGNED __attribute__((aligned((8U))))
#else
#define MG_8BYTE_ALIGNED
#endif // compiler
#endif // 8BYTE_ALIGNED
#ifndef MG_16BYTE_ALIGNED
#if defined(__GNUC__)
#define MG_16BYTE_ALIGNED __attribute__((aligned((16U))))
#else
#define MG_16BYTE_ALIGNED
#endif // compiler
#endif // 16BYTE_ALIGNED
#ifndef MG_32BYTE_ALIGNED
#if defined(__GNUC__)
#define MG_32BYTE_ALIGNED __attribute__((aligned((32U))))
#else
#define MG_32BYTE_ALIGNED
#endif // compiler
#endif // 32BYTE_ALIGNED
#ifndef MG_64BYTE_ALIGNED
#if defined(__GNUC__)
#define MG_64BYTE_ALIGNED __attribute__((aligned((64U))))
#else
#define MG_64BYTE_ALIGNED
#endif // compiler
#endif // 64BYTE_ALIGNED
// Memory section attribute for Ethernet frame buffers. Define to
// __attribute__((section(".eth_ram"))) or similar if the Ethernet DMA
// requires buffers in a specific RAM region.
#ifndef MG_ETH_RAM
#define MG_ETH_RAM
#endif
#endif
#if MG_ENABLE_BSD_SOCKETS
void mg_bsd_init(struct mg_mgr *);
void mg_bsd_poll(struct mg_mgr *);
#include <errno.h>
#include <fcntl.h>
#ifndef EAGAIN
#define EAGAIN 11
#endif
#ifndef EWOULDBLOCK
#define EWOULDBLOCK EAGAIN
#endif
#ifndef EINPROGRESS
#define EINPROGRESS 36
#endif
#ifndef ENOTCONN
#define ENOTCONN 107
#endif
#ifndef ECONNREFUSED
#define ECONNREFUSED 111
#endif
#ifndef ECONNRESET
#define ECONNRESET 104
#endif
#ifndef EADDRINUSE
#define EADDRINUSE 98
#endif
#ifndef ETIMEDOUT
#define ETIMEDOUT 110
#endif
#ifndef EBADF
#define EBADF 9
#endif
#ifndef EMFILE
#define EMFILE 24
#endif
#ifndef ENOMEM
#define ENOMEM 12
#endif
#ifndef EIO
#define EIO 5
#endif
#ifndef EINVAL
#define EINVAL 22
#endif
#ifndef EPERM
#define EPERM 1
#endif
#ifndef EPIPE
#define EPIPE 32
#endif
#ifndef EPROTONOSUPPORT
#define EPROTONOSUPPORT 93
#endif
#if MG_ENABLE_BSD_PROTOTYPES
typedef unsigned int socklen_t;
typedef unsigned long nfds_t;
typedef uint32_t in_addr_t;
typedef unsigned long __fd_mask;
struct in_addr { in_addr_t s_addr; };
struct in6_addr { uint8_t s6_addr[16]; };
struct sockaddr { uint16_t sa_family; char sa_data[14]; };
struct sockaddr_in {
uint16_t sin_family;
uint16_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
struct sockaddr_in6 {
uint16_t sin6_family;
uint16_t sin6_port;
uint32_t sin6_flowinfo;
struct in6_addr sin6_addr;
uint32_t sin6_scope_id;
};
struct sockaddr_storage { uint16_t ss_family; char __ss_pad[126]; };
struct hostent {
char *h_name;
char **h_aliases;
int h_addrtype;
int h_length;
char **h_addr_list;
};
struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};
struct pollfd { int fd; short events; short revents; };
#ifndef FD_SETSIZE
#define FD_SETSIZE 64
#endif
#ifndef FD_ZERO
#define _NFDBITS ((int) sizeof(__fd_mask) * 8)
#define _howmany(x, y) (((x) + ((y) - 1)) / (y))
typedef struct fd_set {
__fd_mask __fds_bits[_howmany(FD_SETSIZE, _NFDBITS)];
} fd_set;
#define __fdset_mask(n) ((__fd_mask) 1 << ((n) % _NFDBITS))
#define FD_CLR(n, p) ((p)->__fds_bits[(n) / _NFDBITS] &= ~__fdset_mask(n))
#define FD_ISSET(n, p) \
(((p)->__fds_bits[(n) / _NFDBITS] & __fdset_mask(n)) != 0)
#define FD_SET(n, p) ((p)->__fds_bits[(n) / _NFDBITS] |= __fdset_mask(n))
#define FD_ZERO(p) \
do { \
fd_set *_p = (p); \
size_t _n = _howmany(FD_SETSIZE, _NFDBITS); \
while (_n > 0) _p->__fds_bits[--_n] = 0; \
} while (0)
#endif
#define h_addr h_addr_list[0]
#define AF_INET 2
#define AF_INET6 10
#define AF_UNSPEC 0
#define PF_INET AF_INET
#define PF_INET6 AF_INET6
#define PF_UNSPEC AF_UNSPEC
#define SOCK_STREAM 1
#define SOCK_DGRAM 2
#define IPPROTO_TCP 6
#define IPPROTO_UDP 17
#define INADDR_ANY 0
#define INADDR_LOOPBACK 0x7f000001
#define INET_ADDRSTRLEN 16
#define SOL_SOCKET 0xffff
#define SO_REUSEADDR 2
#define SO_REUSEPORT 15
#define SO_KEEPALIVE 9
#define SO_ERROR 4
#define SO_BROADCAST 6
#define SO_RCVBUF 8
#define SO_SNDBUF 7
#define SO_NOSIGPIPE 0x1022
#define TCP_NODELAY 1
#define MSG_DONTWAIT 0x40
#define MSG_NOSIGNAL 0x4000
#define MSG_PEEK 0x02
#ifndef F_GETFL
#define F_GETFL 3
#endif
#ifndef F_SETFL
#define F_SETFL 4
#endif
#ifndef O_NONBLOCK
#define O_NONBLOCK 0x4000
#endif
#define SHUT_RD 0
#define SHUT_WR 1
#define SHUT_RDWR 2
#define AI_PASSIVE 0x0001
#define NI_MAXHOST 1025
#define NI_MAXSERV 32
#define POLLIN 0x001
#define POLLOUT 0x004
#define POLLERR 0x008
#define POLLHUP 0x010
#define POLLNVAL 0x020
#define htons(x) mg_ntohs(x)
#define ntohs(x) mg_ntohs(x)
#define htonl(x) mg_ntohl(x)
#define ntohl(x) mg_ntohl(x)
int socket(int, int, int);
int bind(int, const struct sockaddr *, socklen_t);
int listen(int, int);
int accept(int, struct sockaddr *, socklen_t *);
int connect(int, const struct sockaddr *, socklen_t);
ssize_t send(int, const void *, size_t, int);
ssize_t recv(int, void *, size_t, int);
ssize_t sendto(int, const void *, size_t, int, const struct sockaddr *,
socklen_t);
ssize_t recvfrom(int, void *, size_t, int, struct sockaddr *, socklen_t *);
int close(int);
int shutdown(int, int);
int setsockopt(int, int, int, const void *, socklen_t);
int getsockopt(int, int, int, void *, socklen_t *);
int getsockname(int, struct sockaddr *, socklen_t *);
int getpeername(int, struct sockaddr *, socklen_t *);
int select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
int poll(struct pollfd *, nfds_t, int);
struct hostent *gethostbyname(const char *);
int getaddrinfo(const char *, const char *, const struct addrinfo *,
struct addrinfo **);
void freeaddrinfo(struct addrinfo *);
int inet_pton(int, const char *, void *);
const char *inet_ntop(int, const void *, char *, socklen_t);
in_addr_t inet_addr(const char *);
char *inet_ntoa(struct in_addr);
#else
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <poll.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#endif // MG_ENABLE_BSD_PROTOTYPES
#include <sys/time.h>
#else
#define mg_bsd_init(mgr)
#define mg_bsd_poll(mgr)
#endif // MG_ENABLE_BSD_SOCKETS
struct mg_tcpip_driver_atcmd_data {
void *usart; // Opaque UART/SPI descriptor
void (*reset)(void *); // Modem hardware reset
void (*tx)(void *, uint8_t); // USART transmit single byte
int (*rx)(void *); // USART receive single byte
const char **script; // List of AT commands and expected replies
int script_index; // Index of the current AT command in the list
uint64_t deadline; // AT command deadline in ms
bool no_byte_stuff; // Do not perform byte stuffing/unstuffing
bool link; // internal use: done with AT processing
bool unstuffing; // internal use: unstuffing state
};
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_CMSIS) && MG_ENABLE_DRIVER_CMSIS
#include "Driver_ETH_MAC.h" // keep this include
#include "Driver_ETH_PHY.h" // keep this include
#endif
#if MG_ENABLE_TCPIP && \
((defined(MG_ENABLE_DRIVER_CYW) && MG_ENABLE_DRIVER_CYW) || \
(defined(MG_ENABLE_DRIVER_CYW_SDIO) && MG_ENABLE_DRIVER_CYW_SDIO))
struct mg_tcpip_driver_cyw_firmware {
const uint8_t *code_addr;
size_t code_len;
const uint8_t *nvram_addr;
size_t nvram_len;
const uint8_t *clm_addr;
size_t clm_len;
};
struct mg_tcpip_driver_cyw_data {
struct mg_wifi_data wifi;
void *bus;
struct mg_tcpip_driver_cyw_firmware *fw;
bool hs; // use chip "high-speed" mode; otherwise SPI CPOL0 CPHA0 (DS 4.2.3 Table 6)
};
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_cyw_data driver_data_; \
static struct mg_tcpip_if mif_; \
MG_SET_WIFI_CONFIG(&mif_, &driver_data_); \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.driver = &mg_tcpip_driver_cyw; \
mif_.driver_data = &driver_data_; \
mif_.recv_queue.size = 8192; \
mif_.mac[0] = 2; /* MAC read from OTP at driver init */ \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: cyw, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#endif
#if MG_ENABLE_TCPIP && \
(defined(MG_ENABLE_DRIVER_IMXRT10) && MG_ENABLE_DRIVER_IMXRT10) || \
(defined(MG_ENABLE_DRIVER_IMXRT11) && MG_ENABLE_DRIVER_IMXRT11) || \
(defined(MG_ENABLE_DRIVER_MCXE) && MG_ENABLE_DRIVER_MCXE)
struct mg_tcpip_driver_imxrt_data {
// MDC clock divider. MDC clock is derived from IPS Bus clock (ipg_clk),
// must not exceed 2.5MHz. Configuration for clock range 2.36~2.50 MHz
// 37.5.1.8.2, Table 37-46 : f = ipg_clk / (2(mdc_cr + 1))
// ipg_clk mdc_cr VALUE
// --------------------------
// -1 <-- TODO() tell driver to guess the value
// 25 MHz 4
// 33 MHz 6
// 40 MHz 7
// 50 MHz 9
// 66 MHz 13
int mdc_cr; // Valid values: -1 to 63
uint8_t phy_addr; // PHY address
};
#ifndef MG_TCPIP_PHY_ADDR
#define MG_TCPIP_PHY_ADDR 2
#endif
#ifndef MG_DRIVER_MDC_CR
#define MG_DRIVER_MDC_CR 24
#endif
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_imxrt_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.driver = &mg_tcpip_driver_imxrt; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: imxrt, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#endif
#if defined(MG_ENABLE_TCPIP) && MG_ENABLE_TCPIP && \
defined(MG_ENABLE_DRIVER_NETC) && MG_ENABLE_DRIVER_NETC
struct mg_tcpip_driver_netc_data {
int mdc_cr;
uint8_t phy_addr;
// Selects the physical Ethernet link (ETH0 through ETH3)
// Its switch port is bridged to pseudo-port 4,
// which connects internally to ENETC1
// IMXRT1180 RM 53.4.1.1 NETC Block diagram
uint8_t link;
};
#ifndef MG_TCPIP_PHY_ADDR
#define MG_TCPIP_PHY_ADDR 5
#endif
#ifndef MG_DRIVER_MDC_CR
#define MG_DRIVER_MDC_CR 48
#endif
#ifndef MG_TCPIP_NETC_LINK
#define MG_TCPIP_NETC_LINK 0
#endif
struct mg_tcpip_driver;
extern struct mg_tcpip_driver mg_tcpip_driver_netc;
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_netc_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
driver_data_.link = MG_TCPIP_NETC_LINK; \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.driver = &mg_tcpip_driver_netc; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: netc, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#endif
#if MG_ENABLE_TCPIP && \
defined(MG_ENABLE_DRIVER_NXP_WIFI) && MG_ENABLE_DRIVER_NXP_WIFI
struct mg_tcpip_driver_nxp_wifi_data {
struct mg_wifi_data wifi;
};
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_nxp_wifi_data driver_data_; \
static struct mg_tcpip_if mif_; \
MG_SET_WIFI_CONFIG(&mif_, &driver_data_); \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.driver = &mg_tcpip_driver_nxp_wifi; \
mif_.driver_data = &driver_data_; \
mif_.recv_queue.size = 8192; \
mif_.mac[0] = 2; /* MAC read from OTP at driver init */ \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: nxp wifi, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#endif
struct mg_phy {
uint16_t (*read_reg)(uint8_t addr, uint8_t reg);
void (*write_reg)(uint8_t addr, uint8_t reg, uint16_t value);
};
// PHY configuration settings, bitmask
enum {
// Set if PHY LEDs are connected to ground
MG_PHY_LEDS_ACTIVE_HIGH = (1 << 0),
// Set when PHY clocks MAC. Otherwise, MAC clocks PHY
MG_PHY_CLOCKS_MAC = (1 << 1)
};
enum { MG_PHY_SPEED_10M, MG_PHY_SPEED_100M, MG_PHY_SPEED_1000M };
void mg_phy_init(struct mg_phy *, uint8_t addr, uint8_t config);
bool mg_phy_up(struct mg_phy *, uint8_t addr, bool *full_duplex,
uint8_t *speed);
#if MG_ENABLE_TCPIP && MG_ARCH == MG_ARCH_PICOSDK && \
defined(MG_ENABLE_DRIVER_PICO_W) && MG_ENABLE_DRIVER_PICO_W
#include "cyw43.h" // keep this include
#include "pico/cyw43_arch.h" // keep this include
#include "pico/unique_id.h" // keep this include
struct mg_tcpip_driver_pico_w_data {
struct mg_wifi_data wifi;
};
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_pico_w_data driver_data_; \
static struct mg_tcpip_if mif_; \
MG_SET_WIFI_CONFIG(&mif_, &driver_data_); \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.driver = &mg_tcpip_driver_pico_w; \
mif_.driver_data = &driver_data_; \
mif_.recv_queue.size = 8192; \
mif_.mac[0] = 2; /* MAC read from OTP at driver init */ \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: pico-w, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#endif
#if MG_ENABLE_TCPIP && \
(defined(MG_ENABLE_DRIVER_RA6) && MG_ENABLE_DRIVER_RA6) || \
(defined(MG_ENABLE_DRIVER_RA8) && MG_ENABLE_DRIVER_RA8)
struct mg_tcpip_driver_ra_data {
// MDC clock "divider". MDC clock is software generated,
uint32_t clock; // core clock frequency in Hz
uint16_t irqno; // IRQn, R_ICU->IELSR[irqno]
uint8_t phy_addr; // PHY address
};
#ifndef MG_DRIVER_CLK_FREQ
#define MG_DRIVER_CLK_FREQ 100000000UL
#endif
#ifndef MG_DRIVER_IRQ_NO
#define MG_DRIVER_IRQ_NO 0
#endif
#ifndef MG_TCPIP_PHY_ADDR
#define MG_TCPIP_PHY_ADDR 0
#endif
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_ra_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.clock = MG_DRIVER_CLK_FREQ; \
driver_data_.irqno = MG_DRIVER_IRQ_NO; \
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.driver = &mg_tcpip_driver_ra; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: ra, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#endif
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RW612) && MG_ENABLE_DRIVER_RW612
struct mg_tcpip_driver_rw612_data {
// 38.1.8 MII Speed Control Register (MSCR)
// MDC clock frequency must not exceed 2.5 MHz and is calculated as follows:
// MDC_freq = P_clock / ((mdc_cr + 1) * 2), where P_clock is the
// peripheral bus clock.
// IEEE802.3 clause 22 defines a minimum of 10 ns for the hold time on the
// MDIO output. Depending on the host bus frequency, the setting may need
// to be increased.
int mdc_cr;
int mdc_holdtime; // Valid values: [0-7]
uint8_t phy_addr;
};
#ifndef MG_TCPIP_PHY_ADDR
#define MG_TCPIP_PHY_ADDR 2
#endif
#ifndef MG_DRIVER_MDC_CR
#define MG_DRIVER_MDC_CR 51
#endif
#ifndef MG_DRIVER_MDC_HOLDTIME
#define MG_DRIVER_MDC_HOLDTIME 3
#endif
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_rw612_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.driver = &mg_tcpip_driver_rw612; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: rw612, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#endif
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_SAME54) && \
MG_ENABLE_DRIVER_SAME54
#ifndef MG_DRIVER_MDC_CR
#define MG_DRIVER_MDC_CR 5
#endif
#ifndef MG_DRIVER_PHY_ADDR
#define MG_DRIVER_PHY_ADDR 0
#endif
struct mg_tcpip_driver_same54_data {
int mdc_cr;
uint8_t phy_addr;
};
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_same54_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
driver_data_.phy_addr = MG_DRIVER_PHY_ADDR; \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.driver = &mg_tcpip_driver_same54; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: same54, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#endif
#if MG_ENABLE_TCPIP && \
(defined(MG_ENABLE_DRIVER_CYW_SDIO) && MG_ENABLE_DRIVER_CYW_SDIO)
// Specific chip/card driver --> SDIO driver --> HAL --> SDIO hw controller
// API with HAL for hardware controller
// - Provide a function to init the controller (external)
// - Provide these functions:
struct mg_tcpip_sdio {
void *sdio; // Opaque SDIO bus descriptor
void (*cfg)(void *, uint8_t); // select operating parameters
// SDIO transaction: send cmd with a possible 1-byte read or write
bool (*txn)(void *, uint8_t cmd, uint32_t arg, uint32_t *r);
// SDIO extended transaction: write or read len bytes, using blksz blocks
bool (*xfr)(void *, bool write, uint32_t arg, uint16_t blksz, uint32_t *,
uint32_t len, uint32_t *r);
};
// API with driver (e.g.: cyw.c)
// Once the hardware controller has been initialized:
// - Init card: selects the card, sets F0 block size, sets bus width and speed
bool mg_sdio_init(struct mg_tcpip_sdio *sdio);
// - Enable other possible functions (F1 to F7)
bool mg_sdio_enable_f(struct mg_tcpip_sdio *sdio, unsigned int f);
// - Wait for them to be ready
bool mg_sdio_waitready_f(struct mg_tcpip_sdio *sdio, unsigned int f);
// - Set their transfer block length
bool mg_sdio_set_blksz(struct mg_tcpip_sdio *sdio, unsigned int f,
uint16_t blksz);
// - Transfer data to/from a function (abstracts from transaction type)
// - Requesting a read transfer > blocksize means block transfer will be used.
// - Drivers must have room to accomodate a whole block transfer, see sdio.c
// - Transfers of > 1 byte --> (uint32_t *) data. 1-byte --> (uint8_t *) data
bool mg_sdio_transfer(struct mg_tcpip_sdio *sdio, bool write, unsigned int f,
uint32_t addr, void *data, uint32_t len);
#endif
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_ST67W6) && \
MG_ENABLE_DRIVER_ST67W6
struct mg_tcpip_driver_st67w6_data {
struct mg_wifi_data wifi;
void *spi;
bool (*is_ready)(void); // return state of module RDY pin
struct mg_queue send_queue; // decouple tx calls from module polls
};
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_st67w6_data driver_data_; \
static struct mg_tcpip_if mif_; \
MG_SET_WIFI_CONFIG(&mif_, &driver_data_); \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.driver = &mg_tcpip_driver_st67w6; \
mif_.driver_data = &driver_data_; \
mif_.recv_queue.size = 8192; \
mif_.mac[0] = 2; /* MAC read from OTP at driver init */ \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: st67w6, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#endif
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32F) && \
MG_ENABLE_DRIVER_STM32F
struct mg_tcpip_driver_stm32f_data {
// MDC clock divider. MDC clock is derived from HCLK, must not exceed 2.5MHz
// HCLK range DIVIDER mdc_cr VALUE
// -------------------------------------
// -1 <-- tell driver to guess the value
// 60-100 MHz HCLK/42 0
// 100-150 MHz HCLK/62 1
// 20-35 MHz HCLK/16 2
// 35-60 MHz HCLK/26 3
// 150-216 MHz HCLK/102 4 <-- value for Nucleo-F* on max speed
// 216-310 MHz HCLK/124 5
// 110, 111 Reserved
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
uint8_t phy_addr; // PHY address
};
#ifndef MG_TCPIP_PHY_ADDR
#define MG_TCPIP_PHY_ADDR 0
#endif
#ifndef MG_DRIVER_MDC_CR
#define MG_DRIVER_MDC_CR 4
#endif
#if MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
#define MG_ENABLE_ETH_IRQ() NVIC_EnableIRQ(ETH_IRQn)
#elif !defined(MG_ENABLE_ETH_IRQ)
#define MG_ENABLE_ETH_IRQ()
#endif
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_stm32f_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.driver = &mg_tcpip_driver_stm32f; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
MG_ENABLE_ETH_IRQ(); \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: stm32f, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#endif
#if MG_ENABLE_TCPIP
#if !defined(MG_ENABLE_DRIVER_STM32H)
#define MG_ENABLE_DRIVER_STM32H 0
#endif
#if !defined(MG_ENABLE_DRIVER_MCXN)
#define MG_ENABLE_DRIVER_MCXN 0
#endif
#if !defined(MG_ENABLE_DRIVER_STM32N)
#define MG_ENABLE_DRIVER_STM32N 0
#endif
#if MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN || MG_ENABLE_DRIVER_STM32N
struct mg_tcpip_driver_stm32h_data {
// MDC clock divider. MDC clock is derived from HCLK, must not exceed 2.5MHz
// HCLK range DIVIDER mdc_cr VALUE
// -------------------------------------
// -1 <-- tell driver to guess the value
// 60-100 MHz HCLK/42 0
// 100-150 MHz HCLK/62 1
// 20-35 MHz HCLK/16 2
// 35-60 MHz HCLK/26 3
// 150-250 MHz HCLK/102 4 <-- value for max speed HSI
// 250-300 MHz HCLK/124 5 <-- value for Nucleo-H* on CSI
// 300-500 MHz HCLK/204 6
// 500-800 MHz HCLK/324 7
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
uint8_t phy_addr; // PHY address
uint8_t phy_conf; // PHY config
};
#ifndef MG_TCPIP_PHY_CONF
#define MG_TCPIP_PHY_CONF MG_PHY_CLOCKS_MAC
#endif
#ifndef MG_TCPIP_PHY_ADDR
#define MG_TCPIP_PHY_ADDR 0
#endif
#ifndef MG_DRIVER_MDC_CR
#define MG_DRIVER_MDC_CR 4
#endif
#if MG_ENABLE_DRIVER_STM32H && MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
#define MG_ENABLE_ETH_IRQ() NVIC_EnableIRQ(ETH_IRQn)
#elif MG_ENABLE_DRIVER_STM32N && MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
#define MG_ENABLE_ETH_IRQ() NVIC_EnableIRQ(ETH1_IRQn)
#elif !defined(MG_ENABLE_ETH_IRQ)
#define MG_ENABLE_ETH_IRQ()
#endif
#if MG_ENABLE_IPV6
#define MG_IPV6_INIT(mif) \
do { \
memcpy(mif.ip6ll, (uint8_t[16]) MG_TCPIP_IPV6_LINKLOCAL, 16); \
memcpy(mif.ip6, (uint8_t[16]) MG_TCPIP_GLOBAL, 16); \
memcpy(mif.gw6, (uint8_t[16]) MG_TCPIP_GW6, 16); \
mif.prefix_len = MG_TCPIP_PREFIX_LEN; \
} while(0)
#else
#define MG_IPV6_INIT(mif)
#endif
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_stm32h_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
driver_data_.phy_conf = MG_TCPIP_PHY_CONF; \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.driver = &mg_tcpip_driver_stm32h; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
MG_IPV6_INIT(mif_); \
mg_tcpip_init(mgr, &mif_); \
MG_ENABLE_ETH_IRQ(); \
MG_INFO(("Driver: stm32h, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#endif
#endif
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_TM4C) && MG_ENABLE_DRIVER_TM4C
struct mg_tcpip_driver_tm4c_data {
// MDC clock divider. MDC clock is derived from SYSCLK, must not exceed 2.5MHz
// SYSCLK range DIVIDER mdc_cr VALUE
// -------------------------------------
// -1 <-- tell driver to guess the value
// 60-100 MHz SYSCLK/42 0
// 100-150 MHz SYSCLK/62 1 <-- value for EK-TM4C129* on max speed
// 20-35 MHz SYSCLK/16 2
// 35-60 MHz SYSCLK/26 3
// 0x4-0xF Reserved
int mdc_cr; // Valid values: -1, 0, 1, 2, 3
};
#ifndef MG_DRIVER_MDC_CR
#define MG_DRIVER_MDC_CR 1
#endif
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_tm4c_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.driver = &mg_tcpip_driver_tm4c; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: tm4c, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#endif
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_TMS570) && MG_ENABLE_DRIVER_TMS570
struct mg_tcpip_driver_tms570_data {
int mdc_cr;
int phy_addr;
};
#ifndef MG_TCPIP_PHY_ADDR
#define MG_TCPIP_PHY_ADDR 0
#endif
#ifndef MG_DRIVER_MDC_CR
#define MG_DRIVER_MDC_CR 1
#endif
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_tms570_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.driver = &mg_tcpip_driver_tms570; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: tms570, MAC: %M", mg_print_mac, mif_.mac));\
} while (0)
#endif
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC) && MG_ENABLE_DRIVER_XMC
struct mg_tcpip_driver_xmc_data {
// 13.2.8.1 Station Management Functions
// MDC clock divider (). MDC clock is derived from ETH MAC clock
// It must not exceed 2.5MHz
// ETH Clock range DIVIDER mdc_cr VALUE
// --------------------------------------------
// -1 <-- tell driver to guess the value
// 60-100 MHz ETH Clock/42 0
// 100-150 MHz ETH Clock/62 1
// 20-35 MHz ETH Clock/16 2
// 35-60 MHz ETH Clock/26 3
// 150-250 MHz ETH Clock/102 4
// 250-300 MHz ETH Clock/124 5
// 110, 111 Reserved
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
uint8_t phy_addr;
};
#ifndef MG_TCPIP_PHY_ADDR
#define MG_TCPIP_PHY_ADDR 0
#endif
#ifndef MG_DRIVER_MDC_CR
#define MG_DRIVER_MDC_CR 4
#endif
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_xmc_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.driver = &mg_tcpip_driver_xmc; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: xmc, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#endif
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_XMC7) && MG_ENABLE_DRIVER_XMC7
struct mg_tcpip_driver_xmc7_data {
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
uint8_t phy_addr;
};
#ifndef MG_TCPIP_PHY_ADDR
#define MG_TCPIP_PHY_ADDR 0
#endif
#ifndef MG_DRIVER_MDC_CR
#define MG_DRIVER_MDC_CR 3
#endif
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_xmc7_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.mdc_cr = MG_DRIVER_MDC_CR; \
driver_data_.phy_addr = MG_TCPIP_PHY_ADDR; \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.driver = &mg_tcpip_driver_xmc7; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: xmc7, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#endif
#ifdef __cplusplus
}
#endif
#endif // MONGOOSE_H