mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-25 12:04:47 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b766c65615 | ||
|
|
8296b88fb6 | ||
|
|
a5efbf0592 | ||
|
|
37c98dd893 | ||
|
|
770a4ff27e | ||
|
|
b6560306ba | ||
|
|
5b39c93478 | ||
|
|
086008b716 | ||
|
|
35eb50a1e6 | ||
|
|
431e35ae8c | ||
|
|
25e2a99d8e | ||
|
|
88b01ef6f0 | ||
|
|
bd8ee86f9e | ||
|
|
ef96f7a28c | ||
|
|
03f240a244 | ||
|
|
c775f31ca7 | ||
|
|
1ca4fd1fef |
No files matched your search
@@ -1,8 +1,12 @@
|
||||
# Mongoose - Embedded Network Library
|
||||
# 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 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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Mongoose - Embedded Web Server / Embedded Network Library
|
||||
# Mongoose: a full-stack embedded networking library
|
||||
|
||||
[](https://opensource.org/licenses/gpl-2.0.php)
|
||||
[](https://github.com/cesanta/mongoose/actions)
|
||||
@@ -7,11 +7,17 @@
|
||||
|
||||
<img src="https://mongoose.ws/images/logo.svg" width="48" height="48"
|
||||
align="left" style="float:left;" />
|
||||
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.
|
||||
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.
|
||||
|
||||
On the market since 2004, used by vast number of open source and commercial
|
||||
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
|
||||
products - it even runs on the International Space Station! Features include:
|
||||
|
||||
- Cross-platform:
|
||||
@@ -24,7 +30,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 ECC stack. Also can use external TLS libraries - mbedTLS, OpenSSL, or other
|
||||
- Built-in TLS 1.3 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/)
|
||||
@@ -35,8 +41,32 @@ See https://mongoose.ws/ for complete documentation, videos, case studies, etc.
|
||||
|
||||
## Supported platforms
|
||||
|
||||
Mongoose can work on top of any TCP/IP stack that supports BSD sockets API.
|
||||
Platforms supported by the 3rd party TCP/IP stacks:
|
||||
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:
|
||||
|
||||
| TCP/IP stack | Notes |
|
||||
| :-------------- | :------- |
|
||||
@@ -45,23 +75,6 @@ Platforms supported by the 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
|
||||
@@ -158,6 +171,17 @@ 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
|
||||
@@ -222,12 +246,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/mdash/)
|
||||
- [Firmware OTA and remote control on NXP FRDM-RW612](https://mongoose.ws/articles/rw612-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
@@ -0,0 +1,41 @@
|
||||
# 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/>
|
||||
+214
-1043
File diff suppressed because it is too large.
Load diff
+102
-184
@@ -165,6 +165,7 @@ 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)
|
||||
@@ -173,6 +174,8 @@ 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
|
||||
@@ -189,70 +192,8 @@ extern "C" {
|
||||
#define MG_OTA MG_OTA_STM32H5
|
||||
#elif !defined(MG_OTA) && defined(STM32H7)
|
||||
#define MG_OTA MG_OTA_STM32H7
|
||||
#endif
|
||||
|
||||
// OTA rollback timer via IWDG. Fixed 10 s hang-detection window (prescaler
|
||||
// /1024, LSI ~32 kHz: reload = 10 * 32000 / 1024 = 312). mg_ota_poll() feeds
|
||||
// the watchdog every 500 ms while in MG_OTA_TESTING state; if the event loop
|
||||
// hangs for 10 s, the IWDG fires and the device rolls back.
|
||||
// STM32H7 names the peripheral IWDG1; all others use IWDG.
|
||||
// The total evaluation period is set by MG_OTA_ROLLBACK_TIMEOUT_SECONDS.
|
||||
// STM32H7 names the watchdog peripheral IWDG1; all other families use IWDG.
|
||||
#if defined(IWDG1) && !defined(IWDG)
|
||||
#define MG_IWDG IWDG1
|
||||
#else
|
||||
#define MG_IWDG IWDG
|
||||
#endif
|
||||
// PR=6 → /256 prescaler, RLR=1250 → ~10 s at LSI 32 kHz.
|
||||
// Works on F4/F7/H5/H7; uses direct register writes so it is safe before HAL_Init().
|
||||
#ifndef MG_OTA_ROLLBACK_TIMER_START
|
||||
#define MG_OTA_ROLLBACK_TIMER_START() \
|
||||
do { \
|
||||
MG_IWDG->KR = 0xCCCCU; /* start + LSI */ \
|
||||
MG_IWDG->KR = 0x5555U; /* unlock PR/RLR */ \
|
||||
MG_IWDG->PR = 6U; /* /256 */ \
|
||||
MG_IWDG->RLR = 1250U; /* 10 s */ \
|
||||
while (MG_IWDG->SR & (IWDG_SR_PVU | IWDG_SR_RVU)) (void) 0; \
|
||||
MG_IWDG->KR = 0xAAAAU; /* reload */ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
// Feed (reload) the IWDG. Called from mg_ota_poll() every 500 ms.
|
||||
#ifndef MG_OTA_ROLLBACK_TIMER_FEED
|
||||
#define MG_OTA_ROLLBACK_TIMER_FEED() (MG_IWDG->KR = 0xAAAAU)
|
||||
#endif
|
||||
|
||||
// OTA state in a backup register that survives warm resets but clears on POR.
|
||||
// STATE_GET reads work without any setup. STATE_SET enables the RTC APB clock
|
||||
// and unlocks backup domain write access via direct register writes, so it is
|
||||
// safe to call from any context (before or after HAL_Init).
|
||||
// H5 (TAMP->BKP0R), H7 (RTC->BKP0R), F4/F7 (RTC->BKP0R).
|
||||
#ifndef MG_OTA_STATE_GET
|
||||
#if defined(PWR_DBPCR_DBP)
|
||||
#define MG_OTA_DBP_ENABLE() (PWR->DBPCR |= PWR_DBPCR_DBP)
|
||||
#elif defined(PWR_CR1_DBP)
|
||||
#define MG_OTA_DBP_ENABLE() (PWR->CR1 |= PWR_CR1_DBP)
|
||||
#else
|
||||
#define MG_OTA_DBP_ENABLE() (PWR->CR |= PWR_CR_DBP)
|
||||
#endif
|
||||
|
||||
#if defined(TAMP)
|
||||
#define MG_OTA_STATE_GET() \
|
||||
(RCC->APB3ENR |= RCC_APB3ENR_RTCAPBEN, TAMP->BKP0R)
|
||||
#define MG_OTA_STATE_SET(v) \
|
||||
(RCC->APB3ENR |= RCC_APB3ENR_RTCAPBEN, MG_OTA_DBP_ENABLE(), \
|
||||
(TAMP->BKP0R = (uint32_t) (v)), TAMP->BKP0R)
|
||||
#elif defined(RCC_APB4ENR_RTCAPBEN)
|
||||
#define MG_OTA_STATE_GET() (RTC->BKP0R)
|
||||
#define MG_OTA_STATE_SET(v) \
|
||||
(RCC->APB4ENR |= RCC_APB4ENR_RTCAPBEN, MG_OTA_DBP_ENABLE(), \
|
||||
(RTC->BKP0R = (uint32_t) (v)))
|
||||
#else
|
||||
#define MG_OTA_STATE_GET() (RTC->BKP0R)
|
||||
#define MG_OTA_STATE_SET(v) \
|
||||
(RCC->APB1ENR |= RCC_APB1ENR_PWREN, MG_OTA_DBP_ENABLE(), \
|
||||
(RTC->BKP0R = (uint32_t) (v)))
|
||||
#endif
|
||||
#elif !defined(MG_OTA) && defined(STM32C5)
|
||||
#define MG_OTA MG_OTA_STM32C5
|
||||
#endif
|
||||
|
||||
// use HAL-defined execute-in-ram section
|
||||
@@ -1251,13 +1192,8 @@ 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
|
||||
@@ -1402,8 +1338,6 @@ 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
|
||||
@@ -1445,7 +1379,6 @@ 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.
|
||||
@@ -2791,29 +2724,32 @@ int mg_http_status(const struct mg_http_message *hm);
|
||||
|
||||
// Streams a POST/PUT body to a callback as it arrives, without buffering it.
|
||||
// Call it from an MG_EV_HTTP_HDRS handler, after matching the request, e.g.
|
||||
// with mg_match(). Returns true if it took over the connection, false if the
|
||||
// request is not POST/PUT, is chunked, or has no Content-Length.
|
||||
// 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.
|
||||
//
|
||||
// Callback upload_fn(hm, data, user_data) is called:
|
||||
// - at start: hm is set. Open the file, set *user_data.
|
||||
// - per chunk: data is set, 512 byte aligned except the last. Not called
|
||||
// again after a failure.
|
||||
// - at end: both NULL. Always called once, also when the connection drops
|
||||
// early, so release resources here. A truncated body looks like a complete
|
||||
// one: validate before committing.
|
||||
// Return true on success, false on error.
|
||||
// - 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.
|
||||
// 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:
|
||||
// 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,
|
||||
// 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,
|
||||
bool (*upload_fn)(struct mg_http_message *,
|
||||
struct mg_str *, void **user_data),
|
||||
void *user_data);
|
||||
@@ -4242,6 +4178,7 @@ 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
|
||||
@@ -4286,9 +4223,8 @@ 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)) {
|
||||
// mg_http_reply(c, 400, "", "POST firmware with Content-Length\n");
|
||||
// if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
// mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
@@ -4441,11 +4377,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))
|
||||
#if !defined(MG_HEALTH_RAM) && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5))
|
||||
#define MG_HEALTH_RAM __attribute__((section(".mg_health")))
|
||||
#endif
|
||||
|
||||
#if !defined(MG_HEALTH_DATA_SIZE) && (defined(STM32H7_DEV_ID) || defined(STM32H5))
|
||||
#if !defined(MG_HEALTH_DATA_SIZE) && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5))
|
||||
#define MG_HEALTH_DATA_SIZE 250
|
||||
#endif
|
||||
|
||||
@@ -4606,13 +4542,7 @@ 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_LOWPAN,
|
||||
MG_TCPIP_L2_USER
|
||||
};
|
||||
enum mg_l2type { MG_TCPIP_L2_ETH = 0, MG_TCPIP_L2_PPP, MG_TCPIP_L2_PPPoE, MG_TCPIP_L2_USER };
|
||||
|
||||
#if defined(__DCC__)
|
||||
#pragma pack(1)
|
||||
@@ -4635,27 +4565,10 @@ 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__)
|
||||
@@ -4664,6 +4577,7 @@ union mg_l2data {
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
|
||||
enum mg_l2proto {
|
||||
MG_TCPIP_L2PROTO_IPV4 = 0,
|
||||
MG_TCPIP_L2PROTO_IPV6,
|
||||
@@ -4724,24 +4638,6 @@ 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.
|
||||
@@ -4776,8 +4672,6 @@ 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
|
||||
@@ -4809,13 +4703,6 @@ 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
|
||||
};
|
||||
|
||||
@@ -4829,15 +4716,6 @@ 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
|
||||
@@ -4873,6 +4751,39 @@ 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 {
|
||||
@@ -5262,24 +5173,23 @@ 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. Frames passed to tx() and
|
||||
// returned by rx() are raw 802.15.4 frames: MAC header and payload, no FCS.
|
||||
// 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().
|
||||
// The transceiver (XCVR) must be set up for 802.15.4 before mg_tcpip_init(),
|
||||
// see hal_radio_init() in the FRDM-MCXW71 tutorial.
|
||||
// Use with MG_TCPIP_L2_LOWPAN: channel, PAN ID and addresses are taken from
|
||||
// ifp->l2data.lowpan and ifp->mac. No driver_data
|
||||
// driver_data: struct mg_l2_802154_data, channel and PAN ID set
|
||||
|
||||
#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")); \
|
||||
#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)); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
@@ -5506,27 +5416,26 @@ 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. 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
|
||||
// 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
|
||||
|
||||
#ifndef MG_TCPIP_DRIVER_INIT
|
||||
#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")); \
|
||||
#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)); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
@@ -5694,6 +5603,13 @@ 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
|
||||
@@ -5733,9 +5649,11 @@ struct mg_tcpip_driver_stm32h_data {
|
||||
#define MG_DRIVER_MDC_CR 4
|
||||
#endif
|
||||
|
||||
#if MG_ENABLE_DRIVER_STM32H && MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
|
||||
#if MG_ENABLE_DRIVER_STM32H && !MG_ENABLE_DRIVER_STM32C && \
|
||||
MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
|
||||
#define MG_ENABLE_ETH_IRQ() NVIC_EnableIRQ(ETH_IRQn)
|
||||
#elif MG_ENABLE_DRIVER_STM32N && MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
|
||||
#elif (MG_ENABLE_DRIVER_STM32N || MG_ENABLE_DRIVER_STM32C) && \
|
||||
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,6 +15,7 @@
|
||||
</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>
|
||||
@@ -28,7 +29,7 @@
|
||||
<keyword>IMPLEMENTED_WITH_STM32CUBEMX</keyword>
|
||||
</keywords>
|
||||
<components>
|
||||
<component Cclass="Mongoose" Cgroup="Networking Core" Cversion="7.23.2" maxInstances="1">
|
||||
<component Cclass="Mongoose" Cgroup="Networking Core" Cversion="7.23.3" maxInstances="1">
|
||||
<description>Networking Core</description>
|
||||
<files>
|
||||
<file category="doc" name="README.md"/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VERSION = 7.23.2
|
||||
VERSION = 7.23.3
|
||||
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
|
||||
|
||||
+5
-64
@@ -32,6 +32,7 @@
|
||||
|
||||
#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)
|
||||
@@ -40,6 +41,8 @@
|
||||
#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
|
||||
@@ -56,70 +59,8 @@
|
||||
#define MG_OTA MG_OTA_STM32H5
|
||||
#elif !defined(MG_OTA) && defined(STM32H7)
|
||||
#define MG_OTA MG_OTA_STM32H7
|
||||
#endif
|
||||
|
||||
// OTA rollback timer via IWDG. Fixed 10 s hang-detection window (prescaler
|
||||
// /1024, LSI ~32 kHz: reload = 10 * 32000 / 1024 = 312). mg_ota_poll() feeds
|
||||
// the watchdog every 500 ms while in MG_OTA_TESTING state; if the event loop
|
||||
// hangs for 10 s, the IWDG fires and the device rolls back.
|
||||
// STM32H7 names the peripheral IWDG1; all others use IWDG.
|
||||
// The total evaluation period is set by MG_OTA_ROLLBACK_TIMEOUT_SECONDS.
|
||||
// STM32H7 names the watchdog peripheral IWDG1; all other families use IWDG.
|
||||
#if defined(IWDG1) && !defined(IWDG)
|
||||
#define MG_IWDG IWDG1
|
||||
#else
|
||||
#define MG_IWDG IWDG
|
||||
#endif
|
||||
// PR=6 → /256 prescaler, RLR=1250 → ~10 s at LSI 32 kHz.
|
||||
// Works on F4/F7/H5/H7; uses direct register writes so it is safe before HAL_Init().
|
||||
#ifndef MG_OTA_ROLLBACK_TIMER_START
|
||||
#define MG_OTA_ROLLBACK_TIMER_START() \
|
||||
do { \
|
||||
MG_IWDG->KR = 0xCCCCU; /* start + LSI */ \
|
||||
MG_IWDG->KR = 0x5555U; /* unlock PR/RLR */ \
|
||||
MG_IWDG->PR = 6U; /* /256 */ \
|
||||
MG_IWDG->RLR = 1250U; /* 10 s */ \
|
||||
while (MG_IWDG->SR & (IWDG_SR_PVU | IWDG_SR_RVU)) (void) 0; \
|
||||
MG_IWDG->KR = 0xAAAAU; /* reload */ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
// Feed (reload) the IWDG. Called from mg_ota_poll() every 500 ms.
|
||||
#ifndef MG_OTA_ROLLBACK_TIMER_FEED
|
||||
#define MG_OTA_ROLLBACK_TIMER_FEED() (MG_IWDG->KR = 0xAAAAU)
|
||||
#endif
|
||||
|
||||
// OTA state in a backup register that survives warm resets but clears on POR.
|
||||
// STATE_GET reads work without any setup. STATE_SET enables the RTC APB clock
|
||||
// and unlocks backup domain write access via direct register writes, so it is
|
||||
// safe to call from any context (before or after HAL_Init).
|
||||
// H5 (TAMP->BKP0R), H7 (RTC->BKP0R), F4/F7 (RTC->BKP0R).
|
||||
#ifndef MG_OTA_STATE_GET
|
||||
#if defined(PWR_DBPCR_DBP)
|
||||
#define MG_OTA_DBP_ENABLE() (PWR->DBPCR |= PWR_DBPCR_DBP)
|
||||
#elif defined(PWR_CR1_DBP)
|
||||
#define MG_OTA_DBP_ENABLE() (PWR->CR1 |= PWR_CR1_DBP)
|
||||
#else
|
||||
#define MG_OTA_DBP_ENABLE() (PWR->CR |= PWR_CR_DBP)
|
||||
#endif
|
||||
|
||||
#if defined(TAMP)
|
||||
#define MG_OTA_STATE_GET() \
|
||||
(RCC->APB3ENR |= RCC_APB3ENR_RTCAPBEN, TAMP->BKP0R)
|
||||
#define MG_OTA_STATE_SET(v) \
|
||||
(RCC->APB3ENR |= RCC_APB3ENR_RTCAPBEN, MG_OTA_DBP_ENABLE(), \
|
||||
(TAMP->BKP0R = (uint32_t) (v)), TAMP->BKP0R)
|
||||
#elif defined(RCC_APB4ENR_RTCAPBEN)
|
||||
#define MG_OTA_STATE_GET() (RTC->BKP0R)
|
||||
#define MG_OTA_STATE_SET(v) \
|
||||
(RCC->APB4ENR |= RCC_APB4ENR_RTCAPBEN, MG_OTA_DBP_ENABLE(), \
|
||||
(RTC->BKP0R = (uint32_t) (v)))
|
||||
#else
|
||||
#define MG_OTA_STATE_GET() (RTC->BKP0R)
|
||||
#define MG_OTA_STATE_SET(v) \
|
||||
(RCC->APB1ENR |= RCC_APB1ENR_PWREN, MG_OTA_DBP_ENABLE(), \
|
||||
(RTC->BKP0R = (uint32_t) (v)))
|
||||
#endif
|
||||
#elif !defined(MG_OTA) && defined(STM32C5)
|
||||
#define MG_OTA MG_OTA_STM32C5
|
||||
#endif
|
||||
|
||||
// use HAL-defined execute-in-ram section
|
||||
|
||||
@@ -65,13 +65,8 @@
|
||||
#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
|
||||
@@ -216,8 +211,6 @@
|
||||
|
||||
#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
|
||||
@@ -255,4 +248,3 @@
|
||||
#ifndef MG_ENABLE_HEALTH
|
||||
#define MG_ENABLE_HEALTH 0
|
||||
#endif
|
||||
|
||||
+3
-3
@@ -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. 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;
|
||||
// Authenticated above. Streaming owns the connection and c->data.
|
||||
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') {
|
||||
|
||||
@@ -173,15 +173,6 @@ 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;
|
||||
@@ -205,14 +196,6 @@ 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
|
||||
@@ -331,11 +314,7 @@ 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;
|
||||
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);
|
||||
sendnsreq(c, &host, c->mgr->dnstimeout, dns, c->mgr->use_dns6);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+31
-69
@@ -17,6 +17,8 @@ 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)
|
||||
@@ -24,6 +26,7 @@ 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
|
||||
|
||||
@@ -31,35 +34,20 @@ 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, 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
|
||||
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
|
||||
|
||||
// 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();
|
||||
@@ -72,7 +60,8 @@ 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;
|
||||
}
|
||||
@@ -85,88 +74,61 @@ 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) {
|
||||
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"));
|
||||
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"));
|
||||
return false;
|
||||
}
|
||||
MGI_ZLL->SEQ_CTRL_STS |= MGI_SEQ_CTRL_NO_RX_RECYCLE;
|
||||
// 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->PHY_CTRL = MGI_PHY_CTRL_ALL_IRQ_MASKS; // Mask all IRQs, we poll
|
||||
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 = channel;
|
||||
MGI_ZLL->PA_PWR = 0x3f; // PA slices, max output power
|
||||
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
|
||||
mg_mcxw71_start(MGI_SEQ_RX);
|
||||
mg_mcxw71_addrs(ifp);
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t mg_mcxw71_tx(const void *buf, size_t len,
|
||||
struct mg_tcpip_if *ifp) {
|
||||
const uint8_t *p = (const uint8_t *) buf;
|
||||
// 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 = 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
|
||||
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
|
||||
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_TX);
|
||||
ok = mg_mcxw71_wait();
|
||||
mg_mcxw71_idle();
|
||||
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_tx(const void *buf, size_t len,
|
||||
struct mg_tcpip_if *ifp) {
|
||||
return mg_l2_802154_tx(ifp, buf, len, mg_mcxw71_send);
|
||||
}
|
||||
|
||||
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 > len) n = len;
|
||||
for (i = 0; i < n; i++) ((uint8_t *) buf)[i] = MGI_RX_PB[i];
|
||||
if (n > sizeof(f)) n = sizeof(f);
|
||||
for (i = 0; i < n; i++) f[i] = MGI_RX_PB[i];
|
||||
}
|
||||
mg_mcxw71_idle();
|
||||
mg_mcxw71_start(MGI_SEQ_RX);
|
||||
(void) ifp;
|
||||
return n;
|
||||
return mg_l2_802154_rx(ifp, f, n, buf, len);
|
||||
}
|
||||
|
||||
static bool mg_mcxw71_poll(struct mg_tcpip_if *ifp, bool s1) {
|
||||
|
||||
+14
-15
@@ -3,24 +3,23 @@
|
||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_MCXW71) && \
|
||||
MG_ENABLE_DRIVER_MCXW71
|
||||
|
||||
// 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.
|
||||
// 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().
|
||||
// The transceiver (XCVR) must be set up for 802.15.4 before mg_tcpip_init(),
|
||||
// see hal_radio_init() in the FRDM-MCXW71 tutorial.
|
||||
// Use with MG_TCPIP_L2_LOWPAN: channel, PAN ID and addresses are taken from
|
||||
// ifp->l2data.lowpan and ifp->mac. No driver_data
|
||||
// driver_data: struct mg_l2_802154_data, channel and PAN ID set
|
||||
|
||||
#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")); \
|
||||
#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)); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
+29
-50
@@ -73,8 +73,6 @@ 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
|
||||
@@ -84,7 +82,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 4
|
||||
#define MGI_RXQ_CNT 16 // A whole Ethernet frame, 15 radio frames
|
||||
|
||||
// Driver state, one radio
|
||||
struct mg_rw612 {
|
||||
@@ -103,9 +101,6 @@ 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;
|
||||
|
||||
@@ -337,31 +332,6 @@ 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));
|
||||
@@ -370,11 +340,12 @@ 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;
|
||||
uint8_t f[8], one = 1;
|
||||
struct mg_l2_802154_data *d =
|
||||
(struct mg_l2_802154_data *) ifp->driver_data;
|
||||
uint8_t f[8], one = 1, pan[2];
|
||||
uint64_t expire;
|
||||
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"));
|
||||
if (d == NULL || d->channel < 11 || d->channel > 26) {
|
||||
MG_ERROR(("Set driver_data, channel 11..26"));
|
||||
return false;
|
||||
}
|
||||
if (MGI_MBOX->magic[0] != MGI_SYNC_MAGIC) {
|
||||
@@ -383,7 +354,6 @@ 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);
|
||||
@@ -415,25 +385,31 @@ 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");
|
||||
}
|
||||
mg_rw612_addrs(ifp);
|
||||
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");
|
||||
}
|
||||
if (!mg_rw612_set(MGI_PROP_RAW_STREAM, &one, 1)) {
|
||||
return mg_rw612_fail("Raw stream enable failed");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t mg_rw612_154_tx(const void *buf, size_t len,
|
||||
struct mg_tcpip_if *ifp) {
|
||||
// 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;
|
||||
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, buf, len), n += len;
|
||||
memcpy(f + n, p, len), n += len;
|
||||
f[n++] = 0, f[n++] = 0; // FCS, filled in by the radio
|
||||
f[n++] = ifp->l2data.lowpan.channel;
|
||||
f[n++] = d->channel;
|
||||
f[n++] = 4; // Max CSMA backoffs
|
||||
f[n++] = 3; // Max frame retries, for frames with ACK request
|
||||
f[n++] = 0; // Max frame retries: broadcast frames are not acknowledged
|
||||
f[n++] = 1; // CSMA-CA enabled
|
||||
if (!mg_rw612_request(f, n, MGI_TX_TIMEOUT_MS)) {
|
||||
MG_ERROR(("TX timeout"));
|
||||
@@ -446,18 +422,21 @@ static size_t mg_rw612_154_tx(const void *buf, 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 n = 0;
|
||||
size_t res = 0;
|
||||
mg_rw612_poll_imu();
|
||||
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);
|
||||
while (s->rxtail != s->rxhead && res == 0) {
|
||||
res = mg_l2_802154_rx(ifp, s->rxq[s->rxtail], s->rxlen[s->rxtail], buf,
|
||||
len);
|
||||
s->rxtail = (uint8_t) ((s->rxtail + 1) % MGI_RXQ_CNT);
|
||||
}
|
||||
return n;
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool mg_rw612_154_poll(struct mg_tcpip_if *ifp, bool s1) {
|
||||
|
||||
+18
-19
@@ -3,27 +3,26 @@
|
||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RW612_154) && \
|
||||
MG_ENABLE_DRIVER_RW612_154
|
||||
|
||||
// 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
|
||||
// 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
|
||||
|
||||
#ifndef MG_TCPIP_DRIVER_INIT
|
||||
#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")); \
|
||||
#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)); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
|
||||
@@ -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_DEBUG(("%.*s", s_at_resp_len, at_resp)); // TODO(scaprile): --> VERBOSE
|
||||
MG_VERBOSE(("%.*s", s_at_resp_len, at_resp));
|
||||
// BT-ENABLED DEPENDENCY
|
||||
|
||||
// MODULE DEPENDENCY
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#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
|
||||
@@ -310,7 +311,7 @@ static uint32_t s_rxno;
|
||||
#if MG_ENABLE_DRIVER_MCXN
|
||||
void ETHERNET_IRQHandler(void);
|
||||
void ETHERNET_IRQHandler(void) {
|
||||
#elif MG_ENABLE_DRIVER_STM32H
|
||||
#elif MG_ENABLE_DRIVER_STM32H && !MG_ENABLE_DRIVER_STM32C
|
||||
void ETH_IRQHandler(void);
|
||||
void ETH_IRQHandler(void) {
|
||||
#else
|
||||
@@ -339,8 +340,10 @@ void ETH1_IRQHandler(void) {
|
||||
}
|
||||
ETH->DMACSR =
|
||||
MG_BIT(7) | MG_BIT(8); // Clear possible RBU RPS while processing
|
||||
ETH->DMACRDTPR =
|
||||
(uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT - 1]; // and resume RX
|
||||
// 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];
|
||||
}
|
||||
|
||||
struct mg_tcpip_driver mg_tcpip_driver_stm32h = {
|
||||
|
||||
+11
-2
@@ -4,6 +4,13 @@
|
||||
#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
|
||||
@@ -43,9 +50,11 @@ struct mg_tcpip_driver_stm32h_data {
|
||||
#define MG_DRIVER_MDC_CR 4
|
||||
#endif
|
||||
|
||||
#if MG_ENABLE_DRIVER_STM32H && MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
|
||||
#if MG_ENABLE_DRIVER_STM32H && !MG_ENABLE_DRIVER_STM32C && \
|
||||
MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
|
||||
#define MG_ENABLE_ETH_IRQ() NVIC_EnableIRQ(ETH_IRQn)
|
||||
#elif MG_ENABLE_DRIVER_STM32N && MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
|
||||
#elif (MG_ENABLE_DRIVER_STM32N || MG_ENABLE_DRIVER_STM32C) && \
|
||||
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
@@ -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))
|
||||
#if MG_ENABLE_HEALTH && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5))
|
||||
// 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
@@ -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))
|
||||
#if !defined(MG_HEALTH_RAM) && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5))
|
||||
#define MG_HEALTH_RAM __attribute__((section(".mg_health")))
|
||||
#endif
|
||||
|
||||
#if !defined(MG_HEALTH_DATA_SIZE) && (defined(STM32H7_DEV_ID) || defined(STM32H5))
|
||||
#if !defined(MG_HEALTH_DATA_SIZE) && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5))
|
||||
#define MG_HEALTH_DATA_SIZE 250
|
||||
#endif
|
||||
|
||||
|
||||
+43
-33
@@ -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 if (!mg_http_stream_body(c, hm, serve_upload_cb, fd)) {
|
||||
mg_fs_close(fd);
|
||||
} else {
|
||||
mg_http_stream_body(c, hm, serve_upload_cb, fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1014,6 +1014,7 @@ 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
|
||||
@@ -1021,18 +1022,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->received < us->expected) {
|
||||
if (!us->finished) {
|
||||
// Connection dropped mid-upload. Let cb clean up, e.g. call mg_ota_end()
|
||||
us->received = us->expected; // MG_EV_ERROR is followed by MG_EV_CLOSE
|
||||
us->finished = true; // MG_EV_ERROR is followed by MG_EV_CLOSE
|
||||
fn(NULL, NULL, &us->user_data);
|
||||
}
|
||||
} else if (ev == MG_EV_READ && !c->is_draining) {
|
||||
if (c->recv.len > 0 && us->received < us->expected) {
|
||||
} else if (ev == MG_EV_READ && !c->is_draining && !us->finished) {
|
||||
if (!us->failed && 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;
|
||||
@@ -1043,10 +1044,13 @@ 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->received >= us->expected) {
|
||||
if (!us->finished && (us->failed || 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) {
|
||||
if (c->is_client && us->failed) {
|
||||
mg_error(c, "Body streaming failed");
|
||||
} else if (!c->is_client) {
|
||||
mg_http_reply(c, us->failed ? 500 : 200, NULL, "%zu %s\n",
|
||||
us->received, us->failed ? "fail" : "ok");
|
||||
}
|
||||
@@ -1055,48 +1059,54 @@ static void mg_stream_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
}
|
||||
}
|
||||
|
||||
bool mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
|
||||
void mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
|
||||
bool (*cb)(struct mg_http_message *, struct mg_str *,
|
||||
void **user_data),
|
||||
void **),
|
||||
void *user_data) {
|
||||
struct mg_upload_state *us = (struct mg_upload_state *) c->data;
|
||||
struct mg_str *te = mg_http_get_header(hm, "Transfer-Encoding");
|
||||
bool handled = false;
|
||||
int code = 0;
|
||||
size_t head_len = hm->head.len;
|
||||
|
||||
// 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 (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
|
||||
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;
|
||||
} else if (sizeof(*us) > sizeof(c->data)) {
|
||||
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;
|
||||
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, "");
|
||||
}
|
||||
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 (mg_http_get_header(hm, "Expect") != NULL) {
|
||||
if (!c->is_client && !us->failed && !us->finished &&
|
||||
mg_http_get_header(hm, "Expect") != NULL) {
|
||||
mg_http_reply(c, 100, NULL, ""); // If curl expects, we continue
|
||||
}
|
||||
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_iobuf_del(&c->recv, 0, head_len); // Delete HTTP headers
|
||||
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) {
|
||||
|
||||
+14
-11
@@ -205,29 +205,32 @@ int mg_http_status(const struct mg_http_message *hm);
|
||||
|
||||
// Streams a POST/PUT body to a callback as it arrives, without buffering it.
|
||||
// Call it from an MG_EV_HTTP_HDRS handler, after matching the request, e.g.
|
||||
// with mg_match(). Returns true if it took over the connection, false if the
|
||||
// request is not POST/PUT, is chunked, or has no Content-Length.
|
||||
// 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.
|
||||
//
|
||||
// Callback upload_fn(hm, data, user_data) is called:
|
||||
// - at start: hm is set. Open the file, set *user_data.
|
||||
// - per chunk: data is set, 512 byte aligned except the last. Not called
|
||||
// again after a failure.
|
||||
// - at end: both NULL. Always called once, also when the connection drops
|
||||
// early, so release resources here. A truncated body looks like a complete
|
||||
// one: validate before committing.
|
||||
// Return true on success, false on error.
|
||||
// - 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.
|
||||
// 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:
|
||||
// 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,
|
||||
// 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,
|
||||
bool (*upload_fn)(struct mg_http_message *,
|
||||
struct mg_str *, void **user_data),
|
||||
void *user_data);
|
||||
@@ -56,19 +56,6 @@ 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 *));
|
||||
@@ -84,27 +71,25 @@ 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_lowpan_init};
|
||||
mg_l2_pppoe_init};
|
||||
static const l2_poll_fn l2_poll[] = {mg_l2_eth_poll, mg_l2_ppp_poll,
|
||||
mg_l2_pppoe_poll, mg_l2_lowpan_poll};
|
||||
mg_l2_pppoe_poll};
|
||||
static const l2_header_fn l2_header[] = {mg_l2_eth_header, mg_l2_ppp_header,
|
||||
mg_l2_pppoe_header,
|
||||
mg_l2_lowpan_header};
|
||||
mg_l2_pppoe_header};
|
||||
static const l2_trailer_fn l2_trailer[] = {mg_l2_eth_trailer, mg_l2_ppp_trailer,
|
||||
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};
|
||||
mg_l2_pppoe_trailer};
|
||||
static const l2_rx_fn l2_rx[] = {mg_l2_eth_rx, mg_l2_ppp_rx, mg_l2_pppoe_rx};
|
||||
static const l2_getaddr_fn l2_getaddr[] = {mg_l2_eth_getaddr, mg_l2_ppp_getaddr,
|
||||
mg_l2_ppp_getaddr, mg_l2_lowpan_getaddr};
|
||||
mg_l2_ppp_getaddr};
|
||||
static const l2_mapip_fn l2_mapip[] = {mg_l2_eth_mapip, mg_l2_ppp_mapip,
|
||||
mg_l2_ppp_mapip, mg_l2_lowpan_mapip};
|
||||
mg_l2_ppp_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_lowpan_genip6};
|
||||
mg_l2_ppp_genip6};
|
||||
static const l2_ip6get_fn l2_ip6get[] = {mg_l2_eth_ip6get, mg_l2_ppp_ip6get,
|
||||
mg_l2_ppp_ip6get, mg_l2_lowpan_ip6get};
|
||||
mg_l2_ppp_ip6get};
|
||||
static const l2_ip6put_fn l2_ip6put[] = {mg_l2_eth_ip6put, mg_l2_ppp_ip6put,
|
||||
mg_l2_ppp_ip6put, mg_l2_lowpan_ip6put};
|
||||
mg_l2_ppp_ip6put};
|
||||
#endif
|
||||
|
||||
void mg_l2_init(struct mg_tcpip_if *ifp) {
|
||||
|
||||
@@ -6,13 +6,7 @@
|
||||
#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_LOWPAN,
|
||||
MG_TCPIP_L2_USER
|
||||
};
|
||||
enum mg_l2type { MG_TCPIP_L2_ETH = 0, MG_TCPIP_L2_PPP, MG_TCPIP_L2_PPPoE, MG_TCPIP_L2_USER };
|
||||
|
||||
#if defined(__DCC__)
|
||||
#pragma pack(1)
|
||||
@@ -35,27 +29,10 @@ 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__)
|
||||
@@ -64,6 +41,7 @@ union mg_l2data {
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
|
||||
enum mg_l2proto {
|
||||
MG_TCPIP_L2PROTO_IPV4 = 0,
|
||||
MG_TCPIP_L2PROTO_IPV6,
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#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
@@ -1,551 +0,0 @@
|
||||
#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
|
||||
@@ -211,13 +211,7 @@ 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
|
||||
#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
|
||||
) {
|
||||
} else if (mgr->ifp != NULL && mgr->ifp->state != MG_TCPIP_STATE_READY) {
|
||||
MG_ERROR(("Network is down"));
|
||||
#endif
|
||||
} else if ((c = mg_alloc_conn(mgr)) == NULL) {
|
||||
|
||||
+19
-262
@@ -1,7 +1,6 @@
|
||||
#include "net_builtin.h"
|
||||
#include "bsd.h"
|
||||
#include "profile.h"
|
||||
#include "url.h"
|
||||
|
||||
#if MG_ENABLE_TCPIP
|
||||
#define MG_EPHEMERAL_PORT_BASE 32768
|
||||
@@ -970,7 +969,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_VERBOSE(("NDP Router Solicitation sent"));
|
||||
MG_DEBUG(("NDP Router Solicitation sent"));
|
||||
}
|
||||
|
||||
static void fill_prefix(uint8_t *dst, uint8_t *src, uint8_t len) {
|
||||
@@ -1009,10 +1008,9 @@ 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, *dns = NULL;
|
||||
uint8_t *prefix = NULL, prefix_len = 0;
|
||||
|
||||
if (pkt->pay.len < sizeof(*ra)) return;
|
||||
if (ifp->state6 == MG_TCPIP_STATE_UP) {
|
||||
@@ -1048,21 +1046,11 @@ 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;
|
||||
@@ -1088,208 +1076,6 @@ 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;
|
||||
@@ -1313,9 +1099,6 @@ 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;
|
||||
@@ -1577,9 +1360,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;
|
||||
// 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;
|
||||
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
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
@@ -2096,9 +1879,6 @@ 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);
|
||||
@@ -2124,9 +1904,6 @@ 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));
|
||||
@@ -2144,8 +1921,6 @@ 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) {
|
||||
@@ -2155,7 +1930,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_DEBUG(("IPv6 extension header %d", (int) next));
|
||||
MG_INFO(("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));
|
||||
@@ -2188,8 +1963,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_VERBOSE(("ICMPv6 %M -> %M len %u", mg_print_ip6, &pkt->ip6->src,
|
||||
mg_print_ip6, &pkt->ip6->dst, (int) pkt->pay.len));
|
||||
MG_DEBUG(("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);
|
||||
@@ -2199,9 +1974,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_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));
|
||||
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));
|
||||
if (ifp->enable_dhcp6_client && pkt->udp->dport == mg_htons(546)) {
|
||||
pkt->dhcp6 = (struct dhcp6 *) (pkt->udp + 1);
|
||||
mkpay(pkt, pkt->dhcp6 + 1);
|
||||
@@ -2223,9 +1998,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_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));
|
||||
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));
|
||||
rx_tcp(ifp, pkt);
|
||||
} else {
|
||||
MG_DEBUG(("Unknown IPv6 next hdr %x", (int) next));
|
||||
@@ -2303,18 +2078,13 @@ 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 || ifp->l2type == MG_TCPIP_L2_LOWPAN) &&
|
||||
l2_up && ifp->ip6ll[0] == 0 &&
|
||||
if (ifp->l2type == MG_TCPIP_L2_ETH && 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
|
||||
@@ -2506,8 +2276,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->l2type != MG_TCPIP_L2_LOWPAN)
|
||||
ifp->enable_dhcp_client = true; // No IPv4 over 6LoWPAN
|
||||
ifp->l2type != MG_TCPIP_L2_PPPoE)
|
||||
ifp->enable_dhcp_client = true;
|
||||
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
|
||||
@@ -2529,18 +2299,8 @@ 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));
|
||||
@@ -2714,13 +2474,10 @@ 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;
|
||||
// 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)) {
|
||||
if (!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->is_resolving && c->loc.is_ip6 &&
|
||||
ifp->state6 != MG_TCPIP_STATE_READY) {
|
||||
} else if (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)) {
|
||||
|
||||
+33
-36
@@ -44,24 +44,6 @@ 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.
|
||||
@@ -96,8 +78,6 @@ 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
|
||||
@@ -129,13 +109,6 @@ 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
|
||||
};
|
||||
|
||||
@@ -149,15 +122,6 @@ 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
|
||||
@@ -193,6 +157,39 @@ 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 {
|
||||
|
||||
@@ -208,9 +208,7 @@ 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));
|
||||
if (!mg_http_stream_body(c, hm, s_firmware_cb, NULL)) {
|
||||
mg_error(c, "Cannot stream firmware");
|
||||
}
|
||||
mg_http_stream_body(c, hm, s_firmware_cb, NULL);
|
||||
}
|
||||
} else if (ev == MG_EV_ERROR) {
|
||||
s_ota->fn((char *) ev_data);
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#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
|
||||
@@ -79,9 +80,8 @@ 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)) {
|
||||
// mg_http_reply(c, 400, "", "POST firmware with Content-Length\n");
|
||||
// if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||
// mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
|
||||
@@ -172,8 +172,6 @@ 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
@@ -2,7 +2,7 @@
|
||||
#include "log.h"
|
||||
#include "ota.h"
|
||||
|
||||
#if MG_OTA == MG_OTA_STM32H5
|
||||
#if MG_OTA == MG_OTA_STM32H5 || MG_OTA == MG_OTA_STM32C5
|
||||
|
||||
static bool mg_stm32h5_write(void *, const void *, size_t);
|
||||
static bool mg_stm32h5_swap(void);
|
||||
|
||||
@@ -138,10 +138,6 @@ 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;
|
||||
|
||||
+3
-2
@@ -261,8 +261,9 @@ uint64_t mg_ntohll(uint64_t net) {
|
||||
}
|
||||
|
||||
void mg_delayms(unsigned int ms) {
|
||||
uint64_t to = mg_millis() + ms + 1;
|
||||
while (mg_millis() < to) (void) 0;
|
||||
uint64_t now, to = mg_millis() + ms + 1;
|
||||
while ((now = mg_millis()) < to)
|
||||
if (now + ms + 1 < to) to = now + ms + 1;
|
||||
}
|
||||
|
||||
#if MG_ENABLE_CUSTOM_CALLOC
|
||||
|
||||
@@ -555,7 +555,6 @@ 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,
|
||||
@@ -570,12 +569,6 @@ 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,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
// See https://mongoose.ws/docs/getting-started/build-environment/
|
||||
// See https://mongoose.ws/documentation/#build-options
|
||||
|
||||
#define MG_ENABLE_TCPIP 1
|
||||
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
|
||||
|
||||
+108
-6
@@ -3158,8 +3158,6 @@ 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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3194,8 +3192,7 @@ 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) {
|
||||
ASSERT(mg_http_stream_body(c, (struct mg_http_message *) ev_data, cr_cb,
|
||||
NULL));
|
||||
mg_http_stream_body(c, (struct mg_http_message *) ev_data, cr_cb, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3208,12 +3205,103 @@ 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);
|
||||
@@ -3257,7 +3345,7 @@ static void test_http_upload(void) {
|
||||
ASSERT(mgr.conns == NULL);
|
||||
}
|
||||
|
||||
// mg_http_stream_body: chunked request is not streamed, caller replies "400"
|
||||
// mg_http_stream_body: chunked request rejected with "400", cleanup still runs
|
||||
{
|
||||
char buf[FETCH_BUF_SIZE];
|
||||
s_su_done = s_su_end = 0;
|
||||
@@ -3268,7 +3356,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 == 0 && s_su_end == 0); // The callback was never called
|
||||
ASSERT(s_su_done == 1 && s_su_end == 1); // End called without start
|
||||
ASSERT(fopen("su_chunked.txt", "r") == NULL);
|
||||
mg_mgr_free(&mgr);
|
||||
ASSERT(mgr.conns == NULL);
|
||||
@@ -5010,6 +5098,20 @@ 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,9 +1,11 @@
|
||||
THISDIR = $(realpath $(CURDIR))
|
||||
ROOTDIR = $(realpath $(CURDIR)/../../..)
|
||||
DOCKER ?= docker run --rm $(DA) -v $(ROOTDIR):$(ROOTDIR) -w $(THISDIR) espressif/idf
|
||||
CMD ?= idf.py build
|
||||
CMD ?= idf.py -DCFLAGS_EXTRA="$(CFLAGS_EXTRA)" build
|
||||
PORT ?= /dev/ttyUSB0
|
||||
|
||||
CFLAGS_EXTRA ?= -DMG_TLS=MG_TLS_BUILTIN
|
||||
|
||||
all: example
|
||||
|
||||
example:
|
||||
|
||||
@@ -12,3 +12,5 @@ idf_component_register(SRCS "main.c"
|
||||
espcoredump
|
||||
nvs_flash
|
||||
app_update)
|
||||
separate_arguments(CFLAGS_EXTRA)
|
||||
component_compile_options(${CFLAGS_EXTRA})
|
||||
@@ -44,6 +44,11 @@ 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,4 +1 @@
|
||||
#define MG_ARCH MG_ARCH_ESP32
|
||||
|
||||
#define MG_TLS MG_TLS_BUILTIN
|
||||
//#define MG_TLS MG_TLS_MBED
|
||||
@@ -9,7 +9,12 @@ 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_MSG) mg_http_reply(c, 200, "", "hi\n");
|
||||
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");
|
||||
(void) ev_data;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,12 @@ 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_MSG) mg_http_reply(c, 200, "", "hi\n");
|
||||
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");
|
||||
(void) ev_data;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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_now())) {
|
||||
if (mg_timer_expired(&timer, MQTT_RECONNECT_PERIOD_MS, mg_millis())) {
|
||||
if (s_mqtt_conn == NULL) {
|
||||
struct mg_mqtt_opts opts = {
|
||||
.clean = true,
|
||||
|
||||
@@ -65,6 +65,11 @@ 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,6 +89,11 @@ 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,6 +97,11 @@ 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,10 +1,10 @@
|
||||
// Copyright (c) 2026 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
//
|
||||
// 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
|
||||
// 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
|
||||
|
||||
#include "hal.h"
|
||||
#include "mongoose.h"
|
||||
@@ -49,11 +49,8 @@ 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)) {
|
||||
mg_http_reply(c, 400, "", "POST firmware with Content-Length\n");
|
||||
c->is_draining = 1;
|
||||
mg_iobuf_del(&c->recv, 0, c->recv.len);
|
||||
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;
|
||||
@@ -81,8 +78,8 @@ int main(void) {
|
||||
|
||||
struct mg_mgr mgr;
|
||||
mg_mgr_init(&mgr);
|
||||
mg_http_listen(&mgr, "http://[::]:80", http_ev_handler, NULL);
|
||||
mg_http_listen(&mgr, "https://[::]:443", http_ev_handler, NULL);
|
||||
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_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 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]; \
|
||||
// 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]; \
|
||||
} while (0)
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,101 @@
|
||||
// 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 = ð_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(ð.recv_queue, eth_queue, sizeof(eth_queue));
|
||||
if (!eth.driver->init(ð)) 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(ð.recv_queue, &p)) > 0) { // Ethernet -> radio
|
||||
if (n >= 14 && for_radio((uint8_t *) p)) radio.driver->tx(p, n, &radio);
|
||||
mg_queue_del(ð.recv_queue, n);
|
||||
}
|
||||
if ((n = radio.driver->rx(frame, sizeof(frame), &radio)) >= 14) {
|
||||
learn(frame + 6); // Radio -> Ethernet
|
||||
eth.driver->tx(frame, n, ð);
|
||||
}
|
||||
if (mg_timer_expired(&timer, 1000, mg_millis())) { // Ethernet link
|
||||
bool up = eth.driver->poll(ð, true);
|
||||
eth.state = up ? MG_TCPIP_STATE_UP : MG_TCPIP_STATE_DOWN;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#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
|
||||
File renamed without changes.
File renamed without changes.
@@ -1,14 +0,0 @@
|
||||
#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
|
||||
@@ -65,6 +65,11 @@ 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)) {
|
||||
|
||||
@@ -61,6 +61,11 @@ 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)) {
|
||||
|
||||
@@ -65,6 +65,11 @@ 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,7 +5,12 @@
|
||||
#include "mongoose.h"
|
||||
|
||||
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
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());
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -33,7 +33,12 @@ 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_MSG) { // New HTTP request received
|
||||
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());
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -58,7 +58,12 @@ 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_MSG) { // New HTTP request received
|
||||
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());
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
|
||||
@@ -39,6 +39,11 @@ 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());
|
||||
|
||||
@@ -82,6 +82,11 @@ 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());
|
||||
|
||||
@@ -195,6 +195,11 @@ 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());
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# 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
@@ -0,0 +1,46 @@
|
||||
# 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_*
|
||||
@@ -0,0 +1,141 @@
|
||||
// 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) {
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
// 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();
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
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
|
||||
}
|
||||
+27
-75
@@ -1,16 +1,16 @@
|
||||
// Copyright (c) 2026 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include "mongoose.h"
|
||||
#include "hal.h"
|
||||
#include "mongoose.h"
|
||||
|
||||
#ifndef UART_DEBUG
|
||||
#define UART_DEBUG USART3
|
||||
#endif
|
||||
#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)
|
||||
|
||||
#define LED1 PIN(0, 12) // Green
|
||||
#define LED2 PIN(0, 0) // Blue
|
||||
#define LED3 PIN(0, 1) // Red
|
||||
#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
|
||||
|
||||
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, 100, hal_get_tick())) {
|
||||
// hal_gpio_toggle(LED1);
|
||||
if (hal_timer_expired(&blink_timer, 500, hal_get_tick())) {
|
||||
hal_gpio_toggle(LED2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ 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();
|
||||
@@ -35,7 +34,6 @@ 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
|
||||
@@ -65,7 +63,12 @@ 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_MSG) { // New HTTP request received
|
||||
} 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)) {
|
||||
mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick());
|
||||
@@ -82,80 +85,29 @@ 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_rng_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);
|
||||
hal_uart_init(UART_DEBUG, UART_DEBUG_TX_PIN, UART_DEBUG_RX_PIN, 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));
|
||||
}
|
||||
|
||||
// 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 = ð_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_rng_init();
|
||||
hal_ethernet_init();
|
||||
mg_mgr_init(&mgr_eth);
|
||||
mg_tcpip_init(&mgr_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
|
||||
hal_gpio_output(LED1);
|
||||
hal_gpio_output(LED2);
|
||||
hal_gpio_output(LED3);
|
||||
|
||||
// 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(ð, &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);
|
||||
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);
|
||||
|
||||
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_eth, 0);
|
||||
mg_mgr_poll(&mgr_radio, 0);
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
blink_task();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#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,7 +66,12 @@ 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_MSG) {
|
||||
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/debug"), NULL)) {
|
||||
int level = mg_json_get_long(hm->body, "$.level", MG_LL_DEBUG);
|
||||
|
||||
@@ -68,6 +68,11 @@ 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,7 +28,12 @@ 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_MSG) {
|
||||
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;
|
||||
(void) hm;
|
||||
mg_http_reply(c, 200, "", "hi\n");
|
||||
|
||||
@@ -66,7 +66,12 @@ 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_MSG) {
|
||||
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/debug"), NULL)) {
|
||||
int level = mg_json_get_long(hm->body, "$.level", MG_LL_DEBUG);
|
||||
|
||||
@@ -68,6 +68,11 @@ 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,6 +68,11 @@ 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,7 +47,12 @@ 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_MSG) {
|
||||
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/hello"),
|
||||
NULL)) { // Request to /api/hello
|
||||
|
||||
@@ -68,6 +68,11 @@ 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,7 +89,12 @@ 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_MSG) {
|
||||
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)) {
|
||||
mg_http_reply(c, 200, "", "{%m:%lu}\n", MG_ESC("tick"), HAL_GetTick());
|
||||
|
||||
@@ -44,7 +44,12 @@ 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_MSG) {
|
||||
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)) {
|
||||
mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick());
|
||||
|
||||
@@ -43,7 +43,12 @@ 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_MSG) {
|
||||
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)) {
|
||||
mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick());
|
||||
|
||||
@@ -68,6 +68,11 @@ 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,6 +68,11 @@ 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,6 +64,11 @@ 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,6 +60,11 @@ 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,6 +68,11 @@ 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,6 +68,11 @@ 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,6 +68,11 @@ 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,7 +63,12 @@ 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_MSG) {
|
||||
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/debug"), NULL)) {
|
||||
int level = mg_json_get_long(hm->body, "$.level", MG_LL_DEBUG);
|
||||
|
||||
@@ -30,7 +30,12 @@ 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_MSG) {
|
||||
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;
|
||||
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
Reference in new issue
Block a user