mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-31 13:00:49 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
361a70517e | ||
|
|
278c4ca116 |
No files matched your search
@@ -637,6 +637,7 @@ jobs:
|
|||||||
example:
|
example:
|
||||||
- path: micropython/esp32
|
- path: micropython/esp32
|
||||||
- path: esp32/uart-bridge
|
- path: esp32/uart-bridge
|
||||||
|
- path: esp32/minimal
|
||||||
name: ${{ matrix.example.path }}
|
name: ${{ matrix.example.path }}
|
||||||
env:
|
env:
|
||||||
GO: 0
|
GO: 0
|
||||||
|
|||||||
@@ -186,6 +186,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
example:
|
example:
|
||||||
- path: esp32/uart-bridge
|
- path: esp32/uart-bridge
|
||||||
|
- path: esp32/minimal
|
||||||
name: ${{ matrix.example.path }}
|
name: ${{ matrix.example.path }}
|
||||||
env:
|
env:
|
||||||
GO: 0
|
GO: 0
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
# Mongoose: a full-stack embedded networking library
|
# Mongoose - Embedded Network 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: a full-stack embedded networking library
|
# Mongoose - Embedded Web Server / Embedded Network 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,17 +7,11 @@
|
|||||||
|
|
||||||
<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;" />
|
||||||
Mongoose is a full-stack embedded networking library for C/C++: a TCP/IP stack
|
Two files. Drop `mongoose.c` and `mongoose.h` into any C/C++ project and get a
|
||||||
with drivers, TLS 1.3, an embedded web server (HTTP, WebSocket), MQTT and
|
full network stack — HTTP, WebSocket, MQTT, TLS, firmware OTA
|
||||||
firmware OTA updates in two files, `mongoose.c` and `mongoose.h`. Mongoose runs on
|
updates, device dashboard — on any microcontroller or desktop OS.
|
||||||
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.
|
|
||||||
|
|
||||||
Use it instead of lwIP + mbedTLS + httpd + an MQTT client + OTA glue code, or
|
On the market since 2004, used by vast number of open source and commercial
|
||||||
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:
|
||||||
@@ -30,7 +24,7 @@ products - it even runs on the International Space Station! Features include:
|
|||||||
- Source code is both ISO C and ISO C++ compliant
|
- 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 stack. Also can use external TLS libraries - mbedTLS, OpenSSL, or other
|
- Built-in TLS 1.3 ECC stack. Also can use external TLS libraries - mbedTLS, OpenSSL, or other
|
||||||
- Does not depend on any other software to implement networking
|
- 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/)
|
||||||
@@ -41,32 +35,8 @@ See https://mongoose.ws/ for complete documentation, videos, case studies, etc.
|
|||||||
|
|
||||||
## Supported platforms
|
## Supported platforms
|
||||||
|
|
||||||
Mongoose has its own built-in TCP/IP stack with drivers, so it needs no
|
Mongoose can work on top of any TCP/IP stack that supports BSD sockets API.
|
||||||
additional software to implement networking. The built-in stack runs on bare
|
Platforms supported by the 3rd party TCP/IP stacks:
|
||||||
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 |
|
||||||
| :-------------- | :------- |
|
| :-------------- | :------- |
|
||||||
@@ -75,6 +45,23 @@ sockets API. Platforms supported via 3rd party TCP/IP stacks:
|
|||||||
| **Other** | Any other TCP/IP stack that supports BSD socket API, for example Amazon FreeRTOS-TCP |
|
| **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
|
||||||
@@ -171,17 +158,6 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Licensing
|
|
||||||
|
|
||||||
Mongoose is dual-licensed:
|
|
||||||
- GPLv2: free for open source projects. Many popular open source projects use
|
|
||||||
Mongoose, for example
|
|
||||||
[SWUpdate](https://github.com/sbabic/swupdate/tree/master/mongoose), the
|
|
||||||
embedded Linux update framework
|
|
||||||
- Commercial: for closed-source products. Includes support directly from the
|
|
||||||
engineers who wrote the whole stack, and early security fixes.
|
|
||||||
See [licensing](https://mongoose.ws/licensing/)
|
|
||||||
|
|
||||||
## Commercial use
|
## 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
|
||||||
@@ -246,12 +222,12 @@ Technical guides and deep dives into embedded web servers, WebUI integration and
|
|||||||
- [lwIP vs Mongoose - TCP/IP Stack Integration Benchmark](https://mongoose.ws/articles/lwip-vs-mongoose-tcpip-stack-integration/)
|
- [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/rw612-mdash/)
|
- [Firmware OTA and remote control on NXP FRDM-RW612](https://mongoose.ws/articles/mdash/)
|
||||||
- [25000 WebSocket messages per second on STM32 Nucleo-H723ZG](https://mongoose.ws/articles/stm32-websocket-benchmark/)
|
- [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/)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+239
-311
@@ -167,7 +167,6 @@ fail:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
#if MG_ENABLE_BSD_SOCKETS && MG_ENABLE_FREERTOS
|
#if MG_ENABLE_BSD_SOCKETS && MG_ENABLE_FREERTOS
|
||||||
@@ -278,57 +277,6 @@ static struct mg_connection *bsd_new_conn(struct mg_bsd_sock *s) {
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mirror net_builtin.c::trim_len() logic
|
|
||||||
static bool bsd_udp_too_big(struct mg_connection *c, size_t len) {
|
|
||||||
struct mg_tcpip_if *ifp = c->mgr->ifp;
|
|
||||||
size_t hlen = c->rem.is_ip6 ? 48 : 32; // IP plus UDP headers
|
|
||||||
size_t cap = ifp->mtu - hlen;
|
|
||||||
size_t room = ifp->tx.len - (ifp->framesize - ifp->l2mtu) - hlen;
|
|
||||||
|
|
||||||
return len > cap || len > room;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool bsd_udp_send(struct mg_connection *c, uint32_t ip, uint16_t port,
|
|
||||||
const void *buf, size_t len) {
|
|
||||||
struct mg_bsd_udp_frame *h;
|
|
||||||
size_t off = c->send.len, total = sizeof(*h) + len;
|
|
||||||
|
|
||||||
if (mg_iobuf_add(&c->send, off, NULL, total) != total) return false;
|
|
||||||
h = (struct mg_bsd_udp_frame *) (c->send.buf + off);
|
|
||||||
h->ip = ip;
|
|
||||||
h->port = port;
|
|
||||||
h->len = (uint16_t) len;
|
|
||||||
memcpy(h + 1, buf, len);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t bsd_recv(struct mg_bsd_sock *s, void *buf, size_t len,
|
|
||||||
int flags, struct sockaddr *src, socklen_t *addrlen) {
|
|
||||||
struct mg_connection *c = s->c;
|
|
||||||
size_t n;
|
|
||||||
|
|
||||||
if (!s->is_udp) {
|
|
||||||
n = c->recv.len < len ? c->recv.len : len;
|
|
||||||
memcpy(buf, c->recv.buf, n);
|
|
||||||
if (!(flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, n);
|
|
||||||
} else {
|
|
||||||
struct mg_bsd_udp_frame *h = (struct mg_bsd_udp_frame *) c->recv.buf;
|
|
||||||
assert(c->recv.len >= sizeof(*h));
|
|
||||||
assert(h->len <= c->recv.len - sizeof(*h));
|
|
||||||
n = h->len < len ? h->len : len;
|
|
||||||
memcpy(buf, h + 1, n);
|
|
||||||
if (src != NULL && addrlen != NULL) {
|
|
||||||
struct sockaddr_in *sa = (struct sockaddr_in *) src;
|
|
||||||
sa->sin_family = AF_INET;
|
|
||||||
sa->sin_port = h->port;
|
|
||||||
sa->sin_addr.s_addr = h->ip;
|
|
||||||
*addrlen = sizeof(*sa);
|
|
||||||
}
|
|
||||||
if (!(flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, sizeof(*h) + h->len);
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MG_ENABLE_FREERTOS
|
#if MG_ENABLE_FREERTOS
|
||||||
// Complete operations blocked on c before Mongoose destroys it.
|
// Complete operations blocked on c before Mongoose destroys it.
|
||||||
static void bsd_finish_waiters(struct mg_bsd_sock *s, bool error) {
|
static void bsd_finish_waiters(struct mg_bsd_sock *s, bool error) {
|
||||||
@@ -509,9 +457,9 @@ static int bsd_ready(struct mg_bsd_sock *s) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (s->c->recv.len > 0 || s->c->is_draining) ready |= BSD_READ;
|
if (s->c->recv.len > 0 || s->c->is_draining) ready |= BSD_READ;
|
||||||
if ((s->is_udp || !s->c->is_connecting) && !s->c->is_closing &&
|
if (!s->is_udp && !s->c->is_connecting && !s->c->is_closing &&
|
||||||
!s->c->is_draining)
|
!s->c->is_draining)
|
||||||
ready |= BSD_WRITE; // UDP socket or established TCP socket
|
ready |= BSD_WRITE; // Established TCP socket
|
||||||
return ready;
|
return ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -525,7 +473,7 @@ static void bsd_close(struct mg_bsd_sock *s) {
|
|||||||
if (t->orphan && t->listener == s) {
|
if (t->orphan && t->listener == s) {
|
||||||
if (t->c != NULL) {
|
if (t->c != NULL) {
|
||||||
t->c->fn_data = NULL;
|
t->c->fn_data = NULL;
|
||||||
t->c->is_draining = 1;
|
mg_close_conn(t->c);
|
||||||
t->c = NULL;
|
t->c = NULL;
|
||||||
}
|
}
|
||||||
memset(t, 0, sizeof(*t)); // Release orphan
|
memset(t, 0, sizeof(*t)); // Release orphan
|
||||||
@@ -533,7 +481,7 @@ static void bsd_close(struct mg_bsd_sock *s) {
|
|||||||
}
|
}
|
||||||
if (s->c != NULL) {
|
if (s->c != NULL) {
|
||||||
s->c->fn_data = NULL;
|
s->c->fn_data = NULL;
|
||||||
s->c->is_draining = 1;
|
mg_close_conn(s->c);
|
||||||
s->c = NULL;
|
s->c = NULL;
|
||||||
}
|
}
|
||||||
memset(s, 0, sizeof(*s)); // Release socket
|
memset(s, 0, sizeof(*s)); // Release socket
|
||||||
@@ -712,8 +660,8 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
cmd->result = -1, cmd->err = ECONNREFUSED;
|
cmd->result = -1, cmd->err = ECONNREFUSED;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (s->is_udp || !c->is_connecting) {
|
if (!c->is_connecting) {
|
||||||
cmd->result = 0; // UDP may still resolve ARP; TCP waits for SYN/SYN-ACK
|
cmd->result = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (s->nonblock) {
|
if (s->nonblock) {
|
||||||
@@ -737,8 +685,11 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (c->recv.len > 0) {
|
if (c->recv.len > 0) {
|
||||||
cmd->result = (int) bsd_recv(s, cmd->op_recv.buf, cmd->op_recv.len,
|
size_t n = cmd->op_recv.len;
|
||||||
cmd->op_recv.flags, NULL, NULL);
|
if (c->recv.len < n) n = c->recv.len;
|
||||||
|
memcpy(cmd->op_recv.buf, c->recv.buf, n);
|
||||||
|
if (!(cmd->op_recv.flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, n);
|
||||||
|
cmd->result = (int) n;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (s->err != 0) {
|
if (s->err != 0) {
|
||||||
@@ -759,17 +710,10 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
case BSD_OP_SEND:
|
case BSD_OP_SEND:
|
||||||
if ((s = bsd_get(cmd->op_send.fd)) == NULL) {
|
if ((s = bsd_get(cmd->op_send.fd)) == NULL) {
|
||||||
cmd->result = -1, cmd->err = EBADF;
|
cmd->result = -1, cmd->err = EBADF;
|
||||||
} else if (s->err != 0 || s->closed ||
|
} else if ((c = s->c) == NULL || s->err != 0 || c->is_closing ||
|
||||||
((c = s->c) != NULL && (c->is_closing || c->is_draining))) {
|
c->is_draining) {
|
||||||
cmd->result = -1, cmd->err = EPIPE;
|
cmd->result = -1, cmd->err = EPIPE;
|
||||||
} else if (c == NULL || (s->is_udp && !c->is_client)) {
|
} else if (mg_send(c, cmd->op_send.buf, cmd->op_send.len)) {
|
||||||
cmd->result = -1, cmd->err = s->is_udp ? EDESTADDRREQ : EPIPE;
|
|
||||||
} else if (s->is_udp && bsd_udp_too_big(c, cmd->op_send.len)) {
|
|
||||||
cmd->result = -1, cmd->err = EMSGSIZE;
|
|
||||||
} else if ((s->is_udp ? bsd_udp_send(c, c->rem.addr.ip4, c->rem.port,
|
|
||||||
cmd->op_send.buf, cmd->op_send.len)
|
|
||||||
: mg_send(c, cmd->op_send.buf,
|
|
||||||
cmd->op_send.len))) {
|
|
||||||
cmd->result = (int) cmd->op_send.len;
|
cmd->result = (int) cmd->op_send.len;
|
||||||
} else if (s->nonblock || (cmd->op_send.flags & MSG_DONTWAIT)) {
|
} else if (s->nonblock || (cmd->op_send.flags & MSG_DONTWAIT)) {
|
||||||
cmd->result = -1, cmd->err = EAGAIN;
|
cmd->result = -1, cmd->err = EAGAIN;
|
||||||
@@ -784,20 +728,16 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
case BSD_OP_SENDTO:
|
case BSD_OP_SENDTO:
|
||||||
if ((s = bsd_get(cmd->op_sendto.fd)) == NULL) {
|
if ((s = bsd_get(cmd->op_sendto.fd)) == NULL) {
|
||||||
cmd->result = -1, cmd->err = EBADF;
|
cmd->result = -1, cmd->err = EBADF;
|
||||||
} else if (!s->is_udp) {
|
|
||||||
cmd->result = -1, cmd->err = EOPNOTSUPP;
|
|
||||||
} else if ((c = s->c) == NULL || s->err != 0 || c->is_closing ||
|
} else if ((c = s->c) == NULL || s->err != 0 || c->is_closing ||
|
||||||
c->is_draining) {
|
c->is_draining) {
|
||||||
cmd->result = -1, cmd->err = EPIPE;
|
cmd->result = -1, cmd->err = EPIPE;
|
||||||
} else if (c->is_client) {
|
|
||||||
cmd->result = -1, cmd->err = EISCONN;
|
|
||||||
} else {
|
} else {
|
||||||
const struct sockaddr_in *sa =
|
const struct sockaddr_in *sa =
|
||||||
(const struct sockaddr_in *) cmd->op_sendto.addr;
|
(const struct sockaddr_in *) cmd->op_sendto.addr;
|
||||||
if (bsd_udp_too_big(c, cmd->op_sendto.len)) {
|
c->rem.port = sa->sin_port;
|
||||||
cmd->result = -1, cmd->err = EMSGSIZE;
|
c->rem.addr.ip4 = sa->sin_addr.s_addr;
|
||||||
} else if (bsd_udp_send(c, sa->sin_addr.s_addr, sa->sin_port,
|
c->rem.is_ip6 = false;
|
||||||
cmd->op_sendto.buf, cmd->op_sendto.len)) {
|
if (mg_send(c, cmd->op_sendto.buf, cmd->op_sendto.len)) {
|
||||||
cmd->result = (int) cmd->op_sendto.len;
|
cmd->result = (int) cmd->op_sendto.len;
|
||||||
} else if (s->nonblock || (cmd->op_sendto.flags & MSG_DONTWAIT)) {
|
} else if (s->nonblock || (cmd->op_sendto.flags & MSG_DONTWAIT)) {
|
||||||
cmd->result = -1, cmd->err = EAGAIN;
|
cmd->result = -1, cmd->err = EAGAIN;
|
||||||
@@ -814,9 +754,6 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
if ((s = bsd_get(cmd->op_recvfrom.fd)) == NULL) {
|
if ((s = bsd_get(cmd->op_recvfrom.fd)) == NULL) {
|
||||||
cmd->result = -1, cmd->err = EBADF;
|
cmd->result = -1, cmd->err = EBADF;
|
||||||
return true;
|
return true;
|
||||||
} else if (!s->is_udp) {
|
|
||||||
cmd->result = -1, cmd->err = EOPNOTSUPP;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if ((c = s->c) == NULL) {
|
if ((c = s->c) == NULL) {
|
||||||
cmd->result = s->closed && s->err == 0 ? 0 : -1;
|
cmd->result = s->closed && s->err == 0 ? 0 : -1;
|
||||||
@@ -824,11 +761,18 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (c->recv.len > 0) {
|
if (c->recv.len > 0) {
|
||||||
cmd->result = (int) bsd_recv(s, cmd->op_recvfrom.buf,
|
size_t n = cmd->op_recvfrom.len;
|
||||||
cmd->op_recvfrom.len,
|
if (c->recv.len < n) n = c->recv.len;
|
||||||
cmd->op_recvfrom.flags,
|
memcpy(cmd->op_recvfrom.buf, c->recv.buf, n);
|
||||||
cmd->op_recvfrom.src,
|
if (!(cmd->op_recvfrom.flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, n);
|
||||||
cmd->op_recvfrom.addrlen);
|
if (cmd->op_recvfrom.src != NULL && cmd->op_recvfrom.addrlen != NULL) {
|
||||||
|
struct sockaddr_in *sa = (struct sockaddr_in *) cmd->op_recvfrom.src;
|
||||||
|
sa->sin_family = AF_INET;
|
||||||
|
sa->sin_port = c->rem.port;
|
||||||
|
sa->sin_addr.s_addr = c->rem.addr.ip4;
|
||||||
|
*cmd->op_recvfrom.addrlen = sizeof(*sa);
|
||||||
|
}
|
||||||
|
cmd->result = (int) n;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (s->err != 0) {
|
if (s->err != 0) {
|
||||||
@@ -1724,6 +1668,21 @@ static void mg_dash_handle_add(struct mg_connection *c, struct mg_dash *dash,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Firmware upload callback for mg_http_stream_body(). Uses *p as a flag:
|
||||||
|
// non-NULL means an OTA session is open and must be closed by mg_ota_end()
|
||||||
|
static bool otacb(struct mg_http_message *hm, struct mg_str *data, void **p) {
|
||||||
|
if (hm != NULL) { // Start
|
||||||
|
if (!mg_ota_begin(hm->body.len)) return false;
|
||||||
|
*p = (void *) (ptrdiff_t) 1;
|
||||||
|
return true;
|
||||||
|
} else if (data != NULL) { // Next chunk
|
||||||
|
return mg_ota_write(data->buf, data->len);
|
||||||
|
}
|
||||||
|
if (*p == NULL) return false; // End. Begin failed, nothing to finish
|
||||||
|
*p = NULL;
|
||||||
|
return mg_ota_end();
|
||||||
|
}
|
||||||
|
|
||||||
static void mg_serve_fs(struct mg_connection *c, struct mg_http_message *hm,
|
static void mg_serve_fs(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
struct mg_dash_user *u, bool stream) {
|
struct mg_dash_user *u, bool stream) {
|
||||||
struct mg_dash *dash = (struct mg_dash *) c->fn_data;
|
struct mg_dash *dash = (struct mg_dash *) c->fn_data;
|
||||||
@@ -1770,9 +1729,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. Streaming owns the connection and c->data.
|
// Authenticated above. If streaming started, the stream handler owns
|
||||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
// the connection and c->data, so skip the marker check below
|
||||||
return;
|
if (mg_http_stream_body(c, hm, otacb, NULL)) 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') {
|
||||||
@@ -3682,12 +3641,66 @@ struct mg_fs mg_fs_posix = {p_stat, p_list, p_open, p_close, p_read,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#if MG_ENABLE_HEALTH
|
||||||
|
#if MG_ARCH == MG_ARCH_ESP32
|
||||||
|
// reusable for other platforms, currently ESP32-only
|
||||||
|
struct mg_health_coredump {
|
||||||
|
const void *ptr;
|
||||||
|
size_t poffset;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct mg_health_coredump s_coredump;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool mg_health_coredump_begin(size_t *size) {
|
||||||
|
#if MG_ARCH == MG_ARCH_ESP32 && CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||||
|
const esp_partition_t *p = esp_partition_find_first(
|
||||||
|
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_COREDUMP, NULL);
|
||||||
|
size_t address = 0, len = 0;
|
||||||
|
esp_err_t err = esp_core_dump_image_get(&address, &len);
|
||||||
|
if (size == NULL || s_coredump.ptr != NULL || err != ESP_OK || p == NULL)
|
||||||
|
return false;
|
||||||
|
s_coredump.ptr = p;
|
||||||
|
s_coredump.poffset = address - p->address;
|
||||||
|
s_coredump.size = len;
|
||||||
|
*size = len;
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
(void) size;
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mg_health_coredump_read(size_t offset, void *buf, size_t *len) {
|
||||||
|
#if MG_ARCH == MG_ARCH_ESP32 && CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||||
|
const esp_partition_t *p = (const esp_partition_t *) s_coredump.ptr;
|
||||||
|
if (p == NULL || offset > s_coredump.size)
|
||||||
|
return false;
|
||||||
|
if (*len > s_coredump.size - offset) *len = s_coredump.size - offset;
|
||||||
|
return esp_partition_read(p, s_coredump.poffset + offset, buf, *len) == ESP_OK;
|
||||||
|
#else
|
||||||
|
(void) offset, (void) buf, (void) len;
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mg_health_coredump_end(void) {
|
||||||
|
#if MG_ARCH == MG_ARCH_ESP32 && CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||||
|
bool ok = s_coredump.ptr != NULL;
|
||||||
|
memset(&s_coredump, 0, sizeof(s_coredump));
|
||||||
|
return ok;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// The one health record. Lives in RAM that survives a warm reset, see
|
// The one health record. Lives in RAM that survives a warm reset, see
|
||||||
// MG_HEALTH_RAM and the .mg_health region in the linker script
|
// MG_HEALTH_RAM and the .mg_health region in the linker script
|
||||||
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 H7, H5, C5 support. Needs the _estack symbol from the linker script
|
||||||
#if MG_ENABLE_HEALTH && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5))
|
#if 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.
|
||||||
@@ -3729,52 +3742,57 @@ void BusFault_Handler(void) __attribute__((alias("HardFault_Handler")));
|
|||||||
void UsageFault_Handler(void) __attribute__((alias("HardFault_Handler")));
|
void UsageFault_Handler(void) __attribute__((alias("HardFault_Handler")));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Start a new boot: validate the record, count the boot, store the reset
|
// Start a new boot: validate the record. Called by MG_HEALTH_INIT()
|
||||||
// reason. The current boot moves to prev_* slots first, so the previous
|
|
||||||
// boot's uptime and reason stay readable. Called by MG_HEALTH_INIT()
|
|
||||||
void mg_health_init(void) {
|
void mg_health_init(void) {
|
||||||
char magic[] = MG_HEALTH_MAGIC;
|
char magic[] = MG_HEALTH_MAGIC;
|
||||||
// struct mg_health *h = mg_health_get();
|
|
||||||
if (!mg_health_valid()) { // First boot, or RAM lost its contents
|
if (!mg_health_valid()) { // First boot, or RAM lost its contents
|
||||||
memset(&mg_health_record, 0, sizeof(mg_health_record));
|
memset(&mg_health_record, 0, sizeof(mg_health_record));
|
||||||
memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic));
|
memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic));
|
||||||
}
|
}
|
||||||
|
#if MG_ARCH == MG_ARCH_ESP32
|
||||||
#if 0 && MG_ARCH == MG_ARCH_ESP32 && CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||||
{
|
mg_health_record.arch = (uint16_t) MG_HEALTH_ARCH_XTENSA;
|
||||||
|
#elif CONFIG_IDF_TARGET_ARCH_RISCV
|
||||||
|
mg_health_record.arch = (uint16_t) MG_HEALTH_ARCH_RISCV;
|
||||||
|
#endif
|
||||||
|
esp_reset_reason_t rr = esp_reset_reason();
|
||||||
|
mg_health_record.reset_reason = esp2mg(rr);
|
||||||
|
#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH == 0
|
||||||
|
MG_ERROR(("MG_HEALTH is enabled but CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH=n"));
|
||||||
|
#else
|
||||||
// After a crash, copy the backtrace from the core dump in flash: the same
|
// After a crash, copy the backtrace from the core dump in flash: the same
|
||||||
// addresses the panic handler prints as "Backtrace: PC:SP PC:SP ...".
|
// addresses the panic handler prints as "Backtrace: PC:SP PC:SP ...".
|
||||||
// The core dump stays in flash, so check the reset reason, otherwise an
|
// The core dump stays in flash, so check the reset reason, otherwise an
|
||||||
// old crash is reported on every boot
|
// old crash is reported on every boot
|
||||||
static esp_core_dump_summary_t s; // Big on RISC-V, keep it off the stack
|
esp_core_dump_summary_t *s = calloc(1, sizeof(*s));
|
||||||
esp_reset_reason_t rr = esp_reset_reason();
|
|
||||||
uint32_t *out = mg_health_record.data;
|
uint32_t *out = mg_health_record.data;
|
||||||
size_t n = 0, max = MG_HEALTH_DATA_SIZE;
|
size_t n = 0;
|
||||||
if (rr != ESP_RST_PANIC && rr != ESP_RST_INT_WDT && rr != ESP_RST_TASK_WDT)
|
|
||||||
return; // Not a crash that went through the panic handler
|
|
||||||
if (max == 0 || esp_core_dump_get_summary(&s) != ESP_OK) return;
|
|
||||||
|
|
||||||
mg_health_record.cpuid = 42; // ESP32
|
|
||||||
mg_health_record.reset_reason = MG_HEALTH_RESET_FAULT;
|
|
||||||
|
|
||||||
memset(out, 0, sizeof(mg_health_record.data));
|
memset(out, 0, sizeof(mg_health_record.data));
|
||||||
out[n++] = s.exc_pc;
|
if (s == NULL) return;
|
||||||
|
if ((rr != ESP_RST_PANIC && rr != ESP_RST_INT_WDT &&
|
||||||
|
rr != ESP_RST_TASK_WDT) // Not a crash caught by the panic handler
|
||||||
|
|| esp_core_dump_get_summary(s) != ESP_OK) {
|
||||||
|
free(s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
out[n++] = s->exc_pc;
|
||||||
|
memcpy(&out[n], s->exc_task, 16), n += 4;
|
||||||
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||||
for (uint32_t i = 0; i < s.exc_bt_info.depth && n < max; i++) {
|
out[n++] = s->ex_info.exc_a[0];
|
||||||
if (s.exc_bt_info.bt[i] != s.exc_pc) out[n++] = s.exc_bt_info.bt[i];
|
out[n++] = s->ex_info.exc_a[1];
|
||||||
}
|
size_t len = s->exc_bt_info.depth;
|
||||||
|
if (len > MG_HEALTH_DATA_SIZE - n) len = (MG_HEALTH_DATA_SIZE - n);
|
||||||
|
memcpy(&out[n], s->exc_bt_info.bt, 4 * len); // sizeof(uint32_t)
|
||||||
#elif CONFIG_IDF_TARGET_ARCH_RISCV
|
#elif CONFIG_IDF_TARGET_ARCH_RISCV
|
||||||
// No unwinder: take the return address, then words on the stack that
|
out[n++] = s->ex_info.exc_ra;
|
||||||
// point into executable memory (a heuristic, may include stale values)
|
out[n++] = s->ex_info.exc_sp;
|
||||||
if (n < max) out[n++] = s.ex_info.ra;
|
size_t len = s->exc_bt_info.dump_size / 4 ; // sizeof(uint32_t)
|
||||||
for (uint32_t i = 0; i + 4 <= s.exc_bt_info.dump_size && n < max; i += 4) {
|
if (len > MG_HEALTH_DATA_SIZE - n) len = (MG_HEALTH_DATA_SIZE - n);
|
||||||
uint32_t w;
|
memcpy(&out[n], s->exc_bt_info.stackdump, 4 * len);
|
||||||
memcpy(&w, &s.exc_bt_info.stackdump[i], sizeof(w)); // Byte array
|
|
||||||
if (esp_ptr_executable((void *) (uintptr_t) w)) out[n++] = w;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
free(s);
|
||||||
|
#endif // CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||||
|
#endif // MG_ARCH_ESP32
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) {
|
static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) {
|
||||||
@@ -3788,10 +3806,16 @@ static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) {
|
|||||||
size_t mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) {
|
size_t mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) {
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
if (mg_health_valid() &&
|
if (mg_health_valid() &&
|
||||||
mg_health_record.reset_reason == MG_HEALTH_RESET_FAULT) {
|
(mg_health_record.reset_reason == (uint16_t) MG_HEALTH_RESET_FAULT
|
||||||
|
#if MG_ARCH == MG_ARCH_ESP32
|
||||||
|
|| mg_health_record.reset_reason == (uint16_t) MG_HEALTH_RESET_WATCHDOG
|
||||||
|
#endif
|
||||||
|
)) {
|
||||||
size_t n = sizeof(mg_health_record.data) / sizeof(mg_health_record.data[0]);
|
size_t n = sizeof(mg_health_record.data) / sizeof(mg_health_record.data[0]);
|
||||||
len += mg_xprintf(fn, arg, "{%m:%hhu,%m:%u,%m:[%M]}", //
|
len += mg_xprintf(fn, arg, "{%m:%hhu,%m:%hu,%m:%hu,%m:%u,%m:[%M]}", //
|
||||||
MG_ESC("version"), mg_health_record.magic[3], //
|
MG_ESC("version"), mg_health_record.magic[3], //
|
||||||
|
MG_ESC("arch"), mg_health_record.arch, //
|
||||||
|
MG_ESC("reason"), mg_health_record.reset_reason, //
|
||||||
MG_ESC("cpuid"), mg_health_record.cpuid, //
|
MG_ESC("cpuid"), mg_health_record.cpuid, //
|
||||||
MG_ESC("data"), print_uint32, n, mg_health_record.data);
|
MG_ESC("data"), print_uint32, n, mg_health_record.data);
|
||||||
} else {
|
} else {
|
||||||
@@ -3800,6 +3824,7 @@ size_t mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) {
|
|||||||
(void) ap;
|
(void) ap;
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef MG_ENABLE_LINES
|
#ifdef MG_ENABLE_LINES
|
||||||
#line 1 "src/http.c"
|
#line 1 "src/http.c"
|
||||||
@@ -4685,8 +4710,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 {
|
} else if (!mg_http_stream_body(c, hm, serve_upload_cb, fd)) {
|
||||||
mg_http_stream_body(c, hm, serve_upload_cb, fd);
|
mg_fs_close(fd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4820,7 +4845,6 @@ 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
|
||||||
@@ -4833,13 +4857,13 @@ static void mg_stream_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
(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->finished) {
|
if (us->received < us->expected) {
|
||||||
// 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->finished = true; // MG_EV_ERROR is followed by MG_EV_CLOSE
|
us->received = us->expected; // MG_EV_ERROR is followed by MG_EV_CLOSE
|
||||||
fn(NULL, NULL, &us->user_data);
|
fn(NULL, NULL, &us->user_data);
|
||||||
}
|
}
|
||||||
} else if (ev == MG_EV_READ && !c->is_draining && !us->finished) {
|
} else if (ev == MG_EV_READ && !c->is_draining) {
|
||||||
if (!us->failed && c->recv.len > 0 && us->received < us->expected) {
|
if (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;
|
||||||
@@ -4850,13 +4874,10 @@ 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->finished && (us->failed || us->received >= us->expected)) {
|
if (us->received >= us->expected) {
|
||||||
// Finished upload. Always call cb, even after error, so it can clean up
|
// 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 && us->failed) {
|
if (!c->is_client) {
|
||||||
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");
|
||||||
}
|
}
|
||||||
@@ -4865,54 +4886,48 @@ static void mg_stream_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
|
bool mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
bool (*cb)(struct mg_http_message *, struct mg_str *,
|
bool (*cb)(struct mg_http_message *, struct mg_str *,
|
||||||
void **),
|
void **user_data),
|
||||||
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;
|
||||||
int code = 0;
|
struct mg_str *te = mg_http_get_header(hm, "Transfer-Encoding");
|
||||||
size_t head_len = hm->head.len;
|
bool handled = false;
|
||||||
|
|
||||||
// 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 (!c->is_client && mg_strcmp(hm->method, mg_str("POST")) != 0 &&
|
if (hm->body.len == (size_t) ~0 ||
|
||||||
|
(te != NULL && mg_strcasecmp(*te, mg_str("chunked")) == 0)) {
|
||||||
|
// Unknown length or chunked, cannot stream. Leave it to the caller
|
||||||
|
} else if (!c->is_client &&
|
||||||
|
mg_strcmp(hm->method, mg_str("POST")) != 0 &&
|
||||||
mg_strcmp(hm->method, mg_str("PUT")) != 0) {
|
mg_strcmp(hm->method, mg_str("PUT")) != 0) {
|
||||||
code = 405;
|
// Not an upload request. Leave it to the caller
|
||||||
} else if (mg_http_get_header(hm, "Transfer-Encoding") != NULL) {
|
|
||||||
code = 400;
|
|
||||||
} else if (hm->body.len == (size_t) ~0 ||
|
|
||||||
(!c->is_client && mg_http_get_header(hm, "Content-Length") == NULL)) {
|
|
||||||
code = 411;
|
|
||||||
} else if (sizeof(*us) > sizeof(c->data)) {
|
} else if (sizeof(*us) > sizeof(c->data)) {
|
||||||
code = 500;
|
if (!c->is_client) { // Nobody to reply to on a client connection
|
||||||
}
|
c->pfn = NULL;
|
||||||
c->pfn = NULL; // Take ownership even when validation fails
|
mg_http_reply(c, 500, NULL, "data too small\n");
|
||||||
if (code != 0) {
|
|
||||||
c->fn = NULL;
|
|
||||||
c->fn_data = NULL;
|
|
||||||
mg_iobuf_del(&c->recv, 0, c->recv.len);
|
|
||||||
cb(NULL, NULL, &user_data); // Release resources even if start was skipped
|
|
||||||
if (c->is_client) {
|
|
||||||
mg_error(c, "%s", mg_http_status_code_str(code));
|
|
||||||
} else {
|
|
||||||
mg_http_reply(c, code, code == 405 ? "Allow: POST, PUT\r\n" : NULL, "");
|
|
||||||
}
|
|
||||||
c->is_draining = 1;
|
c->is_draining = 1;
|
||||||
|
cb(NULL, NULL, &user_data); // Let cb release user_data, start skipped
|
||||||
|
handled = true;
|
||||||
|
}
|
||||||
} 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 (!c->is_client && !us->failed && !us->finished &&
|
if (mg_http_get_header(hm, "Expect") != NULL) {
|
||||||
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, head_len); // Delete HTTP headers
|
mg_iobuf_del(&c->recv, 0, hm->head.len); // Delete HTTP headers
|
||||||
|
c->fn = mg_stream_handler; // We own the connection from now
|
||||||
|
c->fn_data = (void *) (ptrdiff_t) cb;
|
||||||
mg_call(c, MG_EV_READ, &c->recv.len); // Process the body bytes we have
|
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) {
|
||||||
@@ -5837,7 +5852,7 @@ size_t mg_l2_driver_output(struct mg_tcpip_if *ifp, size_t len) {
|
|||||||
#define MGI_802154_DATA (125 - MGI_802154_HDR)
|
#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 mg_l2_802154_tx(struct mg_tcpip_if *ifp, const void *buf, size_t len,
|
||||||
size_t (*send_fn)(const uint8_t *, size_t,
|
size_t (*send)(const uint8_t *, size_t,
|
||||||
struct mg_tcpip_if *)) {
|
struct mg_tcpip_if *)) {
|
||||||
struct mg_l2_802154_data *d =
|
struct mg_l2_802154_data *d =
|
||||||
(struct mg_l2_802154_data *) ifp->driver_data;
|
(struct mg_l2_802154_data *) ifp->driver_data;
|
||||||
@@ -5855,7 +5870,7 @@ size_t mg_l2_802154_tx(struct mg_tcpip_if *ifp, const void *buf, size_t len,
|
|||||||
f[12] = m[2], f[13] = m[1], f[14] = m[0]; // Little endian EUI-64
|
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);
|
f[15] = d->tag, f[16] = (uint8_t) ((i << 4) | last);
|
||||||
memcpy(f + MGI_802154_HDR, p + i * MGI_802154_DATA, n);
|
memcpy(f + MGI_802154_HDR, p + i * MGI_802154_DATA, n);
|
||||||
if (send_fn(f, MGI_802154_HDR + n, ifp) == 0) return 0;
|
if (send(f, MGI_802154_HDR + n, ifp) == 0) return 0;
|
||||||
}
|
}
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
@@ -7389,6 +7404,7 @@ static void mg_mdash_fn(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
}
|
}
|
||||||
mg_iobuf_free(&io);
|
mg_iobuf_free(&io);
|
||||||
} else if (ev == MG_EV_CLOSE) {
|
} else if (ev == MG_EV_CLOSE) {
|
||||||
|
mg_health_coredump_end();
|
||||||
mg_mdash_conn = NULL;
|
mg_mdash_conn = NULL;
|
||||||
MG_INFO(("disconnected, ram %lu", 0));
|
MG_INFO(("disconnected, ram %lu", 0));
|
||||||
}
|
}
|
||||||
@@ -7446,6 +7462,35 @@ static void mg_mdash_rpc_ota_end(struct mg_rpc_req *r) {
|
|||||||
// MG_ESC("percent"));
|
// MG_ESC("percent"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rpc_coredump_begin(struct mg_rpc_req *r) {
|
||||||
|
size_t size = 0;
|
||||||
|
if (mg_health_coredump_begin(&size)) {
|
||||||
|
mg_rpc_ok(r, "{%m:%llu}", MG_ESC("size"), (uint64_t) size);
|
||||||
|
} else {
|
||||||
|
mg_rpc_err(r, 500, "%m", MG_ESC("failed"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rpc_coredump_read(struct mg_rpc_req *r) {
|
||||||
|
uint8_t buf[768];
|
||||||
|
size_t len = sizeof(buf);
|
||||||
|
size_t offset = (size_t) mg_json_get_long(r->frame, "$.params.offset", -1);
|
||||||
|
if (mg_health_coredump_read(offset, buf, &len)) {
|
||||||
|
mg_rpc_ok(r, "{%m:\"%M\"}", MG_ESC("data"), mg_print_base64,
|
||||||
|
(int) len, buf);
|
||||||
|
} else {
|
||||||
|
mg_rpc_err(r, 500, "%m", MG_ESC("failed"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rpc_coredump_end(struct mg_rpc_req *r) {
|
||||||
|
if (mg_health_coredump_end()) {
|
||||||
|
mg_rpc_ok(r, "true");
|
||||||
|
} else {
|
||||||
|
mg_rpc_err(r, 500, "%m", MG_ESC("failed"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void mg_mdash_init(struct mg_mgr *mgr) {
|
void mg_mdash_init(struct mg_mgr *mgr) {
|
||||||
MG_INFO(("Initialising mdash agent, key [%s]", MG_MDASH_KEY));
|
MG_INFO(("Initialising mdash agent, key [%s]", MG_MDASH_KEY));
|
||||||
mg_rpc_add(&mgr->rpcs, mg_str("RPC.List"), mg_rpc_list, NULL);
|
mg_rpc_add(&mgr->rpcs, mg_str("RPC.List"), mg_rpc_list, NULL);
|
||||||
@@ -7453,6 +7498,9 @@ void mg_mdash_init(struct mg_mgr *mgr) {
|
|||||||
mg_rpc_add(&mgr->rpcs, mg_str("OTA.Begin"), mg_mdash_rpc_ota_begin, NULL);
|
mg_rpc_add(&mgr->rpcs, mg_str("OTA.Begin"), mg_mdash_rpc_ota_begin, NULL);
|
||||||
mg_rpc_add(&mgr->rpcs, mg_str("OTA.Write"), mg_mdash_rpc_ota_write, NULL);
|
mg_rpc_add(&mgr->rpcs, mg_str("OTA.Write"), mg_mdash_rpc_ota_write, NULL);
|
||||||
mg_rpc_add(&mgr->rpcs, mg_str("OTA.End"), mg_mdash_rpc_ota_end, NULL);
|
mg_rpc_add(&mgr->rpcs, mg_str("OTA.End"), mg_mdash_rpc_ota_end, NULL);
|
||||||
|
mg_rpc_add(&mgr->rpcs, mg_str("CoreDump.Begin"), rpc_coredump_begin, NULL);
|
||||||
|
mg_rpc_add(&mgr->rpcs, mg_str("CoreDump.Read"), rpc_coredump_read, NULL);
|
||||||
|
mg_rpc_add(&mgr->rpcs, mg_str("CoreDump.End"), rpc_coredump_end, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mg_mdash_poll(struct mg_mgr *mgr) {
|
void mg_mdash_poll(struct mg_mgr *mgr) {
|
||||||
@@ -9330,36 +9378,6 @@ static void l2addr_resolved(struct mg_connection *c);
|
|||||||
static uint8_t *get_return_l2addr(struct mg_tcpip_if *ifp, struct mg_addr *rem,
|
static uint8_t *get_return_l2addr(struct mg_tcpip_if *ifp, struct mg_addr *rem,
|
||||||
bool is_udp, struct pkt *pkt);
|
bool is_udp, struct pkt *pkt);
|
||||||
|
|
||||||
#if MG_ENABLE_BSD_SOCKETS
|
|
||||||
static uint8_t *sendto_cache_find(struct mg_tcpip_if *ifp, uint32_t ip) {
|
|
||||||
struct mg_tcpip_sendto_entry *a;
|
|
||||||
for (a = ifp->sendto; a < ifp->sendto + MG_TCPIP_SENDTO_CACHE_SIZE; a++) {
|
|
||||||
if (a->ip == ip) return (uint8_t *) &a->addr;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sendto_cache_add(struct mg_tcpip_if *ifp, uint32_t ip,
|
|
||||||
const uint8_t *addr) {
|
|
||||||
struct mg_tcpip_sendto_entry *a;
|
|
||||||
for (a = ifp->sendto; a < ifp->sendto + MG_TCPIP_SENDTO_CACHE_SIZE; a++) {
|
|
||||||
if (a->ip == ip) return; // already cached, skip
|
|
||||||
if (a->ip == 0) break; // not present, first empty slot
|
|
||||||
}
|
|
||||||
if (a == ifp->sendto + MG_TCPIP_SENDTO_CACHE_SIZE) { // no room
|
|
||||||
memmove(ifp->sendto, ifp->sendto + 1,
|
|
||||||
sizeof(ifp->sendto[0]) * (MG_TCPIP_SENDTO_CACHE_SIZE - 1));
|
|
||||||
a--; // make room and point to last entry (just made)
|
|
||||||
}
|
|
||||||
a->ip = ip; // populate this entry
|
|
||||||
memcpy(&a->addr, addr, sizeof(a->addr));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sendto_cache_clear(struct mg_tcpip_if *ifp) {
|
|
||||||
memset(ifp->sendto, 0, sizeof(ifp->sendto));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// RFC826, ARP assumes Ethernet MAC addresses
|
// RFC826, ARP assumes Ethernet MAC addresses
|
||||||
static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||||
if (pkt->arp->op == mg_htons(1) && pkt->arp->tpa == ifp->ip) {
|
if (pkt->arp->op == mg_htons(1) && pkt->arp->tpa == ifp->ip) {
|
||||||
@@ -9965,57 +9983,30 @@ static void proto_error(struct mg_connection *c, bool r, const char *f, ...) {
|
|||||||
static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||||
struct mg_connection *c = getpeer(ifp->mgr, pkt, true);
|
struct mg_connection *c = getpeer(ifp->mgr, pkt, true);
|
||||||
struct connstate *s;
|
struct connstate *s;
|
||||||
struct mg_addr rem;
|
|
||||||
uint8_t *l2addr;
|
uint8_t *l2addr;
|
||||||
size_t len = pkt->pay.len;
|
|
||||||
if (c == NULL) return false; // No UDP listener on this port
|
if (c == NULL) return false; // No UDP listener on this port
|
||||||
s = (struct connstate *) (c + 1);
|
s = (struct connstate *) (c + 1);
|
||||||
|
c->rem.port = pkt->udp->sport;
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
if (c->loc.is_ip6) { // matching of v4/v6 to dest is done bt getpeer()
|
if (c->loc.is_ip6) { // matching of v4/v6 to dest is done bt getpeer()
|
||||||
if (!udp6csum_ok(pkt->ip6, pkt->udp)) return false;
|
if (!udp6csum_ok(pkt->ip6, pkt->udp)) return false;
|
||||||
if (c->is_bsd && c->is_client &&
|
c->rem.addr.ip6[0] = pkt->ip6->src[0],
|
||||||
(c->rem.port != pkt->udp->sport ||
|
c->rem.addr.ip6[1] = pkt->ip6->src[1], c->rem.is_ip6 = true;
|
||||||
!MG_IP6MATCH(c->rem.addr.ip6, pkt->ip6->src)))
|
|
||||||
return true;
|
|
||||||
rem.addr.ip6[0] = pkt->ip6->src[0],
|
|
||||||
rem.addr.ip6[1] = pkt->ip6->src[1], rem.is_ip6 = true;
|
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (!udpcsum_ok(pkt->ip, pkt->udp)) return false;
|
if (!udpcsum_ok(pkt->ip, pkt->udp)) return false;
|
||||||
if (c->is_bsd && c->is_client &&
|
c->rem.addr.ip4 = pkt->ip->src;
|
||||||
(c->rem.port != pkt->udp->sport || c->rem.addr.ip4 != pkt->ip->src))
|
|
||||||
return true;
|
|
||||||
rem.addr.ip4 = pkt->ip->src, rem.is_ip6 = false;
|
|
||||||
}
|
}
|
||||||
rem.port = pkt->udp->sport;
|
if ((l2addr = get_return_l2addr(ifp, &c->rem, true, pkt)) == NULL)
|
||||||
if (!c->is_bsd || c->is_client) c->rem = rem;
|
|
||||||
if ((l2addr = get_return_l2addr(ifp, &rem, true, pkt)) == NULL)
|
|
||||||
return false; // safety net for lousy networks
|
return false; // safety net for lousy networks
|
||||||
if (!c->is_bsd || c->is_client) memcpy(s->mac, l2addr, sizeof(s->mac));
|
memcpy(s->mac, l2addr, sizeof(s->mac));
|
||||||
#if MG_ENABLE_BSD_SOCKETS
|
|
||||||
if (c->is_bsd && !rem.is_ip6 && MG_IPISLOCAL(ifp, rem.addr.ip4))
|
|
||||||
sendto_cache_add(ifp, rem.addr.ip4, l2addr);
|
|
||||||
#endif
|
|
||||||
#if MG_ENABLE_BSD_SOCKETS
|
|
||||||
if (c->is_bsd) len += sizeof(struct mg_bsd_udp_frame);
|
|
||||||
#endif
|
|
||||||
if (c->recv.len >= MG_MAX_RECV_SIZE) {
|
if (c->recv.len >= MG_MAX_RECV_SIZE) {
|
||||||
proto_error(c, false, "max_recv_buf_size reached");
|
proto_error(c, false, "max_recv_buf_size reached");
|
||||||
} else if (c->recv.size - c->recv.len < len &&
|
} else if (c->recv.size - c->recv.len < pkt->pay.len &&
|
||||||
!mg_iobuf_resize(&c->recv, c->recv.len + len)) {
|
!mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) {
|
||||||
proto_error(c, false, "oom");
|
proto_error(c, false, "oom");
|
||||||
} else {
|
} else {
|
||||||
#if MG_ENABLE_BSD_SOCKETS
|
|
||||||
if (c->is_bsd) {
|
|
||||||
struct mg_bsd_udp_frame *h =
|
|
||||||
(struct mg_bsd_udp_frame *) &c->recv.buf[c->recv.len];
|
|
||||||
h->ip = rem.addr.ip4;
|
|
||||||
h->port = rem.port;
|
|
||||||
h->len = (uint16_t) pkt->pay.len;
|
|
||||||
c->recv.len += sizeof(*h);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len);
|
memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len);
|
||||||
c->recv.len += pkt->pay.len;
|
c->recv.len += pkt->pay.len;
|
||||||
mg_call(c, MG_EV_READ, &pkt->pay.len);
|
mg_call(c, MG_EV_READ, &pkt->pay.len);
|
||||||
@@ -11068,9 +11059,6 @@ void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) {
|
|||||||
|
|
||||||
static void close_conn(struct mg_connection *c) {
|
static void close_conn(struct mg_connection *c) {
|
||||||
struct connstate *s = (struct connstate *) (c + 1);
|
struct connstate *s = (struct connstate *) (c + 1);
|
||||||
#if MG_ENABLE_BSD_SOCKETS
|
|
||||||
if (c->is_bsd && c->is_udp) sendto_cache_clear(c->mgr->ifp);
|
|
||||||
#endif
|
|
||||||
mg_iobuf_free(&s->txq);
|
mg_iobuf_free(&s->txq);
|
||||||
mg_close_conn(c);
|
mg_close_conn(c);
|
||||||
}
|
}
|
||||||
@@ -11092,11 +11080,6 @@ static void send_syn(struct mg_connection *c) {
|
|||||||
static void l2addr_resolved(struct mg_connection *c) {
|
static void l2addr_resolved(struct mg_connection *c) {
|
||||||
if (c->is_udp) {
|
if (c->is_udp) {
|
||||||
c->is_connecting = 0;
|
c->is_connecting = 0;
|
||||||
#if MG_ENABLE_BSD_SOCKETS
|
|
||||||
if (c->is_bsd)
|
|
||||||
sendto_cache_add(c->mgr->ifp, c->rem.addr.ip4,
|
|
||||||
((struct connstate *) (c + 1))->mac);
|
|
||||||
#endif
|
|
||||||
mg_call(c, MG_EV_CONNECT, NULL);
|
mg_call(c, MG_EV_CONNECT, NULL);
|
||||||
} else {
|
} else {
|
||||||
send_syn(c);
|
send_syn(c);
|
||||||
@@ -11111,25 +11094,20 @@ void mg_connect_resolved(struct mg_connection *c) {
|
|||||||
s->retransmit = ifp->enable_tcp_retransmit;
|
s->retransmit = ifp->enable_tcp_retransmit;
|
||||||
s->txq.align = MG_TCPIP_TXQ_ALIGN;
|
s->txq.align = MG_TCPIP_TXQ_ALIGN;
|
||||||
c->is_resolving = 0;
|
c->is_resolving = 0;
|
||||||
if (c->loc.port == 0) {
|
if (ifp->eport < MG_EPHEMERAL_PORT_BASE) ifp->eport = MG_EPHEMERAL_PORT_BASE;
|
||||||
if (ifp->eport < MG_EPHEMERAL_PORT_BASE)
|
|
||||||
ifp->eport = MG_EPHEMERAL_PORT_BASE;
|
|
||||||
c->loc.port = mg_htons(ifp->eport++);
|
c->loc.port = mg_htons(ifp->eport++);
|
||||||
} // otherwise retain an existing bound port
|
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
if (c->rem.is_ip6) {
|
if (c->rem.is_ip6) {
|
||||||
if (c->loc.addr.ip6[0] == 0 && c->loc.addr.ip6[1] == 0 &&
|
if (c->rem.addr.ip6[0] == ifp->ip6ll[0]) { // same local LAN, use ll
|
||||||
c->rem.addr.ip6[0] == ifp->ip6ll[0]) { // same local LAN, use ll
|
|
||||||
c->loc.addr.ip6[0] = ifp->ip6ll[0], c->loc.addr.ip6[1] = ifp->ip6ll[1];
|
c->loc.addr.ip6[0] = ifp->ip6ll[0], c->loc.addr.ip6[1] = ifp->ip6ll[1];
|
||||||
} else if (c->loc.addr.ip6[0] == 0 && c->loc.addr.ip6[1] == 0) {
|
} else { // use global address
|
||||||
// Use global address
|
|
||||||
c->loc.addr.ip6[0] = ifp->ip6[0], c->loc.addr.ip6[1] = ifp->ip6[1];
|
c->loc.addr.ip6[0] = ifp->ip6[0], c->loc.addr.ip6[1] = ifp->ip6[1];
|
||||||
} // otherwise retain an existing bound address
|
}
|
||||||
c->loc.is_ip6 = true;
|
c->loc.is_ip6 = true;
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (c->loc.addr.ip4 == 0) c->loc.addr.ip4 = ifp->ip;
|
c->loc.addr.ip4 = ifp->ip;
|
||||||
}
|
}
|
||||||
MG_DEBUG(("%lu %M -> %M", c->id, mg_print_ip_port, &c->loc, mg_print_ip_port,
|
MG_DEBUG(("%lu %M -> %M", c->id, mg_print_ip_port, &c->loc, mg_print_ip_port,
|
||||||
&c->rem));
|
&c->rem));
|
||||||
@@ -11160,20 +11138,11 @@ void mg_connect_resolved(struct mg_connection *c) {
|
|||||||
uint32_t rem_ip = c->rem.addr.ip4;
|
uint32_t rem_ip = c->rem.addr.ip4;
|
||||||
if (ifp->ip && MG_IPISLOCAL(ifp, rem_ip) &&
|
if (ifp->ip && MG_IPISLOCAL(ifp, rem_ip) &&
|
||||||
rem_ip != ifp->gw) { // skip if gw (onstatechange -> ARP)
|
rem_ip != ifp->gw) { // skip if gw (onstatechange -> ARP)
|
||||||
// We're in the same LAN
|
// If we're in the same LAN, fire an ARP lookup.
|
||||||
#if MG_ENABLE_BSD_SOCKETS
|
|
||||||
if (c->is_bsd && c->is_udp && // see if we already have L2 data
|
|
||||||
(l2addr = sendto_cache_find(ifp, rem_ip)) != NULL) {
|
|
||||||
memcpy(s->mac, l2addr, sizeof(s->mac));
|
|
||||||
l2addr_resolved(c);
|
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
{ // fire an ARP lookup
|
|
||||||
MG_DEBUG(("%lu ARP lookup...", c->id));
|
MG_DEBUG(("%lu ARP lookup...", c->id));
|
||||||
mg_tcpip_arp_request(ifp, rem_ip, NULL);
|
mg_tcpip_arp_request(ifp, rem_ip, NULL);
|
||||||
settmout(c, MIP_TTYPE_ARP);
|
settmout(c, MIP_TTYPE_ARP);
|
||||||
c->is_arplooking = 1;
|
c->is_arplooking = 1;
|
||||||
}
|
|
||||||
} else if (ifp->gw_ready) {
|
} else if (ifp->gw_ready) {
|
||||||
memcpy(s->mac, ifp->gwmac, sizeof(s->mac));
|
memcpy(s->mac, ifp->gwmac, sizeof(s->mac));
|
||||||
l2addr_resolved(c);
|
l2addr_resolved(c);
|
||||||
@@ -11193,36 +11162,13 @@ bool mg_open_listener(struct mg_connection *c, const char *url) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void write_conn(struct mg_connection *c) {
|
static void write_conn(struct mg_connection *c) {
|
||||||
long len;
|
long len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len)
|
||||||
size_t off = 0;
|
|
||||||
bool empty = false;
|
|
||||||
#if MG_ENABLE_BSD_SOCKETS
|
|
||||||
struct mg_bsd_udp_frame *h;
|
|
||||||
if (c->is_bsd && c->is_udp) {
|
|
||||||
if (c->is_connecting) return;
|
|
||||||
h = (struct mg_bsd_udp_frame *) c->send.buf;
|
|
||||||
assert(c->send.len >= sizeof(*h));
|
|
||||||
assert(h->len <= c->send.len - sizeof(*h));
|
|
||||||
c->rem.port = h->port;
|
|
||||||
c->rem.is_ip6 = false;
|
|
||||||
if (!c->is_client && c->rem.addr.ip4 != h->ip) {
|
|
||||||
c->rem.addr.ip4 = h->ip;
|
|
||||||
mg_connect_resolved(c);
|
|
||||||
if (c->is_connecting) return;
|
|
||||||
}
|
|
||||||
len = mg_io_send(c, h + 1, h->len);
|
|
||||||
off = sizeof(*h), empty = h->len == 0;
|
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len)
|
|
||||||
: mg_io_send(c, c->send.buf, c->send.len);
|
: mg_io_send(c, c->send.buf, c->send.len);
|
||||||
}
|
|
||||||
// TODO(): mg_tls_send() may return 0 forever on steady OOM
|
// TODO(): mg_tls_send() may return 0 forever on steady OOM
|
||||||
if (len == MG_IO_ERR) {
|
if (len == MG_IO_ERR) {
|
||||||
proto_error(c, false, "tx err");
|
proto_error(c, false, "tx err");
|
||||||
} else if (len > 0 || empty) {
|
} else if (len > 0) {
|
||||||
mg_iobuf_del(&c->send, 0, off + (size_t) len);
|
mg_iobuf_del(&c->send, 0, (size_t) len);
|
||||||
mg_call(c, MG_EV_WRITE, &len);
|
mg_call(c, MG_EV_WRITE, &len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11582,7 +11528,9 @@ static void s_firmware_fn(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
(unsigned long) hm->body.len, (unsigned long) s_ota->size);
|
(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));
|
||||||
mg_http_stream_body(c, hm, s_firmware_cb, NULL);
|
if (!mg_http_stream_body(c, hm, s_firmware_cb, NULL)) {
|
||||||
|
mg_error(c, "Cannot stream firmware");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (ev == MG_EV_ERROR) {
|
} else if (ev == MG_EV_ERROR) {
|
||||||
s_ota->fn((char *) ev_data);
|
s_ota->fn((char *) ev_data);
|
||||||
@@ -11615,21 +11563,6 @@ void mg_ota_url_check(struct mg_mgr *mgr, const char *json_url,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uses *p as a flag: non-NULL means an OTA session is open and must be
|
|
||||||
// closed by mg_ota_end()
|
|
||||||
bool mg_http_ota_cb(struct mg_http_message *hm, struct mg_str *data, void **p) {
|
|
||||||
if (hm != NULL) { // Start
|
|
||||||
if (!mg_ota_begin(hm->body.len)) return false;
|
|
||||||
*p = (void *) (ptrdiff_t) 1;
|
|
||||||
return true;
|
|
||||||
} else if (data != NULL) { // Next chunk
|
|
||||||
return mg_ota_write(data->buf, data->len);
|
|
||||||
}
|
|
||||||
if (*p == NULL) return false; // End. Begin failed, nothing to finish
|
|
||||||
*p = NULL;
|
|
||||||
return mg_ota_end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void mg_ota_poll(struct mg_mgr *mgr) {
|
void mg_ota_poll(struct mg_mgr *mgr) {
|
||||||
static uint64_t t = 5000; // Fire first time 5 sec after boot
|
static uint64_t t = 5000; // Fire first time 5 sec after boot
|
||||||
|
|
||||||
@@ -15531,11 +15464,7 @@ static long recv_raw(struct mg_connection *c, void *buf, size_t len) {
|
|||||||
union usa usa;
|
union usa usa;
|
||||||
socklen_t slen = tousa(&c->rem, &usa);
|
socklen_t slen = tousa(&c->rem, &usa);
|
||||||
n = recvfrom(FD(c), (char *) buf, len, 0, &usa.sa, &slen);
|
n = recvfrom(FD(c), (char *) buf, len, 0, &usa.sa, &slen);
|
||||||
if (n > 0) {
|
if (n > 0) tomgaddr(&usa, &c->rem, slen != sizeof(usa.sin));
|
||||||
struct mg_addr rem;
|
|
||||||
tomgaddr(&usa, &rem, slen != sizeof(usa.sin));
|
|
||||||
c->rem = rem;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING);
|
n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING);
|
||||||
}
|
}
|
||||||
@@ -29561,9 +29490,8 @@ uint64_t mg_ntohll(uint64_t net) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void mg_delayms(unsigned int ms) {
|
void mg_delayms(unsigned int ms) {
|
||||||
uint64_t now, to = mg_millis() + ms + 1;
|
uint64_t to = mg_millis() + ms + 1;
|
||||||
while ((now = mg_millis()) < to)
|
while (mg_millis() < to) (void) 0;
|
||||||
if (now + ms + 1 < to) to = now + ms + 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if MG_ENABLE_CUSTOM_CALLOC
|
#if MG_ENABLE_CUSTOM_CALLOC
|
||||||
@@ -34690,7 +34618,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_VERBOSE(("%.*s", s_at_resp_len, at_resp));
|
MG_DEBUG(("%.*s", s_at_resp_len, at_resp)); // TODO(scaprile): --> VERBOSE
|
||||||
// BT-ENABLED DEPENDENCY
|
// BT-ENABLED DEPENDENCY
|
||||||
|
|
||||||
// MODULE DEPENDENCY
|
// MODULE DEPENDENCY
|
||||||
|
|||||||
+99
-64
@@ -253,8 +253,11 @@ extern "C" {
|
|||||||
#include <esp_system.h>
|
#include <esp_system.h>
|
||||||
#include <esp_mac.h>
|
#include <esp_mac.h>
|
||||||
#include <esp_idf_version.h>
|
#include <esp_idf_version.h>
|
||||||
// #include <esp_memory_utils.h> // For health record coredumps
|
#if MG_ENABLE_HEALTH
|
||||||
// #include <esp_core_dump.h> // For health record coredumps
|
#include <esp_memory_utils.h>
|
||||||
|
#include <esp_core_dump.h>
|
||||||
|
#include <esp_partition.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define MG_PATH_MAX 128
|
#define MG_PATH_MAX 128
|
||||||
|
|
||||||
@@ -2724,32 +2727,29 @@ int mg_http_status(const struct mg_http_message *hm);
|
|||||||
|
|
||||||
// Streams a POST/PUT body to a callback as it arrives, without buffering it.
|
// 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(). Always takes over the connection. Invalid requests receive
|
// with mg_match(). Returns true if it took over the connection, false if the
|
||||||
// an HTTP error; client-side failures call mg_error(). Replies and connection
|
// request is not POST/PUT, is chunked, or has no Content-Length.
|
||||||
// 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 exactly once, including validation
|
// - at end: both NULL. Always called once, also when the connection drops
|
||||||
// failures before start and connections that drop early. Release resources
|
// early, so release resources here. A truncated body looks like a complete
|
||||||
// here. A truncated body looks like a complete one: validate before
|
// one: validate before committing.
|
||||||
// committing.
|
// Return true on success, false on error.
|
||||||
// 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:
|
||||||
// The existing event handler is no longer called. hm is invalid after this
|
// After it returns true, this function hijacks the connection. Existing event
|
||||||
// call; c->data and c->fn_data are reserved for streaming state.
|
// handler not called, hm is invalid, c->data and c->fn_data are modified
|
||||||
void mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
|
bool mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
bool (*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);
|
||||||
@@ -4223,8 +4223,9 @@ 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");
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
@@ -4373,6 +4374,8 @@ void mg_ota_poll(struct mg_mgr *);
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#if MG_ENABLE_HEALTH
|
||||||
|
|
||||||
#ifndef MG_HEALTH_MAGIC
|
#ifndef MG_HEALTH_MAGIC
|
||||||
#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
|
||||||
@@ -4381,13 +4384,15 @@ void mg_ota_poll(struct mg_mgr *);
|
|||||||
#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) || defined(STM32C5))
|
|
||||||
#define MG_HEALTH_DATA_SIZE 250
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MG_HEALTH_DATA_SIZE
|
#ifndef MG_HEALTH_DATA_SIZE
|
||||||
|
#if (defined(STM32H7_DEV_ID) || defined(STM32H5)) || defined(STM32C5)
|
||||||
|
#define MG_HEALTH_DATA_SIZE 250
|
||||||
|
#elif MG_ARCH == MG_ARCH_ESP32
|
||||||
|
#define MG_HEALTH_DATA_SIZE 23
|
||||||
|
#else
|
||||||
#define MG_HEALTH_DATA_SIZE 10
|
#define MG_HEALTH_DATA_SIZE 10
|
||||||
#endif
|
#endif
|
||||||
|
#endif // MG_HEALTH_DATA_SIZE
|
||||||
|
|
||||||
// Places the record in RAM that survives a reset. Define in mongoose_config.h
|
// Places the record in RAM that survives a reset. Define in mongoose_config.h
|
||||||
// to match the linker script, e.g. __attribute__((section(".mg_health")))
|
// to match the linker script, e.g. __attribute__((section(".mg_health")))
|
||||||
@@ -4395,6 +4400,13 @@ void mg_ota_poll(struct mg_mgr *);
|
|||||||
#define MG_HEALTH_RAM
|
#define MG_HEALTH_RAM
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// CPU core architecture
|
||||||
|
enum mg_health_arch {
|
||||||
|
MG_HEALTH_ARCH_ARM = 0,
|
||||||
|
MG_HEALTH_ARCH_XTENSA,
|
||||||
|
MG_HEALTH_ARCH_RISCV
|
||||||
|
};
|
||||||
|
|
||||||
// Why the device restarted. Hardware usually reports several flags at once,
|
// Why the device restarted. Hardware usually reports several flags at once,
|
||||||
// so these are ordered by how much they tell you: the most specific cause a
|
// so these are ordered by how much they tell you: the most specific cause a
|
||||||
// device reports wins
|
// device reports wins
|
||||||
@@ -4411,7 +4423,8 @@ enum mg_health_reason {
|
|||||||
|
|
||||||
struct mg_health {
|
struct mg_health {
|
||||||
char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data
|
char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data
|
||||||
uint32_t reset_reason; // enum mg_health_reason that started this boot
|
uint16_t reset_reason; // enum mg_health_reason that started this boot
|
||||||
|
uint16_t arch; // enum mg_health_arch, core architecture (ARM, etc)
|
||||||
uint32_t cpuid; // ARM CPU ID
|
uint32_t cpuid; // ARM CPU ID
|
||||||
uint32_t data[MG_HEALTH_DATA_SIZE];
|
uint32_t data[MG_HEALTH_DATA_SIZE];
|
||||||
};
|
};
|
||||||
@@ -4444,9 +4457,72 @@ static inline const char *mg_health_reason_str(int reason) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if MG_ARCH == MG_ARCH_ESP32
|
||||||
|
#ifndef CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||||
|
#define CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH 0
|
||||||
|
#endif
|
||||||
|
#ifndef ESP_PARTITION_SUBTYPE_DATA_COREDUMP
|
||||||
|
#define ESP_PARTITION_SUBTYPE_DATA_COREDUMP 0x03
|
||||||
|
#endif
|
||||||
|
// clang-format off
|
||||||
|
static inline uint16_t esp2mg(esp_reset_reason_t rr) {
|
||||||
|
switch(rr) {
|
||||||
|
case ESP_RST_POWERON: return (uint16_t) MG_HEALTH_RESET_POWER;
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 2)
|
||||||
|
case ESP_RST_PWR_GLITCH:
|
||||||
|
#endif
|
||||||
|
case ESP_RST_BROWNOUT: return (uint16_t) MG_HEALTH_RESET_BROWNOUT;
|
||||||
|
case ESP_RST_EXT: return (uint16_t) MG_HEALTH_RESET_PIN;
|
||||||
|
case ESP_RST_SW: return (uint16_t) MG_HEALTH_RESET_SOFTWARE;
|
||||||
|
case ESP_RST_DEEPSLEEP: return (uint16_t) MG_HEALTH_RESET_LOWPOWER;
|
||||||
|
case ESP_RST_INT_WDT:
|
||||||
|
case ESP_RST_TASK_WDT:
|
||||||
|
case ESP_RST_WDT: return (uint16_t) MG_HEALTH_RESET_WATCHDOG;
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 2)
|
||||||
|
case ESP_RST_CPU_LOCKUP:
|
||||||
|
#endif
|
||||||
|
case ESP_RST_PANIC: return (uint16_t) MG_HEALTH_RESET_FAULT;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
return (uint16_t) MG_HEALTH_RESET_UNKNOWN;
|
||||||
|
}
|
||||||
|
// clang-format on
|
||||||
|
#endif
|
||||||
|
|
||||||
#define MG_HEALTH_INIT() mg_health_init()
|
#define MG_HEALTH_INIT() mg_health_init()
|
||||||
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
|
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
|
||||||
extern void mg_health_init(void);
|
extern void mg_health_init(void);
|
||||||
|
extern bool mg_health_coredump_begin(size_t *size);
|
||||||
|
extern bool mg_health_coredump_read(size_t offset, void *buf, size_t *len);
|
||||||
|
extern bool mg_health_coredump_end(void);
|
||||||
|
#else
|
||||||
|
#define MG_HEALTH_INIT()
|
||||||
|
static inline int mg_health_reason(void) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static inline const char *mg_health_reason_str(int reason) {
|
||||||
|
(void) reason;
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
static inline size_t mg_print_crash_record(mg_pfn_t fn, void *arg,
|
||||||
|
va_list *ap) {
|
||||||
|
(void) ap;
|
||||||
|
return mg_xprintf(fn, arg, "{}");
|
||||||
|
}
|
||||||
|
static inline bool mg_health_coredump_begin(size_t *size) {
|
||||||
|
(void) size;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
static inline bool mg_health_coredump_read(size_t offset, void *buf,
|
||||||
|
size_t *len) {
|
||||||
|
(void) offset, (void) buf, (void) len;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
static inline bool mg_health_coredump_end(void) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
// Cloud connector API to https://mdash.net - device management cloud
|
// Cloud connector API to https://mdash.net - device management cloud
|
||||||
|
|
||||||
|
|
||||||
@@ -4620,17 +4696,6 @@ struct mg_tcpip_driver {
|
|||||||
typedef void (*mg_tcpip_event_handler_t)(struct mg_tcpip_if *ifp, int ev,
|
typedef void (*mg_tcpip_event_handler_t)(struct mg_tcpip_if *ifp, int ev,
|
||||||
void *ev_data);
|
void *ev_data);
|
||||||
|
|
||||||
#if MG_ENABLE_BSD_SOCKETS
|
|
||||||
#ifndef MG_TCPIP_SENDTO_CACHE_SIZE
|
|
||||||
#define MG_TCPIP_SENDTO_CACHE_SIZE 4
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct mg_tcpip_sendto_entry {
|
|
||||||
uint32_t ip;
|
|
||||||
struct mg_l2addr addr;
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
// Events delivered to mg_tcpip_if.fn (user) and mg_tcpip_if.pfn (driver).
|
// Events delivered to mg_tcpip_if.fn (user) and mg_tcpip_if.pfn (driver).
|
||||||
// Each entry shows the ev_data type to cast inside the handler.
|
// Each entry shows the ev_data type to cast inside the handler.
|
||||||
@@ -4693,9 +4758,6 @@ struct mg_tcpip_if {
|
|||||||
enum mg_l2type l2type; // Layer-2 type: Ethernet, PPP, etc. (see l2.h)
|
enum mg_l2type l2type; // Layer-2 type: Ethernet, PPP, etc. (see l2.h)
|
||||||
union mg_l2data l2data; // Layer-2 config and state (see l2.h)
|
union mg_l2data l2data; // Layer-2 config and state (see l2.h)
|
||||||
char *dns4_url; // Active DNS server URL (set from DHCP or config)
|
char *dns4_url; // Active DNS server URL (set from DHCP or config)
|
||||||
#if MG_ENABLE_BSD_SOCKETS
|
|
||||||
struct mg_tcpip_sendto_entry sendto[MG_TCPIP_SENDTO_CACHE_SIZE]; // Local IPv4 neighbors
|
|
||||||
#endif
|
|
||||||
uint64_t now; // Current time in ms (updated each mg_mgr_poll)
|
uint64_t now; // Current time in ms (updated each mg_mgr_poll)
|
||||||
uint64_t timer_1000ms; // Timestamp of last 1-second tick
|
uint64_t timer_1000ms; // Timestamp of last 1-second tick
|
||||||
uint64_t lease_expire; // DHCP lease expiry time in ms
|
uint64_t lease_expire; // DHCP lease expiry time in ms
|
||||||
@@ -4907,37 +4969,10 @@ void mg_bsd_poll(struct mg_mgr *);
|
|||||||
#ifndef EPIPE
|
#ifndef EPIPE
|
||||||
#define EPIPE 32
|
#define EPIPE 32
|
||||||
#endif
|
#endif
|
||||||
#ifndef EOPNOTSUPP
|
|
||||||
#define EOPNOTSUPP 95
|
|
||||||
#endif
|
|
||||||
#ifndef EISCONN
|
|
||||||
#define EISCONN 106
|
|
||||||
#endif
|
|
||||||
#ifndef EDESTADDRREQ
|
|
||||||
#define EDESTADDRREQ 121
|
|
||||||
#endif
|
|
||||||
#ifndef EMSGSIZE
|
|
||||||
#define EMSGSIZE 90
|
|
||||||
#endif
|
|
||||||
#ifndef EPROTONOSUPPORT
|
#ifndef EPROTONOSUPPORT
|
||||||
#define EPROTONOSUPPORT 93
|
#define EPROTONOSUPPORT 93
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__DCC__)
|
|
||||||
#pragma pack(1)
|
|
||||||
#else
|
|
||||||
#pragma pack(push, 1)
|
|
||||||
#endif
|
|
||||||
struct mg_bsd_udp_frame {
|
|
||||||
uint32_t ip;
|
|
||||||
uint16_t port, len;
|
|
||||||
};
|
|
||||||
#if defined(__DCC__)
|
|
||||||
#pragma pack(0)
|
|
||||||
#else
|
|
||||||
#pragma pack(pop)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MG_ENABLE_BSD_PROTOTYPES
|
#if MG_ENABLE_BSD_PROTOTYPES
|
||||||
typedef unsigned int socklen_t;
|
typedef unsigned int socklen_t;
|
||||||
typedef unsigned long nfds_t;
|
typedef unsigned long nfds_t;
|
||||||
|
|||||||
+5
-2
@@ -22,8 +22,11 @@
|
|||||||
#include <esp_system.h>
|
#include <esp_system.h>
|
||||||
#include <esp_mac.h>
|
#include <esp_mac.h>
|
||||||
#include <esp_idf_version.h>
|
#include <esp_idf_version.h>
|
||||||
// #include <esp_memory_utils.h> // For health record coredumps
|
#if MG_ENABLE_HEALTH
|
||||||
// #include <esp_core_dump.h> // For health record coredumps
|
#include <esp_memory_utils.h>
|
||||||
|
#include <esp_core_dump.h>
|
||||||
|
#include <esp_partition.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define MG_PATH_MAX 128
|
#define MG_PATH_MAX 128
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#include "bsd.h"
|
#include "bsd.h"
|
||||||
#include "dns.h"
|
#include "dns.h"
|
||||||
#include "util.h"
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
#if MG_ENABLE_BSD_SOCKETS && MG_ENABLE_FREERTOS
|
#if MG_ENABLE_BSD_SOCKETS && MG_ENABLE_FREERTOS
|
||||||
@@ -111,57 +110,6 @@ static struct mg_connection *bsd_new_conn(struct mg_bsd_sock *s) {
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mirror net_builtin.c::trim_len() logic
|
|
||||||
static bool bsd_udp_too_big(struct mg_connection *c, size_t len) {
|
|
||||||
struct mg_tcpip_if *ifp = c->mgr->ifp;
|
|
||||||
size_t hlen = c->rem.is_ip6 ? 48 : 32; // IP plus UDP headers
|
|
||||||
size_t cap = ifp->mtu - hlen;
|
|
||||||
size_t room = ifp->tx.len - (ifp->framesize - ifp->l2mtu) - hlen;
|
|
||||||
|
|
||||||
return len > cap || len > room;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool bsd_udp_send(struct mg_connection *c, uint32_t ip, uint16_t port,
|
|
||||||
const void *buf, size_t len) {
|
|
||||||
struct mg_bsd_udp_frame *h;
|
|
||||||
size_t off = c->send.len, total = sizeof(*h) + len;
|
|
||||||
|
|
||||||
if (mg_iobuf_add(&c->send, off, NULL, total) != total) return false;
|
|
||||||
h = (struct mg_bsd_udp_frame *) (c->send.buf + off);
|
|
||||||
h->ip = ip;
|
|
||||||
h->port = port;
|
|
||||||
h->len = (uint16_t) len;
|
|
||||||
memcpy(h + 1, buf, len);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t bsd_recv(struct mg_bsd_sock *s, void *buf, size_t len,
|
|
||||||
int flags, struct sockaddr *src, socklen_t *addrlen) {
|
|
||||||
struct mg_connection *c = s->c;
|
|
||||||
size_t n;
|
|
||||||
|
|
||||||
if (!s->is_udp) {
|
|
||||||
n = c->recv.len < len ? c->recv.len : len;
|
|
||||||
memcpy(buf, c->recv.buf, n);
|
|
||||||
if (!(flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, n);
|
|
||||||
} else {
|
|
||||||
struct mg_bsd_udp_frame *h = (struct mg_bsd_udp_frame *) c->recv.buf;
|
|
||||||
assert(c->recv.len >= sizeof(*h));
|
|
||||||
assert(h->len <= c->recv.len - sizeof(*h));
|
|
||||||
n = h->len < len ? h->len : len;
|
|
||||||
memcpy(buf, h + 1, n);
|
|
||||||
if (src != NULL && addrlen != NULL) {
|
|
||||||
struct sockaddr_in *sa = (struct sockaddr_in *) src;
|
|
||||||
sa->sin_family = AF_INET;
|
|
||||||
sa->sin_port = h->port;
|
|
||||||
sa->sin_addr.s_addr = h->ip;
|
|
||||||
*addrlen = sizeof(*sa);
|
|
||||||
}
|
|
||||||
if (!(flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, sizeof(*h) + h->len);
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if MG_ENABLE_FREERTOS
|
#if MG_ENABLE_FREERTOS
|
||||||
// Complete operations blocked on c before Mongoose destroys it.
|
// Complete operations blocked on c before Mongoose destroys it.
|
||||||
static void bsd_finish_waiters(struct mg_bsd_sock *s, bool error) {
|
static void bsd_finish_waiters(struct mg_bsd_sock *s, bool error) {
|
||||||
@@ -342,9 +290,9 @@ static int bsd_ready(struct mg_bsd_sock *s) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (s->c->recv.len > 0 || s->c->is_draining) ready |= BSD_READ;
|
if (s->c->recv.len > 0 || s->c->is_draining) ready |= BSD_READ;
|
||||||
if ((s->is_udp || !s->c->is_connecting) && !s->c->is_closing &&
|
if (!s->is_udp && !s->c->is_connecting && !s->c->is_closing &&
|
||||||
!s->c->is_draining)
|
!s->c->is_draining)
|
||||||
ready |= BSD_WRITE; // UDP socket or established TCP socket
|
ready |= BSD_WRITE; // Established TCP socket
|
||||||
return ready;
|
return ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,7 +306,7 @@ static void bsd_close(struct mg_bsd_sock *s) {
|
|||||||
if (t->orphan && t->listener == s) {
|
if (t->orphan && t->listener == s) {
|
||||||
if (t->c != NULL) {
|
if (t->c != NULL) {
|
||||||
t->c->fn_data = NULL;
|
t->c->fn_data = NULL;
|
||||||
t->c->is_draining = 1;
|
mg_close_conn(t->c);
|
||||||
t->c = NULL;
|
t->c = NULL;
|
||||||
}
|
}
|
||||||
memset(t, 0, sizeof(*t)); // Release orphan
|
memset(t, 0, sizeof(*t)); // Release orphan
|
||||||
@@ -366,7 +314,7 @@ static void bsd_close(struct mg_bsd_sock *s) {
|
|||||||
}
|
}
|
||||||
if (s->c != NULL) {
|
if (s->c != NULL) {
|
||||||
s->c->fn_data = NULL;
|
s->c->fn_data = NULL;
|
||||||
s->c->is_draining = 1;
|
mg_close_conn(s->c);
|
||||||
s->c = NULL;
|
s->c = NULL;
|
||||||
}
|
}
|
||||||
memset(s, 0, sizeof(*s)); // Release socket
|
memset(s, 0, sizeof(*s)); // Release socket
|
||||||
@@ -545,8 +493,8 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
cmd->result = -1, cmd->err = ECONNREFUSED;
|
cmd->result = -1, cmd->err = ECONNREFUSED;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (s->is_udp || !c->is_connecting) {
|
if (!c->is_connecting) {
|
||||||
cmd->result = 0; // UDP may still resolve ARP; TCP waits for SYN/SYN-ACK
|
cmd->result = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (s->nonblock) {
|
if (s->nonblock) {
|
||||||
@@ -570,8 +518,11 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (c->recv.len > 0) {
|
if (c->recv.len > 0) {
|
||||||
cmd->result = (int) bsd_recv(s, cmd->op_recv.buf, cmd->op_recv.len,
|
size_t n = cmd->op_recv.len;
|
||||||
cmd->op_recv.flags, NULL, NULL);
|
if (c->recv.len < n) n = c->recv.len;
|
||||||
|
memcpy(cmd->op_recv.buf, c->recv.buf, n);
|
||||||
|
if (!(cmd->op_recv.flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, n);
|
||||||
|
cmd->result = (int) n;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (s->err != 0) {
|
if (s->err != 0) {
|
||||||
@@ -592,17 +543,10 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
case BSD_OP_SEND:
|
case BSD_OP_SEND:
|
||||||
if ((s = bsd_get(cmd->op_send.fd)) == NULL) {
|
if ((s = bsd_get(cmd->op_send.fd)) == NULL) {
|
||||||
cmd->result = -1, cmd->err = EBADF;
|
cmd->result = -1, cmd->err = EBADF;
|
||||||
} else if (s->err != 0 || s->closed ||
|
} else if ((c = s->c) == NULL || s->err != 0 || c->is_closing ||
|
||||||
((c = s->c) != NULL && (c->is_closing || c->is_draining))) {
|
c->is_draining) {
|
||||||
cmd->result = -1, cmd->err = EPIPE;
|
cmd->result = -1, cmd->err = EPIPE;
|
||||||
} else if (c == NULL || (s->is_udp && !c->is_client)) {
|
} else if (mg_send(c, cmd->op_send.buf, cmd->op_send.len)) {
|
||||||
cmd->result = -1, cmd->err = s->is_udp ? EDESTADDRREQ : EPIPE;
|
|
||||||
} else if (s->is_udp && bsd_udp_too_big(c, cmd->op_send.len)) {
|
|
||||||
cmd->result = -1, cmd->err = EMSGSIZE;
|
|
||||||
} else if ((s->is_udp ? bsd_udp_send(c, c->rem.addr.ip4, c->rem.port,
|
|
||||||
cmd->op_send.buf, cmd->op_send.len)
|
|
||||||
: mg_send(c, cmd->op_send.buf,
|
|
||||||
cmd->op_send.len))) {
|
|
||||||
cmd->result = (int) cmd->op_send.len;
|
cmd->result = (int) cmd->op_send.len;
|
||||||
} else if (s->nonblock || (cmd->op_send.flags & MSG_DONTWAIT)) {
|
} else if (s->nonblock || (cmd->op_send.flags & MSG_DONTWAIT)) {
|
||||||
cmd->result = -1, cmd->err = EAGAIN;
|
cmd->result = -1, cmd->err = EAGAIN;
|
||||||
@@ -617,20 +561,16 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
case BSD_OP_SENDTO:
|
case BSD_OP_SENDTO:
|
||||||
if ((s = bsd_get(cmd->op_sendto.fd)) == NULL) {
|
if ((s = bsd_get(cmd->op_sendto.fd)) == NULL) {
|
||||||
cmd->result = -1, cmd->err = EBADF;
|
cmd->result = -1, cmd->err = EBADF;
|
||||||
} else if (!s->is_udp) {
|
|
||||||
cmd->result = -1, cmd->err = EOPNOTSUPP;
|
|
||||||
} else if ((c = s->c) == NULL || s->err != 0 || c->is_closing ||
|
} else if ((c = s->c) == NULL || s->err != 0 || c->is_closing ||
|
||||||
c->is_draining) {
|
c->is_draining) {
|
||||||
cmd->result = -1, cmd->err = EPIPE;
|
cmd->result = -1, cmd->err = EPIPE;
|
||||||
} else if (c->is_client) {
|
|
||||||
cmd->result = -1, cmd->err = EISCONN;
|
|
||||||
} else {
|
} else {
|
||||||
const struct sockaddr_in *sa =
|
const struct sockaddr_in *sa =
|
||||||
(const struct sockaddr_in *) cmd->op_sendto.addr;
|
(const struct sockaddr_in *) cmd->op_sendto.addr;
|
||||||
if (bsd_udp_too_big(c, cmd->op_sendto.len)) {
|
c->rem.port = sa->sin_port;
|
||||||
cmd->result = -1, cmd->err = EMSGSIZE;
|
c->rem.addr.ip4 = sa->sin_addr.s_addr;
|
||||||
} else if (bsd_udp_send(c, sa->sin_addr.s_addr, sa->sin_port,
|
c->rem.is_ip6 = false;
|
||||||
cmd->op_sendto.buf, cmd->op_sendto.len)) {
|
if (mg_send(c, cmd->op_sendto.buf, cmd->op_sendto.len)) {
|
||||||
cmd->result = (int) cmd->op_sendto.len;
|
cmd->result = (int) cmd->op_sendto.len;
|
||||||
} else if (s->nonblock || (cmd->op_sendto.flags & MSG_DONTWAIT)) {
|
} else if (s->nonblock || (cmd->op_sendto.flags & MSG_DONTWAIT)) {
|
||||||
cmd->result = -1, cmd->err = EAGAIN;
|
cmd->result = -1, cmd->err = EAGAIN;
|
||||||
@@ -647,9 +587,6 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
if ((s = bsd_get(cmd->op_recvfrom.fd)) == NULL) {
|
if ((s = bsd_get(cmd->op_recvfrom.fd)) == NULL) {
|
||||||
cmd->result = -1, cmd->err = EBADF;
|
cmd->result = -1, cmd->err = EBADF;
|
||||||
return true;
|
return true;
|
||||||
} else if (!s->is_udp) {
|
|
||||||
cmd->result = -1, cmd->err = EOPNOTSUPP;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
if ((c = s->c) == NULL) {
|
if ((c = s->c) == NULL) {
|
||||||
cmd->result = s->closed && s->err == 0 ? 0 : -1;
|
cmd->result = s->closed && s->err == 0 ? 0 : -1;
|
||||||
@@ -657,11 +594,18 @@ static bool bsd_run(struct mg_bsd_cmd *cmd) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (c->recv.len > 0) {
|
if (c->recv.len > 0) {
|
||||||
cmd->result = (int) bsd_recv(s, cmd->op_recvfrom.buf,
|
size_t n = cmd->op_recvfrom.len;
|
||||||
cmd->op_recvfrom.len,
|
if (c->recv.len < n) n = c->recv.len;
|
||||||
cmd->op_recvfrom.flags,
|
memcpy(cmd->op_recvfrom.buf, c->recv.buf, n);
|
||||||
cmd->op_recvfrom.src,
|
if (!(cmd->op_recvfrom.flags & MSG_PEEK)) mg_iobuf_del(&c->recv, 0, n);
|
||||||
cmd->op_recvfrom.addrlen);
|
if (cmd->op_recvfrom.src != NULL && cmd->op_recvfrom.addrlen != NULL) {
|
||||||
|
struct sockaddr_in *sa = (struct sockaddr_in *) cmd->op_recvfrom.src;
|
||||||
|
sa->sin_family = AF_INET;
|
||||||
|
sa->sin_port = c->rem.port;
|
||||||
|
sa->sin_addr.s_addr = c->rem.addr.ip4;
|
||||||
|
*cmd->op_recvfrom.addrlen = sizeof(*sa);
|
||||||
|
}
|
||||||
|
cmd->result = (int) n;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (s->err != 0) {
|
if (s->err != 0) {
|
||||||
|
|||||||
@@ -55,37 +55,10 @@ void mg_bsd_poll(struct mg_mgr *);
|
|||||||
#ifndef EPIPE
|
#ifndef EPIPE
|
||||||
#define EPIPE 32
|
#define EPIPE 32
|
||||||
#endif
|
#endif
|
||||||
#ifndef EOPNOTSUPP
|
|
||||||
#define EOPNOTSUPP 95
|
|
||||||
#endif
|
|
||||||
#ifndef EISCONN
|
|
||||||
#define EISCONN 106
|
|
||||||
#endif
|
|
||||||
#ifndef EDESTADDRREQ
|
|
||||||
#define EDESTADDRREQ 121
|
|
||||||
#endif
|
|
||||||
#ifndef EMSGSIZE
|
|
||||||
#define EMSGSIZE 90
|
|
||||||
#endif
|
|
||||||
#ifndef EPROTONOSUPPORT
|
#ifndef EPROTONOSUPPORT
|
||||||
#define EPROTONOSUPPORT 93
|
#define EPROTONOSUPPORT 93
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__DCC__)
|
|
||||||
#pragma pack(1)
|
|
||||||
#else
|
|
||||||
#pragma pack(push, 1)
|
|
||||||
#endif
|
|
||||||
struct mg_bsd_udp_frame {
|
|
||||||
uint32_t ip;
|
|
||||||
uint16_t port, len;
|
|
||||||
};
|
|
||||||
#if defined(__DCC__)
|
|
||||||
#pragma pack(0)
|
|
||||||
#else
|
|
||||||
#pragma pack(pop)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MG_ENABLE_BSD_PROTOTYPES
|
#if MG_ENABLE_BSD_PROTOTYPES
|
||||||
typedef unsigned int socklen_t;
|
typedef unsigned int socklen_t;
|
||||||
typedef unsigned long nfds_t;
|
typedef unsigned long nfds_t;
|
||||||
|
|||||||
+18
-3
@@ -533,6 +533,21 @@ static void mg_dash_handle_add(struct mg_connection *c, struct mg_dash *dash,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Firmware upload callback for mg_http_stream_body(). Uses *p as a flag:
|
||||||
|
// non-NULL means an OTA session is open and must be closed by mg_ota_end()
|
||||||
|
static bool otacb(struct mg_http_message *hm, struct mg_str *data, void **p) {
|
||||||
|
if (hm != NULL) { // Start
|
||||||
|
if (!mg_ota_begin(hm->body.len)) return false;
|
||||||
|
*p = (void *) (ptrdiff_t) 1;
|
||||||
|
return true;
|
||||||
|
} else if (data != NULL) { // Next chunk
|
||||||
|
return mg_ota_write(data->buf, data->len);
|
||||||
|
}
|
||||||
|
if (*p == NULL) return false; // End. Begin failed, nothing to finish
|
||||||
|
*p = NULL;
|
||||||
|
return mg_ota_end();
|
||||||
|
}
|
||||||
|
|
||||||
static void mg_serve_fs(struct mg_connection *c, struct mg_http_message *hm,
|
static void mg_serve_fs(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
struct mg_dash_user *u, bool stream) {
|
struct mg_dash_user *u, bool stream) {
|
||||||
struct mg_dash *dash = (struct mg_dash *) c->fn_data;
|
struct mg_dash *dash = (struct mg_dash *) c->fn_data;
|
||||||
@@ -579,9 +594,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. Streaming owns the connection and c->data.
|
// Authenticated above. If streaming started, the stream handler owns
|
||||||
mg_http_stream_body(c, hm, mg_http_ota_cb, NULL);
|
// the connection and c->data, so skip the marker check below
|
||||||
return;
|
if (mg_http_stream_body(c, hm, otacb, NULL)) 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') {
|
||||||
|
|||||||
@@ -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_VERBOSE(("%.*s", s_at_resp_len, at_resp));
|
MG_DEBUG(("%.*s", s_at_resp_len, at_resp)); // TODO(scaprile): --> VERBOSE
|
||||||
// BT-ENABLED DEPENDENCY
|
// BT-ENABLED DEPENDENCY
|
||||||
|
|
||||||
// MODULE DEPENDENCY
|
// MODULE DEPENDENCY
|
||||||
|
|||||||
+101
-35
@@ -1,12 +1,66 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "health.h"
|
#include "health.h"
|
||||||
|
|
||||||
|
#if MG_ENABLE_HEALTH
|
||||||
|
#if MG_ARCH == MG_ARCH_ESP32
|
||||||
|
// reusable for other platforms, currently ESP32-only
|
||||||
|
struct mg_health_coredump {
|
||||||
|
const void *ptr;
|
||||||
|
size_t poffset;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct mg_health_coredump s_coredump;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool mg_health_coredump_begin(size_t *size) {
|
||||||
|
#if MG_ARCH == MG_ARCH_ESP32 && CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||||
|
const esp_partition_t *p = esp_partition_find_first(
|
||||||
|
ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_COREDUMP, NULL);
|
||||||
|
size_t address = 0, len = 0;
|
||||||
|
esp_err_t err = esp_core_dump_image_get(&address, &len);
|
||||||
|
if (size == NULL || s_coredump.ptr != NULL || err != ESP_OK || p == NULL)
|
||||||
|
return false;
|
||||||
|
s_coredump.ptr = p;
|
||||||
|
s_coredump.poffset = address - p->address;
|
||||||
|
s_coredump.size = len;
|
||||||
|
*size = len;
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
(void) size;
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mg_health_coredump_read(size_t offset, void *buf, size_t *len) {
|
||||||
|
#if MG_ARCH == MG_ARCH_ESP32 && CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||||
|
const esp_partition_t *p = (const esp_partition_t *) s_coredump.ptr;
|
||||||
|
if (p == NULL || offset > s_coredump.size)
|
||||||
|
return false;
|
||||||
|
if (*len > s_coredump.size - offset) *len = s_coredump.size - offset;
|
||||||
|
return esp_partition_read(p, s_coredump.poffset + offset, buf, *len) == ESP_OK;
|
||||||
|
#else
|
||||||
|
(void) offset, (void) buf, (void) len;
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mg_health_coredump_end(void) {
|
||||||
|
#if MG_ARCH == MG_ARCH_ESP32 && CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||||
|
bool ok = s_coredump.ptr != NULL;
|
||||||
|
memset(&s_coredump, 0, sizeof(s_coredump));
|
||||||
|
return ok;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// The one health record. Lives in RAM that survives a warm reset, see
|
// The one health record. Lives in RAM that survives a warm reset, see
|
||||||
// MG_HEALTH_RAM and the .mg_health region in the linker script
|
// MG_HEALTH_RAM and the .mg_health region in the linker script
|
||||||
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 H7, H5, C5 support. Needs the _estack symbol from the linker script
|
||||||
#if MG_ENABLE_HEALTH && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5))
|
#if 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.
|
||||||
@@ -48,52 +102,57 @@ void BusFault_Handler(void) __attribute__((alias("HardFault_Handler")));
|
|||||||
void UsageFault_Handler(void) __attribute__((alias("HardFault_Handler")));
|
void UsageFault_Handler(void) __attribute__((alias("HardFault_Handler")));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Start a new boot: validate the record, count the boot, store the reset
|
// Start a new boot: validate the record. Called by MG_HEALTH_INIT()
|
||||||
// reason. The current boot moves to prev_* slots first, so the previous
|
|
||||||
// boot's uptime and reason stay readable. Called by MG_HEALTH_INIT()
|
|
||||||
void mg_health_init(void) {
|
void mg_health_init(void) {
|
||||||
char magic[] = MG_HEALTH_MAGIC;
|
char magic[] = MG_HEALTH_MAGIC;
|
||||||
// struct mg_health *h = mg_health_get();
|
|
||||||
if (!mg_health_valid()) { // First boot, or RAM lost its contents
|
if (!mg_health_valid()) { // First boot, or RAM lost its contents
|
||||||
memset(&mg_health_record, 0, sizeof(mg_health_record));
|
memset(&mg_health_record, 0, sizeof(mg_health_record));
|
||||||
memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic));
|
memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic));
|
||||||
}
|
}
|
||||||
|
#if MG_ARCH == MG_ARCH_ESP32
|
||||||
#if 0 && MG_ARCH == MG_ARCH_ESP32 && CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||||
{
|
mg_health_record.arch = (uint16_t) MG_HEALTH_ARCH_XTENSA;
|
||||||
|
#elif CONFIG_IDF_TARGET_ARCH_RISCV
|
||||||
|
mg_health_record.arch = (uint16_t) MG_HEALTH_ARCH_RISCV;
|
||||||
|
#endif
|
||||||
|
esp_reset_reason_t rr = esp_reset_reason();
|
||||||
|
mg_health_record.reset_reason = esp2mg(rr);
|
||||||
|
#if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH == 0
|
||||||
|
MG_ERROR(("MG_HEALTH is enabled but CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH=n"));
|
||||||
|
#else
|
||||||
// After a crash, copy the backtrace from the core dump in flash: the same
|
// After a crash, copy the backtrace from the core dump in flash: the same
|
||||||
// addresses the panic handler prints as "Backtrace: PC:SP PC:SP ...".
|
// addresses the panic handler prints as "Backtrace: PC:SP PC:SP ...".
|
||||||
// The core dump stays in flash, so check the reset reason, otherwise an
|
// The core dump stays in flash, so check the reset reason, otherwise an
|
||||||
// old crash is reported on every boot
|
// old crash is reported on every boot
|
||||||
static esp_core_dump_summary_t s; // Big on RISC-V, keep it off the stack
|
esp_core_dump_summary_t *s = calloc(1, sizeof(*s));
|
||||||
esp_reset_reason_t rr = esp_reset_reason();
|
|
||||||
uint32_t *out = mg_health_record.data;
|
uint32_t *out = mg_health_record.data;
|
||||||
size_t n = 0, max = MG_HEALTH_DATA_SIZE;
|
size_t n = 0;
|
||||||
if (rr != ESP_RST_PANIC && rr != ESP_RST_INT_WDT && rr != ESP_RST_TASK_WDT)
|
|
||||||
return; // Not a crash that went through the panic handler
|
|
||||||
if (max == 0 || esp_core_dump_get_summary(&s) != ESP_OK) return;
|
|
||||||
|
|
||||||
mg_health_record.cpuid = 42; // ESP32
|
|
||||||
mg_health_record.reset_reason = MG_HEALTH_RESET_FAULT;
|
|
||||||
|
|
||||||
memset(out, 0, sizeof(mg_health_record.data));
|
memset(out, 0, sizeof(mg_health_record.data));
|
||||||
out[n++] = s.exc_pc;
|
if (s == NULL) return;
|
||||||
|
if ((rr != ESP_RST_PANIC && rr != ESP_RST_INT_WDT &&
|
||||||
|
rr != ESP_RST_TASK_WDT) // Not a crash caught by the panic handler
|
||||||
|
|| esp_core_dump_get_summary(s) != ESP_OK) {
|
||||||
|
free(s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
out[n++] = s->exc_pc;
|
||||||
|
memcpy(&out[n], s->exc_task, 16), n += 4;
|
||||||
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
#if CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||||
for (uint32_t i = 0; i < s.exc_bt_info.depth && n < max; i++) {
|
out[n++] = s->ex_info.exc_a[0];
|
||||||
if (s.exc_bt_info.bt[i] != s.exc_pc) out[n++] = s.exc_bt_info.bt[i];
|
out[n++] = s->ex_info.exc_a[1];
|
||||||
}
|
size_t len = s->exc_bt_info.depth;
|
||||||
|
if (len > MG_HEALTH_DATA_SIZE - n) len = (MG_HEALTH_DATA_SIZE - n);
|
||||||
|
memcpy(&out[n], s->exc_bt_info.bt, 4 * len); // sizeof(uint32_t)
|
||||||
#elif CONFIG_IDF_TARGET_ARCH_RISCV
|
#elif CONFIG_IDF_TARGET_ARCH_RISCV
|
||||||
// No unwinder: take the return address, then words on the stack that
|
out[n++] = s->ex_info.exc_ra;
|
||||||
// point into executable memory (a heuristic, may include stale values)
|
out[n++] = s->ex_info.exc_sp;
|
||||||
if (n < max) out[n++] = s.ex_info.ra;
|
size_t len = s->exc_bt_info.dump_size / 4 ; // sizeof(uint32_t)
|
||||||
for (uint32_t i = 0; i + 4 <= s.exc_bt_info.dump_size && n < max; i += 4) {
|
if (len > MG_HEALTH_DATA_SIZE - n) len = (MG_HEALTH_DATA_SIZE - n);
|
||||||
uint32_t w;
|
memcpy(&out[n], s->exc_bt_info.stackdump, 4 * len);
|
||||||
memcpy(&w, &s.exc_bt_info.stackdump[i], sizeof(w)); // Byte array
|
|
||||||
if (esp_ptr_executable((void *) (uintptr_t) w)) out[n++] = w;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
free(s);
|
||||||
|
#endif // CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||||
|
#endif // MG_ARCH_ESP32
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) {
|
static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) {
|
||||||
@@ -107,10 +166,16 @@ static size_t print_uint32(void (*fn)(char, void *), void *arg, va_list *ap) {
|
|||||||
size_t mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) {
|
size_t mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) {
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
if (mg_health_valid() &&
|
if (mg_health_valid() &&
|
||||||
mg_health_record.reset_reason == MG_HEALTH_RESET_FAULT) {
|
(mg_health_record.reset_reason == (uint16_t) MG_HEALTH_RESET_FAULT
|
||||||
|
#if MG_ARCH == MG_ARCH_ESP32
|
||||||
|
|| mg_health_record.reset_reason == (uint16_t) MG_HEALTH_RESET_WATCHDOG
|
||||||
|
#endif
|
||||||
|
)) {
|
||||||
size_t n = sizeof(mg_health_record.data) / sizeof(mg_health_record.data[0]);
|
size_t n = sizeof(mg_health_record.data) / sizeof(mg_health_record.data[0]);
|
||||||
len += mg_xprintf(fn, arg, "{%m:%hhu,%m:%u,%m:[%M]}", //
|
len += mg_xprintf(fn, arg, "{%m:%hhu,%m:%hu,%m:%hu,%m:%u,%m:[%M]}", //
|
||||||
MG_ESC("version"), mg_health_record.magic[3], //
|
MG_ESC("version"), mg_health_record.magic[3], //
|
||||||
|
MG_ESC("arch"), mg_health_record.arch, //
|
||||||
|
MG_ESC("reason"), mg_health_record.reset_reason, //
|
||||||
MG_ESC("cpuid"), mg_health_record.cpuid, //
|
MG_ESC("cpuid"), mg_health_record.cpuid, //
|
||||||
MG_ESC("data"), print_uint32, n, mg_health_record.data);
|
MG_ESC("data"), print_uint32, n, mg_health_record.data);
|
||||||
} else {
|
} else {
|
||||||
@@ -119,3 +184,4 @@ size_t mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) {
|
|||||||
(void) ap;
|
(void) ap;
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
+80
-5
@@ -5,6 +5,8 @@
|
|||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
#include "printf.h"
|
#include "printf.h"
|
||||||
|
|
||||||
|
#if MG_ENABLE_HEALTH
|
||||||
|
|
||||||
#ifndef MG_HEALTH_MAGIC
|
#ifndef MG_HEALTH_MAGIC
|
||||||
#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
|
||||||
@@ -13,13 +15,15 @@
|
|||||||
#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) || defined(STM32C5))
|
|
||||||
#define MG_HEALTH_DATA_SIZE 250
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MG_HEALTH_DATA_SIZE
|
#ifndef MG_HEALTH_DATA_SIZE
|
||||||
|
#if (defined(STM32H7_DEV_ID) || defined(STM32H5)) || defined(STM32C5)
|
||||||
|
#define MG_HEALTH_DATA_SIZE 250
|
||||||
|
#elif MG_ARCH == MG_ARCH_ESP32
|
||||||
|
#define MG_HEALTH_DATA_SIZE 23
|
||||||
|
#else
|
||||||
#define MG_HEALTH_DATA_SIZE 10
|
#define MG_HEALTH_DATA_SIZE 10
|
||||||
#endif
|
#endif
|
||||||
|
#endif // MG_HEALTH_DATA_SIZE
|
||||||
|
|
||||||
// Places the record in RAM that survives a reset. Define in mongoose_config.h
|
// Places the record in RAM that survives a reset. Define in mongoose_config.h
|
||||||
// to match the linker script, e.g. __attribute__((section(".mg_health")))
|
// to match the linker script, e.g. __attribute__((section(".mg_health")))
|
||||||
@@ -27,6 +31,13 @@
|
|||||||
#define MG_HEALTH_RAM
|
#define MG_HEALTH_RAM
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// CPU core architecture
|
||||||
|
enum mg_health_arch {
|
||||||
|
MG_HEALTH_ARCH_ARM = 0,
|
||||||
|
MG_HEALTH_ARCH_XTENSA,
|
||||||
|
MG_HEALTH_ARCH_RISCV
|
||||||
|
};
|
||||||
|
|
||||||
// Why the device restarted. Hardware usually reports several flags at once,
|
// Why the device restarted. Hardware usually reports several flags at once,
|
||||||
// so these are ordered by how much they tell you: the most specific cause a
|
// so these are ordered by how much they tell you: the most specific cause a
|
||||||
// device reports wins
|
// device reports wins
|
||||||
@@ -43,7 +54,8 @@ enum mg_health_reason {
|
|||||||
|
|
||||||
struct mg_health {
|
struct mg_health {
|
||||||
char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data
|
char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data
|
||||||
uint32_t reset_reason; // enum mg_health_reason that started this boot
|
uint16_t reset_reason; // enum mg_health_reason that started this boot
|
||||||
|
uint16_t arch; // enum mg_health_arch, core architecture (ARM, etc)
|
||||||
uint32_t cpuid; // ARM CPU ID
|
uint32_t cpuid; // ARM CPU ID
|
||||||
uint32_t data[MG_HEALTH_DATA_SIZE];
|
uint32_t data[MG_HEALTH_DATA_SIZE];
|
||||||
};
|
};
|
||||||
@@ -76,6 +88,69 @@ static inline const char *mg_health_reason_str(int reason) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if MG_ARCH == MG_ARCH_ESP32
|
||||||
|
#ifndef CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||||
|
#define CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH 0
|
||||||
|
#endif
|
||||||
|
#ifndef ESP_PARTITION_SUBTYPE_DATA_COREDUMP
|
||||||
|
#define ESP_PARTITION_SUBTYPE_DATA_COREDUMP 0x03
|
||||||
|
#endif
|
||||||
|
// clang-format off
|
||||||
|
static inline uint16_t esp2mg(esp_reset_reason_t rr) {
|
||||||
|
switch(rr) {
|
||||||
|
case ESP_RST_POWERON: return (uint16_t) MG_HEALTH_RESET_POWER;
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 2)
|
||||||
|
case ESP_RST_PWR_GLITCH:
|
||||||
|
#endif
|
||||||
|
case ESP_RST_BROWNOUT: return (uint16_t) MG_HEALTH_RESET_BROWNOUT;
|
||||||
|
case ESP_RST_EXT: return (uint16_t) MG_HEALTH_RESET_PIN;
|
||||||
|
case ESP_RST_SW: return (uint16_t) MG_HEALTH_RESET_SOFTWARE;
|
||||||
|
case ESP_RST_DEEPSLEEP: return (uint16_t) MG_HEALTH_RESET_LOWPOWER;
|
||||||
|
case ESP_RST_INT_WDT:
|
||||||
|
case ESP_RST_TASK_WDT:
|
||||||
|
case ESP_RST_WDT: return (uint16_t) MG_HEALTH_RESET_WATCHDOG;
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 2, 2)
|
||||||
|
case ESP_RST_CPU_LOCKUP:
|
||||||
|
#endif
|
||||||
|
case ESP_RST_PANIC: return (uint16_t) MG_HEALTH_RESET_FAULT;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
return (uint16_t) MG_HEALTH_RESET_UNKNOWN;
|
||||||
|
}
|
||||||
|
// clang-format on
|
||||||
|
#endif
|
||||||
|
|
||||||
#define MG_HEALTH_INIT() mg_health_init()
|
#define MG_HEALTH_INIT() mg_health_init()
|
||||||
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
|
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
|
||||||
extern void mg_health_init(void);
|
extern void mg_health_init(void);
|
||||||
|
extern bool mg_health_coredump_begin(size_t *size);
|
||||||
|
extern bool mg_health_coredump_read(size_t offset, void *buf, size_t *len);
|
||||||
|
extern bool mg_health_coredump_end(void);
|
||||||
|
#else
|
||||||
|
#define MG_HEALTH_INIT()
|
||||||
|
static inline int mg_health_reason(void) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static inline const char *mg_health_reason_str(int reason) {
|
||||||
|
(void) reason;
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
static inline size_t mg_print_crash_record(mg_pfn_t fn, void *arg,
|
||||||
|
va_list *ap) {
|
||||||
|
(void) ap;
|
||||||
|
return mg_xprintf(fn, arg, "{}");
|
||||||
|
}
|
||||||
|
static inline bool mg_health_coredump_begin(size_t *size) {
|
||||||
|
(void) size;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
static inline bool mg_health_coredump_read(size_t offset, void *buf,
|
||||||
|
size_t *len) {
|
||||||
|
(void) offset, (void) buf, (void) len;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
static inline bool mg_health_coredump_end(void) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
+31
-41
@@ -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 {
|
} else if (!mg_http_stream_body(c, hm, serve_upload_cb, fd)) {
|
||||||
mg_http_stream_body(c, hm, serve_upload_cb, fd);
|
mg_fs_close(fd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1014,7 +1014,6 @@ struct mg_upload_state {
|
|||||||
size_t received; // Number of body bytes received so far
|
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
|
||||||
@@ -1027,13 +1026,13 @@ static void mg_stream_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
(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->finished) {
|
if (us->received < us->expected) {
|
||||||
// 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->finished = true; // MG_EV_ERROR is followed by MG_EV_CLOSE
|
us->received = us->expected; // MG_EV_ERROR is followed by MG_EV_CLOSE
|
||||||
fn(NULL, NULL, &us->user_data);
|
fn(NULL, NULL, &us->user_data);
|
||||||
}
|
}
|
||||||
} else if (ev == MG_EV_READ && !c->is_draining && !us->finished) {
|
} else if (ev == MG_EV_READ && !c->is_draining) {
|
||||||
if (!us->failed && c->recv.len > 0 && us->received < us->expected) {
|
if (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;
|
||||||
@@ -1044,13 +1043,10 @@ 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->finished && (us->failed || us->received >= us->expected)) {
|
if (us->received >= us->expected) {
|
||||||
// Finished upload. Always call cb, even after error, so it can clean up
|
// 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 && us->failed) {
|
if (!c->is_client) {
|
||||||
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");
|
||||||
}
|
}
|
||||||
@@ -1059,54 +1055,48 @@ static void mg_stream_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
|
bool mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
bool (*cb)(struct mg_http_message *, struct mg_str *,
|
bool (*cb)(struct mg_http_message *, struct mg_str *,
|
||||||
void **),
|
void **user_data),
|
||||||
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;
|
||||||
int code = 0;
|
struct mg_str *te = mg_http_get_header(hm, "Transfer-Encoding");
|
||||||
size_t head_len = hm->head.len;
|
bool handled = false;
|
||||||
|
|
||||||
// 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 (!c->is_client && mg_strcmp(hm->method, mg_str("POST")) != 0 &&
|
if (hm->body.len == (size_t) ~0 ||
|
||||||
|
(te != NULL && mg_strcasecmp(*te, mg_str("chunked")) == 0)) {
|
||||||
|
// Unknown length or chunked, cannot stream. Leave it to the caller
|
||||||
|
} else if (!c->is_client &&
|
||||||
|
mg_strcmp(hm->method, mg_str("POST")) != 0 &&
|
||||||
mg_strcmp(hm->method, mg_str("PUT")) != 0) {
|
mg_strcmp(hm->method, mg_str("PUT")) != 0) {
|
||||||
code = 405;
|
// Not an upload request. Leave it to the caller
|
||||||
} else if (mg_http_get_header(hm, "Transfer-Encoding") != NULL) {
|
|
||||||
code = 400;
|
|
||||||
} else if (hm->body.len == (size_t) ~0 ||
|
|
||||||
(!c->is_client && mg_http_get_header(hm, "Content-Length") == NULL)) {
|
|
||||||
code = 411;
|
|
||||||
} else if (sizeof(*us) > sizeof(c->data)) {
|
} else if (sizeof(*us) > sizeof(c->data)) {
|
||||||
code = 500;
|
if (!c->is_client) { // Nobody to reply to on a client connection
|
||||||
}
|
c->pfn = NULL;
|
||||||
c->pfn = NULL; // Take ownership even when validation fails
|
mg_http_reply(c, 500, NULL, "data too small\n");
|
||||||
if (code != 0) {
|
|
||||||
c->fn = NULL;
|
|
||||||
c->fn_data = NULL;
|
|
||||||
mg_iobuf_del(&c->recv, 0, c->recv.len);
|
|
||||||
cb(NULL, NULL, &user_data); // Release resources even if start was skipped
|
|
||||||
if (c->is_client) {
|
|
||||||
mg_error(c, "%s", mg_http_status_code_str(code));
|
|
||||||
} else {
|
|
||||||
mg_http_reply(c, code, code == 405 ? "Allow: POST, PUT\r\n" : NULL, "");
|
|
||||||
}
|
|
||||||
c->is_draining = 1;
|
c->is_draining = 1;
|
||||||
|
cb(NULL, NULL, &user_data); // Let cb release user_data, start skipped
|
||||||
|
handled = true;
|
||||||
|
}
|
||||||
} 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 (!c->is_client && !us->failed && !us->finished &&
|
if (mg_http_get_header(hm, "Expect") != NULL) {
|
||||||
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, head_len); // Delete HTTP headers
|
mg_iobuf_del(&c->recv, 0, hm->head.len); // Delete HTTP headers
|
||||||
|
c->fn = mg_stream_handler; // We own the connection from now
|
||||||
|
c->fn_data = (void *) (ptrdiff_t) cb;
|
||||||
mg_call(c, MG_EV_READ, &c->recv.len); // Process the body bytes we have
|
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) {
|
||||||
|
|||||||
+11
-14
@@ -205,32 +205,29 @@ int mg_http_status(const struct mg_http_message *hm);
|
|||||||
|
|
||||||
// Streams a POST/PUT body to a callback as it arrives, without buffering it.
|
// 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(). Always takes over the connection. Invalid requests receive
|
// with mg_match(). Returns true if it took over the connection, false if the
|
||||||
// an HTTP error; client-side failures call mg_error(). Replies and connection
|
// request is not POST/PUT, is chunked, or has no Content-Length.
|
||||||
// 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 exactly once, including validation
|
// - at end: both NULL. Always called once, also when the connection drops
|
||||||
// failures before start and connections that drop early. Release resources
|
// early, so release resources here. A truncated body looks like a complete
|
||||||
// here. A truncated body looks like a complete one: validate before
|
// one: validate before committing.
|
||||||
// committing.
|
// Return true on success, false on error.
|
||||||
// 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:
|
||||||
// The existing event handler is no longer called. hm is invalid after this
|
// After it returns true, this function hijacks the connection. Existing event
|
||||||
// call; c->data and c->fn_data are reserved for streaming state.
|
// handler not called, hm is invalid, c->data and c->fn_data are modified
|
||||||
void mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
|
bool mg_http_stream_body(struct mg_connection *c, struct mg_http_message *hm,
|
||||||
bool (*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);
|
||||||
+2
-2
@@ -11,7 +11,7 @@
|
|||||||
#define MGI_802154_DATA (125 - MGI_802154_HDR)
|
#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 mg_l2_802154_tx(struct mg_tcpip_if *ifp, const void *buf, size_t len,
|
||||||
size_t (*send_fn)(const uint8_t *, size_t,
|
size_t (*send)(const uint8_t *, size_t,
|
||||||
struct mg_tcpip_if *)) {
|
struct mg_tcpip_if *)) {
|
||||||
struct mg_l2_802154_data *d =
|
struct mg_l2_802154_data *d =
|
||||||
(struct mg_l2_802154_data *) ifp->driver_data;
|
(struct mg_l2_802154_data *) ifp->driver_data;
|
||||||
@@ -29,7 +29,7 @@ size_t mg_l2_802154_tx(struct mg_tcpip_if *ifp, const void *buf, size_t len,
|
|||||||
f[12] = m[2], f[13] = m[1], f[14] = m[0]; // Little endian EUI-64
|
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);
|
f[15] = d->tag, f[16] = (uint8_t) ((i << 4) | last);
|
||||||
memcpy(f + MGI_802154_HDR, p + i * MGI_802154_DATA, n);
|
memcpy(f + MGI_802154_HDR, p + i * MGI_802154_DATA, n);
|
||||||
if (send_fn(f, MGI_802154_HDR + n, ifp) == 0) return 0;
|
if (send(f, MGI_802154_HDR + n, ifp) == 0) return 0;
|
||||||
}
|
}
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|||||||
+33
@@ -89,6 +89,7 @@ static void mg_mdash_fn(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
}
|
}
|
||||||
mg_iobuf_free(&io);
|
mg_iobuf_free(&io);
|
||||||
} else if (ev == MG_EV_CLOSE) {
|
} else if (ev == MG_EV_CLOSE) {
|
||||||
|
mg_health_coredump_end();
|
||||||
mg_mdash_conn = NULL;
|
mg_mdash_conn = NULL;
|
||||||
MG_INFO(("disconnected, ram %lu", 0));
|
MG_INFO(("disconnected, ram %lu", 0));
|
||||||
}
|
}
|
||||||
@@ -146,6 +147,35 @@ static void mg_mdash_rpc_ota_end(struct mg_rpc_req *r) {
|
|||||||
// MG_ESC("percent"));
|
// MG_ESC("percent"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rpc_coredump_begin(struct mg_rpc_req *r) {
|
||||||
|
size_t size = 0;
|
||||||
|
if (mg_health_coredump_begin(&size)) {
|
||||||
|
mg_rpc_ok(r, "{%m:%llu}", MG_ESC("size"), (uint64_t) size);
|
||||||
|
} else {
|
||||||
|
mg_rpc_err(r, 500, "%m", MG_ESC("failed"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rpc_coredump_read(struct mg_rpc_req *r) {
|
||||||
|
uint8_t buf[768];
|
||||||
|
size_t len = sizeof(buf);
|
||||||
|
size_t offset = (size_t) mg_json_get_long(r->frame, "$.params.offset", -1);
|
||||||
|
if (mg_health_coredump_read(offset, buf, &len)) {
|
||||||
|
mg_rpc_ok(r, "{%m:\"%M\"}", MG_ESC("data"), mg_print_base64,
|
||||||
|
(int) len, buf);
|
||||||
|
} else {
|
||||||
|
mg_rpc_err(r, 500, "%m", MG_ESC("failed"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rpc_coredump_end(struct mg_rpc_req *r) {
|
||||||
|
if (mg_health_coredump_end()) {
|
||||||
|
mg_rpc_ok(r, "true");
|
||||||
|
} else {
|
||||||
|
mg_rpc_err(r, 500, "%m", MG_ESC("failed"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void mg_mdash_init(struct mg_mgr *mgr) {
|
void mg_mdash_init(struct mg_mgr *mgr) {
|
||||||
MG_INFO(("Initialising mdash agent, key [%s]", MG_MDASH_KEY));
|
MG_INFO(("Initialising mdash agent, key [%s]", MG_MDASH_KEY));
|
||||||
mg_rpc_add(&mgr->rpcs, mg_str("RPC.List"), mg_rpc_list, NULL);
|
mg_rpc_add(&mgr->rpcs, mg_str("RPC.List"), mg_rpc_list, NULL);
|
||||||
@@ -153,6 +183,9 @@ void mg_mdash_init(struct mg_mgr *mgr) {
|
|||||||
mg_rpc_add(&mgr->rpcs, mg_str("OTA.Begin"), mg_mdash_rpc_ota_begin, NULL);
|
mg_rpc_add(&mgr->rpcs, mg_str("OTA.Begin"), mg_mdash_rpc_ota_begin, NULL);
|
||||||
mg_rpc_add(&mgr->rpcs, mg_str("OTA.Write"), mg_mdash_rpc_ota_write, NULL);
|
mg_rpc_add(&mgr->rpcs, mg_str("OTA.Write"), mg_mdash_rpc_ota_write, NULL);
|
||||||
mg_rpc_add(&mgr->rpcs, mg_str("OTA.End"), mg_mdash_rpc_ota_end, NULL);
|
mg_rpc_add(&mgr->rpcs, mg_str("OTA.End"), mg_mdash_rpc_ota_end, NULL);
|
||||||
|
mg_rpc_add(&mgr->rpcs, mg_str("CoreDump.Begin"), rpc_coredump_begin, NULL);
|
||||||
|
mg_rpc_add(&mgr->rpcs, mg_str("CoreDump.Read"), rpc_coredump_read, NULL);
|
||||||
|
mg_rpc_add(&mgr->rpcs, mg_str("CoreDump.End"), rpc_coredump_end, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void mg_mdash_poll(struct mg_mgr *mgr) {
|
void mg_mdash_poll(struct mg_mgr *mgr) {
|
||||||
|
|||||||
+17
-119
@@ -609,36 +609,6 @@ static void l2addr_resolved(struct mg_connection *c);
|
|||||||
static uint8_t *get_return_l2addr(struct mg_tcpip_if *ifp, struct mg_addr *rem,
|
static uint8_t *get_return_l2addr(struct mg_tcpip_if *ifp, struct mg_addr *rem,
|
||||||
bool is_udp, struct pkt *pkt);
|
bool is_udp, struct pkt *pkt);
|
||||||
|
|
||||||
#if MG_ENABLE_BSD_SOCKETS
|
|
||||||
static uint8_t *sendto_cache_find(struct mg_tcpip_if *ifp, uint32_t ip) {
|
|
||||||
struct mg_tcpip_sendto_entry *a;
|
|
||||||
for (a = ifp->sendto; a < ifp->sendto + MG_TCPIP_SENDTO_CACHE_SIZE; a++) {
|
|
||||||
if (a->ip == ip) return (uint8_t *) &a->addr;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sendto_cache_add(struct mg_tcpip_if *ifp, uint32_t ip,
|
|
||||||
const uint8_t *addr) {
|
|
||||||
struct mg_tcpip_sendto_entry *a;
|
|
||||||
for (a = ifp->sendto; a < ifp->sendto + MG_TCPIP_SENDTO_CACHE_SIZE; a++) {
|
|
||||||
if (a->ip == ip) return; // already cached, skip
|
|
||||||
if (a->ip == 0) break; // not present, first empty slot
|
|
||||||
}
|
|
||||||
if (a == ifp->sendto + MG_TCPIP_SENDTO_CACHE_SIZE) { // no room
|
|
||||||
memmove(ifp->sendto, ifp->sendto + 1,
|
|
||||||
sizeof(ifp->sendto[0]) * (MG_TCPIP_SENDTO_CACHE_SIZE - 1));
|
|
||||||
a--; // make room and point to last entry (just made)
|
|
||||||
}
|
|
||||||
a->ip = ip; // populate this entry
|
|
||||||
memcpy(&a->addr, addr, sizeof(a->addr));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sendto_cache_clear(struct mg_tcpip_if *ifp) {
|
|
||||||
memset(ifp->sendto, 0, sizeof(ifp->sendto));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// RFC826, ARP assumes Ethernet MAC addresses
|
// RFC826, ARP assumes Ethernet MAC addresses
|
||||||
static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||||
if (pkt->arp->op == mg_htons(1) && pkt->arp->tpa == ifp->ip) {
|
if (pkt->arp->op == mg_htons(1) && pkt->arp->tpa == ifp->ip) {
|
||||||
@@ -1244,57 +1214,30 @@ static void proto_error(struct mg_connection *c, bool r, const char *f, ...) {
|
|||||||
static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
static bool rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||||
struct mg_connection *c = getpeer(ifp->mgr, pkt, true);
|
struct mg_connection *c = getpeer(ifp->mgr, pkt, true);
|
||||||
struct connstate *s;
|
struct connstate *s;
|
||||||
struct mg_addr rem;
|
|
||||||
uint8_t *l2addr;
|
uint8_t *l2addr;
|
||||||
size_t len = pkt->pay.len;
|
|
||||||
if (c == NULL) return false; // No UDP listener on this port
|
if (c == NULL) return false; // No UDP listener on this port
|
||||||
s = (struct connstate *) (c + 1);
|
s = (struct connstate *) (c + 1);
|
||||||
|
c->rem.port = pkt->udp->sport;
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
if (c->loc.is_ip6) { // matching of v4/v6 to dest is done bt getpeer()
|
if (c->loc.is_ip6) { // matching of v4/v6 to dest is done bt getpeer()
|
||||||
if (!udp6csum_ok(pkt->ip6, pkt->udp)) return false;
|
if (!udp6csum_ok(pkt->ip6, pkt->udp)) return false;
|
||||||
if (c->is_bsd && c->is_client &&
|
c->rem.addr.ip6[0] = pkt->ip6->src[0],
|
||||||
(c->rem.port != pkt->udp->sport ||
|
c->rem.addr.ip6[1] = pkt->ip6->src[1], c->rem.is_ip6 = true;
|
||||||
!MG_IP6MATCH(c->rem.addr.ip6, pkt->ip6->src)))
|
|
||||||
return true;
|
|
||||||
rem.addr.ip6[0] = pkt->ip6->src[0],
|
|
||||||
rem.addr.ip6[1] = pkt->ip6->src[1], rem.is_ip6 = true;
|
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (!udpcsum_ok(pkt->ip, pkt->udp)) return false;
|
if (!udpcsum_ok(pkt->ip, pkt->udp)) return false;
|
||||||
if (c->is_bsd && c->is_client &&
|
c->rem.addr.ip4 = pkt->ip->src;
|
||||||
(c->rem.port != pkt->udp->sport || c->rem.addr.ip4 != pkt->ip->src))
|
|
||||||
return true;
|
|
||||||
rem.addr.ip4 = pkt->ip->src, rem.is_ip6 = false;
|
|
||||||
}
|
}
|
||||||
rem.port = pkt->udp->sport;
|
if ((l2addr = get_return_l2addr(ifp, &c->rem, true, pkt)) == NULL)
|
||||||
if (!c->is_bsd || c->is_client) c->rem = rem;
|
|
||||||
if ((l2addr = get_return_l2addr(ifp, &rem, true, pkt)) == NULL)
|
|
||||||
return false; // safety net for lousy networks
|
return false; // safety net for lousy networks
|
||||||
if (!c->is_bsd || c->is_client) memcpy(s->mac, l2addr, sizeof(s->mac));
|
memcpy(s->mac, l2addr, sizeof(s->mac));
|
||||||
#if MG_ENABLE_BSD_SOCKETS
|
|
||||||
if (c->is_bsd && !rem.is_ip6 && MG_IPISLOCAL(ifp, rem.addr.ip4))
|
|
||||||
sendto_cache_add(ifp, rem.addr.ip4, l2addr);
|
|
||||||
#endif
|
|
||||||
#if MG_ENABLE_BSD_SOCKETS
|
|
||||||
if (c->is_bsd) len += sizeof(struct mg_bsd_udp_frame);
|
|
||||||
#endif
|
|
||||||
if (c->recv.len >= MG_MAX_RECV_SIZE) {
|
if (c->recv.len >= MG_MAX_RECV_SIZE) {
|
||||||
proto_error(c, false, "max_recv_buf_size reached");
|
proto_error(c, false, "max_recv_buf_size reached");
|
||||||
} else if (c->recv.size - c->recv.len < len &&
|
} else if (c->recv.size - c->recv.len < pkt->pay.len &&
|
||||||
!mg_iobuf_resize(&c->recv, c->recv.len + len)) {
|
!mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) {
|
||||||
proto_error(c, false, "oom");
|
proto_error(c, false, "oom");
|
||||||
} else {
|
} else {
|
||||||
#if MG_ENABLE_BSD_SOCKETS
|
|
||||||
if (c->is_bsd) {
|
|
||||||
struct mg_bsd_udp_frame *h =
|
|
||||||
(struct mg_bsd_udp_frame *) &c->recv.buf[c->recv.len];
|
|
||||||
h->ip = rem.addr.ip4;
|
|
||||||
h->port = rem.port;
|
|
||||||
h->len = (uint16_t) pkt->pay.len;
|
|
||||||
c->recv.len += sizeof(*h);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len);
|
memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len);
|
||||||
c->recv.len += pkt->pay.len;
|
c->recv.len += pkt->pay.len;
|
||||||
mg_call(c, MG_EV_READ, &pkt->pay.len);
|
mg_call(c, MG_EV_READ, &pkt->pay.len);
|
||||||
@@ -2347,9 +2290,6 @@ void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) {
|
|||||||
|
|
||||||
static void close_conn(struct mg_connection *c) {
|
static void close_conn(struct mg_connection *c) {
|
||||||
struct connstate *s = (struct connstate *) (c + 1);
|
struct connstate *s = (struct connstate *) (c + 1);
|
||||||
#if MG_ENABLE_BSD_SOCKETS
|
|
||||||
if (c->is_bsd && c->is_udp) sendto_cache_clear(c->mgr->ifp);
|
|
||||||
#endif
|
|
||||||
mg_iobuf_free(&s->txq);
|
mg_iobuf_free(&s->txq);
|
||||||
mg_close_conn(c);
|
mg_close_conn(c);
|
||||||
}
|
}
|
||||||
@@ -2371,11 +2311,6 @@ static void send_syn(struct mg_connection *c) {
|
|||||||
static void l2addr_resolved(struct mg_connection *c) {
|
static void l2addr_resolved(struct mg_connection *c) {
|
||||||
if (c->is_udp) {
|
if (c->is_udp) {
|
||||||
c->is_connecting = 0;
|
c->is_connecting = 0;
|
||||||
#if MG_ENABLE_BSD_SOCKETS
|
|
||||||
if (c->is_bsd)
|
|
||||||
sendto_cache_add(c->mgr->ifp, c->rem.addr.ip4,
|
|
||||||
((struct connstate *) (c + 1))->mac);
|
|
||||||
#endif
|
|
||||||
mg_call(c, MG_EV_CONNECT, NULL);
|
mg_call(c, MG_EV_CONNECT, NULL);
|
||||||
} else {
|
} else {
|
||||||
send_syn(c);
|
send_syn(c);
|
||||||
@@ -2390,25 +2325,20 @@ void mg_connect_resolved(struct mg_connection *c) {
|
|||||||
s->retransmit = ifp->enable_tcp_retransmit;
|
s->retransmit = ifp->enable_tcp_retransmit;
|
||||||
s->txq.align = MG_TCPIP_TXQ_ALIGN;
|
s->txq.align = MG_TCPIP_TXQ_ALIGN;
|
||||||
c->is_resolving = 0;
|
c->is_resolving = 0;
|
||||||
if (c->loc.port == 0) {
|
if (ifp->eport < MG_EPHEMERAL_PORT_BASE) ifp->eport = MG_EPHEMERAL_PORT_BASE;
|
||||||
if (ifp->eport < MG_EPHEMERAL_PORT_BASE)
|
|
||||||
ifp->eport = MG_EPHEMERAL_PORT_BASE;
|
|
||||||
c->loc.port = mg_htons(ifp->eport++);
|
c->loc.port = mg_htons(ifp->eport++);
|
||||||
} // otherwise retain an existing bound port
|
|
||||||
#if MG_ENABLE_IPV6
|
#if MG_ENABLE_IPV6
|
||||||
if (c->rem.is_ip6) {
|
if (c->rem.is_ip6) {
|
||||||
if (c->loc.addr.ip6[0] == 0 && c->loc.addr.ip6[1] == 0 &&
|
if (c->rem.addr.ip6[0] == ifp->ip6ll[0]) { // same local LAN, use ll
|
||||||
c->rem.addr.ip6[0] == ifp->ip6ll[0]) { // same local LAN, use ll
|
|
||||||
c->loc.addr.ip6[0] = ifp->ip6ll[0], c->loc.addr.ip6[1] = ifp->ip6ll[1];
|
c->loc.addr.ip6[0] = ifp->ip6ll[0], c->loc.addr.ip6[1] = ifp->ip6ll[1];
|
||||||
} else if (c->loc.addr.ip6[0] == 0 && c->loc.addr.ip6[1] == 0) {
|
} else { // use global address
|
||||||
// Use global address
|
|
||||||
c->loc.addr.ip6[0] = ifp->ip6[0], c->loc.addr.ip6[1] = ifp->ip6[1];
|
c->loc.addr.ip6[0] = ifp->ip6[0], c->loc.addr.ip6[1] = ifp->ip6[1];
|
||||||
} // otherwise retain an existing bound address
|
}
|
||||||
c->loc.is_ip6 = true;
|
c->loc.is_ip6 = true;
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (c->loc.addr.ip4 == 0) c->loc.addr.ip4 = ifp->ip;
|
c->loc.addr.ip4 = ifp->ip;
|
||||||
}
|
}
|
||||||
MG_DEBUG(("%lu %M -> %M", c->id, mg_print_ip_port, &c->loc, mg_print_ip_port,
|
MG_DEBUG(("%lu %M -> %M", c->id, mg_print_ip_port, &c->loc, mg_print_ip_port,
|
||||||
&c->rem));
|
&c->rem));
|
||||||
@@ -2439,20 +2369,11 @@ void mg_connect_resolved(struct mg_connection *c) {
|
|||||||
uint32_t rem_ip = c->rem.addr.ip4;
|
uint32_t rem_ip = c->rem.addr.ip4;
|
||||||
if (ifp->ip && MG_IPISLOCAL(ifp, rem_ip) &&
|
if (ifp->ip && MG_IPISLOCAL(ifp, rem_ip) &&
|
||||||
rem_ip != ifp->gw) { // skip if gw (onstatechange -> ARP)
|
rem_ip != ifp->gw) { // skip if gw (onstatechange -> ARP)
|
||||||
// We're in the same LAN
|
// If we're in the same LAN, fire an ARP lookup.
|
||||||
#if MG_ENABLE_BSD_SOCKETS
|
|
||||||
if (c->is_bsd && c->is_udp && // see if we already have L2 data
|
|
||||||
(l2addr = sendto_cache_find(ifp, rem_ip)) != NULL) {
|
|
||||||
memcpy(s->mac, l2addr, sizeof(s->mac));
|
|
||||||
l2addr_resolved(c);
|
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
{ // fire an ARP lookup
|
|
||||||
MG_DEBUG(("%lu ARP lookup...", c->id));
|
MG_DEBUG(("%lu ARP lookup...", c->id));
|
||||||
mg_tcpip_arp_request(ifp, rem_ip, NULL);
|
mg_tcpip_arp_request(ifp, rem_ip, NULL);
|
||||||
settmout(c, MIP_TTYPE_ARP);
|
settmout(c, MIP_TTYPE_ARP);
|
||||||
c->is_arplooking = 1;
|
c->is_arplooking = 1;
|
||||||
}
|
|
||||||
} else if (ifp->gw_ready) {
|
} else if (ifp->gw_ready) {
|
||||||
memcpy(s->mac, ifp->gwmac, sizeof(s->mac));
|
memcpy(s->mac, ifp->gwmac, sizeof(s->mac));
|
||||||
l2addr_resolved(c);
|
l2addr_resolved(c);
|
||||||
@@ -2472,36 +2393,13 @@ bool mg_open_listener(struct mg_connection *c, const char *url) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void write_conn(struct mg_connection *c) {
|
static void write_conn(struct mg_connection *c) {
|
||||||
long len;
|
long len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len)
|
||||||
size_t off = 0;
|
|
||||||
bool empty = false;
|
|
||||||
#if MG_ENABLE_BSD_SOCKETS
|
|
||||||
struct mg_bsd_udp_frame *h;
|
|
||||||
if (c->is_bsd && c->is_udp) {
|
|
||||||
if (c->is_connecting) return;
|
|
||||||
h = (struct mg_bsd_udp_frame *) c->send.buf;
|
|
||||||
assert(c->send.len >= sizeof(*h));
|
|
||||||
assert(h->len <= c->send.len - sizeof(*h));
|
|
||||||
c->rem.port = h->port;
|
|
||||||
c->rem.is_ip6 = false;
|
|
||||||
if (!c->is_client && c->rem.addr.ip4 != h->ip) {
|
|
||||||
c->rem.addr.ip4 = h->ip;
|
|
||||||
mg_connect_resolved(c);
|
|
||||||
if (c->is_connecting) return;
|
|
||||||
}
|
|
||||||
len = mg_io_send(c, h + 1, h->len);
|
|
||||||
off = sizeof(*h), empty = h->len == 0;
|
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len)
|
|
||||||
: mg_io_send(c, c->send.buf, c->send.len);
|
: mg_io_send(c, c->send.buf, c->send.len);
|
||||||
}
|
|
||||||
// TODO(): mg_tls_send() may return 0 forever on steady OOM
|
// TODO(): mg_tls_send() may return 0 forever on steady OOM
|
||||||
if (len == MG_IO_ERR) {
|
if (len == MG_IO_ERR) {
|
||||||
proto_error(c, false, "tx err");
|
proto_error(c, false, "tx err");
|
||||||
} else if (len > 0 || empty) {
|
} else if (len > 0) {
|
||||||
mg_iobuf_del(&c->send, 0, off + (size_t) len);
|
mg_iobuf_del(&c->send, 0, (size_t) len);
|
||||||
mg_call(c, MG_EV_WRITE, &len);
|
mg_call(c, MG_EV_WRITE, &len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,17 +26,6 @@ struct mg_tcpip_driver {
|
|||||||
typedef void (*mg_tcpip_event_handler_t)(struct mg_tcpip_if *ifp, int ev,
|
typedef void (*mg_tcpip_event_handler_t)(struct mg_tcpip_if *ifp, int ev,
|
||||||
void *ev_data);
|
void *ev_data);
|
||||||
|
|
||||||
#if MG_ENABLE_BSD_SOCKETS
|
|
||||||
#ifndef MG_TCPIP_SENDTO_CACHE_SIZE
|
|
||||||
#define MG_TCPIP_SENDTO_CACHE_SIZE 4
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct mg_tcpip_sendto_entry {
|
|
||||||
uint32_t ip;
|
|
||||||
struct mg_l2addr addr;
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
// Events delivered to mg_tcpip_if.fn (user) and mg_tcpip_if.pfn (driver).
|
// Events delivered to mg_tcpip_if.fn (user) and mg_tcpip_if.pfn (driver).
|
||||||
// Each entry shows the ev_data type to cast inside the handler.
|
// Each entry shows the ev_data type to cast inside the handler.
|
||||||
@@ -99,9 +88,6 @@ struct mg_tcpip_if {
|
|||||||
enum mg_l2type l2type; // Layer-2 type: Ethernet, PPP, etc. (see l2.h)
|
enum mg_l2type l2type; // Layer-2 type: Ethernet, PPP, etc. (see l2.h)
|
||||||
union mg_l2data l2data; // Layer-2 config and state (see l2.h)
|
union mg_l2data l2data; // Layer-2 config and state (see l2.h)
|
||||||
char *dns4_url; // Active DNS server URL (set from DHCP or config)
|
char *dns4_url; // Active DNS server URL (set from DHCP or config)
|
||||||
#if MG_ENABLE_BSD_SOCKETS
|
|
||||||
struct mg_tcpip_sendto_entry sendto[MG_TCPIP_SENDTO_CACHE_SIZE]; // Local IPv4 neighbors
|
|
||||||
#endif
|
|
||||||
uint64_t now; // Current time in ms (updated each mg_mgr_poll)
|
uint64_t now; // Current time in ms (updated each mg_mgr_poll)
|
||||||
uint64_t timer_1000ms; // Timestamp of last 1-second tick
|
uint64_t timer_1000ms; // Timestamp of last 1-second tick
|
||||||
uint64_t lease_expire; // DHCP lease expiry time in ms
|
uint64_t lease_expire; // DHCP lease expiry time in ms
|
||||||
|
|||||||
@@ -208,7 +208,9 @@ static void s_firmware_fn(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
(unsigned long) hm->body.len, (unsigned long) s_ota->size);
|
(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));
|
||||||
mg_http_stream_body(c, hm, s_firmware_cb, NULL);
|
if (!mg_http_stream_body(c, hm, s_firmware_cb, NULL)) {
|
||||||
|
mg_error(c, "Cannot stream firmware");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (ev == MG_EV_ERROR) {
|
} else if (ev == MG_EV_ERROR) {
|
||||||
s_ota->fn((char *) ev_data);
|
s_ota->fn((char *) ev_data);
|
||||||
@@ -241,21 +243,6 @@ void mg_ota_url_check(struct mg_mgr *mgr, const char *json_url,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uses *p as a flag: non-NULL means an OTA session is open and must be
|
|
||||||
// closed by mg_ota_end()
|
|
||||||
bool mg_http_ota_cb(struct mg_http_message *hm, struct mg_str *data, void **p) {
|
|
||||||
if (hm != NULL) { // Start
|
|
||||||
if (!mg_ota_begin(hm->body.len)) return false;
|
|
||||||
*p = (void *) (ptrdiff_t) 1;
|
|
||||||
return true;
|
|
||||||
} else if (data != NULL) { // Next chunk
|
|
||||||
return mg_ota_write(data->buf, data->len);
|
|
||||||
}
|
|
||||||
if (*p == NULL) return false; // End. Begin failed, nothing to finish
|
|
||||||
*p = NULL;
|
|
||||||
return mg_ota_end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void mg_ota_poll(struct mg_mgr *mgr) {
|
void mg_ota_poll(struct mg_mgr *mgr) {
|
||||||
static uint64_t t = 5000; // Fire first time 5 sec after boot
|
static uint64_t t = 5000; // Fire first time 5 sec after boot
|
||||||
|
|
||||||
|
|||||||
@@ -80,8 +80,9 @@ 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");
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
|
|||||||
+1
-5
@@ -299,11 +299,7 @@ static long recv_raw(struct mg_connection *c, void *buf, size_t len) {
|
|||||||
union usa usa;
|
union usa usa;
|
||||||
socklen_t slen = tousa(&c->rem, &usa);
|
socklen_t slen = tousa(&c->rem, &usa);
|
||||||
n = recvfrom(FD(c), (char *) buf, len, 0, &usa.sa, &slen);
|
n = recvfrom(FD(c), (char *) buf, len, 0, &usa.sa, &slen);
|
||||||
if (n > 0) {
|
if (n > 0) tomgaddr(&usa, &c->rem, slen != sizeof(usa.sin));
|
||||||
struct mg_addr rem;
|
|
||||||
tomgaddr(&usa, &rem, slen != sizeof(usa.sin));
|
|
||||||
c->rem = rem;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING);
|
n = recv(FD(c), (char *) buf, len, MSG_NONBLOCKING);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -261,9 +261,8 @@ uint64_t mg_ntohll(uint64_t net) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void mg_delayms(unsigned int ms) {
|
void mg_delayms(unsigned int ms) {
|
||||||
uint64_t now, to = mg_millis() + ms + 1;
|
uint64_t to = mg_millis() + ms + 1;
|
||||||
while ((now = mg_millis()) < to)
|
while (mg_millis() < to) (void) 0;
|
||||||
if (now + ms + 1 < to) to = now + ms + 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if MG_ENABLE_CUSTOM_CALLOC
|
#if MG_ENABLE_CUSTOM_CALLOC
|
||||||
|
|||||||
+6
-9
@@ -13,7 +13,7 @@ else
|
|||||||
C_WARN ?= -Wno-deprecated
|
C_WARN ?= -Wno-deprecated
|
||||||
endif
|
endif
|
||||||
|
|
||||||
.PHONY: all bsd bsd_test bsd_client bsd_udp bsd_nortos bsd_nortos_test shm_tap_bridge \
|
.PHONY: all bsd bsd_test bsd_client bsd_nortos bsd_nortos_test shm_tap_bridge \
|
||||||
shm_port_bridge bsd_s390 bsd_nortos_s390 clean
|
shm_port_bridge bsd_s390 bsd_nortos_s390 clean
|
||||||
|
|
||||||
TEST_SOURCES = bsd_shm_test.c
|
TEST_SOURCES = bsd_shm_test.c
|
||||||
@@ -47,11 +47,11 @@ DOCKER ?= $(DOCKER_BIN) run --platform linux/amd64 --net=host --rm $(ENV) -v $(R
|
|||||||
CFLAGS_MONGOOSE += -DMG_ARCH=MG_ARCH_CUSTOM
|
CFLAGS_MONGOOSE += -DMG_ARCH=MG_ARCH_CUSTOM
|
||||||
|
|
||||||
|
|
||||||
all bsd: shm_tap_bridge bsd_client bsd_udp FreeRTOS-Kernel bsd_test
|
all bsd: shm_tap_bridge bsd_client FreeRTOS-Kernel bsd_test
|
||||||
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./bsd_test $(ARGS)
|
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./bsd_test $(ARGS)
|
||||||
|
|
||||||
# also
|
# also
|
||||||
bsd_nortos: shm_tap_bridge bsd_client bsd_udp bsd_nortos_test
|
bsd_nortos: shm_tap_bridge bsd_client bsd_nortos_test
|
||||||
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./bsd_nortos_test $(ARGS)
|
ASAN_OPTIONS=$(ASAN_OPTIONS) $(RUN) ./bsd_nortos_test $(ARGS)
|
||||||
|
|
||||||
# requires shm_tap_bridge to be running
|
# requires shm_tap_bridge to be running
|
||||||
@@ -70,9 +70,6 @@ bsd_nortos_test:
|
|||||||
bsd_client: bsd_client.c
|
bsd_client: bsd_client.c
|
||||||
$(CC) bsd_client.c -pthread -o $@
|
$(CC) bsd_client.c -pthread -o $@
|
||||||
|
|
||||||
bsd_udp: bsd_udp.c
|
|
||||||
$(CC) bsd_udp.c -o $@
|
|
||||||
|
|
||||||
# bridge shm queue <--> TAP interface
|
# bridge shm queue <--> TAP interface
|
||||||
shm_tap_bridge: shm_tap_bridge.c shm_queue.h
|
shm_tap_bridge: shm_tap_bridge.c shm_queue.h
|
||||||
$(CC) shm_tap_bridge.c -pthread -o $@
|
$(CC) shm_tap_bridge.c -pthread -o $@
|
||||||
@@ -82,7 +79,7 @@ bsd_s390: CFLAGS_MONGOOSE += -DMG_ENABLE_BSD_PROTOTYPES=0
|
|||||||
bsd_s390: ASAN=
|
bsd_s390: ASAN=
|
||||||
bsd_s390: CC = $(DOCKER) mdashnet/s390 cc
|
bsd_s390: CC = $(DOCKER) mdashnet/s390 cc
|
||||||
bsd_s390: RUN = $(DOCKER) --init mdashnet/s390
|
bsd_s390: RUN = $(DOCKER) --init mdashnet/s390
|
||||||
bsd_s390: shm_port_bridge bsd_client bsd_udp FreeRTOS-Kernel bsd_test
|
bsd_s390: shm_port_bridge bsd_client FreeRTOS-Kernel bsd_test
|
||||||
$(RUN) sh -c './shm_port_bridge & pid=$$!; sleep 1; ./bsd_test; rc=$$?; kill $$pid >/dev/null 2>&1; wait $$pid >/dev/null 2>&1; exit $$rc'
|
$(RUN) sh -c './shm_port_bridge & pid=$$!; sleep 1; ./bsd_test; rc=$$?; kill $$pid >/dev/null 2>&1; wait $$pid >/dev/null 2>&1; exit $$rc'
|
||||||
|
|
||||||
#also
|
#also
|
||||||
@@ -90,14 +87,14 @@ bsd_nortos_s390: CFLAGS_MONGOOSE += -DMG_ENABLE_BSD_PROTOTYPES=0
|
|||||||
bsd_nortos_s390: ASAN=
|
bsd_nortos_s390: ASAN=
|
||||||
bsd_nortos_s390: CC = $(DOCKER) mdashnet/s390 cc
|
bsd_nortos_s390: CC = $(DOCKER) mdashnet/s390 cc
|
||||||
bsd_nortos_s390: RUN = $(DOCKER) --init mdashnet/s390
|
bsd_nortos_s390: RUN = $(DOCKER) --init mdashnet/s390
|
||||||
bsd_nortos_s390: shm_port_bridge bsd_client bsd_udp bsd_nortos_test
|
bsd_nortos_s390: shm_port_bridge bsd_client bsd_nortos_test
|
||||||
$(RUN) sh -c './shm_port_bridge & pid=$$!; sleep 1; ./bsd_nortos_test; rc=$$?; kill $$pid >/dev/null 2>&1; wait $$pid >/dev/null 2>&1; exit $$rc'
|
$(RUN) sh -c './shm_port_bridge & pid=$$!; sleep 1; ./bsd_nortos_test; rc=$$?; kill $$pid >/dev/null 2>&1; wait $$pid >/dev/null 2>&1; exit $$rc'
|
||||||
|
|
||||||
shm_port_bridge: shm_port_bridge.c shm_queue.h
|
shm_port_bridge: shm_port_bridge.c shm_queue.h
|
||||||
$(CC) shm_port_bridge.c $(CFLAGS) -pthread -o $@
|
$(CC) shm_port_bridge.c $(CFLAGS) -pthread -o $@
|
||||||
|
|
||||||
clean: # Cleanup. Delete built program and all build artifacts
|
clean: # Cleanup. Delete built program and all build artifacts
|
||||||
rm -rf bsd_test bsd_client bsd_udp shm_port_bridge *.o *.obj FreeRTOS-Kernel
|
rm -rf bsd_test bsd_client shm_port_bridge *.o *.obj FreeRTOS-Kernel
|
||||||
|
|
||||||
FreeRTOS-Kernel: # FreeRTOS sources
|
FreeRTOS-Kernel: # FreeRTOS sources
|
||||||
git clone -q -c advice.detachedHead=false --depth 1 -b V11.2.0 https://github.com/FreeRTOS/FreeRTOS-Kernel $@
|
git clone -q -c advice.detachedHead=false --depth 1 -b V11.2.0 https://github.com/FreeRTOS/FreeRTOS-Kernel $@
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
#include <arpa/inet.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#define PORT 1235
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
struct sockaddr_in sa;
|
|
||||||
struct timeval timeout;
|
|
||||||
char buf[8];
|
|
||||||
ssize_t n;
|
|
||||||
int fd, rogue;
|
|
||||||
|
|
||||||
if (argc != 2) return 1;
|
|
||||||
memset(&sa, 0, sizeof(sa));
|
|
||||||
sa.sin_family = AF_INET;
|
|
||||||
sa.sin_port = htons(PORT);
|
|
||||||
if (inet_pton(AF_INET, argv[1], &sa.sin_addr) != 1) return 1;
|
|
||||||
timeout.tv_sec = 5, timeout.tv_usec = 0;
|
|
||||||
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ||
|
|
||||||
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0 ||
|
|
||||||
connect(fd, (struct sockaddr *) &sa, sizeof(sa)) != 0 ||
|
|
||||||
send(fd, "hello", 5, 0) != 5 || recv(fd, buf, sizeof(buf), 0) != 5 ||
|
|
||||||
memcmp(buf, "ready", 5) != 0)
|
|
||||||
goto fail;
|
|
||||||
if ((rogue = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ||
|
|
||||||
setsockopt(rogue, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) !=
|
|
||||||
0 ||
|
|
||||||
send(fd, "one", 3, 0) != 3 ||
|
|
||||||
sendto(rogue, "two2", 4, 0, (struct sockaddr *) &sa, sizeof(sa)) != 4 ||
|
|
||||||
recv(fd, buf, sizeof(buf), 0) != 4 || memcmp(buf, "peer", 4) != 0 ||
|
|
||||||
recv(rogue, buf, sizeof(buf), 0) != 5 || memcmp(buf, "rogue", 5) != 0 ||
|
|
||||||
send(fd, "truncate", 8, 0) != 8 || send(fd, "peek", 4, 0) != 4 ||
|
|
||||||
recv(fd, buf, sizeof(buf), 0) != 4 || memcmp(buf, "more", 4) != 0 ||
|
|
||||||
recv(fd, buf, sizeof(buf), 0) != 3 || memcmp(buf, "one", 3) != 0 ||
|
|
||||||
recv(fd, buf, sizeof(buf), 0) != 4 || memcmp(buf, "two2", 4) != 0 ||
|
|
||||||
recv(fd, buf, sizeof(buf), 0) != 0 ||
|
|
||||||
sendto(rogue, "rogue", 5, 0, (struct sockaddr *) &sa, sizeof(sa)) != 5 ||
|
|
||||||
recv(fd, buf, sizeof(buf), 0) != 7 || memcmp(buf, "checked", 7) != 0 ||
|
|
||||||
send(fd, "echo", 4, 0) != 4 || recv(fd, buf, sizeof(buf), 0) != 4 ||
|
|
||||||
memcmp(buf, "echo", 4) != 0)
|
|
||||||
goto fail_close_rogue;
|
|
||||||
close(rogue);
|
|
||||||
close(fd);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
fail_close_rogue:
|
|
||||||
close(rogue);
|
|
||||||
fail:
|
|
||||||
close(fd);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
+9
-230
@@ -33,175 +33,6 @@ static int s_abort = 0;
|
|||||||
#define DASHBOARD(x) \
|
#define DASHBOARD(x) \
|
||||||
printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false" : "true")
|
printf("HEALTH_DASHBOARD\t\"%s\": %s,\n", x, s_error ? "false" : "true")
|
||||||
|
|
||||||
#define UDP_PORT 1235
|
|
||||||
|
|
||||||
#if MG_ENABLE_FREERTOS
|
|
||||||
static volatile bool s_tx_held, s_tx_waits;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int open_udp(void) {
|
|
||||||
struct sockaddr_in sa;
|
|
||||||
char buf;
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
memset(&sa, 0, sizeof(sa));
|
|
||||||
sa.sin_family = AF_INET;
|
|
||||||
sa.sin_port = htons(UDP_PORT);
|
|
||||||
sa.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
ASSERT((fd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0);
|
|
||||||
ASSERT(bind(fd, (struct sockaddr *) &sa, sizeof(sa)) == 0);
|
|
||||||
ASSERT(send(fd, &buf, sizeof(buf), 0) < 0 && errno == EDESTADDRREQ);
|
|
||||||
ASSERT(fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void check_udp_oversize(struct mg_mgr *mgr, int fd,
|
|
||||||
const struct sockaddr *dst, socklen_t dstlen) {
|
|
||||||
static char buf[1600];
|
|
||||||
size_t len = mgr->ifp->mtu + 1;
|
|
||||||
|
|
||||||
ASSERT(len <= sizeof(buf));
|
|
||||||
if (dst == NULL) {
|
|
||||||
ASSERT(send(fd, buf, len, 0) < 0 && errno == EMSGSIZE);
|
|
||||||
} else {
|
|
||||||
ASSERT(sendto(fd, buf, len, 0, dst, dstlen) < 0 && errno == EMSGSIZE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void udp_exchange(struct mg_mgr *mgr, int fd,
|
|
||||||
void (*progress)(struct mg_mgr *)) {
|
|
||||||
struct sockaddr_in peer, other, rogue = {0};
|
|
||||||
socklen_t peerlen, otherlen;
|
|
||||||
uint64_t deadline;
|
|
||||||
struct timeval zero = {0, 0};
|
|
||||||
struct pollfd p;
|
|
||||||
fd_set r, w, e;
|
|
||||||
bool one;
|
|
||||||
char buf[8];
|
|
||||||
ssize_t n;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
peerlen = sizeof(peer);
|
|
||||||
n = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) &peer, &peerlen);
|
|
||||||
if (n >= 0) break;
|
|
||||||
ASSERT(errno == EAGAIN);
|
|
||||||
progress(mgr);
|
|
||||||
}
|
|
||||||
ASSERT(n == 5 && memcmp(buf, "hello", 5) == 0);
|
|
||||||
ASSERT(sendto(fd, "ready", 5, 0, (struct sockaddr *) &peer, peerlen) ==
|
|
||||||
5);
|
|
||||||
deadline = mg_millis() + 100;
|
|
||||||
while (mg_millis() < deadline) progress(mgr); // Queue both UDP datagrams
|
|
||||||
do {
|
|
||||||
otherlen = sizeof(other);
|
|
||||||
n = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) &other,
|
|
||||||
&otherlen);
|
|
||||||
if (n < 0) {
|
|
||||||
ASSERT(errno == EAGAIN);
|
|
||||||
progress(mgr);
|
|
||||||
}
|
|
||||||
} while (n < 0);
|
|
||||||
one = n == 3;
|
|
||||||
if (one) {
|
|
||||||
ASSERT(memcmp(buf, "one", 3) == 0 && other.sin_port == peer.sin_port);
|
|
||||||
} else {
|
|
||||||
ASSERT(n == 4 && memcmp(buf, "two2", 4) == 0 &&
|
|
||||||
other.sin_port != peer.sin_port);
|
|
||||||
}
|
|
||||||
if (other.sin_port != peer.sin_port) rogue = other;
|
|
||||||
do {
|
|
||||||
otherlen = sizeof(other);
|
|
||||||
n = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) &other,
|
|
||||||
&otherlen);
|
|
||||||
if (n < 0) {
|
|
||||||
ASSERT(errno == EAGAIN);
|
|
||||||
progress(mgr);
|
|
||||||
}
|
|
||||||
} while (n < 0);
|
|
||||||
if (one) {
|
|
||||||
ASSERT(n == 4 && memcmp(buf, "two2", 4) == 0 &&
|
|
||||||
other.sin_port != peer.sin_port);
|
|
||||||
} else {
|
|
||||||
ASSERT(n == 3 && memcmp(buf, "one", 3) == 0 &&
|
|
||||||
other.sin_port == peer.sin_port);
|
|
||||||
}
|
|
||||||
if (other.sin_port != peer.sin_port) rogue = other;
|
|
||||||
check_udp_oversize(mgr, fd, (struct sockaddr *) &peer, peerlen);
|
|
||||||
ASSERT(sendto(fd, "peer", 4, 0, (struct sockaddr *) &peer, peerlen) == 4);
|
|
||||||
ASSERT(sendto(fd, "rogue", 5, 0, (struct sockaddr *) &rogue,
|
|
||||||
sizeof(rogue)) == 5);
|
|
||||||
deadline = mg_millis() + 100;
|
|
||||||
while (mg_millis() < deadline) progress(mgr);
|
|
||||||
ASSERT(connect(fd, (struct sockaddr *) &peer, peerlen) == 0);
|
|
||||||
ASSERT(sendto(fd, "no", 2, 0, (struct sockaddr *) &peer, peerlen) < 0 &&
|
|
||||||
errno == EISCONN);
|
|
||||||
check_udp_oversize(mgr, fd, NULL, 0);
|
|
||||||
ASSERT(send(fd, "more", 4, 0) == 4);
|
|
||||||
deadline = mg_millis() + 100;
|
|
||||||
while (mg_millis() < deadline) progress(mgr); // Queue both UDP datagrams
|
|
||||||
do {
|
|
||||||
p.fd = fd, p.events = POLLIN | POLLOUT, p.revents = 0;
|
|
||||||
ASSERT(poll(&p, 1, 0) == 1);
|
|
||||||
if (!(p.revents & POLLIN)) progress(mgr);
|
|
||||||
} while (!(p.revents & POLLIN));
|
|
||||||
ASSERT(p.revents == (POLLIN | POLLOUT));
|
|
||||||
FD_ZERO(&r);
|
|
||||||
FD_ZERO(&w);
|
|
||||||
FD_ZERO(&e);
|
|
||||||
FD_SET(fd, &r);
|
|
||||||
FD_SET(fd, &w);
|
|
||||||
FD_SET(fd, &e);
|
|
||||||
ASSERT(select(fd + 1, &r, &w, &e, &zero) == 1);
|
|
||||||
ASSERT(FD_ISSET(fd, &r) && FD_ISSET(fd, &w) && !FD_ISSET(fd, &e));
|
|
||||||
do {
|
|
||||||
n = recv(fd, buf, 3, 0);
|
|
||||||
if (n < 0) {
|
|
||||||
ASSERT(errno == EAGAIN);
|
|
||||||
progress(mgr);
|
|
||||||
}
|
|
||||||
} while (n < 0);
|
|
||||||
ASSERT(n == 3 && memcmp(buf, "tru", 3) == 0);
|
|
||||||
do {
|
|
||||||
n = recv(fd, buf, sizeof(buf), MSG_PEEK);
|
|
||||||
if (n < 0) {
|
|
||||||
ASSERT(errno == EAGAIN);
|
|
||||||
progress(mgr);
|
|
||||||
}
|
|
||||||
} while (n < 0);
|
|
||||||
ASSERT(n == 4 && memcmp(buf, "peek", 4) == 0);
|
|
||||||
ASSERT(recv(fd, buf, sizeof(buf), 0) == 4 && memcmp(buf, "peek", 4) == 0);
|
|
||||||
p.events = POLLIN | POLLOUT, p.revents = 0;
|
|
||||||
ASSERT(poll(&p, 1, 0) == 1 && p.revents == POLLOUT);
|
|
||||||
#if MG_ENABLE_FREERTOS
|
|
||||||
s_tx_waits = false, s_tx_held = true;
|
|
||||||
#endif
|
|
||||||
ASSERT(send(fd, "one", 3, 0) == 3);
|
|
||||||
ASSERT(send(fd, "two2", 4, 0) == 4);
|
|
||||||
ASSERT(send(fd, "", 0, 0) == 0);
|
|
||||||
#if MG_ENABLE_FREERTOS
|
|
||||||
while (!s_tx_waits) progress(mgr); // MG_IO_WAIT keeps all records queued
|
|
||||||
s_tx_held = false;
|
|
||||||
#endif
|
|
||||||
deadline = mg_millis() + 100;
|
|
||||||
while (mg_millis() < deadline) progress(mgr); // Let rogue arrive
|
|
||||||
ASSERT(recv(fd, buf, sizeof(buf), MSG_DONTWAIT) < 0 && errno == EAGAIN);
|
|
||||||
while ((n = send(fd, "checked", 7, 0)) < 0) {
|
|
||||||
ASSERT(errno == EAGAIN);
|
|
||||||
progress(mgr);
|
|
||||||
}
|
|
||||||
ASSERT(n == 7);
|
|
||||||
for (;;) {
|
|
||||||
n = recv(fd, buf, sizeof(buf), 0);
|
|
||||||
if (n >= 0) break;
|
|
||||||
ASSERT(errno == EAGAIN);
|
|
||||||
progress(mgr);
|
|
||||||
}
|
|
||||||
ASSERT(n == 4 && memcmp(buf, "echo", 4) == 0);
|
|
||||||
ASSERT(send(fd, "echo", 4, 0) == 4);
|
|
||||||
progress(mgr); // Drain the last queued datagram before close
|
|
||||||
ASSERT(close(fd) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static pid_t start_client(const char *ip, const char *connections) {
|
static pid_t start_client(const char *ip, const char *connections) {
|
||||||
char *cmd[4];
|
char *cmd[4];
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
@@ -215,18 +46,6 @@ static pid_t start_client(const char *ip, const char *connections) {
|
|||||||
return pid;
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static pid_t start_udp(const char *ip) {
|
|
||||||
char *cmd[3];
|
|
||||||
pid_t pid;
|
|
||||||
extern char **environ;
|
|
||||||
|
|
||||||
cmd[0] = (char *) "./bsd_udp";
|
|
||||||
cmd[1] = (char *) ip;
|
|
||||||
cmd[2] = NULL;
|
|
||||||
ASSERT(posix_spawn(&pid, cmd[0], NULL, NULL, cmd, environ) == 0);
|
|
||||||
return pid;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool client_done(pid_t pid, int *status) {
|
static bool client_done(pid_t pid, int *status) {
|
||||||
pid_t r = waitpid(pid, status, WNOHANG);
|
pid_t r = waitpid(pid, status, WNOHANG);
|
||||||
ASSERT(r >= 0);
|
ASSERT(r >= 0);
|
||||||
@@ -241,17 +60,12 @@ static void check_client(int status) {
|
|||||||
static int open_listener(int backlog, bool nonblock) {
|
static int open_listener(int backlog, bool nonblock) {
|
||||||
int lfd = socket(AF_INET, SOCK_STREAM, 0);
|
int lfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
struct sockaddr_in sa;
|
struct sockaddr_in sa;
|
||||||
char buf;
|
|
||||||
|
|
||||||
memset(&sa, 0, sizeof(sa));
|
memset(&sa, 0, sizeof(sa));
|
||||||
sa.sin_family = AF_INET;
|
sa.sin_family = AF_INET;
|
||||||
sa.sin_port = htons(1234);
|
sa.sin_port = htons(1234);
|
||||||
sa.sin_addr.s_addr = INADDR_ANY;
|
sa.sin_addr.s_addr = INADDR_ANY;
|
||||||
ASSERT(lfd >= 0);
|
ASSERT(lfd >= 0);
|
||||||
ASSERT(sendto(lfd, &buf, sizeof(buf), 0, (struct sockaddr *) &sa,
|
|
||||||
sizeof(sa)) < 0 && errno == EOPNOTSUPP);
|
|
||||||
ASSERT(recvfrom(lfd, &buf, sizeof(buf), 0, NULL, NULL) < 0 &&
|
|
||||||
errno == EOPNOTSUPP);
|
|
||||||
ASSERT(bind(lfd, (struct sockaddr *) &sa, sizeof(sa)) == 0);
|
ASSERT(bind(lfd, (struct sockaddr *) &sa, sizeof(sa)) == 0);
|
||||||
ASSERT(listen(lfd, backlog) == 0);
|
ASSERT(listen(lfd, backlog) == 0);
|
||||||
if (nonblock) ASSERT(fcntl(lfd, F_SETFL, O_NONBLOCK) == 0);
|
if (nonblock) ASSERT(fcntl(lfd, F_SETFL, O_NONBLOCK) == 0);
|
||||||
@@ -316,12 +130,11 @@ static volatile bool s_select_waiting, s_select_done;
|
|||||||
static volatile bool s_poll_waiting, s_poll_done;
|
static volatile bool s_poll_waiting, s_poll_done;
|
||||||
static volatile bool s_ready_waiting, s_ready_done;
|
static volatile bool s_ready_waiting, s_ready_done;
|
||||||
static volatile bool s_backlog_ready, s_backlog_full, s_backlog_accept;
|
static volatile bool s_backlog_ready, s_backlog_full, s_backlog_accept;
|
||||||
static volatile bool s_tx_fail, s_tx_error;
|
static volatile bool s_tx_held, s_tx_waits, s_tx_fail, s_tx_error;
|
||||||
static volatile bool s_send_waiting;
|
static volatile bool s_send_waiting;
|
||||||
static volatile bool s_send_done, s_send_expect_error;
|
static volatile bool s_send_done, s_send_expect_error;
|
||||||
static volatile bool s_recv_start, s_recv_waiting, s_recv_done;
|
static volatile bool s_recv_start, s_recv_waiting, s_recv_done;
|
||||||
static volatile bool s_recv_expect_error;
|
static volatile bool s_recv_expect_error;
|
||||||
static volatile bool s_udp_ready, s_udp_done;
|
|
||||||
static size_t (*s_tx)(const void *, size_t, struct mg_tcpip_if *);
|
static size_t (*s_tx)(const void *, size_t, struct mg_tcpip_if *);
|
||||||
|
|
||||||
struct client_data {
|
struct client_data {
|
||||||
@@ -339,7 +152,6 @@ static void select_task(void *args);
|
|||||||
static void readiness_task(void *args);
|
static void readiness_task(void *args);
|
||||||
static void backlog_task(void *args);
|
static void backlog_task(void *args);
|
||||||
static void tx_gate_task(void *args);
|
static void tx_gate_task(void *args);
|
||||||
static void udp_task(void *args);
|
|
||||||
|
|
||||||
struct atask_args {
|
struct atask_args {
|
||||||
bool listener_nonblock;
|
bool listener_nonblock;
|
||||||
@@ -494,21 +306,6 @@ static void tx_gate_task(void *args) {
|
|||||||
(void) args;
|
(void) args;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void udp_delay(struct mg_mgr *mgr) {
|
|
||||||
(void) mgr;
|
|
||||||
vTaskDelay(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void udp_task(void *args) {
|
|
||||||
struct mg_mgr *mgr = (struct mg_mgr *) args;
|
|
||||||
int fd = open_udp();
|
|
||||||
|
|
||||||
s_udp_ready = true;
|
|
||||||
udp_exchange(mgr, fd, udp_delay);
|
|
||||||
s_udp_done = true;
|
|
||||||
vTaskDelete(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void recv_wait_task(void *args) {
|
static void recv_wait_task(void *args) {
|
||||||
char buf;
|
char buf;
|
||||||
int fd = (int) (uintptr_t) args;
|
int fd = (int) (uintptr_t) args;
|
||||||
@@ -722,22 +519,12 @@ static void task2(void *args) {
|
|||||||
struct mg_mgr *mgr = (struct mg_mgr *)args; // Event manager
|
struct mg_mgr *mgr = (struct mg_mgr *)args; // Event manager
|
||||||
char *ip;
|
char *ip;
|
||||||
int status;
|
int status;
|
||||||
pid_t first, second, udp;
|
pid_t first, second;
|
||||||
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms
|
vTaskDelay(pdMS_TO_TICKS(500)); // 500 ms
|
||||||
s_error = false;
|
s_error = false;
|
||||||
s_wait = false;
|
s_wait = false;
|
||||||
ip = mg_mprintf("%M", mg_print_ip4, &mgr->ifp->ip);
|
ip = mg_mprintf("%M", mg_print_ip4, &mgr->ifp->ip);
|
||||||
s_udp_ready = s_udp_done = false;
|
|
||||||
ASSERT(xTaskCreate(udp_task, "udp", 512, mgr, configMAX_PRIORITIES - 1,
|
|
||||||
NULL) == pdPASS);
|
|
||||||
while (!s_udp_ready) vTaskDelay(1);
|
|
||||||
udp = start_udp(ip);
|
|
||||||
while (!client_done(udp, &status)) vTaskDelay(1);
|
|
||||||
check_client(status);
|
|
||||||
while (!s_udp_done) vTaskDelay(1);
|
|
||||||
DASHBOARD("udp");
|
|
||||||
|
|
||||||
s_poll_waiting = s_poll_done = false;
|
s_poll_waiting = s_poll_done = false;
|
||||||
ASSERT(xTaskCreate(poll_task, "poll", 512, NULL,
|
ASSERT(xTaskCreate(poll_task, "poll", 512, NULL,
|
||||||
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
configMAX_PRIORITIES - 1, NULL) == pdPASS);
|
||||||
@@ -768,6 +555,7 @@ 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,
|
||||||
@@ -782,6 +570,12 @@ 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
|
||||||
@@ -1065,20 +859,6 @@ static void nortos_echo(struct mg_mgr *mgr, struct nortos_echo_client *clients,
|
|||||||
nortos_wait_client(mgr, pid);
|
nortos_wait_client(mgr, pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void udp_poll(struct mg_mgr *mgr) {
|
|
||||||
mg_mgr_poll(mgr, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void nortos_udp(struct mg_mgr *mgr, const char *ip) {
|
|
||||||
pid_t pid;
|
|
||||||
int fd = open_udp();
|
|
||||||
|
|
||||||
pid = start_udp(ip);
|
|
||||||
udp_exchange(mgr, fd, udp_poll);
|
|
||||||
nortos_wait_client(mgr, pid);
|
|
||||||
DASHBOARD("nortos_udp");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void nortos_test(struct mg_mgr *mgr) {
|
static void nortos_test(struct mg_mgr *mgr) {
|
||||||
static const char bootstrap = '\0';
|
static const char bootstrap = '\0';
|
||||||
static const char grow[512] = {0};
|
static const char grow[512] = {0};
|
||||||
@@ -1180,7 +960,6 @@ static void nortos_test(struct mg_mgr *mgr) {
|
|||||||
ASSERT(close(fd) == 0);
|
ASSERT(close(fd) == 0);
|
||||||
mg_mgr_poll(mgr, 0);
|
mg_mgr_poll(mgr, 0);
|
||||||
DASHBOARD("nortos_nonblocking_connect");
|
DASHBOARD("nortos_nonblocking_connect");
|
||||||
nortos_udp(mgr, ip);
|
|
||||||
mg_free(ip);
|
mg_free(ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-108
@@ -3158,6 +3158,8 @@ static bool su_cb(struct mg_http_message *hm, struct mg_str *data, void **p) {
|
|||||||
static void su(struct mg_connection *c, int ev, void *ev_data) {
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3192,7 +3194,8 @@ 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) {
|
||||||
mg_http_stream_body(c, (struct mg_http_message *) ev_data, cr_cb, NULL);
|
ASSERT(mg_http_stream_body(c, (struct mg_http_message *) ev_data, cr_cb,
|
||||||
|
NULL));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3205,103 +3208,12 @@ static void cs(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
(void) ev_data;
|
(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);
|
||||||
@@ -3345,7 +3257,7 @@ static void test_http_upload(void) {
|
|||||||
ASSERT(mgr.conns == NULL);
|
ASSERT(mgr.conns == NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// mg_http_stream_body: chunked request rejected with "400", cleanup still runs
|
// mg_http_stream_body: chunked request is not streamed, caller replies "400"
|
||||||
{
|
{
|
||||||
char buf[FETCH_BUF_SIZE];
|
char buf[FETCH_BUF_SIZE];
|
||||||
s_su_done = s_su_end = 0;
|
s_su_done = s_su_end = 0;
|
||||||
@@ -3356,7 +3268,7 @@ static void test_http_upload(void) {
|
|||||||
"POST /su_chunked.txt HTTP/1.1\r\n"
|
"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 == 1 && s_su_end == 1); // End called without start
|
ASSERT(s_su_done == 0 && s_su_end == 0); // The callback was never called
|
||||||
ASSERT(fopen("su_chunked.txt", "r") == NULL);
|
ASSERT(fopen("su_chunked.txt", "r") == NULL);
|
||||||
mg_mgr_free(&mgr);
|
mg_mgr_free(&mgr);
|
||||||
ASSERT(mgr.conns == NULL);
|
ASSERT(mgr.conns == NULL);
|
||||||
@@ -5098,20 +5010,6 @@ 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,11 +1,9 @@
|
|||||||
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 -DCFLAGS_EXTRA="$(CFLAGS_EXTRA)" build
|
CMD ?= idf.py build
|
||||||
PORT ?= /dev/ttyUSB0
|
PORT ?= /dev/ttyUSB0
|
||||||
|
|
||||||
CFLAGS_EXTRA ?= -DMG_TLS=MG_TLS_BUILTIN
|
|
||||||
|
|
||||||
all: example
|
all: example
|
||||||
|
|
||||||
example:
|
example:
|
||||||
|
|||||||
@@ -12,5 +12,3 @@ 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,11 +44,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
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);
|
||||||
@@ -70,8 +65,10 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern void wifi_init(const char *ssid, const char *pass);
|
||||||
|
|
||||||
void app_main(void) {
|
void app_main(void) {
|
||||||
void wifi_init(const char *ssid, const char *pass);
|
MG_HEALTH_INIT();
|
||||||
wifi_init(WIFI_SSID, WIFI_PASS);
|
wifi_init(WIFI_SSID, WIFI_PASS);
|
||||||
|
|
||||||
struct mg_mgr mgr;
|
struct mg_mgr mgr;
|
||||||
|
|||||||
@@ -1 +1,6 @@
|
|||||||
#define MG_ARCH MG_ARCH_ESP32
|
#define MG_ARCH MG_ARCH_ESP32
|
||||||
|
|
||||||
|
#define MG_ENABLE_HEALTH 1
|
||||||
|
|
||||||
|
#define MG_TLS MG_TLS_BUILTIN
|
||||||
|
//#define MG_TLS MG_TLS_MBED
|
||||||
@@ -9,12 +9,7 @@ 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_HDRS) {
|
if (ev == MG_EV_HTTP_MSG) mg_http_reply(c, 200, "", "hi\n");
|
||||||
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,12 +9,7 @@ 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_HDRS) {
|
if (ev == MG_EV_HTTP_MSG) mg_http_reply(c, 200, "", "hi\n");
|
||||||
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_millis())) {
|
if (mg_timer_expired(&timer, MQTT_RECONNECT_PERIOD_MS, mg_now())) {
|
||||||
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,11 +65,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
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,11 +89,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
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,11 +97,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
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,11 +47,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
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)) {
|
||||||
|
|||||||
@@ -65,11 +65,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
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,11 +61,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
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,11 +65,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
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,12 +5,7 @@
|
|||||||
#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_HDRS) {
|
if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
|
||||||
if (mg_match(hm->uri, mg_str("/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,12 +33,7 @@ static void driver_init(struct mg_mgr *mgr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_HTTP_HDRS) {
|
if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
|
||||||
if (mg_match(hm->uri, mg_str("/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,12 +58,7 @@ static void driver_init(struct mg_mgr *mgr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_HTTP_HDRS) {
|
if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
|
||||||
if (mg_match(hm->uri, mg_str("/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,11 +39,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
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,11 +82,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
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,11 +195,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
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());
|
||||||
|
|||||||
@@ -63,11 +63,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
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)) {
|
||||||
|
|||||||
@@ -66,12 +66,7 @@ static bool usb_poll(struct mg_tcpip_if *ifp, bool s1) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_HTTP_HDRS) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
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,11 +68,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
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,12 +28,7 @@ bool mg_random(void *buf, size_t len) { // Use on-board RNG
|
|||||||
|
|
||||||
|
|
||||||
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_HTTP_HDRS) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
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,12 +66,7 @@ static bool usb_poll(struct mg_tcpip_if *ifp, bool s1) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_HTTP_HDRS) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
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,11 +68,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
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,11 +68,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
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,12 +47,7 @@ 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_HDRS) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
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,11 +68,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
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,12 +89,7 @@ static void log_fn(char ch, void *param) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_HTTP_HDRS) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
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());
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
struct echo_server {
|
struct echo_server {
|
||||||
@@ -42,12 +43,12 @@ int echo_init(uint16_t port) {
|
|||||||
|
|
||||||
if (fd < 0 || !echo_nonblock(fd) ||
|
if (fd < 0 || !echo_nonblock(fd) ||
|
||||||
bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0 || listen(fd, 3) < 0) {
|
bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0 || listen(fd, 3) < 0) {
|
||||||
MG_ERROR(("BSD echo init: %d", errno));
|
printf("BSD echo init: %d\n", errno);
|
||||||
if (fd >= 0) close(fd);
|
if (fd >= 0) close(fd);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
s_server.sock = fd;
|
s_server.sock = fd;
|
||||||
MG_INFO(("BSD echo server on port %hu", port));
|
printf("BSD echo server on port %hu\n", port);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +80,7 @@ void echo_poll(void) {
|
|||||||
if (fd >= 0) {
|
if (fd >= 0) {
|
||||||
c = (struct echo_client *) calloc(1, sizeof(*c));
|
c = (struct echo_client *) calloc(1, sizeof(*c));
|
||||||
if (c == NULL || !echo_nonblock(fd)) {
|
if (c == NULL || !echo_nonblock(fd)) {
|
||||||
MG_ERROR(("BSD echo accept: %d", c == NULL ? ENOMEM : errno));
|
printf("BSD echo accept: %d\n", c == NULL ? ENOMEM : errno);
|
||||||
close(fd);
|
close(fd);
|
||||||
free(c);
|
free(c);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -44,12 +44,7 @@ bool mg_random(void *buf, size_t len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_HTTP_HDRS) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
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,12 +43,7 @@ bool mg_random(void *buf, size_t len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_HTTP_HDRS) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
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());
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
# Copyright (c) 2026 Cesanta Software Limited
|
|
||||||
# Environment setup: https://mongoose.ws/docs/getting-started/build-environment/
|
|
||||||
|
|
||||||
MGDIR = ../../../..
|
|
||||||
CFLAGS = -W -Wall -Wextra -Wundef -Wshadow -Wdouble-promotion
|
|
||||||
CFLAGS += -Wformat-truncation -fno-common -Wconversion -Wno-sign-conversion
|
|
||||||
CFLAGS += -g3 -Os -ffunction-sections -fdata-sections
|
|
||||||
CFLAGS += -I. -I$(MGDIR) -Icmsis_core/CMSIS/Core/Include -Icmsis_h7/Include
|
|
||||||
CFLAGS += -mcpu=cortex-m7 -mthumb -mfloat-abi=hard -mfpu=fpv5-d16 $(CFLAGS_EXTRA)
|
|
||||||
LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs nosys.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map
|
|
||||||
|
|
||||||
SOURCES = main.c hal.c udp.c
|
|
||||||
SOURCES += cmsis_h7/Source/Templates/gcc/startup_stm32h723xx.s
|
|
||||||
SOURCES += $(MGDIR)/mongoose.c
|
|
||||||
|
|
||||||
all build example: firmware.bin
|
|
||||||
|
|
||||||
firmware.elf: cmsis_core cmsis_h7 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"
|
|
||||||
|
|
||||||
flash: firmware.bin
|
|
||||||
STM32_Programmer_CLI -c port=swd -w firmware.elf -hardRst
|
|
||||||
|
|
||||||
cmsis_core:
|
|
||||||
git clone -q -c advice.detachedHead=false --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@
|
|
||||||
|
|
||||||
cmsis_h7:
|
|
||||||
git clone -q -c advice.detachedHead=false --depth 1 -b v1.10.6 https://github.com/STMicroelectronics/cmsis_device_h7 $@
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf firmware.* cmsis_*
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
// Copyright (c) 2026 Cesanta Software Limited
|
|
||||||
// All rights reserved
|
|
||||||
|
|
||||||
#include "hal.h"
|
|
||||||
|
|
||||||
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) {
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
}
|
|
||||||
@@ -1,225 +0,0 @@
|
|||||||
// Copyright (c) 2022-2026 Cesanta Software Limited
|
|
||||||
// Datasheet: RM0468, devboard manual: UM2407
|
|
||||||
// https://www.st.com/resource/en/reference_manual/rm0468-stm32h723733-stm32h725735-and-stm32h730-value-line-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
|
|
||||||
// https://www.st.com/resource/en/user_manual/um2407-stm32h7-nucleo144-boards-mb1364-stmicroelectronics.pdf
|
|
||||||
// Alternate functions: https://www.st.com/resource/en/datasheet/stm32h723.pdf
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <stm32h723xx.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)
|
|
||||||
|
|
||||||
#define HAL_ETH_PINS PIN('A', 1), PIN('A', 2), PIN('A', 7), \
|
|
||||||
PIN('B', 13), PIN('C', 1), PIN('C', 4), \
|
|
||||||
PIN('C', 5), PIN('G', 11), PIN('G', 13)
|
|
||||||
|
|
||||||
// System clock (2.1, Figure 1; 8.5, Figure 45; 8.5.5, Figure 47; 8.5.6, Figure
|
|
||||||
// 49; 8.5.8 Table 56; datasheet) CPU_FREQUENCY <= 550 MHz; hclk = CPU_FREQUENCY
|
|
||||||
// / HAL_HPRE ; hclk <= 275 MHz; APB clocks <= 137.5 MHz. D1 domain bus matrix (and
|
|
||||||
// so flash) runs at hclk frequency. Configure flash latency (WS) in accordance
|
|
||||||
// to hclk freq (4.3.8, Table 16) The Ethernet controller is in D2 domain and
|
|
||||||
// runs at hclk frequency
|
|
||||||
enum {
|
|
||||||
HAL_D1CPRE = 1, // actual divisor value
|
|
||||||
HAL_HPRE = 2, // actual divisor value
|
|
||||||
HAL_D1PPRE = 4, // register values, divisor value = BIT(value - 3) = / 2
|
|
||||||
HAL_D2PPRE1 = 4,
|
|
||||||
HAL_D2PPRE2 = 4,
|
|
||||||
HAL_D3PPRE = 4
|
|
||||||
};
|
|
||||||
// PLL1_P: odd division factors are not allowed (8.7.12) (except for '1')
|
|
||||||
enum { PLL1_HSI = 64, PLL1_M = 32, PLL1_N = 225, PLL1_P = 1 };
|
|
||||||
#define HAL_FLASH_LATENCY 0x33 // WRHIGHFREQ LATENCY
|
|
||||||
#define SYS_FREQUENCY ((PLL1_HSI * PLL1_N / PLL1_M / PLL1_P / HAL_D1CPRE) * 1000000)
|
|
||||||
#define AHB_FREQUENCY (SYS_FREQUENCY / HAL_HPRE)
|
|
||||||
#define APB2_FREQUENCY (AHB_FREQUENCY / (BIT(HAL_D2PPRE2 - 3)))
|
|
||||||
#define APB1_FREQUENCY (AHB_FREQUENCY / (BIT(HAL_D2PPRE1 - 3)))
|
|
||||||
|
|
||||||
|
|
||||||
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 *) (0x40000000 + 0x18020000UL + 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->AHB4ENR |= 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// D2 Kernel clock (8.7.21) USART1 defaults to pclk2 (APB2), while USART2,3
|
|
||||||
// default to pclk1 (APB1). Even if using other kernel clocks, the APBx clocks
|
|
||||||
// must be enabled for CPU access, as the kernel clock drives the BRR, not the
|
|
||||||
// APB bus interface
|
|
||||||
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 |= BIT(4);
|
|
||||||
if (uart == USART2) freq = APB1_FREQUENCY, RCC->APB1LENR |= BIT(17);
|
|
||||||
if (uart == USART3) freq = APB1_FREQUENCY, RCC->APB1LENR |= BIT(18);
|
|
||||||
|
|
||||||
#if 0 // CONSTANT BAUD RATE FOR REMOTE DEBUGGING WHILE SETTING THE PLL
|
|
||||||
CLRSET(RCC->D2CCIP2R, 7 << 3, 3 << 3); // use HSI for UART1
|
|
||||||
freq = 64000000;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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) defaults to HSI48 and is characterized at this frequency.
|
|
||||||
// Enable at SystemInit. (STM32CubeMX) It won't work with a 275MHz (AHB)
|
|
||||||
static inline void hal_rng_init(void) {
|
|
||||||
RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN; // Enable RNG AHB clock (hal_rng_hclk)
|
|
||||||
// 39.6.2 Table 320 init
|
|
||||||
RNG->CR = RNG_CR_CONDRST | (0x0F << RNG_CR_RNG_CONFIG1_Pos) |
|
|
||||||
(0 << RNG_CR_RNG_CONFIG2_Pos) | (0xD << RNG_CR_RNG_CONFIG3_Pos);
|
|
||||||
RNG->HTCR = 0x17590abc;
|
|
||||||
RNG->HTCR = 0xaa74;
|
|
||||||
RNG->CR &= ~RNG_CR_CONDRST ;
|
|
||||||
while (RNG->CR & RNG_CR_CONDRST) (void) 0; // 39.7.1
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hw pull-ups on PHY RXD0,1,DV to enable autonegotiation
|
|
||||||
static inline void hal_ethernet_init(void) {
|
|
||||||
// Initialise Ethernet. Enable MAC GPIO pins, see
|
|
||||||
// https://www.st.com/resource/en/user_manual/um2407-stm32h7-nucleo144-boards-mb1364-stmicroelectronics.pdf
|
|
||||||
uint16_t pins[] = {HAL_ETH_PINS};
|
|
||||||
for (size_t i = 0; i < sizeof(pins) / sizeof(pins[0]); i++) {
|
|
||||||
hal_gpio_init(pins[i], HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_INSANE,
|
|
||||||
HAL_GPIO_PULL_NONE, 11); // 11 is the Ethernet function
|
|
||||||
}
|
|
||||||
NVIC_EnableIRQ(ETH_IRQn); // Setup Ethernet IRQ handler
|
|
||||||
CLRSET(SYSCFG->PMCR, 7 << 21, 4 << 21); // Use RMII (12.3.1)
|
|
||||||
RCC->AHB1ENR |= BIT(15) | BIT(16) | BIT(17); // Enable Ethernet clocks
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline unsigned int hal_div2prescval(unsigned int div) {
|
|
||||||
// 0 --> /1; 8 --> /2 ... 11 --> /16; 12 --> /64 ... 15 --> /512
|
|
||||||
if (div == 1) return 0;
|
|
||||||
if (div > 16) div /= 2;
|
|
||||||
unsigned int val = 7;
|
|
||||||
while (div >>= 1) ++val;
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline unsigned int hal_pllrge(unsigned int f) {
|
|
||||||
unsigned int val = 0;
|
|
||||||
while (f >>= 1) ++val;
|
|
||||||
return val - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable RTC/BKP APB clock and backup domain write access.
|
|
||||||
// Must be called before accessing RTC backup registers or IWDG.
|
|
||||||
static inline void hal_backup_domain_init(void) {
|
|
||||||
RCC->APB4ENR |= RCC_APB4ENR_RTCAPBEN; // enable RTC/BKP APB clock
|
|
||||||
PWR->CR1 |= PWR_CR1_DBP; // enable backup domain write access
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void hal_clock_init(void) {
|
|
||||||
hal_backup_domain_init();
|
|
||||||
PWR->CR3 |= PWR_CR3_LDOEN; // select LDO (reset value)
|
|
||||||
while ((PWR->CSR1 & PWR_CSR1_ACTVOSRDY) == 0) (void) 0; // ACTVOSRDY
|
|
||||||
PWR->D3CR &= ~PWR_D3CR_VOS_Msk; // Select VOS0
|
|
||||||
while ((PWR->D3CR & PWR_D3CR_VOSRDY) == 0) (void) 0; // VOSRDY
|
|
||||||
CLRSET(
|
|
||||||
RCC->D1CFGR, (0x0F << 8) | (7 << 4) | (0x0F << 0),
|
|
||||||
(hal_div2prescval(HAL_D1CPRE) << 8) | (HAL_D1PPRE << 4) | (hal_div2prescval(HAL_HPRE) << 0));
|
|
||||||
RCC->D2CFGR = (HAL_D2PPRE2 << 8) | (HAL_D2PPRE1 << 4);
|
|
||||||
RCC->D3CFGR = (HAL_D3PPRE << 4);
|
|
||||||
CLRSET(RCC->PLLCFGR, 3 << 2,
|
|
||||||
hal_pllrge(PLL1_HSI / PLL1_M)
|
|
||||||
<< 2); // keep reset config (DIVP1EN, !PLL1VCOSEL), PLL1RGE
|
|
||||||
CLRSET(RCC->PLL1DIVR, (0x7F << 9) | (0x1FF << 0),
|
|
||||||
((PLL1_P - 1) << 9) | ((PLL1_N - 1) << 0)); // Set PLL1_P PLL1_N
|
|
||||||
CLRSET(RCC->PLLCKSELR, 0x3F << 4,
|
|
||||||
PLL1_M << 4); // Set PLL1_M (source defaults to HSI)
|
|
||||||
RCC->CR |= RCC_CR_PLL1ON | RCC_CR_HSI48ON; // Enable PLL1 and HSI48 (for RNG)
|
|
||||||
while ((RCC->CR & RCC_CR_PLL1RDY) == 0) (void) 0; // Wait until done
|
|
||||||
RCC->CFGR |= (3 << 0); // Set clock source to PLL1
|
|
||||||
while ((RCC->CFGR & (7 << 3)) != (3 << 3)) (void) 0; // Wait until done
|
|
||||||
FLASH->ACR = HAL_FLASH_LATENCY; // default is larger
|
|
||||||
RCC->APB4ENR |= RCC_APB4ENR_SYSCFGEN; // Enable SYSCFG
|
|
||||||
while ((RCC->CR & RCC_CR_HSI48RDY) == 0) (void) 0; // Make sure HSI48 is ready
|
|
||||||
SystemCoreClock = SYS_FREQUENCY; // Update SystemCoreClock global var
|
|
||||||
SysTick_Config(SystemCoreClock / 1000); // Sys tick every 1ms
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static inline void hal_system_init(void) {
|
|
||||||
SCB->CPACR |= ((3UL << 10 * 2) | (3UL << 11 * 2)); // Enable FPU
|
|
||||||
__DSB();
|
|
||||||
__ISB();
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
ENTRY(Reset_Handler);
|
|
||||||
MEMORY {
|
|
||||||
flash(rx) : ORIGIN = 0x08000000, LENGTH = 1024k
|
|
||||||
|
|
||||||
dtcmram (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
|
|
||||||
ram_d1 (rwx) : ORIGIN = 0x24000000, LENGTH = 320K
|
|
||||||
ram_d2 (rwx) : ORIGIN = 0x30000000, LENGTH = 32K
|
|
||||||
ram_d3 (rwx) : ORIGIN = 0x38000000, LENGTH = 16K
|
|
||||||
itcmram (rwx) : ORIGIN = 0x00000000, LENGTH = 64K
|
|
||||||
}
|
|
||||||
_estack = ORIGIN(ram_d1) + LENGTH(ram_d1);
|
|
||||||
|
|
||||||
SECTIONS {
|
|
||||||
.vectors : { KEEP(*(.isr_vector)) } > flash
|
|
||||||
.text : { *(.text* .text.*) } > flash
|
|
||||||
.rodata : { *(.rodata*) } > 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.*)) _edata = .; } > ram_d1 AT > flash
|
|
||||||
_sidata = LOADADDR(.data);
|
|
||||||
|
|
||||||
.bss : { _sbss = .; *(.bss SORT(.bss.*) COMMON) _ebss = .; } > ram_d1
|
|
||||||
|
|
||||||
.eth_ram : { *(.eth_ram .eth_ram*) } > ram_d1 AT > flash
|
|
||||||
|
|
||||||
/* Health record. NOLOAD and in unused DTCM, so startup code neither copies
|
|
||||||
nor zeroes it and the contents survive a warm reset */
|
|
||||||
.mg_health (NOLOAD) : { KEEP(*(.mg_health .mg_health*)) } > dtcmram
|
|
||||||
|
|
||||||
. = ALIGN(8);
|
|
||||||
_end = .;
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
// Copyright (c) 2026 Cesanta Software Limited
|
|
||||||
// All rights reserved
|
|
||||||
|
|
||||||
#include "hal.h"
|
|
||||||
#include "mongoose.h"
|
|
||||||
|
|
||||||
int udp_init(uint16_t port);
|
|
||||||
void udp_poll(void);
|
|
||||||
|
|
||||||
#ifndef UART_DEBUG
|
|
||||||
#define UART_DEBUG USART3
|
|
||||||
#define UART_DEBUG_TX_PIN PIN('D', 8)
|
|
||||||
#define UART_DEBUG_RX_PIN PIN('D', 9)
|
|
||||||
#else
|
|
||||||
#define UART_DEBUG_TX_PIN PIN('A', 9)
|
|
||||||
#define UART_DEBUG_RX_PIN PIN('A', 10)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define LED1 PIN('B', 0)
|
|
||||||
#define LED2 PIN('E', 1)
|
|
||||||
#define LED3 PIN('B', 14)
|
|
||||||
|
|
||||||
static void log_fn(char ch, void *param) {
|
|
||||||
hal_uart_write_buf(param, &ch, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void blink_task(void) {
|
|
||||||
static uint64_t blink_timer = 0;
|
|
||||||
if (hal_timer_expired(&blink_timer, 500, hal_get_tick())) {
|
|
||||||
hal_gpio_toggle(LED2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t mg_millis(void) {
|
|
||||||
return hal_get_tick();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mg_random(void *buf, size_t len) {
|
|
||||||
for (size_t n = 0; n < len; n += sizeof(uint32_t)) {
|
|
||||||
uint32_t r = hal_rng_read();
|
|
||||||
memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
struct mg_mgr mgr;
|
|
||||||
|
|
||||||
hal_clock_init();
|
|
||||||
hal_uart_init(UART_DEBUG, UART_DEBUG_TX_PIN, UART_DEBUG_RX_PIN, 115200);
|
|
||||||
mg_log_set_fn(log_fn, UART_DEBUG);
|
|
||||||
hal_rng_init();
|
|
||||||
hal_ethernet_init();
|
|
||||||
hal_gpio_output(LED1);
|
|
||||||
hal_gpio_output(LED2);
|
|
||||||
hal_gpio_output(LED3);
|
|
||||||
|
|
||||||
MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000));
|
|
||||||
mg_mgr_init(&mgr);
|
|
||||||
udp_init(1234);
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
mg_mgr_poll(&mgr, 0);
|
|
||||||
udp_poll();
|
|
||||||
blink_task();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "hal.h"
|
|
||||||
|
|
||||||
// See https://mongoose.ws/docs/getting-started/build-options/
|
|
||||||
#define MG_ARCH MG_ARCH_ARMGCC
|
|
||||||
#define MG_TLS MG_TLS_BUILTIN
|
|
||||||
#define MG_OTA MG_OTA_STM32H7
|
|
||||||
|
|
||||||
#define MG_ENABLE_TCPIP 1
|
|
||||||
#define MG_ENABLE_BSD_SOCKETS 1
|
|
||||||
#define MG_ENABLE_CUSTOM_MILLIS 1
|
|
||||||
#define MG_ENABLE_CUSTOM_RANDOM 1
|
|
||||||
#define MG_ENABLE_DRIVER_STM32H 1
|
|
||||||
#define MG_ENABLE_PACKED_FS 1
|
|
||||||
#define MG_ETH_RAM __attribute__((section(".eth_ram")))
|
|
||||||
|
|
||||||
// #define MG_DRIVER_MDC_CR 4 // RMII MDC clock divider, from 0 to 5
|
|
||||||
// #define MG_TCPIP_PHY_ADDR 0 // PHY address
|
|
||||||
|
|
||||||
// For static IP configuration, define MG_TCPIP_{IP,MASK,GW}
|
|
||||||
// By default, those are set to zero, meaning that DHCP is used
|
|
||||||
//
|
|
||||||
// #define MG_TCPIP_IP MG_IPV4(192, 168, 0, 10) // IP
|
|
||||||
// #define MG_TCPIP_GW MG_IPV4(192, 168, 0, 1) // Gateway
|
|
||||||
// #define MG_TCPIP_MASK MG_IPV4(255, 255, 255, 0) // Netmask
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
// Copyright (c) 2026 Cesanta Software Limited
|
|
||||||
// All rights reserved
|
|
||||||
|
|
||||||
#include "mongoose.h"
|
|
||||||
|
|
||||||
struct udp_server {
|
|
||||||
int fd;
|
|
||||||
bool pending;
|
|
||||||
struct sockaddr_in peer;
|
|
||||||
socklen_t peerlen;
|
|
||||||
size_t len;
|
|
||||||
char buf[128];
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct udp_server s_server = {.fd = -1};
|
|
||||||
|
|
||||||
static int udp_nonblock(int fd) {
|
|
||||||
int flags = fcntl(fd, F_GETFL, 0);
|
|
||||||
return flags >= 0 && fcntl(fd, F_SETFL, flags | O_NONBLOCK) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int udp_init(uint16_t port) {
|
|
||||||
struct sockaddr_in sa = {
|
|
||||||
.sin_family = AF_INET,
|
|
||||||
.sin_port = htons(port),
|
|
||||||
.sin_addr = {.s_addr = INADDR_ANY},
|
|
||||||
};
|
|
||||||
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
||||||
|
|
||||||
if (fd < 0 || !udp_nonblock(fd) ||
|
|
||||||
bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
|
|
||||||
MG_ERROR(("BSD UDP init: %d", errno));
|
|
||||||
if (fd >= 0) close(fd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
s_server.fd = fd;
|
|
||||||
MG_INFO(("BSD UDP echo server on port %hu", port));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void udp_poll(void) {
|
|
||||||
struct udp_server *s = &s_server;
|
|
||||||
struct pollfd p;
|
|
||||||
ssize_t n;
|
|
||||||
|
|
||||||
if (s->fd < 0) return;
|
|
||||||
p.fd = s->fd, p.events = s->pending ? POLLOUT : POLLIN, p.revents = 0;
|
|
||||||
if (poll(&p, 1, 0) <= 0) return;
|
|
||||||
if (s->pending) {
|
|
||||||
n = sendto(s->fd, s->buf, s->len, 0, (struct sockaddr *) &s->peer,
|
|
||||||
s->peerlen);
|
|
||||||
if (n == (ssize_t) s->len) {
|
|
||||||
s->pending = false;
|
|
||||||
} else if (n < 0 && errno != EAGAIN) {
|
|
||||||
MG_ERROR(("BSD UDP send: %d", errno));
|
|
||||||
s->pending = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
s->peerlen = sizeof(s->peer);
|
|
||||||
n = recvfrom(s->fd, s->buf, sizeof(s->buf), 0,
|
|
||||||
(struct sockaddr *) &s->peer, &s->peerlen);
|
|
||||||
if (n >= 0) {
|
|
||||||
s->len = (size_t) n, s->pending = true;
|
|
||||||
} else if (errno != EAGAIN) {
|
|
||||||
MG_ERROR(("BSD UDP recv: %d", errno));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -68,11 +68,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
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,11 +68,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
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,11 +64,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
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,11 +60,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
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,11 +68,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
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,11 +68,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
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,11 +68,6 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
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,12 +63,7 @@ static bool usb_poll(struct mg_tcpip_if *ifp, bool s1) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_HTTP_HDRS) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
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,12 +30,7 @@ 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_HDRS) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,11 +14,6 @@ static void wcb(struct mg_connection *c, int ev, void *ev_data) {
|
|||||||
if (ev == MG_EV_ACCEPT && c->is_tls) {
|
if (ev == MG_EV_ACCEPT && c->is_tls) {
|
||||||
struct mg_tls_opts opts = {.cert = (char *) s_ssl_cert, .key = (char *) s_ssl_key};
|
struct mg_tls_opts opts = {.cert = (char *) s_ssl_cert, .key = (char *) s_ssl_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 = ev_data;
|
struct mg_http_message *hm = ev_data;
|
||||||
MG_INFO(("%.*s %.*s %ld", (int) hm->method.len, hm->method.buf,
|
MG_INFO(("%.*s %.*s %ld", (int) hm->method.len, hm->method.buf,
|
||||||
|
|||||||
Reference in new issue
Block a user