mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-26 12:14:56 +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.
|
License: GPLv2 or commercial.
|
||||||
Website: https://mongoose.ws/
|
Website: https://mongoose.ws/
|
||||||
GitHub repo: https://github.com/cesanta/mongoose
|
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://opensource.org/licenses/gpl-2.0.php)
|
||||||
[](https://github.com/cesanta/mongoose/actions)
|
[](https://github.com/cesanta/mongoose/actions)
|
||||||
@@ -7,11 +7,17 @@
|
|||||||
|
|
||||||
<img src="https://mongoose.ws/images/logo.svg" width="48" height="48"
|
<img src="https://mongoose.ws/images/logo.svg" width="48" height="48"
|
||||||
align="left" style="float:left;" />
|
align="left" style="float:left;" />
|
||||||
Two files. Drop `mongoose.c` and `mongoose.h` into any C/C++ project and get a
|
Mongoose is a full-stack embedded networking library for C/C++: a TCP/IP stack
|
||||||
full network stack — HTTP, WebSocket, MQTT, TLS, firmware OTA
|
with drivers, TLS 1.3, an embedded web server (HTTP, WebSocket), MQTT and
|
||||||
updates, device dashboard — on any microcontroller or desktop OS.
|
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:
|
products - it even runs on the International Space Station! Features include:
|
||||||
|
|
||||||
- Cross-platform:
|
- 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
|
- Source code is both ISO C and ISO C++ compliant
|
||||||
- Very easy to integrate
|
- Very easy to integrate
|
||||||
- Own TCP/IP stack for bare metal / RTOS, or run on top of any existing one (lwIP, Zephyr, ...)
|
- 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
|
- 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
|
- 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/)
|
- 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
|
## Supported platforms
|
||||||
|
|
||||||
Mongoose can work on top of any TCP/IP stack that supports BSD sockets API.
|
Mongoose has its own built-in TCP/IP stack with drivers, so it needs no
|
||||||
Platforms supported by the 3rd party TCP/IP stacks:
|
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 |
|
| 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 |
|
| **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 |
|
| **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
|
## Usage Examples
|
||||||
|
|
||||||
Below are quick snippets that should give an idea how simple the API is and
|
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
|
## Commercial use
|
||||||
- Mongoose is used by hundreds of businesses, from Fortune500 giants like
|
- Mongoose is used by hundreds of businesses, from Fortune500 giants like
|
||||||
Siemens, Schneider Electric, Broadcom, Bosch, Google, Samsung, Qualcomm, Caterpillar to the small businesses
|
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/)
|
- [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/)
|
- [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/)
|
- [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/)
|
- [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 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 Azure IoT Hub](https://mongoose.ws/articles/stm32-azure-iot-hub/)
|
||||||
- [How to connect STM32 to EMQX](https://mongoose.ws/articles/stm32-emqx/)
|
- [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/)
|
- [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) && \
|
#if MG_ENABLE_TCPIP && !defined(MG_ENABLE_DRIVER_STM32F) && \
|
||||||
!defined(MG_ENABLE_DRIVER_STM32H) && !defined(MG_ENABLE_DRIVER_STM32N) && \
|
!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_CYW) && !defined(MG_ENABLE_DRIVER_CYW_SDIO) && \
|
||||||
!defined(MG_ENABLE_DRIVER_ST67W6)
|
!defined(MG_ENABLE_DRIVER_ST67W6)
|
||||||
#if defined(STM32F1) || defined(STM32F2) || defined(STM32F4) || defined(STM32F7)
|
#if defined(STM32F1) || defined(STM32F2) || defined(STM32F4) || defined(STM32F7)
|
||||||
@@ -173,6 +174,8 @@ extern "C" {
|
|||||||
#define MG_ENABLE_DRIVER_STM32H 1
|
#define MG_ENABLE_DRIVER_STM32H 1
|
||||||
#elif defined(STM32N6)
|
#elif defined(STM32N6)
|
||||||
#define MG_ENABLE_DRIVER_STM32N 1
|
#define MG_ENABLE_DRIVER_STM32N 1
|
||||||
|
#elif defined(STM32C5)
|
||||||
|
#define MG_ENABLE_DRIVER_STM32C 1
|
||||||
#else
|
#else
|
||||||
#error Select a driver in mongoose_config.h
|
#error Select a driver in mongoose_config.h
|
||||||
#endif
|
#endif
|
||||||
@@ -189,70 +192,8 @@ extern "C" {
|
|||||||
#define MG_OTA MG_OTA_STM32H5
|
#define MG_OTA MG_OTA_STM32H5
|
||||||
#elif !defined(MG_OTA) && defined(STM32H7)
|
#elif !defined(MG_OTA) && defined(STM32H7)
|
||||||
#define MG_OTA MG_OTA_STM32H7
|
#define MG_OTA MG_OTA_STM32H7
|
||||||
#endif
|
#elif !defined(MG_OTA) && defined(STM32C5)
|
||||||
|
#define MG_OTA MG_OTA_STM32C5
|
||||||
// OTA rollback timer via IWDG. Fixed 10 s hang-detection window (prescaler
|
|
||||||
// /1024, LSI ~32 kHz: reload = 10 * 32000 / 1024 = 312). mg_ota_poll() feeds
|
|
||||||
// the watchdog every 500 ms while in MG_OTA_TESTING state; if the event loop
|
|
||||||
// hangs for 10 s, the IWDG fires and the device rolls back.
|
|
||||||
// STM32H7 names the peripheral IWDG1; all others use IWDG.
|
|
||||||
// The total evaluation period is set by MG_OTA_ROLLBACK_TIMEOUT_SECONDS.
|
|
||||||
// STM32H7 names the watchdog peripheral IWDG1; all other families use IWDG.
|
|
||||||
#if defined(IWDG1) && !defined(IWDG)
|
|
||||||
#define MG_IWDG IWDG1
|
|
||||||
#else
|
|
||||||
#define MG_IWDG IWDG
|
|
||||||
#endif
|
|
||||||
// PR=6 → /256 prescaler, RLR=1250 → ~10 s at LSI 32 kHz.
|
|
||||||
// Works on F4/F7/H5/H7; uses direct register writes so it is safe before HAL_Init().
|
|
||||||
#ifndef MG_OTA_ROLLBACK_TIMER_START
|
|
||||||
#define MG_OTA_ROLLBACK_TIMER_START() \
|
|
||||||
do { \
|
|
||||||
MG_IWDG->KR = 0xCCCCU; /* start + LSI */ \
|
|
||||||
MG_IWDG->KR = 0x5555U; /* unlock PR/RLR */ \
|
|
||||||
MG_IWDG->PR = 6U; /* /256 */ \
|
|
||||||
MG_IWDG->RLR = 1250U; /* 10 s */ \
|
|
||||||
while (MG_IWDG->SR & (IWDG_SR_PVU | IWDG_SR_RVU)) (void) 0; \
|
|
||||||
MG_IWDG->KR = 0xAAAAU; /* reload */ \
|
|
||||||
} while (0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Feed (reload) the IWDG. Called from mg_ota_poll() every 500 ms.
|
|
||||||
#ifndef MG_OTA_ROLLBACK_TIMER_FEED
|
|
||||||
#define MG_OTA_ROLLBACK_TIMER_FEED() (MG_IWDG->KR = 0xAAAAU)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// OTA state in a backup register that survives warm resets but clears on POR.
|
|
||||||
// STATE_GET reads work without any setup. STATE_SET enables the RTC APB clock
|
|
||||||
// and unlocks backup domain write access via direct register writes, so it is
|
|
||||||
// safe to call from any context (before or after HAL_Init).
|
|
||||||
// H5 (TAMP->BKP0R), H7 (RTC->BKP0R), F4/F7 (RTC->BKP0R).
|
|
||||||
#ifndef MG_OTA_STATE_GET
|
|
||||||
#if defined(PWR_DBPCR_DBP)
|
|
||||||
#define MG_OTA_DBP_ENABLE() (PWR->DBPCR |= PWR_DBPCR_DBP)
|
|
||||||
#elif defined(PWR_CR1_DBP)
|
|
||||||
#define MG_OTA_DBP_ENABLE() (PWR->CR1 |= PWR_CR1_DBP)
|
|
||||||
#else
|
|
||||||
#define MG_OTA_DBP_ENABLE() (PWR->CR |= PWR_CR_DBP)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(TAMP)
|
|
||||||
#define MG_OTA_STATE_GET() \
|
|
||||||
(RCC->APB3ENR |= RCC_APB3ENR_RTCAPBEN, TAMP->BKP0R)
|
|
||||||
#define MG_OTA_STATE_SET(v) \
|
|
||||||
(RCC->APB3ENR |= RCC_APB3ENR_RTCAPBEN, MG_OTA_DBP_ENABLE(), \
|
|
||||||
(TAMP->BKP0R = (uint32_t) (v)), TAMP->BKP0R)
|
|
||||||
#elif defined(RCC_APB4ENR_RTCAPBEN)
|
|
||||||
#define MG_OTA_STATE_GET() (RTC->BKP0R)
|
|
||||||
#define MG_OTA_STATE_SET(v) \
|
|
||||||
(RCC->APB4ENR |= RCC_APB4ENR_RTCAPBEN, MG_OTA_DBP_ENABLE(), \
|
|
||||||
(RTC->BKP0R = (uint32_t) (v)))
|
|
||||||
#else
|
|
||||||
#define MG_OTA_STATE_GET() (RTC->BKP0R)
|
|
||||||
#define MG_OTA_STATE_SET(v) \
|
|
||||||
(RCC->APB1ENR |= RCC_APB1ENR_PWREN, MG_OTA_DBP_ENABLE(), \
|
|
||||||
(RTC->BKP0R = (uint32_t) (v)))
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// use HAL-defined execute-in-ram section
|
// use HAL-defined execute-in-ram section
|
||||||
@@ -1251,13 +1192,8 @@ struct timeval {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef MG_ENABLE_IPV6
|
#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
|
#define MG_ENABLE_IPV6 0
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MG_IPV6_V6ONLY
|
#ifndef MG_IPV6_V6ONLY
|
||||||
#define MG_IPV6_V6ONLY 0 // IPv6 socket binds only to V6, not V4 address
|
#define MG_IPV6_V6ONLY 0 // IPv6 socket binds only to V6, not V4 address
|
||||||
@@ -1402,8 +1338,6 @@ struct timeval {
|
|||||||
|
|
||||||
#endif
|
#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
|
#ifndef MG_SET_MAC_ADDRESS
|
||||||
#define MG_SET_MAC_ADDRESS(mac)
|
#define MG_SET_MAC_ADDRESS(mac)
|
||||||
#endif
|
#endif
|
||||||
@@ -1445,7 +1379,6 @@ struct timeval {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Macros to record timestamped events that happens with a connection.
|
// 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
|
// 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.
|
// 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.
|
// 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.
|
// 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
|
// with mg_match(). Always takes over the connection. Invalid requests receive
|
||||||
// request is not POST/PUT, is chunked, or has no Content-Length.
|
// 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:
|
// Callback upload_fn(hm, data, user_data) is called:
|
||||||
// - at start: hm is set. Open the file, set *user_data.
|
// - at start: hm is set. Open the file, set *user_data.
|
||||||
// - per chunk: data is set, 512 byte aligned except the last. Not called
|
// - per chunk: data is set, 512 byte aligned except the last. Not called
|
||||||
// again after a failure.
|
// again after a failure.
|
||||||
// - at end: both NULL. Always called once, also when the connection drops
|
// - at end: both NULL. Always called exactly once, including validation
|
||||||
// early, so release resources here. A truncated body looks like a complete
|
// failures before start and connections that drop early. Release resources
|
||||||
// one: validate before committing.
|
// here. A truncated body looks like a complete one: validate before
|
||||||
// Return true on success, false on error.
|
// committing.
|
||||||
|
// The callback returns true on success, false on error. A failure stops
|
||||||
|
// streaming immediately and calls the end callback before closing.
|
||||||
// Example:
|
// Example:
|
||||||
// if (ev == MG_EV_HTTP_HDRS && mg_match(hm->uri, mg_str("/fs/*"), NULL) {
|
// if (ev == MG_EV_HTTP_HDRS && mg_match(hm->uri, mg_str("/fs/*"), NULL)) {
|
||||||
// mg_http_stream_body(c, hm, upload, NULL));
|
// mg_http_stream_body(c, hm, upload, NULL);
|
||||||
// }
|
// }
|
||||||
// Full example:
|
// Full example:
|
||||||
// tutorials/http/file-upload-single-post
|
// tutorials/http/file-upload-single-post
|
||||||
// Related APIs:
|
// Related APIs:
|
||||||
// mg_http_listen(), mg_http_next_multipart()
|
// mg_http_listen(), mg_http_next_multipart()
|
||||||
// Notes:
|
// Notes:
|
||||||
// After it returns true, this function hijacks the connection. Existing event
|
// The existing event handler is no longer called. hm is invalid after this
|
||||||
// handler not called, hm is invalid, c->data and c->fn_data are modified
|
// call; c->data and c->fn_data are reserved for streaming state.
|
||||||
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 (*upload_fn)(struct mg_http_message *,
|
bool (*upload_fn)(struct mg_http_message *,
|
||||||
struct mg_str *, void **user_data),
|
struct mg_str *, void **user_data),
|
||||||
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 2 // STM32 H7
|
||||||
#define MG_OTA_STM32H7_DUAL_CORE 3 // STM32 H7 dual core
|
#define MG_OTA_STM32H7_DUAL_CORE 3 // STM32 H7 dual core
|
||||||
#define MG_OTA_STM32F 4 // STM32 F7/F4/F2
|
#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_CH32V307 100 // WCH CH32V307
|
||||||
#define MG_OTA_U2A 200 // Renesas U2A16, U2A8, U2A6
|
#define MG_OTA_U2A 200 // Renesas U2A16, U2A8, U2A6
|
||||||
#define MG_OTA_RT1020 300 // IMXRT1020
|
#define MG_OTA_RT1020 300 // IMXRT1020
|
||||||
@@ -4286,9 +4223,8 @@ bool mg_ota_end(void);
|
|||||||
// ```c
|
// ```c
|
||||||
// if (ev == MG_EV_HTTP_HDRS) {
|
// if (ev == MG_EV_HTTP_HDRS) {
|
||||||
// struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
// struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
// if (mg_match(hm->uri, mg_str("/ota"), NULL) &&
|
// if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||||
// !mg_http_stream_body(c, hm, mg_http_ota_cb, NULL)) {
|
// mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||||
// mg_http_reply(c, 400, "", "POST firmware with Content-Length\n");
|
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
@@ -4441,11 +4377,11 @@ void mg_ota_poll(struct mg_mgr *);
|
|||||||
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
|
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
|
||||||
#endif
|
#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")))
|
#define MG_HEALTH_RAM __attribute__((section(".mg_health")))
|
||||||
#endif
|
#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
|
#define MG_HEALTH_DATA_SIZE 250
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -4606,13 +4542,7 @@ bool mg_wifi_ap_stop(void);
|
|||||||
#if MG_ENABLE_TCPIP
|
#if MG_ENABLE_TCPIP
|
||||||
|
|
||||||
// no config defaults to 0 => Ethernet
|
// no config defaults to 0 => Ethernet
|
||||||
enum mg_l2type {
|
enum mg_l2type { MG_TCPIP_L2_ETH = 0, MG_TCPIP_L2_PPP, MG_TCPIP_L2_PPPoE, MG_TCPIP_L2_USER };
|
||||||
MG_TCPIP_L2_ETH = 0,
|
|
||||||
MG_TCPIP_L2_PPP,
|
|
||||||
MG_TCPIP_L2_PPPoE,
|
|
||||||
MG_TCPIP_L2_LOWPAN,
|
|
||||||
MG_TCPIP_L2_USER
|
|
||||||
};
|
|
||||||
|
|
||||||
#if defined(__DCC__)
|
#if defined(__DCC__)
|
||||||
#pragma pack(1)
|
#pragma pack(1)
|
||||||
@@ -4635,27 +4565,10 @@ struct eth_data {
|
|||||||
struct pppoe_data { // (struct eth_data *) &pppoe_data = pppoedata.eth
|
struct pppoe_data { // (struct eth_data *) &pppoe_data = pppoedata.eth
|
||||||
struct eth_data 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 {
|
union mg_l2data {
|
||||||
struct eth_data eth;
|
struct eth_data eth;
|
||||||
struct pppoe_data pppoe;
|
struct pppoe_data pppoe;
|
||||||
struct lowpan_data lowpan;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(__DCC__)
|
#if defined(__DCC__)
|
||||||
@@ -4664,6 +4577,7 @@ union mg_l2data {
|
|||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
enum mg_l2proto {
|
enum mg_l2proto {
|
||||||
MG_TCPIP_L2PROTO_IPV4 = 0,
|
MG_TCPIP_L2PROTO_IPV4 = 0,
|
||||||
MG_TCPIP_L2PROTO_IPV6,
|
MG_TCPIP_L2PROTO_IPV6,
|
||||||
@@ -4724,24 +4638,6 @@ enum {
|
|||||||
};
|
};
|
||||||
// clang-format on
|
// 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"
|
// Network interface. Zero-initialise, then set the fields marked "user-set"
|
||||||
// before calling mg_tcpip_init(). Fields in the "internal state" section
|
// 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.
|
// 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[8]; // Global prefix bytes
|
||||||
uint8_t prefix_len; // Global prefix length in bits
|
uint8_t prefix_len; // Global prefix length in bits
|
||||||
uint64_t gw6[2]; // IPv6 default gateway
|
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_slaac; // Enable SLAAC (stateless address autoconfiguration)
|
||||||
bool enable_dhcp6_client; // Enable DHCPv6 client (not yet fully implemented)
|
bool enable_dhcp6_client; // Enable DHCPv6 client (not yet fully implemented)
|
||||||
bool is_ip6_changed; // Set by stack when IPv6 address changes
|
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 gw6mac[sizeof(struct mg_l2addr)]; // IPv6 gateway hardware address
|
||||||
uint8_t state6; // Current IPv6 link/IP state (MG_TCPIP_STATE_*)
|
uint8_t state6; // Current IPv6 link/IP state (MG_TCPIP_STATE_*)
|
||||||
bool gw6_ready; // IPv6 gateway MAC address has been resolved
|
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
|
#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().
|
// Frees the TX buffer and RX queue allocated by mg_tcpip_init().
|
||||||
void mg_tcpip_free(struct mg_tcpip_if *);
|
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
|
// 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
|
// 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
|
// 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_mcxw71;
|
||||||
extern struct mg_tcpip_driver mg_tcpip_driver_rw612_154;
|
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).
|
// SPI bus abstraction for SPI-attached network chips (e.g. W5500).
|
||||||
// Populate and assign to the driver's driver_data field.
|
// Populate and assign to the driver's driver_data field.
|
||||||
struct mg_tcpip_spi {
|
struct mg_tcpip_spi {
|
||||||
@@ -5262,24 +5173,23 @@ struct mg_tcpip_driver_imxrt_data {
|
|||||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_MCXW71) && \
|
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_MCXW71) && \
|
||||||
MG_ENABLE_DRIVER_MCXW71
|
MG_ENABLE_DRIVER_MCXW71
|
||||||
|
|
||||||
// NXP MCXW71 IEEE 802.15.4 radio, ZLL link layer. Frames passed to tx() and
|
// NXP MCXW71 IEEE 802.15.4 radio, ZLL link layer, as an Ethernet interface:
|
||||||
// returned by rx() are raw 802.15.4 frames: MAC header and payload, no FCS.
|
// 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(),
|
// The transceiver (XCVR) must be set up for 802.15.4 before mg_tcpip_init(),
|
||||||
// see hal_radio_init() in the FRDM-MCXW71 tutorial.
|
// see hal_radio_init() in the FRDM-MCXW71 tutorial.
|
||||||
// Use with MG_TCPIP_L2_LOWPAN: channel, PAN ID and addresses are taken from
|
// driver_data: struct mg_l2_802154_data, channel and PAN ID set
|
||||||
// ifp->l2data.lowpan and ifp->mac. No driver_data
|
|
||||||
|
|
||||||
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
||||||
do { \
|
do { \
|
||||||
static struct mg_tcpip_if mif_; \
|
static struct mg_l2_802154_data driver_data_; \
|
||||||
mif_.l2type = MG_TCPIP_L2_LOWPAN; \
|
static struct mg_tcpip_if mif_; \
|
||||||
mif_.driver = &mg_tcpip_driver_mcxw71; \
|
driver_data_.channel = MG_L2_802154_CHANNEL; \
|
||||||
mif_.l2data.lowpan.channel = 15; \
|
driver_data_.pan_id = MG_L2_802154_PAN_ID; \
|
||||||
mif_.l2data.lowpan.pan_id = 0xabcd; \
|
mif_.driver = &mg_tcpip_driver_mcxw71; \
|
||||||
MG_SET_MAC_ADDRESS(mif_.mac); /* EUI-64 */ \
|
mif_.driver_data = &driver_data_; \
|
||||||
mg_tcpip_init(mgr, &mif_); \
|
MG_SET_MAC_ADDRESS(mif_.mac); \
|
||||||
(mgr)->use_dns6 = true; /* IPv6 only: no IPv4 over 6LoWPAN */ \
|
mg_tcpip_init(mgr, &mif_); \
|
||||||
MG_INFO(("Driver: mcxw71, 6LoWPAN")); \
|
MG_INFO(("Driver: mcxw71, channel %d", MG_L2_802154_CHANNEL)); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -5506,27 +5416,26 @@ struct mg_tcpip_driver_rw612_data {
|
|||||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RW612_154) && \
|
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RW612_154) && \
|
||||||
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
|
// NXP RW612 IEEE 802.15.4 radio, as an Ethernet interface: Ethernet frames
|
||||||
// combo firmware, an OpenThread radio co-processor (RCP). The firmware must
|
// are split into radio frames, see mg_l2_802154_tx(). The radio runs on
|
||||||
// be loaded and running before mg_tcpip_init(), see the FRDM-RW612 lowpan
|
// CPU2: NXP BLE/802.15.4 combo firmware, an OpenThread radio co-processor
|
||||||
// tutorial. The driver talks to it over the CPU2 <-> CPU3 IMU mailbox:
|
// (RCP). The firmware must be loaded and running before mg_tcpip_init(), see
|
||||||
// IMUMC messages carrying HDLC framed Spinel. Frames passed to tx() and
|
// the FRDM-RW612 eth_radio_bridge tutorial. The driver talks to it over the
|
||||||
// returned by rx() are raw 802.15.4 frames: MAC header and payload, no FCS.
|
// CPU2 <-> CPU3 IMU mailbox: IMUMC messages carrying HDLC framed Spinel.
|
||||||
// Use with MG_TCPIP_L2_LOWPAN: channel, PAN ID and addresses are taken from
|
// driver_data: struct mg_l2_802154_data, channel and PAN ID set
|
||||||
// ifp->l2data.lowpan and ifp->mac. No driver_data
|
|
||||||
|
|
||||||
#ifndef MG_TCPIP_DRIVER_INIT
|
#ifndef MG_TCPIP_DRIVER_INIT
|
||||||
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
||||||
do { \
|
do { \
|
||||||
static struct mg_tcpip_if mif_; \
|
static struct mg_l2_802154_data driver_data_; \
|
||||||
mif_.l2type = MG_TCPIP_L2_LOWPAN; \
|
static struct mg_tcpip_if mif_; \
|
||||||
mif_.driver = &mg_tcpip_driver_rw612_154; \
|
driver_data_.channel = MG_L2_802154_CHANNEL; \
|
||||||
mif_.l2data.lowpan.channel = 15; \
|
driver_data_.pan_id = MG_L2_802154_PAN_ID; \
|
||||||
mif_.l2data.lowpan.pan_id = 0xabcd; \
|
mif_.driver = &mg_tcpip_driver_rw612_154; \
|
||||||
MG_SET_MAC_ADDRESS(mif_.mac); /* EUI-64 */ \
|
mif_.driver_data = &driver_data_; \
|
||||||
mg_tcpip_init(mgr, &mif_); \
|
MG_SET_MAC_ADDRESS(mif_.mac); \
|
||||||
(mgr)->use_dns6 = true; /* IPv6 only: no IPv4 over 6LoWPAN */ \
|
mg_tcpip_init(mgr, &mif_); \
|
||||||
MG_INFO(("Driver: rw612_154, 6LoWPAN")); \
|
MG_INFO(("Driver: rw612_154, channel %d", MG_L2_802154_CHANNEL)); \
|
||||||
} while (0)
|
} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -5694,6 +5603,13 @@ struct mg_tcpip_driver_stm32f_data {
|
|||||||
#if !defined(MG_ENABLE_DRIVER_STM32H)
|
#if !defined(MG_ENABLE_DRIVER_STM32H)
|
||||||
#define MG_ENABLE_DRIVER_STM32H 0
|
#define MG_ENABLE_DRIVER_STM32H 0
|
||||||
#endif
|
#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)
|
#if !defined(MG_ENABLE_DRIVER_MCXN)
|
||||||
#define MG_ENABLE_DRIVER_MCXN 0
|
#define MG_ENABLE_DRIVER_MCXN 0
|
||||||
#endif
|
#endif
|
||||||
@@ -5733,9 +5649,11 @@ struct mg_tcpip_driver_stm32h_data {
|
|||||||
#define MG_DRIVER_MDC_CR 4
|
#define MG_DRIVER_MDC_CR 4
|
||||||
#endif
|
#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)
|
#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)
|
#define MG_ENABLE_ETH_IRQ() NVIC_EnableIRQ(ETH1_IRQn)
|
||||||
#elif !defined(MG_ENABLE_ETH_IRQ)
|
#elif !defined(MG_ENABLE_ETH_IRQ)
|
||||||
#define MG_ENABLE_ETH_IRQ()
|
#define MG_ENABLE_ETH_IRQ()
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
</environment>
|
</environment>
|
||||||
</environments>
|
</environments>
|
||||||
<releases>
|
<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-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-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>
|
<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>
|
<keyword>IMPLEMENTED_WITH_STM32CUBEMX</keyword>
|
||||||
</keywords>
|
</keywords>
|
||||||
<components>
|
<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>
|
<description>Networking Core</description>
|
||||||
<files>
|
<files>
|
||||||
<file category="doc" name="README.md"/>
|
<file category="doc" name="README.md"/>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
VERSION = 7.23.2
|
VERSION = 7.23.3
|
||||||
REPO_ROOT = ../..
|
REPO_ROOT = ../..
|
||||||
MX ?= /Applications/STMicroelectronics/STM32CubeMX.app/Contents/MacOs/STM32CubeMX
|
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
|
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) && \
|
#if MG_ENABLE_TCPIP && !defined(MG_ENABLE_DRIVER_STM32F) && \
|
||||||
!defined(MG_ENABLE_DRIVER_STM32H) && !defined(MG_ENABLE_DRIVER_STM32N) && \
|
!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_CYW) && !defined(MG_ENABLE_DRIVER_CYW_SDIO) && \
|
||||||
!defined(MG_ENABLE_DRIVER_ST67W6)
|
!defined(MG_ENABLE_DRIVER_ST67W6)
|
||||||
#if defined(STM32F1) || defined(STM32F2) || defined(STM32F4) || defined(STM32F7)
|
#if defined(STM32F1) || defined(STM32F2) || defined(STM32F4) || defined(STM32F7)
|
||||||
@@ -40,6 +41,8 @@
|
|||||||
#define MG_ENABLE_DRIVER_STM32H 1
|
#define MG_ENABLE_DRIVER_STM32H 1
|
||||||
#elif defined(STM32N6)
|
#elif defined(STM32N6)
|
||||||
#define MG_ENABLE_DRIVER_STM32N 1
|
#define MG_ENABLE_DRIVER_STM32N 1
|
||||||
|
#elif defined(STM32C5)
|
||||||
|
#define MG_ENABLE_DRIVER_STM32C 1
|
||||||
#else
|
#else
|
||||||
#error Select a driver in mongoose_config.h
|
#error Select a driver in mongoose_config.h
|
||||||
#endif
|
#endif
|
||||||
@@ -56,70 +59,8 @@
|
|||||||
#define MG_OTA MG_OTA_STM32H5
|
#define MG_OTA MG_OTA_STM32H5
|
||||||
#elif !defined(MG_OTA) && defined(STM32H7)
|
#elif !defined(MG_OTA) && defined(STM32H7)
|
||||||
#define MG_OTA MG_OTA_STM32H7
|
#define MG_OTA MG_OTA_STM32H7
|
||||||
#endif
|
#elif !defined(MG_OTA) && defined(STM32C5)
|
||||||
|
#define MG_OTA MG_OTA_STM32C5
|
||||||
// OTA rollback timer via IWDG. Fixed 10 s hang-detection window (prescaler
|
|
||||||
// /1024, LSI ~32 kHz: reload = 10 * 32000 / 1024 = 312). mg_ota_poll() feeds
|
|
||||||
// the watchdog every 500 ms while in MG_OTA_TESTING state; if the event loop
|
|
||||||
// hangs for 10 s, the IWDG fires and the device rolls back.
|
|
||||||
// STM32H7 names the peripheral IWDG1; all others use IWDG.
|
|
||||||
// The total evaluation period is set by MG_OTA_ROLLBACK_TIMEOUT_SECONDS.
|
|
||||||
// STM32H7 names the watchdog peripheral IWDG1; all other families use IWDG.
|
|
||||||
#if defined(IWDG1) && !defined(IWDG)
|
|
||||||
#define MG_IWDG IWDG1
|
|
||||||
#else
|
|
||||||
#define MG_IWDG IWDG
|
|
||||||
#endif
|
|
||||||
// PR=6 → /256 prescaler, RLR=1250 → ~10 s at LSI 32 kHz.
|
|
||||||
// Works on F4/F7/H5/H7; uses direct register writes so it is safe before HAL_Init().
|
|
||||||
#ifndef MG_OTA_ROLLBACK_TIMER_START
|
|
||||||
#define MG_OTA_ROLLBACK_TIMER_START() \
|
|
||||||
do { \
|
|
||||||
MG_IWDG->KR = 0xCCCCU; /* start + LSI */ \
|
|
||||||
MG_IWDG->KR = 0x5555U; /* unlock PR/RLR */ \
|
|
||||||
MG_IWDG->PR = 6U; /* /256 */ \
|
|
||||||
MG_IWDG->RLR = 1250U; /* 10 s */ \
|
|
||||||
while (MG_IWDG->SR & (IWDG_SR_PVU | IWDG_SR_RVU)) (void) 0; \
|
|
||||||
MG_IWDG->KR = 0xAAAAU; /* reload */ \
|
|
||||||
} while (0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Feed (reload) the IWDG. Called from mg_ota_poll() every 500 ms.
|
|
||||||
#ifndef MG_OTA_ROLLBACK_TIMER_FEED
|
|
||||||
#define MG_OTA_ROLLBACK_TIMER_FEED() (MG_IWDG->KR = 0xAAAAU)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// OTA state in a backup register that survives warm resets but clears on POR.
|
|
||||||
// STATE_GET reads work without any setup. STATE_SET enables the RTC APB clock
|
|
||||||
// and unlocks backup domain write access via direct register writes, so it is
|
|
||||||
// safe to call from any context (before or after HAL_Init).
|
|
||||||
// H5 (TAMP->BKP0R), H7 (RTC->BKP0R), F4/F7 (RTC->BKP0R).
|
|
||||||
#ifndef MG_OTA_STATE_GET
|
|
||||||
#if defined(PWR_DBPCR_DBP)
|
|
||||||
#define MG_OTA_DBP_ENABLE() (PWR->DBPCR |= PWR_DBPCR_DBP)
|
|
||||||
#elif defined(PWR_CR1_DBP)
|
|
||||||
#define MG_OTA_DBP_ENABLE() (PWR->CR1 |= PWR_CR1_DBP)
|
|
||||||
#else
|
|
||||||
#define MG_OTA_DBP_ENABLE() (PWR->CR |= PWR_CR_DBP)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(TAMP)
|
|
||||||
#define MG_OTA_STATE_GET() \
|
|
||||||
(RCC->APB3ENR |= RCC_APB3ENR_RTCAPBEN, TAMP->BKP0R)
|
|
||||||
#define MG_OTA_STATE_SET(v) \
|
|
||||||
(RCC->APB3ENR |= RCC_APB3ENR_RTCAPBEN, MG_OTA_DBP_ENABLE(), \
|
|
||||||
(TAMP->BKP0R = (uint32_t) (v)), TAMP->BKP0R)
|
|
||||||
#elif defined(RCC_APB4ENR_RTCAPBEN)
|
|
||||||
#define MG_OTA_STATE_GET() (RTC->BKP0R)
|
|
||||||
#define MG_OTA_STATE_SET(v) \
|
|
||||||
(RCC->APB4ENR |= RCC_APB4ENR_RTCAPBEN, MG_OTA_DBP_ENABLE(), \
|
|
||||||
(RTC->BKP0R = (uint32_t) (v)))
|
|
||||||
#else
|
|
||||||
#define MG_OTA_STATE_GET() (RTC->BKP0R)
|
|
||||||
#define MG_OTA_STATE_SET(v) \
|
|
||||||
(RCC->APB1ENR |= RCC_APB1ENR_PWREN, MG_OTA_DBP_ENABLE(), \
|
|
||||||
(RTC->BKP0R = (uint32_t) (v)))
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// use HAL-defined execute-in-ram section
|
// use HAL-defined execute-in-ram section
|
||||||
|
|||||||
@@ -65,13 +65,8 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef MG_ENABLE_IPV6
|
#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
|
#define MG_ENABLE_IPV6 0
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MG_IPV6_V6ONLY
|
#ifndef MG_IPV6_V6ONLY
|
||||||
#define MG_IPV6_V6ONLY 0 // IPv6 socket binds only to V6, not V4 address
|
#define MG_IPV6_V6ONLY 0 // IPv6 socket binds only to V6, not V4 address
|
||||||
@@ -216,8 +211,6 @@
|
|||||||
|
|
||||||
#endif
|
#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
|
#ifndef MG_SET_MAC_ADDRESS
|
||||||
#define MG_SET_MAC_ADDRESS(mac)
|
#define MG_SET_MAC_ADDRESS(mac)
|
||||||
#endif
|
#endif
|
||||||
@@ -255,4 +248,3 @@
|
|||||||
#ifndef MG_ENABLE_HEALTH
|
#ifndef MG_ENABLE_HEALTH
|
||||||
#define MG_ENABLE_HEALTH 0
|
#define MG_ENABLE_HEALTH 0
|
||||||
#endif
|
#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);
|
mg_serve_fs(c, hm, u, true);
|
||||||
return; // Important - the serve_fs() call can invalidate hm
|
return; // Important - the serve_fs() call can invalidate hm
|
||||||
} else if (mg_match(hm->uri, mg_str("/api/ota"), NULL)) {
|
} else if (mg_match(hm->uri, mg_str("/api/ota"), NULL)) {
|
||||||
// Authenticated above. If streaming started, the stream handler owns
|
// Authenticated above. Streaming owns the connection and c->data.
|
||||||
// the connection and c->data, so skip the marker check below
|
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||||
if (mg_http_stream_body(c, hm, mg_http_ota_cb, NULL)) return;
|
return;
|
||||||
}
|
}
|
||||||
if (d->marker != '\0') mg_log_http_req(c, hm);
|
if (d->marker != '\0') mg_log_http_req(c, hm);
|
||||||
} else if (ev == MG_EV_HTTP_MSG && d->marker != '\0') {
|
} 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;
|
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) {
|
static void dns_cb(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
struct dns_data *d, *tmp;
|
struct dns_data *d, *tmp;
|
||||||
struct dns_data **head = (struct dns_data **) &c->mgr->active_dns_requests;
|
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));
|
// MG_INFO(("d %p %hu %hu", d, d->txnid, dm.txnid));
|
||||||
if (dm.txnid != d->txnid) continue;
|
if (dm.txnid != d->txnid) continue;
|
||||||
if (d->c->is_resolving) {
|
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) {
|
if (dm.resolved) {
|
||||||
dm.addr.port = d->c->rem.port; // Save port
|
dm.addr.port = d->c->rem.port; // Save port
|
||||||
d->c->rem = dm.addr; // Copy resolved address
|
d->c->rem = dm.addr; // Copy resolved address
|
||||||
@@ -331,11 +314,7 @@ void mg_resolve(struct mg_connection *c, const char *url) {
|
|||||||
} else {
|
} else {
|
||||||
// host is not an IP nor a .local, send DNS resolution request
|
// 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;
|
struct mg_dns *dns = c->mgr->use_dns6 ? &c->mgr->dns6 : &c->mgr->dns4;
|
||||||
bool aaaa = c->mgr->use_dns6;
|
sendnsreq(c, &host, c->mgr->dnstimeout, dns, 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+31
-69
@@ -17,6 +17,8 @@ struct mg_zll {
|
|||||||
PART_ID, COEX_CTRL, COEX_PRIORITY, ENHACK_CTRL0;
|
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)
|
#if defined(__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE & 2)
|
||||||
#define MGI_ZLL ((struct mg_zll *) 0x58A01000)
|
#define MGI_ZLL ((struct mg_zll *) 0x58A01000)
|
||||||
#define MGI_PKT_RAM ((volatile uint8_t *) 0x58A08000)
|
#define MGI_PKT_RAM ((volatile uint8_t *) 0x58A08000)
|
||||||
@@ -24,6 +26,7 @@ struct mg_zll {
|
|||||||
#define MGI_ZLL ((struct mg_zll *) 0x48A01000)
|
#define MGI_ZLL ((struct mg_zll *) 0x48A01000)
|
||||||
#define MGI_PKT_RAM ((volatile uint8_t *) 0x48A08000)
|
#define MGI_PKT_RAM ((volatile uint8_t *) 0x48A08000)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MGI_TX_PB MGI_PKT_RAM // Length byte (incl. FCS), then frame
|
#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
|
#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_RXIRQ MG_BIT(2)
|
||||||
#define MGI_IRQSTS_RX_LEN(x) (((x) >> 24) & 0x7f) // Frame length incl. FCS
|
#define MGI_IRQSTS_RX_LEN(x) (((x) >> 24) & 0x7f) // Frame length incl. FCS
|
||||||
#define MGI_PHY_CTRL_XCVSEQ 7U
|
#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_PHY_CTRL_ALL_IRQ_MASKS 0x8007ff00U // Bits 8..18, 31: mask all IRQs
|
||||||
#define MGI_SEQ_STATE_MASK 0x1fU
|
#define MGI_SEQ_STATE_MASK 0x1fU
|
||||||
#define MGI_SEQ_STATE_RX_WU 0x10U // RX warm up
|
#define MGI_SEQ_STATE_RX_WU 0x10U // RX warm up
|
||||||
#define MGI_SEQ_CTRL_NO_RX_RECYCLE MG_BIT(5)
|
#define MGI_SEQ_CTRL_NO_RX_RECYCLE MG_BIT(5)
|
||||||
#define MGI_FRAME_FILTER 0x70bU // Beacon, data, command; versions 0..2
|
#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]
|
enum { MGI_SEQ_IDLE = 0, MGI_SEQ_RX = 1, MGI_SEQ_TX = 2 }; // PHY_CTRL[XCVSEQ]
|
||||||
#define MGI_FCF_ACK_REQUEST 0x20U // First frame byte: acknowledgment request
|
#define MGI_TX_TIMEOUT 400U // Symbols, 16 us: 127 bytes
|
||||||
#define MGI_TX_TIMEOUT 400U // Symbols, 16 us: 127 bytes, then ACK wait
|
|
||||||
#define MGI_TX_TRIES 10 // Tries for unacknowledged frames
|
|
||||||
#define MGI_TX_BACKOFF 64U // Symbols, 1 ms: backoff grows by this per try
|
|
||||||
|
|
||||||
// Symbol timer, 16 us, 24 bits
|
// Symbol timer, 16 us, 24 bits
|
||||||
static uint32_t mg_mcxw71_symbols(void) {
|
static uint32_t mg_mcxw71_symbols(void) {
|
||||||
return MGI_ZLL->EVENT_TMR >> 8;
|
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
|
// Wait for the sequence to end, up to MGI_TX_TIMEOUT symbols
|
||||||
static bool mg_mcxw71_wait(void) {
|
static bool mg_mcxw71_wait(void) {
|
||||||
uint32_t start = mg_mcxw71_symbols();
|
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()
|
// Stop the current sequence, wait for the sequencer to go idle. See PhyAbort()
|
||||||
static void mg_mcxw71_idle(void) {
|
static void mg_mcxw71_idle(void) {
|
||||||
if (MGI_ZLL->PHY_CTRL & MGI_PHY_CTRL_XCVSEQ) {
|
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;
|
MGI_ZLL->PHY_CTRL &= ~MGI_PHY_CTRL_XCVSEQ;
|
||||||
while (MGI_ZLL->SEQ_STATE & MGI_SEQ_STATE_MASK) (void) 0;
|
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;
|
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) {
|
static bool mg_mcxw71_init(struct mg_tcpip_if *ifp) {
|
||||||
uint8_t channel = ifp->l2data.lowpan.channel;
|
struct mg_l2_802154_data *d =
|
||||||
if (ifp->l2type != MG_TCPIP_L2_LOWPAN || channel < 11 || channel > 26) {
|
(struct mg_l2_802154_data *) ifp->driver_data;
|
||||||
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;
|
return false;
|
||||||
}
|
}
|
||||||
MGI_ZLL->SEQ_CTRL_STS |= MGI_SEQ_CTRL_NO_RX_RECYCLE;
|
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; // Mask all IRQs, we poll
|
||||||
MGI_ZLL->PHY_CTRL = MGI_PHY_CTRL_ALL_IRQ_MASKS | MGI_PHY_CTRL_AUTOACK;
|
|
||||||
MGI_ZLL->IRQSTS = MGI_ZLL->IRQSTS;
|
MGI_ZLL->IRQSTS = MGI_ZLL->IRQSTS;
|
||||||
MGI_ZLL->SAM_CTRL = 0;
|
MGI_ZLL->SAM_CTRL = 0;
|
||||||
MGI_ZLL->RX_FRAME_FILTER = MGI_FRAME_FILTER;
|
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->TMR_PRESCALE = 5; // 16 us timebase, one symbol
|
||||||
MGI_ZLL->CHANNEL_NUM0 = channel;
|
MGI_ZLL->CHANNEL_NUM0 = d->channel;
|
||||||
MGI_ZLL->PA_PWR = 0x3f; // PA slices, max output power
|
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_start(MGI_SEQ_RX);
|
||||||
mg_mcxw71_addrs(ifp);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t mg_mcxw71_tx(const void *buf, size_t len,
|
// Send one radio frame: MAC header and payload, no FCS
|
||||||
struct mg_tcpip_if *ifp) {
|
static size_t mg_mcxw71_send(const uint8_t *p, size_t len,
|
||||||
const uint8_t *p = (const uint8_t *) buf;
|
struct mg_tcpip_if *ifp) {
|
||||||
size_t i;
|
size_t i;
|
||||||
bool ok = false, ack = len > 0 && (p[0] & MGI_FCF_ACK_REQUEST);
|
bool ok;
|
||||||
int tries;
|
|
||||||
if (len == 0 || len > 125) return 0; // 127 bytes max, incl. 2 bytes FCS
|
|
||||||
mg_mcxw71_idle();
|
mg_mcxw71_idle();
|
||||||
MGI_TX_PB[0] = (uint8_t) (len + 2); // FCS is added by hardware
|
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
|
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);
|
mg_mcxw71_start(MGI_SEQ_TX);
|
||||||
if (ack) MGI_ZLL->PHY_CTRL |= MGI_PHY_CTRL_RXACKRQD;
|
ok = mg_mcxw71_wait();
|
||||||
// Frames with ACK request: transmit, then receive the ACK (TR sequence),
|
mg_mcxw71_idle();
|
||||||
// retry with growing backoff. See PhyPdDataRequest(), PhyISR.c
|
|
||||||
for (tries = 0; tries < (ack ? MGI_TX_TRIES : 1) && !ok; tries++) {
|
|
||||||
if (tries > 0) mg_mcxw71_delay(MGI_TX_BACKOFF * (uint32_t) tries);
|
|
||||||
mg_mcxw71_start(ack ? MGI_SEQ_TR : MGI_SEQ_TX);
|
|
||||||
ok = mg_mcxw71_wait() && (!ack || (MGI_ZLL->IRQSTS & MGI_IRQSTS_RXIRQ));
|
|
||||||
mg_mcxw71_idle();
|
|
||||||
}
|
|
||||||
MGI_ZLL->PHY_CTRL &= ~MGI_PHY_CTRL_RXACKRQD;
|
|
||||||
mg_mcxw71_start(MGI_SEQ_RX);
|
mg_mcxw71_start(MGI_SEQ_RX);
|
||||||
if (!ok) MG_DEBUG(("TX failed, %d tries", tries));
|
|
||||||
(void) ifp;
|
(void) ifp;
|
||||||
return ok ? len : 0;
|
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) {
|
static size_t mg_mcxw71_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
||||||
uint32_t irqsts = MGI_ZLL->IRQSTS;
|
uint32_t irqsts = MGI_ZLL->IRQSTS;
|
||||||
|
uint8_t f[125];
|
||||||
size_t i, n = 0;
|
size_t i, n = 0;
|
||||||
mg_mcxw71_addrs(ifp);
|
|
||||||
if ((irqsts & MGI_IRQSTS_SEQIRQ) == 0) return 0; // Still receiving
|
if ((irqsts & MGI_IRQSTS_SEQIRQ) == 0) return 0; // Still receiving
|
||||||
if (irqsts & MGI_IRQSTS_RXIRQ) {
|
if (irqsts & MGI_IRQSTS_RXIRQ) {
|
||||||
n = MGI_IRQSTS_RX_LEN(irqsts);
|
n = MGI_IRQSTS_RX_LEN(irqsts);
|
||||||
n = n >= 2 ? n - 2 : 0; // Strip FCS
|
n = n >= 2 ? n - 2 : 0; // Strip FCS
|
||||||
if (n > len) n = len;
|
if (n > sizeof(f)) n = sizeof(f);
|
||||||
for (i = 0; i < n; i++) ((uint8_t *) buf)[i] = MGI_RX_PB[i];
|
for (i = 0; i < n; i++) f[i] = MGI_RX_PB[i];
|
||||||
}
|
}
|
||||||
mg_mcxw71_idle();
|
mg_mcxw71_idle();
|
||||||
mg_mcxw71_start(MGI_SEQ_RX);
|
mg_mcxw71_start(MGI_SEQ_RX);
|
||||||
(void) ifp;
|
return mg_l2_802154_rx(ifp, f, n, buf, len);
|
||||||
return n;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool mg_mcxw71_poll(struct mg_tcpip_if *ifp, bool s1) {
|
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) && \
|
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_MCXW71) && \
|
||||||
MG_ENABLE_DRIVER_MCXW71
|
MG_ENABLE_DRIVER_MCXW71
|
||||||
|
|
||||||
// NXP MCXW71 IEEE 802.15.4 radio, ZLL link layer. Frames passed to tx() and
|
// NXP MCXW71 IEEE 802.15.4 radio, ZLL link layer, as an Ethernet interface:
|
||||||
// returned by rx() are raw 802.15.4 frames: MAC header and payload, no FCS.
|
// 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(),
|
// The transceiver (XCVR) must be set up for 802.15.4 before mg_tcpip_init(),
|
||||||
// see hal_radio_init() in the FRDM-MCXW71 tutorial.
|
// see hal_radio_init() in the FRDM-MCXW71 tutorial.
|
||||||
// Use with MG_TCPIP_L2_LOWPAN: channel, PAN ID and addresses are taken from
|
// driver_data: struct mg_l2_802154_data, channel and PAN ID set
|
||||||
// ifp->l2data.lowpan and ifp->mac. No driver_data
|
|
||||||
|
|
||||||
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
||||||
do { \
|
do { \
|
||||||
static struct mg_tcpip_if mif_; \
|
static struct mg_l2_802154_data driver_data_; \
|
||||||
mif_.l2type = MG_TCPIP_L2_LOWPAN; \
|
static struct mg_tcpip_if mif_; \
|
||||||
mif_.driver = &mg_tcpip_driver_mcxw71; \
|
driver_data_.channel = MG_L2_802154_CHANNEL; \
|
||||||
mif_.l2data.lowpan.channel = 15; \
|
driver_data_.pan_id = MG_L2_802154_PAN_ID; \
|
||||||
mif_.l2data.lowpan.pan_id = 0xabcd; \
|
mif_.driver = &mg_tcpip_driver_mcxw71; \
|
||||||
MG_SET_MAC_ADDRESS(mif_.mac); /* EUI-64 */ \
|
mif_.driver_data = &driver_data_; \
|
||||||
mg_tcpip_init(mgr, &mif_); \
|
MG_SET_MAC_ADDRESS(mif_.mac); \
|
||||||
(mgr)->use_dns6 = true; /* IPv6 only: no IPv4 over 6LoWPAN */ \
|
mg_tcpip_init(mgr, &mif_); \
|
||||||
MG_INFO(("Driver: mcxw71, 6LoWPAN")); \
|
MG_INFO(("Driver: mcxw71, channel %d", MG_L2_802154_CHANNEL)); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
+29
-50
@@ -73,8 +73,6 @@ enum {
|
|||||||
MGI_PROP_NCP_VERSION = 2,
|
MGI_PROP_NCP_VERSION = 2,
|
||||||
MGI_PROP_PHY_ENABLED = 0x20,
|
MGI_PROP_PHY_ENABLED = 0x20,
|
||||||
MGI_PROP_PHY_CHAN = 0x21,
|
MGI_PROP_PHY_CHAN = 0x21,
|
||||||
MGI_PROP_LADDR = 0x34,
|
|
||||||
MGI_PROP_SADDR = 0x35,
|
|
||||||
MGI_PROP_PANID = 0x36,
|
MGI_PROP_PANID = 0x36,
|
||||||
MGI_PROP_RAW_STREAM = 0x37,
|
MGI_PROP_RAW_STREAM = 0x37,
|
||||||
MGI_PROP_STREAM_RAW = 0x71
|
MGI_PROP_STREAM_RAW = 0x71
|
||||||
@@ -84,7 +82,7 @@ enum {
|
|||||||
#define MGI_STATUS_RESET_MAX 127
|
#define MGI_STATUS_RESET_MAX 127
|
||||||
#define MGI_TX_TIMEOUT_MS 200 // Wait for transmit done, incl. retries
|
#define MGI_TX_TIMEOUT_MS 200 // Wait for transmit done, incl. retries
|
||||||
#define MGI_RSP_TIMEOUT_MS 500
|
#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
|
// Driver state, one radio
|
||||||
struct mg_rw612 {
|
struct mg_rw612 {
|
||||||
@@ -103,9 +101,6 @@ struct mg_rw612 {
|
|||||||
uint8_t rxq[MGI_RXQ_CNT][127]; // Received frames, no FCS
|
uint8_t rxq[MGI_RXQ_CNT][127]; // Received frames, no FCS
|
||||||
uint8_t rxlen[MGI_RXQ_CNT]; // Their lengths
|
uint8_t rxlen[MGI_RXQ_CNT]; // Their lengths
|
||||||
uint8_t rxhead, rxtail; // Queue indexes
|
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;
|
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;
|
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
|
// Log an init error, return false
|
||||||
static bool mg_rw612_fail(const char *msg) {
|
static bool mg_rw612_fail(const char *msg) {
|
||||||
MG_ERROR(("%s", 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) {
|
static bool mg_rw612_154_init(struct mg_tcpip_if *ifp) {
|
||||||
struct mg_rw612 *s = &s_rw612;
|
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;
|
uint64_t expire;
|
||||||
if (ifp->l2type != MG_TCPIP_L2_LOWPAN || ifp->l2data.lowpan.channel < 11 ||
|
if (d == NULL || d->channel < 11 || d->channel > 26) {
|
||||||
ifp->l2data.lowpan.channel > 26) {
|
MG_ERROR(("Set driver_data, channel 11..26"));
|
||||||
MG_ERROR(("Use MG_TCPIP_L2_LOWPAN, channel 11..26"));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (MGI_MBOX->magic[0] != MGI_SYNC_MAGIC) {
|
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));
|
memset(s, 0, sizeof(*s));
|
||||||
s->free_bufs = 0xffff;
|
s->free_bufs = 0xffff;
|
||||||
s->pan_id = s->short_addr = 0xffff; // Force programming
|
|
||||||
// Reset our send FIFO, drop stale messages. See IMU_Init()
|
// Reset our send FIFO, drop stale messages. See IMU_Init()
|
||||||
MGI_IMU->CNTL |= MGI_IMU_FLUSH;
|
MGI_IMU->CNTL |= MGI_IMU_FLUSH;
|
||||||
MGI_IMU->CNTL &= ~(MGI_IMU_FLUSH | MGI_IMU_WAIT_FOR_ACK);
|
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)) {
|
if (!mg_rw612_set(MGI_PROP_PHY_ENABLED, &one, 1)) {
|
||||||
return mg_rw612_fail("PHY enable failed");
|
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)) {
|
if (!mg_rw612_set(MGI_PROP_RAW_STREAM, &one, 1)) {
|
||||||
return mg_rw612_fail("Raw stream enable failed");
|
return mg_rw612_fail("Raw stream enable failed");
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t mg_rw612_154_tx(const void *buf, size_t len,
|
// Send one radio frame: MAC header and payload, no FCS
|
||||||
struct mg_tcpip_if *ifp) {
|
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];
|
uint8_t f[3 + 2 + 127 + 4];
|
||||||
size_t n = 3;
|
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[1] = MGI_CMD_SET, f[2] = MGI_PROP_STREAM_RAW;
|
||||||
f[n++] = (uint8_t) (len + 2), f[n++] = 0; // PSDU length, incl. FCS
|
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++] = 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++] = 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
|
f[n++] = 1; // CSMA-CA enabled
|
||||||
if (!mg_rw612_request(f, n, MGI_TX_TIMEOUT_MS)) {
|
if (!mg_rw612_request(f, n, MGI_TX_TIMEOUT_MS)) {
|
||||||
MG_ERROR(("TX timeout"));
|
MG_ERROR(("TX timeout"));
|
||||||
@@ -446,18 +422,21 @@ static size_t mg_rw612_154_tx(const void *buf, size_t len,
|
|||||||
return 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) {
|
static size_t mg_rw612_154_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
||||||
struct mg_rw612 *s = &s_rw612;
|
struct mg_rw612 *s = &s_rw612;
|
||||||
size_t n = 0;
|
size_t res = 0;
|
||||||
mg_rw612_poll_imu();
|
mg_rw612_poll_imu();
|
||||||
mg_rw612_addrs(ifp);
|
while (s->rxtail != s->rxhead && res == 0) {
|
||||||
if (s->rxtail != s->rxhead) {
|
res = mg_l2_802154_rx(ifp, s->rxq[s->rxtail], s->rxlen[s->rxtail], buf,
|
||||||
n = s->rxlen[s->rxtail];
|
len);
|
||||||
if (n > len) n = len;
|
|
||||||
memcpy(buf, s->rxq[s->rxtail], n);
|
|
||||||
s->rxtail = (uint8_t) ((s->rxtail + 1) % MGI_RXQ_CNT);
|
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) {
|
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) && \
|
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RW612_154) && \
|
||||||
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
|
// NXP RW612 IEEE 802.15.4 radio, as an Ethernet interface: Ethernet frames
|
||||||
// combo firmware, an OpenThread radio co-processor (RCP). The firmware must
|
// are split into radio frames, see mg_l2_802154_tx(). The radio runs on
|
||||||
// be loaded and running before mg_tcpip_init(), see the FRDM-RW612 lowpan
|
// CPU2: NXP BLE/802.15.4 combo firmware, an OpenThread radio co-processor
|
||||||
// tutorial. The driver talks to it over the CPU2 <-> CPU3 IMU mailbox:
|
// (RCP). The firmware must be loaded and running before mg_tcpip_init(), see
|
||||||
// IMUMC messages carrying HDLC framed Spinel. Frames passed to tx() and
|
// the FRDM-RW612 eth_radio_bridge tutorial. The driver talks to it over the
|
||||||
// returned by rx() are raw 802.15.4 frames: MAC header and payload, no FCS.
|
// CPU2 <-> CPU3 IMU mailbox: IMUMC messages carrying HDLC framed Spinel.
|
||||||
// Use with MG_TCPIP_L2_LOWPAN: channel, PAN ID and addresses are taken from
|
// driver_data: struct mg_l2_802154_data, channel and PAN ID set
|
||||||
// ifp->l2data.lowpan and ifp->mac. No driver_data
|
|
||||||
|
|
||||||
#ifndef MG_TCPIP_DRIVER_INIT
|
#ifndef MG_TCPIP_DRIVER_INIT
|
||||||
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
||||||
do { \
|
do { \
|
||||||
static struct mg_tcpip_if mif_; \
|
static struct mg_l2_802154_data driver_data_; \
|
||||||
mif_.l2type = MG_TCPIP_L2_LOWPAN; \
|
static struct mg_tcpip_if mif_; \
|
||||||
mif_.driver = &mg_tcpip_driver_rw612_154; \
|
driver_data_.channel = MG_L2_802154_CHANNEL; \
|
||||||
mif_.l2data.lowpan.channel = 15; \
|
driver_data_.pan_id = MG_L2_802154_PAN_ID; \
|
||||||
mif_.l2data.lowpan.pan_id = 0xabcd; \
|
mif_.driver = &mg_tcpip_driver_rw612_154; \
|
||||||
MG_SET_MAC_ADDRESS(mif_.mac); /* EUI-64 */ \
|
mif_.driver_data = &driver_data_; \
|
||||||
mg_tcpip_init(mgr, &mif_); \
|
MG_SET_MAC_ADDRESS(mif_.mac); \
|
||||||
(mgr)->use_dns6 = true; /* IPv6 only: no IPv4 over 6LoWPAN */ \
|
mg_tcpip_init(mgr, &mif_); \
|
||||||
MG_INFO(("Driver: rw612_154, 6LoWPAN")); \
|
MG_INFO(("Driver: rw612_154, channel %d", MG_L2_802154_CHANNEL)); \
|
||||||
} while (0)
|
} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -365,7 +365,7 @@ static bool st67w6_init(uint8_t *mac) {
|
|||||||
}
|
}
|
||||||
// set clock, who cares ???
|
// set clock, who cares ???
|
||||||
if (!st67w6_at_cmd("AT+GET_CLOCK\r\n", 14)) return false;
|
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
|
// BT-ENABLED DEPENDENCY
|
||||||
|
|
||||||
// MODULE DEPENDENCY
|
// MODULE DEPENDENCY
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#if MG_ENABLE_TCPIP && (MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN || \
|
#if MG_ENABLE_TCPIP && (MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN || \
|
||||||
MG_ENABLE_DRIVER_STM32N)
|
MG_ENABLE_DRIVER_STM32N)
|
||||||
|
// STM32C: vendor modded single-queue Synopsys v5.4, same settings as STM32H
|
||||||
// STM32H: vendor modded single-queue Synopsys v4.2
|
// STM32H: vendor modded single-queue Synopsys v4.2
|
||||||
// STM32N: dual-queue GbE Synopsys v5.2 with no hash table option, 64-bit AXI
|
// 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
|
// 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
|
#if MG_ENABLE_DRIVER_MCXN
|
||||||
void ETHERNET_IRQHandler(void);
|
void ETHERNET_IRQHandler(void);
|
||||||
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);
|
||||||
void ETH_IRQHandler(void) {
|
void ETH_IRQHandler(void) {
|
||||||
#else
|
#else
|
||||||
@@ -339,8 +340,10 @@ void ETH1_IRQHandler(void) {
|
|||||||
}
|
}
|
||||||
ETH->DMACSR =
|
ETH->DMACSR =
|
||||||
MG_BIT(7) | MG_BIT(8); // Clear possible RBU RPS while processing
|
MG_BIT(7) | MG_BIT(8); // Clear possible RBU RPS while processing
|
||||||
ETH->DMACRDTPR =
|
// Resume RX: tail is the last descriptor given back to DMA. It must differ
|
||||||
(uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT - 1]; // and resume RX
|
// 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 = {
|
struct mg_tcpip_driver mg_tcpip_driver_stm32h = {
|
||||||
|
|||||||
+11
-2
@@ -4,6 +4,13 @@
|
|||||||
#if !defined(MG_ENABLE_DRIVER_STM32H)
|
#if !defined(MG_ENABLE_DRIVER_STM32H)
|
||||||
#define MG_ENABLE_DRIVER_STM32H 0
|
#define MG_ENABLE_DRIVER_STM32H 0
|
||||||
#endif
|
#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)
|
#if !defined(MG_ENABLE_DRIVER_MCXN)
|
||||||
#define MG_ENABLE_DRIVER_MCXN 0
|
#define MG_ENABLE_DRIVER_MCXN 0
|
||||||
#endif
|
#endif
|
||||||
@@ -43,9 +50,11 @@ struct mg_tcpip_driver_stm32h_data {
|
|||||||
#define MG_DRIVER_MDC_CR 4
|
#define MG_DRIVER_MDC_CR 4
|
||||||
#endif
|
#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)
|
#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)
|
#define MG_ENABLE_ETH_IRQ() NVIC_EnableIRQ(ETH1_IRQn)
|
||||||
#elif !defined(MG_ENABLE_ETH_IRQ)
|
#elif !defined(MG_ENABLE_ETH_IRQ)
|
||||||
#define MG_ENABLE_ETH_IRQ()
|
#define MG_ENABLE_ETH_IRQ()
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
struct mg_health mg_health_record MG_HEALTH_RAM;
|
struct mg_health mg_health_record MG_HEALTH_RAM;
|
||||||
|
|
||||||
// STM32 H723 support. Needs the _estack symbol from the linker script
|
// 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
|
// Fault handler body. Runs in exception context: no printf, no malloc, no
|
||||||
// blocking calls. Copies the unwind registers and raw stack into the health
|
// blocking calls. Copies the unwind registers and raw stack into the health
|
||||||
// record, then resets.
|
// record, then resets.
|
||||||
|
|||||||
+2
-2
@@ -9,11 +9,11 @@
|
|||||||
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
|
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
|
||||||
#endif
|
#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")))
|
#define MG_HEALTH_RAM __attribute__((section(".mg_health")))
|
||||||
#endif
|
#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
|
#define MG_HEALTH_DATA_SIZE 250
|
||||||
#endif
|
#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
|
fs->rm(path); // MG_FS_WRITE appends, not truncates: drop any old file
|
||||||
if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) {
|
if ((fd = mg_fs_open(fs, path, MG_FS_WRITE)) == NULL) {
|
||||||
MG_ERROR(("Cannot open %s for upload", path));
|
MG_ERROR(("Cannot open %s for upload", path));
|
||||||
} else if (!mg_http_stream_body(c, hm, serve_upload_cb, fd)) {
|
} else {
|
||||||
mg_fs_close(fd);
|
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
|
size_t received; // Number of body bytes received so far
|
||||||
void *user_data; // Passed to cb, free for cb to use
|
void *user_data; // Passed to cb, free for cb to use
|
||||||
bool failed; // Set when cb returned false
|
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
|
// 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.
|
// handler: the connection is closed after the reply.
|
||||||
static void mg_stream_handler(struct mg_connection *c, int ev, void *ev_data) {
|
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;
|
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 **))
|
(bool (*)(struct mg_http_message *, struct mg_str *, void **))
|
||||||
(ptrdiff_t) c->fn_data;
|
(ptrdiff_t) c->fn_data;
|
||||||
(void) ev_data;
|
(void) ev_data;
|
||||||
if (ev == MG_EV_CLOSE || ev == MG_EV_ERROR) {
|
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()
|
// 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);
|
fn(NULL, NULL, &us->user_data);
|
||||||
}
|
}
|
||||||
} else if (ev == MG_EV_READ && !c->is_draining) {
|
} else if (ev == MG_EV_READ && !c->is_draining && !us->finished) {
|
||||||
if (c->recv.len > 0 && us->received < us->expected) {
|
if (!us->failed && c->recv.len > 0 && us->received < us->expected) {
|
||||||
// Got some uploaded data. Feed by 512-byte aligned chunks, for OTA
|
// 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 left = us->expected - us->received, r = c->recv.len;
|
||||||
size_t aligned = r < left ? MG_ROUND_DOWN(r, 512) : left;
|
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;
|
us->received += aligned;
|
||||||
mg_iobuf_del(&c->recv, 0, aligned); // Shift the unprocessed tail
|
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
|
// Finished upload. Always call cb, even after error, so it can clean up
|
||||||
|
us->finished = true;
|
||||||
us->failed |= !fn(NULL, NULL, &us->user_data);
|
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",
|
mg_http_reply(c, us->failed ? 500 : 200, NULL, "%zu %s\n",
|
||||||
us->received, us->failed ? "fail" : "ok");
|
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 *,
|
bool (*cb)(struct mg_http_message *, struct mg_str *,
|
||||||
void **user_data),
|
void **),
|
||||||
void *user_data) {
|
void *user_data) {
|
||||||
struct mg_upload_state *us = (struct mg_upload_state *) c->data;
|
struct mg_upload_state *us = (struct mg_upload_state *) c->data;
|
||||||
struct mg_str *te = mg_http_get_header(hm, "Transfer-Encoding");
|
int code = 0;
|
||||||
bool handled = false;
|
size_t head_len = hm->head.len;
|
||||||
|
|
||||||
// Catch upload requests early, without buffering whole body. Called on the
|
// 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
|
// MG_EV_HTTP_HDRS event: we've received all HTTP headers but not necessarily
|
||||||
// the full HTTP body
|
// the full HTTP body
|
||||||
if (hm->body.len == (size_t) ~0 ||
|
if (!c->is_client && mg_strcmp(hm->method, mg_str("POST")) != 0 &&
|
||||||
(te != NULL && mg_strcasecmp(*te, mg_str("chunked")) == 0)) {
|
mg_strcmp(hm->method, mg_str("PUT")) != 0) {
|
||||||
// Unknown length or chunked, cannot stream. Leave it to the caller
|
code = 405;
|
||||||
} else if (!c->is_client &&
|
} else if (mg_http_get_header(hm, "Transfer-Encoding") != NULL) {
|
||||||
mg_strcmp(hm->method, mg_str("POST")) != 0 &&
|
code = 400;
|
||||||
mg_strcmp(hm->method, mg_str("PUT")) != 0) {
|
} else if (hm->body.len == (size_t) ~0 ||
|
||||||
// Not an upload request. Leave it to the caller
|
(!c->is_client && mg_http_get_header(hm, "Content-Length") == NULL)) {
|
||||||
|
code = 411;
|
||||||
} else if (sizeof(*us) > sizeof(c->data)) {
|
} else if (sizeof(*us) > sizeof(c->data)) {
|
||||||
if (!c->is_client) { // Nobody to reply to on a client connection
|
code = 500;
|
||||||
c->pfn = NULL;
|
}
|
||||||
mg_http_reply(c, 500, NULL, "data too small\n");
|
c->pfn = NULL; // Take ownership even when validation fails
|
||||||
c->is_draining = 1;
|
if (code != 0) {
|
||||||
cb(NULL, NULL, &user_data); // Let cb release user_data, start skipped
|
c->fn = NULL;
|
||||||
handled = true;
|
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 {
|
} else {
|
||||||
c->pfn = NULL; // Silence HTTP protocol handler
|
|
||||||
memset(us, 0, sizeof(*us));
|
memset(us, 0, sizeof(*us));
|
||||||
us->expected = hm->body.len; // Store number of bytes we expect
|
us->expected = hm->body.len; // Store number of bytes we expect
|
||||||
us->user_data = user_data;
|
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
|
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_http_reply(c, 100, NULL, ""); // If curl expects, we continue
|
||||||
}
|
}
|
||||||
mg_iobuf_del(&c->recv, 0, hm->head.len); // Delete HTTP headers
|
mg_iobuf_del(&c->recv, 0, head_len); // Delete HTTP headers
|
||||||
c->fn = mg_stream_handler; // We own the connection from now
|
|
||||||
c->fn_data = (void *) (ptrdiff_t) cb;
|
|
||||||
mg_call(c, MG_EV_READ, &c->recv.len); // Process the body bytes we have
|
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) {
|
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.
|
// 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.
|
// 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
|
// with mg_match(). Always takes over the connection. Invalid requests receive
|
||||||
// request is not POST/PUT, is chunked, or has no Content-Length.
|
// 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:
|
// Callback upload_fn(hm, data, user_data) is called:
|
||||||
// - at start: hm is set. Open the file, set *user_data.
|
// - at start: hm is set. Open the file, set *user_data.
|
||||||
// - per chunk: data is set, 512 byte aligned except the last. Not called
|
// - per chunk: data is set, 512 byte aligned except the last. Not called
|
||||||
// again after a failure.
|
// again after a failure.
|
||||||
// - at end: both NULL. Always called once, also when the connection drops
|
// - at end: both NULL. Always called exactly once, including validation
|
||||||
// early, so release resources here. A truncated body looks like a complete
|
// failures before start and connections that drop early. Release resources
|
||||||
// one: validate before committing.
|
// here. A truncated body looks like a complete one: validate before
|
||||||
// Return true on success, false on error.
|
// committing.
|
||||||
|
// The callback returns true on success, false on error. A failure stops
|
||||||
|
// streaming immediately and calls the end callback before closing.
|
||||||
// Example:
|
// Example:
|
||||||
// if (ev == MG_EV_HTTP_HDRS && mg_match(hm->uri, mg_str("/fs/*"), NULL) {
|
// if (ev == MG_EV_HTTP_HDRS && mg_match(hm->uri, mg_str("/fs/*"), NULL)) {
|
||||||
// mg_http_stream_body(c, hm, upload, NULL));
|
// mg_http_stream_body(c, hm, upload, NULL);
|
||||||
// }
|
// }
|
||||||
// Full example:
|
// Full example:
|
||||||
// tutorials/http/file-upload-single-post
|
// tutorials/http/file-upload-single-post
|
||||||
// Related APIs:
|
// Related APIs:
|
||||||
// mg_http_listen(), mg_http_next_multipart()
|
// mg_http_listen(), mg_http_next_multipart()
|
||||||
// Notes:
|
// Notes:
|
||||||
// After it returns true, this function hijacks the connection. Existing event
|
// The existing event handler is no longer called. hm is invalid after this
|
||||||
// handler not called, hm is invalid, c->data and c->fn_data are modified
|
// call; c->data and c->fn_data are reserved for streaming state.
|
||||||
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 (*upload_fn)(struct mg_http_message *,
|
bool (*upload_fn)(struct mg_http_message *,
|
||||||
struct mg_str *, void **user_data),
|
struct mg_str *, void **user_data),
|
||||||
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 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 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 void (*l2_init_fn)(struct mg_tcpip_if *);
|
||||||
typedef bool (*l2_poll_fn)(struct mg_tcpip_if *, bool);
|
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 *));
|
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
|
// clang-format on
|
||||||
|
|
||||||
static const l2_init_fn l2_init[] = {mg_l2_eth_init, mg_l2_ppp_init,
|
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,
|
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,
|
static const l2_header_fn l2_header[] = {mg_l2_eth_header, mg_l2_ppp_header,
|
||||||
mg_l2_pppoe_header,
|
mg_l2_pppoe_header};
|
||||||
mg_l2_lowpan_header};
|
|
||||||
static const l2_trailer_fn l2_trailer[] = {mg_l2_eth_trailer, mg_l2_ppp_trailer,
|
static const l2_trailer_fn l2_trailer[] = {mg_l2_eth_trailer, mg_l2_ppp_trailer,
|
||||||
mg_l2_pppoe_trailer, mg_l2_lowpan_trailer};
|
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_rx_fn l2_rx[] = {mg_l2_eth_rx, mg_l2_ppp_rx, mg_l2_pppoe_rx};
|
||||||
mg_l2_lowpan_rx};
|
|
||||||
static const l2_getaddr_fn l2_getaddr[] = {mg_l2_eth_getaddr, mg_l2_ppp_getaddr,
|
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,
|
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
|
#if MG_ENABLE_IPV6
|
||||||
static const l2_genip6_fn l2_genip6[] = {mg_l2_eth_genip6, mg_l2_ppp_genip6,
|
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,
|
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,
|
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
|
#endif
|
||||||
|
|
||||||
void mg_l2_init(struct mg_tcpip_if *ifp) {
|
void mg_l2_init(struct mg_tcpip_if *ifp) {
|
||||||
|
|||||||
@@ -6,13 +6,7 @@
|
|||||||
#if MG_ENABLE_TCPIP
|
#if MG_ENABLE_TCPIP
|
||||||
|
|
||||||
// no config defaults to 0 => Ethernet
|
// no config defaults to 0 => Ethernet
|
||||||
enum mg_l2type {
|
enum mg_l2type { MG_TCPIP_L2_ETH = 0, MG_TCPIP_L2_PPP, MG_TCPIP_L2_PPPoE, MG_TCPIP_L2_USER };
|
||||||
MG_TCPIP_L2_ETH = 0,
|
|
||||||
MG_TCPIP_L2_PPP,
|
|
||||||
MG_TCPIP_L2_PPPoE,
|
|
||||||
MG_TCPIP_L2_LOWPAN,
|
|
||||||
MG_TCPIP_L2_USER
|
|
||||||
};
|
|
||||||
|
|
||||||
#if defined(__DCC__)
|
#if defined(__DCC__)
|
||||||
#pragma pack(1)
|
#pragma pack(1)
|
||||||
@@ -35,27 +29,10 @@ struct eth_data {
|
|||||||
struct pppoe_data { // (struct eth_data *) &pppoe_data = pppoedata.eth
|
struct pppoe_data { // (struct eth_data *) &pppoe_data = pppoedata.eth
|
||||||
struct eth_data 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 {
|
union mg_l2data {
|
||||||
struct eth_data eth;
|
struct eth_data eth;
|
||||||
struct pppoe_data pppoe;
|
struct pppoe_data pppoe;
|
||||||
struct lowpan_data lowpan;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(__DCC__)
|
#if defined(__DCC__)
|
||||||
@@ -64,6 +41,7 @@ union mg_l2data {
|
|||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
enum mg_l2proto {
|
enum mg_l2proto {
|
||||||
MG_TCPIP_L2PROTO_IPV4 = 0,
|
MG_TCPIP_L2PROTO_IPV4 = 0,
|
||||||
MG_TCPIP_L2PROTO_IPV6,
|
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') {
|
if (url == NULL || url[0] == '\0') {
|
||||||
MG_ERROR(("null url"));
|
MG_ERROR(("null url"));
|
||||||
#if MG_ENABLE_TCPIP
|
#if MG_ENABLE_TCPIP
|
||||||
} else if (mgr->ifp != NULL && mgr->ifp->state != MG_TCPIP_STATE_READY
|
} else if (mgr->ifp != NULL && mgr->ifp->state != MG_TCPIP_STATE_READY) {
|
||||||
#if MG_ENABLE_IPV6
|
|
||||||
// IPv6-only links: a link-local address is enough for local peers
|
|
||||||
&& (mgr->ifp->state6 < MG_TCPIP_STATE_UP ||
|
|
||||||
mgr->ifp->ip6ll[0] == 0)
|
|
||||||
#endif
|
|
||||||
) {
|
|
||||||
MG_ERROR(("Network is down"));
|
MG_ERROR(("Network is down"));
|
||||||
#endif
|
#endif
|
||||||
} else if ((c = mg_alloc_conn(mgr)) == NULL) {
|
} else if ((c = mg_alloc_conn(mgr)) == NULL) {
|
||||||
|
|||||||
+19
-262
@@ -1,7 +1,6 @@
|
|||||||
#include "net_builtin.h"
|
#include "net_builtin.h"
|
||||||
#include "bsd.h"
|
#include "bsd.h"
|
||||||
#include "profile.h"
|
#include "profile.h"
|
||||||
#include "url.h"
|
|
||||||
|
|
||||||
#if MG_ENABLE_TCPIP
|
#if MG_ENABLE_TCPIP
|
||||||
#define MG_EPHEMERAL_PORT_BASE 32768
|
#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),
|
(struct mg_addr *) &ip6_allrouters),
|
||||||
ifp->ip6ll, (uint64_t *) ip6_allrouters.addr.ip6, 133, 0, payload,
|
ifp->ip6ll, (uint64_t *) ip6_allrouters.addr.ip6, 133, 0, payload,
|
||||||
payload_len);
|
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) {
|
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);
|
uint8_t *opts = (uint8_t *) (ra + 1);
|
||||||
size_t opt_left = pkt->pay.len - sizeof(*ra);
|
size_t opt_left = pkt->pay.len - sizeof(*ra);
|
||||||
bool gotl2addr = false, gotprefix = false, changed = false;
|
bool gotl2addr = false, gotprefix = false, changed = false;
|
||||||
bool ip6only = ifp->ip == 0 && !ifp->enable_dhcp_client;
|
|
||||||
uint8_t l2[sizeof(struct mg_l2addr)];
|
uint8_t l2[sizeof(struct mg_l2addr)];
|
||||||
uint32_t mtu = 0;
|
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 (pkt->pay.len < sizeof(*ra)) return;
|
||||||
if (ifp->state6 == MG_TCPIP_STATE_UP) {
|
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 ||
|
if (prefix_len != ifp->prefix_len ||
|
||||||
!match_prefix(prefix, ifp->prefix, ifp->prefix_len))
|
!match_prefix(prefix, ifp->prefix, ifp->prefix_len))
|
||||||
changed = true;
|
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;
|
opts += length;
|
||||||
opt_left -= 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
|
// fill prefix and global
|
||||||
if (gotprefix && !fill_global(ifp, prefix, prefix_len)) return;
|
if (gotprefix && !fill_global(ifp, prefix, prefix_len)) return;
|
||||||
if (changed) ifp->is_ip6_changed = true;
|
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) {
|
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))
|
if (!icmp6csum_ok(pkt->ip6, pkt->icmp6, sizeof(*pkt->icmp6) + pkt->pay.len))
|
||||||
return;
|
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);
|
tx_icmp6(ifp, l2addr, target, ips.addr.ip6, 129, 0, pkt->pay.buf, plen);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case 133: // Router Solicitation: answer if we route
|
|
||||||
if (ifp->route != NULL) tx_ndp_ra(ifp);
|
|
||||||
break;
|
|
||||||
case 134: // Router Advertisement
|
case 134: // Router Advertisement
|
||||||
rx_ndp_ra(ifp, pkt);
|
rx_ndp_ra(ifp, pkt);
|
||||||
break;
|
break;
|
||||||
@@ -1577,9 +1360,9 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn,
|
|||||||
if (lsn->loc.is_ip6) {
|
if (lsn->loc.is_ip6) {
|
||||||
c->rem.addr.ip6[0] = pkt->ip6->src[0],
|
c->rem.addr.ip6[0] = pkt->ip6->src[0],
|
||||||
c->rem.addr.ip6[1] = pkt->ip6->src[1], c->rem.is_ip6 = true;
|
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] = c->mgr->ifp->ip6[0],
|
||||||
c->loc.addr.ip6[0] = pkt->ip6->dst[0],
|
c->loc.addr.ip6[1] = c->mgr->ifp->ip6[1], c->loc.is_ip6 = true;
|
||||||
c->loc.addr.ip6[1] = pkt->ip6->dst[1], c->loc.is_ip6 = true;
|
// TODO(): compare lsn to link-local, or rem as link-local: use ll instead
|
||||||
} else
|
} else
|
||||||
#endif
|
#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_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->sport), mg_print_ip4, &pkt->ip->dst,
|
||||||
mg_ntohs(pkt->udp->dport), (int) pkt->pay.len));
|
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) &&
|
if (ifp->enable_dhcp_client && pkt->udp->dport == mg_htons(68) &&
|
||||||
len >= offsetof(struct dhcp, options)) {
|
len >= offsetof(struct dhcp, options)) {
|
||||||
pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
|
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_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->sport), mg_print_ip4, &pkt->ip->dst,
|
||||||
mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len));
|
mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len));
|
||||||
#if MG_ENABLE_IPV6
|
|
||||||
if (nat64_in(ifp, pkt)) return;
|
|
||||||
#endif
|
|
||||||
rx_tcp(ifp, pkt);
|
rx_tcp(ifp, pkt);
|
||||||
} else {
|
} else {
|
||||||
MG_DEBUG(("Unknown IP proto %x", (int) pkt->ip->proto));
|
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
|
if ((pkt->ip6->ver >> 4) != 0x6) return; // Not IPv6
|
||||||
plen = mg_ntohs(pkt->ip6->plen);
|
plen = mg_ntohs(pkt->ip6->plen);
|
||||||
if (plen > (pkt->pay.len - sizeof(*pkt->ip6))) return; // malformed
|
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;
|
next = pkt->ip6->next;
|
||||||
nhdr = (uint8_t *) (pkt->ip6 + 1);
|
nhdr = (uint8_t *) (pkt->ip6 + 1);
|
||||||
while (loop) {
|
while (loop) {
|
||||||
@@ -2155,7 +1930,7 @@ static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
|||||||
case 43: // Routing 4.4
|
case 43: // Routing 4.4
|
||||||
case 60: // Destination Options 4.6
|
case 60: // Destination Options 4.6
|
||||||
case 51: // Authentication RFC-4302
|
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
|
if (((uint32_t) len + 2) > plen) return; // nhdr[0, 1]; malformed
|
||||||
next = nhdr[0];
|
next = nhdr[0];
|
||||||
hlen = (uint16_t) (8 * (nhdr[1] + 1));
|
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);
|
pkt->icmp6 = (struct icmp6 *) (pkt->pay.buf);
|
||||||
if (pkt->pay.len < sizeof(*pkt->icmp6)) return;
|
if (pkt->pay.len < sizeof(*pkt->icmp6)) return;
|
||||||
mkpay(pkt, pkt->icmp6 + 1);
|
mkpay(pkt, pkt->icmp6 + 1);
|
||||||
MG_VERBOSE(("ICMPv6 %M -> %M len %u", mg_print_ip6, &pkt->ip6->src,
|
MG_DEBUG(("ICMPv6 %M -> %M len %u", mg_print_ip6, &pkt->ip6->src,
|
||||||
mg_print_ip6, &pkt->ip6->dst, (int) pkt->pay.len));
|
mg_print_ip6, &pkt->ip6->dst, (int) pkt->pay.len));
|
||||||
rx_icmp6(ifp, pkt);
|
rx_icmp6(ifp, pkt);
|
||||||
} else if (next == 17) {
|
} else if (next == 17) {
|
||||||
pkt->udp = (struct udp *) (pkt->pay.buf);
|
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
|
if (len < sizeof(*pkt->udp) || len > pkt->pay.len) return; // malformed
|
||||||
pkt->pay.len = len; // strip excess data
|
pkt->pay.len = len; // strip excess data
|
||||||
mkpay(pkt, pkt->udp + 1);
|
mkpay(pkt, pkt->udp + 1);
|
||||||
MG_VERBOSE(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src,
|
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->sport), mg_print_ip6, &pkt->ip6->dst,
|
||||||
mg_ntohs(pkt->udp->dport), (int) pkt->pay.len));
|
mg_ntohs(pkt->udp->dport), (int) pkt->pay.len));
|
||||||
if (ifp->enable_dhcp6_client && pkt->udp->dport == mg_htons(546)) {
|
if (ifp->enable_dhcp6_client && pkt->udp->dport == mg_htons(546)) {
|
||||||
pkt->dhcp6 = (struct dhcp6 *) (pkt->udp + 1);
|
pkt->dhcp6 = (struct dhcp6 *) (pkt->udp + 1);
|
||||||
mkpay(pkt, pkt->dhcp6 + 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))
|
if (off < sizeof(*pkt->tcp) / 4 || pkt->pay.len < (uint16_t) (4 * off))
|
||||||
return;
|
return;
|
||||||
mkpay(pkt, (uint32_t *) pkt->tcp + off);
|
mkpay(pkt, (uint32_t *) pkt->tcp + off);
|
||||||
MG_VERBOSE(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src,
|
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->sport), mg_print_ip6, &pkt->ip6->dst,
|
||||||
mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len));
|
mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len));
|
||||||
rx_tcp(ifp, pkt);
|
rx_tcp(ifp, pkt);
|
||||||
} else {
|
} else {
|
||||||
MG_DEBUG(("Unknown IPv6 next hdr %x", (int) next));
|
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->state6 < MG_TCPIP_STATE_UP) return;
|
||||||
if (ifp->enable_slaac && s1 && ifp->state6 == MG_TCPIP_STATE_UP)
|
if (ifp->enable_slaac && s1 && ifp->state6 == MG_TCPIP_STATE_UP)
|
||||||
tx_ndp_rs(ifp);
|
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) {
|
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_drv = (ifp->state6 != MG_TCPIP_STATE_DOWN);
|
||||||
bool cur_l2 = (ifp->state6 >= MG_TCPIP_STATE_UP);
|
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
|
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 (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) &&
|
if (ifp->l2type == MG_TCPIP_L2_ETH && l2_up && ifp->ip6ll[0] == 0 &&
|
||||||
l2_up && ifp->ip6ll[0] == 0 &&
|
|
||||||
ifp->ip6ll[1] == 0) { // gen ll address
|
ifp->ip6ll[1] == 0) { // gen ll address
|
||||||
mg_l2_genip6(ifp->l2type, ifp->ip6ll, 64, ifp->mac);
|
mg_l2_genip6(ifp->l2type, ifp->ip6ll, 64, ifp->mac);
|
||||||
memcpy(ifp->ip6ll, px, 8); // RFC-2464 5
|
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;
|
ifp->mgr = mgr;
|
||||||
mgr->extraconnsize = sizeof(struct connstate);
|
mgr->extraconnsize = sizeof(struct connstate);
|
||||||
if (ifp->ip == 0 && ifp->l2type != MG_TCPIP_L2_PPP &&
|
if (ifp->ip == 0 && ifp->l2type != MG_TCPIP_L2_PPP &&
|
||||||
ifp->l2type != MG_TCPIP_L2_PPPoE && ifp->l2type != MG_TCPIP_L2_LOWPAN)
|
ifp->l2type != MG_TCPIP_L2_PPPoE)
|
||||||
ifp->enable_dhcp_client = true; // No IPv4 over 6LoWPAN
|
ifp->enable_dhcp_client = true;
|
||||||
mg_random(&ifp->eport, sizeof(ifp->eport)); // Random from 0 to 65535
|
mg_random(&ifp->eport, sizeof(ifp->eport)); // Random from 0 to 65535
|
||||||
ifp->eport |= MG_EPHEMERAL_PORT_BASE; // Random from
|
ifp->eport |= MG_EPHEMERAL_PORT_BASE; // Random from
|
||||||
// MG_EPHEMERAL_PORT_BASE to 65535
|
// 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->recv_queue.buf);
|
||||||
mg_free(ifp->tx.buf);
|
mg_free(ifp->tx.buf);
|
||||||
mg_free(ifp->dns4_url);
|
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) {
|
static void send_syn(struct mg_connection *c) {
|
||||||
struct connstate *s = (struct connstate *) (c + 1);
|
struct connstate *s = (struct connstate *) (c + 1);
|
||||||
uint32_t isn = mg_htonl((uint32_t) mg_ntohs(c->loc.port));
|
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) {
|
bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
|
||||||
struct mg_tcpip_if *ifp = c->mgr->ifp;
|
struct mg_tcpip_if *ifp = c->mgr->ifp;
|
||||||
bool res = false;
|
bool res = false;
|
||||||
// While resolving, the address family is unknown yet: just queue the data
|
if (!c->loc.is_ip6 && (ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY)) {
|
||||||
if (!c->is_resolving && !c->loc.is_ip6 &&
|
|
||||||
(ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY)) {
|
|
||||||
proto_error(c, false, "net down");
|
proto_error(c, false, "net down");
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
} else if (!c->is_resolving && c->loc.is_ip6 &&
|
} else if (c->loc.is_ip6 && ifp->state6 != MG_TCPIP_STATE_READY) {
|
||||||
ifp->state6 != MG_TCPIP_STATE_READY) {
|
|
||||||
proto_error(c, false, "net down");
|
proto_error(c, false, "net down");
|
||||||
#endif
|
#endif
|
||||||
} else if (c->is_udp && (c->is_arplooking || c->is_resolving)) {
|
} else if (c->is_udp && (c->is_arplooking || c->is_resolving)) {
|
||||||
|
|||||||
+33
-36
@@ -44,24 +44,6 @@ enum {
|
|||||||
};
|
};
|
||||||
// clang-format on
|
// 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"
|
// Network interface. Zero-initialise, then set the fields marked "user-set"
|
||||||
// before calling mg_tcpip_init(). Fields in the "internal state" section
|
// 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.
|
// 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[8]; // Global prefix bytes
|
||||||
uint8_t prefix_len; // Global prefix length in bits
|
uint8_t prefix_len; // Global prefix length in bits
|
||||||
uint64_t gw6[2]; // IPv6 default gateway
|
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_slaac; // Enable SLAAC (stateless address autoconfiguration)
|
||||||
bool enable_dhcp6_client; // Enable DHCPv6 client (not yet fully implemented)
|
bool enable_dhcp6_client; // Enable DHCPv6 client (not yet fully implemented)
|
||||||
bool is_ip6_changed; // Set by stack when IPv6 address changes
|
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 gw6mac[sizeof(struct mg_l2addr)]; // IPv6 gateway hardware address
|
||||||
uint8_t state6; // Current IPv6 link/IP state (MG_TCPIP_STATE_*)
|
uint8_t state6; // Current IPv6 link/IP state (MG_TCPIP_STATE_*)
|
||||||
bool gw6_ready; // IPv6 gateway MAC address has been resolved
|
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
|
#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().
|
// Frees the TX buffer and RX queue allocated by mg_tcpip_init().
|
||||||
void mg_tcpip_free(struct mg_tcpip_if *);
|
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
|
// 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
|
// 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
|
// 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_mcxw71;
|
||||||
extern struct mg_tcpip_driver mg_tcpip_driver_rw612_154;
|
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).
|
// SPI bus abstraction for SPI-attached network chips (e.g. W5500).
|
||||||
// Populate and assign to the driver's driver_data field.
|
// Populate and assign to the driver's driver_data field.
|
||||||
struct mg_tcpip_spi {
|
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);
|
(unsigned long) hm->body.len, (unsigned long) s_ota->size);
|
||||||
} else {
|
} else {
|
||||||
MG_DEBUG(("Beginning OTA (%lu bytes)", (unsigned long) s_ota->size));
|
MG_DEBUG(("Beginning OTA (%lu bytes)", (unsigned long) s_ota->size));
|
||||||
if (!mg_http_stream_body(c, hm, s_firmware_cb, NULL)) {
|
mg_http_stream_body(c, hm, s_firmware_cb, NULL);
|
||||||
mg_error(c, "Cannot stream firmware");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (ev == MG_EV_ERROR) {
|
} else if (ev == MG_EV_ERROR) {
|
||||||
s_ota->fn((char *) ev_data);
|
s_ota->fn((char *) ev_data);
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
#define MG_OTA_STM32H7 2 // STM32 H7
|
#define MG_OTA_STM32H7 2 // STM32 H7
|
||||||
#define MG_OTA_STM32H7_DUAL_CORE 3 // STM32 H7 dual core
|
#define MG_OTA_STM32H7_DUAL_CORE 3 // STM32 H7 dual core
|
||||||
#define MG_OTA_STM32F 4 // STM32 F7/F4/F2
|
#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_CH32V307 100 // WCH CH32V307
|
||||||
#define MG_OTA_U2A 200 // Renesas U2A16, U2A8, U2A6
|
#define MG_OTA_U2A 200 // Renesas U2A16, U2A8, U2A6
|
||||||
#define MG_OTA_RT1020 300 // IMXRT1020
|
#define MG_OTA_RT1020 300 // IMXRT1020
|
||||||
@@ -79,9 +80,8 @@ bool mg_ota_end(void);
|
|||||||
// ```c
|
// ```c
|
||||||
// if (ev == MG_EV_HTTP_HDRS) {
|
// if (ev == MG_EV_HTTP_HDRS) {
|
||||||
// struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
// struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
// if (mg_match(hm->uri, mg_str("/ota"), NULL) &&
|
// if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||||
// !mg_http_stream_body(c, hm, mg_http_ota_cb, NULL)) {
|
// mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||||
// mg_http_reply(c, 400, "", "POST firmware with Content-Length\n");
|
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
|
|||||||
@@ -172,8 +172,6 @@ bool mg_ota_end(void) {
|
|||||||
ok = mg_ota_flash_end(&s_mg_flash_mcxw71);
|
ok = mg_ota_flash_end(&s_mg_flash_mcxw71);
|
||||||
s_mcxw71_size = s_mcxw71_pending = 0;
|
s_mcxw71_size = s_mcxw71_pending = 0;
|
||||||
if (ok) {
|
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_INFO(("Applying %lu bytes from staging flash", (unsigned long) size));
|
||||||
mg_mcxw71_apply(size);
|
mg_mcxw71_apply(size);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "ota.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_write(void *, const void *, size_t);
|
||||||
static bool mg_stm32h5_swap(void);
|
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 *);
|
uint8_t *p = va_arg(*ap, uint8_t *);
|
||||||
return print_mac(out, arg, p);
|
return print_mac(out, arg, p);
|
||||||
} break;
|
} break;
|
||||||
case MG_TCPIP_L2_LOWPAN: {
|
|
||||||
uint8_t *p = va_arg(*ap, uint8_t *);
|
|
||||||
return print_ieee64(out, arg, p);
|
|
||||||
} break;
|
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+3
-2
@@ -261,8 +261,9 @@ uint64_t mg_ntohll(uint64_t net) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void mg_delayms(unsigned int ms) {
|
void mg_delayms(unsigned int ms) {
|
||||||
uint64_t to = mg_millis() + ms + 1;
|
uint64_t now, to = mg_millis() + ms + 1;
|
||||||
while (mg_millis() < to) (void) 0;
|
while ((now = mg_millis()) < to)
|
||||||
|
if (now + ms + 1 < to) to = now + ms + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if MG_ENABLE_CUSTOM_CALLOC
|
#if MG_ENABLE_CUSTOM_CALLOC
|
||||||
|
|||||||
@@ -555,7 +555,6 @@ static void task2(void *args) {
|
|||||||
DASHBOARD("select_child_write");
|
DASHBOARD("select_child_write");
|
||||||
DASHBOARD("poll_child_write");
|
DASHBOARD("poll_child_write");
|
||||||
|
|
||||||
#if 0
|
|
||||||
s_error = false;
|
s_error = false;
|
||||||
s_backlog_ready = s_backlog_full = s_backlog_accept = false;
|
s_backlog_ready = s_backlog_full = s_backlog_accept = false;
|
||||||
ASSERT(xTaskCreate(backlog_task, "backlog", 512, NULL,
|
ASSERT(xTaskCreate(backlog_task, "backlog", 512, NULL,
|
||||||
@@ -570,12 +569,6 @@ static void task2(void *args) {
|
|||||||
while (!client_done(first, &status)) vTaskDelay(1);
|
while (!client_done(first, &status)) vTaskDelay(1);
|
||||||
check_client(status);
|
check_client(status);
|
||||||
DASHBOARD("listen_backlog");
|
DASHBOARD("listen_backlog");
|
||||||
#else
|
|
||||||
(void) first;
|
|
||||||
(void) second;
|
|
||||||
(void) status;
|
|
||||||
(void) backlog_task;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
s_error = false;
|
s_error = false;
|
||||||
// atask at higher priority than task1
|
// atask at higher priority than task1
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#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 1
|
||||||
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
|
#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) {
|
static void su(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_HTTP_HDRS) {
|
if (ev == MG_EV_HTTP_HDRS) {
|
||||||
mg_http_stream_body(c, (struct mg_http_message *) ev_data, su_cb, NULL);
|
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) {
|
if (ev == MG_EV_CONNECT) {
|
||||||
mg_printf(c, "GET / HTTP/1.0\r\n\r\n");
|
mg_printf(c, "GET / HTTP/1.0\r\n\r\n");
|
||||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||||
ASSERT(mg_http_stream_body(c, (struct mg_http_message *) ev_data, cr_cb,
|
mg_http_stream_body(c, (struct mg_http_message *) ev_data, cr_cb, NULL);
|
||||||
NULL));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3208,12 +3205,103 @@ static void cs(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
(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) {
|
static void test_http_upload(void) {
|
||||||
struct mg_mgr mgr;
|
struct mg_mgr mgr;
|
||||||
const char *url = "http://127.0.0.1:12352";
|
const char *url = "http://127.0.0.1:12352";
|
||||||
int i;
|
int i;
|
||||||
const char *s = "ok (8 foo\nbar\n)\n";
|
const char *s = "ok (8 foo\nbar\n)\n";
|
||||||
|
|
||||||
|
test_http_stream_errors();
|
||||||
mg_mgr_init(&mgr);
|
mg_mgr_init(&mgr);
|
||||||
mg_http_listen(&mgr, url, us, NULL);
|
mg_http_listen(&mgr, url, us, NULL);
|
||||||
mg_http_connect(&mgr, url, uc, (void *) &s);
|
mg_http_connect(&mgr, url, uc, (void *) &s);
|
||||||
@@ -3257,7 +3345,7 @@ static void test_http_upload(void) {
|
|||||||
ASSERT(mgr.conns == NULL);
|
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];
|
char buf[FETCH_BUF_SIZE];
|
||||||
s_su_done = s_su_end = 0;
|
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"
|
"POST /su_chunked.txt HTTP/1.1\r\n"
|
||||||
"Transfer-Encoding: chunked\r\n\r\n"
|
"Transfer-Encoding: chunked\r\n\r\n"
|
||||||
"3\r\nfoo\r\n0\r\n\r\n") == 400);
|
"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);
|
ASSERT(fopen("su_chunked.txt", "r") == NULL);
|
||||||
mg_mgr_free(&mgr);
|
mg_mgr_free(&mgr);
|
||||||
ASSERT(mgr.conns == NULL);
|
ASSERT(mgr.conns == NULL);
|
||||||
@@ -5010,6 +5098,20 @@ static void test_dash(void) {
|
|||||||
"Authorization: Basic YWRtaW46YWRtaW4=\n\n") == 200);
|
"Authorization: Basic YWRtaW46YWRtaW4=\n\n") == 200);
|
||||||
ASSERT(cmpbody(buf, login_admin_expected) == 0);
|
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
|
// set3 is admin-only
|
||||||
ASSERT(fetch(&mgr, buf, url,
|
ASSERT(fetch(&mgr, buf, url,
|
||||||
"GET /api/get/set3 HTTP/1.0\n"
|
"GET /api/get/set3 HTTP/1.0\n"
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
THISDIR = $(realpath $(CURDIR))
|
THISDIR = $(realpath $(CURDIR))
|
||||||
ROOTDIR = $(realpath $(CURDIR)/../../..)
|
ROOTDIR = $(realpath $(CURDIR)/../../..)
|
||||||
DOCKER ?= docker run --rm $(DA) -v $(ROOTDIR):$(ROOTDIR) -w $(THISDIR) espressif/idf
|
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
|
PORT ?= /dev/ttyUSB0
|
||||||
|
|
||||||
|
CFLAGS_EXTRA ?= -DMG_TLS=MG_TLS_BUILTIN
|
||||||
|
|
||||||
all: example
|
all: example
|
||||||
|
|
||||||
example:
|
example:
|
||||||
|
|||||||
@@ -12,3 +12,5 @@ idf_component_register(SRCS "main.c"
|
|||||||
espcoredump
|
espcoredump
|
||||||
nvs_flash
|
nvs_flash
|
||||||
app_update)
|
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),
|
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||||
.key = mg_str(TLS_KEY)};
|
.key = mg_str(TLS_KEY)};
|
||||||
mg_tls_init(c, &opts);
|
mg_tls_init(c, &opts);
|
||||||
|
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||||
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
|
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||||
|
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||||
|
}
|
||||||
} else if (ev == MG_EV_HTTP_MSG) {
|
} else if (ev == MG_EV_HTTP_MSG) {
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
size_t total = heap_caps_get_total_size(MALLOC_CAP_DEFAULT);
|
size_t total = heap_caps_get_total_size(MALLOC_CAP_DEFAULT);
|
||||||
|
|||||||
@@ -1,4 +1 @@
|
|||||||
#define MG_ARCH MG_ARCH_ESP32
|
#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) {
|
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;
|
(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) {
|
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;
|
(void) ev_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,24 +3,24 @@
|
|||||||
extern void mgmp_init(void);
|
extern void mgmp_init(void);
|
||||||
extern void mgmp_poll(int);
|
extern void mgmp_poll(int);
|
||||||
|
|
||||||
STATIC mp_obj_t mongoose_init(void) {
|
static mp_obj_t mongoose_init(void) {
|
||||||
mgmp_init();
|
mgmp_init();
|
||||||
return mp_const_none;
|
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));
|
mgmp_poll(mp_obj_get_int(t_obj));
|
||||||
return mp_const_none;
|
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___name__), MP_ROM_QSTR(MP_QSTR_mongoose) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&mongoose_init_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&mongoose_init_obj) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&mongoose_poll_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 = {
|
const mp_obj_module_t mongoose_module = {
|
||||||
.base = { &mp_type_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
|
// Reconnect if connection is closed, and send MQTT PINGs to keep
|
||||||
// the connection alive or to detect connection loss
|
// 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) {
|
if (s_mqtt_conn == NULL) {
|
||||||
struct mg_mqtt_opts opts = {
|
struct mg_mqtt_opts opts = {
|
||||||
.clean = true,
|
.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),
|
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||||
.key = mg_str(TLS_KEY)};
|
.key = mg_str(TLS_KEY)};
|
||||||
mg_tls_init(c, &opts);
|
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
|
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
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),
|
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||||
.key = mg_str(TLS_KEY)};
|
.key = mg_str(TLS_KEY)};
|
||||||
mg_tls_init(c, &opts);
|
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
|
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
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),
|
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||||
.key = mg_str(TLS_KEY)};
|
.key = mg_str(TLS_KEY)};
|
||||||
mg_tls_init(c, &opts);
|
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
|
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
// Copyright (c) 2026 Cesanta Software Limited
|
// Copyright (c) 2026 Cesanta Software Limited
|
||||||
// All rights reserved
|
// All rights reserved
|
||||||
//
|
//
|
||||||
// IPv6 over IEEE 802.15.4 on FRDM-MCXW71, Mongoose built-in TCP/IP stack.
|
// IPv4 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
|
// The radio carries Ethernet frames, channel 15, PAN ID 0xabcd. With the
|
||||||
// link, e.g. the FRDM-RW612 lowpan tutorial, gets a global address and is
|
// FRDM-RW612 eth_radio_bridge tutorial, gets an address from the LAN's DHCP
|
||||||
// reachable from the LAN. Serves pages on ports 80 and 443
|
// server and is reachable from the LAN. Serves pages on ports 80 and 443
|
||||||
|
|
||||||
#include "hal.h"
|
#include "hal.h"
|
||||||
#include "mongoose.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);
|
mg_tls_init(c, &opts);
|
||||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/ota"), NULL) &&
|
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||||
!mg_http_stream_body(c, hm, mg_http_ota_cb, NULL)) {
|
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||||
mg_http_reply(c, 400, "", "POST firmware with Content-Length\n");
|
|
||||||
c->is_draining = 1;
|
|
||||||
mg_iobuf_del(&c->recv, 0, c->recv.len);
|
|
||||||
}
|
}
|
||||||
} else if (ev == MG_EV_HTTP_MSG) {
|
} else if (ev == MG_EV_HTTP_MSG) {
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
@@ -81,8 +78,8 @@ int main(void) {
|
|||||||
|
|
||||||
struct mg_mgr mgr;
|
struct mg_mgr mgr;
|
||||||
mg_mgr_init(&mgr);
|
mg_mgr_init(&mgr);
|
||||||
mg_http_listen(&mgr, "http://[::]:80", http_ev_handler, NULL);
|
mg_http_listen(&mgr, "http://0.0.0.0:80", http_ev_handler, NULL);
|
||||||
mg_http_listen(&mgr, "https://[::]:443", http_ev_handler, NULL);
|
mg_http_listen(&mgr, "https://0.0.0.0:443", http_ev_handler, NULL);
|
||||||
mg_mqtt_init(&mgr);
|
mg_mqtt_init(&mgr);
|
||||||
|
|
||||||
MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000));
|
MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000));
|
||||||
|
|||||||
@@ -12,12 +12,12 @@
|
|||||||
#define MG_ENABLE_CUSTOM_RANDOM 1
|
#define MG_ENABLE_CUSTOM_RANDOM 1
|
||||||
#define MG_ENABLE_TCPIP_TCPRTX 1 // Link can be lossy, enable retransmit
|
#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
|
// Stable MAC, so the DHCP address stays the same across reboots: a hash of
|
||||||
// of the 128-bit chip unique ID, MSCM UID, locally administered
|
// the 128-bit chip unique ID, MSCM UID, locally administered
|
||||||
#define MG_SET_MAC_ADDRESS(mac) \
|
#define MG_SET_MAC_ADDRESS(mac) \
|
||||||
do { \
|
do { \
|
||||||
uint8_t b_[32]; \
|
uint8_t b_[32]; \
|
||||||
mg_sha256(b_, (uint8_t *) MSCM->UID, sizeof(MSCM->UID)); \
|
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)[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]; \
|
(mac)[4] = b_[4], (mac)[5] = b_[5]; \
|
||||||
} while (0)
|
} 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),
|
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||||
.key = mg_str(TLS_KEY)};
|
.key = mg_str(TLS_KEY)};
|
||||||
mg_tls_init(c, &opts);
|
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
|
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
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),
|
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||||
.key = mg_str(TLS_KEY)};
|
.key = mg_str(TLS_KEY)};
|
||||||
mg_tls_init(c, &opts);
|
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
|
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
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),
|
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||||
.key = mg_str(TLS_KEY)};
|
.key = mg_str(TLS_KEY)};
|
||||||
mg_tls_init(c, &opts);
|
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
|
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||||
|
|||||||
@@ -5,7 +5,12 @@
|
|||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
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;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
uint64_t tick = to_ms_since_boot(get_absolute_time());
|
uint64_t tick = to_ms_since_boot(get_absolute_time());
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
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) {
|
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;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
uint64_t tick = to_ms_since_boot(get_absolute_time());
|
uint64_t tick = to_ms_since_boot(get_absolute_time());
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
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) {
|
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;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
uint64_t tick = to_ms_since_boot(get_absolute_time());
|
uint64_t tick = to_ms_since_boot(get_absolute_time());
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
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),
|
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||||
.key = mg_str(TLS_KEY)};
|
.key = mg_str(TLS_KEY)};
|
||||||
mg_tls_init(c, &opts);
|
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
|
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
uint64_t tick = to_ms_since_boot(get_absolute_time());
|
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),
|
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||||
.key = mg_str(TLS_KEY)};
|
.key = mg_str(TLS_KEY)};
|
||||||
mg_tls_init(c, &opts);
|
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
|
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
uint64_t tick = to_ms_since_boot(get_absolute_time());
|
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),
|
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||||
.key = mg_str(TLS_KEY)};
|
.key = mg_str(TLS_KEY)};
|
||||||
mg_tls_init(c, &opts);
|
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
|
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
uint64_t tick = to_ms_since_boot(get_absolute_time());
|
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
|
// Copyright (c) 2026 Cesanta Software Limited
|
||||||
// All rights reserved
|
// All rights reserved
|
||||||
|
|
||||||
#include "mongoose.h"
|
|
||||||
#include "hal.h"
|
#include "hal.h"
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
#ifndef UART_DEBUG
|
#define UART_DEBUG USART2 // ST-LINK virtual COM port, UM3616 7.12
|
||||||
#define UART_DEBUG USART3
|
#define UART_DEBUG_TX_PIN PIN('A', 2)
|
||||||
#endif
|
#define UART_DEBUG_RX_PIN PIN('A', 3)
|
||||||
|
|
||||||
#define LED1 PIN(0, 12) // Green
|
#define LED1 PIN('A', 5) // Green, on when high. UM3616 7.8
|
||||||
#define LED2 PIN(0, 0) // Blue
|
#define LED2 PIN('G', 1) // Red, on when low
|
||||||
#define LED3 PIN(0, 1) // Red
|
#define LED3 PIN('G', 2) // Blue, on when low
|
||||||
|
|
||||||
static void log_fn(char ch, void *param) {
|
static void log_fn(char ch, void *param) {
|
||||||
hal_uart_write_buf(param, &ch, 1);
|
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 void blink_task(void) {
|
||||||
static uint64_t blink_timer = 0;
|
static uint64_t blink_timer = 0;
|
||||||
if (hal_timer_expired(&blink_timer, 100, hal_get_tick())) {
|
if (hal_timer_expired(&blink_timer, 500, hal_get_tick())) {
|
||||||
// hal_gpio_toggle(LED1);
|
hal_gpio_toggle(LED2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,7 +27,6 @@ uint64_t mg_millis(void) {
|
|||||||
return hal_get_tick();
|
return hal_get_tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if MG_ENABLE_CUSTOM_RANDOM
|
|
||||||
bool mg_random(void *buf, size_t len) {
|
bool mg_random(void *buf, size_t len) {
|
||||||
for (size_t n = 0; n < len; n += sizeof(uint32_t)) {
|
for (size_t n = 0; n < len; n += sizeof(uint32_t)) {
|
||||||
uint32_t r = hal_rng_read();
|
uint32_t r = hal_rng_read();
|
||||||
@@ -35,7 +34,6 @@ bool mg_random(void *buf, size_t len) {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Self-signed P-256 certificate and key for the HTTPS listener. Created by:
|
// Self-signed P-256 certificate and key for the HTTPS listener. Created by:
|
||||||
// openssl ecparam -genkey -name prime256v1 -noout -out key.pem
|
// 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),
|
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||||
.key = mg_str(TLS_KEY)};
|
.key = mg_str(TLS_KEY)};
|
||||||
mg_tls_init(c, &opts);
|
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;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||||
mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick());
|
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) {
|
int main(void) {
|
||||||
|
MG_HEALTH_INIT();
|
||||||
|
|
||||||
hal_clock_init();
|
hal_clock_init();
|
||||||
hal_rng_init();
|
hal_uart_init(UART_DEBUG, UART_DEBUG_TX_PIN, UART_DEBUG_RX_PIN, 115200);
|
||||||
hal_gpio_output(LED1);
|
|
||||||
hal_gpio_write(LED1, true);
|
|
||||||
hal_gpio_output(LED2);
|
|
||||||
hal_gpio_write(LED2, true);
|
|
||||||
hal_gpio_output(LED3);
|
|
||||||
hal_gpio_write(LED3, true);
|
|
||||||
hal_uart_init(UART_DEBUG, 115200);
|
|
||||||
mg_log_set_fn(log_fn, UART_DEBUG);
|
mg_log_set_fn(log_fn, UART_DEBUG);
|
||||||
|
hal_rng_init();
|
||||||
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_ethernet_init();
|
hal_ethernet_init();
|
||||||
mg_mgr_init(&mgr_eth);
|
hal_gpio_output(LED1);
|
||||||
mg_tcpip_init(&mgr_eth, ð);
|
hal_gpio_output(LED2);
|
||||||
radio.l2data.lowpan.channel = 15;
|
hal_gpio_output(LED3);
|
||||||
radio.l2data.lowpan.pan_id = 0xabcd;
|
|
||||||
memcpy(radio.ip6, RADIO_IP6, sizeof(radio.ip6)); // Static: we are router
|
|
||||||
memcpy(radio.prefix, RADIO_IP6, sizeof(radio.prefix));
|
|
||||||
radio.prefix_len = 64;
|
|
||||||
memcpy(radio.nat64, NAT64_PREFIX, sizeof(radio.nat64)); // We do NAT64
|
|
||||||
mg_mgr_init(&mgr_radio);
|
|
||||||
mg_tcpip_init(&mgr_radio, &radio);
|
|
||||||
mgr_radio.use_dns6 = true; // IPv6 only: no IPv4 over 6LoWPAN
|
|
||||||
|
|
||||||
// Route IPv6 between the two: radio nodes get addresses from RADIO_IP6/64,
|
struct mg_mgr mgr;
|
||||||
// the Ethernet LAN gets a route to that prefix via us
|
mg_mgr_init(&mgr);
|
||||||
mg_tcpip_route(ð, &radio);
|
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);
|
||||||
// Minimal web server on both interfaces
|
|
||||||
mg_http_listen(&mgr_eth, "http://0.0.0.0:80", http_ev_handler, NULL);
|
|
||||||
mg_http_listen(&mgr_eth, "https://0.0.0.0:443", http_ev_handler, NULL);
|
|
||||||
mg_http_listen(&mgr_radio, "http://[::]:80", http_ev_handler, NULL);
|
|
||||||
mg_http_listen(&mgr_radio, "https://[::]:443", http_ev_handler, NULL);
|
|
||||||
|
|
||||||
MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000));
|
MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000));
|
||||||
MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free()));
|
MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free()));
|
||||||
MG_INFO(("Crash record: %M", mg_print_crash_record));
|
MG_INFO(("Crash record: %M", mg_print_crash_record));
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
mg_mgr_poll(&mgr_eth, 0);
|
mg_mgr_poll(&mgr, 0);
|
||||||
mg_mgr_poll(&mgr_radio, 0);
|
|
||||||
blink_task();
|
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) {
|
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;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/debug"), NULL)) {
|
if (mg_match(hm->uri, mg_str("/api/debug"), NULL)) {
|
||||||
int level = mg_json_get_long(hm->body, "$.level", MG_LL_DEBUG);
|
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),
|
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||||
.key = mg_str(TLS_KEY)};
|
.key = mg_str(TLS_KEY)};
|
||||||
mg_tls_init(c, &opts);
|
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
|
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
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) {
|
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;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
(void) hm;
|
(void) hm;
|
||||||
mg_http_reply(c, 200, "", "hi\n");
|
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) {
|
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;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/debug"), NULL)) {
|
if (mg_match(hm->uri, mg_str("/api/debug"), NULL)) {
|
||||||
int level = mg_json_get_long(hm->body, "$.level", MG_LL_DEBUG);
|
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),
|
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||||
.key = mg_str(TLS_KEY)};
|
.key = mg_str(TLS_KEY)};
|
||||||
mg_tls_init(c, &opts);
|
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
|
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
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),
|
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||||
.key = mg_str(TLS_KEY)};
|
.key = mg_str(TLS_KEY)};
|
||||||
mg_tls_init(c, &opts);
|
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
|
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
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) {
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->fn_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;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/hello"),
|
if (mg_match(hm->uri, mg_str("/api/hello"),
|
||||||
NULL)) { // Request to /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),
|
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||||
.key = mg_str(TLS_KEY)};
|
.key = mg_str(TLS_KEY)};
|
||||||
mg_tls_init(c, &opts);
|
mg_tls_init(c, &opts);
|
||||||
|
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||||
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
|
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||||
|
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||||
|
}
|
||||||
} else if (ev == MG_EV_HTTP_MSG) {
|
} else if (ev == MG_EV_HTTP_MSG) {
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
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) {
|
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;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||||
mg_http_reply(c, 200, "", "{%m:%lu}\n", MG_ESC("tick"), HAL_GetTick());
|
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) {
|
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;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||||
mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick());
|
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) {
|
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;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||||
mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick());
|
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),
|
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||||
.key = mg_str(TLS_KEY)};
|
.key = mg_str(TLS_KEY)};
|
||||||
mg_tls_init(c, &opts);
|
mg_tls_init(c, &opts);
|
||||||
|
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||||
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
|
if (mg_match(hm->uri, mg_str("/ota"), NULL)) {
|
||||||
|
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
||||||
|
}
|
||||||
} else if (ev == MG_EV_HTTP_MSG) {
|
} else if (ev == MG_EV_HTTP_MSG) {
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
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),
|
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||||
.key = mg_str(TLS_KEY)};
|
.key = mg_str(TLS_KEY)};
|
||||||
mg_tls_init(c, &opts);
|
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
|
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
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),
|
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||||
.key = mg_str(TLS_KEY)};
|
.key = mg_str(TLS_KEY)};
|
||||||
mg_tls_init(c, &opts);
|
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
|
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
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),
|
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||||
.key = mg_str(TLS_KEY)};
|
.key = mg_str(TLS_KEY)};
|
||||||
mg_tls_init(c, &opts);
|
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
|
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
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),
|
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||||
.key = mg_str(TLS_KEY)};
|
.key = mg_str(TLS_KEY)};
|
||||||
mg_tls_init(c, &opts);
|
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
|
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
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),
|
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||||
.key = mg_str(TLS_KEY)};
|
.key = mg_str(TLS_KEY)};
|
||||||
mg_tls_init(c, &opts);
|
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
|
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
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),
|
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||||
.key = mg_str(TLS_KEY)};
|
.key = mg_str(TLS_KEY)};
|
||||||
mg_tls_init(c, &opts);
|
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
|
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
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) {
|
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;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_match(hm->uri, mg_str("/api/debug"), NULL)) {
|
if (mg_match(hm->uri, mg_str("/api/debug"), NULL)) {
|
||||||
int level = mg_json_get_long(hm->body, "$.level", MG_LL_DEBUG);
|
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
|
// https://mongoose.ws/documentation/#2-minute-integration-guide
|
||||||
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
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;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
mg_http_reply(c, 200, "", "ok %p\r\n", hm);
|
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