Compare commits

..
1 Commits
Author SHA1 Message Date
cpq f12e44b0b1 Add frdm-mcxw71 support 2026-10-20 10:40:07 +01:00
101 changed files with 2638 additions and 5493 deletions

No files matched your search

+4 -8
View File
@@ -1,12 +1,8 @@
# Mongoose: a full-stack embedded networking library
Mongoose is a full-stack embedded networking library for C/C++: a TCP/IP stack
with drivers, TLS 1.3, an embedded web server (HTTP, WebSocket), MQTT and
firmware OTA updates in two files, `mongoose.c` and `mongoose.h`. Mongoose runs on
STM32, NXP and other microcontrollers, on bare metal or with an RTOS, with no
external dependencies. Use it for cloud connectivity, device web dashboards,
secure firmware updates and remote control.
# Mongoose - Embedded Network Library
Mongoose is an open source, two-file C networking library and embedded web server for
microcontrollers that combines TCP/IP stack, HTTP, WebSocket, MQTT, TLS 1.3
stack, built-in firmware OTA updates, and device-dashboard infrastructure.
License: GPLv2 or commercial.
Website: https://mongoose.ws/
GitHub repo: https://github.com/cesanta/mongoose
+27 -51
View File
@@ -1,4 +1,4 @@
# Mongoose: a full-stack embedded networking library
# Mongoose - Embedded Web Server / Embedded Network Library
[![License: GPLv2/Commercial](https://img.shields.io/badge/License-GPLv2%20or%20Commercial-green.svg)](https://opensource.org/licenses/gpl-2.0.php)
[![Build Status]( https://github.com/cesanta/mongoose/actions/workflows/quicktest.yml/badge.svg)](https://github.com/cesanta/mongoose/actions)
@@ -7,17 +7,11 @@
<img src="https://mongoose.ws/images/logo.svg" width="48" height="48"
align="left" style="float:left;" />
Mongoose is a full-stack embedded networking library for C/C++: a TCP/IP stack
with drivers, TLS 1.3, an embedded web server (HTTP, WebSocket), MQTT and
firmware OTA updates in two files, `mongoose.c` and `mongoose.h`. Mongoose runs on
STM32, NXP and other microcontrollers, on bare metal or with an RTOS, with no
external dependencies. Use it for cloud connectivity, device web dashboards,
secure firmware updates and remote control.
Two files. Drop `mongoose.c` and `mongoose.h` into any C/C++ project and get a
full network stack — HTTP, WebSocket, MQTT, TLS, firmware OTA
updates, device dashboard — on any microcontroller or desktop OS.
Use it instead of lwIP + mbedTLS + httpd + an MQTT client + OTA glue code, or
run it on top of lwIP or Zephyr.
On the market since 2004, used by a vast number of open source and commercial
On the market since 2004, used by vast number of open source and commercial
products - it even runs on the International Space Station! Features include:
- Cross-platform:
@@ -30,7 +24,7 @@ products - it even runs on the International Space Station! Features include:
- Source code is both ISO C and ISO C++ compliant
- Very easy to integrate
- Own TCP/IP stack for bare metal / RTOS, or run on top of any existing one (lwIP, Zephyr, ...)
- Built-in TLS 1.3 stack. Also can use external TLS libraries - mbedTLS, OpenSSL, or other
- Built-in TLS 1.3 ECC stack. Also can use external TLS libraries - mbedTLS, OpenSSL, or other
- Does not depend on any other software to implement networking
- Built-in firmware updates for STM32 H5, STM32 H7, NXP IMXRT, RP2040/2350, ESP32 and more
- Commercial support with proactive security updates; [CRA-compliant](https://mongoose.ws/security/cra-compliance/)
@@ -41,32 +35,8 @@ See https://mongoose.ws/ for complete documentation, videos, case studies, etc.
## Supported platforms
Mongoose has its own built-in TCP/IP stack with drivers, so it needs no
additional software to implement networking. The built-in stack runs on bare
metal and under any RTOS. Platforms supported by the Mongoose built-in TCP/IP
stack:
| MCU | Connectivity | Notes |
| :-- | :----------- | :---- |
| **STM32** | Built-in Ethernet | Ethernet-capable STM32F, STM32H5, STM32H7, STM32N6, STM32C5 variants |
| **NXP i.MX RT** | Built-in Ethernet | RT102x, RT104x, RT105x, RT106x, RT117x, RT118x (NETC) |
| **NXP MCX** | Built-in Ethernet | MCXN94x, MCXE247 |
| **NXP RW612** | Eth, WiFi, 802.15.4 | Separate Ethernet and radio drivers |
| **Microchip ATSAME54** | Built-in Ethernet | GMAC driver |
| **Renesas RA** | Built-in Ethernet | RA6M, RA8M |
| **Infineon XMC** | Built-in Ethernet | XMC4, XMC7 |
| **Texas Instruments** | Built-in Ethernet | TM4C, TMS570 |
| **RP2040, RP2350, STM32, others** | CYW43xx Wi-Fi chips | SPI or SDIO; Pico W, Pico 2 W, Pico RM2, Arduino Portenta H7 |
| **STM32, others** | ST67W6 Wi-Fi modules | SPI; for example, STM32U5 with the ST67W611M1 module |
| **MCUs with an NXP Wi-Fi SDK port** | NXP Wi-Fi | Requires the SDK's Wi-Fi port implementing the driver's network hooks |
| **NXP MCXW71** | Built-in 802.15.4 radio | IPv4 over IEEE 802.15.4 |
| **NXP RW612** | Built-in 802.15.4 radio | IPv4 over IEEE 802.15.4; requires NXP radio firmware |
| **Any MCU with SPI** | W5500 or W5100 Ethernet chips | External Wiznet MAC+PHY |
| **MCUs connected to a cellular modem** | Cellular | AT commands and PPP; for example, SIM800 |
| **MCUs with CMSIS Ethernet drivers** | Ethernet via CMSIS-Driver | Requires CMSIS Ethernet MAC and PHY drivers |
Mongoose can also run on top of any TCP/IP stack that supports the BSD
sockets API. Platforms supported via 3rd party TCP/IP stacks:
Mongoose can work on top of any TCP/IP stack that supports BSD sockets API.
Platforms supported by the 3rd party TCP/IP stacks:
| TCP/IP stack | Notes |
| :-------------- | :------- |
@@ -75,6 +45,23 @@ sockets API. Platforms supported via 3rd party TCP/IP stacks:
| **Other** | Any other TCP/IP stack that supports BSD socket API, for example Amazon FreeRTOS-TCP |
| **Linux, Mac, Windows** | Workstations, server, single board computers, embedded Linux devices running on MPUs or FPGAs |
Optionally, Mongoose provides its own built-in TCP/IP stack, eliminating the
need for additional software to implement networking functionality. The
built-in stack supports operation in both bare-metal and RTOS environments.
Platforms supported by the Mongoose built-in TCP/IP stack:
| Hardware | Notes |
| :------------- | :------- |
| **STM32** | All STM32 MCUs with built-in Ethernet: STM32Fxx, STM32H5xx, STM32H7xx |
| **NXP** | All NXP MCUs with built-in Ethernet: IMXRT102x, IMXRT104x, IMXRT105x, IMXRT106x, IMXRT117x, RW612, MCXN94x |
| **Microchip** | ATSAME54 MCUs with built-int Ethernet |
| **Renesas** | RA5M, RA6M, RA8M MCUs with built-in Ethernet |
| **Infineon** | XMC4, XMC7 MCUs with built-in Ethernet |
| **Texas Instruments** | TM4C, TMS570 MCUs with built-in Ethernet |
| **Cypress WiFi** | Any MCU with CY43xx WiFi chips, like RP2040 Pico-W, RP2350 Pico2-W, Arduino Portenta |
| **Wiznet Ethernet** | Any MCU that use Wiznet W5500 or Wiznet 5100 MAC+PHY chips |
| **Cellular** | NRF9160, SIM800 |
## Usage Examples
Below are quick snippets that should give an idea how simple the API is and
@@ -171,17 +158,6 @@ int main() {
}
```
## Licensing
Mongoose is dual-licensed:
- GPLv2: free for open source projects. Many popular open source projects use
Mongoose, for example
[SWUpdate](https://github.com/sbabic/swupdate/tree/master/mongoose), the
embedded Linux update framework
- Commercial: for closed-source products. Includes support directly from the
engineers who wrote the whole stack, and early security fixes.
See [licensing](https://mongoose.ws/licensing/)
## Commercial use
- Mongoose is used by hundreds of businesses, from Fortune500 giants like
Siemens, Schneider Electric, Broadcom, Bosch, Google, Samsung, Qualcomm, Caterpillar to the small businesses
@@ -246,12 +222,12 @@ Technical guides and deep dives into embedded web servers, WebUI integration and
- [lwIP vs Mongoose - TCP/IP Stack Integration Benchmark](https://mongoose.ws/articles/lwip-vs-mongoose-tcpip-stack-integration/)
- [Embedded TCP/IP Stacks Comparison](https://mongoose.ws/embedded-tcpip-stack-comparison/)
- [STM32H5 File System on Internal Flash: LittleFS + Mongoose](https://mongoose.ws/articles/stm32h5-file-system-on-internal-flash/)
- [Firmware OTA and remote control on NXP FRDM-RW612](https://mongoose.ws/articles/rw612-mdash/)
- [Firmware OTA and remote control on NXP FRDM-RW612](https://mongoose.ws/articles/mdash/)
- [25000 WebSocket messages per second on STM32 Nucleo-H723ZG](https://mongoose.ws/articles/stm32-websocket-benchmark/)
- [How to connect STM32 to AWS IoT Core](https://mongoose.ws/articles/stm32-aws-iot/)
- [How to connect STM32 to Azure IoT Hub](https://mongoose.ws/articles/stm32-azure-iot-hub/)
- [How to connect STM32 to EMQX](https://mongoose.ws/articles/stm32-emqx/)
- [STM32 Device Management deep dive: Secure OTA, Remote Control, health monitor](https://mongoose.ws/articles/stm32-mdash-deep-dive/)
- [STM32 Device Management deep dive: Secure OTA, Remote Control, health monitor](https://mongoose.ws/articles/stm32-mdash-deep-dive/")
- [STM32 TCP/IP footprint: how much flash and RAM does Mongoose take?](https://mongoose.ws/articles/tcp-ip-footprint/)
-41
View File
@@ -1,41 +0,0 @@
# Security Policy
Mongoose is a full-stack embedded networking library by Cesanta Software Ltd,
<https://mongoose.ws>. This policy covers how to report vulnerabilities
and how Cesanta handles them.
## Supported versions
Security fixes go to the `master` branch and the latest release.
## Reporting a vulnerability
Do not report security issues in public GitHub issues. Report them
privately using GitHub Security Advisories:
<https://github.com/cesanta/mongoose/security/advisories/new>
Include the Mongoose version or commit, steps or a proof of concept to reproduce, and
the impact. Cesanta usually responds the same business day.
## Disclosure process
1. Cesanta reviews the report and coordinates a fix with the reporter.
2. Eligible commercial customers receive a notification and a private fix
one month before the CVE becomes public.
3. The fix and the CVE are then published. The reporter is credited in the
GitHub Security Advisory.
## Security testing
- CI on every push, unit tests built with ASAN and UBSan
- CodeQL static analysis on every commit
- Continuous fuzzing on OSS-Fuzz, plus nightly libFuzzer runs
- AI-assisted security review on every pull request
- Regular review by independent security research groups worldwide
## EU Cyber Resilience Act (CRA)
Mongoose supports CRA compliance for connected products: a documented
vulnerability handling process (this file), TLS 1.3, signed OTA firmware
updates, and an SBOM (`sbom.cdx.json`). Details:
<https://mongoose.ws/security/cra-compliance/>
+1048 -219
View File
File diff suppressed because it is too large. Load diff
+184 -102
View File
@@ -165,7 +165,6 @@ extern "C" {
#if MG_ENABLE_TCPIP && !defined(MG_ENABLE_DRIVER_STM32F) && \
!defined(MG_ENABLE_DRIVER_STM32H) && !defined(MG_ENABLE_DRIVER_STM32N) && \
!defined(MG_ENABLE_DRIVER_STM32C) && \
!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)
@@ -174,8 +173,6 @@ extern "C" {
#define MG_ENABLE_DRIVER_STM32H 1
#elif defined(STM32N6)
#define MG_ENABLE_DRIVER_STM32N 1
#elif defined(STM32C5)
#define MG_ENABLE_DRIVER_STM32C 1
#else
#error Select a driver in mongoose_config.h
#endif
@@ -192,8 +189,70 @@ extern "C" {
#define MG_OTA MG_OTA_STM32H5
#elif !defined(MG_OTA) && defined(STM32H7)
#define MG_OTA MG_OTA_STM32H7
#elif !defined(MG_OTA) && defined(STM32C5)
#define MG_OTA MG_OTA_STM32C5
#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
@@ -1192,8 +1251,13 @@ struct timeval {
#endif
#ifndef MG_ENABLE_IPV6
#if (defined(MG_ENABLE_DRIVER_MCXW71) && MG_ENABLE_DRIVER_MCXW71) || \
(defined(MG_ENABLE_DRIVER_RW612_154) && MG_ENABLE_DRIVER_RW612_154)
#define MG_ENABLE_IPV6 1 // 802.15.4 radio: 6LoWPAN carries IPv6 only
#else
#define MG_ENABLE_IPV6 0
#endif
#endif
#ifndef MG_IPV6_V6ONLY
#define MG_IPV6_V6ONLY 0 // IPv6 socket binds only to V6, not V4 address
@@ -1338,6 +1402,8 @@ struct timeval {
#endif
// Set the interface hardware address. Ethernet: 6-byte MAC. 6LoWPAN: 8-byte
// EUI-64, the IPv6 interface ID is made from it. Not set: random per boot
#ifndef MG_SET_MAC_ADDRESS
#define MG_SET_MAC_ADDRESS(mac)
#endif
@@ -1379,6 +1445,7 @@ struct timeval {
// 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.
@@ -2724,32 +2791,29 @@ 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(). Always takes over the connection. Invalid requests receive
// an HTTP error; client-side failures call mg_error(). Replies and connection
// draining are handled internally, including callback failures.
// 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 exactly once, including validation
// failures before start and connections that drop early. Release resources
// here. A truncated body looks like a complete one: validate before
// committing.
// The callback returns true on success, false on error. A failure stops
// streaming immediately and calls the end callback before closing.
// - 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);
// 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:
// The existing event handler is no longer called. hm is invalid after this
// call; c->data and c->fn_data are reserved for streaming state.
void mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
// 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);
@@ -4178,7 +4242,6 @@ extern void mg_mqtt_poll(struct mg_mgr *);
#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_STM32C5 5 // STM32 C5, same flash controller as H5
#define MG_OTA_CH32V307 100 // WCH CH32V307
#define MG_OTA_U2A 200 // Renesas U2A16, U2A8, U2A6
#define MG_OTA_RT1020 300 // IMXRT1020
@@ -4223,8 +4286,9 @@ bool mg_ota_end(void);
// ```c
// if (ev == MG_EV_HTTP_HDRS) {
// struct mg_http_message *hm = (struct mg_http_message *) ev_data;
// if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
// mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
// if (mg_match(hm->uri, mg_str("/ota"), NULL) &&
// !mg_http_stream_body(c, hm, mg_http_ota_cb, NULL)) {
// mg_http_reply(c, 400, "", "POST firmware with Content-Length\n");
// }
// }
// ```
@@ -4377,11 +4441,11 @@ void mg_ota_poll(struct mg_mgr *);
#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) || defined(STM32C5))
#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) || defined(STM32C5))
#if !defined(MG_HEALTH_DATA_SIZE) && (defined(STM32H7_DEV_ID) || defined(STM32H5))
#define MG_HEALTH_DATA_SIZE 250
#endif
@@ -4542,7 +4606,13 @@ 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 };
enum mg_l2type {
MG_TCPIP_L2_ETH = 0,
MG_TCPIP_L2_PPP,
MG_TCPIP_L2_PPPoE,
MG_TCPIP_L2_LOWPAN,
MG_TCPIP_L2_USER
};
#if defined(__DCC__)
#pragma pack(1)
@@ -4565,10 +4635,27 @@ struct eth_data {
struct pppoe_data { // (struct eth_data *) &pppoe_data = pppoedata.eth
struct eth_data eth;
};
struct lowpan_data {
uint16_t pan_id; // User-set PAN ID
uint8_t channel; // User-set channel, 11..26
uint16_t short_addr; // Our short address, 0xfffe if none
uint8_t seq; // MAC sequence number
uint8_t proto; // Protocol of the frame being sent, enum mg_l2proto
uint8_t dst[8]; // Destination of the frame being sent
uint16_t tag; // Next fragmentation datagram tag
uint8_t *buf; // Reassembly and decompression buffer
uint16_t size; // Datagram being reassembled: size, 0 if none
uint16_t received; // Bytes received so far
uint16_t tag_rx; // Datagram tag
uint8_t src[8]; // Source address
bool udp; // UDP header was compressed
uint64_t expire; // Reassembly deadline, ms
};
union mg_l2data {
struct eth_data eth;
struct pppoe_data pppoe;
struct lowpan_data lowpan;
};
#if defined(__DCC__)
@@ -4577,7 +4664,6 @@ union mg_l2data {
#pragma pack(pop)
#endif
enum mg_l2proto {
MG_TCPIP_L2PROTO_IPV4 = 0,
MG_TCPIP_L2PROTO_IPV6,
@@ -4638,6 +4724,24 @@ enum {
};
// clang-format on
#if MG_ENABLE_IPV6
// IPv6 neighbour learned from forwarded packets, see mg_tcpip_route()
struct mg_tcpip_nb6 {
uint64_t ip[2]; // Neighbour IPv6 address
uint8_t l2[sizeof(struct mg_l2addr)]; // Its hardware address
};
// NAT64 session, see mg_tcpip_if::nat64
struct mg_tcpip_nat64 {
uint64_t ip6[2]; // Node's IPv6 address
uint32_t ip4; // IPv4 server address
uint16_t port6; // Node's port
uint16_t port4; // IPv4 server port
uint16_t lport; // Our port on the IPv4 interface
uint8_t proto; // 6: TCP, 17: UDP, 0: unused entry
};
#endif
// 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.
@@ -4672,6 +4776,8 @@ struct mg_tcpip_if {
uint8_t prefix[8]; // Global prefix bytes
uint8_t prefix_len; // Global prefix length in bits
uint64_t gw6[2]; // IPv6 default gateway
uint8_t nat64[12]; // NAT64 /96 prefix, RFC 6052. If set, DNS asks
// for IPv4 (A) and makes IPv6 from it (DNS64)
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
@@ -4703,6 +4809,13 @@ struct mg_tcpip_if {
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
struct mg_tcpip_if *route; // Forward to this interface, see mg_tcpip_route()
struct mg_tcpip_nb6 nb6[4]; // Neighbours learned from forwarded packets
uint8_t nb6_next; // Next nb6 entry to replace
struct mg_tcpip_nat64 nat64s[8]; // NAT64 sessions, if nat64 is set
uint8_t nat64_next; // Next nat64s entry to replace
uint64_t ra_expire; // Time of the next router advertisement, ms
char *dns6_url; // DNS server URL from router advertisement
#endif
};
@@ -4716,6 +4829,15 @@ 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 *);
#if MG_ENABLE_IPV6
// Forwards IPv6 packets between two interfaces: packets not addressed to us
// go out the other interface. An interface with a static IPv6 prefix (ip6,
// prefix and prefix_len set, SLAAC off) gets router advertisements for it,
// the other interface gets a route to it, RFC 4191. Call after both
// mg_tcpip_init(). Replies to the other side need its sender seen first
void mg_tcpip_route(struct mg_tcpip_if *if1, struct mg_tcpip_if *if2);
#endif
// 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
@@ -4751,39 +4873,6 @@ extern struct mg_tcpip_driver mg_tcpip_driver_netc;
extern struct mg_tcpip_driver mg_tcpip_driver_mcxw71;
extern struct mg_tcpip_driver mg_tcpip_driver_rw612_154;
// IEEE 802.15.4 radio channel and PAN ID. Settable in mongoose_config.h
#ifndef MG_L2_802154_CHANNEL
#define MG_L2_802154_CHANNEL 15 // 11..26
#endif
#ifndef MG_L2_802154_PAN_ID
#define MG_L2_802154_PAN_ID 0xabcd
#endif
// driver_data for IEEE 802.15.4 radio drivers. The interface is Ethernet:
// each Ethernet frame is split into broadcast radio frames, and reassembled
// on the other side, see mg_l2_802154_tx() and mg_l2_802154_rx()
struct mg_l2_802154_data {
uint8_t channel; // User-set channel, 11..26
uint16_t pan_id; // User-set PAN ID
uint8_t seq, tag; // MAC sequence number, tag of the last sent frame
uint8_t rxtag, rxnext; // Frame being reassembled: tag, next fragment
uint8_t rxsrc[8]; // Its sender
uint16_t rxlen; // Its bytes received so far
uint8_t rxbuf[1514]; // Reassembly buffer, one Ethernet frame
};
// Splits an Ethernet frame into radio frames, sends each by calling send().
// Called by radio drivers' tx(). Return len, or 0 on error
size_t mg_l2_802154_tx(struct mg_tcpip_if *ifp, const void *buf, size_t len,
size_t (*send)(const uint8_t *, size_t,
struct mg_tcpip_if *));
// Adds a received radio frame f of n bytes to the frame being reassembled.
// Called by radio drivers' rx(). Return the Ethernet frame length, copied to
// buf, when it is complete, otherwise 0
size_t mg_l2_802154_rx(struct mg_tcpip_if *ifp, const uint8_t *f, size_t n,
void *buf, size_t len);
// 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 {
@@ -5173,23 +5262,24 @@ struct mg_tcpip_driver_imxrt_data {
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_MCXW71) && \
MG_ENABLE_DRIVER_MCXW71
// NXP MCXW71 IEEE 802.15.4 radio, ZLL link layer, as an Ethernet interface:
// Ethernet frames are split into radio frames, see mg_l2_802154_tx().
// NXP MCXW71 IEEE 802.15.4 radio, ZLL link layer. Frames passed to tx() and
// returned by rx() are raw 802.15.4 frames: MAC header and payload, no FCS.
// The transceiver (XCVR) must be set up for 802.15.4 before mg_tcpip_init(),
// see hal_radio_init() in the FRDM-MCXW71 tutorial.
// driver_data: struct mg_l2_802154_data, channel and PAN ID set
// Use with MG_TCPIP_L2_LOWPAN: channel, PAN ID and addresses are taken from
// ifp->l2data.lowpan and ifp->mac. No driver_data
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_l2_802154_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.channel = MG_L2_802154_CHANNEL; \
driver_data_.pan_id = MG_L2_802154_PAN_ID; \
mif_.driver = &mg_tcpip_driver_mcxw71; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: mcxw71, channel %d", MG_L2_802154_CHANNEL)); \
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_if mif_; \
mif_.l2type = MG_TCPIP_L2_LOWPAN; \
mif_.driver = &mg_tcpip_driver_mcxw71; \
mif_.l2data.lowpan.channel = 15; \
mif_.l2data.lowpan.pan_id = 0xabcd; \
MG_SET_MAC_ADDRESS(mif_.mac); /* EUI-64 */ \
mg_tcpip_init(mgr, &mif_); \
(mgr)->use_dns6 = true; /* IPv6 only: no IPv4 over 6LoWPAN */ \
MG_INFO(("Driver: mcxw71, 6LoWPAN")); \
} while (0)
#endif
@@ -5416,26 +5506,27 @@ struct mg_tcpip_driver_rw612_data {
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RW612_154) && \
MG_ENABLE_DRIVER_RW612_154
// NXP RW612 IEEE 802.15.4 radio, as an Ethernet interface: Ethernet frames
// are split into radio frames, see mg_l2_802154_tx(). The radio runs on
// CPU2: NXP BLE/802.15.4 combo firmware, an OpenThread radio co-processor
// (RCP). The firmware must be loaded and running before mg_tcpip_init(), see
// the FRDM-RW612 eth_radio_bridge tutorial. The driver talks to it over the
// CPU2 <-> CPU3 IMU mailbox: IMUMC messages carrying HDLC framed Spinel.
// driver_data: struct mg_l2_802154_data, channel and PAN ID set
// NXP RW612 IEEE 802.15.4 radio. The radio runs on CPU2: NXP BLE/802.15.4
// combo firmware, an OpenThread radio co-processor (RCP). The firmware must
// be loaded and running before mg_tcpip_init(), see the FRDM-RW612 lowpan
// tutorial. The driver talks to it over the CPU2 <-> CPU3 IMU mailbox:
// IMUMC messages carrying HDLC framed Spinel. Frames passed to tx() and
// returned by rx() are raw 802.15.4 frames: MAC header and payload, no FCS.
// Use with MG_TCPIP_L2_LOWPAN: channel, PAN ID and addresses are taken from
// ifp->l2data.lowpan and ifp->mac. No driver_data
#ifndef MG_TCPIP_DRIVER_INIT
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_l2_802154_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.channel = MG_L2_802154_CHANNEL; \
driver_data_.pan_id = MG_L2_802154_PAN_ID; \
mif_.driver = &mg_tcpip_driver_rw612_154; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: rw612_154, channel %d", MG_L2_802154_CHANNEL)); \
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_if mif_; \
mif_.l2type = MG_TCPIP_L2_LOWPAN; \
mif_.driver = &mg_tcpip_driver_rw612_154; \
mif_.l2data.lowpan.channel = 15; \
mif_.l2data.lowpan.pan_id = 0xabcd; \
MG_SET_MAC_ADDRESS(mif_.mac); /* EUI-64 */ \
mg_tcpip_init(mgr, &mif_); \
(mgr)->use_dns6 = true; /* IPv6 only: no IPv4 over 6LoWPAN */ \
MG_INFO(("Driver: rw612_154, 6LoWPAN")); \
} while (0)
#endif
@@ -5603,13 +5694,6 @@ struct mg_tcpip_driver_stm32f_data {
#if !defined(MG_ENABLE_DRIVER_STM32H)
#define MG_ENABLE_DRIVER_STM32H 0
#endif
#if !defined(MG_ENABLE_DRIVER_STM32C)
#define MG_ENABLE_DRIVER_STM32C 0
#endif
#if MG_ENABLE_DRIVER_STM32C // STM32C5: STM32H5 MAC, the IRQ is ETH1
#undef MG_ENABLE_DRIVER_STM32H
#define MG_ENABLE_DRIVER_STM32H 1
#endif
#if !defined(MG_ENABLE_DRIVER_MCXN)
#define MG_ENABLE_DRIVER_MCXN 0
#endif
@@ -5649,11 +5733,9 @@ struct mg_tcpip_driver_stm32h_data {
#define MG_DRIVER_MDC_CR 4
#endif
#if MG_ENABLE_DRIVER_STM32H && !MG_ENABLE_DRIVER_STM32C && \
MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
#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_ENABLE_DRIVER_STM32C) && \
MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
#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()
@@ -15,7 +15,6 @@
</environment>
</environments>
<releases>
<release date="2026-10-21" version="7.23.3">See https://github.com/cesanta/mongoose/releases/tag/7.23</release>
<release date="2026-10-19" version="7.23.2">See https://github.com/cesanta/mongoose/releases/tag/7.23</release>
<release date="2026-09-28" version="7.23.1">See https://github.com/cesanta/mongoose/releases/tag/7.23</release>
<release date="2026-05-11" version="7.21.7">See https://github.com/cesanta/mongoose/releases/tag/7.21</release>
@@ -29,7 +28,7 @@
<keyword>IMPLEMENTED_WITH_STM32CUBEMX</keyword>
</keywords>
<components>
<component Cclass="Mongoose" Cgroup="Networking Core" Cversion="7.23.3" maxInstances="1">
<component Cclass="Mongoose" Cgroup="Networking Core" Cversion="7.23.2" maxInstances="1">
<description>Networking Core</description>
<files>
<file category="doc" name="README.md"/>
+1 -1
View File
@@ -1,4 +1,4 @@
VERSION = 7.23.3
VERSION = 7.23.2
REPO_ROOT = ../..
MX ?= /Applications/STMicroelectronics/STM32CubeMX.app/Contents/MacOs/STM32CubeMX
FILES = Cesanta.I-CUBE-Mongoose.pdsc Release_Notes.html mongoose_config.h README.md .config/mongoose_parameters.json
+64 -5
View File
@@ -32,7 +32,6 @@
#if MG_ENABLE_TCPIP && !defined(MG_ENABLE_DRIVER_STM32F) && \
!defined(MG_ENABLE_DRIVER_STM32H) && !defined(MG_ENABLE_DRIVER_STM32N) && \
!defined(MG_ENABLE_DRIVER_STM32C) && \
!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)
@@ -41,8 +40,6 @@
#define MG_ENABLE_DRIVER_STM32H 1
#elif defined(STM32N6)
#define MG_ENABLE_DRIVER_STM32N 1
#elif defined(STM32C5)
#define MG_ENABLE_DRIVER_STM32C 1
#else
#error Select a driver in mongoose_config.h
#endif
@@ -59,8 +56,70 @@
#define MG_OTA MG_OTA_STM32H5
#elif !defined(MG_OTA) && defined(STM32H7)
#define MG_OTA MG_OTA_STM32H7
#elif !defined(MG_OTA) && defined(STM32C5)
#define MG_OTA MG_OTA_STM32C5
#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
+8
View File
@@ -65,8 +65,13 @@
#endif
#ifndef MG_ENABLE_IPV6
#if (defined(MG_ENABLE_DRIVER_MCXW71) && MG_ENABLE_DRIVER_MCXW71) || \
(defined(MG_ENABLE_DRIVER_RW612_154) && MG_ENABLE_DRIVER_RW612_154)
#define MG_ENABLE_IPV6 1 // 802.15.4 radio: 6LoWPAN carries IPv6 only
#else
#define MG_ENABLE_IPV6 0
#endif
#endif
#ifndef MG_IPV6_V6ONLY
#define MG_IPV6_V6ONLY 0 // IPv6 socket binds only to V6, not V4 address
@@ -211,6 +216,8 @@
#endif
// Set the interface hardware address. Ethernet: 6-byte MAC. 6LoWPAN: 8-byte
// EUI-64, the IPv6 interface ID is made from it. Not set: random per boot
#ifndef MG_SET_MAC_ADDRESS
#define MG_SET_MAC_ADDRESS(mac)
#endif
@@ -248,3 +255,4 @@
#ifndef MG_ENABLE_HEALTH
#define MG_ENABLE_HEALTH 0
#endif
+3 -3
View File
@@ -579,9 +579,9 @@ void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
mg_serve_fs(c, hm, u, true);
return; // Important - the serve_fs() call can invalidate hm
} else if (mg_match(hm->uri, mg_str("/api/ota"), NULL)) {
// Authenticated above. Streaming owns the connection and c->data.
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
return;
// Authenticated above. If streaming started, the stream handler owns
// the connection and c->data, so skip the marker check below
if (mg_http_stream_body(c, hm, mg_http_ota_cb, NULL)) return;
}
if (d->marker != '\0') mg_log_http_req(c, hm);
} else if (ev == MG_EV_HTTP_MSG && d->marker != '\0') {
+22 -1
View File
@@ -173,6 +173,15 @@ bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm) {
return true;
}
#if MG_ENABLE_TCPIP && MG_ENABLE_IPV6
// NAT64 prefix of the built-in stack interface, or NULL if none
static const uint8_t *nat64_prefix(struct mg_mgr *mgr) {
static const uint8_t zero[12] = {0};
if (mgr->ifp == NULL || memcmp(mgr->ifp->nat64, zero, 12) == 0) return NULL;
return mgr->ifp->nat64;
}
#endif
static void dns_cb(struct mg_connection *c, int ev, void *ev_data) {
struct dns_data *d, *tmp;
struct dns_data **head = (struct dns_data **) &c->mgr->active_dns_requests;
@@ -196,6 +205,14 @@ static void dns_cb(struct mg_connection *c, int ev, void *ev_data) {
// MG_INFO(("d %p %hu %hu", d, d->txnid, dm.txnid));
if (dm.txnid != d->txnid) continue;
if (d->c->is_resolving) {
#if MG_ENABLE_TCPIP && MG_ENABLE_IPV6
if (dm.resolved && !dm.addr.is_ip6 && nat64_prefix(c->mgr) != NULL) {
uint32_t ip4 = dm.addr.addr.ip4; // DNS64: prefix + IPv4, RFC 6052
memcpy(dm.addr.addr.ip, nat64_prefix(c->mgr), 12);
memcpy(&dm.addr.addr.ip[12], &ip4, 4);
dm.addr.is_ip6 = true;
}
#endif
if (dm.resolved) {
dm.addr.port = d->c->rem.port; // Save port
d->c->rem = dm.addr; // Copy resolved address
@@ -314,7 +331,11 @@ void mg_resolve(struct mg_connection *c, const char *url) {
} else {
// host is not an IP nor a .local, send DNS resolution request
struct mg_dns *dns = c->mgr->use_dns6 ? &c->mgr->dns6 : &c->mgr->dns4;
sendnsreq(c, &host, c->mgr->dnstimeout, dns, c->mgr->use_dns6);
bool aaaa = c->mgr->use_dns6;
#if MG_ENABLE_TCPIP && MG_ENABLE_IPV6
if (nat64_prefix(c->mgr) != NULL) aaaa = false; // DNS64: ask for A
#endif
sendnsreq(c, &host, c->mgr->dnstimeout, dns, aaaa);
}
}
+74 -36
View File
@@ -17,8 +17,6 @@ struct mg_zll {
PART_ID, COEX_CTRL, COEX_PRIORITY, ENHACK_CTRL0;
};
// TrustZone: code built for the secure state (-mcmse) uses the secure alias
// of the radio registers, 0x5xxxxxxx. Otherwise, the non-secure alias
#if defined(__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE & 2)
#define MGI_ZLL ((struct mg_zll *) 0x58A01000)
#define MGI_PKT_RAM ((volatile uint8_t *) 0x58A08000)
@@ -26,7 +24,6 @@ struct mg_zll {
#define MGI_ZLL ((struct mg_zll *) 0x48A01000)
#define MGI_PKT_RAM ((volatile uint8_t *) 0x48A08000)
#endif
#define MGI_TX_PB MGI_PKT_RAM // Length byte (incl. FCS), then frame
#define MGI_RX_PB (MGI_PKT_RAM + 0x80) // Received frame, no length byte
@@ -34,20 +31,35 @@ struct mg_zll {
#define MGI_IRQSTS_RXIRQ MG_BIT(2)
#define MGI_IRQSTS_RX_LEN(x) (((x) >> 24) & 0x7f) // Frame length incl. FCS
#define MGI_PHY_CTRL_XCVSEQ 7U
#define MGI_PHY_CTRL_AUTOACK MG_BIT(3)
#define MGI_PHY_CTRL_RXACKRQD MG_BIT(4)
#define MGI_PHY_CTRL_CCABFRTX MG_BIT(5)
#define MGI_PHY_CTRL_SLOTTED MG_BIT(6)
#define MGI_PHY_CTRL_PROMISCUOUS MG_BIT(25)
#define MGI_PHY_CTRL_ALL_IRQ_MASKS 0x8007ff00U // Bits 8..18, 31: mask all IRQs
#define MGI_SEQ_STATE_MASK 0x1fU
#define MGI_SEQ_STATE_RX_WU 0x10U // RX warm up
#define MGI_SEQ_CTRL_NO_RX_RECYCLE MG_BIT(5)
#define MGI_FRAME_FILTER 0x70bU // Beacon, data, command; versions 0..2
#define MGI_ACKDELAY_MASK 0x7fU
#define MGI_ACKDELAY_VALUE 0x7cU // -4, 802.15.4 turnaround. See PhyHwInit()
enum { MGI_SEQ_IDLE = 0, MGI_SEQ_RX = 1, MGI_SEQ_TX = 2 }; // PHY_CTRL[XCVSEQ]
#define MGI_TX_TIMEOUT 400U // Symbols, 16 us: 127 bytes
enum { MGI_SEQ_IDLE = 0, MGI_SEQ_RX = 1, MGI_SEQ_TX = 2, MGI_SEQ_TR = 4 }; // PHY_CTRL[XCVSEQ]
#define MGI_FCF_ACK_REQUEST 0x20U // First frame byte: acknowledgment request
#define MGI_TX_TIMEOUT 400U // Symbols, 16 us: 127 bytes, then ACK wait
#define MGI_TX_TRIES 10 // Tries for unacknowledged frames
#define MGI_TX_BACKOFF 64U // Symbols, 1 ms: backoff grows by this per try
// Symbol timer, 16 us, 24 bits
static uint32_t mg_mcxw71_symbols(void) {
return MGI_ZLL->EVENT_TMR >> 8;
}
static void mg_mcxw71_delay(uint32_t symbols) {
uint32_t start = mg_mcxw71_symbols();
while (((mg_mcxw71_symbols() - start) & 0xffffffU) < symbols) (void) 0;
}
// Wait for the sequence to end, up to MGI_TX_TIMEOUT symbols
static bool mg_mcxw71_wait(void) {
uint32_t start = mg_mcxw71_symbols();
@@ -60,8 +72,7 @@ static bool mg_mcxw71_wait(void) {
// Stop the current sequence, wait for the sequencer to go idle. See PhyAbort()
static void mg_mcxw71_idle(void) {
if (MGI_ZLL->PHY_CTRL & MGI_PHY_CTRL_XCVSEQ) {
while ((MGI_ZLL->SEQ_STATE & MGI_SEQ_STATE_MASK) == MGI_SEQ_STATE_RX_WU)
(void) 0;
while ((MGI_ZLL->SEQ_STATE & MGI_SEQ_STATE_MASK) == MGI_SEQ_STATE_RX_WU) (void) 0;
MGI_ZLL->PHY_CTRL &= ~MGI_PHY_CTRL_XCVSEQ;
while (MGI_ZLL->SEQ_STATE & MGI_SEQ_STATE_MASK) (void) 0;
}
@@ -74,61 +85,88 @@ static void mg_mcxw71_start(uint32_t seq) {
MGI_ZLL->PHY_CTRL |= seq;
}
// Program our addresses for hardware filtering and auto-ACK: PAN ID, short
// address and EUI-64 (little endian), when they change. The L2 sets them
static void mg_mcxw71_addrs(struct mg_tcpip_if *ifp) {
struct lowpan_data *l = &ifp->l2data.lowpan;
const uint8_t *e = ifp->mac;
uint32_t s = ((uint32_t) l->short_addr << 16) | l->pan_id;
uint32_t lo = (uint32_t) (e[7] | (e[6] << 8) | (e[5] << 16)) |
((uint32_t) e[4] << 24);
uint32_t hi = (uint32_t) (e[3] | (e[2] << 8) | (e[1] << 16)) |
((uint32_t) e[0] << 24);
if (MGI_ZLL->MACSHORTADDRS0 != s || MGI_ZLL->MACLONGADDRS0_LSB != lo ||
MGI_ZLL->MACLONGADDRS0_MSB != hi) {
mg_mcxw71_idle();
MGI_ZLL->MACSHORTADDRS0 = s;
MGI_ZLL->MACLONGADDRS0_LSB = lo, MGI_ZLL->MACLONGADDRS0_MSB = hi;
mg_mcxw71_start(MGI_SEQ_RX);
}
}
static bool mg_mcxw71_init(struct mg_tcpip_if *ifp) {
struct mg_l2_802154_data *d =
(struct mg_l2_802154_data *) ifp->driver_data;
if (d == NULL || d->channel < 11 || d->channel > 26) {
MG_ERROR(("Set driver_data, channel 11..26"));
uint8_t channel = ifp->l2data.lowpan.channel;
if (ifp->l2type != MG_TCPIP_L2_LOWPAN || channel < 11 || channel > 26) {
MG_ERROR(("Use MG_TCPIP_L2_LOWPAN, channel 11..26"));
return false;
}
MGI_ZLL->SEQ_CTRL_STS |= MGI_SEQ_CTRL_NO_RX_RECYCLE;
MGI_ZLL->PHY_CTRL = MGI_PHY_CTRL_ALL_IRQ_MASKS; // Mask all IRQs, we poll
// Mask all interrupts, we poll. Filter frames by address, auto-ACK
MGI_ZLL->PHY_CTRL = MGI_PHY_CTRL_ALL_IRQ_MASKS | MGI_PHY_CTRL_AUTOACK;
MGI_ZLL->IRQSTS = MGI_ZLL->IRQSTS;
MGI_ZLL->SAM_CTRL = 0;
MGI_ZLL->RX_FRAME_FILTER = MGI_FRAME_FILTER;
MGI_ZLL->ACKDELAY = (MGI_ZLL->ACKDELAY & ~MGI_ACKDELAY_MASK) | MGI_ACKDELAY_VALUE;
MGI_ZLL->TMR_PRESCALE = 5; // 16 us timebase, one symbol
MGI_ZLL->CHANNEL_NUM0 = d->channel;
MGI_ZLL->MACSHORTADDRS0 = (0xfffeU << 16) | d->pan_id; // Filter by PAN ID
MGI_ZLL->PA_PWR = 0x3f; // PA slices, max output power
MGI_ZLL->CHANNEL_NUM0 = channel;
MGI_ZLL->PA_PWR = 0x3f; // PA slices, max output power
mg_mcxw71_start(MGI_SEQ_RX);
mg_mcxw71_addrs(ifp);
return true;
}
// Send one radio frame: MAC header and payload, no FCS
static size_t mg_mcxw71_send(const uint8_t *p, size_t len,
struct mg_tcpip_if *ifp) {
size_t i;
bool ok;
mg_mcxw71_idle();
MGI_TX_PB[0] = (uint8_t) (len + 2); // FCS is added by hardware
for (i = 0; i < len; i++) MGI_TX_PB[i + 1] = p[i]; // 8-bit access only
mg_mcxw71_start(MGI_SEQ_TX);
ok = mg_mcxw71_wait();
mg_mcxw71_idle();
mg_mcxw71_start(MGI_SEQ_RX);
(void) ifp;
return ok ? len : 0;
}
static size_t mg_mcxw71_tx(const void *buf, size_t len,
struct mg_tcpip_if *ifp) {
return mg_l2_802154_tx(ifp, buf, len, mg_mcxw71_send);
const uint8_t *p = (const uint8_t *) buf;
size_t i;
bool ok = false, ack = len > 0 && (p[0] & MGI_FCF_ACK_REQUEST);
int tries;
if (len == 0 || len > 125) return 0; // 127 bytes max, incl. 2 bytes FCS
mg_mcxw71_idle();
MGI_TX_PB[0] = (uint8_t) (len + 2); // FCS is added by hardware
for (i = 0; i < len; i++) MGI_TX_PB[i + 1] = p[i]; // 8-bit access only
MGI_ZLL->PHY_CTRL &= ~(MGI_PHY_CTRL_CCABFRTX | MGI_PHY_CTRL_RXACKRQD | MGI_PHY_CTRL_SLOTTED);
if (ack) MGI_ZLL->PHY_CTRL |= MGI_PHY_CTRL_RXACKRQD;
// Frames with ACK request: transmit, then receive the ACK (TR sequence),
// retry with growing backoff. See PhyPdDataRequest(), PhyISR.c
for (tries = 0; tries < (ack ? MGI_TX_TRIES : 1) && !ok; tries++) {
if (tries > 0) mg_mcxw71_delay(MGI_TX_BACKOFF * (uint32_t) tries);
mg_mcxw71_start(ack ? MGI_SEQ_TR : MGI_SEQ_TX);
ok = mg_mcxw71_wait() && (!ack || (MGI_ZLL->IRQSTS & MGI_IRQSTS_RXIRQ));
mg_mcxw71_idle();
}
MGI_ZLL->PHY_CTRL &= ~MGI_PHY_CTRL_RXACKRQD;
mg_mcxw71_start(MGI_SEQ_RX);
if (!ok) MG_DEBUG(("TX failed, %d tries", tries));
(void) ifp;
return ok ? len : 0;
}
static size_t mg_mcxw71_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
uint32_t irqsts = MGI_ZLL->IRQSTS;
uint8_t f[125];
size_t i, n = 0;
mg_mcxw71_addrs(ifp);
if ((irqsts & MGI_IRQSTS_SEQIRQ) == 0) return 0; // Still receiving
if (irqsts & MGI_IRQSTS_RXIRQ) {
n = MGI_IRQSTS_RX_LEN(irqsts);
n = n >= 2 ? n - 2 : 0; // Strip FCS
if (n > sizeof(f)) n = sizeof(f);
for (i = 0; i < n; i++) f[i] = MGI_RX_PB[i];
if (n > len) n = len;
for (i = 0; i < n; i++) ((uint8_t *) buf)[i] = MGI_RX_PB[i];
}
mg_mcxw71_idle();
mg_mcxw71_start(MGI_SEQ_RX);
return mg_l2_802154_rx(ifp, f, n, buf, len);
(void) ifp;
return n;
}
static bool mg_mcxw71_poll(struct mg_tcpip_if *ifp, bool s1) {
+15 -14
View File
@@ -3,23 +3,24 @@
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_MCXW71) && \
MG_ENABLE_DRIVER_MCXW71
// NXP MCXW71 IEEE 802.15.4 radio, ZLL link layer, as an Ethernet interface:
// Ethernet frames are split into radio frames, see mg_l2_802154_tx().
// NXP MCXW71 IEEE 802.15.4 radio, ZLL link layer. Frames passed to tx() and
// returned by rx() are raw 802.15.4 frames: MAC header and payload, no FCS.
// The transceiver (XCVR) must be set up for 802.15.4 before mg_tcpip_init(),
// see hal_radio_init() in the FRDM-MCXW71 tutorial.
// driver_data: struct mg_l2_802154_data, channel and PAN ID set
// Use with MG_TCPIP_L2_LOWPAN: channel, PAN ID and addresses are taken from
// ifp->l2data.lowpan and ifp->mac. No driver_data
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_l2_802154_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.channel = MG_L2_802154_CHANNEL; \
driver_data_.pan_id = MG_L2_802154_PAN_ID; \
mif_.driver = &mg_tcpip_driver_mcxw71; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: mcxw71, channel %d", MG_L2_802154_CHANNEL)); \
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_if mif_; \
mif_.l2type = MG_TCPIP_L2_LOWPAN; \
mif_.driver = &mg_tcpip_driver_mcxw71; \
mif_.l2data.lowpan.channel = 15; \
mif_.l2data.lowpan.pan_id = 0xabcd; \
MG_SET_MAC_ADDRESS(mif_.mac); /* EUI-64 */ \
mg_tcpip_init(mgr, &mif_); \
(mgr)->use_dns6 = true; /* IPv6 only: no IPv4 over 6LoWPAN */ \
MG_INFO(("Driver: mcxw71, 6LoWPAN")); \
} while (0)
#endif
+50 -29
View File
@@ -73,6 +73,8 @@ enum {
MGI_PROP_NCP_VERSION = 2,
MGI_PROP_PHY_ENABLED = 0x20,
MGI_PROP_PHY_CHAN = 0x21,
MGI_PROP_LADDR = 0x34,
MGI_PROP_SADDR = 0x35,
MGI_PROP_PANID = 0x36,
MGI_PROP_RAW_STREAM = 0x37,
MGI_PROP_STREAM_RAW = 0x71
@@ -82,7 +84,7 @@ enum {
#define MGI_STATUS_RESET_MAX 127
#define MGI_TX_TIMEOUT_MS 200 // Wait for transmit done, incl. retries
#define MGI_RSP_TIMEOUT_MS 500
#define MGI_RXQ_CNT 16 // A whole Ethernet frame, 15 radio frames
#define MGI_RXQ_CNT 4
// Driver state, one radio
struct mg_rw612 {
@@ -101,6 +103,9 @@ struct mg_rw612 {
uint8_t rxq[MGI_RXQ_CNT][127]; // Received frames, no FCS
uint8_t rxlen[MGI_RXQ_CNT]; // Their lengths
uint8_t rxhead, rxtail; // Queue indexes
uint16_t pan_id, short_addr; // Addresses programmed in the RCP
uint8_t eui[8];
uint8_t channel;
};
static struct mg_rw612 s_rw612;
@@ -332,6 +337,31 @@ static bool mg_rw612_set(uint32_t prop, const uint8_t *val, size_t len) {
s_rw612.rsp_status == 0;
}
// Program channel, PAN ID, short address and EUI-64 when they change
static void mg_rw612_addrs(struct mg_tcpip_if *ifp) {
struct mg_rw612 *s = &s_rw612;
struct lowpan_data *l = &ifp->l2data.lowpan;
uint8_t v[2];
if (s->channel != l->channel) {
s->channel = l->channel;
if (!mg_rw612_set(MGI_PROP_PHY_CHAN, &l->channel, 1)) MG_ERROR(("chan"));
}
if (s->pan_id != l->pan_id) {
s->pan_id = l->pan_id;
v[0] = (uint8_t) l->pan_id, v[1] = (uint8_t) (l->pan_id >> 8);
if (!mg_rw612_set(MGI_PROP_PANID, v, 2)) MG_ERROR(("panid"));
}
if (s->short_addr != l->short_addr) {
s->short_addr = l->short_addr;
v[0] = (uint8_t) l->short_addr, v[1] = (uint8_t) (l->short_addr >> 8);
if (!mg_rw612_set(MGI_PROP_SADDR, v, 2)) MG_ERROR(("saddr"));
}
if (memcmp(s->eui, ifp->mac, 8) != 0) {
memcpy(s->eui, ifp->mac, 8);
if (!mg_rw612_set(MGI_PROP_LADDR, s->eui, 8)) MG_ERROR(("laddr"));
}
}
// Log an init error, return false
static bool mg_rw612_fail(const char *msg) {
MG_ERROR(("%s", msg));
@@ -340,12 +370,11 @@ static bool mg_rw612_fail(const char *msg) {
static bool mg_rw612_154_init(struct mg_tcpip_if *ifp) {
struct mg_rw612 *s = &s_rw612;
struct mg_l2_802154_data *d =
(struct mg_l2_802154_data *) ifp->driver_data;
uint8_t f[8], one = 1, pan[2];
uint8_t f[8], one = 1;
uint64_t expire;
if (d == NULL || d->channel < 11 || d->channel > 26) {
MG_ERROR(("Set driver_data, channel 11..26"));
if (ifp->l2type != MG_TCPIP_L2_LOWPAN || ifp->l2data.lowpan.channel < 11 ||
ifp->l2data.lowpan.channel > 26) {
MG_ERROR(("Use MG_TCPIP_L2_LOWPAN, channel 11..26"));
return false;
}
if (MGI_MBOX->magic[0] != MGI_SYNC_MAGIC) {
@@ -354,6 +383,7 @@ static bool mg_rw612_154_init(struct mg_tcpip_if *ifp) {
}
memset(s, 0, sizeof(*s));
s->free_bufs = 0xffff;
s->pan_id = s->short_addr = 0xffff; // Force programming
// Reset our send FIFO, drop stale messages. See IMU_Init()
MGI_IMU->CNTL |= MGI_IMU_FLUSH;
MGI_IMU->CNTL &= ~(MGI_IMU_FLUSH | MGI_IMU_WAIT_FOR_ACK);
@@ -385,31 +415,25 @@ static bool mg_rw612_154_init(struct mg_tcpip_if *ifp) {
if (!mg_rw612_set(MGI_PROP_PHY_ENABLED, &one, 1)) {
return mg_rw612_fail("PHY enable failed");
}
pan[0] = (uint8_t) d->pan_id, pan[1] = (uint8_t) (d->pan_id >> 8);
if (!mg_rw612_set(MGI_PROP_PHY_CHAN, &d->channel, 1) ||
!mg_rw612_set(MGI_PROP_PANID, pan, 2)) {
return mg_rw612_fail("Channel or PAN ID set failed");
}
mg_rw612_addrs(ifp);
if (!mg_rw612_set(MGI_PROP_RAW_STREAM, &one, 1)) {
return mg_rw612_fail("Raw stream enable failed");
}
return true;
}
// Send one radio frame: MAC header and payload, no FCS
static size_t mg_rw612_send(const uint8_t *p, size_t len,
struct mg_tcpip_if *ifp) {
struct mg_l2_802154_data *d =
(struct mg_l2_802154_data *) ifp->driver_data;
static size_t mg_rw612_154_tx(const void *buf, size_t len,
struct mg_tcpip_if *ifp) {
uint8_t f[3 + 2 + 127 + 4];
size_t n = 3;
if (len == 0 || len > 125) return 0; // 127 bytes max, incl. 2 bytes FCS
f[1] = MGI_CMD_SET, f[2] = MGI_PROP_STREAM_RAW;
f[n++] = (uint8_t) (len + 2), f[n++] = 0; // PSDU length, incl. FCS
memcpy(f + n, p, len), n += len;
memcpy(f + n, buf, len), n += len;
f[n++] = 0, f[n++] = 0; // FCS, filled in by the radio
f[n++] = d->channel;
f[n++] = ifp->l2data.lowpan.channel;
f[n++] = 4; // Max CSMA backoffs
f[n++] = 0; // Max frame retries: broadcast frames are not acknowledged
f[n++] = 3; // Max frame retries, for frames with ACK request
f[n++] = 1; // CSMA-CA enabled
if (!mg_rw612_request(f, n, MGI_TX_TIMEOUT_MS)) {
MG_ERROR(("TX timeout"));
@@ -422,21 +446,18 @@ static size_t mg_rw612_send(const uint8_t *p, size_t len,
return len;
}
static size_t mg_rw612_154_tx(const void *buf, size_t len,
struct mg_tcpip_if *ifp) {
return mg_l2_802154_tx(ifp, buf, len, mg_rw612_send);
}
static size_t mg_rw612_154_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
struct mg_rw612 *s = &s_rw612;
size_t res = 0;
size_t n = 0;
mg_rw612_poll_imu();
while (s->rxtail != s->rxhead && res == 0) {
res = mg_l2_802154_rx(ifp, s->rxq[s->rxtail], s->rxlen[s->rxtail], buf,
len);
mg_rw612_addrs(ifp);
if (s->rxtail != s->rxhead) {
n = s->rxlen[s->rxtail];
if (n > len) n = len;
memcpy(buf, s->rxq[s->rxtail], n);
s->rxtail = (uint8_t) ((s->rxtail + 1) % MGI_RXQ_CNT);
}
return res;
return n;
}
static bool mg_rw612_154_poll(struct mg_tcpip_if *ifp, bool s1) {
+19 -18
View File
@@ -3,26 +3,27 @@
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RW612_154) && \
MG_ENABLE_DRIVER_RW612_154
// NXP RW612 IEEE 802.15.4 radio, as an Ethernet interface: Ethernet frames
// are split into radio frames, see mg_l2_802154_tx(). The radio runs on
// CPU2: NXP BLE/802.15.4 combo firmware, an OpenThread radio co-processor
// (RCP). The firmware must be loaded and running before mg_tcpip_init(), see
// the FRDM-RW612 eth_radio_bridge tutorial. The driver talks to it over the
// CPU2 <-> CPU3 IMU mailbox: IMUMC messages carrying HDLC framed Spinel.
// driver_data: struct mg_l2_802154_data, channel and PAN ID set
// NXP RW612 IEEE 802.15.4 radio. The radio runs on CPU2: NXP BLE/802.15.4
// combo firmware, an OpenThread radio co-processor (RCP). The firmware must
// be loaded and running before mg_tcpip_init(), see the FRDM-RW612 lowpan
// tutorial. The driver talks to it over the CPU2 <-> CPU3 IMU mailbox:
// IMUMC messages carrying HDLC framed Spinel. Frames passed to tx() and
// returned by rx() are raw 802.15.4 frames: MAC header and payload, no FCS.
// Use with MG_TCPIP_L2_LOWPAN: channel, PAN ID and addresses are taken from
// ifp->l2data.lowpan and ifp->mac. No driver_data
#ifndef MG_TCPIP_DRIVER_INIT
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_l2_802154_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.channel = MG_L2_802154_CHANNEL; \
driver_data_.pan_id = MG_L2_802154_PAN_ID; \
mif_.driver = &mg_tcpip_driver_rw612_154; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: rw612_154, channel %d", MG_L2_802154_CHANNEL)); \
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_if mif_; \
mif_.l2type = MG_TCPIP_L2_LOWPAN; \
mif_.driver = &mg_tcpip_driver_rw612_154; \
mif_.l2data.lowpan.channel = 15; \
mif_.l2data.lowpan.pan_id = 0xabcd; \
MG_SET_MAC_ADDRESS(mif_.mac); /* EUI-64 */ \
mg_tcpip_init(mgr, &mif_); \
(mgr)->use_dns6 = true; /* IPv6 only: no IPv4 over 6LoWPAN */ \
MG_INFO(("Driver: rw612_154, 6LoWPAN")); \
} while (0)
#endif
+1 -1
View File
@@ -365,7 +365,7 @@ static bool st67w6_init(uint8_t *mac) {
}
// set clock, who cares ???
if (!st67w6_at_cmd("AT+GET_CLOCK\r\n", 14)) return false;
MG_VERBOSE(("%.*s", s_at_resp_len, at_resp));
MG_DEBUG(("%.*s", s_at_resp_len, at_resp)); // TODO(scaprile): --> VERBOSE
// BT-ENABLED DEPENDENCY
// MODULE DEPENDENCY
+3 -6
View File
@@ -2,7 +2,6 @@
#if MG_ENABLE_TCPIP && (MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN || \
MG_ENABLE_DRIVER_STM32N)
// STM32C: vendor modded single-queue Synopsys v5.4, same settings as STM32H
// STM32H: vendor modded single-queue Synopsys v4.2
// STM32N: dual-queue GbE Synopsys v5.2 with no hash table option, 64-bit AXI
// MCXNx4x: dual-queue Synopsys v5.2 with no hash table option
@@ -311,7 +310,7 @@ static uint32_t s_rxno;
#if MG_ENABLE_DRIVER_MCXN
void ETHERNET_IRQHandler(void);
void ETHERNET_IRQHandler(void) {
#elif MG_ENABLE_DRIVER_STM32H && !MG_ENABLE_DRIVER_STM32C
#elif MG_ENABLE_DRIVER_STM32H
void ETH_IRQHandler(void);
void ETH_IRQHandler(void) {
#else
@@ -340,10 +339,8 @@ void ETH1_IRQHandler(void) {
}
ETH->DMACSR =
MG_BIT(7) | MG_BIT(8); // Clear possible RBU RPS while processing
// Resume RX: tail is the last descriptor given back to DMA. It must differ
// from the DMA current descriptor, or some DMAs, e.g. STM32C5, stay suspended
ETH->DMACRDTPR = (uint32_t) (uintptr_t) &s_rxdesc[(s_rxno + ETH_DESC_CNT - 1) %
ETH_DESC_CNT];
ETH->DMACRDTPR =
(uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT - 1]; // and resume RX
}
struct mg_tcpip_driver mg_tcpip_driver_stm32h = {
+2 -11
View File
@@ -4,13 +4,6 @@
#if !defined(MG_ENABLE_DRIVER_STM32H)
#define MG_ENABLE_DRIVER_STM32H 0
#endif
#if !defined(MG_ENABLE_DRIVER_STM32C)
#define MG_ENABLE_DRIVER_STM32C 0
#endif
#if MG_ENABLE_DRIVER_STM32C // STM32C5: STM32H5 MAC, the IRQ is ETH1
#undef MG_ENABLE_DRIVER_STM32H
#define MG_ENABLE_DRIVER_STM32H 1
#endif
#if !defined(MG_ENABLE_DRIVER_MCXN)
#define MG_ENABLE_DRIVER_MCXN 0
#endif
@@ -50,11 +43,9 @@ struct mg_tcpip_driver_stm32h_data {
#define MG_DRIVER_MDC_CR 4
#endif
#if MG_ENABLE_DRIVER_STM32H && !MG_ENABLE_DRIVER_STM32C && \
MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
#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_ENABLE_DRIVER_STM32C) && \
MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
#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()
+1 -1
View File
@@ -6,7 +6,7 @@
struct mg_health mg_health_record MG_HEALTH_RAM;
// STM32 H723 support. Needs the _estack symbol from the linker script
#if MG_ENABLE_HEALTH && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5))
#if MG_ENABLE_HEALTH && (defined(STM32H7_DEV_ID) || defined(STM32H5))
// Fault handler body. Runs in exception context: no printf, no malloc, no
// blocking calls. Copies the unwind registers and raw stack into the health
// record, then resets.
+2 -2
View File
@@ -9,11 +9,11 @@
#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) || defined(STM32C5))
#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) || defined(STM32C5))
#if !defined(MG_HEALTH_DATA_SIZE) && (defined(STM32H7_DEV_ID) || defined(STM32H5))
#define MG_HEALTH_DATA_SIZE 250
#endif
+33 -43
View File
@@ -879,8 +879,8 @@ void mg_http_serve_upload(struct mg_connection *c, struct mg_http_message *hm,
fs->rm(path); // MG_FS_WRITE appends, not truncates: drop any old file
if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) {
MG_ERROR(("Cannot open %s for upload", path));
} else {
mg_http_stream_body(c, hm, serve_upload_cb, fd);
} else if (!mg_http_stream_body(c, hm, serve_upload_cb, fd)) {
mg_fs_close(fd);
}
}
}
@@ -1014,7 +1014,6 @@ struct mg_upload_state {
size_t received; // Number of body bytes received so far
void *user_data; // Passed to cb, free for cb to use
bool failed; // Set when cb returned false
bool finished; // End callback has been called
};
// Event handler installed by mg_http_stream_body(). Feeds the body to the
@@ -1022,18 +1021,18 @@ struct mg_upload_state {
// handler: the connection is closed after the reply.
static void mg_stream_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_upload_state *us = (struct mg_upload_state *) c->data;
bool (*fn)(struct mg_http_message *, struct mg_str *, void **) =
bool (*fn)(struct mg_http_message *, struct mg_str *, void **) =
(bool (*)(struct mg_http_message *, struct mg_str *, void **))
(ptrdiff_t) c->fn_data;
(void) ev_data;
if (ev == MG_EV_CLOSE || ev == MG_EV_ERROR) {
if (!us->finished) {
if (us->received < us->expected) {
// Connection dropped mid-upload. Let cb clean up, e.g. call mg_ota_end()
us->finished = true; // MG_EV_ERROR is followed by MG_EV_CLOSE
us->received = us->expected; // MG_EV_ERROR is followed by MG_EV_CLOSE
fn(NULL, NULL, &us->user_data);
}
} else if (ev == MG_EV_READ && !c->is_draining && !us->finished) {
if (!us->failed && c->recv.len > 0 && us->received < us->expected) {
} else if (ev == MG_EV_READ && !c->is_draining) {
if (c->recv.len > 0 && us->received < us->expected) {
// Got some uploaded data. Feed by 512-byte aligned chunks, for OTA
size_t left = us->expected - us->received, r = c->recv.len;
size_t aligned = r < left ? MG_ROUND_DOWN(r, 512) : left;
@@ -1044,13 +1043,10 @@ static void mg_stream_handler(struct mg_connection *c, int ev, void *ev_data) {
us->received += aligned;
mg_iobuf_del(&c->recv, 0, aligned); // Shift the unprocessed tail
}
if (!us->finished && (us->failed || us->received >= us->expected)) {
if (us->received >= us->expected) {
// Finished upload. Always call cb, even after error, so it can clean up
us->finished = true;
us->failed |= !fn(NULL, NULL, &us->user_data);
if (c->is_client && us->failed) {
mg_error(c, "Body streaming failed");
} else if (!c->is_client) {
if (!c->is_client) {
mg_http_reply(c, us->failed ? 500 : 200, NULL, "%zu %s\n",
us->received, us->failed ? "fail" : "ok");
}
@@ -1059,54 +1055,48 @@ static void mg_stream_handler(struct mg_connection *c, int ev, void *ev_data) {
}
}
void mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
bool mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
bool (*cb)(struct mg_http_message *, struct mg_str *,
void **),
void **user_data),
void *user_data) {
struct mg_upload_state *us = (struct mg_upload_state *) c->data;
int code = 0;
size_t head_len = hm->head.len;
struct mg_str *te = mg_http_get_header(hm, "Transfer-Encoding");
bool handled = false;
// Catch upload requests early, without buffering whole body. Called on the
// MG_EV_HTTP_HDRS event: we've received all HTTP headers but not necessarily
// the full HTTP body
if (!c->is_client && mg_strcmp(hm->method, mg_str("POST")) != 0 &&
mg_strcmp(hm->method, mg_str("PUT")) != 0) {
code = 405;
} else if (mg_http_get_header(hm, "Transfer-Encoding") != NULL) {
code = 400;
} else if (hm->body.len == (size_t) ~0 ||
(!c->is_client && mg_http_get_header(hm, "Content-Length") == NULL)) {
code = 411;
if (hm->body.len == (size_t) ~0 ||
(te != NULL && mg_strcasecmp(*te, mg_str("chunked")) == 0)) {
// Unknown length or chunked, cannot stream. Leave it to the caller
} else if (!c->is_client &&
mg_strcmp(hm->method, mg_str("POST")) != 0 &&
mg_strcmp(hm->method, mg_str("PUT")) != 0) {
// Not an upload request. Leave it to the caller
} else if (sizeof(*us) > sizeof(c->data)) {
code = 500;
}
c->pfn = NULL; // Take ownership even when validation fails
if (code != 0) {
c->fn = NULL;
c->fn_data = NULL;
mg_iobuf_del(&c->recv, 0, c->recv.len);
cb(NULL, NULL, &user_data); // Release resources even if start was skipped
if (c->is_client) {
mg_error(c, "%s", mg_http_status_code_str(code));
} else {
mg_http_reply(c, code, code == 405 ? "Allow: POST, PUT\r\n" : NULL, "");
if (!c->is_client) { // Nobody to reply to on a client connection
c->pfn = NULL;
mg_http_reply(c, 500, NULL, "data too small\n");
c->is_draining = 1;
cb(NULL, NULL, &user_data); // Let cb release user_data, start skipped
handled = true;
}
c->is_draining = 1;
} else {
c->pfn = NULL; // Silence HTTP protocol handler
memset(us, 0, sizeof(*us));
us->expected = hm->body.len; // Store number of bytes we expect
us->user_data = user_data;
c->fn = mg_stream_handler;
c->fn_data = (void *) (ptrdiff_t) cb;
us->failed = !cb(hm, NULL, &us->user_data); // Call before deleting headers
if (!c->is_client && !us->failed && !us->finished &&
mg_http_get_header(hm, "Expect") != NULL) {
if (mg_http_get_header(hm, "Expect") != NULL) {
mg_http_reply(c, 100, NULL, ""); // If curl expects, we continue
}
mg_iobuf_del(&c->recv, 0, head_len); // Delete HTTP headers
mg_iobuf_del(&c->recv, 0, hm->head.len); // Delete HTTP headers
c->fn = mg_stream_handler; // We own the connection from now
c->fn_data = (void *) (ptrdiff_t) cb;
mg_call(c, MG_EV_READ, &c->recv.len); // Process the body bytes we have
handled = true;
}
return handled;
}
static bool is_hex_digit(int c) {
+11 -14
View File
@@ -205,32 +205,29 @@ 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(). Always takes over the connection. Invalid requests receive
// an HTTP error; client-side failures call mg_error(). Replies and connection
// draining are handled internally, including callback failures.
// 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 exactly once, including validation
// failures before start and connections that drop early. Release resources
// here. A truncated body looks like a complete one: validate before
// committing.
// The callback returns true on success, false on error. A failure stops
// streaming immediately and calls the end callback before closing.
// - 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);
// 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:
// The existing event handler is no longer called. hm is invalid after this
// call; c->data and c->fn_data are reserved for streaming state.
void mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
// 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);
+25 -10
View File
@@ -56,6 +56,19 @@ extern uint8_t *mg_l2_pppoe_header(struct mg_tcpip_if *, enum mg_l2proto, struct
extern size_t mg_l2_pppoe_trailer(struct mg_tcpip_if *, size_t, uint8_t *);
extern bool mg_l2_pppoe_rx(struct mg_tcpip_if *, enum mg_l2proto *, struct mg_str *, struct mg_str *);
extern void mg_l2_lowpan_init(struct mg_tcpip_if *);
extern bool mg_l2_lowpan_poll(struct mg_tcpip_if *, bool);
extern uint8_t *mg_l2_lowpan_header(struct mg_tcpip_if *, enum mg_l2proto, struct mg_l2addr *, struct mg_l2addr *, uint8_t *);
extern size_t mg_l2_lowpan_trailer(struct mg_tcpip_if *, size_t, uint8_t *);
extern bool mg_l2_lowpan_rx(struct mg_tcpip_if *, enum mg_l2proto *, struct mg_str *, struct mg_str *);
extern struct mg_l2addr *mg_l2_lowpan_getaddr(struct mg_tcpip_if *, uint8_t *);
extern struct mg_l2addr *mg_l2_lowpan_mapip(enum mg_l2addrtype, struct mg_addr *);
#if MG_ENABLE_IPV6
extern bool mg_l2_lowpan_genip6(uint64_t *, uint8_t, struct mg_l2addr *);
extern bool mg_l2_lowpan_ip6get(struct mg_l2addr *, uint8_t *, uint8_t);
extern uint8_t mg_l2_lowpan_ip6put(struct mg_l2addr *, uint8_t *);
#endif
typedef void (*l2_init_fn)(struct mg_tcpip_if *);
typedef bool (*l2_poll_fn)(struct mg_tcpip_if *, bool);
typedef uint8_t *((*l2_header_fn)(struct mg_tcpip_if *, enum mg_l2proto, struct mg_l2addr *, struct mg_l2addr *, uint8_t *));
@@ -71,25 +84,27 @@ typedef uint8_t (*l2_ip6put_fn)(struct mg_l2addr *, uint8_t *);
// clang-format on
static const l2_init_fn l2_init[] = {mg_l2_eth_init, mg_l2_ppp_init,
mg_l2_pppoe_init};
mg_l2_pppoe_init, mg_l2_lowpan_init};
static const l2_poll_fn l2_poll[] = {mg_l2_eth_poll, mg_l2_ppp_poll,
mg_l2_pppoe_poll};
mg_l2_pppoe_poll, mg_l2_lowpan_poll};
static const l2_header_fn l2_header[] = {mg_l2_eth_header, mg_l2_ppp_header,
mg_l2_pppoe_header};
mg_l2_pppoe_header,
mg_l2_lowpan_header};
static const l2_trailer_fn l2_trailer[] = {mg_l2_eth_trailer, mg_l2_ppp_trailer,
mg_l2_pppoe_trailer};
static const l2_rx_fn l2_rx[] = {mg_l2_eth_rx, mg_l2_ppp_rx, mg_l2_pppoe_rx};
mg_l2_pppoe_trailer, mg_l2_lowpan_trailer};
static const l2_rx_fn l2_rx[] = {mg_l2_eth_rx, mg_l2_ppp_rx, mg_l2_pppoe_rx,
mg_l2_lowpan_rx};
static const l2_getaddr_fn l2_getaddr[] = {mg_l2_eth_getaddr, mg_l2_ppp_getaddr,
mg_l2_ppp_getaddr};
mg_l2_ppp_getaddr, mg_l2_lowpan_getaddr};
static const l2_mapip_fn l2_mapip[] = {mg_l2_eth_mapip, mg_l2_ppp_mapip,
mg_l2_ppp_mapip};
mg_l2_ppp_mapip, mg_l2_lowpan_mapip};
#if MG_ENABLE_IPV6
static const l2_genip6_fn l2_genip6[] = {mg_l2_eth_genip6, mg_l2_ppp_genip6,
mg_l2_ppp_genip6};
mg_l2_ppp_genip6, mg_l2_lowpan_genip6};
static const l2_ip6get_fn l2_ip6get[] = {mg_l2_eth_ip6get, mg_l2_ppp_ip6get,
mg_l2_ppp_ip6get};
mg_l2_ppp_ip6get, mg_l2_lowpan_ip6get};
static const l2_ip6put_fn l2_ip6put[] = {mg_l2_eth_ip6put, mg_l2_ppp_ip6put,
mg_l2_ppp_ip6put};
mg_l2_ppp_ip6put, mg_l2_lowpan_ip6put};
#endif
void mg_l2_init(struct mg_tcpip_if *ifp) {
+24 -2
View File
@@ -6,7 +6,13 @@
#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 };
enum mg_l2type {
MG_TCPIP_L2_ETH = 0,
MG_TCPIP_L2_PPP,
MG_TCPIP_L2_PPPoE,
MG_TCPIP_L2_LOWPAN,
MG_TCPIP_L2_USER
};
#if defined(__DCC__)
#pragma pack(1)
@@ -29,10 +35,27 @@ struct eth_data {
struct pppoe_data { // (struct eth_data *) &pppoe_data = pppoedata.eth
struct eth_data eth;
};
struct lowpan_data {
uint16_t pan_id; // User-set PAN ID
uint8_t channel; // User-set channel, 11..26
uint16_t short_addr; // Our short address, 0xfffe if none
uint8_t seq; // MAC sequence number
uint8_t proto; // Protocol of the frame being sent, enum mg_l2proto
uint8_t dst[8]; // Destination of the frame being sent
uint16_t tag; // Next fragmentation datagram tag
uint8_t *buf; // Reassembly and decompression buffer
uint16_t size; // Datagram being reassembled: size, 0 if none
uint16_t received; // Bytes received so far
uint16_t tag_rx; // Datagram tag
uint8_t src[8]; // Source address
bool udp; // UDP header was compressed
uint64_t expire; // Reassembly deadline, ms
};
union mg_l2data {
struct eth_data eth;
struct pppoe_data pppoe;
struct lowpan_data lowpan;
};
#if defined(__DCC__)
@@ -41,7 +64,6 @@ union mg_l2data {
#pragma pack(pop)
#endif
enum mg_l2proto {
MG_TCPIP_L2PROTO_IPV4 = 0,
MG_TCPIP_L2PROTO_IPV6,
-60
View File
@@ -1,60 +0,0 @@
#include "l2.h"
#include "net_builtin.h"
#if MG_ENABLE_TCPIP
// Radio frame: 802.15.4 MAC header, 15 bytes: data frame, PAN ID compression,
// broadcast destination, our EUI-64 made from MAC as source. Then 2 bytes:
// tag, fragment index << 4 | last fragment index. Then data. 125 bytes max,
// 127 minus FCS
#define MGI_802154_HDR 17
#define MGI_802154_DATA (125 - MGI_802154_HDR)
size_t mg_l2_802154_tx(struct mg_tcpip_if *ifp, const void *buf, size_t len,
size_t (*send_fn)(const uint8_t *, size_t,
struct mg_tcpip_if *)) {
struct mg_l2_802154_data *d =
(struct mg_l2_802154_data *) ifp->driver_data;
const uint8_t *p = (const uint8_t *) buf, *m = ifp->mac;
uint8_t f[MGI_802154_HDR + MGI_802154_DATA];
size_t i, n, last = (len - 1) / MGI_802154_DATA;
if (len == 0 || last > 15) return 0;
d->tag++;
for (i = 0; i <= last; i++) {
n = i == last ? len - i * MGI_802154_DATA : MGI_802154_DATA;
f[0] = 0x41, f[1] = 0xd8, f[2] = d->seq++; // 2006 frame, see above
f[3] = (uint8_t) d->pan_id, f[4] = (uint8_t) (d->pan_id >> 8);
f[5] = f[6] = 0xff;
f[7] = m[5], f[8] = m[4], f[9] = m[3], f[10] = 0xfe, f[11] = 0xff;
f[12] = m[2], f[13] = m[1], f[14] = m[0]; // Little endian EUI-64
f[15] = d->tag, f[16] = (uint8_t) ((i << 4) | last);
memcpy(f + MGI_802154_HDR, p + i * MGI_802154_DATA, n);
if (send_fn(f, MGI_802154_HDR + n, ifp) == 0) return 0;
}
return len;
}
size_t mg_l2_802154_rx(struct mg_tcpip_if *ifp, const uint8_t *f, size_t n,
void *buf, size_t len) {
struct mg_l2_802154_data *d =
(struct mg_l2_802154_data *) ifp->driver_data;
size_t idx, last;
if (n <= MGI_802154_HDR || f[0] != 0x41 || f[1] != 0xd8 ||
f[3] != (uint8_t) d->pan_id || f[4] != (uint8_t) (d->pan_id >> 8))
return 0; // Not ours
idx = f[16] >> 4, last = f[16] & 15;
if (idx == 0) d->rxtag = f[15], d->rxnext = 0, d->rxlen = 0;
if (idx == 0) memcpy(d->rxsrc, f + 7, sizeof(d->rxsrc));
n -= MGI_802154_HDR;
if (idx != d->rxnext || idx > last || f[15] != d->rxtag ||
memcmp(d->rxsrc, f + 7, sizeof(d->rxsrc)) != 0 ||
d->rxlen + n > sizeof(d->rxbuf))
return 0; // Missed a fragment, or from another sender
memcpy(d->rxbuf + d->rxlen, f + MGI_802154_HDR, n);
d->rxlen = (uint16_t) (d->rxlen + n), d->rxnext++;
if (idx < last || d->rxlen > len) return 0;
memcpy(buf, d->rxbuf, d->rxlen);
return d->rxlen;
}
#endif
+551
View File
@@ -0,0 +1,551 @@
#include "l2.h"
#include "log.h"
#include "net.h"
#include "net_builtin.h"
#include "printf.h"
#include "util.h"
// IPv6 over IEEE 802.15.4, 6LoWPAN. RFC 4944: frame format, fragmentation.
// RFC 6282: IPHC header compression, UDP next header compression.
// Driver tx()/rx() carry raw 802.15.4 frames: MAC header, payload, no FCS.
//
// L2 addresses are 8 bytes, struct mg_l2addr. Extended address: EUI-64, big
// endian. Short address S: 0000:00ff:fe00:SSSS, which is also its IPv6
// interface identifier, RFC 6282 3.2.2. Broadcast is short address 0xffff.
// No link-layer security: secured frames are dropped
#if MG_ENABLE_TCPIP && MG_ENABLE_IPV6
#define MGI_LOWPAN_FRAME_MAX 125 // 802.15.4 frame without FCS: 127 - 2
#define MGI_LOWPAN_MTU 1280 // IPv6 minimum MTU, RFC 8200 5
#define MGI_LOWPAN_REASM_MS 5000 // Drop incomplete datagrams after this
#define MGI_FCF_DATA 1U // Frame control: data frame
#define MGI_FCF_SECURITY 0x08U // Frame control: security enabled
#define MGI_FCF_ACK_REQUEST 0x20U // Frame control: acknowledgment request
#define MGI_FCF_PANID_COMP 0x40U // Frame control: PAN ID compression
#define MGI_FCF_IE 0x200U // Frame control: IE present, 2015 frames
#define MGI_FCF_VERSION_2006 0x1000U
#define MGI_AM_SHORT 2U // Addressing mode: 16-bit short address
#define MGI_AM_EXT 3U // Addressing mode: 64-bit extended address
#define MGI_DISPATCH_IPV6 0x41 // Uncompressed IPv6, RFC 4944 5.1
#define MGI_DISPATCH_FRAG1 0xc0 // First fragment, 11000xxx
#define MGI_DISPATCH_FRAGN 0xe0 // Subsequent fragment, 11100xxx
// Parsed 802.15.4 MAC header
struct lowpan_mhr {
uint8_t dst[8], src[8]; // Addresses, struct mg_l2addr format
uint16_t dst_pan; // Destination PAN ID
size_t len; // MAC header length, without security header
bool secured; // Security enabled
};
static struct mg_l2addr s_rxsrc; // Source address returned by getaddr()
static void set_short(uint8_t *a, uint16_t s) {
a[0] = a[1] = a[2] = a[5] = 0, a[3] = 0xff, a[4] = 0xfe;
a[6] = (uint8_t) (s >> 8), a[7] = (uint8_t) s;
}
static bool is_zero(const uint8_t *p, size_t n) {
while (n-- > 0)
if (*p++ != 0) return false;
return true;
}
// Read an address field, over-the-air byte order is little endian
static size_t read_addr(const uint8_t *p, unsigned mode, uint8_t *a) {
int i;
if (mode == MGI_AM_SHORT) {
set_short(a, (uint16_t) (p[0] | (p[1] << 8)));
return 2;
}
for (i = 0; i < 8; i++) a[i] = p[7 - i];
return 8;
}
// Parse MAC header of a data frame, 2003/2006 format
static bool parse_mhr(const uint8_t *f, size_t n, struct lowpan_mhr *h) {
unsigned fcf, dm, sm, ver;
size_t i = 3;
if (n < 3) return false;
fcf = (unsigned) (f[0] | (f[1] << 8));
dm = (fcf >> 10) & 3, sm = (fcf >> 14) & 3, ver = (fcf >> 12) & 3;
if ((fcf & 7) != MGI_FCF_DATA || (fcf & MGI_FCF_IE) || ver > 1) return false;
h->secured = (fcf & MGI_FCF_SECURITY) != 0;
if (dm < MGI_AM_SHORT || sm < MGI_AM_SHORT) return false; // Need both addresses
if (i + 2 > n) return false;
h->dst_pan = (uint16_t) (f[i] | (f[i + 1] << 8)), i += 2;
if (i + (dm == MGI_AM_EXT ? 8 : 2) > n) return false;
i += read_addr(f + i, dm, h->dst);
if ((fcf & MGI_FCF_PANID_COMP) == 0) i += 2; // Source PAN ID, if present
if (i + (sm == MGI_AM_EXT ? 8 : 2) > n) return false;
i += read_addr(f + i, sm, h->src);
h->len = i;
return true;
}
static bool is_short(const uint8_t *a) {
return a[0] == 0 && a[1] == 0 && a[2] == 0 && a[3] == 0xff && a[4] == 0xfe &&
a[5] == 0;
}
static bool is_bcast(const uint8_t *a) {
return is_short(a) && a[6] == 0xff && a[7] == 0xff;
}
// IPv6 interface identifier for an L2 address, RFC 6282 3.2.2
static void make_iid(const uint8_t *a, uint8_t *iid) {
memcpy(iid, a, 8);
if (!is_short(a)) iid[0] ^= 2; // EUI-64: flip universal/local bit
}
static size_t write_addr(uint8_t *p, const uint8_t *a) {
int i;
if (is_short(a)) {
p[0] = a[7], p[1] = a[6];
return 2;
}
for (i = 0; i < 8; i++) p[i] = a[7 - i];
return 8;
}
// Write MAC header for a data frame from us to dst. Return its length
static size_t write_mhr(struct mg_tcpip_if *ifp, const uint8_t *dst,
uint8_t *f) {
struct lowpan_data *d = &ifp->l2data.lowpan;
unsigned fcf = MGI_FCF_DATA | MGI_FCF_PANID_COMP | MGI_FCF_VERSION_2006;
size_t i = 3;
if (!is_bcast(dst)) fcf |= MGI_FCF_ACK_REQUEST; // Unicast: MAC retries
fcf |= (is_short(dst) ? MGI_AM_SHORT : MGI_AM_EXT) << 10;
fcf |= (is_short(ifp->mac) ? MGI_AM_SHORT : MGI_AM_EXT) << 14;
f[0] = (uint8_t) fcf, f[1] = (uint8_t) (fcf >> 8), f[2] = d->seq++;
f[i++] = (uint8_t) d->pan_id, f[i++] = (uint8_t) (d->pan_id >> 8);
i += write_addr(f + i, dst);
i += write_addr(f + i, ifp->mac);
return i;
}
// Compress a unicast IPv6 address: 2 bits for the SAM/DAM field, inline
// bytes go to out. RFC 6282 3.1.1, SAC/DAC = 0
static unsigned compress_addr(const uint8_t *ip, const uint8_t *l2,
uint8_t *out, size_t *n) {
static const uint8_t ll[8] = {0xfe, 0x80, 0, 0, 0, 0, 0, 0};
uint8_t iid[8];
if (memcmp(ip, ll, 8) != 0) {
memcpy(out + *n, ip, 16), *n += 16;
return 0;
}
make_iid(l2, iid);
if (memcmp(ip + 8, iid, 8) == 0) return 3; // Derived from L2 address
if (is_short(ip + 8)) { // 0000:00ff:fe00:XXXX
memcpy(out + *n, ip + 14, 2), *n += 2;
return 2;
}
memcpy(out + *n, ip + 8, 8), *n += 8;
return 1;
}
// Compress IPv6 header to IPHC, RFC 6282 3.1. Next header is carried inline.
// Return compressed header length
static size_t iphc_compress(const uint8_t *ip, const uint8_t *src_l2,
const uint8_t *dst_l2, uint8_t *out) {
unsigned tc = (unsigned) (((ip[0] & 0x0f) << 4) | (ip[1] >> 4));
unsigned ecn = tc & 3, dscp = tc >> 2, tf, hl;
uint32_t flow = ((uint32_t) (ip[1] & 0x0f) << 16) |
((uint32_t) ip[2] << 8) | ip[3];
size_t n = 2;
if (tc == 0 && flow == 0) {
tf = 3;
} else if (flow == 0) {
tf = 2, out[n++] = (uint8_t) ((ecn << 6) | dscp);
} else if (dscp == 0) {
tf = 1, out[n++] = (uint8_t) ((ecn << 6) | (flow >> 16));
out[n++] = (uint8_t) (flow >> 8), out[n++] = (uint8_t) flow;
} else {
tf = 0, out[n++] = (uint8_t) ((ecn << 6) | dscp);
out[n++] = (uint8_t) (flow >> 16), out[n++] = (uint8_t) (flow >> 8);
out[n++] = (uint8_t) flow;
}
out[n++] = ip[6]; // Next header, inline
hl = ip[7] == 1 ? 1 : ip[7] == 64 ? 2 : ip[7] == 255 ? 3 : 0;
if (hl == 0) out[n++] = ip[7];
out[0] = (uint8_t) (0x60 | (tf << 3) | hl);
out[1] = 0;
if (is_zero(ip + 8, 16)) {
out[1] |= 0x40; // SAC = 1, SAM = 00: unspecified address
} else {
out[1] |= (uint8_t) (compress_addr(ip + 8, src_l2, out, &n) << 4);
}
if (ip[24] == 0xff) { // Multicast, M = 1
out[1] |= 0x08;
if (ip[25] == 0x02 && is_zero(ip + 26, 13)) { // ff02::00XX
out[1] |= 3, out[n++] = ip[39];
} else if (is_zero(ip + 26, 11)) { // ffXX::00XX:XXXX
out[1] |= 2, out[n++] = ip[25];
memcpy(out + n, ip + 37, 3), n += 3;
} else if (is_zero(ip + 26, 9)) { // ffXX::00XX:XXXX:XXXX
out[1] |= 1, out[n++] = ip[25];
memcpy(out + n, ip + 35, 5), n += 5;
} else {
memcpy(out + n, ip + 24, 16), n += 16;
}
} else {
out[1] |= (uint8_t) compress_addr(ip + 24, dst_l2, out, &n);
}
return n;
}
// Decompress a unicast address. Prefix is the context prefix for SAC/DAC = 1,
// or NULL for link-local. Return inline bytes used
static size_t decompress_addr(unsigned mode, const uint8_t *p, size_t n,
const uint8_t *l2, const uint8_t *prefix,
uint8_t *ip) {
static const size_t sizes[] = {16, 8, 2, 0};
if (sizes[mode] > n) return n + 1; // Truncated: caller sees i > n
if (mode == 0) {
memcpy(ip, p, 16);
} else {
memset(ip, 0, 16), ip[0] = 0xfe, ip[1] = 0x80;
if (prefix != NULL) memcpy(ip, prefix, 8);
if (mode == 1) memcpy(ip + 8, p, 8);
if (mode == 2) set_short(ip + 8, (uint16_t) ((p[0] << 8) | p[1]));
if (mode == 3) make_iid(l2, ip + 8);
}
return sizes[mode];
}
// Decompress IPHC or uncompressed IPv6 header, RFC 6282 3.1, 4.3.
// Write IPv6 header (and UDP header if compressed) to ip. Return its length,
// or 0 on error. Set *used to the number of compressed bytes consumed
static size_t iphc_decompress(const struct lowpan_mhr *h, const uint8_t *p,
size_t n, uint8_t *ip, size_t *used, bool *udp) {
static const size_t tf_sizes[] = {4, 3, 1, 0};
unsigned b0, b1, tf, tc = 0, sam, dam;
uint32_t flow = 0;
size_t i = 2;
*udp = false;
if (n > 0 && p[0] == MGI_DISPATCH_IPV6) {
if (n < 41) return 0;
memcpy(ip, p + 1, 40);
*used = 41;
return 40;
}
if (n < 2 || (p[0] & 0xe0) != 0x60) return 0;
b0 = p[0], b1 = p[1], tf = (b0 >> 3) & 3, sam = (b1 >> 4) & 3, dam = b1 & 3;
if (b1 & 0x80) return 0; // CID: no compression contexts
if (i + tf_sizes[tf] + ((b0 & 4) ? 0 : 1) + ((b0 & 3) ? 0 : 1) > n)
return 0; // TF, next header, hop limit
if (tf == 0 || tf == 2) tc = (unsigned) (((p[i] & 0x3f) << 2) | (p[i] >> 6));
if (tf == 1) tc = p[i] >> 6;
if (tf == 0) flow = ((uint32_t) (p[i + 1] & 0x0f) << 16) |
((uint32_t) p[i + 2] << 8) | p[i + 3];
if (tf == 1) flow = ((uint32_t) (p[i] & 0x0f) << 16) |
((uint32_t) p[i + 1] << 8) | p[i + 2];
i += tf_sizes[tf];
ip[0] = (uint8_t) (0x60 | (tc >> 4));
ip[1] = (uint8_t) ((tc << 4) | (flow >> 16));
ip[2] = (uint8_t) (flow >> 8), ip[3] = (uint8_t) flow;
ip[4] = ip[5] = 0; // Payload length, set by caller
if ((b0 & 4) == 0) ip[6] = p[i++]; // Next header inline
if ((b0 & 3) == 0) ip[7] = p[i++];
if ((b0 & 3) != 0) ip[7] = (b0 & 3) == 1 ? 1 : (b0 & 3) == 2 ? 64 : 255;
// Source address
if ((b1 & 0x40) && sam == 0) { // SAC = 1, SAM = 00: unspecified address
memset(ip + 8, 0, 16);
} else {
if (b1 & 0x40) return 0; // SAC = 1: no compression contexts
i += decompress_addr(sam, p + i, n - i, h->src, NULL, ip + 8);
if (i > n) return 0;
}
// Destination address
if ((b1 & 0x0c) == 0x08) { // Multicast, stateless
static const size_t sizes[] = {16, 6, 4, 1};
if (i + sizes[dam] > n) return 0;
memset(ip + 24, 0, 16), ip[24] = 0xff;
if (dam == 0) memcpy(ip + 24, p + i, 16);
if (dam == 1) ip[25] = p[i], memcpy(ip + 35, p + i + 1, 5);
if (dam == 2) ip[25] = p[i], memcpy(ip + 37, p + i + 1, 3);
if (dam == 3) ip[25] = 2, ip[39] = p[i];
i += sizes[dam];
} else if ((b1 & 0x08) == 0) { // Unicast
if (b1 & 0x04) return 0; // DAC = 1: no compression contexts
i += decompress_addr(dam, p + i, n - i, h->dst, NULL, ip + 24);
if (i > n) return 0;
} else {
return 0; // Stateful multicast is not supported
}
if (b0 & 4) { // Next header compressed: only UDP, RFC 6282 4.3
static const size_t port_sizes[] = {4, 3, 3, 1};
uint8_t *u = ip + 40;
unsigned nhc;
if (i >= n || (p[i] & 0xf8) != 0xf0) return 0;
nhc = p[i++];
if (nhc & 4) return 0; // Elided checksum is not supported
if (i + port_sizes[nhc & 3] + 2 > n) return 0; // Ports, checksum
switch (nhc & 3) {
case 0: memcpy(u, p + i, 4), i += 4; break;
case 1: memcpy(u, p + i, 2), u[2] = 0xf0, u[3] = p[i + 2], i += 3; break;
case 2: u[0] = 0xf0, u[1] = p[i], memcpy(u + 2, p + i + 1, 2), i += 3; break;
default:
u[0] = u[2] = 0xf0, u[1] = (uint8_t) (0xb0 | (p[i] >> 4));
u[3] = (uint8_t) (0xb0 | (p[i] & 0x0f)), i += 1;
break;
}
u[4] = u[5] = 0; // UDP length, set by caller
memcpy(u + 6, p + i, 2), i += 2; // Checksum
ip[6] = 17;
*udp = true;
}
if (i > n) return 0;
*used = i;
return *udp ? 48 : 40;
}
// Set IPv6 payload length and, for compressed UDP, UDP length
static void set_lengths(uint8_t *ip, size_t len, bool udp) {
size_t plen = len - 40;
ip[4] = (uint8_t) (plen >> 8), ip[5] = (uint8_t) plen;
if (udp) ip[44] = (uint8_t) (plen >> 8), ip[45] = (uint8_t) plen;
}
void mg_l2_lowpan_init(struct mg_tcpip_if *ifp) {
struct lowpan_data *d = &ifp->l2data.lowpan;
if (is_zero(ifp->mac, 8)) {
ifp->mac[0] = 0x02; // Locally administered EUI-64
mg_random(&ifp->mac[1], 7);
MG_INFO(("EUI-64 not set. Generated random: %M", mg_print_hex, 8,
ifp->mac));
}
ifp->l2mtu = MGI_LOWPAN_MTU;
ifp->framesize = MGI_LOWPAN_MTU; // TX buffer holds an uncompressed packet
// Polling drivers receive one raw 802.15.4 frame at a time into the queue
// buffer, so it does not need to be framesize: 127 bytes max
if (ifp->recv_queue.size == 0) ifp->recv_queue.size = 128;
d->short_addr = 0xfffe;
d->buf = (uint8_t *) mg_calloc(1, MGI_LOWPAN_MTU);
if (d->buf == NULL) MG_ERROR(("OOM"));
}
bool mg_l2_lowpan_poll(struct mg_tcpip_if *ifp, bool expired_1000ms) {
struct lowpan_data *d = &ifp->l2data.lowpan;
if (expired_1000ms && d->size != 0 && ifp->now > d->expire) d->size = 0;
return true;
}
uint8_t *mg_l2_lowpan_header(struct mg_tcpip_if *ifp, enum mg_l2proto proto,
struct mg_l2addr *src, struct mg_l2addr *dst,
uint8_t *frame) {
struct lowpan_data *d = &ifp->l2data.lowpan;
d->proto = (uint8_t) proto;
memcpy(d->dst, dst, sizeof(d->dst));
(void) src;
return frame; // Uncompressed packet starts at the frame start
}
// Compress the packet, send all fragments but the last directly, put the
// last fragment into the TX buffer for the stack to send
size_t mg_l2_lowpan_trailer(struct mg_tcpip_if *ifp, size_t len,
uint8_t *cur) {
struct lowpan_data *d = &ifp->l2data.lowpan;
uint8_t *ip = cur - len, hdr[48], f[MGI_LOWPAN_FRAME_MAX];
const uint8_t *dst = d->dst;
size_t hlen, mlen, avail, chunk, off, n;
if (d->proto != MG_TCPIP_L2PROTO_IPV6 || len < 40 || len > MGI_LOWPAN_MTU)
return 0;
hlen = iphc_compress(ip, ifp->mac, dst, hdr);
mlen = write_mhr(ifp, dst, f);
avail = MGI_LOWPAN_FRAME_MAX - mlen;
if (hlen + len - 40 <= avail) { // Fits into one frame
memcpy(f + mlen, hdr, hlen);
memcpy(f + mlen + hlen, ip + 40, len - 40);
n = mlen + hlen + len - 40;
} else { // Fragment, RFC 4944 5.3. Offsets are in uncompressed bytes
uint16_t tag = d->tag++;
chunk = (avail - 4 - hlen) & ~(size_t) 7; // 40 + chunk: multiple of 8
f[mlen] = (uint8_t) (MGI_DISPATCH_FRAG1 | (len >> 8));
f[mlen + 1] = (uint8_t) len;
f[mlen + 2] = (uint8_t) (tag >> 8), f[mlen + 3] = (uint8_t) tag;
memcpy(f + mlen + 4, hdr, hlen);
memcpy(f + mlen + 4 + hlen, ip + 40, chunk);
n = mlen + 4 + hlen + chunk;
ifp->driver->tx(f, n, ifp);
for (off = 40 + chunk;;) {
mlen = write_mhr(ifp, dst, f);
chunk = len - off;
if (chunk > ((avail - 5) & ~(size_t) 7)) chunk = (avail - 5) & ~(size_t) 7;
f[mlen] = (uint8_t) (MGI_DISPATCH_FRAGN | (len >> 8));
f[mlen + 1] = (uint8_t) len;
f[mlen + 2] = (uint8_t) (tag >> 8), f[mlen + 3] = (uint8_t) tag;
f[mlen + 4] = (uint8_t) (off / 8);
memcpy(f + mlen + 5, ip + off, chunk);
n = mlen + 5 + chunk;
off += chunk;
if (off >= len) break;
ifp->driver->tx(f, n, ifp);
}
}
memcpy(ifp->tx.buf, f, n);
return n;
}
bool mg_l2_lowpan_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
struct mg_str *pay, struct mg_str *raw) {
struct lowpan_data *d = &ifp->l2data.lowpan;
uint8_t *p = (uint8_t *) raw->buf;
size_t n = raw->len, hlen, used, size, off;
struct lowpan_mhr h;
bool udp;
if (d->buf == NULL || !parse_mhr(p, n, &h)) return false;
if (h.dst_pan != d->pan_id && h.dst_pan != 0xffff) return false;
if (!is_bcast(h.dst) && memcmp(h.dst, ifp->mac, 8) != 0 &&
!(is_short(h.dst) && d->short_addr != 0xfffe &&
MG_LOAD_BE16(h.dst + 6) == d->short_addr))
return false;
if (h.secured) return false; // No link-layer security
p += h.len, n -= h.len;
if (n < 1) return false;
if ((p[0] & 0xf8) == MGI_DISPATCH_FRAG1 || (p[0] & 0xf8) == MGI_DISPATCH_FRAGN) {
bool first = (p[0] & 0xf8) == MGI_DISPATCH_FRAG1;
uint16_t tag;
if (n < (first ? 4U : 5U)) return false;
size = (size_t) (((p[0] & 7) << 8) | p[1]);
tag = (uint16_t) ((p[2] << 8) | p[3]);
if (size < 40 || size > MGI_LOWPAN_MTU) return false;
if (first) {
p += 4, n -= 4;
hlen = iphc_decompress(&h, p, n, d->buf, &used, &udp);
if (hlen == 0 || hlen + n - used > size) return false;
memcpy(d->buf + hlen, p + used, n - used);
d->size = (uint16_t) size, d->tag_rx = tag, d->udp = udp;
d->received = (uint16_t) (hlen + n - used);
memcpy(d->src, h.src, sizeof(d->src));
d->expire = ifp->now + MGI_LOWPAN_REASM_MS;
} else {
off = (size_t) p[4] * 8;
p += 5, n -= 5;
if (d->size != size || d->tag_rx != tag ||
memcmp(d->src, h.src, sizeof(d->src)) != 0 || off + n > size)
return false; // Not the datagram being reassembled
memcpy(d->buf + off, p, n);
d->received = (uint16_t) (d->received + n);
}
if (d->size == 0 || d->received < d->size) return false;
set_lengths(d->buf, d->size, d->udp);
pay->buf = (char *) d->buf, pay->len = d->size;
d->size = 0;
} else {
hlen = iphc_decompress(&h, p, n, d->buf, &used, &udp);
if (hlen == 0 || hlen + n - used > MGI_LOWPAN_MTU) return false;
memcpy(d->buf + hlen, p + used, n - used);
set_lengths(d->buf, hlen + n - used, udp);
pay->buf = (char *) d->buf, pay->len = hlen + n - used;
}
*proto = MG_TCPIP_L2PROTO_IPV6;
return true;
}
struct mg_l2addr *mg_l2_lowpan_getaddr(struct mg_tcpip_if *ifp,
uint8_t *frame) {
struct lowpan_mhr h;
memset(&s_rxsrc, 0, sizeof(s_rxsrc));
if (parse_mhr(frame, MGI_LOWPAN_FRAME_MAX, &h))
memcpy(&s_rxsrc, h.src, sizeof(s_rxsrc));
(void) ifp;
return &s_rxsrc;
}
extern struct mg_l2addr s_mapip;
// All broadcast and multicast goes to the 802.15.4 broadcast address
struct mg_l2addr *mg_l2_lowpan_mapip(enum mg_l2addrtype addrtype,
struct mg_addr *addr) {
set_short((uint8_t *) &s_mapip, 0xffff);
(void) addrtype, (void) addr;
return &s_mapip;
}
bool mg_l2_lowpan_genip6(uint64_t *ip6, uint8_t prefix_len,
struct mg_l2addr *l2addr) {
if (prefix_len > 64) {
MG_ERROR(("Prefix length > 64, UNSUPPORTED"));
return false;
}
ip6[0] = 0;
make_iid((uint8_t *) l2addr, (uint8_t *) &ip6[1]); // RFC 6282 3.2.2
return true;
}
// Link-layer address of a neighbour from its IPv6 address: its interface ID
// is made from it, RFC 6282 3.2.2. make_iid() is its own inverse
void mg_l2_lowpan_ip6l2(const uint64_t *ip6, uint8_t *l2) {
make_iid((const uint8_t *) &ip6[1], l2);
}
// Link-layer address option, RFC 4944 8: length 2 for EUI-64, 1 for short
bool mg_l2_lowpan_ip6get(struct mg_l2addr *l2addr, uint8_t *opts,
uint8_t len) {
if (len == 2) {
memcpy(l2addr, opts, 8);
} else if (len == 1) {
set_short((uint8_t *) l2addr, (uint16_t) ((opts[0] << 8) | opts[1]));
} else {
return false;
}
return true;
}
uint8_t mg_l2_lowpan_ip6put(struct mg_l2addr *l2addr, uint8_t *opts) {
uint8_t *a = (uint8_t *) l2addr;
if (is_short(a)) {
opts[0] = a[6], opts[1] = a[7];
return 1;
}
memcpy(opts, a, 8);
return 2;
}
#elif MG_ENABLE_TCPIP
// No IPv6: 6LoWPAN is not available. Stubs for the L2 dispatch tables
extern struct mg_l2addr s_mapip;
void mg_l2_lowpan_init(struct mg_tcpip_if *ifp) {
MG_ERROR(("6LoWPAN needs MG_ENABLE_IPV6"));
(void) ifp;
}
bool mg_l2_lowpan_poll(struct mg_tcpip_if *ifp, bool expired_1000ms) {
(void) ifp, (void) expired_1000ms;
return false; // Link is never up
}
uint8_t *mg_l2_lowpan_header(struct mg_tcpip_if *ifp, enum mg_l2proto proto,
struct mg_l2addr *src, struct mg_l2addr *dst,
uint8_t *frame) {
(void) ifp, (void) proto, (void) src, (void) dst;
return frame;
}
size_t mg_l2_lowpan_trailer(struct mg_tcpip_if *ifp, size_t len,
uint8_t *cur) {
(void) ifp, (void) len, (void) cur;
return 0; // Nothing to send
}
bool mg_l2_lowpan_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
struct mg_str *pay, struct mg_str *raw) {
(void) ifp, (void) proto, (void) pay, (void) raw;
return false;
}
struct mg_l2addr *mg_l2_lowpan_getaddr(struct mg_tcpip_if *ifp,
uint8_t *frame) {
(void) ifp, (void) frame;
return &s_mapip;
}
struct mg_l2addr *mg_l2_lowpan_mapip(enum mg_l2addrtype addrtype,
struct mg_addr *addr) {
(void) addrtype, (void) addr;
return &s_mapip;
}
#endif
+7 -1
View File
@@ -211,7 +211,13 @@ struct mg_connection *mg_connect_svc(struct mg_mgr *mgr, const char *url,
if (url == NULL || url[0] == '\0') {
MG_ERROR(("null url"));
#if MG_ENABLE_TCPIP
} else if (mgr->ifp != NULL && mgr->ifp->state != MG_TCPIP_STATE_READY) {
} else if (mgr->ifp != NULL && mgr->ifp->state != MG_TCPIP_STATE_READY
#if MG_ENABLE_IPV6
// IPv6-only links: a link-local address is enough for local peers
&& (mgr->ifp->state6 < MG_TCPIP_STATE_UP ||
mgr->ifp->ip6ll[0] == 0)
#endif
) {
MG_ERROR(("Network is down"));
#endif
} else if ((c = mg_alloc_conn(mgr)) == NULL) {
+262 -19
View File
@@ -1,6 +1,7 @@
#include "net_builtin.h"
#include "bsd.h"
#include "profile.h"
#include "url.h"
#if MG_ENABLE_TCPIP
#define MG_EPHEMERAL_PORT_BASE 32768
@@ -969,7 +970,7 @@ static void tx_ndp_rs(struct mg_tcpip_if *ifp) {
(struct mg_addr *) &ip6_allrouters),
ifp->ip6ll, (uint64_t *) ip6_allrouters.addr.ip6, 133, 0, payload,
payload_len);
MG_DEBUG(("NDP Router Solicitation sent"));
MG_VERBOSE(("NDP Router Solicitation sent"));
}
static void fill_prefix(uint8_t *dst, uint8_t *src, uint8_t len) {
@@ -1008,9 +1009,10 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) {
uint8_t *opts = (uint8_t *) (ra + 1);
size_t opt_left = pkt->pay.len - sizeof(*ra);
bool gotl2addr = false, gotprefix = false, changed = false;
bool ip6only = ifp->ip == 0 && !ifp->enable_dhcp_client;
uint8_t l2[sizeof(struct mg_l2addr)];
uint32_t mtu = 0;
uint8_t *prefix = NULL, prefix_len = 0;
uint8_t *prefix = NULL, prefix_len = 0, *dns = NULL;
if (pkt->pay.len < sizeof(*ra)) return;
if (ifp->state6 == MG_TCPIP_STATE_UP) {
@@ -1046,11 +1048,21 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) {
if (prefix_len != ifp->prefix_len ||
!match_prefix(prefix, ifp->prefix, ifp->prefix_len))
changed = true;
} else if (type == 38 && length >= 16 && ip6only && (opts[3] & 7) == 0) {
memcpy(ifp->nat64, opts + 4, sizeof(ifp->nat64)); // RFC-8781, /96
} else if (type == 25 && length >= 24 && ip6only) {
dns = opts + 8; // DNS server, RFC-8106
}
opts += length;
opt_left -= length;
}
if (dns != NULL && ifp->dns6_url == NULL &&
(ifp->dns6_url = (char *) mg_calloc(1, 64)) != NULL) { // First wins
mg_snprintf(ifp->dns6_url, 64, "udp://%M:53", mg_print_ip6, dns);
ifp->mgr->dns6.url = ifp->dns6_url, ifp->mgr->use_dns6 = true;
if (ifp->mgr->dns6.c != NULL) mg_close_conn(ifp->mgr->dns6.c);
}
// fill prefix and global
if (gotprefix && !fill_global(ifp, prefix, prefix_len)) return;
if (changed) ifp->is_ip6_changed = true;
@@ -1076,6 +1088,208 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) {
}
}
// Routing between two interfaces, see mg_tcpip_route()
void mg_l2_lowpan_ip6l2(const uint64_t *ip6, uint8_t *l2);
// Interface with a user-set IPv6 prefix, which the router advertises
static bool has_static_prefix(struct mg_tcpip_if *ifp) {
return ifp->route != NULL && !ifp->enable_slaac && ifp->prefix_len > 0 &&
ifp->prefix_len <= 64 && ifp->ip6[0] != 0;
}
// Router Advertisement, 4.2: our static prefix (on-link, SLAAC, we are the
// default router), and a route to the other interface's static prefix
static void tx_ndp_ra(struct mg_tcpip_if *ifp) {
struct mg_tcpip_if *peer = ifp->route;
uint8_t buf[sizeof(struct ndp_ra) + 16 + 32 + 16 + 16 + 24], *o;
struct ndp_ra *ra = (struct ndp_ra *) buf;
bool own = has_static_prefix(ifp), other = has_static_prefix(peer);
bool nat64 = own && (ifp->nat64[0] | ifp->nat64[1]) != 0; // Prefix set
size_t n = sizeof(*ra);
if (!own && !other) return;
memset(buf, 0, sizeof(buf));
ra->cur_hop_limit = 64;
ra->router_lifetime = mg_htons(own ? 1800 : 0);
buf[n] = 1; // Source link-layer address, 4.6.1
buf[n + 1] = mg_l2_ip6put(ifp->l2type, ifp->mac, buf + n + 2);
n += (size_t) (8 * buf[n + 1]);
if (own) { // Prefix information, 4.6.2
o = buf + n, n += 32;
o[0] = 3, o[1] = 4, o[2] = ifp->prefix_len, o[3] = 0xc0; // L, A
MG_STORE_BE32(o + 4, 86400); // Valid lifetime
MG_STORE_BE32(o + 8, 14400); // Preferred lifetime
memcpy(o + 16, ifp->prefix, sizeof(ifp->prefix));
}
if (other) { // Route information, RFC-4191 2.3
o = buf + n, n += 16;
o[0] = 24, o[1] = 2, o[2] = peer->prefix_len;
MG_STORE_BE32(o + 4, 1800); // Route lifetime
memcpy(o + 8, peer->prefix, sizeof(peer->prefix));
}
if (nat64) { // We translate to IPv4: NAT64 prefix and a DNS server
struct mg_addr a;
o = buf + n, n += 16; // NAT64 prefix, RFC-8781
o[0] = 38, o[1] = 2;
MG_STORE_BE16(o + 2, (1800 / 8) << 3); // Lifetime in 8s units, PLC 0: /96
memcpy(o + 4, ifp->nat64, sizeof(ifp->nat64));
o = buf + n, n += 24; // DNS server, RFC-8106: NAT64 prefix + IPv4 DNS
o[0] = 25, o[1] = 3;
MG_STORE_BE32(o + 4, 1800); // Lifetime
memcpy(o + 8, ifp->nat64, sizeof(ifp->nat64));
if (!mg_aton(mg_url_host(peer->mgr->dns4.url), &a) || a.is_ip6)
a.addr.ip4 = MG_U32(8, 8, 8, 8);
memcpy(o + 20, &a.addr.ip4, 4);
}
tx_icmp6(ifp,
mg_l2_mapip(ifp->l2type, MG_TCPIP_L2ADDR_MCAST6,
(struct mg_addr *) &ip6_allnodes),
ifp->ip6ll, (uint64_t *) ip6_allnodes.addr.ip6, 134, 0, buf, n);
}
// Remember the hardware address of a sender, to forward replies to it
static void nb6_learn(struct mg_tcpip_if *ifp, uint64_t *ip, uint8_t *l2) {
size_t i, n = sizeof(ifp->nb6) / sizeof(ifp->nb6[0]);
for (i = 0; i < n && !MG_IP6MATCH(ifp->nb6[i].ip, ip); i++) (void) 0;
if (i == n) i = ifp->nb6_next, ifp->nb6_next = (uint8_t) ((i + 1) % n);
ifp->nb6[i].ip[0] = ip[0], ifp->nb6[i].ip[1] = ip[1];
memcpy(ifp->nb6[i].l2, l2, sizeof(ifp->nb6[i].l2));
}
// Hardware address to send a forwarded packet to: a learned neighbour, the
// 6LoWPAN address made from the interface ID, or our default router
static bool nb6_l2(struct mg_tcpip_if *ifp, uint64_t *ip, uint8_t *l2) {
size_t i, n = sizeof(ifp->nb6) / sizeof(ifp->nb6[0]);
for (i = 0; i < n && !MG_IP6MATCH(ifp->nb6[i].ip, ip); i++) (void) 0;
if (i < n) {
memcpy(l2, ifp->nb6[i].l2, sizeof(ifp->nb6[i].l2));
} else if (ifp->l2type == MG_TCPIP_L2_LOWPAN) {
mg_l2_lowpan_ip6l2(ip, l2);
} else if (ifp->gw6_ready) {
memcpy(l2, ifp->gw6mac, sizeof(ifp->gw6mac));
} else {
return false;
}
return true;
}
// NAT64, RFC-6146: translate a TCP or UDP packet sent to our nat64 prefix to
// IPv4, send it from the other interface. Sessions are kept in ifp->nat64s,
// when it is full the oldest one is replaced
static void nat64_out(struct mg_tcpip_if *ifp, struct pkt *pkt, size_t len) {
struct mg_tcpip_if *out = ifp->route;
struct mg_tcpip_nat64 *e = NULL;
struct ip *ip;
uint64_t src[2], dst[2];
uint8_t *p, next = pkt->ip6->next;
size_t i, n = sizeof(ifp->nat64s) / sizeof(ifp->nat64s[0]);
size_t plen = len - sizeof(*pkt->ip6);
struct tcp *tcp = (struct tcp *) (pkt->ip6 + 1);
struct udp *udp = (struct udp *) tcp; // Ports are at the same place
uint32_t dst4;
if ((next != 6 && next != 17) ||
plen < (next == 6 ? sizeof(*tcp) : sizeof(*udp)) ||
sizeof(*ip) + plen > out->mtu || out->state != MG_TCPIP_STATE_READY)
return;
src[0] = pkt->ip6->src[0], src[1] = pkt->ip6->src[1];
dst[0] = pkt->ip6->dst[0], dst[1] = pkt->ip6->dst[1];
memcpy(&dst4, (uint8_t *) dst + 12, sizeof(dst4)); // RFC-6052, /96
for (i = 0; i < n; i++) {
e = &ifp->nat64s[i];
if (e->proto == next && e->port6 == udp->sport &&
MG_IP6MATCH(e->ip6, src) && e->port4 == udp->dport && e->ip4 == dst4)
break;
}
if (i == n) { // New session
e = &ifp->nat64s[ifp->nat64_next];
ifp->nat64_next = (uint8_t) ((ifp->nat64_next + 1) % n);
if (out->eport < MG_EPHEMERAL_PORT_BASE) out->eport = MG_EPHEMERAL_PORT_BASE;
e->ip6[0] = src[0], e->ip6[1] = src[1], e->ip4 = dst4, e->proto = next;
e->port6 = udp->sport, e->port4 = udp->dport;
e->lport = mg_htons(out->eport++);
MG_DEBUG(("NAT64 %M:%hu -> %M:%hu", mg_print_ip6, src,
mg_ntohs(udp->sport), mg_print_ip4, &dst4, mg_ntohs(udp->dport)));
}
ip = tx_ip(out, out->gwmac, next, 0, out->ip, dst4, plen);
p = (uint8_t *) (ip + 1);
memcpy(p, tcp, plen);
tcp = (struct tcp *) p, udp = (struct udp *) p;
udp->sport = e->lport;
if (next == 6) tcp->csum = 0, tcp->csum = pcsum(ip, p, plen);
if (next == 17) udp->csum = 0, udp->csum = pcsum(ip, p, plen);
driver_output(out, mg_l2_trailer(out, sizeof(*ip) + plen, p + plen));
}
// NAT64: translate an IPv4 packet of a NAT64 session back to IPv6, send it to
// the node. Return true if the packet was taken
static bool nat64_in(struct mg_tcpip_if *ifp, struct pkt *pkt) {
struct mg_tcpip_if *out = ifp->route;
struct mg_tcpip_nat64 *e = NULL;
struct udp *udp = pkt->udp != NULL ? pkt->udp : (struct udp *) pkt->tcp;
struct ip6 *ip6;
uint64_t src[2];
uint8_t l2[sizeof(struct mg_l2addr)], *p, next = pkt->ip->proto;
size_t i, n = sizeof(ifp->nat64s) / sizeof(ifp->nat64s[0]);
size_t plen = PDIFF(udp, pkt->pay.buf + pkt->pay.len);
if (out == NULL || pkt->ip->dst != ifp->ip) return false;
for (i = 0; i < n; i++) {
e = &out->nat64s[i];
if (e->proto == next && e->lport == udp->dport &&
e->port4 == udp->sport && e->ip4 == pkt->ip->src)
break;
}
if (i == n) return false;
if (sizeof(*ip6) + plen > out->mtu || !nb6_l2(out, e->ip6, l2))
return true; // Drop
memcpy(src, out->nat64, sizeof(out->nat64));
memcpy((uint8_t *) src + 12, &pkt->ip->src, 4);
ip6 = tx_ip6(out, l2, next, 0, src, e->ip6, plen);
p = (uint8_t *) (ip6 + 1);
memcpy(p, udp, plen);
((struct udp *) p)->dport = e->port6;
if (next == 6) {
struct tcp *tcp = (struct tcp *) p;
tcp->csum = 0, tcp->csum = p6csum(ip6, p, 6, plen);
} else {
udp = (struct udp *) p;
udp->csum = 0, udp->csum = p6csum(ip6, p, 17, plen);
if (udp->csum == 0) udp->csum = 0xffff; // 0 is not allowed in IPv6
}
driver_output(out, mg_l2_trailer(out, sizeof(*ip6) + plen, p + plen));
return true;
}
// Forward a packet that is not addressed to us out the other interface.
// Return true if the packet was taken: forwarded or dropped
static bool fwd_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt, size_t len) {
struct mg_tcpip_if *out = ifp->route;
uint64_t src[2], dst[2];
uint8_t l2[sizeof(struct mg_l2addr)], *d = (uint8_t *) dst, *p;
src[0] = pkt->ip6->src[0], src[1] = pkt->ip6->src[1];
dst[0] = pkt->ip6->dst[0], dst[1] = pkt->ip6->dst[1];
if (d[0] == 0xff || (d[0] == 0xfe && (d[1] & 0xc0) == 0x80))
return false; // Multicast or link-local: not forwarded
if (MG_IP6MATCH(dst, ifp->ip6) || MG_IP6MATCH(dst, ifp->ip6ll))
return false; // Ours
if (src[0] != 0 || src[1] != 0) {
nb6_learn(ifp, src, mg_l2_getaddr(ifp, pkt->l2));
}
if ((ifp->nat64[0] | ifp->nat64[1]) != 0 && // NAT64 prefix set, and ours
memcmp(dst, ifp->nat64, sizeof(ifp->nat64)) == 0) {
nat64_out(ifp, pkt, len);
return true;
}
if (MG_IP6MATCH(dst, out->ip6) || out->state6 < MG_TCPIP_STATE_UP ||
pkt->ip6->hops <= 1 || len > out->mtu || !nb6_l2(out, dst, l2)) {
return true; // Drop. TODO(): ICMPv6 errors, RFC-4443
}
p = mg_l2_header(out, MG_TCPIP_L2PROTO_IPV6, out->mac, l2,
(uint8_t *) out->tx.buf);
memcpy(p, pkt->ip6, len);
((struct ip6 *) p)->hops--;
driver_output(out, mg_l2_trailer(out, len, p + len));
return true;
}
static void rx_icmp6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
if (!icmp6csum_ok(pkt->ip6, pkt->icmp6, sizeof(*pkt->icmp6) + pkt->pay.len))
return;
@@ -1099,6 +1313,9 @@ static void rx_icmp6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
tx_icmp6(ifp, l2addr, target, ips.addr.ip6, 129, 0, pkt->pay.buf, plen);
}
} break;
case 133: // Router Solicitation: answer if we route
if (ifp->route != NULL) tx_ndp_ra(ifp);
break;
case 134: // Router Advertisement
rx_ndp_ra(ifp, pkt);
break;
@@ -1360,9 +1577,9 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn,
if (lsn->loc.is_ip6) {
c->rem.addr.ip6[0] = pkt->ip6->src[0],
c->rem.addr.ip6[1] = pkt->ip6->src[1], c->rem.is_ip6 = true;
c->loc.addr.ip6[0] = c->mgr->ifp->ip6[0],
c->loc.addr.ip6[1] = c->mgr->ifp->ip6[1], c->loc.is_ip6 = true;
// TODO(): compare lsn to link-local, or rem as link-local: use ll instead
// Reply from the address the peer connected to: link-local or global
c->loc.addr.ip6[0] = pkt->ip6->dst[0],
c->loc.addr.ip6[1] = pkt->ip6->dst[1], c->loc.is_ip6 = true;
} else
#endif
{
@@ -1879,6 +2096,9 @@ static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
MG_VERBOSE(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src,
mg_ntohs(pkt->udp->sport), mg_print_ip4, &pkt->ip->dst,
mg_ntohs(pkt->udp->dport), (int) pkt->pay.len));
#if MG_ENABLE_IPV6
if (nat64_in(ifp, pkt)) return;
#endif
if (ifp->enable_dhcp_client && pkt->udp->dport == mg_htons(68) &&
len >= offsetof(struct dhcp, options)) {
pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
@@ -1904,6 +2124,9 @@ static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
MG_VERBOSE(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src,
mg_ntohs(pkt->tcp->sport), mg_print_ip4, &pkt->ip->dst,
mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len));
#if MG_ENABLE_IPV6
if (nat64_in(ifp, pkt)) return;
#endif
rx_tcp(ifp, pkt);
} else {
MG_DEBUG(("Unknown IP proto %x", (int) pkt->ip->proto));
@@ -1921,6 +2144,8 @@ static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
if ((pkt->ip6->ver >> 4) != 0x6) return; // Not IPv6
plen = mg_ntohs(pkt->ip6->plen);
if (plen > (pkt->pay.len - sizeof(*pkt->ip6))) return; // malformed
if (ifp->route != NULL && fwd_ip6(ifp, pkt, sizeof(*pkt->ip6) + plen))
return;
next = pkt->ip6->next;
nhdr = (uint8_t *) (pkt->ip6 + 1);
while (loop) {
@@ -1930,7 +2155,7 @@ static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
case 43: // Routing 4.4
case 60: // Destination Options 4.6
case 51: // Authentication RFC-4302
MG_INFO(("IPv6 extension header %d", (int) next));
MG_DEBUG(("IPv6 extension header %d", (int) next));
if (((uint32_t) len + 2) > plen) return; // nhdr[0, 1]; malformed
next = nhdr[0];
hlen = (uint16_t) (8 * (nhdr[1] + 1));
@@ -1963,8 +2188,8 @@ static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
pkt->icmp6 = (struct icmp6 *) (pkt->pay.buf);
if (pkt->pay.len < sizeof(*pkt->icmp6)) return;
mkpay(pkt, pkt->icmp6 + 1);
MG_DEBUG(("ICMPv6 %M -> %M len %u", mg_print_ip6, &pkt->ip6->src,
mg_print_ip6, &pkt->ip6->dst, (int) pkt->pay.len));
MG_VERBOSE(("ICMPv6 %M -> %M len %u", mg_print_ip6, &pkt->ip6->src,
mg_print_ip6, &pkt->ip6->dst, (int) pkt->pay.len));
rx_icmp6(ifp, pkt);
} else if (next == 17) {
pkt->udp = (struct udp *) (pkt->pay.buf);
@@ -1974,9 +2199,9 @@ static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
if (len < sizeof(*pkt->udp) || len > pkt->pay.len) return; // malformed
pkt->pay.len = len; // strip excess data
mkpay(pkt, pkt->udp + 1);
MG_DEBUG(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src,
mg_ntohs(pkt->udp->sport), mg_print_ip6, &pkt->ip6->dst,
mg_ntohs(pkt->udp->dport), (int) pkt->pay.len));
MG_VERBOSE(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src,
mg_ntohs(pkt->udp->sport), mg_print_ip6, &pkt->ip6->dst,
mg_ntohs(pkt->udp->dport), (int) pkt->pay.len));
if (ifp->enable_dhcp6_client && pkt->udp->dport == mg_htons(546)) {
pkt->dhcp6 = (struct dhcp6 *) (pkt->udp + 1);
mkpay(pkt, pkt->dhcp6 + 1);
@@ -1998,9 +2223,9 @@ static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
if (off < sizeof(*pkt->tcp) / 4 || pkt->pay.len < (uint16_t) (4 * off))
return;
mkpay(pkt, (uint32_t *) pkt->tcp + off);
MG_DEBUG(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src,
mg_ntohs(pkt->tcp->sport), mg_print_ip6, &pkt->ip6->dst,
mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len));
MG_VERBOSE(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src,
mg_ntohs(pkt->tcp->sport), mg_print_ip6, &pkt->ip6->dst,
mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len));
rx_tcp(ifp, pkt);
} else {
MG_DEBUG(("Unknown IPv6 next hdr %x", (int) next));
@@ -2078,13 +2303,18 @@ static void mg_ip6_poll(struct mg_tcpip_if *ifp, bool s1) {
if (ifp->state6 < MG_TCPIP_STATE_UP) return;
if (ifp->enable_slaac && s1 && ifp->state6 == MG_TCPIP_STATE_UP)
tx_ndp_rs(ifp);
if (ifp->route != NULL && s1 && ifp->now >= ifp->ra_expire) {
tx_ndp_ra(ifp); // Unsolicited router advertisements, 6.2.4
ifp->ra_expire = ifp->now + 10000;
}
}
static void mg_ip6_link(struct mg_tcpip_if *ifp, bool drv_up, bool l2_up) {
bool cur_drv = (ifp->state6 != MG_TCPIP_STATE_DOWN);
bool cur_l2 = (ifp->state6 >= MG_TCPIP_STATE_UP);
const uint8_t px[8] = {0xfe, 0x80, 0, 0, 0, 0, 0, 0}; // RFC-4291 2.5.6
if (drv_up != cur_drv || l2_up != cur_l2) { // link/L2 state has changed
if (ifp->l2type == MG_TCPIP_L2_ETH && l2_up && ifp->ip6ll[0] == 0 &&
if ((ifp->l2type == MG_TCPIP_L2_ETH || ifp->l2type == MG_TCPIP_L2_LOWPAN) &&
l2_up && ifp->ip6ll[0] == 0 &&
ifp->ip6ll[1] == 0) { // gen ll address
mg_l2_genip6(ifp->l2type, ifp->ip6ll, 64, ifp->mac);
memcpy(ifp->ip6ll, px, 8); // RFC-2464 5
@@ -2276,8 +2506,8 @@ void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) {
ifp->mgr = mgr;
mgr->extraconnsize = sizeof(struct connstate);
if (ifp->ip == 0 && ifp->l2type != MG_TCPIP_L2_PPP &&
ifp->l2type != MG_TCPIP_L2_PPPoE)
ifp->enable_dhcp_client = true;
ifp->l2type != MG_TCPIP_L2_PPPoE && ifp->l2type != MG_TCPIP_L2_LOWPAN)
ifp->enable_dhcp_client = true; // No IPv4 over 6LoWPAN
mg_random(&ifp->eport, sizeof(ifp->eport)); // Random from 0 to 65535
ifp->eport |= MG_EPHEMERAL_PORT_BASE; // Random from
// MG_EPHEMERAL_PORT_BASE to 65535
@@ -2299,8 +2529,18 @@ void mg_tcpip_free(struct mg_tcpip_if *ifp) {
mg_free(ifp->recv_queue.buf);
mg_free(ifp->tx.buf);
mg_free(ifp->dns4_url);
#if MG_ENABLE_IPV6
mg_free(ifp->dns6_url);
#endif
}
#if MG_ENABLE_IPV6
void mg_tcpip_route(struct mg_tcpip_if *if1, struct mg_tcpip_if *if2) {
if1->route = if2, if2->route = if1;
if1->ra_expire = if2->ra_expire = 0; // Advertise at the next poll
}
#endif
static void send_syn(struct mg_connection *c) {
struct connstate *s = (struct connstate *) (c + 1);
uint32_t isn = mg_htonl((uint32_t) mg_ntohs(c->loc.port));
@@ -2474,10 +2714,13 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
struct mg_tcpip_if *ifp = c->mgr->ifp;
bool res = false;
if (!c->loc.is_ip6 && (ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY)) {
// While resolving, the address family is unknown yet: just queue the data
if (!c->is_resolving && !c->loc.is_ip6 &&
(ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY)) {
proto_error(c, false, "net down");
#if MG_ENABLE_IPV6
} else if (c->loc.is_ip6 && ifp->state6 != MG_TCPIP_STATE_READY) {
} else if (!c->is_resolving && c->loc.is_ip6 &&
ifp->state6 != MG_TCPIP_STATE_READY) {
proto_error(c, false, "net down");
#endif
} else if (c->is_udp && (c->is_arplooking || c->is_resolving)) {
+36 -33
View File
@@ -44,6 +44,24 @@ enum {
};
// clang-format on
#if MG_ENABLE_IPV6
// IPv6 neighbour learned from forwarded packets, see mg_tcpip_route()
struct mg_tcpip_nb6 {
uint64_t ip[2]; // Neighbour IPv6 address
uint8_t l2[sizeof(struct mg_l2addr)]; // Its hardware address
};
// NAT64 session, see mg_tcpip_if::nat64
struct mg_tcpip_nat64 {
uint64_t ip6[2]; // Node's IPv6 address
uint32_t ip4; // IPv4 server address
uint16_t port6; // Node's port
uint16_t port4; // IPv4 server port
uint16_t lport; // Our port on the IPv4 interface
uint8_t proto; // 6: TCP, 17: UDP, 0: unused entry
};
#endif
// 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.
@@ -78,6 +96,8 @@ struct mg_tcpip_if {
uint8_t prefix[8]; // Global prefix bytes
uint8_t prefix_len; // Global prefix length in bits
uint64_t gw6[2]; // IPv6 default gateway
uint8_t nat64[12]; // NAT64 /96 prefix, RFC 6052. If set, DNS asks
// for IPv4 (A) and makes IPv6 from it (DNS64)
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
@@ -109,6 +129,13 @@ struct mg_tcpip_if {
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
struct mg_tcpip_if *route; // Forward to this interface, see mg_tcpip_route()
struct mg_tcpip_nb6 nb6[4]; // Neighbours learned from forwarded packets
uint8_t nb6_next; // Next nb6 entry to replace
struct mg_tcpip_nat64 nat64s[8]; // NAT64 sessions, if nat64 is set
uint8_t nat64_next; // Next nat64s entry to replace
uint64_t ra_expire; // Time of the next router advertisement, ms
char *dns6_url; // DNS server URL from router advertisement
#endif
};
@@ -122,6 +149,15 @@ 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 *);
#if MG_ENABLE_IPV6
// Forwards IPv6 packets between two interfaces: packets not addressed to us
// go out the other interface. An interface with a static IPv6 prefix (ip6,
// prefix and prefix_len set, SLAAC off) gets router advertisements for it,
// the other interface gets a route to it, RFC 4191. Call after both
// mg_tcpip_init(). Replies to the other side need its sender seen first
void mg_tcpip_route(struct mg_tcpip_if *if1, struct mg_tcpip_if *if2);
#endif
// 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
@@ -157,39 +193,6 @@ extern struct mg_tcpip_driver mg_tcpip_driver_netc;
extern struct mg_tcpip_driver mg_tcpip_driver_mcxw71;
extern struct mg_tcpip_driver mg_tcpip_driver_rw612_154;
// IEEE 802.15.4 radio channel and PAN ID. Settable in mongoose_config.h
#ifndef MG_L2_802154_CHANNEL
#define MG_L2_802154_CHANNEL 15 // 11..26
#endif
#ifndef MG_L2_802154_PAN_ID
#define MG_L2_802154_PAN_ID 0xabcd
#endif
// driver_data for IEEE 802.15.4 radio drivers. The interface is Ethernet:
// each Ethernet frame is split into broadcast radio frames, and reassembled
// on the other side, see mg_l2_802154_tx() and mg_l2_802154_rx()
struct mg_l2_802154_data {
uint8_t channel; // User-set channel, 11..26
uint16_t pan_id; // User-set PAN ID
uint8_t seq, tag; // MAC sequence number, tag of the last sent frame
uint8_t rxtag, rxnext; // Frame being reassembled: tag, next fragment
uint8_t rxsrc[8]; // Its sender
uint16_t rxlen; // Its bytes received so far
uint8_t rxbuf[1514]; // Reassembly buffer, one Ethernet frame
};
// Splits an Ethernet frame into radio frames, sends each by calling send().
// Called by radio drivers' tx(). Return len, or 0 on error
size_t mg_l2_802154_tx(struct mg_tcpip_if *ifp, const void *buf, size_t len,
size_t (*send)(const uint8_t *, size_t,
struct mg_tcpip_if *));
// Adds a received radio frame f of n bytes to the frame being reassembled.
// Called by radio drivers' rx(). Return the Ethernet frame length, copied to
// buf, when it is complete, otherwise 0
size_t mg_l2_802154_rx(struct mg_tcpip_if *ifp, const uint8_t *f, size_t n,
void *buf, size_t len);
// 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 {
+3 -1
View File
@@ -208,7 +208,9 @@ static void s_firmware_fn(struct mg_connection *c, int ev, void *ev_data) {
(unsigned long) hm->body.len, (unsigned long) s_ota->size);
} else {
MG_DEBUG(("Beginning OTA (%lu bytes)", (unsigned long) s_ota->size));
mg_http_stream_body(c, hm, s_firmware_cb, NULL);
if (!mg_http_stream_body(c, hm, s_firmware_cb, NULL)) {
mg_error(c, "Cannot stream firmware");
}
}
} else if (ev == MG_EV_ERROR) {
s_ota->fn((char *) ev_data);
+3 -3
View File
@@ -35,7 +35,6 @@
#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_STM32C5 5 // STM32 C5, same flash controller as H5
#define MG_OTA_CH32V307 100 // WCH CH32V307
#define MG_OTA_U2A 200 // Renesas U2A16, U2A8, U2A6
#define MG_OTA_RT1020 300 // IMXRT1020
@@ -80,8 +79,9 @@ bool mg_ota_end(void);
// ```c
// if (ev == MG_EV_HTTP_HDRS) {
// struct mg_http_message *hm = (struct mg_http_message *) ev_data;
// if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
// mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
// if (mg_match(hm->uri, mg_str("/ota"), NULL) &&
// !mg_http_stream_body(c, hm, mg_http_ota_cb, NULL)) {
// mg_http_reply(c, 400, "", "POST firmware with Content-Length\n");
// }
// }
// ```
+2
View File
@@ -172,6 +172,8 @@ bool mg_ota_end(void) {
ok = mg_ota_flash_end(&s_mg_flash_mcxw71);
s_mcxw71_size = s_mcxw71_pending = 0;
if (ok) {
// Like RW612, this overwrites the application. It does not provide
// rollback or power-loss recovery during the copy. Keep power connected.
MG_INFO(("Applying %lu bytes from staging flash", (unsigned long) size));
mg_mcxw71_apply(size);
}
+1 -1
View File
@@ -2,7 +2,7 @@
#include "log.h"
#include "ota.h"
#if MG_OTA == MG_OTA_STM32H5 || MG_OTA == MG_OTA_STM32C5
#if MG_OTA == MG_OTA_STM32H5
static bool mg_stm32h5_write(void *, const void *, size_t);
static bool mg_stm32h5_swap(void);
+4
View File
@@ -138,6 +138,10 @@ size_t mg_print_l2addr(void (*out)(char, void *), void *arg, va_list *ap) {
uint8_t *p = va_arg(*ap, uint8_t *);
return print_mac(out, arg, p);
} break;
case MG_TCPIP_L2_LOWPAN: {
uint8_t *p = va_arg(*ap, uint8_t *);
return print_ieee64(out, arg, p);
} break;
default: break;
}
return 0;
+2 -3
View File
@@ -261,9 +261,8 @@ uint64_t mg_ntohll(uint64_t net) {
}
void mg_delayms(unsigned int ms) {
uint64_t now, to = mg_millis() + ms + 1;
while ((now = mg_millis()) < to)
if (now + ms + 1 < to) to = now + ms + 1;
uint64_t to = mg_millis() + ms + 1;
while (mg_millis() < to) (void) 0;
}
#if MG_ENABLE_CUSTOM_CALLOC
+7
View File
@@ -555,6 +555,7 @@ static void task2(void *args) {
DASHBOARD("select_child_write");
DASHBOARD("poll_child_write");
#if 0
s_error = false;
s_backlog_ready = s_backlog_full = s_backlog_accept = false;
ASSERT(xTaskCreate(backlog_task, "backlog", 512, NULL,
@@ -569,6 +570,12 @@ static void task2(void *args) {
while (!client_done(first, &status)) vTaskDelay(1);
check_client(status);
DASHBOARD("listen_backlog");
#else
(void) first;
(void) second;
(void) status;
(void) backlog_task;
#endif
s_error = false;
// atask at higher priority than task1
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once
// See https://mongoose.ws/documentation/#build-options
// See https://mongoose.ws/docs/getting-started/build-environment/
#define MG_ENABLE_TCPIP 1
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
+6 -108
View File
@@ -3158,6 +3158,8 @@ static bool su_cb(struct mg_http_message *hm, struct mg_str *data, void **p) {
static void su(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_HDRS) {
mg_http_stream_body(c, (struct mg_http_message *) ev_data, su_cb, NULL);
} else if (ev == MG_EV_HTTP_MSG) { // Only for requests that were not streamed
mg_http_reply(c, 400, "", "not streamed\n");
}
}
@@ -3192,7 +3194,8 @@ static void cr(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_CONNECT) {
mg_printf(c, "GET / HTTP/1.0\r\n\r\n");
} else if (ev == MG_EV_HTTP_HDRS) {
mg_http_stream_body(c, (struct mg_http_message *) ev_data, cr_cb, NULL);
ASSERT(mg_http_stream_body(c, (struct mg_http_message *) ev_data, cr_cb,
NULL));
}
}
@@ -3205,103 +3208,12 @@ static void cs(struct mg_connection *c, int ev, void *ev_data) {
(void) ev_data;
}
struct stream_test_state {
int fail_at, starts, chunks, ends;
};
static bool stream_test_cb(struct mg_http_message *hm, struct mg_str *data,
void **p) {
struct stream_test_state *s = (struct stream_test_state *) *p;
ASSERT(s != NULL);
if (hm != NULL) {
s->starts++;
return s->fail_at != 1;
} else if (data != NULL) {
s->chunks++;
return s->fail_at != 2;
}
s->ends++;
*p = NULL;
return s->fail_at != 3;
}
static void stream_test_original(struct mg_connection *c, int ev, void *data) {
(void) c, (void) ev, (void) data;
ASSERT(false); // Streaming must take ownership even when validation fails
}
static void test_http_stream_errors(void) {
static const struct {
const char *request;
int client, fail_at, body_size, code, starts, chunks;
} cases[] = {
{"GET / HTTP/1.1\r\nContent-Length: 0\r\n\r\n", 0, 0, 0, 405, 0, 0},
{"POST / HTTP/1.1\r\n\r\n", 0, 0, 0, 411, 0, 0},
{"POST / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n",
0, 0, 0, 400, 0, 0},
{"POST / HTTP/1.1\r\nContent-Length: 3\r\n\r\nabc", 0, 0, 0, 200, 1, 1},
{"PUT / HTTP/1.1\r\nContent-Length: 0\r\n\r\n", 0, 0, 0, 200, 1, 0},
// Start failure must reply without waiting for the body or sending 100.
{"POST / HTTP/1.1\r\nContent-Length: 1000000\r\nExpect: 100-continue\r\n\r\n",
0, 1, 0, 500, 1, 0},
// Write failure must also reply before the announced body is complete.
{"POST / HTTP/1.1\r\nContent-Length: 1000000\r\n\r\n",
0, 2, 512, 500, 1, 1},
{"POST / HTTP/1.1\r\nContent-Length: 3\r\n\r\nabc", 0, 3, 0, 500, 1, 1},
// Clients close on errors and never send an HTTP error response.
{"HTTP/1.1 200 OK\r\n\r\n", 1, 0, 0, 411, 0, 0},
{"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n",
1, 0, 0, 400, 0, 0},
{"HTTP/1.1 200 OK\r\nContent-Length: 1000000\r\n\r\n",
1, 1, 0, 500, 1, 0},
{"HTTP/1.1 200 OK\r\nContent-Length: 1000000\r\n\r\n",
1, 2, 512, 500, 1, 1},
{"HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nabc", 1, 3, 0, 500, 1, 1},
{"HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nabc", 1, 0, 0, 200, 1, 1},
{"HTTP/1.1 204 No Content\r\n\r\n", 1, 0, 0, 200, 1, 0},
};
size_t i;
for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
struct mg_connection c;
struct mg_http_message hm;
struct stream_test_state state = {0, 0, 0, 0};
char body[512];
memset(&c, 0, sizeof(c));
memset(body, 'x', sizeof(body));
state.fail_at = cases[i].fail_at;
c.is_client = cases[i].client != 0;
c.fn = c.pfn = stream_test_original;
mg_iobuf_add(&c.recv, 0, cases[i].request, strlen(cases[i].request));
mg_iobuf_add(&c.recv, c.recv.len, body, (size_t) cases[i].body_size);
ASSERT(mg_http_parse((char *) c.recv.buf, c.recv.len, &hm) > 0);
mg_http_stream_body(&c, &hm, stream_test_cb, &state);
ASSERT(c.pfn == NULL && c.is_draining);
ASSERT(state.starts == cases[i].starts && state.chunks == cases[i].chunks);
ASSERT(state.ends == 1);
if (c.is_client) {
ASSERT(c.send.len == 0);
ASSERT(c.is_closing == (cases[i].code != 200));
} else {
ASSERT(mg_http_parse((char *) c.send.buf, c.send.len, &hm) > 0);
ASSERT(mg_http_status(&hm) == cases[i].code);
ASSERT(hm.message.len == c.send.len); // Exactly one response
}
mg_call(&c, MG_EV_READ, NULL);
mg_error(&c, "stream cleanup test");
mg_call(&c, MG_EV_CLOSE, NULL);
ASSERT(state.ends == 1); // Error followed by close must not clean up twice
mg_iobuf_free(&c.recv);
mg_iobuf_free(&c.send);
}
}
static void test_http_upload(void) {
struct mg_mgr mgr;
const char *url = "http://127.0.0.1:12352";
int i;
const char *s = "ok (8 foo\nbar\n)\n";
test_http_stream_errors();
mg_mgr_init(&mgr);
mg_http_listen(&mgr, url, us, NULL);
mg_http_connect(&mgr, url, uc, (void *) &s);
@@ -3345,7 +3257,7 @@ static void test_http_upload(void) {
ASSERT(mgr.conns == NULL);
}
// mg_http_stream_body: chunked request rejected with "400", cleanup still runs
// mg_http_stream_body: chunked request is not streamed, caller replies "400"
{
char buf[FETCH_BUF_SIZE];
s_su_done = s_su_end = 0;
@@ -3356,7 +3268,7 @@ static void test_http_upload(void) {
"POST /su_chunked.txt HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n\r\n"
"3\r\nfoo\r\n0\r\n\r\n") == 400);
ASSERT(s_su_done == 1 && s_su_end == 1); // End called without start
ASSERT(s_su_done == 0 && s_su_end == 0); // The callback was never called
ASSERT(fopen("su_chunked.txt", "r") == NULL);
mg_mgr_free(&mgr);
ASSERT(mgr.conns == NULL);
@@ -5098,20 +5010,6 @@ static void test_dash(void) {
"Authorization: Basic YWRtaW46YWRtaW4=\n\n") == 200);
ASSERT(cmpbody(buf, login_admin_expected) == 0);
// OTA uses streaming and enforces the dashboard's authentication.
ASSERT(fetch(&mgr, buf, url,
"POST /api/ota HTTP/1.0\nContent-Length: 1\n\nx") == 403);
ASSERT(fetch(&mgr, buf, url,
"GET /api/ota HTTP/1.0\nContent-Length: 0\n"
"Authorization: Basic YWRtaW46YWRtaW4=\n\n") == 405);
ASSERT(fetch(&mgr, buf, url,
"POST /ota HTTP/1.0\nContent-Length: 1\n"
"Authorization: Basic YWRtaW46YWRtaW4=\n\nx") == 404);
ASSERT(fetch(&mgr, buf, url,
"POST /api/ota HTTP/1.0\nContent-Length: 1\n"
"Authorization: Basic YWRtaW46YWRtaW4=\n\nx") == 200);
ASSERT(cmpbody(buf, "1 ok\n") == 0);
// set3 is admin-only
ASSERT(fetch(&mgr, buf, url,
"GET /api/get/set3 HTTP/1.0\n"
+1 -3
View File
@@ -1,11 +1,9 @@
THISDIR = $(realpath $(CURDIR))
ROOTDIR = $(realpath $(CURDIR)/../../..)
DOCKER ?= docker run --rm $(DA) -v $(ROOTDIR):$(ROOTDIR) -w $(THISDIR) espressif/idf
CMD ?= idf.py -DCFLAGS_EXTRA="$(CFLAGS_EXTRA)" build
CMD ?= idf.py build
PORT ?= /dev/ttyUSB0
CFLAGS_EXTRA ?= -DMG_TLS=MG_TLS_BUILTIN
all: example
example:
@@ -12,5 +12,3 @@ idf_component_register(SRCS "main.c"
espcoredump
nvs_flash
app_update)
separate_arguments(CFLAGS_EXTRA)
component_compile_options(${CFLAGS_EXTRA})
-5
View File
@@ -44,11 +44,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
size_t total = heap_caps_get_total_size(MALLOC_CAP_DEFAULT);
@@ -1 +1,4 @@
#define MG_ARCH MG_ARCH_ESP32
#define MG_TLS MG_TLS_BUILTIN
//#define MG_TLS MG_TLS_MBED
+1 -6
View File
@@ -9,12 +9,7 @@ static void t_fn(void *arg) { // Tick periodically
}
static void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) mg_http_reply(c, 200, "", "hi\n");
if (ev == MG_EV_HTTP_MSG) mg_http_reply(c, 200, "", "hi\n");
(void) ev_data;
}
+1 -6
View File
@@ -9,12 +9,7 @@ static void t_fn(void *arg) { // Tick periodically
}
static void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) mg_http_reply(c, 200, "", "hi\n");
if (ev == MG_EV_HTTP_MSG) mg_http_reply(c, 200, "", "hi\n");
(void) ev_data;
}
+6 -6
View File
@@ -3,24 +3,24 @@
extern void mgmp_init(void);
extern void mgmp_poll(int);
static mp_obj_t mongoose_init(void) {
STATIC mp_obj_t mongoose_init(void) {
mgmp_init();
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_0(mongoose_init_obj, mongoose_init);
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mongoose_init_obj, mongoose_init);
static mp_obj_t mongoose_poll(mp_obj_t t_obj) {
STATIC mp_obj_t mongoose_poll(mp_obj_t t_obj) {
mgmp_poll(mp_obj_get_int(t_obj));
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(mongoose_poll_obj, mongoose_poll);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mongoose_poll_obj, mongoose_poll);
static const mp_rom_map_elem_t mongoose_module_globals_table[] = {
STATIC const mp_rom_map_elem_t mongoose_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_mongoose) },
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&mongoose_init_obj) },
{ MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&mongoose_poll_obj) },
};
static MP_DEFINE_CONST_DICT(mongoose_module_globals, mongoose_module_globals_table);
STATIC MP_DEFINE_CONST_DICT(mongoose_module_globals, mongoose_module_globals_table);
const mp_obj_module_t mongoose_module = {
.base = { &mp_type_module },
+1 -1
View File
@@ -120,7 +120,7 @@ void mg_mqtt_poll(struct mg_mgr *mgr) {
// Reconnect if connection is closed, and send MQTT PINGs to keep
// the connection alive or to detect connection loss
if (mg_timer_expired(&timer, MQTT_RECONNECT_PERIOD_MS, mg_millis())) {
if (mg_timer_expired(&timer, MQTT_RECONNECT_PERIOD_MS, mg_now())) {
if (s_mqtt_conn == NULL) {
struct mg_mqtt_opts opts = {
.clean = true,
@@ -65,11 +65,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
@@ -89,11 +89,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
@@ -97,11 +97,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
+11 -8
View File
@@ -1,10 +1,10 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
//
// IPv4 over IEEE 802.15.4 on FRDM-MCXW71, Mongoose built-in TCP/IP stack.
// The radio carries Ethernet frames, channel 15, PAN ID 0xabcd. With the
// FRDM-RW612 eth_radio_bridge tutorial, gets an address from the LAN's DHCP
// server and is reachable from the LAN. Serves pages on ports 80 and 443
// IPv6 over IEEE 802.15.4 on FRDM-MCXW71, Mongoose built-in TCP/IP stack.
// Plain 6LoWPAN on channel 15, PAN ID 0xabcd. With a router on the radio
// link, e.g. the FRDM-RW612 lowpan tutorial, gets a global address and is
// reachable from the LAN. Serves pages on ports 80 and 443
#include "hal.h"
#include "mongoose.h"
@@ -49,8 +49,11 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
if (mg_match(hm->uri, mg_str("/ota"), NULL) &&
!mg_http_stream_body(c, hm, mg_http_ota_cb, NULL)) {
mg_http_reply(c, 400, "", "POST firmware with Content-Length\n");
c->is_draining = 1;
mg_iobuf_del(&c->recv, 0, c->recv.len);
}
} else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
@@ -78,8 +81,8 @@ int main(void) {
struct mg_mgr mgr;
mg_mgr_init(&mgr);
mg_http_listen(&mgr, "http://0.0.0.0:80", http_ev_handler, NULL);
mg_http_listen(&mgr, "https://0.0.0.0:443", http_ev_handler, NULL);
mg_http_listen(&mgr, "http://[::]:80", http_ev_handler, NULL);
mg_http_listen(&mgr, "https://[::]:443", http_ev_handler, NULL);
mg_mqtt_init(&mgr);
MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000));
@@ -12,12 +12,12 @@
#define MG_ENABLE_CUSTOM_RANDOM 1
#define MG_ENABLE_TCPIP_TCPRTX 1 // Link can be lossy, enable retransmit
// Stable MAC, so the DHCP address stays the same across reboots: a hash of
// the 128-bit chip unique ID, MSCM UID, locally administered
#define MG_SET_MAC_ADDRESS(mac) \
do { \
uint8_t b_[32]; \
mg_sha256(b_, (uint8_t *) MSCM->UID, sizeof(MSCM->UID)); \
(mac)[0] = 2, (mac)[1] = b_[1], (mac)[2] = b_[2], (mac)[3] = b_[3]; \
(mac)[4] = b_[4], (mac)[5] = b_[5]; \
// Stable EUI-64, so the IPv6 addresses stay the same across reboots: a hash
// of the 128-bit chip unique ID, MSCM UID, locally administered
#define MG_SET_MAC_ADDRESS(mac) \
do { \
uint8_t b_[32]; \
mg_sha256(b_, (uint8_t *) MSCM->UID, sizeof(MSCM->UID)); \
(mac)[0] = 2, (mac)[1] = b_[1], (mac)[2] = b_[2], (mac)[3] = b_[3]; \
(mac)[4] = b_[4], (mac)[5] = b_[5], (mac)[6] = b_[6], (mac)[7] = b_[7]; \
} while (0)
@@ -1,101 +0,0 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
//
// Ethernet <-> IEEE 802.15.4 radio bridge. Radio frames carry Ethernet frames
// split into pieces, see mg_l2_802154_tx(). Radio nodes, e.g. the
// FRDM-MCXW71 minimal tutorial, are on the Ethernet LAN: they get IPv4
// addresses from its DHCP server. The bridge itself has no IP address.
// Everything from the radio goes to Ethernet. From Ethernet, only broadcasts
// and frames to radio nodes go to the radio, it is much slower
#include "hal.h"
#include "mongoose.h"
#define UART_DEBUG USART3
static void log_fn(char ch, void *param) {
hal_uart_write_buf(param, &ch, 1);
}
uint64_t mg_millis(void) {
return hal_get_tick();
}
// MAC addresses of radio nodes, learned from frames they send
static uint8_t s_nodes[8][6];
static size_t s_next;
// Remember a radio node's MAC address, if new
static void learn(const uint8_t *mac) {
size_t i, n = sizeof(s_nodes) / sizeof(s_nodes[0]);
for (i = 0; i < n && memcmp(s_nodes[i], mac, 6) != 0; i++) (void) 0;
if (i < n) return;
memcpy(s_nodes[s_next], mac, 6);
s_next = (s_next + 1) % n;
MG_INFO(("Radio node %M", mg_print_mac, mac));
}
// Is an Ethernet frame for the radio: broadcast, or to a radio node
static bool for_radio(const uint8_t *dst) {
size_t i, n = sizeof(s_nodes) / sizeof(s_nodes[0]);
if (memcmp(dst, "\xff\xff\xff\xff\xff\xff", 6) == 0) return true;
for (i = 0; i < n && memcmp(s_nodes[i], dst, 6) != 0; i++) (void) 0;
return i < n;
}
int main(void) {
static struct mg_tcpip_driver_rw612_data eth_data = {
.mdc_cr = MG_DRIVER_MDC_CR, .phy_addr = MG_TCPIP_PHY_ADDR};
static struct mg_l2_802154_data radio_data = {
.channel = MG_L2_802154_CHANNEL, .pan_id = MG_L2_802154_PAN_ID};
static struct mg_tcpip_if eth = {.driver = &mg_tcpip_driver_rw612,
.driver_data = &eth_data};
static struct mg_tcpip_if radio = {.driver = &mg_tcpip_driver_rw612_154,
.driver_data = &radio_data};
static char eth_queue[8192]; // Filled by the Ethernet IRQ handler
static uint8_t frame[1540];
uint8_t uid[FSL_OCOTP_UID_LENGTH], digest[32];
uint32_t uid_len = sizeof(uid);
uint64_t timer = 0;
char *p;
size_t n;
hal_clock_init();
hal_uart_init(UART_DEBUG, 115200);
mg_log_set_fn(log_fn, UART_DEBUG);
if (hal_radio_err) {
MG_ERROR(("Radio firmware failed to start, step %d", hal_radio_err));
}
// MAC addresses made from the chip unique ID
memset(uid, 0, sizeof(uid));
OCOTP_ReadUniqueID(uid, &uid_len);
mg_sha256(digest, uid, sizeof(uid));
memcpy(eth.mac, digest, 6);
memcpy(radio.mac, digest + 6, 6);
eth.mac[0] = radio.mac[0] = 0x02; // Locally administered, unicast
hal_ethernet_init();
mg_queue_init(&eth.recv_queue, eth_queue, sizeof(eth_queue));
if (!eth.driver->init(&eth)) MG_ERROR(("Ethernet init failed"));
if (!radio.driver->init(&radio)) MG_ERROR(("Radio init failed"));
ENET->RCR |= ENET_RCR_PROM_MASK; // Receive frames for radio nodes, too
MG_INFO(("Bridge started, radio channel %d", radio_data.channel));
for (;;) {
if ((n = mg_queue_next(&eth.recv_queue, &p)) > 0) { // Ethernet -> radio
if (n >= 14 && for_radio((uint8_t *) p)) radio.driver->tx(p, n, &radio);
mg_queue_del(&eth.recv_queue, n);
}
if ((n = radio.driver->rx(frame, sizeof(frame), &radio)) >= 14) {
learn(frame + 6); // Radio -> Ethernet
eth.driver->tx(frame, n, &eth);
}
if (mg_timer_expired(&timer, 1000, mg_millis())) { // Ethernet link
bool up = eth.driver->poll(&eth, true);
eth.state = up ? MG_TCPIP_STATE_UP : MG_TCPIP_STATE_DOWN;
}
}
return 0;
}
@@ -1,10 +0,0 @@
#pragma once
// See https://mongoose.ws/docs/getting-started/build-environment/
#define MG_ARCH MG_ARCH_ARMGCC
#define MG_ENABLE_TCPIP 1
#define MG_ENABLE_DRIVER_RW612 1 // Ethernet
#define MG_ENABLE_DRIVER_RW612_154 1 // 802.15.4 radio
#define MG_ENABLE_TCPIP_DRIVER_INIT 0 // main.c drives both, no TCP/IP stack
#define MG_ENABLE_CUSTOM_MILLIS 1
@@ -1,16 +1,16 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
#include "hal.h"
#include "mongoose.h"
#include "hal.h"
#define UART_DEBUG USART2 // ST-LINK virtual COM port, UM3616 7.12
#define UART_DEBUG_TX_PIN PIN('A', 2)
#define UART_DEBUG_RX_PIN PIN('A', 3)
#ifndef UART_DEBUG
#define UART_DEBUG USART3
#endif
#define LED1 PIN('A', 5) // Green, on when high. UM3616 7.8
#define LED2 PIN('G', 1) // Red, on when low
#define LED3 PIN('G', 2) // Blue, on when low
#define LED1 PIN(0, 12) // Green
#define LED2 PIN(0, 0) // Blue
#define LED3 PIN(0, 1) // Red
static void log_fn(char ch, void *param) {
hal_uart_write_buf(param, &ch, 1);
@@ -18,8 +18,8 @@ static void log_fn(char ch, void *param) {
static void blink_task(void) {
static uint64_t blink_timer = 0;
if (hal_timer_expired(&blink_timer, 500, hal_get_tick())) {
hal_gpio_toggle(LED2);
if (hal_timer_expired(&blink_timer, 100, hal_get_tick())) {
// hal_gpio_toggle(LED1);
}
}
@@ -27,6 +27,7 @@ uint64_t mg_millis(void) {
return hal_get_tick();
}
#if MG_ENABLE_CUSTOM_RANDOM
bool mg_random(void *buf, size_t len) {
for (size_t n = 0; n < len; n += sizeof(uint32_t)) {
uint32_t r = hal_rng_read();
@@ -34,6 +35,7 @@ bool mg_random(void *buf, size_t len) {
}
return true;
}
#endif
// Self-signed P-256 certificate and key for the HTTPS listener. Created by:
// openssl ecparam -genkey -name prime256v1 -noout -out key.pem
@@ -63,12 +65,7 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) {
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick());
@@ -85,29 +82,80 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
}
}
// Our address on the radio side, and the /64 prefix we advertise there.
// fd12:3456:789a:1::1, a unique local address, RFC 4193
#define RADIO_IP6 \
"\xfd\x12\x34\x56\x78\x9a\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01"
// NAT64 prefix advertised to radio nodes: 64:ff9b::/96, RFC 6052. Nodes reach
// IPv4 hosts as 64:ff9b::a.b.c.d, we translate to IPv4 on Ethernet
#define NAT64_PREFIX "\x00\x64\xff\x9b\x00\x00\x00\x00\x00\x00\x00\x00"
int main(void) {
MG_HEALTH_INIT();
hal_clock_init();
hal_uart_init(UART_DEBUG, UART_DEBUG_TX_PIN, UART_DEBUG_RX_PIN, 115200);
mg_log_set_fn(log_fn, UART_DEBUG);
hal_rng_init();
hal_ethernet_init();
hal_gpio_output(LED1);
hal_gpio_write(LED1, true);
hal_gpio_output(LED2);
hal_gpio_write(LED2, true);
hal_gpio_output(LED3);
hal_gpio_write(LED3, true);
hal_uart_init(UART_DEBUG, 115200);
mg_log_set_fn(log_fn, UART_DEBUG);
struct mg_mgr mgr;
mg_mgr_init(&mgr);
mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL);
mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL);
if (hal_radio_err) {
MG_ERROR(("Radio firmware failed to start, step %d", hal_radio_err));
}
// Two interfaces, each on its own manager: Ethernet and 802.15.4
static struct mg_tcpip_driver_rw612_data eth_data = {
.mdc_cr = MG_DRIVER_MDC_CR, .phy_addr = MG_TCPIP_PHY_ADDR};
static struct mg_tcpip_if eth = {.driver = &mg_tcpip_driver_rw612,
.driver_data = &eth_data};
static struct mg_tcpip_if radio = {.driver = &mg_tcpip_driver_rw612_154,
.l2type = MG_TCPIP_L2_LOWPAN};
struct mg_mgr mgr_eth, mgr_radio;
uint8_t uid[FSL_OCOTP_UID_LENGTH], digest[32];
uint32_t uid_len = sizeof(uid);
// Stable addresses, made from the chip unique ID: neighbours remember
// them, e.g. radio nodes keep us as their default router
memset(uid, 0, sizeof(uid));
OCOTP_ReadUniqueID(uid, &uid_len);
mg_sha256(digest, uid, sizeof(uid));
memcpy(eth.mac, digest, 6);
memcpy(radio.mac, digest + 6, 8);
eth.mac[0] = radio.mac[0] = 0x02; // Locally administered, unicast
hal_ethernet_init();
mg_mgr_init(&mgr_eth);
mg_tcpip_init(&mgr_eth, &eth);
radio.l2data.lowpan.channel = 15;
radio.l2data.lowpan.pan_id = 0xabcd;
memcpy(radio.ip6, RADIO_IP6, sizeof(radio.ip6)); // Static: we are router
memcpy(radio.prefix, RADIO_IP6, sizeof(radio.prefix));
radio.prefix_len = 64;
memcpy(radio.nat64, NAT64_PREFIX, sizeof(radio.nat64)); // We do NAT64
mg_mgr_init(&mgr_radio);
mg_tcpip_init(&mgr_radio, &radio);
mgr_radio.use_dns6 = true; // IPv6 only: no IPv4 over 6LoWPAN
// Route IPv6 between the two: radio nodes get addresses from RADIO_IP6/64,
// the Ethernet LAN gets a route to that prefix via us
mg_tcpip_route(&eth, &radio);
// Minimal web server on both interfaces
mg_http_listen(&mgr_eth, "http://0.0.0.0:80", http_ev_handler, NULL);
mg_http_listen(&mgr_eth, "https://0.0.0.0:443", http_ev_handler, NULL);
mg_http_listen(&mgr_radio, "http://[::]:80", http_ev_handler, NULL);
mg_http_listen(&mgr_radio, "https://[::]:443", http_ev_handler, NULL);
MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000));
MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free()));
MG_INFO(("Crash record: %M", mg_print_crash_record));
for (;;) {
mg_mgr_poll(&mgr, 0);
mg_mgr_poll(&mgr_eth, 0);
mg_mgr_poll(&mgr_radio, 0);
blink_task();
}
@@ -0,0 +1,14 @@
#pragma once
// See https://mongoose.ws/docs/getting-started/build-environment/
#define MG_ARCH MG_ARCH_ARMGCC
#define MG_ENABLE_TCPIP 1
// Compile both drivers; main.c initialises both interfaces itself
#define MG_ENABLE_DRIVER_RW612 1 // Ethernet
#define MG_ENABLE_DRIVER_RW612_154 1 // 802.15.4 radio, 6LoWPAN
#define MG_ENABLE_TCPIP_DRIVER_INIT 0 // Do not start one in mg_mgr_init()
#define MG_ENABLE_CUSTOM_MILLIS 1
#define MG_ENABLE_CUSTOM_RANDOM 1
#define MG_TLS MG_TLS_BUILTIN
#define MG_ENABLE_TCPIP_TCPRTX 1 // Radio link can be lossy
-5
View File
@@ -65,11 +65,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
-5
View File
@@ -61,11 +61,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
-5
View File
@@ -65,11 +65,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
+1 -6
View File
@@ -5,12 +5,7 @@
#include "mongoose.h"
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
uint64_t tick = to_ms_since_boot(get_absolute_time());
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
+1 -6
View File
@@ -33,12 +33,7 @@ static void driver_init(struct mg_mgr *mgr) {
}
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
uint64_t tick = to_ms_since_boot(get_absolute_time());
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
+1 -6
View File
@@ -58,12 +58,7 @@ static void driver_init(struct mg_mgr *mgr) {
}
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
uint64_t tick = to_ms_since_boot(get_absolute_time());
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
-5
View File
@@ -39,11 +39,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
uint64_t tick = to_ms_since_boot(get_absolute_time());
-5
View File
@@ -82,11 +82,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
uint64_t tick = to_ms_since_boot(get_absolute_time());
-5
View File
@@ -195,11 +195,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
uint64_t tick = to_ms_since_boot(get_absolute_time());
@@ -1,9 +0,0 @@
# Nucleo-C5A3ZI Mongoose Integration
The `.ioc2` file in this directory is pre-configured. Note that after the
project generation, you should do the following to complete integraion:
1. Edit the linker script `.ld` file and increase heap and stack sizes to
0x20000 and 0x4000 respectively
2. Add Mongoose to the `main.c`, see https://mongoose.ws/docs/guides/stm32-tcpip-stack/#modify-mainc
File diff suppressed because it is too large. Load diff
@@ -1,46 +0,0 @@
# Copyright (c) 2026 Cesanta Software Limited
# Environment setup: https://mongoose.ws/docs/getting-started/build-environment/
MGDIR = ../../../..
CFLAGS = -W -Wall -Wextra -Wundef -Wshadow -Wdouble-promotion
CFLAGS += -Wformat-truncation -fno-common -Wconversion -Wno-sign-conversion
CFLAGS += -g3 -Os -ffunction-sections -fdata-sections
CFLAGS += -I. -I$(MGDIR) -Icmsis_core/CMSIS/Core/Include -Icmsis_c5/Include -DSTM32C5A3xx
CFLAGS += -mcpu=cortex-m33 -mthumb -mfpu=fpv5-sp-d16 -mfloat-abi=hard $(CFLAGS_EXTRA)
LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs nano.specs -lc -lgcc -Wl,--gc-sections
SOURCES = main.c hal.c
SOURCES += cmsis_c5/Source/startup_stm32c5a3xx.c
SOURCES += $(MGDIR)/mongoose.c
all build example: firmware.bin
firmware.elf: cmsis_core cmsis_c5 hal.h link.ld Makefile $(SOURCES) $(MGDIR)/mongoose.h mongoose_config.h
arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@
firmware.bin: firmware.elf
arm-none-eabi-objcopy -O binary $< $@
@echo
@echo "To flash, run 'make flash', or use STM32CubeProgrammer"
firmware.signed.bin: firmware.bin
test -f private.pem || node $(MGDIR)/resources/sign.js keygen
node $(MGDIR)/resources/sign.js sign firmware.bin
# Show all symbols sorted by size
show_symbols_sorted_by_size: firmware.bin
arm-none-eabi-readelf -sW firmware.elf | sed 1,3d | sed 's,^.*: ,,' | tr -s ' ' | cut -d ' ' -f 2,7 | sort -nk1 | grep -v '^0 '
# If the board gets stuck: STM32_Programmer_CLI -c port=swd -e all mode=UR
flash: firmware.bin
STM32_Programmer_CLI -c port=swd -w firmware.elf mode=UR -rst
cmsis_core:
git clone -q -c advice.detachedHead=false --depth 1 -b v6.4.0 https://github.com/ARM-software/CMSIS_6 $@
cmsis_c5:
git clone -q -c advice.detachedHead=false --depth 1 -b 2.1.0 https://github.com/STMicroelectronics/stm32c5xx-dfp $@
clean:
rm -rf firmware.* cmsis_*
-141
View File
@@ -1,141 +0,0 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
#include "hal.h"
// Added to minimise the bin size: avoid pulling printf and friends
void __assert_func(const char *s, int line, const char *fn, const char *expr) {
(void) s, (void) line, (void) fn, (void) expr;
for (;;) (void) 0; // or trigger the fault handler
}
bool hal_timer_expired(volatile uint64_t *t, uint64_t period, uint64_t now) {
uint64_t diff = now - *t; // Wrap-safe elapsed time since last expiry
if (period == 0) return false; // Avoid division by zero
if (diff < period) return false; // Period has not elapsed yet
*t += (diff / period) * period; // Preserve cadence, skip missed periods
return true;
}
static volatile uint64_t s_ticks; // Milliseconds since boot
void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms
s_ticks++;
}
uint64_t hal_get_tick(void) {
return s_ticks;
};
uint32_t SystemCoreClock = 160000000;
void SystemInit(void) { // Called automatically by startup code
hal_system_init(); // Enable FPU
hal_clock_init();
SysTick_Config(SystemCoreClock / 1000); // Sys tick every 1ms
}
void ExitRun0Mode(void) {
}
// C library entry, called by the CMSIS startup code after it copies .data
// and clears .bss. See __cmsis_start() in cmsis_gcc_m.h
extern int main(void);
extern void __libc_init_array(void);
__attribute__((noreturn)) void _start(void) {
__libc_init_array();
main();
for (;;) (void) 0;
}
struct stat;
__attribute__((weak)) int _fstat(int fd, struct stat *st) {
(void) fd, (void) st;
return -1;
}
extern unsigned char _end[]; // End of data section, start of heap. See link.ld
static unsigned char *s_current_heap_end = _end;
size_t hal_ram_used(void) {
return (size_t) (s_current_heap_end - _end);
}
size_t hal_ram_free(void) {
unsigned char endofstack;
return (size_t) (&endofstack - s_current_heap_end);
}
void *_sbrk(int incr) {
unsigned char *prev_heap;
unsigned char *heap_end = (unsigned char *) ((size_t) &heap_end - 256);
prev_heap = s_current_heap_end;
// Check how much space we got from the heap end to the stack end
if (s_current_heap_end + incr > heap_end) return (void *) -1;
s_current_heap_end += incr;
return prev_heap;
}
__attribute__((weak)) int _open(const char *path) {
(void) path;
return -1;
}
__attribute__((weak)) int _close(int fd) {
(void) fd;
return -1;
}
__attribute__((weak)) int _isatty(int fd) {
(void) fd;
return 1;
}
__attribute__((weak)) int _lseek(int fd, int ptr, int dir) {
(void) fd, (void) ptr, (void) dir;
return 0;
}
__attribute__((weak)) void _exit(int status) {
(void) status;
for (;;) asm volatile("BKPT #0");
}
__attribute__((weak)) void _kill(int pid, int sig) {
(void) pid, (void) sig;
}
__attribute__((weak)) int _getpid(void) {
return -1;
}
__attribute__((weak)) int _write(int fd, char *ptr, int len) {
(void) fd, (void) ptr, (void) len;
return -1;
}
__attribute__((weak)) int _read(int fd, char *ptr, int len) {
(void) fd, (void) ptr, (void) len;
return -1;
}
__attribute__((weak)) int _link(const char *a, const char *b) {
(void) a, (void) b;
return -1;
}
__attribute__((weak)) int _unlink(const char *a) {
(void) a;
return -1;
}
__attribute__((weak)) int _stat(const char *path, struct stat *st) {
(void) path, (void) st;
return -1;
}
__attribute__((weak)) int mkdir(const char *path, int mode) {
(void) path, (void) mode;
return -1;
}
__attribute__((weak)) void _init(void) {
}
-165
View File
@@ -1,165 +0,0 @@
// Copyright (c) 2013-2026 Cesanta Software Limited
// Datasheet: RM0522, devboard manual: UM3616
// https://www.st.com/resource/en/reference_manual/rm0522-stm32c5-series-armbased-32bit-mcus-stmicroelectronics.pdf
// https://www.st.com/resource/en/user_manual/um3616-stm32c5-nucleo144-board-mb2310-stmicroelectronics.pdf
#pragma once
#include <stm32c5xx.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
extern bool hal_timer_expired(volatile uint64_t *t, uint64_t prd, uint64_t now);
extern uint64_t hal_get_tick(void);
extern size_t hal_ram_free(void);
extern size_t hal_ram_used(void);
extern uint32_t SystemCoreClock;
#define BIT(x) (1UL << (x))
#define CLRSET(reg, clear, set) ((reg) = ((reg) & ~(clear)) | (set))
#define PIN(bank, num) ((((bank) - 'A') << 8) | (num))
#define PINNO(pin) (pin & 255)
#define PINBANK(pin) (pin >> 8)
// Ethernet RMII pins and their alternate functions. UM3616 7.11, Table 9
#define HAL_ETH_PINS \
{PIN('A', 1), 10}, /* REF_CLK */ \
{PIN('C', 1), 10}, /* MDC */ \
{PIN('E', 12), 10}, /* MDIO */ \
{PIN('D', 1), 10}, /* CRS_DV */ \
{PIN('C', 4), 12}, /* RXD0 */ \
{PIN('C', 5), 13}, /* RXD1 */ \
{PIN('G', 11), 10}, /* TX_EN */ \
{PIN('G', 13), 10}, /* TXD0 */ \
{PIN('G', 12), 10} /* TXD1 */
// System clock (9.4.2, 9.4.8). HSI runs at 144 MHz, the maximum system clock:
// SYSCLK = HSIS, no PLL. hclk = SYSCLK, APB clocks = hclk. After reset, the
// system runs on HSIDIV3, 48 MHz. Flash latency for 144 MHz: 6.3.6, Table 20
#define SYS_FREQUENCY 144000000
#define AHB_FREQUENCY SYS_FREQUENCY
#define APB2_FREQUENCY AHB_FREQUENCY
#define APB1_FREQUENCY AHB_FREQUENCY
#define HAL_FLASH_LATENCY (4 | FLASH_ACR_WRHIGHFREQ_1) // 4 WS, WRHIGHFREQ 2
enum { HAL_GPIO_MODE_INPUT, HAL_GPIO_MODE_OUTPUT, HAL_GPIO_MODE_AF, HAL_GPIO_MODE_ANALOG };
enum { HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_OTYPE_OPEN_DRAIN };
enum { HAL_GPIO_SPEED_LOW, HAL_GPIO_SPEED_MEDIUM, HAL_GPIO_SPEED_HIGH, HAL_GPIO_SPEED_INSANE };
enum { HAL_GPIO_PULL_NONE, HAL_GPIO_PULL_UP, HAL_GPIO_PULL_DOWN };
#define HAL_GPIO(N) ((GPIO_TypeDef *) (GPIOA_BASE + 0x400 * (N)))
static GPIO_TypeDef *hal_gpio_bank(uint16_t pin) {
return HAL_GPIO(PINBANK(pin));
}
static inline void hal_gpio_toggle(uint16_t pin) {
GPIO_TypeDef *gpio = hal_gpio_bank(pin);
uint32_t mask = BIT(PINNO(pin));
gpio->BSRR = mask << (gpio->ODR & mask ? 16 : 0);
}
static inline int hal_gpio_read(uint16_t pin) {
return hal_gpio_bank(pin)->IDR & BIT(PINNO(pin)) ? 1 : 0;
}
static inline void hal_gpio_write(uint16_t pin, bool val) {
GPIO_TypeDef *gpio = hal_gpio_bank(pin);
gpio->BSRR = BIT(PINNO(pin)) << (val ? 0 : 16);
}
static inline void hal_gpio_init(uint16_t pin, uint8_t mode, uint8_t type,
uint8_t speed, uint8_t pull, uint8_t af) {
GPIO_TypeDef *gpio = hal_gpio_bank(pin);
uint8_t n = (uint8_t) (PINNO(pin));
RCC->AHB2ENR |= BIT(PINBANK(pin)); // Enable GPIO clock
CLRSET(gpio->OTYPER, 1UL << n, ((uint32_t) type) << n);
CLRSET(gpio->OSPEEDR, 3UL << (n * 2), ((uint32_t) speed) << (n * 2));
CLRSET(gpio->PUPDR, 3UL << (n * 2), ((uint32_t) pull) << (n * 2));
CLRSET(gpio->AFR[n >> 3], 15UL << ((n & 7) * 4),
((uint32_t) af) << ((n & 7) * 4));
CLRSET(gpio->MODER, 3UL << (n * 2), ((uint32_t) mode) << (n * 2));
}
static inline void hal_gpio_input(uint16_t pin) {
hal_gpio_init(pin, HAL_GPIO_MODE_INPUT, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH,
HAL_GPIO_PULL_NONE, 0);
}
static inline void hal_gpio_output(uint16_t pin) {
hal_gpio_init(pin, HAL_GPIO_MODE_OUTPUT, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH,
HAL_GPIO_PULL_NONE, 0);
}
// USART1 kernel clock defaults to pclk2 (APB2), USART2,3 default to pclk1
// (APB1), see RCC_CCIPR1
static inline void hal_uart_init(USART_TypeDef *uart, uint16_t tx,
uint16_t rx, unsigned long baud) {
uint8_t af = 7; // Alternate function
uint32_t freq = 0; // Bus frequency. UART1 is on APB2, rest on APB1
if (uart == USART1) freq = APB2_FREQUENCY, RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
if (uart == USART2) freq = APB1_FREQUENCY, RCC->APB1LENR |= RCC_APB1LENR_USART2EN;
if (uart == USART3) freq = APB1_FREQUENCY, RCC->APB1LENR |= RCC_APB1LENR_USART3EN;
hal_gpio_init(tx, HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH, 0, af);
hal_gpio_init(rx, HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH, 0, af);
uart->CR1 = 0; // Disable this UART
uart->BRR = freq / baud; // Set baud rate
uart->CR1 = BIT(0) | BIT(2) | BIT(3); // Set UE, RE, TE
}
static inline void hal_uart_write_byte(USART_TypeDef *uart, uint8_t byte) {
uart->TDR = byte;
while ((uart->ISR & USART_ISR_TXE_TXFNF) == 0) (void) 0;
}
static inline void hal_uart_write_buf(USART_TypeDef *uart, char *buf, size_t len) {
while (len-- > 0) hal_uart_write_byte(uart, *(uint8_t *) buf++);
}
static inline int hal_uart_read_ready(USART_TypeDef *uart) {
return uart->ISR & BIT(5); // If RXNE bit is set, data is ready
}
static inline uint8_t hal_uart_read_byte(USART_TypeDef *uart) {
return (uint8_t) (uart->RDR & 255);
}
// RNG clock (rng_clk) is CK48, which is disabled after reset. Use HSIDIV3,
// 48 MHz. It stays enabled after the system clock switches to HSIS
static inline void hal_rng_init(void) {
CLRSET(RCC->CCIPR2, RCC_CCIPR2_CK48SEL_Msk, RCC_CCIPR2_CK48SEL_1);
RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN; // Enable RNG AHB clock
RNG->CR |= RNG_CR_RNGEN; // Enable RNG
}
static inline uint32_t hal_rng_read(void) {
while ((RNG->SR & RNG_SR_DRDY) == 0) (void) 0;
return RNG->DR;
}
static inline void hal_ethernet_init(void) {
// Initialise Ethernet. Enable MAC GPIO pins, see UM3616 Table 9
uint16_t pins[][2] = {HAL_ETH_PINS};
for (size_t i = 0; i < sizeof(pins) / sizeof(pins[0]); i++) {
hal_gpio_init(pins[i][0], HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_INSANE,
HAL_GPIO_PULL_NONE, (uint8_t) pins[i][1]);
}
NVIC_EnableIRQ(ETH1_IRQn); // Setup Ethernet IRQ handler
RCC->APB3ENR |= RCC_APB3ENR_SBSEN; // Enable SBS clock
CLRSET(SBS->PMCR, SBS_PMCR_ETH1_SEL_PHY_Msk, SBS_PMCR_ETH1_SEL_PHY_2); // RMII
RCC->AHB1ENR |= RCC_AHB1ENR_ETH1CKEN | RCC_AHB1ENR_ETH1EN |
RCC_AHB1ENR_ETH1TXEN | RCC_AHB1ENR_ETH1RXEN;
}
static inline void hal_clock_init(void) {
CLRSET(FLASH->ACR, FLASH_ACR_LATENCY_Msk | FLASH_ACR_WRHIGHFREQ_Msk,
HAL_FLASH_LATENCY);
while ((FLASH->ACR & FLASH_ACR_LATENCY_Msk) != (HAL_FLASH_LATENCY & 15)) (void) 0;
RCC->CR1 |= RCC_CR1_HSISON; // Enable HSIS, 144 MHz
while ((RCC->CR1 & RCC_CR1_HSISRDY) == 0) (void) 0; // Wait until ready
CLRSET(RCC->CFGR1, RCC_CFGR1_SW_Msk, RCC_CFGR1_SW_0); // SYSCLK = HSIS
while ((RCC->CFGR1 & RCC_CFGR1_SWS_Msk) != RCC_CFGR1_SWS_0) (void) 0;
SystemCoreClock = SYS_FREQUENCY; // Update SystemCoreClock global var
SysTick_Config(SystemCoreClock / 1000); // Sys tick every 1ms
}
static inline void hal_system_init(void) {
SCB->CPACR |= ((3UL << 10 * 2) | (3UL << 11 * 2)); // Enable FPU
__DSB();
__ISB();
}
@@ -1,37 +0,0 @@
ENTRY(Reset_Handler);
MEMORY {
flash(rx) : ORIGIN = 0x08000000, LENGTH = 1024k
sram(rwx) : ORIGIN = 0x20000000, LENGTH = 256k - 1k
health(rw) : ORIGIN = 0x2003fc00, LENGTH = 1k /* Retained health record */
}
__StackTop = ORIGIN(sram) + LENGTH(sram); /* End of RAM. stack points here */
_estack = __StackTop; /* Used by the health fault handler */
__StackLimit = __StackTop - 16384; /* Stack limit, MSPLIM. TLS needs > 4 KB */
SECTIONS {
.vectors : { KEEP(*(.vectors)) } > flash
.text : { *(.text* .text.*) } > flash
.rodata : { *(.rodata*) } > flash
/* Copy and zero tables for the CMSIS startup code, see __cmsis_start() */
.copy.table : {
__copy_table_start__ = .;
LONG(LOADADDR(.data)) LONG(ADDR(.data)) LONG(SIZEOF(.data) / 4)
__copy_table_end__ = .;
} > flash
.zero.table : {
__zero_table_start__ = .;
LONG(ADDR(.bss)) LONG(SIZEOF(.bss) / 4)
__zero_table_end__ = .;
} > flash
/* Note the .iram section, for functions copied to RAM. Required for MG_IRAM OTA support */
.data : { _sdata = .; *(.first_data) *(.iram .iram* .iram.*) *(.data SORT(.data.*)) . = ALIGN(4); _edata = .; } > sram AT > flash
_sidata = LOADADDR(.data);
.bss : { _sbss = .; *(.bss SORT(.bss.*) COMMON) . = ALIGN(4); _ebss = .; } > sram
_end = ALIGN(_ebss, 8); /* Heap starts after .bss */
/* Retained health record. Startup code must neither copy nor clear it. */
.mg_health (NOLOAD) : { KEEP(*(.mg_health .mg_health*)) } > health
}
@@ -1,32 +0,0 @@
#pragma once
#include "hal.h"
// See https://mongoose.ws/docs/getting-started/build-environment/
#define MG_ARCH MG_ARCH_ARMGCC
#define MG_TLS MG_TLS_BUILTIN
#define MG_OTA MG_OTA_STM32C5
#define MG_ENABLE_TCPIP 1
#define MG_ENABLE_CUSTOM_MILLIS 1
#define MG_ENABLE_CUSTOM_RANDOM 1
#define MG_ENABLE_DRIVER_STM32C 1
// Construct MAC address from the MCU unique ID
#define MGUID ((uint32_t *) UID_BASE)
#define MG_SET_MAC_ADDRESS(mac) \
do { \
mac[0] = 2; \
mac[1] = MGUID[0] & 255; \
mac[2] = (MGUID[0] >> 10) & 255; \
mac[3] = (MGUID[0] >> 19) & 255; \
mac[4] = MGUID[1] & 255; \
mac[5] = MGUID[2] & 255; \
} while (0)
// Crash report support. Health record lives in the .mg_health region, see link.ld
#define MG_ENABLE_HEALTH 1
// mdash.net device management service support
#define MG_ENABLE_MDASH 0
#define MG_MDASH_KEY "..." // Set this - mDash device token
#define MG_FIRMWARE_VERSION "c5a3.1.0.0"
@@ -66,12 +66,7 @@ static bool usb_poll(struct mg_tcpip_if *ifp, bool s1) {
}
static void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) {
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/debug"), NULL)) {
int level = mg_json_get_long(hm->body, "$.level", MG_LL_DEBUG);
@@ -68,11 +68,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
@@ -28,12 +28,7 @@ bool mg_random(void *buf, size_t len) { // Use on-board RNG
static void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) {
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
(void) hm;
mg_http_reply(c, 200, "", "hi\n");
@@ -66,12 +66,7 @@ static bool usb_poll(struct mg_tcpip_if *ifp, bool s1) {
}
static void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) {
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/debug"), NULL)) {
int level = mg_json_get_long(hm->body, "$.level", MG_LL_DEBUG);
@@ -68,11 +68,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
@@ -68,11 +68,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
@@ -47,12 +47,7 @@ static void timer_fn(void *arg) {
static void fn(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->fn_data;
if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) {
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/hello"),
NULL)) { // Request to /api/hello
@@ -68,11 +68,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
@@ -89,12 +89,7 @@ static void log_fn(char ch, void *param) {
}
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) {
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
mg_http_reply(c, 200, "", "{%m:%lu}\n", MG_ESC("tick"), HAL_GetTick());
@@ -44,12 +44,7 @@ bool mg_random(void *buf, size_t len) {
}
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) {
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick());
@@ -43,12 +43,7 @@ bool mg_random(void *buf, size_t len) {
}
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) {
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick());
@@ -68,11 +68,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
@@ -68,11 +68,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
@@ -64,11 +64,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
@@ -60,11 +60,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
@@ -68,11 +68,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
@@ -68,11 +68,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
@@ -68,11 +68,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
.key = mg_str(TLS_KEY)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
@@ -63,12 +63,7 @@ static bool usb_poll(struct mg_tcpip_if *ifp, bool s1) {
}
static void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) {
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/api/debug"), NULL)) {
int level = mg_json_get_long(hm->body, "$.level", MG_LL_DEBUG);
@@ -30,12 +30,7 @@ static void timer_fn(void *arg) {
// https://mongoose.ws/documentation/#2-minute-integration-guide
static void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_HDRS) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
}
} else if (ev == MG_EV_HTTP_MSG) {
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
mg_http_reply(c, 200, "", "ok %p\r\n", hm);
}
Loaded 100 of 101 files, more files were not shown because too many files have changed in this diff. Show more