mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-26 12:14:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
278c4ca116 | ||
|
|
88b01ef6f0 | ||
|
|
bd8ee86f9e | ||
|
|
ef96f7a28c | ||
|
|
03f240a244 | ||
|
|
c775f31ca7 | ||
|
|
1ca4fd1fef |
No files matched your search
@@ -637,6 +637,7 @@ jobs:
|
||||
example:
|
||||
- path: micropython/esp32
|
||||
- path: esp32/uart-bridge
|
||||
- path: esp32/minimal
|
||||
name: ${{ matrix.example.path }}
|
||||
env:
|
||||
GO: 0
|
||||
|
||||
@@ -186,6 +186,7 @@ jobs:
|
||||
matrix:
|
||||
example:
|
||||
- path: esp32/uart-bridge
|
||||
- path: esp32/minimal
|
||||
name: ${{ matrix.example.path }}
|
||||
env:
|
||||
GO: 0
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
# Security Policy
|
||||
|
||||
Mongoose is a full-stack embedded networking library by Cesanta Software Ltd,
|
||||
<https://mongoose.ws>. This policy covers how to report vulnerabilities
|
||||
and how Cesanta handles them.
|
||||
|
||||
## Supported versions
|
||||
|
||||
Security fixes go to the `master` branch and the latest release.
|
||||
|
||||
## Reporting a vulnerability
|
||||
|
||||
Do not report security issues in public GitHub issues. Report them
|
||||
privately using GitHub Security Advisories:
|
||||
<https://github.com/cesanta/mongoose/security/advisories/new>
|
||||
|
||||
Include the Mongoose version or commit, steps or a proof of concept to reproduce, and
|
||||
the impact. Cesanta usually responds the same business day.
|
||||
|
||||
## Disclosure process
|
||||
|
||||
1. Cesanta reviews the report and coordinates a fix with the reporter.
|
||||
2. Eligible commercial customers receive a notification and a private fix
|
||||
one month before the CVE becomes public.
|
||||
3. The fix and the CVE are then published. The reporter is credited in the
|
||||
GitHub Security Advisory.
|
||||
|
||||
## Security testing
|
||||
|
||||
- CI on every push, unit tests built with ASAN and UBSan
|
||||
- CodeQL static analysis on every commit
|
||||
- Continuous fuzzing on OSS-Fuzz, plus nightly libFuzzer runs
|
||||
- AI-assisted security review on every pull request
|
||||
- Regular review by independent security research groups worldwide
|
||||
|
||||
## EU Cyber Resilience Act (CRA)
|
||||
|
||||
Mongoose supports CRA compliance for connected products: a documented
|
||||
vulnerability handling process (this file), TLS 1.3, signed OTA firmware
|
||||
updates, and an SBOM (`sbom.cdx.json`). Details:
|
||||
<https://mongoose.ws/security/cra-compliance/>
|
||||
+228
-1053
File diff suppressed because it is too large.
Load diff
+154
-176
@@ -165,6 +165,7 @@ extern "C" {
|
||||
|
||||
#if MG_ENABLE_TCPIP && !defined(MG_ENABLE_DRIVER_STM32F) && \
|
||||
!defined(MG_ENABLE_DRIVER_STM32H) && !defined(MG_ENABLE_DRIVER_STM32N) && \
|
||||
!defined(MG_ENABLE_DRIVER_STM32C) && \
|
||||
!defined(MG_ENABLE_DRIVER_CYW) && !defined(MG_ENABLE_DRIVER_CYW_SDIO) && \
|
||||
!defined(MG_ENABLE_DRIVER_ST67W6)
|
||||
#if defined(STM32F1) || defined(STM32F2) || defined(STM32F4) || defined(STM32F7)
|
||||
@@ -173,6 +174,8 @@ extern "C" {
|
||||
#define MG_ENABLE_DRIVER_STM32H 1
|
||||
#elif defined(STM32N6)
|
||||
#define MG_ENABLE_DRIVER_STM32N 1
|
||||
#elif defined(STM32C5)
|
||||
#define MG_ENABLE_DRIVER_STM32C 1
|
||||
#else
|
||||
#error Select a driver in mongoose_config.h
|
||||
#endif
|
||||
@@ -189,70 +192,8 @@ extern "C" {
|
||||
#define MG_OTA MG_OTA_STM32H5
|
||||
#elif !defined(MG_OTA) && defined(STM32H7)
|
||||
#define MG_OTA MG_OTA_STM32H7
|
||||
#endif
|
||||
|
||||
// OTA rollback timer via IWDG. Fixed 10 s hang-detection window (prescaler
|
||||
// /1024, LSI ~32 kHz: reload = 10 * 32000 / 1024 = 312). mg_ota_poll() feeds
|
||||
// the watchdog every 500 ms while in MG_OTA_TESTING state; if the event loop
|
||||
// hangs for 10 s, the IWDG fires and the device rolls back.
|
||||
// STM32H7 names the peripheral IWDG1; all others use IWDG.
|
||||
// The total evaluation period is set by MG_OTA_ROLLBACK_TIMEOUT_SECONDS.
|
||||
// STM32H7 names the watchdog peripheral IWDG1; all other families use IWDG.
|
||||
#if defined(IWDG1) && !defined(IWDG)
|
||||
#define MG_IWDG IWDG1
|
||||
#else
|
||||
#define MG_IWDG IWDG
|
||||
#endif
|
||||
// PR=6 → /256 prescaler, RLR=1250 → ~10 s at LSI 32 kHz.
|
||||
// Works on F4/F7/H5/H7; uses direct register writes so it is safe before HAL_Init().
|
||||
#ifndef MG_OTA_ROLLBACK_TIMER_START
|
||||
#define MG_OTA_ROLLBACK_TIMER_START() \
|
||||
do { \
|
||||
MG_IWDG->KR = 0xCCCCU; /* start + LSI */ \
|
||||
MG_IWDG->KR = 0x5555U; /* unlock PR/RLR */ \
|
||||
MG_IWDG->PR = 6U; /* /256 */ \
|
||||
MG_IWDG->RLR = 1250U; /* 10 s */ \
|
||||
while (MG_IWDG->SR & (IWDG_SR_PVU | IWDG_SR_RVU)) (void) 0; \
|
||||
MG_IWDG->KR = 0xAAAAU; /* reload */ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
// Feed (reload) the IWDG. Called from mg_ota_poll() every 500 ms.
|
||||
#ifndef MG_OTA_ROLLBACK_TIMER_FEED
|
||||
#define MG_OTA_ROLLBACK_TIMER_FEED() (MG_IWDG->KR = 0xAAAAU)
|
||||
#endif
|
||||
|
||||
// OTA state in a backup register that survives warm resets but clears on POR.
|
||||
// STATE_GET reads work without any setup. STATE_SET enables the RTC APB clock
|
||||
// and unlocks backup domain write access via direct register writes, so it is
|
||||
// safe to call from any context (before or after HAL_Init).
|
||||
// H5 (TAMP->BKP0R), H7 (RTC->BKP0R), F4/F7 (RTC->BKP0R).
|
||||
#ifndef MG_OTA_STATE_GET
|
||||
#if defined(PWR_DBPCR_DBP)
|
||||
#define MG_OTA_DBP_ENABLE() (PWR->DBPCR |= PWR_DBPCR_DBP)
|
||||
#elif defined(PWR_CR1_DBP)
|
||||
#define MG_OTA_DBP_ENABLE() (PWR->CR1 |= PWR_CR1_DBP)
|
||||
#else
|
||||
#define MG_OTA_DBP_ENABLE() (PWR->CR |= PWR_CR_DBP)
|
||||
#endif
|
||||
|
||||
#if defined(TAMP)
|
||||
#define MG_OTA_STATE_GET() \
|
||||
(RCC->APB3ENR |= RCC_APB3ENR_RTCAPBEN, TAMP->BKP0R)
|
||||
#define MG_OTA_STATE_SET(v) \
|
||||
(RCC->APB3ENR |= RCC_APB3ENR_RTCAPBEN, MG_OTA_DBP_ENABLE(), \
|
||||
(TAMP->BKP0R = (uint32_t) (v)), TAMP->BKP0R)
|
||||
#elif defined(RCC_APB4ENR_RTCAPBEN)
|
||||
#define MG_OTA_STATE_GET() (RTC->BKP0R)
|
||||
#define MG_OTA_STATE_SET(v) \
|
||||
(RCC->APB4ENR |= RCC_APB4ENR_RTCAPBEN, MG_OTA_DBP_ENABLE(), \
|
||||
(RTC->BKP0R = (uint32_t) (v)))
|
||||
#else
|
||||
#define MG_OTA_STATE_GET() (RTC->BKP0R)
|
||||
#define MG_OTA_STATE_SET(v) \
|
||||
(RCC->APB1ENR |= RCC_APB1ENR_PWREN, MG_OTA_DBP_ENABLE(), \
|
||||
(RTC->BKP0R = (uint32_t) (v)))
|
||||
#endif
|
||||
#elif !defined(MG_OTA) && defined(STM32C5)
|
||||
#define MG_OTA MG_OTA_STM32C5
|
||||
#endif
|
||||
|
||||
// use HAL-defined execute-in-ram section
|
||||
@@ -312,8 +253,10 @@ extern "C" {
|
||||
#include <esp_system.h>
|
||||
#include <esp_mac.h>
|
||||
#include <esp_idf_version.h>
|
||||
// #include <esp_memory_utils.h> // For health record coredumps
|
||||
// #include <esp_core_dump.h> // For health record coredumps
|
||||
#if MG_ENABLE_HEALTH
|
||||
#include <esp_memory_utils.h>
|
||||
#include <esp_core_dump.h>
|
||||
#endif
|
||||
|
||||
#define MG_PATH_MAX 128
|
||||
|
||||
@@ -1251,13 +1194,8 @@ struct timeval {
|
||||
#endif
|
||||
|
||||
#ifndef MG_ENABLE_IPV6
|
||||
#if (defined(MG_ENABLE_DRIVER_MCXW71) && MG_ENABLE_DRIVER_MCXW71) || \
|
||||
(defined(MG_ENABLE_DRIVER_RW612_154) && MG_ENABLE_DRIVER_RW612_154)
|
||||
#define MG_ENABLE_IPV6 1 // 802.15.4 radio: 6LoWPAN carries IPv6 only
|
||||
#else
|
||||
#define MG_ENABLE_IPV6 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef MG_IPV6_V6ONLY
|
||||
#define MG_IPV6_V6ONLY 0 // IPv6 socket binds only to V6, not V4 address
|
||||
@@ -1402,8 +1340,6 @@ struct timeval {
|
||||
|
||||
#endif
|
||||
|
||||
// Set the interface hardware address. Ethernet: 6-byte MAC. 6LoWPAN: 8-byte
|
||||
// EUI-64, the IPv6 interface ID is made from it. Not set: random per boot
|
||||
#ifndef MG_SET_MAC_ADDRESS
|
||||
#define MG_SET_MAC_ADDRESS(mac)
|
||||
#endif
|
||||
@@ -1445,7 +1381,6 @@ struct timeval {
|
||||
|
||||
|
||||
|
||||
|
||||
// Macros to record timestamped events that happens with a connection.
|
||||
// They are saved into a c->prof IO buffer, each event is a name and a 32-bit
|
||||
// timestamp in milliseconds since connection init time.
|
||||
@@ -4242,6 +4177,7 @@ extern void mg_mqtt_poll(struct mg_mgr *);
|
||||
#define MG_OTA_STM32H7 2 // STM32 H7
|
||||
#define MG_OTA_STM32H7_DUAL_CORE 3 // STM32 H7 dual core
|
||||
#define MG_OTA_STM32F 4 // STM32 F7/F4/F2
|
||||
#define MG_OTA_STM32C5 5 // STM32 C5, same flash controller as H5
|
||||
#define MG_OTA_CH32V307 100 // WCH CH32V307
|
||||
#define MG_OTA_U2A 200 // Renesas U2A16, U2A8, U2A6
|
||||
#define MG_OTA_RT1020 300 // IMXRT1020
|
||||
@@ -4437,21 +4373,25 @@ void mg_ota_poll(struct mg_mgr *);
|
||||
|
||||
|
||||
|
||||
#if MG_ENABLE_HEALTH
|
||||
|
||||
#ifndef MG_HEALTH_MAGIC
|
||||
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
|
||||
#endif
|
||||
|
||||
#if !defined(MG_HEALTH_RAM) && (defined(STM32H7_DEV_ID) || defined(STM32H5))
|
||||
#if !defined(MG_HEALTH_RAM) && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5))
|
||||
#define MG_HEALTH_RAM __attribute__((section(".mg_health")))
|
||||
#endif
|
||||
|
||||
#if !defined(MG_HEALTH_DATA_SIZE) && (defined(STM32H7_DEV_ID) || defined(STM32H5))
|
||||
#define MG_HEALTH_DATA_SIZE 250
|
||||
#endif
|
||||
|
||||
#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
|
||||
#endif
|
||||
#endif // MG_HEALTH_DATA_SIZE
|
||||
|
||||
// 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")))
|
||||
@@ -4459,6 +4399,13 @@ void mg_ota_poll(struct mg_mgr *);
|
||||
#define MG_HEALTH_RAM
|
||||
#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,
|
||||
// so these are ordered by how much they tell you: the most specific cause a
|
||||
// device reports wins
|
||||
@@ -4475,7 +4422,8 @@ enum mg_health_reason {
|
||||
|
||||
struct mg_health {
|
||||
char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data
|
||||
uint32_t reset_reason; // enum mg_health_reason that started this boot
|
||||
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 data[MG_HEALTH_DATA_SIZE];
|
||||
};
|
||||
@@ -4508,9 +4456,57 @@ 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()
|
||||
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
|
||||
extern void mg_health_init(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, "{}");
|
||||
}
|
||||
|
||||
#endif
|
||||
// Cloud connector API to https://mdash.net - device management cloud
|
||||
|
||||
|
||||
@@ -4606,13 +4602,7 @@ bool mg_wifi_ap_stop(void);
|
||||
#if MG_ENABLE_TCPIP
|
||||
|
||||
// no config defaults to 0 => Ethernet
|
||||
enum mg_l2type {
|
||||
MG_TCPIP_L2_ETH = 0,
|
||||
MG_TCPIP_L2_PPP,
|
||||
MG_TCPIP_L2_PPPoE,
|
||||
MG_TCPIP_L2_LOWPAN,
|
||||
MG_TCPIP_L2_USER
|
||||
};
|
||||
enum mg_l2type { MG_TCPIP_L2_ETH = 0, MG_TCPIP_L2_PPP, MG_TCPIP_L2_PPPoE, MG_TCPIP_L2_USER };
|
||||
|
||||
#if defined(__DCC__)
|
||||
#pragma pack(1)
|
||||
@@ -4635,27 +4625,10 @@ struct eth_data {
|
||||
struct pppoe_data { // (struct eth_data *) &pppoe_data = pppoedata.eth
|
||||
struct eth_data eth;
|
||||
};
|
||||
struct lowpan_data {
|
||||
uint16_t pan_id; // User-set PAN ID
|
||||
uint8_t channel; // User-set channel, 11..26
|
||||
uint16_t short_addr; // Our short address, 0xfffe if none
|
||||
uint8_t seq; // MAC sequence number
|
||||
uint8_t proto; // Protocol of the frame being sent, enum mg_l2proto
|
||||
uint8_t dst[8]; // Destination of the frame being sent
|
||||
uint16_t tag; // Next fragmentation datagram tag
|
||||
uint8_t *buf; // Reassembly and decompression buffer
|
||||
uint16_t size; // Datagram being reassembled: size, 0 if none
|
||||
uint16_t received; // Bytes received so far
|
||||
uint16_t tag_rx; // Datagram tag
|
||||
uint8_t src[8]; // Source address
|
||||
bool udp; // UDP header was compressed
|
||||
uint64_t expire; // Reassembly deadline, ms
|
||||
};
|
||||
|
||||
union mg_l2data {
|
||||
struct eth_data eth;
|
||||
struct pppoe_data pppoe;
|
||||
struct lowpan_data lowpan;
|
||||
};
|
||||
|
||||
#if defined(__DCC__)
|
||||
@@ -4664,6 +4637,7 @@ union mg_l2data {
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
|
||||
enum mg_l2proto {
|
||||
MG_TCPIP_L2PROTO_IPV4 = 0,
|
||||
MG_TCPIP_L2PROTO_IPV6,
|
||||
@@ -4724,24 +4698,6 @@ enum {
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
#if MG_ENABLE_IPV6
|
||||
// IPv6 neighbour learned from forwarded packets, see mg_tcpip_route()
|
||||
struct mg_tcpip_nb6 {
|
||||
uint64_t ip[2]; // Neighbour IPv6 address
|
||||
uint8_t l2[sizeof(struct mg_l2addr)]; // Its hardware address
|
||||
};
|
||||
|
||||
// NAT64 session, see mg_tcpip_if::nat64
|
||||
struct mg_tcpip_nat64 {
|
||||
uint64_t ip6[2]; // Node's IPv6 address
|
||||
uint32_t ip4; // IPv4 server address
|
||||
uint16_t port6; // Node's port
|
||||
uint16_t port4; // IPv4 server port
|
||||
uint16_t lport; // Our port on the IPv4 interface
|
||||
uint8_t proto; // 6: TCP, 17: UDP, 0: unused entry
|
||||
};
|
||||
#endif
|
||||
|
||||
// Network interface. Zero-initialise, then set the fields marked "user-set"
|
||||
// before calling mg_tcpip_init(). Fields in the "internal state" section
|
||||
// below are managed by the stack; read them freely but do not modify them.
|
||||
@@ -4776,8 +4732,6 @@ struct mg_tcpip_if {
|
||||
uint8_t prefix[8]; // Global prefix bytes
|
||||
uint8_t prefix_len; // Global prefix length in bits
|
||||
uint64_t gw6[2]; // IPv6 default gateway
|
||||
uint8_t nat64[12]; // NAT64 /96 prefix, RFC 6052. If set, DNS asks
|
||||
// for IPv4 (A) and makes IPv6 from it (DNS64)
|
||||
bool enable_slaac; // Enable SLAAC (stateless address autoconfiguration)
|
||||
bool enable_dhcp6_client; // Enable DHCPv6 client (not yet fully implemented)
|
||||
bool is_ip6_changed; // Set by stack when IPv6 address changes
|
||||
@@ -4809,13 +4763,6 @@ struct mg_tcpip_if {
|
||||
uint8_t gw6mac[sizeof(struct mg_l2addr)]; // IPv6 gateway hardware address
|
||||
uint8_t state6; // Current IPv6 link/IP state (MG_TCPIP_STATE_*)
|
||||
bool gw6_ready; // IPv6 gateway MAC address has been resolved
|
||||
struct mg_tcpip_if *route; // Forward to this interface, see mg_tcpip_route()
|
||||
struct mg_tcpip_nb6 nb6[4]; // Neighbours learned from forwarded packets
|
||||
uint8_t nb6_next; // Next nb6 entry to replace
|
||||
struct mg_tcpip_nat64 nat64s[8]; // NAT64 sessions, if nat64 is set
|
||||
uint8_t nat64_next; // Next nat64s entry to replace
|
||||
uint64_t ra_expire; // Time of the next router advertisement, ms
|
||||
char *dns6_url; // DNS server URL from router advertisement
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -4829,15 +4776,6 @@ void mg_tcpip_init(struct mg_mgr *, struct mg_tcpip_if *);
|
||||
// Frees the TX buffer and RX queue allocated by mg_tcpip_init().
|
||||
void mg_tcpip_free(struct mg_tcpip_if *);
|
||||
|
||||
#if MG_ENABLE_IPV6
|
||||
// Forwards IPv6 packets between two interfaces: packets not addressed to us
|
||||
// go out the other interface. An interface with a static IPv6 prefix (ip6,
|
||||
// prefix and prefix_len set, SLAAC off) gets router advertisements for it,
|
||||
// the other interface gets a route to it, RFC 4191. Call after both
|
||||
// mg_tcpip_init(). Replies to the other side need its sender seen first
|
||||
void mg_tcpip_route(struct mg_tcpip_if *if1, struct mg_tcpip_if *if2);
|
||||
#endif
|
||||
|
||||
// Pushes a received raw frame into ifp->recv_queue. Used by interrupt-driven
|
||||
// or DMA-based drivers (those with rx=NULL) to hand frames to the stack from
|
||||
// an ISR or DMA completion callback. Drops the frame and increments ndrop if
|
||||
@@ -4873,6 +4811,39 @@ extern struct mg_tcpip_driver mg_tcpip_driver_netc;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_mcxw71;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_rw612_154;
|
||||
|
||||
// IEEE 802.15.4 radio channel and PAN ID. Settable in mongoose_config.h
|
||||
#ifndef MG_L2_802154_CHANNEL
|
||||
#define MG_L2_802154_CHANNEL 15 // 11..26
|
||||
#endif
|
||||
#ifndef MG_L2_802154_PAN_ID
|
||||
#define MG_L2_802154_PAN_ID 0xabcd
|
||||
#endif
|
||||
|
||||
// driver_data for IEEE 802.15.4 radio drivers. The interface is Ethernet:
|
||||
// each Ethernet frame is split into broadcast radio frames, and reassembled
|
||||
// on the other side, see mg_l2_802154_tx() and mg_l2_802154_rx()
|
||||
struct mg_l2_802154_data {
|
||||
uint8_t channel; // User-set channel, 11..26
|
||||
uint16_t pan_id; // User-set PAN ID
|
||||
uint8_t seq, tag; // MAC sequence number, tag of the last sent frame
|
||||
uint8_t rxtag, rxnext; // Frame being reassembled: tag, next fragment
|
||||
uint8_t rxsrc[8]; // Its sender
|
||||
uint16_t rxlen; // Its bytes received so far
|
||||
uint8_t rxbuf[1514]; // Reassembly buffer, one Ethernet frame
|
||||
};
|
||||
|
||||
// Splits an Ethernet frame into radio frames, sends each by calling send().
|
||||
// Called by radio drivers' tx(). Return len, or 0 on error
|
||||
size_t mg_l2_802154_tx(struct mg_tcpip_if *ifp, const void *buf, size_t len,
|
||||
size_t (*send)(const uint8_t *, size_t,
|
||||
struct mg_tcpip_if *));
|
||||
|
||||
// Adds a received radio frame f of n bytes to the frame being reassembled.
|
||||
// Called by radio drivers' rx(). Return the Ethernet frame length, copied to
|
||||
// buf, when it is complete, otherwise 0
|
||||
size_t mg_l2_802154_rx(struct mg_tcpip_if *ifp, const uint8_t *f, size_t n,
|
||||
void *buf, size_t len);
|
||||
|
||||
// SPI bus abstraction for SPI-attached network chips (e.g. W5500).
|
||||
// Populate and assign to the driver's driver_data field.
|
||||
struct mg_tcpip_spi {
|
||||
@@ -5262,24 +5233,23 @@ struct mg_tcpip_driver_imxrt_data {
|
||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_MCXW71) && \
|
||||
MG_ENABLE_DRIVER_MCXW71
|
||||
|
||||
// NXP MCXW71 IEEE 802.15.4 radio, ZLL link layer. Frames passed to tx() and
|
||||
// returned by rx() are raw 802.15.4 frames: MAC header and payload, no FCS.
|
||||
// NXP MCXW71 IEEE 802.15.4 radio, ZLL link layer, as an Ethernet interface:
|
||||
// Ethernet frames are split into radio frames, see mg_l2_802154_tx().
|
||||
// The transceiver (XCVR) must be set up for 802.15.4 before mg_tcpip_init(),
|
||||
// see hal_radio_init() in the FRDM-MCXW71 tutorial.
|
||||
// Use with MG_TCPIP_L2_LOWPAN: channel, PAN ID and addresses are taken from
|
||||
// ifp->l2data.lowpan and ifp->mac. No driver_data
|
||||
// driver_data: struct mg_l2_802154_data, channel and PAN ID set
|
||||
|
||||
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
||||
do { \
|
||||
static struct mg_tcpip_if mif_; \
|
||||
mif_.l2type = MG_TCPIP_L2_LOWPAN; \
|
||||
mif_.driver = &mg_tcpip_driver_mcxw71; \
|
||||
mif_.l2data.lowpan.channel = 15; \
|
||||
mif_.l2data.lowpan.pan_id = 0xabcd; \
|
||||
MG_SET_MAC_ADDRESS(mif_.mac); /* EUI-64 */ \
|
||||
mg_tcpip_init(mgr, &mif_); \
|
||||
(mgr)->use_dns6 = true; /* IPv6 only: no IPv4 over 6LoWPAN */ \
|
||||
MG_INFO(("Driver: mcxw71, 6LoWPAN")); \
|
||||
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
||||
do { \
|
||||
static struct mg_l2_802154_data driver_data_; \
|
||||
static struct mg_tcpip_if mif_; \
|
||||
driver_data_.channel = MG_L2_802154_CHANNEL; \
|
||||
driver_data_.pan_id = MG_L2_802154_PAN_ID; \
|
||||
mif_.driver = &mg_tcpip_driver_mcxw71; \
|
||||
mif_.driver_data = &driver_data_; \
|
||||
MG_SET_MAC_ADDRESS(mif_.mac); \
|
||||
mg_tcpip_init(mgr, &mif_); \
|
||||
MG_INFO(("Driver: mcxw71, channel %d", MG_L2_802154_CHANNEL)); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
@@ -5506,27 +5476,26 @@ struct mg_tcpip_driver_rw612_data {
|
||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RW612_154) && \
|
||||
MG_ENABLE_DRIVER_RW612_154
|
||||
|
||||
// NXP RW612 IEEE 802.15.4 radio. The radio runs on CPU2: NXP BLE/802.15.4
|
||||
// combo firmware, an OpenThread radio co-processor (RCP). The firmware must
|
||||
// be loaded and running before mg_tcpip_init(), see the FRDM-RW612 lowpan
|
||||
// tutorial. The driver talks to it over the CPU2 <-> CPU3 IMU mailbox:
|
||||
// IMUMC messages carrying HDLC framed Spinel. Frames passed to tx() and
|
||||
// returned by rx() are raw 802.15.4 frames: MAC header and payload, no FCS.
|
||||
// Use with MG_TCPIP_L2_LOWPAN: channel, PAN ID and addresses are taken from
|
||||
// ifp->l2data.lowpan and ifp->mac. No driver_data
|
||||
// NXP RW612 IEEE 802.15.4 radio, as an Ethernet interface: Ethernet frames
|
||||
// are split into radio frames, see mg_l2_802154_tx(). The radio runs on
|
||||
// CPU2: NXP BLE/802.15.4 combo firmware, an OpenThread radio co-processor
|
||||
// (RCP). The firmware must be loaded and running before mg_tcpip_init(), see
|
||||
// the FRDM-RW612 eth_radio_bridge tutorial. The driver talks to it over the
|
||||
// CPU2 <-> CPU3 IMU mailbox: IMUMC messages carrying HDLC framed Spinel.
|
||||
// driver_data: struct mg_l2_802154_data, channel and PAN ID set
|
||||
|
||||
#ifndef MG_TCPIP_DRIVER_INIT
|
||||
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
||||
do { \
|
||||
static struct mg_tcpip_if mif_; \
|
||||
mif_.l2type = MG_TCPIP_L2_LOWPAN; \
|
||||
mif_.driver = &mg_tcpip_driver_rw612_154; \
|
||||
mif_.l2data.lowpan.channel = 15; \
|
||||
mif_.l2data.lowpan.pan_id = 0xabcd; \
|
||||
MG_SET_MAC_ADDRESS(mif_.mac); /* EUI-64 */ \
|
||||
mg_tcpip_init(mgr, &mif_); \
|
||||
(mgr)->use_dns6 = true; /* IPv6 only: no IPv4 over 6LoWPAN */ \
|
||||
MG_INFO(("Driver: rw612_154, 6LoWPAN")); \
|
||||
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
||||
do { \
|
||||
static struct mg_l2_802154_data driver_data_; \
|
||||
static struct mg_tcpip_if mif_; \
|
||||
driver_data_.channel = MG_L2_802154_CHANNEL; \
|
||||
driver_data_.pan_id = MG_L2_802154_PAN_ID; \
|
||||
mif_.driver = &mg_tcpip_driver_rw612_154; \
|
||||
mif_.driver_data = &driver_data_; \
|
||||
MG_SET_MAC_ADDRESS(mif_.mac); \
|
||||
mg_tcpip_init(mgr, &mif_); \
|
||||
MG_INFO(("Driver: rw612_154, channel %d", MG_L2_802154_CHANNEL)); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
@@ -5694,6 +5663,13 @@ struct mg_tcpip_driver_stm32f_data {
|
||||
#if !defined(MG_ENABLE_DRIVER_STM32H)
|
||||
#define MG_ENABLE_DRIVER_STM32H 0
|
||||
#endif
|
||||
#if !defined(MG_ENABLE_DRIVER_STM32C)
|
||||
#define MG_ENABLE_DRIVER_STM32C 0
|
||||
#endif
|
||||
#if MG_ENABLE_DRIVER_STM32C // STM32C5: STM32H5 MAC, the IRQ is ETH1
|
||||
#undef MG_ENABLE_DRIVER_STM32H
|
||||
#define MG_ENABLE_DRIVER_STM32H 1
|
||||
#endif
|
||||
#if !defined(MG_ENABLE_DRIVER_MCXN)
|
||||
#define MG_ENABLE_DRIVER_MCXN 0
|
||||
#endif
|
||||
@@ -5733,9 +5709,11 @@ struct mg_tcpip_driver_stm32h_data {
|
||||
#define MG_DRIVER_MDC_CR 4
|
||||
#endif
|
||||
|
||||
#if MG_ENABLE_DRIVER_STM32H && MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
|
||||
#if MG_ENABLE_DRIVER_STM32H && !MG_ENABLE_DRIVER_STM32C && \
|
||||
MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
|
||||
#define MG_ENABLE_ETH_IRQ() NVIC_EnableIRQ(ETH_IRQn)
|
||||
#elif MG_ENABLE_DRIVER_STM32N && MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
|
||||
#elif (MG_ENABLE_DRIVER_STM32N || MG_ENABLE_DRIVER_STM32C) && \
|
||||
MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
|
||||
#define MG_ENABLE_ETH_IRQ() NVIC_EnableIRQ(ETH1_IRQn)
|
||||
#elif !defined(MG_ENABLE_ETH_IRQ)
|
||||
#define MG_ENABLE_ETH_IRQ()
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
</environment>
|
||||
</environments>
|
||||
<releases>
|
||||
<release date="2026-10-21" version="7.23.3">See https://github.com/cesanta/mongoose/releases/tag/7.23</release>
|
||||
<release date="2026-10-19" version="7.23.2">See https://github.com/cesanta/mongoose/releases/tag/7.23</release>
|
||||
<release date="2026-09-28" version="7.23.1">See https://github.com/cesanta/mongoose/releases/tag/7.23</release>
|
||||
<release date="2026-05-11" version="7.21.7">See https://github.com/cesanta/mongoose/releases/tag/7.21</release>
|
||||
@@ -28,7 +29,7 @@
|
||||
<keyword>IMPLEMENTED_WITH_STM32CUBEMX</keyword>
|
||||
</keywords>
|
||||
<components>
|
||||
<component Cclass="Mongoose" Cgroup="Networking Core" Cversion="7.23.2" maxInstances="1">
|
||||
<component Cclass="Mongoose" Cgroup="Networking Core" Cversion="7.23.3" maxInstances="1">
|
||||
<description>Networking Core</description>
|
||||
<files>
|
||||
<file category="doc" name="README.md"/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VERSION = 7.23.2
|
||||
VERSION = 7.23.3
|
||||
REPO_ROOT = ../..
|
||||
MX ?= /Applications/STMicroelectronics/STM32CubeMX.app/Contents/MacOs/STM32CubeMX
|
||||
FILES = Cesanta.I-CUBE-Mongoose.pdsc Release_Notes.html mongoose_config.h README.md .config/mongoose_parameters.json
|
||||
|
||||
+5
-64
@@ -32,6 +32,7 @@
|
||||
|
||||
#if MG_ENABLE_TCPIP && !defined(MG_ENABLE_DRIVER_STM32F) && \
|
||||
!defined(MG_ENABLE_DRIVER_STM32H) && !defined(MG_ENABLE_DRIVER_STM32N) && \
|
||||
!defined(MG_ENABLE_DRIVER_STM32C) && \
|
||||
!defined(MG_ENABLE_DRIVER_CYW) && !defined(MG_ENABLE_DRIVER_CYW_SDIO) && \
|
||||
!defined(MG_ENABLE_DRIVER_ST67W6)
|
||||
#if defined(STM32F1) || defined(STM32F2) || defined(STM32F4) || defined(STM32F7)
|
||||
@@ -40,6 +41,8 @@
|
||||
#define MG_ENABLE_DRIVER_STM32H 1
|
||||
#elif defined(STM32N6)
|
||||
#define MG_ENABLE_DRIVER_STM32N 1
|
||||
#elif defined(STM32C5)
|
||||
#define MG_ENABLE_DRIVER_STM32C 1
|
||||
#else
|
||||
#error Select a driver in mongoose_config.h
|
||||
#endif
|
||||
@@ -56,70 +59,8 @@
|
||||
#define MG_OTA MG_OTA_STM32H5
|
||||
#elif !defined(MG_OTA) && defined(STM32H7)
|
||||
#define MG_OTA MG_OTA_STM32H7
|
||||
#endif
|
||||
|
||||
// OTA rollback timer via IWDG. Fixed 10 s hang-detection window (prescaler
|
||||
// /1024, LSI ~32 kHz: reload = 10 * 32000 / 1024 = 312). mg_ota_poll() feeds
|
||||
// the watchdog every 500 ms while in MG_OTA_TESTING state; if the event loop
|
||||
// hangs for 10 s, the IWDG fires and the device rolls back.
|
||||
// STM32H7 names the peripheral IWDG1; all others use IWDG.
|
||||
// The total evaluation period is set by MG_OTA_ROLLBACK_TIMEOUT_SECONDS.
|
||||
// STM32H7 names the watchdog peripheral IWDG1; all other families use IWDG.
|
||||
#if defined(IWDG1) && !defined(IWDG)
|
||||
#define MG_IWDG IWDG1
|
||||
#else
|
||||
#define MG_IWDG IWDG
|
||||
#endif
|
||||
// PR=6 → /256 prescaler, RLR=1250 → ~10 s at LSI 32 kHz.
|
||||
// Works on F4/F7/H5/H7; uses direct register writes so it is safe before HAL_Init().
|
||||
#ifndef MG_OTA_ROLLBACK_TIMER_START
|
||||
#define MG_OTA_ROLLBACK_TIMER_START() \
|
||||
do { \
|
||||
MG_IWDG->KR = 0xCCCCU; /* start + LSI */ \
|
||||
MG_IWDG->KR = 0x5555U; /* unlock PR/RLR */ \
|
||||
MG_IWDG->PR = 6U; /* /256 */ \
|
||||
MG_IWDG->RLR = 1250U; /* 10 s */ \
|
||||
while (MG_IWDG->SR & (IWDG_SR_PVU | IWDG_SR_RVU)) (void) 0; \
|
||||
MG_IWDG->KR = 0xAAAAU; /* reload */ \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
// Feed (reload) the IWDG. Called from mg_ota_poll() every 500 ms.
|
||||
#ifndef MG_OTA_ROLLBACK_TIMER_FEED
|
||||
#define MG_OTA_ROLLBACK_TIMER_FEED() (MG_IWDG->KR = 0xAAAAU)
|
||||
#endif
|
||||
|
||||
// OTA state in a backup register that survives warm resets but clears on POR.
|
||||
// STATE_GET reads work without any setup. STATE_SET enables the RTC APB clock
|
||||
// and unlocks backup domain write access via direct register writes, so it is
|
||||
// safe to call from any context (before or after HAL_Init).
|
||||
// H5 (TAMP->BKP0R), H7 (RTC->BKP0R), F4/F7 (RTC->BKP0R).
|
||||
#ifndef MG_OTA_STATE_GET
|
||||
#if defined(PWR_DBPCR_DBP)
|
||||
#define MG_OTA_DBP_ENABLE() (PWR->DBPCR |= PWR_DBPCR_DBP)
|
||||
#elif defined(PWR_CR1_DBP)
|
||||
#define MG_OTA_DBP_ENABLE() (PWR->CR1 |= PWR_CR1_DBP)
|
||||
#else
|
||||
#define MG_OTA_DBP_ENABLE() (PWR->CR |= PWR_CR_DBP)
|
||||
#endif
|
||||
|
||||
#if defined(TAMP)
|
||||
#define MG_OTA_STATE_GET() \
|
||||
(RCC->APB3ENR |= RCC_APB3ENR_RTCAPBEN, TAMP->BKP0R)
|
||||
#define MG_OTA_STATE_SET(v) \
|
||||
(RCC->APB3ENR |= RCC_APB3ENR_RTCAPBEN, MG_OTA_DBP_ENABLE(), \
|
||||
(TAMP->BKP0R = (uint32_t) (v)), TAMP->BKP0R)
|
||||
#elif defined(RCC_APB4ENR_RTCAPBEN)
|
||||
#define MG_OTA_STATE_GET() (RTC->BKP0R)
|
||||
#define MG_OTA_STATE_SET(v) \
|
||||
(RCC->APB4ENR |= RCC_APB4ENR_RTCAPBEN, MG_OTA_DBP_ENABLE(), \
|
||||
(RTC->BKP0R = (uint32_t) (v)))
|
||||
#else
|
||||
#define MG_OTA_STATE_GET() (RTC->BKP0R)
|
||||
#define MG_OTA_STATE_SET(v) \
|
||||
(RCC->APB1ENR |= RCC_APB1ENR_PWREN, MG_OTA_DBP_ENABLE(), \
|
||||
(RTC->BKP0R = (uint32_t) (v)))
|
||||
#endif
|
||||
#elif !defined(MG_OTA) && defined(STM32C5)
|
||||
#define MG_OTA MG_OTA_STM32C5
|
||||
#endif
|
||||
|
||||
// use HAL-defined execute-in-ram section
|
||||
|
||||
+4
-2
@@ -22,8 +22,10 @@
|
||||
#include <esp_system.h>
|
||||
#include <esp_mac.h>
|
||||
#include <esp_idf_version.h>
|
||||
// #include <esp_memory_utils.h> // For health record coredumps
|
||||
// #include <esp_core_dump.h> // For health record coredumps
|
||||
#if MG_ENABLE_HEALTH
|
||||
#include <esp_memory_utils.h>
|
||||
#include <esp_core_dump.h>
|
||||
#endif
|
||||
|
||||
#define MG_PATH_MAX 128
|
||||
|
||||
|
||||
@@ -65,13 +65,8 @@
|
||||
#endif
|
||||
|
||||
#ifndef MG_ENABLE_IPV6
|
||||
#if (defined(MG_ENABLE_DRIVER_MCXW71) && MG_ENABLE_DRIVER_MCXW71) || \
|
||||
(defined(MG_ENABLE_DRIVER_RW612_154) && MG_ENABLE_DRIVER_RW612_154)
|
||||
#define MG_ENABLE_IPV6 1 // 802.15.4 radio: 6LoWPAN carries IPv6 only
|
||||
#else
|
||||
#define MG_ENABLE_IPV6 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef MG_IPV6_V6ONLY
|
||||
#define MG_IPV6_V6ONLY 0 // IPv6 socket binds only to V6, not V4 address
|
||||
@@ -216,8 +211,6 @@
|
||||
|
||||
#endif
|
||||
|
||||
// Set the interface hardware address. Ethernet: 6-byte MAC. 6LoWPAN: 8-byte
|
||||
// EUI-64, the IPv6 interface ID is made from it. Not set: random per boot
|
||||
#ifndef MG_SET_MAC_ADDRESS
|
||||
#define MG_SET_MAC_ADDRESS(mac)
|
||||
#endif
|
||||
@@ -255,4 +248,3 @@
|
||||
#ifndef MG_ENABLE_HEALTH
|
||||
#define MG_ENABLE_HEALTH 0
|
||||
#endif
|
||||
|
||||
+16
-1
@@ -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,
|
||||
struct mg_dash_user *u, bool stream) {
|
||||
struct mg_dash *dash = (struct mg_dash *) c->fn_data;
|
||||
@@ -581,7 +596,7 @@ void mg_dash_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
} else if (mg_match(hm->uri, mg_str("/api/ota"), NULL)) {
|
||||
// Authenticated above. If streaming started, the stream handler owns
|
||||
// the connection and c->data, so skip the marker check below
|
||||
if (mg_http_stream_body(c, hm, mg_http_ota_cb, NULL)) return;
|
||||
if (mg_http_stream_body(c, hm, otacb, NULL)) return;
|
||||
}
|
||||
if (d->marker != '\0') mg_log_http_req(c, hm);
|
||||
} else if (ev == MG_EV_HTTP_MSG && d->marker != '\0') {
|
||||
|
||||
@@ -173,15 +173,6 @@ bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *dm) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#if MG_ENABLE_TCPIP && MG_ENABLE_IPV6
|
||||
// NAT64 prefix of the built-in stack interface, or NULL if none
|
||||
static const uint8_t *nat64_prefix(struct mg_mgr *mgr) {
|
||||
static const uint8_t zero[12] = {0};
|
||||
if (mgr->ifp == NULL || memcmp(mgr->ifp->nat64, zero, 12) == 0) return NULL;
|
||||
return mgr->ifp->nat64;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void dns_cb(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct dns_data *d, *tmp;
|
||||
struct dns_data **head = (struct dns_data **) &c->mgr->active_dns_requests;
|
||||
@@ -205,14 +196,6 @@ static void dns_cb(struct mg_connection *c, int ev, void *ev_data) {
|
||||
// MG_INFO(("d %p %hu %hu", d, d->txnid, dm.txnid));
|
||||
if (dm.txnid != d->txnid) continue;
|
||||
if (d->c->is_resolving) {
|
||||
#if MG_ENABLE_TCPIP && MG_ENABLE_IPV6
|
||||
if (dm.resolved && !dm.addr.is_ip6 && nat64_prefix(c->mgr) != NULL) {
|
||||
uint32_t ip4 = dm.addr.addr.ip4; // DNS64: prefix + IPv4, RFC 6052
|
||||
memcpy(dm.addr.addr.ip, nat64_prefix(c->mgr), 12);
|
||||
memcpy(&dm.addr.addr.ip[12], &ip4, 4);
|
||||
dm.addr.is_ip6 = true;
|
||||
}
|
||||
#endif
|
||||
if (dm.resolved) {
|
||||
dm.addr.port = d->c->rem.port; // Save port
|
||||
d->c->rem = dm.addr; // Copy resolved address
|
||||
@@ -331,11 +314,7 @@ void mg_resolve(struct mg_connection *c, const char *url) {
|
||||
} else {
|
||||
// host is not an IP nor a .local, send DNS resolution request
|
||||
struct mg_dns *dns = c->mgr->use_dns6 ? &c->mgr->dns6 : &c->mgr->dns4;
|
||||
bool aaaa = c->mgr->use_dns6;
|
||||
#if MG_ENABLE_TCPIP && MG_ENABLE_IPV6
|
||||
if (nat64_prefix(c->mgr) != NULL) aaaa = false; // DNS64: ask for A
|
||||
#endif
|
||||
sendnsreq(c, &host, c->mgr->dnstimeout, dns, aaaa);
|
||||
sendnsreq(c, &host, c->mgr->dnstimeout, dns, c->mgr->use_dns6);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+31
-69
@@ -17,6 +17,8 @@ struct mg_zll {
|
||||
PART_ID, COEX_CTRL, COEX_PRIORITY, ENHACK_CTRL0;
|
||||
};
|
||||
|
||||
// TrustZone: code built for the secure state (-mcmse) uses the secure alias
|
||||
// of the radio registers, 0x5xxxxxxx. Otherwise, the non-secure alias
|
||||
#if defined(__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE & 2)
|
||||
#define MGI_ZLL ((struct mg_zll *) 0x58A01000)
|
||||
#define MGI_PKT_RAM ((volatile uint8_t *) 0x58A08000)
|
||||
@@ -24,6 +26,7 @@ struct mg_zll {
|
||||
#define MGI_ZLL ((struct mg_zll *) 0x48A01000)
|
||||
#define MGI_PKT_RAM ((volatile uint8_t *) 0x48A08000)
|
||||
#endif
|
||||
|
||||
#define MGI_TX_PB MGI_PKT_RAM // Length byte (incl. FCS), then frame
|
||||
#define MGI_RX_PB (MGI_PKT_RAM + 0x80) // Received frame, no length byte
|
||||
|
||||
@@ -31,35 +34,20 @@ struct mg_zll {
|
||||
#define MGI_IRQSTS_RXIRQ MG_BIT(2)
|
||||
#define MGI_IRQSTS_RX_LEN(x) (((x) >> 24) & 0x7f) // Frame length incl. FCS
|
||||
#define MGI_PHY_CTRL_XCVSEQ 7U
|
||||
#define MGI_PHY_CTRL_AUTOACK MG_BIT(3)
|
||||
#define MGI_PHY_CTRL_RXACKRQD MG_BIT(4)
|
||||
#define MGI_PHY_CTRL_CCABFRTX MG_BIT(5)
|
||||
#define MGI_PHY_CTRL_SLOTTED MG_BIT(6)
|
||||
#define MGI_PHY_CTRL_PROMISCUOUS MG_BIT(25)
|
||||
#define MGI_PHY_CTRL_ALL_IRQ_MASKS 0x8007ff00U // Bits 8..18, 31: mask all IRQs
|
||||
#define MGI_SEQ_STATE_MASK 0x1fU
|
||||
#define MGI_SEQ_STATE_RX_WU 0x10U // RX warm up
|
||||
#define MGI_SEQ_CTRL_NO_RX_RECYCLE MG_BIT(5)
|
||||
#define MGI_FRAME_FILTER 0x70bU // Beacon, data, command; versions 0..2
|
||||
#define MGI_ACKDELAY_MASK 0x7fU
|
||||
#define MGI_ACKDELAY_VALUE 0x7cU // -4, 802.15.4 turnaround. See PhyHwInit()
|
||||
|
||||
enum { MGI_SEQ_IDLE = 0, MGI_SEQ_RX = 1, MGI_SEQ_TX = 2, MGI_SEQ_TR = 4 }; // PHY_CTRL[XCVSEQ]
|
||||
#define MGI_FCF_ACK_REQUEST 0x20U // First frame byte: acknowledgment request
|
||||
#define MGI_TX_TIMEOUT 400U // Symbols, 16 us: 127 bytes, then ACK wait
|
||||
#define MGI_TX_TRIES 10 // Tries for unacknowledged frames
|
||||
#define MGI_TX_BACKOFF 64U // Symbols, 1 ms: backoff grows by this per try
|
||||
enum { MGI_SEQ_IDLE = 0, MGI_SEQ_RX = 1, MGI_SEQ_TX = 2 }; // PHY_CTRL[XCVSEQ]
|
||||
#define MGI_TX_TIMEOUT 400U // Symbols, 16 us: 127 bytes
|
||||
|
||||
// Symbol timer, 16 us, 24 bits
|
||||
static uint32_t mg_mcxw71_symbols(void) {
|
||||
return MGI_ZLL->EVENT_TMR >> 8;
|
||||
}
|
||||
|
||||
static void mg_mcxw71_delay(uint32_t symbols) {
|
||||
uint32_t start = mg_mcxw71_symbols();
|
||||
while (((mg_mcxw71_symbols() - start) & 0xffffffU) < symbols) (void) 0;
|
||||
}
|
||||
|
||||
// Wait for the sequence to end, up to MGI_TX_TIMEOUT symbols
|
||||
static bool mg_mcxw71_wait(void) {
|
||||
uint32_t start = mg_mcxw71_symbols();
|
||||
@@ -72,7 +60,8 @@ static bool mg_mcxw71_wait(void) {
|
||||
// Stop the current sequence, wait for the sequencer to go idle. See PhyAbort()
|
||||
static void mg_mcxw71_idle(void) {
|
||||
if (MGI_ZLL->PHY_CTRL & MGI_PHY_CTRL_XCVSEQ) {
|
||||
while ((MGI_ZLL->SEQ_STATE & MGI_SEQ_STATE_MASK) == MGI_SEQ_STATE_RX_WU) (void) 0;
|
||||
while ((MGI_ZLL->SEQ_STATE & MGI_SEQ_STATE_MASK) == MGI_SEQ_STATE_RX_WU)
|
||||
(void) 0;
|
||||
MGI_ZLL->PHY_CTRL &= ~MGI_PHY_CTRL_XCVSEQ;
|
||||
while (MGI_ZLL->SEQ_STATE & MGI_SEQ_STATE_MASK) (void) 0;
|
||||
}
|
||||
@@ -85,88 +74,61 @@ static void mg_mcxw71_start(uint32_t seq) {
|
||||
MGI_ZLL->PHY_CTRL |= seq;
|
||||
}
|
||||
|
||||
// Program our addresses for hardware filtering and auto-ACK: PAN ID, short
|
||||
// address and EUI-64 (little endian), when they change. The L2 sets them
|
||||
static void mg_mcxw71_addrs(struct mg_tcpip_if *ifp) {
|
||||
struct lowpan_data *l = &ifp->l2data.lowpan;
|
||||
const uint8_t *e = ifp->mac;
|
||||
uint32_t s = ((uint32_t) l->short_addr << 16) | l->pan_id;
|
||||
uint32_t lo = (uint32_t) (e[7] | (e[6] << 8) | (e[5] << 16)) |
|
||||
((uint32_t) e[4] << 24);
|
||||
uint32_t hi = (uint32_t) (e[3] | (e[2] << 8) | (e[1] << 16)) |
|
||||
((uint32_t) e[0] << 24);
|
||||
if (MGI_ZLL->MACSHORTADDRS0 != s || MGI_ZLL->MACLONGADDRS0_LSB != lo ||
|
||||
MGI_ZLL->MACLONGADDRS0_MSB != hi) {
|
||||
mg_mcxw71_idle();
|
||||
MGI_ZLL->MACSHORTADDRS0 = s;
|
||||
MGI_ZLL->MACLONGADDRS0_LSB = lo, MGI_ZLL->MACLONGADDRS0_MSB = hi;
|
||||
mg_mcxw71_start(MGI_SEQ_RX);
|
||||
}
|
||||
}
|
||||
|
||||
static bool mg_mcxw71_init(struct mg_tcpip_if *ifp) {
|
||||
uint8_t channel = ifp->l2data.lowpan.channel;
|
||||
if (ifp->l2type != MG_TCPIP_L2_LOWPAN || channel < 11 || channel > 26) {
|
||||
MG_ERROR(("Use MG_TCPIP_L2_LOWPAN, channel 11..26"));
|
||||
struct mg_l2_802154_data *d =
|
||||
(struct mg_l2_802154_data *) ifp->driver_data;
|
||||
if (d == NULL || d->channel < 11 || d->channel > 26) {
|
||||
MG_ERROR(("Set driver_data, channel 11..26"));
|
||||
return false;
|
||||
}
|
||||
MGI_ZLL->SEQ_CTRL_STS |= MGI_SEQ_CTRL_NO_RX_RECYCLE;
|
||||
// Mask all interrupts, we poll. Filter frames by address, auto-ACK
|
||||
MGI_ZLL->PHY_CTRL = MGI_PHY_CTRL_ALL_IRQ_MASKS | MGI_PHY_CTRL_AUTOACK;
|
||||
MGI_ZLL->PHY_CTRL = MGI_PHY_CTRL_ALL_IRQ_MASKS; // Mask all IRQs, we poll
|
||||
MGI_ZLL->IRQSTS = MGI_ZLL->IRQSTS;
|
||||
MGI_ZLL->SAM_CTRL = 0;
|
||||
MGI_ZLL->RX_FRAME_FILTER = MGI_FRAME_FILTER;
|
||||
MGI_ZLL->ACKDELAY = (MGI_ZLL->ACKDELAY & ~MGI_ACKDELAY_MASK) | MGI_ACKDELAY_VALUE;
|
||||
MGI_ZLL->TMR_PRESCALE = 5; // 16 us timebase, one symbol
|
||||
MGI_ZLL->CHANNEL_NUM0 = channel;
|
||||
MGI_ZLL->PA_PWR = 0x3f; // PA slices, max output power
|
||||
MGI_ZLL->CHANNEL_NUM0 = d->channel;
|
||||
MGI_ZLL->MACSHORTADDRS0 = (0xfffeU << 16) | d->pan_id; // Filter by PAN ID
|
||||
MGI_ZLL->PA_PWR = 0x3f; // PA slices, max output power
|
||||
mg_mcxw71_start(MGI_SEQ_RX);
|
||||
mg_mcxw71_addrs(ifp);
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t mg_mcxw71_tx(const void *buf, size_t len,
|
||||
struct mg_tcpip_if *ifp) {
|
||||
const uint8_t *p = (const uint8_t *) buf;
|
||||
// Send one radio frame: MAC header and payload, no FCS
|
||||
static size_t mg_mcxw71_send(const uint8_t *p, size_t len,
|
||||
struct mg_tcpip_if *ifp) {
|
||||
size_t i;
|
||||
bool ok = false, ack = len > 0 && (p[0] & MGI_FCF_ACK_REQUEST);
|
||||
int tries;
|
||||
if (len == 0 || len > 125) return 0; // 127 bytes max, incl. 2 bytes FCS
|
||||
bool ok;
|
||||
mg_mcxw71_idle();
|
||||
MGI_TX_PB[0] = (uint8_t) (len + 2); // FCS is added by hardware
|
||||
for (i = 0; i < len; i++) MGI_TX_PB[i + 1] = p[i]; // 8-bit access only
|
||||
MGI_ZLL->PHY_CTRL &= ~(MGI_PHY_CTRL_CCABFRTX | MGI_PHY_CTRL_RXACKRQD | MGI_PHY_CTRL_SLOTTED);
|
||||
if (ack) MGI_ZLL->PHY_CTRL |= MGI_PHY_CTRL_RXACKRQD;
|
||||
// Frames with ACK request: transmit, then receive the ACK (TR sequence),
|
||||
// retry with growing backoff. See PhyPdDataRequest(), PhyISR.c
|
||||
for (tries = 0; tries < (ack ? MGI_TX_TRIES : 1) && !ok; tries++) {
|
||||
if (tries > 0) mg_mcxw71_delay(MGI_TX_BACKOFF * (uint32_t) tries);
|
||||
mg_mcxw71_start(ack ? MGI_SEQ_TR : MGI_SEQ_TX);
|
||||
ok = mg_mcxw71_wait() && (!ack || (MGI_ZLL->IRQSTS & MGI_IRQSTS_RXIRQ));
|
||||
mg_mcxw71_idle();
|
||||
}
|
||||
MGI_ZLL->PHY_CTRL &= ~MGI_PHY_CTRL_RXACKRQD;
|
||||
mg_mcxw71_start(MGI_SEQ_TX);
|
||||
ok = mg_mcxw71_wait();
|
||||
mg_mcxw71_idle();
|
||||
mg_mcxw71_start(MGI_SEQ_RX);
|
||||
if (!ok) MG_DEBUG(("TX failed, %d tries", tries));
|
||||
(void) ifp;
|
||||
return ok ? len : 0;
|
||||
}
|
||||
|
||||
static size_t mg_mcxw71_tx(const void *buf, size_t len,
|
||||
struct mg_tcpip_if *ifp) {
|
||||
return mg_l2_802154_tx(ifp, buf, len, mg_mcxw71_send);
|
||||
}
|
||||
|
||||
static size_t mg_mcxw71_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
||||
uint32_t irqsts = MGI_ZLL->IRQSTS;
|
||||
uint8_t f[125];
|
||||
size_t i, n = 0;
|
||||
mg_mcxw71_addrs(ifp);
|
||||
if ((irqsts & MGI_IRQSTS_SEQIRQ) == 0) return 0; // Still receiving
|
||||
if (irqsts & MGI_IRQSTS_RXIRQ) {
|
||||
n = MGI_IRQSTS_RX_LEN(irqsts);
|
||||
n = n >= 2 ? n - 2 : 0; // Strip FCS
|
||||
if (n > len) n = len;
|
||||
for (i = 0; i < n; i++) ((uint8_t *) buf)[i] = MGI_RX_PB[i];
|
||||
if (n > sizeof(f)) n = sizeof(f);
|
||||
for (i = 0; i < n; i++) f[i] = MGI_RX_PB[i];
|
||||
}
|
||||
mg_mcxw71_idle();
|
||||
mg_mcxw71_start(MGI_SEQ_RX);
|
||||
(void) ifp;
|
||||
return n;
|
||||
return mg_l2_802154_rx(ifp, f, n, buf, len);
|
||||
}
|
||||
|
||||
static bool mg_mcxw71_poll(struct mg_tcpip_if *ifp, bool s1) {
|
||||
|
||||
+14
-15
@@ -3,24 +3,23 @@
|
||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_MCXW71) && \
|
||||
MG_ENABLE_DRIVER_MCXW71
|
||||
|
||||
// NXP MCXW71 IEEE 802.15.4 radio, ZLL link layer. Frames passed to tx() and
|
||||
// returned by rx() are raw 802.15.4 frames: MAC header and payload, no FCS.
|
||||
// NXP MCXW71 IEEE 802.15.4 radio, ZLL link layer, as an Ethernet interface:
|
||||
// Ethernet frames are split into radio frames, see mg_l2_802154_tx().
|
||||
// The transceiver (XCVR) must be set up for 802.15.4 before mg_tcpip_init(),
|
||||
// see hal_radio_init() in the FRDM-MCXW71 tutorial.
|
||||
// Use with MG_TCPIP_L2_LOWPAN: channel, PAN ID and addresses are taken from
|
||||
// ifp->l2data.lowpan and ifp->mac. No driver_data
|
||||
// driver_data: struct mg_l2_802154_data, channel and PAN ID set
|
||||
|
||||
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
||||
do { \
|
||||
static struct mg_tcpip_if mif_; \
|
||||
mif_.l2type = MG_TCPIP_L2_LOWPAN; \
|
||||
mif_.driver = &mg_tcpip_driver_mcxw71; \
|
||||
mif_.l2data.lowpan.channel = 15; \
|
||||
mif_.l2data.lowpan.pan_id = 0xabcd; \
|
||||
MG_SET_MAC_ADDRESS(mif_.mac); /* EUI-64 */ \
|
||||
mg_tcpip_init(mgr, &mif_); \
|
||||
(mgr)->use_dns6 = true; /* IPv6 only: no IPv4 over 6LoWPAN */ \
|
||||
MG_INFO(("Driver: mcxw71, 6LoWPAN")); \
|
||||
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
||||
do { \
|
||||
static struct mg_l2_802154_data driver_data_; \
|
||||
static struct mg_tcpip_if mif_; \
|
||||
driver_data_.channel = MG_L2_802154_CHANNEL; \
|
||||
driver_data_.pan_id = MG_L2_802154_PAN_ID; \
|
||||
mif_.driver = &mg_tcpip_driver_mcxw71; \
|
||||
mif_.driver_data = &driver_data_; \
|
||||
MG_SET_MAC_ADDRESS(mif_.mac); \
|
||||
mg_tcpip_init(mgr, &mif_); \
|
||||
MG_INFO(("Driver: mcxw71, channel %d", MG_L2_802154_CHANNEL)); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
+29
-50
@@ -73,8 +73,6 @@ enum {
|
||||
MGI_PROP_NCP_VERSION = 2,
|
||||
MGI_PROP_PHY_ENABLED = 0x20,
|
||||
MGI_PROP_PHY_CHAN = 0x21,
|
||||
MGI_PROP_LADDR = 0x34,
|
||||
MGI_PROP_SADDR = 0x35,
|
||||
MGI_PROP_PANID = 0x36,
|
||||
MGI_PROP_RAW_STREAM = 0x37,
|
||||
MGI_PROP_STREAM_RAW = 0x71
|
||||
@@ -84,7 +82,7 @@ enum {
|
||||
#define MGI_STATUS_RESET_MAX 127
|
||||
#define MGI_TX_TIMEOUT_MS 200 // Wait for transmit done, incl. retries
|
||||
#define MGI_RSP_TIMEOUT_MS 500
|
||||
#define MGI_RXQ_CNT 4
|
||||
#define MGI_RXQ_CNT 16 // A whole Ethernet frame, 15 radio frames
|
||||
|
||||
// Driver state, one radio
|
||||
struct mg_rw612 {
|
||||
@@ -103,9 +101,6 @@ struct mg_rw612 {
|
||||
uint8_t rxq[MGI_RXQ_CNT][127]; // Received frames, no FCS
|
||||
uint8_t rxlen[MGI_RXQ_CNT]; // Their lengths
|
||||
uint8_t rxhead, rxtail; // Queue indexes
|
||||
uint16_t pan_id, short_addr; // Addresses programmed in the RCP
|
||||
uint8_t eui[8];
|
||||
uint8_t channel;
|
||||
};
|
||||
static struct mg_rw612 s_rw612;
|
||||
|
||||
@@ -337,31 +332,6 @@ static bool mg_rw612_set(uint32_t prop, const uint8_t *val, size_t len) {
|
||||
s_rw612.rsp_status == 0;
|
||||
}
|
||||
|
||||
// Program channel, PAN ID, short address and EUI-64 when they change
|
||||
static void mg_rw612_addrs(struct mg_tcpip_if *ifp) {
|
||||
struct mg_rw612 *s = &s_rw612;
|
||||
struct lowpan_data *l = &ifp->l2data.lowpan;
|
||||
uint8_t v[2];
|
||||
if (s->channel != l->channel) {
|
||||
s->channel = l->channel;
|
||||
if (!mg_rw612_set(MGI_PROP_PHY_CHAN, &l->channel, 1)) MG_ERROR(("chan"));
|
||||
}
|
||||
if (s->pan_id != l->pan_id) {
|
||||
s->pan_id = l->pan_id;
|
||||
v[0] = (uint8_t) l->pan_id, v[1] = (uint8_t) (l->pan_id >> 8);
|
||||
if (!mg_rw612_set(MGI_PROP_PANID, v, 2)) MG_ERROR(("panid"));
|
||||
}
|
||||
if (s->short_addr != l->short_addr) {
|
||||
s->short_addr = l->short_addr;
|
||||
v[0] = (uint8_t) l->short_addr, v[1] = (uint8_t) (l->short_addr >> 8);
|
||||
if (!mg_rw612_set(MGI_PROP_SADDR, v, 2)) MG_ERROR(("saddr"));
|
||||
}
|
||||
if (memcmp(s->eui, ifp->mac, 8) != 0) {
|
||||
memcpy(s->eui, ifp->mac, 8);
|
||||
if (!mg_rw612_set(MGI_PROP_LADDR, s->eui, 8)) MG_ERROR(("laddr"));
|
||||
}
|
||||
}
|
||||
|
||||
// Log an init error, return false
|
||||
static bool mg_rw612_fail(const char *msg) {
|
||||
MG_ERROR(("%s", msg));
|
||||
@@ -370,11 +340,12 @@ static bool mg_rw612_fail(const char *msg) {
|
||||
|
||||
static bool mg_rw612_154_init(struct mg_tcpip_if *ifp) {
|
||||
struct mg_rw612 *s = &s_rw612;
|
||||
uint8_t f[8], one = 1;
|
||||
struct mg_l2_802154_data *d =
|
||||
(struct mg_l2_802154_data *) ifp->driver_data;
|
||||
uint8_t f[8], one = 1, pan[2];
|
||||
uint64_t expire;
|
||||
if (ifp->l2type != MG_TCPIP_L2_LOWPAN || ifp->l2data.lowpan.channel < 11 ||
|
||||
ifp->l2data.lowpan.channel > 26) {
|
||||
MG_ERROR(("Use MG_TCPIP_L2_LOWPAN, channel 11..26"));
|
||||
if (d == NULL || d->channel < 11 || d->channel > 26) {
|
||||
MG_ERROR(("Set driver_data, channel 11..26"));
|
||||
return false;
|
||||
}
|
||||
if (MGI_MBOX->magic[0] != MGI_SYNC_MAGIC) {
|
||||
@@ -383,7 +354,6 @@ static bool mg_rw612_154_init(struct mg_tcpip_if *ifp) {
|
||||
}
|
||||
memset(s, 0, sizeof(*s));
|
||||
s->free_bufs = 0xffff;
|
||||
s->pan_id = s->short_addr = 0xffff; // Force programming
|
||||
// Reset our send FIFO, drop stale messages. See IMU_Init()
|
||||
MGI_IMU->CNTL |= MGI_IMU_FLUSH;
|
||||
MGI_IMU->CNTL &= ~(MGI_IMU_FLUSH | MGI_IMU_WAIT_FOR_ACK);
|
||||
@@ -415,25 +385,31 @@ static bool mg_rw612_154_init(struct mg_tcpip_if *ifp) {
|
||||
if (!mg_rw612_set(MGI_PROP_PHY_ENABLED, &one, 1)) {
|
||||
return mg_rw612_fail("PHY enable failed");
|
||||
}
|
||||
mg_rw612_addrs(ifp);
|
||||
pan[0] = (uint8_t) d->pan_id, pan[1] = (uint8_t) (d->pan_id >> 8);
|
||||
if (!mg_rw612_set(MGI_PROP_PHY_CHAN, &d->channel, 1) ||
|
||||
!mg_rw612_set(MGI_PROP_PANID, pan, 2)) {
|
||||
return mg_rw612_fail("Channel or PAN ID set failed");
|
||||
}
|
||||
if (!mg_rw612_set(MGI_PROP_RAW_STREAM, &one, 1)) {
|
||||
return mg_rw612_fail("Raw stream enable failed");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t mg_rw612_154_tx(const void *buf, size_t len,
|
||||
struct mg_tcpip_if *ifp) {
|
||||
// Send one radio frame: MAC header and payload, no FCS
|
||||
static size_t mg_rw612_send(const uint8_t *p, size_t len,
|
||||
struct mg_tcpip_if *ifp) {
|
||||
struct mg_l2_802154_data *d =
|
||||
(struct mg_l2_802154_data *) ifp->driver_data;
|
||||
uint8_t f[3 + 2 + 127 + 4];
|
||||
size_t n = 3;
|
||||
if (len == 0 || len > 125) return 0; // 127 bytes max, incl. 2 bytes FCS
|
||||
f[1] = MGI_CMD_SET, f[2] = MGI_PROP_STREAM_RAW;
|
||||
f[n++] = (uint8_t) (len + 2), f[n++] = 0; // PSDU length, incl. FCS
|
||||
memcpy(f + n, buf, len), n += len;
|
||||
memcpy(f + n, p, len), n += len;
|
||||
f[n++] = 0, f[n++] = 0; // FCS, filled in by the radio
|
||||
f[n++] = ifp->l2data.lowpan.channel;
|
||||
f[n++] = d->channel;
|
||||
f[n++] = 4; // Max CSMA backoffs
|
||||
f[n++] = 3; // Max frame retries, for frames with ACK request
|
||||
f[n++] = 0; // Max frame retries: broadcast frames are not acknowledged
|
||||
f[n++] = 1; // CSMA-CA enabled
|
||||
if (!mg_rw612_request(f, n, MGI_TX_TIMEOUT_MS)) {
|
||||
MG_ERROR(("TX timeout"));
|
||||
@@ -446,18 +422,21 @@ static size_t mg_rw612_154_tx(const void *buf, size_t len,
|
||||
return len;
|
||||
}
|
||||
|
||||
static size_t mg_rw612_154_tx(const void *buf, size_t len,
|
||||
struct mg_tcpip_if *ifp) {
|
||||
return mg_l2_802154_tx(ifp, buf, len, mg_rw612_send);
|
||||
}
|
||||
|
||||
static size_t mg_rw612_154_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
||||
struct mg_rw612 *s = &s_rw612;
|
||||
size_t n = 0;
|
||||
size_t res = 0;
|
||||
mg_rw612_poll_imu();
|
||||
mg_rw612_addrs(ifp);
|
||||
if (s->rxtail != s->rxhead) {
|
||||
n = s->rxlen[s->rxtail];
|
||||
if (n > len) n = len;
|
||||
memcpy(buf, s->rxq[s->rxtail], n);
|
||||
while (s->rxtail != s->rxhead && res == 0) {
|
||||
res = mg_l2_802154_rx(ifp, s->rxq[s->rxtail], s->rxlen[s->rxtail], buf,
|
||||
len);
|
||||
s->rxtail = (uint8_t) ((s->rxtail + 1) % MGI_RXQ_CNT);
|
||||
}
|
||||
return n;
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool mg_rw612_154_poll(struct mg_tcpip_if *ifp, bool s1) {
|
||||
|
||||
+18
-19
@@ -3,27 +3,26 @@
|
||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RW612_154) && \
|
||||
MG_ENABLE_DRIVER_RW612_154
|
||||
|
||||
// NXP RW612 IEEE 802.15.4 radio. The radio runs on CPU2: NXP BLE/802.15.4
|
||||
// combo firmware, an OpenThread radio co-processor (RCP). The firmware must
|
||||
// be loaded and running before mg_tcpip_init(), see the FRDM-RW612 lowpan
|
||||
// tutorial. The driver talks to it over the CPU2 <-> CPU3 IMU mailbox:
|
||||
// IMUMC messages carrying HDLC framed Spinel. Frames passed to tx() and
|
||||
// returned by rx() are raw 802.15.4 frames: MAC header and payload, no FCS.
|
||||
// Use with MG_TCPIP_L2_LOWPAN: channel, PAN ID and addresses are taken from
|
||||
// ifp->l2data.lowpan and ifp->mac. No driver_data
|
||||
// NXP RW612 IEEE 802.15.4 radio, as an Ethernet interface: Ethernet frames
|
||||
// are split into radio frames, see mg_l2_802154_tx(). The radio runs on
|
||||
// CPU2: NXP BLE/802.15.4 combo firmware, an OpenThread radio co-processor
|
||||
// (RCP). The firmware must be loaded and running before mg_tcpip_init(), see
|
||||
// the FRDM-RW612 eth_radio_bridge tutorial. The driver talks to it over the
|
||||
// CPU2 <-> CPU3 IMU mailbox: IMUMC messages carrying HDLC framed Spinel.
|
||||
// driver_data: struct mg_l2_802154_data, channel and PAN ID set
|
||||
|
||||
#ifndef MG_TCPIP_DRIVER_INIT
|
||||
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
||||
do { \
|
||||
static struct mg_tcpip_if mif_; \
|
||||
mif_.l2type = MG_TCPIP_L2_LOWPAN; \
|
||||
mif_.driver = &mg_tcpip_driver_rw612_154; \
|
||||
mif_.l2data.lowpan.channel = 15; \
|
||||
mif_.l2data.lowpan.pan_id = 0xabcd; \
|
||||
MG_SET_MAC_ADDRESS(mif_.mac); /* EUI-64 */ \
|
||||
mg_tcpip_init(mgr, &mif_); \
|
||||
(mgr)->use_dns6 = true; /* IPv6 only: no IPv4 over 6LoWPAN */ \
|
||||
MG_INFO(("Driver: rw612_154, 6LoWPAN")); \
|
||||
#define MG_TCPIP_DRIVER_INIT(mgr) \
|
||||
do { \
|
||||
static struct mg_l2_802154_data driver_data_; \
|
||||
static struct mg_tcpip_if mif_; \
|
||||
driver_data_.channel = MG_L2_802154_CHANNEL; \
|
||||
driver_data_.pan_id = MG_L2_802154_PAN_ID; \
|
||||
mif_.driver = &mg_tcpip_driver_rw612_154; \
|
||||
mif_.driver_data = &driver_data_; \
|
||||
MG_SET_MAC_ADDRESS(mif_.mac); \
|
||||
mg_tcpip_init(mgr, &mif_); \
|
||||
MG_INFO(("Driver: rw612_154, channel %d", MG_L2_802154_CHANNEL)); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#if MG_ENABLE_TCPIP && (MG_ENABLE_DRIVER_STM32H || MG_ENABLE_DRIVER_MCXN || \
|
||||
MG_ENABLE_DRIVER_STM32N)
|
||||
// STM32C: vendor modded single-queue Synopsys v5.4, same settings as STM32H
|
||||
// STM32H: vendor modded single-queue Synopsys v4.2
|
||||
// STM32N: dual-queue GbE Synopsys v5.2 with no hash table option, 64-bit AXI
|
||||
// MCXNx4x: dual-queue Synopsys v5.2 with no hash table option
|
||||
@@ -310,7 +311,7 @@ static uint32_t s_rxno;
|
||||
#if MG_ENABLE_DRIVER_MCXN
|
||||
void ETHERNET_IRQHandler(void);
|
||||
void ETHERNET_IRQHandler(void) {
|
||||
#elif MG_ENABLE_DRIVER_STM32H
|
||||
#elif MG_ENABLE_DRIVER_STM32H && !MG_ENABLE_DRIVER_STM32C
|
||||
void ETH_IRQHandler(void);
|
||||
void ETH_IRQHandler(void) {
|
||||
#else
|
||||
@@ -339,8 +340,10 @@ void ETH1_IRQHandler(void) {
|
||||
}
|
||||
ETH->DMACSR =
|
||||
MG_BIT(7) | MG_BIT(8); // Clear possible RBU RPS while processing
|
||||
ETH->DMACRDTPR =
|
||||
(uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT - 1]; // and resume RX
|
||||
// Resume RX: tail is the last descriptor given back to DMA. It must differ
|
||||
// from the DMA current descriptor, or some DMAs, e.g. STM32C5, stay suspended
|
||||
ETH->DMACRDTPR = (uint32_t) (uintptr_t) &s_rxdesc[(s_rxno + ETH_DESC_CNT - 1) %
|
||||
ETH_DESC_CNT];
|
||||
}
|
||||
|
||||
struct mg_tcpip_driver mg_tcpip_driver_stm32h = {
|
||||
|
||||
+11
-2
@@ -4,6 +4,13 @@
|
||||
#if !defined(MG_ENABLE_DRIVER_STM32H)
|
||||
#define MG_ENABLE_DRIVER_STM32H 0
|
||||
#endif
|
||||
#if !defined(MG_ENABLE_DRIVER_STM32C)
|
||||
#define MG_ENABLE_DRIVER_STM32C 0
|
||||
#endif
|
||||
#if MG_ENABLE_DRIVER_STM32C // STM32C5: STM32H5 MAC, the IRQ is ETH1
|
||||
#undef MG_ENABLE_DRIVER_STM32H
|
||||
#define MG_ENABLE_DRIVER_STM32H 1
|
||||
#endif
|
||||
#if !defined(MG_ENABLE_DRIVER_MCXN)
|
||||
#define MG_ENABLE_DRIVER_MCXN 0
|
||||
#endif
|
||||
@@ -43,9 +50,11 @@ struct mg_tcpip_driver_stm32h_data {
|
||||
#define MG_DRIVER_MDC_CR 4
|
||||
#endif
|
||||
|
||||
#if MG_ENABLE_DRIVER_STM32H && MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
|
||||
#if MG_ENABLE_DRIVER_STM32H && !MG_ENABLE_DRIVER_STM32C && \
|
||||
MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
|
||||
#define MG_ENABLE_ETH_IRQ() NVIC_EnableIRQ(ETH_IRQn)
|
||||
#elif MG_ENABLE_DRIVER_STM32N && MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
|
||||
#elif (MG_ENABLE_DRIVER_STM32N || MG_ENABLE_DRIVER_STM32C) && \
|
||||
MG_ARCH == MG_ARCH_CUBE && !defined(MG_ENABLE_ETH_IRQ)
|
||||
#define MG_ENABLE_ETH_IRQ() NVIC_EnableIRQ(ETH1_IRQn)
|
||||
#elif !defined(MG_ENABLE_ETH_IRQ)
|
||||
#define MG_ENABLE_ETH_IRQ()
|
||||
|
||||
+50
-37
@@ -1,12 +1,13 @@
|
||||
#include "config.h"
|
||||
#include "health.h"
|
||||
|
||||
#if MG_ENABLE_HEALTH
|
||||
// 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
|
||||
struct mg_health mg_health_record MG_HEALTH_RAM;
|
||||
|
||||
// STM32 H723 support. Needs the _estack symbol from the linker script
|
||||
#if MG_ENABLE_HEALTH && (defined(STM32H7_DEV_ID) || defined(STM32H5))
|
||||
// STM32 H7, H5, C5 support. Needs the _estack symbol from the linker script
|
||||
#if defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5)
|
||||
// Fault handler body. Runs in exception context: no printf, no malloc, no
|
||||
// blocking calls. Copies the unwind registers and raw stack into the health
|
||||
// record, then resets.
|
||||
@@ -48,52 +49,57 @@ void BusFault_Handler(void) __attribute__((alias("HardFault_Handler")));
|
||||
void UsageFault_Handler(void) __attribute__((alias("HardFault_Handler")));
|
||||
#endif
|
||||
|
||||
// Start a new boot: validate the record, count the boot, store the reset
|
||||
// reason. The current boot moves to prev_* slots first, so the previous
|
||||
// boot's uptime and reason stay readable. Called by MG_HEALTH_INIT()
|
||||
// Start a new boot: validate the record. Called by MG_HEALTH_INIT()
|
||||
void mg_health_init(void) {
|
||||
char magic[] = MG_HEALTH_MAGIC;
|
||||
// struct mg_health *h = mg_health_get();
|
||||
if (!mg_health_valid()) { // First boot, or RAM lost its contents
|
||||
memset(&mg_health_record, 0, sizeof(mg_health_record));
|
||||
memcpy(mg_health_record.magic, magic, sizeof(mg_health_record.magic));
|
||||
}
|
||||
|
||||
#if 0 && MG_ARCH == MG_ARCH_ESP32 && CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH
|
||||
{
|
||||
#if MG_ARCH == MG_ARCH_ESP32
|
||||
#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
|
||||
// addresses the panic handler prints as "Backtrace: PC:SP PC:SP ...".
|
||||
// The core dump stays in flash, so check the reset reason, otherwise an
|
||||
// old crash is reported on every boot
|
||||
static esp_core_dump_summary_t s; // Big on RISC-V, keep it off the stack
|
||||
esp_reset_reason_t rr = esp_reset_reason();
|
||||
esp_core_dump_summary_t *s = calloc(1, sizeof(*s));
|
||||
uint32_t *out = mg_health_record.data;
|
||||
size_t n = 0, max = MG_HEALTH_DATA_SIZE;
|
||||
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;
|
||||
|
||||
size_t n = 0;
|
||||
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
|
||||
for (uint32_t i = 0; i < s.exc_bt_info.depth && n < max; i++) {
|
||||
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[0];
|
||||
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
|
||||
// No unwinder: take the return address, then words on the stack that
|
||||
// point into executable memory (a heuristic, may include stale values)
|
||||
if (n < max) out[n++] = s.ex_info.ra;
|
||||
for (uint32_t i = 0; i + 4 <= s.exc_bt_info.dump_size && n < max; i += 4) {
|
||||
uint32_t w;
|
||||
memcpy(&w, &s.exc_bt_info.stackdump[i], sizeof(w)); // Byte array
|
||||
if (esp_ptr_executable((void *) (uintptr_t) w)) out[n++] = w;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
out[n++] = s->ex_info.exc_ra;
|
||||
out[n++] = s->ex_info.exc_sp;
|
||||
size_t len = s->exc_bt_info.dump_size / 4 ; // sizeof(uint32_t)
|
||||
if (len > MG_HEALTH_DATA_SIZE - n) len = (MG_HEALTH_DATA_SIZE - n);
|
||||
memcpy(&out[n], s->exc_bt_info.stackdump, 4 * len);
|
||||
#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) {
|
||||
@@ -107,11 +113,17 @@ 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 len = 0;
|
||||
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]);
|
||||
len += mg_xprintf(fn, arg, "{%m:%hhu,%m:%u,%m:[%M]}", //
|
||||
MG_ESC("version"), mg_health_record.magic[3], //
|
||||
MG_ESC("cpuid"), mg_health_record.cpuid, //
|
||||
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("arch"), mg_health_record.arch, //
|
||||
MG_ESC("reason"), mg_health_record.reset_reason, //
|
||||
MG_ESC("cpuid"), mg_health_record.cpuid, //
|
||||
MG_ESC("data"), print_uint32, n, mg_health_record.data);
|
||||
} else {
|
||||
len += mg_xprintf(fn, arg, "{}");
|
||||
@@ -119,3 +131,4 @@ size_t mg_print_crash_record(void (*fn)(char, void *), void *arg, va_list *ap) {
|
||||
(void) ap;
|
||||
return len;
|
||||
}
|
||||
#endif
|
||||
+66
-6
@@ -5,21 +5,25 @@
|
||||
#include "arch.h"
|
||||
#include "printf.h"
|
||||
|
||||
#if MG_ENABLE_HEALTH
|
||||
|
||||
#ifndef MG_HEALTH_MAGIC
|
||||
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
|
||||
#endif
|
||||
|
||||
#if !defined(MG_HEALTH_RAM) && (defined(STM32H7_DEV_ID) || defined(STM32H5))
|
||||
#if !defined(MG_HEALTH_RAM) && (defined(STM32H7_DEV_ID) || defined(STM32H5) || defined(STM32C5))
|
||||
#define MG_HEALTH_RAM __attribute__((section(".mg_health")))
|
||||
#endif
|
||||
|
||||
#if !defined(MG_HEALTH_DATA_SIZE) && (defined(STM32H7_DEV_ID) || defined(STM32H5))
|
||||
#define MG_HEALTH_DATA_SIZE 250
|
||||
#endif
|
||||
|
||||
#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
|
||||
#endif
|
||||
#endif // MG_HEALTH_DATA_SIZE
|
||||
|
||||
// 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")))
|
||||
@@ -27,6 +31,13 @@
|
||||
#define MG_HEALTH_RAM
|
||||
#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,
|
||||
// so these are ordered by how much they tell you: the most specific cause a
|
||||
// device reports wins
|
||||
@@ -43,7 +54,8 @@ enum mg_health_reason {
|
||||
|
||||
struct mg_health {
|
||||
char magic[4]; // MG_HEALTH_MAGIC when the record holds valid data
|
||||
uint32_t reset_reason; // enum mg_health_reason that started this boot
|
||||
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 data[MG_HEALTH_DATA_SIZE];
|
||||
};
|
||||
@@ -76,6 +88,54 @@ 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()
|
||||
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
|
||||
extern void mg_health_init(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, "{}");
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -56,19 +56,6 @@ extern uint8_t *mg_l2_pppoe_header(struct mg_tcpip_if *, enum mg_l2proto, struct
|
||||
extern size_t mg_l2_pppoe_trailer(struct mg_tcpip_if *, size_t, uint8_t *);
|
||||
extern bool mg_l2_pppoe_rx(struct mg_tcpip_if *, enum mg_l2proto *, struct mg_str *, struct mg_str *);
|
||||
|
||||
extern void mg_l2_lowpan_init(struct mg_tcpip_if *);
|
||||
extern bool mg_l2_lowpan_poll(struct mg_tcpip_if *, bool);
|
||||
extern uint8_t *mg_l2_lowpan_header(struct mg_tcpip_if *, enum mg_l2proto, struct mg_l2addr *, struct mg_l2addr *, uint8_t *);
|
||||
extern size_t mg_l2_lowpan_trailer(struct mg_tcpip_if *, size_t, uint8_t *);
|
||||
extern bool mg_l2_lowpan_rx(struct mg_tcpip_if *, enum mg_l2proto *, struct mg_str *, struct mg_str *);
|
||||
extern struct mg_l2addr *mg_l2_lowpan_getaddr(struct mg_tcpip_if *, uint8_t *);
|
||||
extern struct mg_l2addr *mg_l2_lowpan_mapip(enum mg_l2addrtype, struct mg_addr *);
|
||||
#if MG_ENABLE_IPV6
|
||||
extern bool mg_l2_lowpan_genip6(uint64_t *, uint8_t, struct mg_l2addr *);
|
||||
extern bool mg_l2_lowpan_ip6get(struct mg_l2addr *, uint8_t *, uint8_t);
|
||||
extern uint8_t mg_l2_lowpan_ip6put(struct mg_l2addr *, uint8_t *);
|
||||
#endif
|
||||
|
||||
typedef void (*l2_init_fn)(struct mg_tcpip_if *);
|
||||
typedef bool (*l2_poll_fn)(struct mg_tcpip_if *, bool);
|
||||
typedef uint8_t *((*l2_header_fn)(struct mg_tcpip_if *, enum mg_l2proto, struct mg_l2addr *, struct mg_l2addr *, uint8_t *));
|
||||
@@ -84,27 +71,25 @@ typedef uint8_t (*l2_ip6put_fn)(struct mg_l2addr *, uint8_t *);
|
||||
// clang-format on
|
||||
|
||||
static const l2_init_fn l2_init[] = {mg_l2_eth_init, mg_l2_ppp_init,
|
||||
mg_l2_pppoe_init, mg_l2_lowpan_init};
|
||||
mg_l2_pppoe_init};
|
||||
static const l2_poll_fn l2_poll[] = {mg_l2_eth_poll, mg_l2_ppp_poll,
|
||||
mg_l2_pppoe_poll, mg_l2_lowpan_poll};
|
||||
mg_l2_pppoe_poll};
|
||||
static const l2_header_fn l2_header[] = {mg_l2_eth_header, mg_l2_ppp_header,
|
||||
mg_l2_pppoe_header,
|
||||
mg_l2_lowpan_header};
|
||||
mg_l2_pppoe_header};
|
||||
static const l2_trailer_fn l2_trailer[] = {mg_l2_eth_trailer, mg_l2_ppp_trailer,
|
||||
mg_l2_pppoe_trailer, mg_l2_lowpan_trailer};
|
||||
static const l2_rx_fn l2_rx[] = {mg_l2_eth_rx, mg_l2_ppp_rx, mg_l2_pppoe_rx,
|
||||
mg_l2_lowpan_rx};
|
||||
mg_l2_pppoe_trailer};
|
||||
static const l2_rx_fn l2_rx[] = {mg_l2_eth_rx, mg_l2_ppp_rx, mg_l2_pppoe_rx};
|
||||
static const l2_getaddr_fn l2_getaddr[] = {mg_l2_eth_getaddr, mg_l2_ppp_getaddr,
|
||||
mg_l2_ppp_getaddr, mg_l2_lowpan_getaddr};
|
||||
mg_l2_ppp_getaddr};
|
||||
static const l2_mapip_fn l2_mapip[] = {mg_l2_eth_mapip, mg_l2_ppp_mapip,
|
||||
mg_l2_ppp_mapip, mg_l2_lowpan_mapip};
|
||||
mg_l2_ppp_mapip};
|
||||
#if MG_ENABLE_IPV6
|
||||
static const l2_genip6_fn l2_genip6[] = {mg_l2_eth_genip6, mg_l2_ppp_genip6,
|
||||
mg_l2_ppp_genip6, mg_l2_lowpan_genip6};
|
||||
mg_l2_ppp_genip6};
|
||||
static const l2_ip6get_fn l2_ip6get[] = {mg_l2_eth_ip6get, mg_l2_ppp_ip6get,
|
||||
mg_l2_ppp_ip6get, mg_l2_lowpan_ip6get};
|
||||
mg_l2_ppp_ip6get};
|
||||
static const l2_ip6put_fn l2_ip6put[] = {mg_l2_eth_ip6put, mg_l2_ppp_ip6put,
|
||||
mg_l2_ppp_ip6put, mg_l2_lowpan_ip6put};
|
||||
mg_l2_ppp_ip6put};
|
||||
#endif
|
||||
|
||||
void mg_l2_init(struct mg_tcpip_if *ifp) {
|
||||
|
||||
@@ -6,13 +6,7 @@
|
||||
#if MG_ENABLE_TCPIP
|
||||
|
||||
// no config defaults to 0 => Ethernet
|
||||
enum mg_l2type {
|
||||
MG_TCPIP_L2_ETH = 0,
|
||||
MG_TCPIP_L2_PPP,
|
||||
MG_TCPIP_L2_PPPoE,
|
||||
MG_TCPIP_L2_LOWPAN,
|
||||
MG_TCPIP_L2_USER
|
||||
};
|
||||
enum mg_l2type { MG_TCPIP_L2_ETH = 0, MG_TCPIP_L2_PPP, MG_TCPIP_L2_PPPoE, MG_TCPIP_L2_USER };
|
||||
|
||||
#if defined(__DCC__)
|
||||
#pragma pack(1)
|
||||
@@ -35,27 +29,10 @@ struct eth_data {
|
||||
struct pppoe_data { // (struct eth_data *) &pppoe_data = pppoedata.eth
|
||||
struct eth_data eth;
|
||||
};
|
||||
struct lowpan_data {
|
||||
uint16_t pan_id; // User-set PAN ID
|
||||
uint8_t channel; // User-set channel, 11..26
|
||||
uint16_t short_addr; // Our short address, 0xfffe if none
|
||||
uint8_t seq; // MAC sequence number
|
||||
uint8_t proto; // Protocol of the frame being sent, enum mg_l2proto
|
||||
uint8_t dst[8]; // Destination of the frame being sent
|
||||
uint16_t tag; // Next fragmentation datagram tag
|
||||
uint8_t *buf; // Reassembly and decompression buffer
|
||||
uint16_t size; // Datagram being reassembled: size, 0 if none
|
||||
uint16_t received; // Bytes received so far
|
||||
uint16_t tag_rx; // Datagram tag
|
||||
uint8_t src[8]; // Source address
|
||||
bool udp; // UDP header was compressed
|
||||
uint64_t expire; // Reassembly deadline, ms
|
||||
};
|
||||
|
||||
union mg_l2data {
|
||||
struct eth_data eth;
|
||||
struct pppoe_data pppoe;
|
||||
struct lowpan_data lowpan;
|
||||
};
|
||||
|
||||
#if defined(__DCC__)
|
||||
@@ -64,6 +41,7 @@ union mg_l2data {
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
|
||||
enum mg_l2proto {
|
||||
MG_TCPIP_L2PROTO_IPV4 = 0,
|
||||
MG_TCPIP_L2PROTO_IPV6,
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#include "l2.h"
|
||||
#include "net_builtin.h"
|
||||
|
||||
#if MG_ENABLE_TCPIP
|
||||
|
||||
// Radio frame: 802.15.4 MAC header, 15 bytes: data frame, PAN ID compression,
|
||||
// broadcast destination, our EUI-64 made from MAC as source. Then 2 bytes:
|
||||
// tag, fragment index << 4 | last fragment index. Then data. 125 bytes max,
|
||||
// 127 minus FCS
|
||||
#define MGI_802154_HDR 17
|
||||
#define MGI_802154_DATA (125 - MGI_802154_HDR)
|
||||
|
||||
size_t mg_l2_802154_tx(struct mg_tcpip_if *ifp, const void *buf, size_t len,
|
||||
size_t (*send)(const uint8_t *, size_t,
|
||||
struct mg_tcpip_if *)) {
|
||||
struct mg_l2_802154_data *d =
|
||||
(struct mg_l2_802154_data *) ifp->driver_data;
|
||||
const uint8_t *p = (const uint8_t *) buf, *m = ifp->mac;
|
||||
uint8_t f[MGI_802154_HDR + MGI_802154_DATA];
|
||||
size_t i, n, last = (len - 1) / MGI_802154_DATA;
|
||||
if (len == 0 || last > 15) return 0;
|
||||
d->tag++;
|
||||
for (i = 0; i <= last; i++) {
|
||||
n = i == last ? len - i * MGI_802154_DATA : MGI_802154_DATA;
|
||||
f[0] = 0x41, f[1] = 0xd8, f[2] = d->seq++; // 2006 frame, see above
|
||||
f[3] = (uint8_t) d->pan_id, f[4] = (uint8_t) (d->pan_id >> 8);
|
||||
f[5] = f[6] = 0xff;
|
||||
f[7] = m[5], f[8] = m[4], f[9] = m[3], f[10] = 0xfe, f[11] = 0xff;
|
||||
f[12] = m[2], f[13] = m[1], f[14] = m[0]; // Little endian EUI-64
|
||||
f[15] = d->tag, f[16] = (uint8_t) ((i << 4) | last);
|
||||
memcpy(f + MGI_802154_HDR, p + i * MGI_802154_DATA, n);
|
||||
if (send(f, MGI_802154_HDR + n, ifp) == 0) return 0;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t mg_l2_802154_rx(struct mg_tcpip_if *ifp, const uint8_t *f, size_t n,
|
||||
void *buf, size_t len) {
|
||||
struct mg_l2_802154_data *d =
|
||||
(struct mg_l2_802154_data *) ifp->driver_data;
|
||||
size_t idx, last;
|
||||
if (n <= MGI_802154_HDR || f[0] != 0x41 || f[1] != 0xd8 ||
|
||||
f[3] != (uint8_t) d->pan_id || f[4] != (uint8_t) (d->pan_id >> 8))
|
||||
return 0; // Not ours
|
||||
idx = f[16] >> 4, last = f[16] & 15;
|
||||
if (idx == 0) d->rxtag = f[15], d->rxnext = 0, d->rxlen = 0;
|
||||
if (idx == 0) memcpy(d->rxsrc, f + 7, sizeof(d->rxsrc));
|
||||
n -= MGI_802154_HDR;
|
||||
if (idx != d->rxnext || idx > last || f[15] != d->rxtag ||
|
||||
memcmp(d->rxsrc, f + 7, sizeof(d->rxsrc)) != 0 ||
|
||||
d->rxlen + n > sizeof(d->rxbuf))
|
||||
return 0; // Missed a fragment, or from another sender
|
||||
memcpy(d->rxbuf + d->rxlen, f + MGI_802154_HDR, n);
|
||||
d->rxlen = (uint16_t) (d->rxlen + n), d->rxnext++;
|
||||
if (idx < last || d->rxlen > len) return 0;
|
||||
memcpy(buf, d->rxbuf, d->rxlen);
|
||||
return d->rxlen;
|
||||
}
|
||||
|
||||
#endif
|
||||
-551
@@ -1,551 +0,0 @@
|
||||
#include "l2.h"
|
||||
#include "log.h"
|
||||
#include "net.h"
|
||||
#include "net_builtin.h"
|
||||
#include "printf.h"
|
||||
#include "util.h"
|
||||
|
||||
// IPv6 over IEEE 802.15.4, 6LoWPAN. RFC 4944: frame format, fragmentation.
|
||||
// RFC 6282: IPHC header compression, UDP next header compression.
|
||||
// Driver tx()/rx() carry raw 802.15.4 frames: MAC header, payload, no FCS.
|
||||
//
|
||||
// L2 addresses are 8 bytes, struct mg_l2addr. Extended address: EUI-64, big
|
||||
// endian. Short address S: 0000:00ff:fe00:SSSS, which is also its IPv6
|
||||
// interface identifier, RFC 6282 3.2.2. Broadcast is short address 0xffff.
|
||||
// No link-layer security: secured frames are dropped
|
||||
|
||||
#if MG_ENABLE_TCPIP && MG_ENABLE_IPV6
|
||||
|
||||
#define MGI_LOWPAN_FRAME_MAX 125 // 802.15.4 frame without FCS: 127 - 2
|
||||
#define MGI_LOWPAN_MTU 1280 // IPv6 minimum MTU, RFC 8200 5
|
||||
#define MGI_LOWPAN_REASM_MS 5000 // Drop incomplete datagrams after this
|
||||
#define MGI_FCF_DATA 1U // Frame control: data frame
|
||||
#define MGI_FCF_SECURITY 0x08U // Frame control: security enabled
|
||||
#define MGI_FCF_ACK_REQUEST 0x20U // Frame control: acknowledgment request
|
||||
#define MGI_FCF_PANID_COMP 0x40U // Frame control: PAN ID compression
|
||||
#define MGI_FCF_IE 0x200U // Frame control: IE present, 2015 frames
|
||||
#define MGI_FCF_VERSION_2006 0x1000U
|
||||
#define MGI_AM_SHORT 2U // Addressing mode: 16-bit short address
|
||||
#define MGI_AM_EXT 3U // Addressing mode: 64-bit extended address
|
||||
#define MGI_DISPATCH_IPV6 0x41 // Uncompressed IPv6, RFC 4944 5.1
|
||||
#define MGI_DISPATCH_FRAG1 0xc0 // First fragment, 11000xxx
|
||||
#define MGI_DISPATCH_FRAGN 0xe0 // Subsequent fragment, 11100xxx
|
||||
|
||||
// Parsed 802.15.4 MAC header
|
||||
struct lowpan_mhr {
|
||||
uint8_t dst[8], src[8]; // Addresses, struct mg_l2addr format
|
||||
uint16_t dst_pan; // Destination PAN ID
|
||||
size_t len; // MAC header length, without security header
|
||||
bool secured; // Security enabled
|
||||
};
|
||||
|
||||
static struct mg_l2addr s_rxsrc; // Source address returned by getaddr()
|
||||
|
||||
static void set_short(uint8_t *a, uint16_t s) {
|
||||
a[0] = a[1] = a[2] = a[5] = 0, a[3] = 0xff, a[4] = 0xfe;
|
||||
a[6] = (uint8_t) (s >> 8), a[7] = (uint8_t) s;
|
||||
}
|
||||
|
||||
static bool is_zero(const uint8_t *p, size_t n) {
|
||||
while (n-- > 0)
|
||||
if (*p++ != 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read an address field, over-the-air byte order is little endian
|
||||
static size_t read_addr(const uint8_t *p, unsigned mode, uint8_t *a) {
|
||||
int i;
|
||||
if (mode == MGI_AM_SHORT) {
|
||||
set_short(a, (uint16_t) (p[0] | (p[1] << 8)));
|
||||
return 2;
|
||||
}
|
||||
for (i = 0; i < 8; i++) a[i] = p[7 - i];
|
||||
return 8;
|
||||
}
|
||||
|
||||
// Parse MAC header of a data frame, 2003/2006 format
|
||||
static bool parse_mhr(const uint8_t *f, size_t n, struct lowpan_mhr *h) {
|
||||
unsigned fcf, dm, sm, ver;
|
||||
size_t i = 3;
|
||||
if (n < 3) return false;
|
||||
fcf = (unsigned) (f[0] | (f[1] << 8));
|
||||
dm = (fcf >> 10) & 3, sm = (fcf >> 14) & 3, ver = (fcf >> 12) & 3;
|
||||
if ((fcf & 7) != MGI_FCF_DATA || (fcf & MGI_FCF_IE) || ver > 1) return false;
|
||||
h->secured = (fcf & MGI_FCF_SECURITY) != 0;
|
||||
if (dm < MGI_AM_SHORT || sm < MGI_AM_SHORT) return false; // Need both addresses
|
||||
if (i + 2 > n) return false;
|
||||
h->dst_pan = (uint16_t) (f[i] | (f[i + 1] << 8)), i += 2;
|
||||
if (i + (dm == MGI_AM_EXT ? 8 : 2) > n) return false;
|
||||
i += read_addr(f + i, dm, h->dst);
|
||||
if ((fcf & MGI_FCF_PANID_COMP) == 0) i += 2; // Source PAN ID, if present
|
||||
if (i + (sm == MGI_AM_EXT ? 8 : 2) > n) return false;
|
||||
i += read_addr(f + i, sm, h->src);
|
||||
h->len = i;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_short(const uint8_t *a) {
|
||||
return a[0] == 0 && a[1] == 0 && a[2] == 0 && a[3] == 0xff && a[4] == 0xfe &&
|
||||
a[5] == 0;
|
||||
}
|
||||
|
||||
static bool is_bcast(const uint8_t *a) {
|
||||
return is_short(a) && a[6] == 0xff && a[7] == 0xff;
|
||||
}
|
||||
|
||||
// IPv6 interface identifier for an L2 address, RFC 6282 3.2.2
|
||||
static void make_iid(const uint8_t *a, uint8_t *iid) {
|
||||
memcpy(iid, a, 8);
|
||||
if (!is_short(a)) iid[0] ^= 2; // EUI-64: flip universal/local bit
|
||||
}
|
||||
|
||||
static size_t write_addr(uint8_t *p, const uint8_t *a) {
|
||||
int i;
|
||||
if (is_short(a)) {
|
||||
p[0] = a[7], p[1] = a[6];
|
||||
return 2;
|
||||
}
|
||||
for (i = 0; i < 8; i++) p[i] = a[7 - i];
|
||||
return 8;
|
||||
}
|
||||
|
||||
// Write MAC header for a data frame from us to dst. Return its length
|
||||
static size_t write_mhr(struct mg_tcpip_if *ifp, const uint8_t *dst,
|
||||
uint8_t *f) {
|
||||
struct lowpan_data *d = &ifp->l2data.lowpan;
|
||||
unsigned fcf = MGI_FCF_DATA | MGI_FCF_PANID_COMP | MGI_FCF_VERSION_2006;
|
||||
size_t i = 3;
|
||||
if (!is_bcast(dst)) fcf |= MGI_FCF_ACK_REQUEST; // Unicast: MAC retries
|
||||
fcf |= (is_short(dst) ? MGI_AM_SHORT : MGI_AM_EXT) << 10;
|
||||
fcf |= (is_short(ifp->mac) ? MGI_AM_SHORT : MGI_AM_EXT) << 14;
|
||||
f[0] = (uint8_t) fcf, f[1] = (uint8_t) (fcf >> 8), f[2] = d->seq++;
|
||||
f[i++] = (uint8_t) d->pan_id, f[i++] = (uint8_t) (d->pan_id >> 8);
|
||||
i += write_addr(f + i, dst);
|
||||
i += write_addr(f + i, ifp->mac);
|
||||
return i;
|
||||
}
|
||||
|
||||
// Compress a unicast IPv6 address: 2 bits for the SAM/DAM field, inline
|
||||
// bytes go to out. RFC 6282 3.1.1, SAC/DAC = 0
|
||||
static unsigned compress_addr(const uint8_t *ip, const uint8_t *l2,
|
||||
uint8_t *out, size_t *n) {
|
||||
static const uint8_t ll[8] = {0xfe, 0x80, 0, 0, 0, 0, 0, 0};
|
||||
uint8_t iid[8];
|
||||
if (memcmp(ip, ll, 8) != 0) {
|
||||
memcpy(out + *n, ip, 16), *n += 16;
|
||||
return 0;
|
||||
}
|
||||
make_iid(l2, iid);
|
||||
if (memcmp(ip + 8, iid, 8) == 0) return 3; // Derived from L2 address
|
||||
if (is_short(ip + 8)) { // 0000:00ff:fe00:XXXX
|
||||
memcpy(out + *n, ip + 14, 2), *n += 2;
|
||||
return 2;
|
||||
}
|
||||
memcpy(out + *n, ip + 8, 8), *n += 8;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Compress IPv6 header to IPHC, RFC 6282 3.1. Next header is carried inline.
|
||||
// Return compressed header length
|
||||
static size_t iphc_compress(const uint8_t *ip, const uint8_t *src_l2,
|
||||
const uint8_t *dst_l2, uint8_t *out) {
|
||||
unsigned tc = (unsigned) (((ip[0] & 0x0f) << 4) | (ip[1] >> 4));
|
||||
unsigned ecn = tc & 3, dscp = tc >> 2, tf, hl;
|
||||
uint32_t flow = ((uint32_t) (ip[1] & 0x0f) << 16) |
|
||||
((uint32_t) ip[2] << 8) | ip[3];
|
||||
size_t n = 2;
|
||||
if (tc == 0 && flow == 0) {
|
||||
tf = 3;
|
||||
} else if (flow == 0) {
|
||||
tf = 2, out[n++] = (uint8_t) ((ecn << 6) | dscp);
|
||||
} else if (dscp == 0) {
|
||||
tf = 1, out[n++] = (uint8_t) ((ecn << 6) | (flow >> 16));
|
||||
out[n++] = (uint8_t) (flow >> 8), out[n++] = (uint8_t) flow;
|
||||
} else {
|
||||
tf = 0, out[n++] = (uint8_t) ((ecn << 6) | dscp);
|
||||
out[n++] = (uint8_t) (flow >> 16), out[n++] = (uint8_t) (flow >> 8);
|
||||
out[n++] = (uint8_t) flow;
|
||||
}
|
||||
out[n++] = ip[6]; // Next header, inline
|
||||
hl = ip[7] == 1 ? 1 : ip[7] == 64 ? 2 : ip[7] == 255 ? 3 : 0;
|
||||
if (hl == 0) out[n++] = ip[7];
|
||||
out[0] = (uint8_t) (0x60 | (tf << 3) | hl);
|
||||
out[1] = 0;
|
||||
if (is_zero(ip + 8, 16)) {
|
||||
out[1] |= 0x40; // SAC = 1, SAM = 00: unspecified address
|
||||
} else {
|
||||
out[1] |= (uint8_t) (compress_addr(ip + 8, src_l2, out, &n) << 4);
|
||||
}
|
||||
if (ip[24] == 0xff) { // Multicast, M = 1
|
||||
out[1] |= 0x08;
|
||||
if (ip[25] == 0x02 && is_zero(ip + 26, 13)) { // ff02::00XX
|
||||
out[1] |= 3, out[n++] = ip[39];
|
||||
} else if (is_zero(ip + 26, 11)) { // ffXX::00XX:XXXX
|
||||
out[1] |= 2, out[n++] = ip[25];
|
||||
memcpy(out + n, ip + 37, 3), n += 3;
|
||||
} else if (is_zero(ip + 26, 9)) { // ffXX::00XX:XXXX:XXXX
|
||||
out[1] |= 1, out[n++] = ip[25];
|
||||
memcpy(out + n, ip + 35, 5), n += 5;
|
||||
} else {
|
||||
memcpy(out + n, ip + 24, 16), n += 16;
|
||||
}
|
||||
} else {
|
||||
out[1] |= (uint8_t) compress_addr(ip + 24, dst_l2, out, &n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
// Decompress a unicast address. Prefix is the context prefix for SAC/DAC = 1,
|
||||
// or NULL for link-local. Return inline bytes used
|
||||
static size_t decompress_addr(unsigned mode, const uint8_t *p, size_t n,
|
||||
const uint8_t *l2, const uint8_t *prefix,
|
||||
uint8_t *ip) {
|
||||
static const size_t sizes[] = {16, 8, 2, 0};
|
||||
if (sizes[mode] > n) return n + 1; // Truncated: caller sees i > n
|
||||
if (mode == 0) {
|
||||
memcpy(ip, p, 16);
|
||||
} else {
|
||||
memset(ip, 0, 16), ip[0] = 0xfe, ip[1] = 0x80;
|
||||
if (prefix != NULL) memcpy(ip, prefix, 8);
|
||||
if (mode == 1) memcpy(ip + 8, p, 8);
|
||||
if (mode == 2) set_short(ip + 8, (uint16_t) ((p[0] << 8) | p[1]));
|
||||
if (mode == 3) make_iid(l2, ip + 8);
|
||||
}
|
||||
return sizes[mode];
|
||||
}
|
||||
|
||||
// Decompress IPHC or uncompressed IPv6 header, RFC 6282 3.1, 4.3.
|
||||
// Write IPv6 header (and UDP header if compressed) to ip. Return its length,
|
||||
// or 0 on error. Set *used to the number of compressed bytes consumed
|
||||
static size_t iphc_decompress(const struct lowpan_mhr *h, const uint8_t *p,
|
||||
size_t n, uint8_t *ip, size_t *used, bool *udp) {
|
||||
static const size_t tf_sizes[] = {4, 3, 1, 0};
|
||||
unsigned b0, b1, tf, tc = 0, sam, dam;
|
||||
uint32_t flow = 0;
|
||||
size_t i = 2;
|
||||
*udp = false;
|
||||
if (n > 0 && p[0] == MGI_DISPATCH_IPV6) {
|
||||
if (n < 41) return 0;
|
||||
memcpy(ip, p + 1, 40);
|
||||
*used = 41;
|
||||
return 40;
|
||||
}
|
||||
if (n < 2 || (p[0] & 0xe0) != 0x60) return 0;
|
||||
b0 = p[0], b1 = p[1], tf = (b0 >> 3) & 3, sam = (b1 >> 4) & 3, dam = b1 & 3;
|
||||
if (b1 & 0x80) return 0; // CID: no compression contexts
|
||||
if (i + tf_sizes[tf] + ((b0 & 4) ? 0 : 1) + ((b0 & 3) ? 0 : 1) > n)
|
||||
return 0; // TF, next header, hop limit
|
||||
if (tf == 0 || tf == 2) tc = (unsigned) (((p[i] & 0x3f) << 2) | (p[i] >> 6));
|
||||
if (tf == 1) tc = p[i] >> 6;
|
||||
if (tf == 0) flow = ((uint32_t) (p[i + 1] & 0x0f) << 16) |
|
||||
((uint32_t) p[i + 2] << 8) | p[i + 3];
|
||||
if (tf == 1) flow = ((uint32_t) (p[i] & 0x0f) << 16) |
|
||||
((uint32_t) p[i + 1] << 8) | p[i + 2];
|
||||
i += tf_sizes[tf];
|
||||
ip[0] = (uint8_t) (0x60 | (tc >> 4));
|
||||
ip[1] = (uint8_t) ((tc << 4) | (flow >> 16));
|
||||
ip[2] = (uint8_t) (flow >> 8), ip[3] = (uint8_t) flow;
|
||||
ip[4] = ip[5] = 0; // Payload length, set by caller
|
||||
if ((b0 & 4) == 0) ip[6] = p[i++]; // Next header inline
|
||||
if ((b0 & 3) == 0) ip[7] = p[i++];
|
||||
if ((b0 & 3) != 0) ip[7] = (b0 & 3) == 1 ? 1 : (b0 & 3) == 2 ? 64 : 255;
|
||||
// Source address
|
||||
if ((b1 & 0x40) && sam == 0) { // SAC = 1, SAM = 00: unspecified address
|
||||
memset(ip + 8, 0, 16);
|
||||
} else {
|
||||
if (b1 & 0x40) return 0; // SAC = 1: no compression contexts
|
||||
i += decompress_addr(sam, p + i, n - i, h->src, NULL, ip + 8);
|
||||
if (i > n) return 0;
|
||||
}
|
||||
// Destination address
|
||||
if ((b1 & 0x0c) == 0x08) { // Multicast, stateless
|
||||
static const size_t sizes[] = {16, 6, 4, 1};
|
||||
if (i + sizes[dam] > n) return 0;
|
||||
memset(ip + 24, 0, 16), ip[24] = 0xff;
|
||||
if (dam == 0) memcpy(ip + 24, p + i, 16);
|
||||
if (dam == 1) ip[25] = p[i], memcpy(ip + 35, p + i + 1, 5);
|
||||
if (dam == 2) ip[25] = p[i], memcpy(ip + 37, p + i + 1, 3);
|
||||
if (dam == 3) ip[25] = 2, ip[39] = p[i];
|
||||
i += sizes[dam];
|
||||
} else if ((b1 & 0x08) == 0) { // Unicast
|
||||
if (b1 & 0x04) return 0; // DAC = 1: no compression contexts
|
||||
i += decompress_addr(dam, p + i, n - i, h->dst, NULL, ip + 24);
|
||||
if (i > n) return 0;
|
||||
} else {
|
||||
return 0; // Stateful multicast is not supported
|
||||
}
|
||||
if (b0 & 4) { // Next header compressed: only UDP, RFC 6282 4.3
|
||||
static const size_t port_sizes[] = {4, 3, 3, 1};
|
||||
uint8_t *u = ip + 40;
|
||||
unsigned nhc;
|
||||
if (i >= n || (p[i] & 0xf8) != 0xf0) return 0;
|
||||
nhc = p[i++];
|
||||
if (nhc & 4) return 0; // Elided checksum is not supported
|
||||
if (i + port_sizes[nhc & 3] + 2 > n) return 0; // Ports, checksum
|
||||
switch (nhc & 3) {
|
||||
case 0: memcpy(u, p + i, 4), i += 4; break;
|
||||
case 1: memcpy(u, p + i, 2), u[2] = 0xf0, u[3] = p[i + 2], i += 3; break;
|
||||
case 2: u[0] = 0xf0, u[1] = p[i], memcpy(u + 2, p + i + 1, 2), i += 3; break;
|
||||
default:
|
||||
u[0] = u[2] = 0xf0, u[1] = (uint8_t) (0xb0 | (p[i] >> 4));
|
||||
u[3] = (uint8_t) (0xb0 | (p[i] & 0x0f)), i += 1;
|
||||
break;
|
||||
}
|
||||
u[4] = u[5] = 0; // UDP length, set by caller
|
||||
memcpy(u + 6, p + i, 2), i += 2; // Checksum
|
||||
ip[6] = 17;
|
||||
*udp = true;
|
||||
}
|
||||
if (i > n) return 0;
|
||||
*used = i;
|
||||
return *udp ? 48 : 40;
|
||||
}
|
||||
|
||||
// Set IPv6 payload length and, for compressed UDP, UDP length
|
||||
static void set_lengths(uint8_t *ip, size_t len, bool udp) {
|
||||
size_t plen = len - 40;
|
||||
ip[4] = (uint8_t) (plen >> 8), ip[5] = (uint8_t) plen;
|
||||
if (udp) ip[44] = (uint8_t) (plen >> 8), ip[45] = (uint8_t) plen;
|
||||
}
|
||||
|
||||
void mg_l2_lowpan_init(struct mg_tcpip_if *ifp) {
|
||||
struct lowpan_data *d = &ifp->l2data.lowpan;
|
||||
if (is_zero(ifp->mac, 8)) {
|
||||
ifp->mac[0] = 0x02; // Locally administered EUI-64
|
||||
mg_random(&ifp->mac[1], 7);
|
||||
MG_INFO(("EUI-64 not set. Generated random: %M", mg_print_hex, 8,
|
||||
ifp->mac));
|
||||
}
|
||||
ifp->l2mtu = MGI_LOWPAN_MTU;
|
||||
ifp->framesize = MGI_LOWPAN_MTU; // TX buffer holds an uncompressed packet
|
||||
// Polling drivers receive one raw 802.15.4 frame at a time into the queue
|
||||
// buffer, so it does not need to be framesize: 127 bytes max
|
||||
if (ifp->recv_queue.size == 0) ifp->recv_queue.size = 128;
|
||||
d->short_addr = 0xfffe;
|
||||
d->buf = (uint8_t *) mg_calloc(1, MGI_LOWPAN_MTU);
|
||||
if (d->buf == NULL) MG_ERROR(("OOM"));
|
||||
}
|
||||
|
||||
bool mg_l2_lowpan_poll(struct mg_tcpip_if *ifp, bool expired_1000ms) {
|
||||
struct lowpan_data *d = &ifp->l2data.lowpan;
|
||||
if (expired_1000ms && d->size != 0 && ifp->now > d->expire) d->size = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t *mg_l2_lowpan_header(struct mg_tcpip_if *ifp, enum mg_l2proto proto,
|
||||
struct mg_l2addr *src, struct mg_l2addr *dst,
|
||||
uint8_t *frame) {
|
||||
struct lowpan_data *d = &ifp->l2data.lowpan;
|
||||
d->proto = (uint8_t) proto;
|
||||
memcpy(d->dst, dst, sizeof(d->dst));
|
||||
(void) src;
|
||||
return frame; // Uncompressed packet starts at the frame start
|
||||
}
|
||||
|
||||
// Compress the packet, send all fragments but the last directly, put the
|
||||
// last fragment into the TX buffer for the stack to send
|
||||
size_t mg_l2_lowpan_trailer(struct mg_tcpip_if *ifp, size_t len,
|
||||
uint8_t *cur) {
|
||||
struct lowpan_data *d = &ifp->l2data.lowpan;
|
||||
uint8_t *ip = cur - len, hdr[48], f[MGI_LOWPAN_FRAME_MAX];
|
||||
const uint8_t *dst = d->dst;
|
||||
size_t hlen, mlen, avail, chunk, off, n;
|
||||
if (d->proto != MG_TCPIP_L2PROTO_IPV6 || len < 40 || len > MGI_LOWPAN_MTU)
|
||||
return 0;
|
||||
hlen = iphc_compress(ip, ifp->mac, dst, hdr);
|
||||
mlen = write_mhr(ifp, dst, f);
|
||||
avail = MGI_LOWPAN_FRAME_MAX - mlen;
|
||||
if (hlen + len - 40 <= avail) { // Fits into one frame
|
||||
memcpy(f + mlen, hdr, hlen);
|
||||
memcpy(f + mlen + hlen, ip + 40, len - 40);
|
||||
n = mlen + hlen + len - 40;
|
||||
} else { // Fragment, RFC 4944 5.3. Offsets are in uncompressed bytes
|
||||
uint16_t tag = d->tag++;
|
||||
chunk = (avail - 4 - hlen) & ~(size_t) 7; // 40 + chunk: multiple of 8
|
||||
f[mlen] = (uint8_t) (MGI_DISPATCH_FRAG1 | (len >> 8));
|
||||
f[mlen + 1] = (uint8_t) len;
|
||||
f[mlen + 2] = (uint8_t) (tag >> 8), f[mlen + 3] = (uint8_t) tag;
|
||||
memcpy(f + mlen + 4, hdr, hlen);
|
||||
memcpy(f + mlen + 4 + hlen, ip + 40, chunk);
|
||||
n = mlen + 4 + hlen + chunk;
|
||||
ifp->driver->tx(f, n, ifp);
|
||||
for (off = 40 + chunk;;) {
|
||||
mlen = write_mhr(ifp, dst, f);
|
||||
chunk = len - off;
|
||||
if (chunk > ((avail - 5) & ~(size_t) 7)) chunk = (avail - 5) & ~(size_t) 7;
|
||||
f[mlen] = (uint8_t) (MGI_DISPATCH_FRAGN | (len >> 8));
|
||||
f[mlen + 1] = (uint8_t) len;
|
||||
f[mlen + 2] = (uint8_t) (tag >> 8), f[mlen + 3] = (uint8_t) tag;
|
||||
f[mlen + 4] = (uint8_t) (off / 8);
|
||||
memcpy(f + mlen + 5, ip + off, chunk);
|
||||
n = mlen + 5 + chunk;
|
||||
off += chunk;
|
||||
if (off >= len) break;
|
||||
ifp->driver->tx(f, n, ifp);
|
||||
}
|
||||
}
|
||||
memcpy(ifp->tx.buf, f, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
bool mg_l2_lowpan_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
|
||||
struct mg_str *pay, struct mg_str *raw) {
|
||||
struct lowpan_data *d = &ifp->l2data.lowpan;
|
||||
uint8_t *p = (uint8_t *) raw->buf;
|
||||
size_t n = raw->len, hlen, used, size, off;
|
||||
struct lowpan_mhr h;
|
||||
bool udp;
|
||||
if (d->buf == NULL || !parse_mhr(p, n, &h)) return false;
|
||||
if (h.dst_pan != d->pan_id && h.dst_pan != 0xffff) return false;
|
||||
if (!is_bcast(h.dst) && memcmp(h.dst, ifp->mac, 8) != 0 &&
|
||||
!(is_short(h.dst) && d->short_addr != 0xfffe &&
|
||||
MG_LOAD_BE16(h.dst + 6) == d->short_addr))
|
||||
return false;
|
||||
if (h.secured) return false; // No link-layer security
|
||||
p += h.len, n -= h.len;
|
||||
if (n < 1) return false;
|
||||
if ((p[0] & 0xf8) == MGI_DISPATCH_FRAG1 || (p[0] & 0xf8) == MGI_DISPATCH_FRAGN) {
|
||||
bool first = (p[0] & 0xf8) == MGI_DISPATCH_FRAG1;
|
||||
uint16_t tag;
|
||||
if (n < (first ? 4U : 5U)) return false;
|
||||
size = (size_t) (((p[0] & 7) << 8) | p[1]);
|
||||
tag = (uint16_t) ((p[2] << 8) | p[3]);
|
||||
if (size < 40 || size > MGI_LOWPAN_MTU) return false;
|
||||
if (first) {
|
||||
p += 4, n -= 4;
|
||||
hlen = iphc_decompress(&h, p, n, d->buf, &used, &udp);
|
||||
if (hlen == 0 || hlen + n - used > size) return false;
|
||||
memcpy(d->buf + hlen, p + used, n - used);
|
||||
d->size = (uint16_t) size, d->tag_rx = tag, d->udp = udp;
|
||||
d->received = (uint16_t) (hlen + n - used);
|
||||
memcpy(d->src, h.src, sizeof(d->src));
|
||||
d->expire = ifp->now + MGI_LOWPAN_REASM_MS;
|
||||
} else {
|
||||
off = (size_t) p[4] * 8;
|
||||
p += 5, n -= 5;
|
||||
if (d->size != size || d->tag_rx != tag ||
|
||||
memcmp(d->src, h.src, sizeof(d->src)) != 0 || off + n > size)
|
||||
return false; // Not the datagram being reassembled
|
||||
memcpy(d->buf + off, p, n);
|
||||
d->received = (uint16_t) (d->received + n);
|
||||
}
|
||||
if (d->size == 0 || d->received < d->size) return false;
|
||||
set_lengths(d->buf, d->size, d->udp);
|
||||
pay->buf = (char *) d->buf, pay->len = d->size;
|
||||
d->size = 0;
|
||||
} else {
|
||||
hlen = iphc_decompress(&h, p, n, d->buf, &used, &udp);
|
||||
if (hlen == 0 || hlen + n - used > MGI_LOWPAN_MTU) return false;
|
||||
memcpy(d->buf + hlen, p + used, n - used);
|
||||
set_lengths(d->buf, hlen + n - used, udp);
|
||||
pay->buf = (char *) d->buf, pay->len = hlen + n - used;
|
||||
}
|
||||
*proto = MG_TCPIP_L2PROTO_IPV6;
|
||||
return true;
|
||||
}
|
||||
|
||||
struct mg_l2addr *mg_l2_lowpan_getaddr(struct mg_tcpip_if *ifp,
|
||||
uint8_t *frame) {
|
||||
struct lowpan_mhr h;
|
||||
memset(&s_rxsrc, 0, sizeof(s_rxsrc));
|
||||
if (parse_mhr(frame, MGI_LOWPAN_FRAME_MAX, &h))
|
||||
memcpy(&s_rxsrc, h.src, sizeof(s_rxsrc));
|
||||
(void) ifp;
|
||||
return &s_rxsrc;
|
||||
}
|
||||
|
||||
extern struct mg_l2addr s_mapip;
|
||||
|
||||
// All broadcast and multicast goes to the 802.15.4 broadcast address
|
||||
struct mg_l2addr *mg_l2_lowpan_mapip(enum mg_l2addrtype addrtype,
|
||||
struct mg_addr *addr) {
|
||||
set_short((uint8_t *) &s_mapip, 0xffff);
|
||||
(void) addrtype, (void) addr;
|
||||
return &s_mapip;
|
||||
}
|
||||
|
||||
bool mg_l2_lowpan_genip6(uint64_t *ip6, uint8_t prefix_len,
|
||||
struct mg_l2addr *l2addr) {
|
||||
if (prefix_len > 64) {
|
||||
MG_ERROR(("Prefix length > 64, UNSUPPORTED"));
|
||||
return false;
|
||||
}
|
||||
ip6[0] = 0;
|
||||
make_iid((uint8_t *) l2addr, (uint8_t *) &ip6[1]); // RFC 6282 3.2.2
|
||||
return true;
|
||||
}
|
||||
|
||||
// Link-layer address of a neighbour from its IPv6 address: its interface ID
|
||||
// is made from it, RFC 6282 3.2.2. make_iid() is its own inverse
|
||||
void mg_l2_lowpan_ip6l2(const uint64_t *ip6, uint8_t *l2) {
|
||||
make_iid((const uint8_t *) &ip6[1], l2);
|
||||
}
|
||||
|
||||
// Link-layer address option, RFC 4944 8: length 2 for EUI-64, 1 for short
|
||||
bool mg_l2_lowpan_ip6get(struct mg_l2addr *l2addr, uint8_t *opts,
|
||||
uint8_t len) {
|
||||
if (len == 2) {
|
||||
memcpy(l2addr, opts, 8);
|
||||
} else if (len == 1) {
|
||||
set_short((uint8_t *) l2addr, (uint16_t) ((opts[0] << 8) | opts[1]));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t mg_l2_lowpan_ip6put(struct mg_l2addr *l2addr, uint8_t *opts) {
|
||||
uint8_t *a = (uint8_t *) l2addr;
|
||||
if (is_short(a)) {
|
||||
opts[0] = a[6], opts[1] = a[7];
|
||||
return 1;
|
||||
}
|
||||
memcpy(opts, a, 8);
|
||||
return 2;
|
||||
}
|
||||
|
||||
#elif MG_ENABLE_TCPIP
|
||||
// No IPv6: 6LoWPAN is not available. Stubs for the L2 dispatch tables
|
||||
extern struct mg_l2addr s_mapip;
|
||||
|
||||
void mg_l2_lowpan_init(struct mg_tcpip_if *ifp) {
|
||||
MG_ERROR(("6LoWPAN needs MG_ENABLE_IPV6"));
|
||||
(void) ifp;
|
||||
}
|
||||
|
||||
bool mg_l2_lowpan_poll(struct mg_tcpip_if *ifp, bool expired_1000ms) {
|
||||
(void) ifp, (void) expired_1000ms;
|
||||
return false; // Link is never up
|
||||
}
|
||||
|
||||
uint8_t *mg_l2_lowpan_header(struct mg_tcpip_if *ifp, enum mg_l2proto proto,
|
||||
struct mg_l2addr *src, struct mg_l2addr *dst,
|
||||
uint8_t *frame) {
|
||||
(void) ifp, (void) proto, (void) src, (void) dst;
|
||||
return frame;
|
||||
}
|
||||
|
||||
size_t mg_l2_lowpan_trailer(struct mg_tcpip_if *ifp, size_t len,
|
||||
uint8_t *cur) {
|
||||
(void) ifp, (void) len, (void) cur;
|
||||
return 0; // Nothing to send
|
||||
}
|
||||
|
||||
bool mg_l2_lowpan_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
|
||||
struct mg_str *pay, struct mg_str *raw) {
|
||||
(void) ifp, (void) proto, (void) pay, (void) raw;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct mg_l2addr *mg_l2_lowpan_getaddr(struct mg_tcpip_if *ifp,
|
||||
uint8_t *frame) {
|
||||
(void) ifp, (void) frame;
|
||||
return &s_mapip;
|
||||
}
|
||||
|
||||
struct mg_l2addr *mg_l2_lowpan_mapip(enum mg_l2addrtype addrtype,
|
||||
struct mg_addr *addr) {
|
||||
(void) addrtype, (void) addr;
|
||||
return &s_mapip;
|
||||
}
|
||||
#endif
|
||||
@@ -211,13 +211,7 @@ struct mg_connection *mg_connect_svc(struct mg_mgr *mgr, const char *url,
|
||||
if (url == NULL || url[0] == '\0') {
|
||||
MG_ERROR(("null url"));
|
||||
#if MG_ENABLE_TCPIP
|
||||
} else if (mgr->ifp != NULL && mgr->ifp->state != MG_TCPIP_STATE_READY
|
||||
#if MG_ENABLE_IPV6
|
||||
// IPv6-only links: a link-local address is enough for local peers
|
||||
&& (mgr->ifp->state6 < MG_TCPIP_STATE_UP ||
|
||||
mgr->ifp->ip6ll[0] == 0)
|
||||
#endif
|
||||
) {
|
||||
} else if (mgr->ifp != NULL && mgr->ifp->state != MG_TCPIP_STATE_READY) {
|
||||
MG_ERROR(("Network is down"));
|
||||
#endif
|
||||
} else if ((c = mg_alloc_conn(mgr)) == NULL) {
|
||||
|
||||
+19
-262
@@ -1,7 +1,6 @@
|
||||
#include "net_builtin.h"
|
||||
#include "bsd.h"
|
||||
#include "profile.h"
|
||||
#include "url.h"
|
||||
|
||||
#if MG_ENABLE_TCPIP
|
||||
#define MG_EPHEMERAL_PORT_BASE 32768
|
||||
@@ -970,7 +969,7 @@ static void tx_ndp_rs(struct mg_tcpip_if *ifp) {
|
||||
(struct mg_addr *) &ip6_allrouters),
|
||||
ifp->ip6ll, (uint64_t *) ip6_allrouters.addr.ip6, 133, 0, payload,
|
||||
payload_len);
|
||||
MG_VERBOSE(("NDP Router Solicitation sent"));
|
||||
MG_DEBUG(("NDP Router Solicitation sent"));
|
||||
}
|
||||
|
||||
static void fill_prefix(uint8_t *dst, uint8_t *src, uint8_t len) {
|
||||
@@ -1009,10 +1008,9 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
uint8_t *opts = (uint8_t *) (ra + 1);
|
||||
size_t opt_left = pkt->pay.len - sizeof(*ra);
|
||||
bool gotl2addr = false, gotprefix = false, changed = false;
|
||||
bool ip6only = ifp->ip == 0 && !ifp->enable_dhcp_client;
|
||||
uint8_t l2[sizeof(struct mg_l2addr)];
|
||||
uint32_t mtu = 0;
|
||||
uint8_t *prefix = NULL, prefix_len = 0, *dns = NULL;
|
||||
uint8_t *prefix = NULL, prefix_len = 0;
|
||||
|
||||
if (pkt->pay.len < sizeof(*ra)) return;
|
||||
if (ifp->state6 == MG_TCPIP_STATE_UP) {
|
||||
@@ -1048,21 +1046,11 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
if (prefix_len != ifp->prefix_len ||
|
||||
!match_prefix(prefix, ifp->prefix, ifp->prefix_len))
|
||||
changed = true;
|
||||
} else if (type == 38 && length >= 16 && ip6only && (opts[3] & 7) == 0) {
|
||||
memcpy(ifp->nat64, opts + 4, sizeof(ifp->nat64)); // RFC-8781, /96
|
||||
} else if (type == 25 && length >= 24 && ip6only) {
|
||||
dns = opts + 8; // DNS server, RFC-8106
|
||||
}
|
||||
opts += length;
|
||||
opt_left -= length;
|
||||
}
|
||||
|
||||
if (dns != NULL && ifp->dns6_url == NULL &&
|
||||
(ifp->dns6_url = (char *) mg_calloc(1, 64)) != NULL) { // First wins
|
||||
mg_snprintf(ifp->dns6_url, 64, "udp://%M:53", mg_print_ip6, dns);
|
||||
ifp->mgr->dns6.url = ifp->dns6_url, ifp->mgr->use_dns6 = true;
|
||||
if (ifp->mgr->dns6.c != NULL) mg_close_conn(ifp->mgr->dns6.c);
|
||||
}
|
||||
// fill prefix and global
|
||||
if (gotprefix && !fill_global(ifp, prefix, prefix_len)) return;
|
||||
if (changed) ifp->is_ip6_changed = true;
|
||||
@@ -1088,208 +1076,6 @@ static void rx_ndp_ra(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
}
|
||||
}
|
||||
|
||||
// Routing between two interfaces, see mg_tcpip_route()
|
||||
void mg_l2_lowpan_ip6l2(const uint64_t *ip6, uint8_t *l2);
|
||||
|
||||
// Interface with a user-set IPv6 prefix, which the router advertises
|
||||
static bool has_static_prefix(struct mg_tcpip_if *ifp) {
|
||||
return ifp->route != NULL && !ifp->enable_slaac && ifp->prefix_len > 0 &&
|
||||
ifp->prefix_len <= 64 && ifp->ip6[0] != 0;
|
||||
}
|
||||
|
||||
// Router Advertisement, 4.2: our static prefix (on-link, SLAAC, we are the
|
||||
// default router), and a route to the other interface's static prefix
|
||||
static void tx_ndp_ra(struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_if *peer = ifp->route;
|
||||
uint8_t buf[sizeof(struct ndp_ra) + 16 + 32 + 16 + 16 + 24], *o;
|
||||
struct ndp_ra *ra = (struct ndp_ra *) buf;
|
||||
bool own = has_static_prefix(ifp), other = has_static_prefix(peer);
|
||||
bool nat64 = own && (ifp->nat64[0] | ifp->nat64[1]) != 0; // Prefix set
|
||||
size_t n = sizeof(*ra);
|
||||
if (!own && !other) return;
|
||||
memset(buf, 0, sizeof(buf));
|
||||
ra->cur_hop_limit = 64;
|
||||
ra->router_lifetime = mg_htons(own ? 1800 : 0);
|
||||
buf[n] = 1; // Source link-layer address, 4.6.1
|
||||
buf[n + 1] = mg_l2_ip6put(ifp->l2type, ifp->mac, buf + n + 2);
|
||||
n += (size_t) (8 * buf[n + 1]);
|
||||
if (own) { // Prefix information, 4.6.2
|
||||
o = buf + n, n += 32;
|
||||
o[0] = 3, o[1] = 4, o[2] = ifp->prefix_len, o[3] = 0xc0; // L, A
|
||||
MG_STORE_BE32(o + 4, 86400); // Valid lifetime
|
||||
MG_STORE_BE32(o + 8, 14400); // Preferred lifetime
|
||||
memcpy(o + 16, ifp->prefix, sizeof(ifp->prefix));
|
||||
}
|
||||
if (other) { // Route information, RFC-4191 2.3
|
||||
o = buf + n, n += 16;
|
||||
o[0] = 24, o[1] = 2, o[2] = peer->prefix_len;
|
||||
MG_STORE_BE32(o + 4, 1800); // Route lifetime
|
||||
memcpy(o + 8, peer->prefix, sizeof(peer->prefix));
|
||||
}
|
||||
if (nat64) { // We translate to IPv4: NAT64 prefix and a DNS server
|
||||
struct mg_addr a;
|
||||
o = buf + n, n += 16; // NAT64 prefix, RFC-8781
|
||||
o[0] = 38, o[1] = 2;
|
||||
MG_STORE_BE16(o + 2, (1800 / 8) << 3); // Lifetime in 8s units, PLC 0: /96
|
||||
memcpy(o + 4, ifp->nat64, sizeof(ifp->nat64));
|
||||
o = buf + n, n += 24; // DNS server, RFC-8106: NAT64 prefix + IPv4 DNS
|
||||
o[0] = 25, o[1] = 3;
|
||||
MG_STORE_BE32(o + 4, 1800); // Lifetime
|
||||
memcpy(o + 8, ifp->nat64, sizeof(ifp->nat64));
|
||||
if (!mg_aton(mg_url_host(peer->mgr->dns4.url), &a) || a.is_ip6)
|
||||
a.addr.ip4 = MG_U32(8, 8, 8, 8);
|
||||
memcpy(o + 20, &a.addr.ip4, 4);
|
||||
}
|
||||
tx_icmp6(ifp,
|
||||
mg_l2_mapip(ifp->l2type, MG_TCPIP_L2ADDR_MCAST6,
|
||||
(struct mg_addr *) &ip6_allnodes),
|
||||
ifp->ip6ll, (uint64_t *) ip6_allnodes.addr.ip6, 134, 0, buf, n);
|
||||
}
|
||||
|
||||
// Remember the hardware address of a sender, to forward replies to it
|
||||
static void nb6_learn(struct mg_tcpip_if *ifp, uint64_t *ip, uint8_t *l2) {
|
||||
size_t i, n = sizeof(ifp->nb6) / sizeof(ifp->nb6[0]);
|
||||
for (i = 0; i < n && !MG_IP6MATCH(ifp->nb6[i].ip, ip); i++) (void) 0;
|
||||
if (i == n) i = ifp->nb6_next, ifp->nb6_next = (uint8_t) ((i + 1) % n);
|
||||
ifp->nb6[i].ip[0] = ip[0], ifp->nb6[i].ip[1] = ip[1];
|
||||
memcpy(ifp->nb6[i].l2, l2, sizeof(ifp->nb6[i].l2));
|
||||
}
|
||||
|
||||
// Hardware address to send a forwarded packet to: a learned neighbour, the
|
||||
// 6LoWPAN address made from the interface ID, or our default router
|
||||
static bool nb6_l2(struct mg_tcpip_if *ifp, uint64_t *ip, uint8_t *l2) {
|
||||
size_t i, n = sizeof(ifp->nb6) / sizeof(ifp->nb6[0]);
|
||||
for (i = 0; i < n && !MG_IP6MATCH(ifp->nb6[i].ip, ip); i++) (void) 0;
|
||||
if (i < n) {
|
||||
memcpy(l2, ifp->nb6[i].l2, sizeof(ifp->nb6[i].l2));
|
||||
} else if (ifp->l2type == MG_TCPIP_L2_LOWPAN) {
|
||||
mg_l2_lowpan_ip6l2(ip, l2);
|
||||
} else if (ifp->gw6_ready) {
|
||||
memcpy(l2, ifp->gw6mac, sizeof(ifp->gw6mac));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// NAT64, RFC-6146: translate a TCP or UDP packet sent to our nat64 prefix to
|
||||
// IPv4, send it from the other interface. Sessions are kept in ifp->nat64s,
|
||||
// when it is full the oldest one is replaced
|
||||
static void nat64_out(struct mg_tcpip_if *ifp, struct pkt *pkt, size_t len) {
|
||||
struct mg_tcpip_if *out = ifp->route;
|
||||
struct mg_tcpip_nat64 *e = NULL;
|
||||
struct ip *ip;
|
||||
uint64_t src[2], dst[2];
|
||||
uint8_t *p, next = pkt->ip6->next;
|
||||
size_t i, n = sizeof(ifp->nat64s) / sizeof(ifp->nat64s[0]);
|
||||
size_t plen = len - sizeof(*pkt->ip6);
|
||||
struct tcp *tcp = (struct tcp *) (pkt->ip6 + 1);
|
||||
struct udp *udp = (struct udp *) tcp; // Ports are at the same place
|
||||
uint32_t dst4;
|
||||
if ((next != 6 && next != 17) ||
|
||||
plen < (next == 6 ? sizeof(*tcp) : sizeof(*udp)) ||
|
||||
sizeof(*ip) + plen > out->mtu || out->state != MG_TCPIP_STATE_READY)
|
||||
return;
|
||||
src[0] = pkt->ip6->src[0], src[1] = pkt->ip6->src[1];
|
||||
dst[0] = pkt->ip6->dst[0], dst[1] = pkt->ip6->dst[1];
|
||||
memcpy(&dst4, (uint8_t *) dst + 12, sizeof(dst4)); // RFC-6052, /96
|
||||
for (i = 0; i < n; i++) {
|
||||
e = &ifp->nat64s[i];
|
||||
if (e->proto == next && e->port6 == udp->sport &&
|
||||
MG_IP6MATCH(e->ip6, src) && e->port4 == udp->dport && e->ip4 == dst4)
|
||||
break;
|
||||
}
|
||||
if (i == n) { // New session
|
||||
e = &ifp->nat64s[ifp->nat64_next];
|
||||
ifp->nat64_next = (uint8_t) ((ifp->nat64_next + 1) % n);
|
||||
if (out->eport < MG_EPHEMERAL_PORT_BASE) out->eport = MG_EPHEMERAL_PORT_BASE;
|
||||
e->ip6[0] = src[0], e->ip6[1] = src[1], e->ip4 = dst4, e->proto = next;
|
||||
e->port6 = udp->sport, e->port4 = udp->dport;
|
||||
e->lport = mg_htons(out->eport++);
|
||||
MG_DEBUG(("NAT64 %M:%hu -> %M:%hu", mg_print_ip6, src,
|
||||
mg_ntohs(udp->sport), mg_print_ip4, &dst4, mg_ntohs(udp->dport)));
|
||||
}
|
||||
ip = tx_ip(out, out->gwmac, next, 0, out->ip, dst4, plen);
|
||||
p = (uint8_t *) (ip + 1);
|
||||
memcpy(p, tcp, plen);
|
||||
tcp = (struct tcp *) p, udp = (struct udp *) p;
|
||||
udp->sport = e->lport;
|
||||
if (next == 6) tcp->csum = 0, tcp->csum = pcsum(ip, p, plen);
|
||||
if (next == 17) udp->csum = 0, udp->csum = pcsum(ip, p, plen);
|
||||
driver_output(out, mg_l2_trailer(out, sizeof(*ip) + plen, p + plen));
|
||||
}
|
||||
|
||||
// NAT64: translate an IPv4 packet of a NAT64 session back to IPv6, send it to
|
||||
// the node. Return true if the packet was taken
|
||||
static bool nat64_in(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
struct mg_tcpip_if *out = ifp->route;
|
||||
struct mg_tcpip_nat64 *e = NULL;
|
||||
struct udp *udp = pkt->udp != NULL ? pkt->udp : (struct udp *) pkt->tcp;
|
||||
struct ip6 *ip6;
|
||||
uint64_t src[2];
|
||||
uint8_t l2[sizeof(struct mg_l2addr)], *p, next = pkt->ip->proto;
|
||||
size_t i, n = sizeof(ifp->nat64s) / sizeof(ifp->nat64s[0]);
|
||||
size_t plen = PDIFF(udp, pkt->pay.buf + pkt->pay.len);
|
||||
if (out == NULL || pkt->ip->dst != ifp->ip) return false;
|
||||
for (i = 0; i < n; i++) {
|
||||
e = &out->nat64s[i];
|
||||
if (e->proto == next && e->lport == udp->dport &&
|
||||
e->port4 == udp->sport && e->ip4 == pkt->ip->src)
|
||||
break;
|
||||
}
|
||||
if (i == n) return false;
|
||||
if (sizeof(*ip6) + plen > out->mtu || !nb6_l2(out, e->ip6, l2))
|
||||
return true; // Drop
|
||||
memcpy(src, out->nat64, sizeof(out->nat64));
|
||||
memcpy((uint8_t *) src + 12, &pkt->ip->src, 4);
|
||||
ip6 = tx_ip6(out, l2, next, 0, src, e->ip6, plen);
|
||||
p = (uint8_t *) (ip6 + 1);
|
||||
memcpy(p, udp, plen);
|
||||
((struct udp *) p)->dport = e->port6;
|
||||
if (next == 6) {
|
||||
struct tcp *tcp = (struct tcp *) p;
|
||||
tcp->csum = 0, tcp->csum = p6csum(ip6, p, 6, plen);
|
||||
} else {
|
||||
udp = (struct udp *) p;
|
||||
udp->csum = 0, udp->csum = p6csum(ip6, p, 17, plen);
|
||||
if (udp->csum == 0) udp->csum = 0xffff; // 0 is not allowed in IPv6
|
||||
}
|
||||
driver_output(out, mg_l2_trailer(out, sizeof(*ip6) + plen, p + plen));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Forward a packet that is not addressed to us out the other interface.
|
||||
// Return true if the packet was taken: forwarded or dropped
|
||||
static bool fwd_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt, size_t len) {
|
||||
struct mg_tcpip_if *out = ifp->route;
|
||||
uint64_t src[2], dst[2];
|
||||
uint8_t l2[sizeof(struct mg_l2addr)], *d = (uint8_t *) dst, *p;
|
||||
src[0] = pkt->ip6->src[0], src[1] = pkt->ip6->src[1];
|
||||
dst[0] = pkt->ip6->dst[0], dst[1] = pkt->ip6->dst[1];
|
||||
if (d[0] == 0xff || (d[0] == 0xfe && (d[1] & 0xc0) == 0x80))
|
||||
return false; // Multicast or link-local: not forwarded
|
||||
if (MG_IP6MATCH(dst, ifp->ip6) || MG_IP6MATCH(dst, ifp->ip6ll))
|
||||
return false; // Ours
|
||||
if (src[0] != 0 || src[1] != 0) {
|
||||
nb6_learn(ifp, src, mg_l2_getaddr(ifp, pkt->l2));
|
||||
}
|
||||
if ((ifp->nat64[0] | ifp->nat64[1]) != 0 && // NAT64 prefix set, and ours
|
||||
memcmp(dst, ifp->nat64, sizeof(ifp->nat64)) == 0) {
|
||||
nat64_out(ifp, pkt, len);
|
||||
return true;
|
||||
}
|
||||
if (MG_IP6MATCH(dst, out->ip6) || out->state6 < MG_TCPIP_STATE_UP ||
|
||||
pkt->ip6->hops <= 1 || len > out->mtu || !nb6_l2(out, dst, l2)) {
|
||||
return true; // Drop. TODO(): ICMPv6 errors, RFC-4443
|
||||
}
|
||||
p = mg_l2_header(out, MG_TCPIP_L2PROTO_IPV6, out->mac, l2,
|
||||
(uint8_t *) out->tx.buf);
|
||||
memcpy(p, pkt->ip6, len);
|
||||
((struct ip6 *) p)->hops--;
|
||||
driver_output(out, mg_l2_trailer(out, len, p + len));
|
||||
return true;
|
||||
}
|
||||
|
||||
static void rx_icmp6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
if (!icmp6csum_ok(pkt->ip6, pkt->icmp6, sizeof(*pkt->icmp6) + pkt->pay.len))
|
||||
return;
|
||||
@@ -1313,9 +1099,6 @@ static void rx_icmp6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
tx_icmp6(ifp, l2addr, target, ips.addr.ip6, 129, 0, pkt->pay.buf, plen);
|
||||
}
|
||||
} break;
|
||||
case 133: // Router Solicitation: answer if we route
|
||||
if (ifp->route != NULL) tx_ndp_ra(ifp);
|
||||
break;
|
||||
case 134: // Router Advertisement
|
||||
rx_ndp_ra(ifp, pkt);
|
||||
break;
|
||||
@@ -1577,9 +1360,9 @@ static struct mg_connection *accept_conn(struct mg_connection *lsn,
|
||||
if (lsn->loc.is_ip6) {
|
||||
c->rem.addr.ip6[0] = pkt->ip6->src[0],
|
||||
c->rem.addr.ip6[1] = pkt->ip6->src[1], c->rem.is_ip6 = true;
|
||||
// Reply from the address the peer connected to: link-local or global
|
||||
c->loc.addr.ip6[0] = pkt->ip6->dst[0],
|
||||
c->loc.addr.ip6[1] = pkt->ip6->dst[1], c->loc.is_ip6 = true;
|
||||
c->loc.addr.ip6[0] = c->mgr->ifp->ip6[0],
|
||||
c->loc.addr.ip6[1] = c->mgr->ifp->ip6[1], c->loc.is_ip6 = true;
|
||||
// TODO(): compare lsn to link-local, or rem as link-local: use ll instead
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
@@ -2096,9 +1879,6 @@ static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
MG_VERBOSE(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src,
|
||||
mg_ntohs(pkt->udp->sport), mg_print_ip4, &pkt->ip->dst,
|
||||
mg_ntohs(pkt->udp->dport), (int) pkt->pay.len));
|
||||
#if MG_ENABLE_IPV6
|
||||
if (nat64_in(ifp, pkt)) return;
|
||||
#endif
|
||||
if (ifp->enable_dhcp_client && pkt->udp->dport == mg_htons(68) &&
|
||||
len >= offsetof(struct dhcp, options)) {
|
||||
pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
|
||||
@@ -2124,9 +1904,6 @@ static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
MG_VERBOSE(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip4, &pkt->ip->src,
|
||||
mg_ntohs(pkt->tcp->sport), mg_print_ip4, &pkt->ip->dst,
|
||||
mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len));
|
||||
#if MG_ENABLE_IPV6
|
||||
if (nat64_in(ifp, pkt)) return;
|
||||
#endif
|
||||
rx_tcp(ifp, pkt);
|
||||
} else {
|
||||
MG_DEBUG(("Unknown IP proto %x", (int) pkt->ip->proto));
|
||||
@@ -2144,8 +1921,6 @@ static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
if ((pkt->ip6->ver >> 4) != 0x6) return; // Not IPv6
|
||||
plen = mg_ntohs(pkt->ip6->plen);
|
||||
if (plen > (pkt->pay.len - sizeof(*pkt->ip6))) return; // malformed
|
||||
if (ifp->route != NULL && fwd_ip6(ifp, pkt, sizeof(*pkt->ip6) + plen))
|
||||
return;
|
||||
next = pkt->ip6->next;
|
||||
nhdr = (uint8_t *) (pkt->ip6 + 1);
|
||||
while (loop) {
|
||||
@@ -2155,7 +1930,7 @@ static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
case 43: // Routing 4.4
|
||||
case 60: // Destination Options 4.6
|
||||
case 51: // Authentication RFC-4302
|
||||
MG_DEBUG(("IPv6 extension header %d", (int) next));
|
||||
MG_INFO(("IPv6 extension header %d", (int) next));
|
||||
if (((uint32_t) len + 2) > plen) return; // nhdr[0, 1]; malformed
|
||||
next = nhdr[0];
|
||||
hlen = (uint16_t) (8 * (nhdr[1] + 1));
|
||||
@@ -2188,8 +1963,8 @@ static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
pkt->icmp6 = (struct icmp6 *) (pkt->pay.buf);
|
||||
if (pkt->pay.len < sizeof(*pkt->icmp6)) return;
|
||||
mkpay(pkt, pkt->icmp6 + 1);
|
||||
MG_VERBOSE(("ICMPv6 %M -> %M len %u", mg_print_ip6, &pkt->ip6->src,
|
||||
mg_print_ip6, &pkt->ip6->dst, (int) pkt->pay.len));
|
||||
MG_DEBUG(("ICMPv6 %M -> %M len %u", mg_print_ip6, &pkt->ip6->src,
|
||||
mg_print_ip6, &pkt->ip6->dst, (int) pkt->pay.len));
|
||||
rx_icmp6(ifp, pkt);
|
||||
} else if (next == 17) {
|
||||
pkt->udp = (struct udp *) (pkt->pay.buf);
|
||||
@@ -2199,9 +1974,9 @@ static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
if (len < sizeof(*pkt->udp) || len > pkt->pay.len) return; // malformed
|
||||
pkt->pay.len = len; // strip excess data
|
||||
mkpay(pkt, pkt->udp + 1);
|
||||
MG_VERBOSE(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src,
|
||||
mg_ntohs(pkt->udp->sport), mg_print_ip6, &pkt->ip6->dst,
|
||||
mg_ntohs(pkt->udp->dport), (int) pkt->pay.len));
|
||||
MG_DEBUG(("UDP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src,
|
||||
mg_ntohs(pkt->udp->sport), mg_print_ip6, &pkt->ip6->dst,
|
||||
mg_ntohs(pkt->udp->dport), (int) pkt->pay.len));
|
||||
if (ifp->enable_dhcp6_client && pkt->udp->dport == mg_htons(546)) {
|
||||
pkt->dhcp6 = (struct dhcp6 *) (pkt->udp + 1);
|
||||
mkpay(pkt, pkt->dhcp6 + 1);
|
||||
@@ -2223,9 +1998,9 @@ static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
if (off < sizeof(*pkt->tcp) / 4 || pkt->pay.len < (uint16_t) (4 * off))
|
||||
return;
|
||||
mkpay(pkt, (uint32_t *) pkt->tcp + off);
|
||||
MG_VERBOSE(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src,
|
||||
mg_ntohs(pkt->tcp->sport), mg_print_ip6, &pkt->ip6->dst,
|
||||
mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len));
|
||||
MG_DEBUG(("TCP %M:%hu -> %M:%hu len %u", mg_print_ip6, &pkt->ip6->src,
|
||||
mg_ntohs(pkt->tcp->sport), mg_print_ip6, &pkt->ip6->dst,
|
||||
mg_ntohs(pkt->tcp->dport), (int) pkt->pay.len));
|
||||
rx_tcp(ifp, pkt);
|
||||
} else {
|
||||
MG_DEBUG(("Unknown IPv6 next hdr %x", (int) next));
|
||||
@@ -2303,18 +2078,13 @@ static void mg_ip6_poll(struct mg_tcpip_if *ifp, bool s1) {
|
||||
if (ifp->state6 < MG_TCPIP_STATE_UP) return;
|
||||
if (ifp->enable_slaac && s1 && ifp->state6 == MG_TCPIP_STATE_UP)
|
||||
tx_ndp_rs(ifp);
|
||||
if (ifp->route != NULL && s1 && ifp->now >= ifp->ra_expire) {
|
||||
tx_ndp_ra(ifp); // Unsolicited router advertisements, 6.2.4
|
||||
ifp->ra_expire = ifp->now + 10000;
|
||||
}
|
||||
}
|
||||
static void mg_ip6_link(struct mg_tcpip_if *ifp, bool drv_up, bool l2_up) {
|
||||
bool cur_drv = (ifp->state6 != MG_TCPIP_STATE_DOWN);
|
||||
bool cur_l2 = (ifp->state6 >= MG_TCPIP_STATE_UP);
|
||||
const uint8_t px[8] = {0xfe, 0x80, 0, 0, 0, 0, 0, 0}; // RFC-4291 2.5.6
|
||||
if (drv_up != cur_drv || l2_up != cur_l2) { // link/L2 state has changed
|
||||
if ((ifp->l2type == MG_TCPIP_L2_ETH || ifp->l2type == MG_TCPIP_L2_LOWPAN) &&
|
||||
l2_up && ifp->ip6ll[0] == 0 &&
|
||||
if (ifp->l2type == MG_TCPIP_L2_ETH && l2_up && ifp->ip6ll[0] == 0 &&
|
||||
ifp->ip6ll[1] == 0) { // gen ll address
|
||||
mg_l2_genip6(ifp->l2type, ifp->ip6ll, 64, ifp->mac);
|
||||
memcpy(ifp->ip6ll, px, 8); // RFC-2464 5
|
||||
@@ -2506,8 +2276,8 @@ void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) {
|
||||
ifp->mgr = mgr;
|
||||
mgr->extraconnsize = sizeof(struct connstate);
|
||||
if (ifp->ip == 0 && ifp->l2type != MG_TCPIP_L2_PPP &&
|
||||
ifp->l2type != MG_TCPIP_L2_PPPoE && ifp->l2type != MG_TCPIP_L2_LOWPAN)
|
||||
ifp->enable_dhcp_client = true; // No IPv4 over 6LoWPAN
|
||||
ifp->l2type != MG_TCPIP_L2_PPPoE)
|
||||
ifp->enable_dhcp_client = true;
|
||||
mg_random(&ifp->eport, sizeof(ifp->eport)); // Random from 0 to 65535
|
||||
ifp->eport |= MG_EPHEMERAL_PORT_BASE; // Random from
|
||||
// MG_EPHEMERAL_PORT_BASE to 65535
|
||||
@@ -2529,18 +2299,8 @@ void mg_tcpip_free(struct mg_tcpip_if *ifp) {
|
||||
mg_free(ifp->recv_queue.buf);
|
||||
mg_free(ifp->tx.buf);
|
||||
mg_free(ifp->dns4_url);
|
||||
#if MG_ENABLE_IPV6
|
||||
mg_free(ifp->dns6_url);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if MG_ENABLE_IPV6
|
||||
void mg_tcpip_route(struct mg_tcpip_if *if1, struct mg_tcpip_if *if2) {
|
||||
if1->route = if2, if2->route = if1;
|
||||
if1->ra_expire = if2->ra_expire = 0; // Advertise at the next poll
|
||||
}
|
||||
#endif
|
||||
|
||||
static void send_syn(struct mg_connection *c) {
|
||||
struct connstate *s = (struct connstate *) (c + 1);
|
||||
uint32_t isn = mg_htonl((uint32_t) mg_ntohs(c->loc.port));
|
||||
@@ -2714,13 +2474,10 @@ void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
|
||||
bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
|
||||
struct mg_tcpip_if *ifp = c->mgr->ifp;
|
||||
bool res = false;
|
||||
// While resolving, the address family is unknown yet: just queue the data
|
||||
if (!c->is_resolving && !c->loc.is_ip6 &&
|
||||
(ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY)) {
|
||||
if (!c->loc.is_ip6 && (ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY)) {
|
||||
proto_error(c, false, "net down");
|
||||
#if MG_ENABLE_IPV6
|
||||
} else if (!c->is_resolving && c->loc.is_ip6 &&
|
||||
ifp->state6 != MG_TCPIP_STATE_READY) {
|
||||
} else if (c->loc.is_ip6 && ifp->state6 != MG_TCPIP_STATE_READY) {
|
||||
proto_error(c, false, "net down");
|
||||
#endif
|
||||
} else if (c->is_udp && (c->is_arplooking || c->is_resolving)) {
|
||||
|
||||
+33
-36
@@ -44,24 +44,6 @@ enum {
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
#if MG_ENABLE_IPV6
|
||||
// IPv6 neighbour learned from forwarded packets, see mg_tcpip_route()
|
||||
struct mg_tcpip_nb6 {
|
||||
uint64_t ip[2]; // Neighbour IPv6 address
|
||||
uint8_t l2[sizeof(struct mg_l2addr)]; // Its hardware address
|
||||
};
|
||||
|
||||
// NAT64 session, see mg_tcpip_if::nat64
|
||||
struct mg_tcpip_nat64 {
|
||||
uint64_t ip6[2]; // Node's IPv6 address
|
||||
uint32_t ip4; // IPv4 server address
|
||||
uint16_t port6; // Node's port
|
||||
uint16_t port4; // IPv4 server port
|
||||
uint16_t lport; // Our port on the IPv4 interface
|
||||
uint8_t proto; // 6: TCP, 17: UDP, 0: unused entry
|
||||
};
|
||||
#endif
|
||||
|
||||
// Network interface. Zero-initialise, then set the fields marked "user-set"
|
||||
// before calling mg_tcpip_init(). Fields in the "internal state" section
|
||||
// below are managed by the stack; read them freely but do not modify them.
|
||||
@@ -96,8 +78,6 @@ struct mg_tcpip_if {
|
||||
uint8_t prefix[8]; // Global prefix bytes
|
||||
uint8_t prefix_len; // Global prefix length in bits
|
||||
uint64_t gw6[2]; // IPv6 default gateway
|
||||
uint8_t nat64[12]; // NAT64 /96 prefix, RFC 6052. If set, DNS asks
|
||||
// for IPv4 (A) and makes IPv6 from it (DNS64)
|
||||
bool enable_slaac; // Enable SLAAC (stateless address autoconfiguration)
|
||||
bool enable_dhcp6_client; // Enable DHCPv6 client (not yet fully implemented)
|
||||
bool is_ip6_changed; // Set by stack when IPv6 address changes
|
||||
@@ -129,13 +109,6 @@ struct mg_tcpip_if {
|
||||
uint8_t gw6mac[sizeof(struct mg_l2addr)]; // IPv6 gateway hardware address
|
||||
uint8_t state6; // Current IPv6 link/IP state (MG_TCPIP_STATE_*)
|
||||
bool gw6_ready; // IPv6 gateway MAC address has been resolved
|
||||
struct mg_tcpip_if *route; // Forward to this interface, see mg_tcpip_route()
|
||||
struct mg_tcpip_nb6 nb6[4]; // Neighbours learned from forwarded packets
|
||||
uint8_t nb6_next; // Next nb6 entry to replace
|
||||
struct mg_tcpip_nat64 nat64s[8]; // NAT64 sessions, if nat64 is set
|
||||
uint8_t nat64_next; // Next nat64s entry to replace
|
||||
uint64_t ra_expire; // Time of the next router advertisement, ms
|
||||
char *dns6_url; // DNS server URL from router advertisement
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -149,15 +122,6 @@ void mg_tcpip_init(struct mg_mgr *, struct mg_tcpip_if *);
|
||||
// Frees the TX buffer and RX queue allocated by mg_tcpip_init().
|
||||
void mg_tcpip_free(struct mg_tcpip_if *);
|
||||
|
||||
#if MG_ENABLE_IPV6
|
||||
// Forwards IPv6 packets between two interfaces: packets not addressed to us
|
||||
// go out the other interface. An interface with a static IPv6 prefix (ip6,
|
||||
// prefix and prefix_len set, SLAAC off) gets router advertisements for it,
|
||||
// the other interface gets a route to it, RFC 4191. Call after both
|
||||
// mg_tcpip_init(). Replies to the other side need its sender seen first
|
||||
void mg_tcpip_route(struct mg_tcpip_if *if1, struct mg_tcpip_if *if2);
|
||||
#endif
|
||||
|
||||
// Pushes a received raw frame into ifp->recv_queue. Used by interrupt-driven
|
||||
// or DMA-based drivers (those with rx=NULL) to hand frames to the stack from
|
||||
// an ISR or DMA completion callback. Drops the frame and increments ndrop if
|
||||
@@ -193,6 +157,39 @@ extern struct mg_tcpip_driver mg_tcpip_driver_netc;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_mcxw71;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_rw612_154;
|
||||
|
||||
// IEEE 802.15.4 radio channel and PAN ID. Settable in mongoose_config.h
|
||||
#ifndef MG_L2_802154_CHANNEL
|
||||
#define MG_L2_802154_CHANNEL 15 // 11..26
|
||||
#endif
|
||||
#ifndef MG_L2_802154_PAN_ID
|
||||
#define MG_L2_802154_PAN_ID 0xabcd
|
||||
#endif
|
||||
|
||||
// driver_data for IEEE 802.15.4 radio drivers. The interface is Ethernet:
|
||||
// each Ethernet frame is split into broadcast radio frames, and reassembled
|
||||
// on the other side, see mg_l2_802154_tx() and mg_l2_802154_rx()
|
||||
struct mg_l2_802154_data {
|
||||
uint8_t channel; // User-set channel, 11..26
|
||||
uint16_t pan_id; // User-set PAN ID
|
||||
uint8_t seq, tag; // MAC sequence number, tag of the last sent frame
|
||||
uint8_t rxtag, rxnext; // Frame being reassembled: tag, next fragment
|
||||
uint8_t rxsrc[8]; // Its sender
|
||||
uint16_t rxlen; // Its bytes received so far
|
||||
uint8_t rxbuf[1514]; // Reassembly buffer, one Ethernet frame
|
||||
};
|
||||
|
||||
// Splits an Ethernet frame into radio frames, sends each by calling send().
|
||||
// Called by radio drivers' tx(). Return len, or 0 on error
|
||||
size_t mg_l2_802154_tx(struct mg_tcpip_if *ifp, const void *buf, size_t len,
|
||||
size_t (*send)(const uint8_t *, size_t,
|
||||
struct mg_tcpip_if *));
|
||||
|
||||
// Adds a received radio frame f of n bytes to the frame being reassembled.
|
||||
// Called by radio drivers' rx(). Return the Ethernet frame length, copied to
|
||||
// buf, when it is complete, otherwise 0
|
||||
size_t mg_l2_802154_rx(struct mg_tcpip_if *ifp, const uint8_t *f, size_t n,
|
||||
void *buf, size_t len);
|
||||
|
||||
// SPI bus abstraction for SPI-attached network chips (e.g. W5500).
|
||||
// Populate and assign to the driver's driver_data field.
|
||||
struct mg_tcpip_spi {
|
||||
|
||||
@@ -243,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) {
|
||||
static uint64_t t = 5000; // Fire first time 5 sec after boot
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#define MG_OTA_STM32H7 2 // STM32 H7
|
||||
#define MG_OTA_STM32H7_DUAL_CORE 3 // STM32 H7 dual core
|
||||
#define MG_OTA_STM32F 4 // STM32 F7/F4/F2
|
||||
#define MG_OTA_STM32C5 5 // STM32 C5, same flash controller as H5
|
||||
#define MG_OTA_CH32V307 100 // WCH CH32V307
|
||||
#define MG_OTA_U2A 200 // Renesas U2A16, U2A8, U2A6
|
||||
#define MG_OTA_RT1020 300 // IMXRT1020
|
||||
|
||||
@@ -172,8 +172,6 @@ bool mg_ota_end(void) {
|
||||
ok = mg_ota_flash_end(&s_mg_flash_mcxw71);
|
||||
s_mcxw71_size = s_mcxw71_pending = 0;
|
||||
if (ok) {
|
||||
// Like RW612, this overwrites the application. It does not provide
|
||||
// rollback or power-loss recovery during the copy. Keep power connected.
|
||||
MG_INFO(("Applying %lu bytes from staging flash", (unsigned long) size));
|
||||
mg_mcxw71_apply(size);
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
#include "log.h"
|
||||
#include "ota.h"
|
||||
|
||||
#if MG_OTA == MG_OTA_STM32H5
|
||||
#if MG_OTA == MG_OTA_STM32H5 || MG_OTA == MG_OTA_STM32C5
|
||||
|
||||
static bool mg_stm32h5_write(void *, const void *, size_t);
|
||||
static bool mg_stm32h5_swap(void);
|
||||
|
||||
@@ -138,10 +138,6 @@ size_t mg_print_l2addr(void (*out)(char, void *), void *arg, va_list *ap) {
|
||||
uint8_t *p = va_arg(*ap, uint8_t *);
|
||||
return print_mac(out, arg, p);
|
||||
} break;
|
||||
case MG_TCPIP_L2_LOWPAN: {
|
||||
uint8_t *p = va_arg(*ap, uint8_t *);
|
||||
return print_ieee64(out, arg, p);
|
||||
} break;
|
||||
default: break;
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
// See https://mongoose.ws/docs/getting-started/build-environment/
|
||||
// See https://mongoose.ws/documentation/#build-options
|
||||
|
||||
#define MG_ENABLE_TCPIP 1
|
||||
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
|
||||
|
||||
@@ -65,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 wifi_init(const char *ssid, const char *pass);
|
||||
MG_HEALTH_INIT();
|
||||
wifi_init(WIFI_SSID, WIFI_PASS);
|
||||
|
||||
struct mg_mgr mgr;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#define MG_ARCH MG_ARCH_ESP32
|
||||
|
||||
#define MG_ENABLE_HEALTH 1
|
||||
|
||||
#define MG_TLS MG_TLS_BUILTIN
|
||||
//#define MG_TLS MG_TLS_MBED
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright (c) 2026 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
//
|
||||
// IPv6 over IEEE 802.15.4 on FRDM-MCXW71, Mongoose built-in TCP/IP stack.
|
||||
// Plain 6LoWPAN on channel 15, PAN ID 0xabcd. With a router on the radio
|
||||
// link, e.g. the FRDM-RW612 lowpan tutorial, gets a global address and is
|
||||
// reachable from the LAN. Serves pages on ports 80 and 443
|
||||
// IPv4 over IEEE 802.15.4 on FRDM-MCXW71, Mongoose built-in TCP/IP stack.
|
||||
// The radio carries Ethernet frames, channel 15, PAN ID 0xabcd. With the
|
||||
// FRDM-RW612 eth_radio_bridge tutorial, gets an address from the LAN's DHCP
|
||||
// server and is reachable from the LAN. Serves pages on ports 80 and 443
|
||||
|
||||
#include "hal.h"
|
||||
#include "mongoose.h"
|
||||
@@ -47,14 +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),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_HDRS) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/ota"), NULL) &&
|
||||
!mg_http_stream_body(c, hm, mg_http_ota_cb, NULL)) {
|
||||
mg_http_reply(c, 400, "", "POST firmware with Content-Length\n");
|
||||
c->is_draining = 1;
|
||||
mg_iobuf_del(&c->recv, 0, c->recv.len);
|
||||
}
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
@@ -81,8 +73,8 @@ int main(void) {
|
||||
|
||||
struct mg_mgr mgr;
|
||||
mg_mgr_init(&mgr);
|
||||
mg_http_listen(&mgr, "http://[::]:80", http_ev_handler, NULL);
|
||||
mg_http_listen(&mgr, "https://[::]:443", http_ev_handler, NULL);
|
||||
mg_http_listen(&mgr, "http://0.0.0.0:80", http_ev_handler, NULL);
|
||||
mg_http_listen(&mgr, "https://0.0.0.0:443", http_ev_handler, NULL);
|
||||
mg_mqtt_init(&mgr);
|
||||
|
||||
MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000));
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
#define MG_ENABLE_CUSTOM_RANDOM 1
|
||||
#define MG_ENABLE_TCPIP_TCPRTX 1 // Link can be lossy, enable retransmit
|
||||
|
||||
// Stable EUI-64, so the IPv6 addresses stay the same across reboots: a hash
|
||||
// of the 128-bit chip unique ID, MSCM UID, locally administered
|
||||
#define MG_SET_MAC_ADDRESS(mac) \
|
||||
do { \
|
||||
uint8_t b_[32]; \
|
||||
mg_sha256(b_, (uint8_t *) MSCM->UID, sizeof(MSCM->UID)); \
|
||||
(mac)[0] = 2, (mac)[1] = b_[1], (mac)[2] = b_[2], (mac)[3] = b_[3]; \
|
||||
(mac)[4] = b_[4], (mac)[5] = b_[5], (mac)[6] = b_[6], (mac)[7] = b_[7]; \
|
||||
// Stable MAC, so the DHCP address stays the same across reboots: a hash of
|
||||
// the 128-bit chip unique ID, MSCM UID, locally administered
|
||||
#define MG_SET_MAC_ADDRESS(mac) \
|
||||
do { \
|
||||
uint8_t b_[32]; \
|
||||
mg_sha256(b_, (uint8_t *) MSCM->UID, sizeof(MSCM->UID)); \
|
||||
(mac)[0] = 2, (mac)[1] = b_[1], (mac)[2] = b_[2], (mac)[3] = b_[3]; \
|
||||
(mac)[4] = b_[4], (mac)[5] = b_[5]; \
|
||||
} while (0)
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,101 @@
|
||||
// Copyright (c) 2026 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
//
|
||||
// Ethernet <-> IEEE 802.15.4 radio bridge. Radio frames carry Ethernet frames
|
||||
// split into pieces, see mg_l2_802154_tx(). Radio nodes, e.g. the
|
||||
// FRDM-MCXW71 minimal tutorial, are on the Ethernet LAN: they get IPv4
|
||||
// addresses from its DHCP server. The bridge itself has no IP address.
|
||||
// Everything from the radio goes to Ethernet. From Ethernet, only broadcasts
|
||||
// and frames to radio nodes go to the radio, it is much slower
|
||||
|
||||
#include "hal.h"
|
||||
#include "mongoose.h"
|
||||
|
||||
#define UART_DEBUG USART3
|
||||
|
||||
static void log_fn(char ch, void *param) {
|
||||
hal_uart_write_buf(param, &ch, 1);
|
||||
}
|
||||
|
||||
uint64_t mg_millis(void) {
|
||||
return hal_get_tick();
|
||||
}
|
||||
|
||||
// MAC addresses of radio nodes, learned from frames they send
|
||||
static uint8_t s_nodes[8][6];
|
||||
static size_t s_next;
|
||||
|
||||
// Remember a radio node's MAC address, if new
|
||||
static void learn(const uint8_t *mac) {
|
||||
size_t i, n = sizeof(s_nodes) / sizeof(s_nodes[0]);
|
||||
for (i = 0; i < n && memcmp(s_nodes[i], mac, 6) != 0; i++) (void) 0;
|
||||
if (i < n) return;
|
||||
memcpy(s_nodes[s_next], mac, 6);
|
||||
s_next = (s_next + 1) % n;
|
||||
MG_INFO(("Radio node %M", mg_print_mac, mac));
|
||||
}
|
||||
|
||||
// Is an Ethernet frame for the radio: broadcast, or to a radio node
|
||||
static bool for_radio(const uint8_t *dst) {
|
||||
size_t i, n = sizeof(s_nodes) / sizeof(s_nodes[0]);
|
||||
if (memcmp(dst, "\xff\xff\xff\xff\xff\xff", 6) == 0) return true;
|
||||
for (i = 0; i < n && memcmp(s_nodes[i], dst, 6) != 0; i++) (void) 0;
|
||||
return i < n;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
static struct mg_tcpip_driver_rw612_data eth_data = {
|
||||
.mdc_cr = MG_DRIVER_MDC_CR, .phy_addr = MG_TCPIP_PHY_ADDR};
|
||||
static struct mg_l2_802154_data radio_data = {
|
||||
.channel = MG_L2_802154_CHANNEL, .pan_id = MG_L2_802154_PAN_ID};
|
||||
static struct mg_tcpip_if eth = {.driver = &mg_tcpip_driver_rw612,
|
||||
.driver_data = ð_data};
|
||||
static struct mg_tcpip_if radio = {.driver = &mg_tcpip_driver_rw612_154,
|
||||
.driver_data = &radio_data};
|
||||
static char eth_queue[8192]; // Filled by the Ethernet IRQ handler
|
||||
static uint8_t frame[1540];
|
||||
uint8_t uid[FSL_OCOTP_UID_LENGTH], digest[32];
|
||||
uint32_t uid_len = sizeof(uid);
|
||||
uint64_t timer = 0;
|
||||
char *p;
|
||||
size_t n;
|
||||
|
||||
hal_clock_init();
|
||||
hal_uart_init(UART_DEBUG, 115200);
|
||||
mg_log_set_fn(log_fn, UART_DEBUG);
|
||||
if (hal_radio_err) {
|
||||
MG_ERROR(("Radio firmware failed to start, step %d", hal_radio_err));
|
||||
}
|
||||
|
||||
// MAC addresses made from the chip unique ID
|
||||
memset(uid, 0, sizeof(uid));
|
||||
OCOTP_ReadUniqueID(uid, &uid_len);
|
||||
mg_sha256(digest, uid, sizeof(uid));
|
||||
memcpy(eth.mac, digest, 6);
|
||||
memcpy(radio.mac, digest + 6, 6);
|
||||
eth.mac[0] = radio.mac[0] = 0x02; // Locally administered, unicast
|
||||
|
||||
hal_ethernet_init();
|
||||
mg_queue_init(ð.recv_queue, eth_queue, sizeof(eth_queue));
|
||||
if (!eth.driver->init(ð)) MG_ERROR(("Ethernet init failed"));
|
||||
if (!radio.driver->init(&radio)) MG_ERROR(("Radio init failed"));
|
||||
ENET->RCR |= ENET_RCR_PROM_MASK; // Receive frames for radio nodes, too
|
||||
MG_INFO(("Bridge started, radio channel %d", radio_data.channel));
|
||||
|
||||
for (;;) {
|
||||
if ((n = mg_queue_next(ð.recv_queue, &p)) > 0) { // Ethernet -> radio
|
||||
if (n >= 14 && for_radio((uint8_t *) p)) radio.driver->tx(p, n, &radio);
|
||||
mg_queue_del(ð.recv_queue, n);
|
||||
}
|
||||
if ((n = radio.driver->rx(frame, sizeof(frame), &radio)) >= 14) {
|
||||
learn(frame + 6); // Radio -> Ethernet
|
||||
eth.driver->tx(frame, n, ð);
|
||||
}
|
||||
if (mg_timer_expired(&timer, 1000, mg_millis())) { // Ethernet link
|
||||
bool up = eth.driver->poll(ð, true);
|
||||
eth.state = up ? MG_TCPIP_STATE_UP : MG_TCPIP_STATE_DOWN;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
// See https://mongoose.ws/docs/getting-started/build-environment/
|
||||
#define MG_ARCH MG_ARCH_ARMGCC
|
||||
|
||||
#define MG_ENABLE_TCPIP 1
|
||||
#define MG_ENABLE_DRIVER_RW612 1 // Ethernet
|
||||
#define MG_ENABLE_DRIVER_RW612_154 1 // 802.15.4 radio
|
||||
#define MG_ENABLE_TCPIP_DRIVER_INIT 0 // main.c drives both, no TCP/IP stack
|
||||
#define MG_ENABLE_CUSTOM_MILLIS 1
|
||||
File renamed without changes.
File renamed without changes.
@@ -1,14 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// See https://mongoose.ws/docs/getting-started/build-environment/
|
||||
#define MG_ARCH MG_ARCH_ARMGCC
|
||||
|
||||
#define MG_ENABLE_TCPIP 1
|
||||
// Compile both drivers; main.c initialises both interfaces itself
|
||||
#define MG_ENABLE_DRIVER_RW612 1 // Ethernet
|
||||
#define MG_ENABLE_DRIVER_RW612_154 1 // 802.15.4 radio, 6LoWPAN
|
||||
#define MG_ENABLE_TCPIP_DRIVER_INIT 0 // Do not start one in mg_mgr_init()
|
||||
#define MG_ENABLE_CUSTOM_MILLIS 1
|
||||
#define MG_ENABLE_CUSTOM_RANDOM 1
|
||||
#define MG_TLS MG_TLS_BUILTIN
|
||||
#define MG_ENABLE_TCPIP_TCPRTX 1 // Radio link can be lossy
|
||||
@@ -0,0 +1,9 @@
|
||||
# Nucleo-C5A3ZI Mongoose Integration
|
||||
|
||||
The `.ioc2` file in this directory is pre-configured. Note that after the
|
||||
project generation, you should do the following to complete integraion:
|
||||
|
||||
1. Edit the linker script `.ld` file and increase heap and stack sizes to
|
||||
0x20000 and 0x4000 respectively
|
||||
2. Add Mongoose to the `main.c`, see https://mongoose.ws/docs/guides/stm32-tcpip-stack/#modify-mainc
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,46 @@
|
||||
# Copyright (c) 2026 Cesanta Software Limited
|
||||
# Environment setup: https://mongoose.ws/docs/getting-started/build-environment/
|
||||
|
||||
MGDIR = ../../../..
|
||||
|
||||
CFLAGS = -W -Wall -Wextra -Wundef -Wshadow -Wdouble-promotion
|
||||
CFLAGS += -Wformat-truncation -fno-common -Wconversion -Wno-sign-conversion
|
||||
CFLAGS += -g3 -Os -ffunction-sections -fdata-sections
|
||||
CFLAGS += -I. -I$(MGDIR) -Icmsis_core/CMSIS/Core/Include -Icmsis_c5/Include -DSTM32C5A3xx
|
||||
CFLAGS += -mcpu=cortex-m33 -mthumb -mfpu=fpv5-sp-d16 -mfloat-abi=hard $(CFLAGS_EXTRA)
|
||||
LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs nano.specs -lc -lgcc -Wl,--gc-sections
|
||||
|
||||
SOURCES = main.c hal.c
|
||||
SOURCES += cmsis_c5/Source/startup_stm32c5a3xx.c
|
||||
SOURCES += $(MGDIR)/mongoose.c
|
||||
|
||||
all build example: firmware.bin
|
||||
|
||||
firmware.elf: cmsis_core cmsis_c5 hal.h link.ld Makefile $(SOURCES) $(MGDIR)/mongoose.h mongoose_config.h
|
||||
arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@
|
||||
|
||||
firmware.bin: firmware.elf
|
||||
arm-none-eabi-objcopy -O binary $< $@
|
||||
@echo
|
||||
@echo "To flash, run 'make flash', or use STM32CubeProgrammer"
|
||||
|
||||
firmware.signed.bin: firmware.bin
|
||||
test -f private.pem || node $(MGDIR)/resources/sign.js keygen
|
||||
node $(MGDIR)/resources/sign.js sign firmware.bin
|
||||
|
||||
# Show all symbols sorted by size
|
||||
show_symbols_sorted_by_size: firmware.bin
|
||||
arm-none-eabi-readelf -sW firmware.elf | sed 1,3d | sed 's,^.*: ,,' | tr -s ' ' | cut -d ' ' -f 2,7 | sort -nk1 | grep -v '^0 '
|
||||
|
||||
# If the board gets stuck: STM32_Programmer_CLI -c port=swd -e all mode=UR
|
||||
flash: firmware.bin
|
||||
STM32_Programmer_CLI -c port=swd -w firmware.elf mode=UR -rst
|
||||
|
||||
cmsis_core:
|
||||
git clone -q -c advice.detachedHead=false --depth 1 -b v6.4.0 https://github.com/ARM-software/CMSIS_6 $@
|
||||
|
||||
cmsis_c5:
|
||||
git clone -q -c advice.detachedHead=false --depth 1 -b 2.1.0 https://github.com/STMicroelectronics/stm32c5xx-dfp $@
|
||||
|
||||
clean:
|
||||
rm -rf firmware.* cmsis_*
|
||||
@@ -0,0 +1,141 @@
|
||||
// Copyright (c) 2026 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include "hal.h"
|
||||
|
||||
// Added to minimise the bin size: avoid pulling printf and friends
|
||||
void __assert_func(const char *s, int line, const char *fn, const char *expr) {
|
||||
(void) s, (void) line, (void) fn, (void) expr;
|
||||
for (;;) (void) 0; // or trigger the fault handler
|
||||
}
|
||||
|
||||
bool hal_timer_expired(volatile uint64_t *t, uint64_t period, uint64_t now) {
|
||||
uint64_t diff = now - *t; // Wrap-safe elapsed time since last expiry
|
||||
if (period == 0) return false; // Avoid division by zero
|
||||
if (diff < period) return false; // Period has not elapsed yet
|
||||
*t += (diff / period) * period; // Preserve cadence, skip missed periods
|
||||
return true;
|
||||
}
|
||||
|
||||
static volatile uint64_t s_ticks; // Milliseconds since boot
|
||||
void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms
|
||||
s_ticks++;
|
||||
}
|
||||
|
||||
uint64_t hal_get_tick(void) {
|
||||
return s_ticks;
|
||||
};
|
||||
|
||||
uint32_t SystemCoreClock = 160000000;
|
||||
void SystemInit(void) { // Called automatically by startup code
|
||||
hal_system_init(); // Enable FPU
|
||||
hal_clock_init();
|
||||
SysTick_Config(SystemCoreClock / 1000); // Sys tick every 1ms
|
||||
}
|
||||
|
||||
void ExitRun0Mode(void) {
|
||||
}
|
||||
|
||||
// C library entry, called by the CMSIS startup code after it copies .data
|
||||
// and clears .bss. See __cmsis_start() in cmsis_gcc_m.h
|
||||
extern int main(void);
|
||||
extern void __libc_init_array(void);
|
||||
__attribute__((noreturn)) void _start(void) {
|
||||
__libc_init_array();
|
||||
main();
|
||||
for (;;) (void) 0;
|
||||
}
|
||||
|
||||
struct stat;
|
||||
__attribute__((weak)) int _fstat(int fd, struct stat *st) {
|
||||
(void) fd, (void) st;
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern unsigned char _end[]; // End of data section, start of heap. See link.ld
|
||||
static unsigned char *s_current_heap_end = _end;
|
||||
|
||||
size_t hal_ram_used(void) {
|
||||
return (size_t) (s_current_heap_end - _end);
|
||||
}
|
||||
|
||||
size_t hal_ram_free(void) {
|
||||
unsigned char endofstack;
|
||||
return (size_t) (&endofstack - s_current_heap_end);
|
||||
}
|
||||
|
||||
void *_sbrk(int incr) {
|
||||
unsigned char *prev_heap;
|
||||
unsigned char *heap_end = (unsigned char *) ((size_t) &heap_end - 256);
|
||||
prev_heap = s_current_heap_end;
|
||||
// Check how much space we got from the heap end to the stack end
|
||||
if (s_current_heap_end + incr > heap_end) return (void *) -1;
|
||||
s_current_heap_end += incr;
|
||||
return prev_heap;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _open(const char *path) {
|
||||
(void) path;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _close(int fd) {
|
||||
(void) fd;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _isatty(int fd) {
|
||||
(void) fd;
|
||||
return 1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _lseek(int fd, int ptr, int dir) {
|
||||
(void) fd, (void) ptr, (void) dir;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((weak)) void _exit(int status) {
|
||||
(void) status;
|
||||
for (;;) asm volatile("BKPT #0");
|
||||
}
|
||||
|
||||
__attribute__((weak)) void _kill(int pid, int sig) {
|
||||
(void) pid, (void) sig;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _getpid(void) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _write(int fd, char *ptr, int len) {
|
||||
(void) fd, (void) ptr, (void) len;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _read(int fd, char *ptr, int len) {
|
||||
(void) fd, (void) ptr, (void) len;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _link(const char *a, const char *b) {
|
||||
(void) a, (void) b;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _unlink(const char *a) {
|
||||
(void) a;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _stat(const char *path, struct stat *st) {
|
||||
(void) path, (void) st;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int mkdir(const char *path, int mode) {
|
||||
(void) path, (void) mode;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) void _init(void) {
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
// Copyright (c) 2013-2026 Cesanta Software Limited
|
||||
// Datasheet: RM0522, devboard manual: UM3616
|
||||
// https://www.st.com/resource/en/reference_manual/rm0522-stm32c5-series-armbased-32bit-mcus-stmicroelectronics.pdf
|
||||
// https://www.st.com/resource/en/user_manual/um3616-stm32c5-nucleo144-board-mb2310-stmicroelectronics.pdf
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stm32c5xx.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
extern bool hal_timer_expired(volatile uint64_t *t, uint64_t prd, uint64_t now);
|
||||
extern uint64_t hal_get_tick(void);
|
||||
extern size_t hal_ram_free(void);
|
||||
extern size_t hal_ram_used(void);
|
||||
extern uint32_t SystemCoreClock;
|
||||
|
||||
#define BIT(x) (1UL << (x))
|
||||
#define CLRSET(reg, clear, set) ((reg) = ((reg) & ~(clear)) | (set))
|
||||
#define PIN(bank, num) ((((bank) - 'A') << 8) | (num))
|
||||
#define PINNO(pin) (pin & 255)
|
||||
#define PINBANK(pin) (pin >> 8)
|
||||
|
||||
// Ethernet RMII pins and their alternate functions. UM3616 7.11, Table 9
|
||||
#define HAL_ETH_PINS \
|
||||
{PIN('A', 1), 10}, /* REF_CLK */ \
|
||||
{PIN('C', 1), 10}, /* MDC */ \
|
||||
{PIN('E', 12), 10}, /* MDIO */ \
|
||||
{PIN('D', 1), 10}, /* CRS_DV */ \
|
||||
{PIN('C', 4), 12}, /* RXD0 */ \
|
||||
{PIN('C', 5), 13}, /* RXD1 */ \
|
||||
{PIN('G', 11), 10}, /* TX_EN */ \
|
||||
{PIN('G', 13), 10}, /* TXD0 */ \
|
||||
{PIN('G', 12), 10} /* TXD1 */
|
||||
|
||||
// System clock (9.4.2, 9.4.8). HSI runs at 144 MHz, the maximum system clock:
|
||||
// SYSCLK = HSIS, no PLL. hclk = SYSCLK, APB clocks = hclk. After reset, the
|
||||
// system runs on HSIDIV3, 48 MHz. Flash latency for 144 MHz: 6.3.6, Table 20
|
||||
#define SYS_FREQUENCY 144000000
|
||||
#define AHB_FREQUENCY SYS_FREQUENCY
|
||||
#define APB2_FREQUENCY AHB_FREQUENCY
|
||||
#define APB1_FREQUENCY AHB_FREQUENCY
|
||||
#define HAL_FLASH_LATENCY (4 | FLASH_ACR_WRHIGHFREQ_1) // 4 WS, WRHIGHFREQ 2
|
||||
|
||||
enum { HAL_GPIO_MODE_INPUT, HAL_GPIO_MODE_OUTPUT, HAL_GPIO_MODE_AF, HAL_GPIO_MODE_ANALOG };
|
||||
enum { HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_OTYPE_OPEN_DRAIN };
|
||||
enum { HAL_GPIO_SPEED_LOW, HAL_GPIO_SPEED_MEDIUM, HAL_GPIO_SPEED_HIGH, HAL_GPIO_SPEED_INSANE };
|
||||
enum { HAL_GPIO_PULL_NONE, HAL_GPIO_PULL_UP, HAL_GPIO_PULL_DOWN };
|
||||
|
||||
#define HAL_GPIO(N) ((GPIO_TypeDef *) (GPIOA_BASE + 0x400 * (N)))
|
||||
|
||||
static GPIO_TypeDef *hal_gpio_bank(uint16_t pin) {
|
||||
return HAL_GPIO(PINBANK(pin));
|
||||
}
|
||||
static inline void hal_gpio_toggle(uint16_t pin) {
|
||||
GPIO_TypeDef *gpio = hal_gpio_bank(pin);
|
||||
uint32_t mask = BIT(PINNO(pin));
|
||||
gpio->BSRR = mask << (gpio->ODR & mask ? 16 : 0);
|
||||
}
|
||||
static inline int hal_gpio_read(uint16_t pin) {
|
||||
return hal_gpio_bank(pin)->IDR & BIT(PINNO(pin)) ? 1 : 0;
|
||||
}
|
||||
static inline void hal_gpio_write(uint16_t pin, bool val) {
|
||||
GPIO_TypeDef *gpio = hal_gpio_bank(pin);
|
||||
gpio->BSRR = BIT(PINNO(pin)) << (val ? 0 : 16);
|
||||
}
|
||||
static inline void hal_gpio_init(uint16_t pin, uint8_t mode, uint8_t type,
|
||||
uint8_t speed, uint8_t pull, uint8_t af) {
|
||||
GPIO_TypeDef *gpio = hal_gpio_bank(pin);
|
||||
uint8_t n = (uint8_t) (PINNO(pin));
|
||||
RCC->AHB2ENR |= BIT(PINBANK(pin)); // Enable GPIO clock
|
||||
CLRSET(gpio->OTYPER, 1UL << n, ((uint32_t) type) << n);
|
||||
CLRSET(gpio->OSPEEDR, 3UL << (n * 2), ((uint32_t) speed) << (n * 2));
|
||||
CLRSET(gpio->PUPDR, 3UL << (n * 2), ((uint32_t) pull) << (n * 2));
|
||||
CLRSET(gpio->AFR[n >> 3], 15UL << ((n & 7) * 4),
|
||||
((uint32_t) af) << ((n & 7) * 4));
|
||||
CLRSET(gpio->MODER, 3UL << (n * 2), ((uint32_t) mode) << (n * 2));
|
||||
}
|
||||
static inline void hal_gpio_input(uint16_t pin) {
|
||||
hal_gpio_init(pin, HAL_GPIO_MODE_INPUT, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH,
|
||||
HAL_GPIO_PULL_NONE, 0);
|
||||
}
|
||||
static inline void hal_gpio_output(uint16_t pin) {
|
||||
hal_gpio_init(pin, HAL_GPIO_MODE_OUTPUT, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH,
|
||||
HAL_GPIO_PULL_NONE, 0);
|
||||
}
|
||||
|
||||
// USART1 kernel clock defaults to pclk2 (APB2), USART2,3 default to pclk1
|
||||
// (APB1), see RCC_CCIPR1
|
||||
static inline void hal_uart_init(USART_TypeDef *uart, uint16_t tx,
|
||||
uint16_t rx, unsigned long baud) {
|
||||
uint8_t af = 7; // Alternate function
|
||||
uint32_t freq = 0; // Bus frequency. UART1 is on APB2, rest on APB1
|
||||
|
||||
if (uart == USART1) freq = APB2_FREQUENCY, RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
|
||||
if (uart == USART2) freq = APB1_FREQUENCY, RCC->APB1LENR |= RCC_APB1LENR_USART2EN;
|
||||
if (uart == USART3) freq = APB1_FREQUENCY, RCC->APB1LENR |= RCC_APB1LENR_USART3EN;
|
||||
|
||||
hal_gpio_init(tx, HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH, 0, af);
|
||||
hal_gpio_init(rx, HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_HIGH, 0, af);
|
||||
uart->CR1 = 0; // Disable this UART
|
||||
uart->BRR = freq / baud; // Set baud rate
|
||||
uart->CR1 = BIT(0) | BIT(2) | BIT(3); // Set UE, RE, TE
|
||||
}
|
||||
static inline void hal_uart_write_byte(USART_TypeDef *uart, uint8_t byte) {
|
||||
uart->TDR = byte;
|
||||
while ((uart->ISR & USART_ISR_TXE_TXFNF) == 0) (void) 0;
|
||||
}
|
||||
static inline void hal_uart_write_buf(USART_TypeDef *uart, char *buf, size_t len) {
|
||||
while (len-- > 0) hal_uart_write_byte(uart, *(uint8_t *) buf++);
|
||||
}
|
||||
static inline int hal_uart_read_ready(USART_TypeDef *uart) {
|
||||
return uart->ISR & BIT(5); // If RXNE bit is set, data is ready
|
||||
}
|
||||
static inline uint8_t hal_uart_read_byte(USART_TypeDef *uart) {
|
||||
return (uint8_t) (uart->RDR & 255);
|
||||
}
|
||||
|
||||
// RNG clock (rng_clk) is CK48, which is disabled after reset. Use HSIDIV3,
|
||||
// 48 MHz. It stays enabled after the system clock switches to HSIS
|
||||
static inline void hal_rng_init(void) {
|
||||
CLRSET(RCC->CCIPR2, RCC_CCIPR2_CK48SEL_Msk, RCC_CCIPR2_CK48SEL_1);
|
||||
RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN; // Enable RNG AHB clock
|
||||
RNG->CR |= RNG_CR_RNGEN; // Enable RNG
|
||||
}
|
||||
|
||||
static inline uint32_t hal_rng_read(void) {
|
||||
while ((RNG->SR & RNG_SR_DRDY) == 0) (void) 0;
|
||||
return RNG->DR;
|
||||
}
|
||||
|
||||
static inline void hal_ethernet_init(void) {
|
||||
// Initialise Ethernet. Enable MAC GPIO pins, see UM3616 Table 9
|
||||
uint16_t pins[][2] = {HAL_ETH_PINS};
|
||||
for (size_t i = 0; i < sizeof(pins) / sizeof(pins[0]); i++) {
|
||||
hal_gpio_init(pins[i][0], HAL_GPIO_MODE_AF, HAL_GPIO_OTYPE_PUSH_PULL, HAL_GPIO_SPEED_INSANE,
|
||||
HAL_GPIO_PULL_NONE, (uint8_t) pins[i][1]);
|
||||
}
|
||||
NVIC_EnableIRQ(ETH1_IRQn); // Setup Ethernet IRQ handler
|
||||
RCC->APB3ENR |= RCC_APB3ENR_SBSEN; // Enable SBS clock
|
||||
CLRSET(SBS->PMCR, SBS_PMCR_ETH1_SEL_PHY_Msk, SBS_PMCR_ETH1_SEL_PHY_2); // RMII
|
||||
RCC->AHB1ENR |= RCC_AHB1ENR_ETH1CKEN | RCC_AHB1ENR_ETH1EN |
|
||||
RCC_AHB1ENR_ETH1TXEN | RCC_AHB1ENR_ETH1RXEN;
|
||||
}
|
||||
|
||||
static inline void hal_clock_init(void) {
|
||||
CLRSET(FLASH->ACR, FLASH_ACR_LATENCY_Msk | FLASH_ACR_WRHIGHFREQ_Msk,
|
||||
HAL_FLASH_LATENCY);
|
||||
while ((FLASH->ACR & FLASH_ACR_LATENCY_Msk) != (HAL_FLASH_LATENCY & 15)) (void) 0;
|
||||
RCC->CR1 |= RCC_CR1_HSISON; // Enable HSIS, 144 MHz
|
||||
while ((RCC->CR1 & RCC_CR1_HSISRDY) == 0) (void) 0; // Wait until ready
|
||||
CLRSET(RCC->CFGR1, RCC_CFGR1_SW_Msk, RCC_CFGR1_SW_0); // SYSCLK = HSIS
|
||||
while ((RCC->CFGR1 & RCC_CFGR1_SWS_Msk) != RCC_CFGR1_SWS_0) (void) 0;
|
||||
SystemCoreClock = SYS_FREQUENCY; // Update SystemCoreClock global var
|
||||
SysTick_Config(SystemCoreClock / 1000); // Sys tick every 1ms
|
||||
}
|
||||
|
||||
static inline void hal_system_init(void) {
|
||||
SCB->CPACR |= ((3UL << 10 * 2) | (3UL << 11 * 2)); // Enable FPU
|
||||
__DSB();
|
||||
__ISB();
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
ENTRY(Reset_Handler);
|
||||
MEMORY {
|
||||
flash(rx) : ORIGIN = 0x08000000, LENGTH = 1024k
|
||||
sram(rwx) : ORIGIN = 0x20000000, LENGTH = 256k - 1k
|
||||
health(rw) : ORIGIN = 0x2003fc00, LENGTH = 1k /* Retained health record */
|
||||
}
|
||||
__StackTop = ORIGIN(sram) + LENGTH(sram); /* End of RAM. stack points here */
|
||||
_estack = __StackTop; /* Used by the health fault handler */
|
||||
__StackLimit = __StackTop - 16384; /* Stack limit, MSPLIM. TLS needs > 4 KB */
|
||||
|
||||
SECTIONS {
|
||||
.vectors : { KEEP(*(.vectors)) } > flash
|
||||
.text : { *(.text* .text.*) } > flash
|
||||
.rodata : { *(.rodata*) } > flash
|
||||
|
||||
/* Copy and zero tables for the CMSIS startup code, see __cmsis_start() */
|
||||
.copy.table : {
|
||||
__copy_table_start__ = .;
|
||||
LONG(LOADADDR(.data)) LONG(ADDR(.data)) LONG(SIZEOF(.data) / 4)
|
||||
__copy_table_end__ = .;
|
||||
} > flash
|
||||
.zero.table : {
|
||||
__zero_table_start__ = .;
|
||||
LONG(ADDR(.bss)) LONG(SIZEOF(.bss) / 4)
|
||||
__zero_table_end__ = .;
|
||||
} > flash
|
||||
|
||||
/* Note the .iram section, for functions copied to RAM. Required for MG_IRAM OTA support */
|
||||
.data : { _sdata = .; *(.first_data) *(.iram .iram* .iram.*) *(.data SORT(.data.*)) . = ALIGN(4); _edata = .; } > sram AT > flash
|
||||
_sidata = LOADADDR(.data);
|
||||
|
||||
.bss : { _sbss = .; *(.bss SORT(.bss.*) COMMON) . = ALIGN(4); _ebss = .; } > sram
|
||||
_end = ALIGN(_ebss, 8); /* Heap starts after .bss */
|
||||
|
||||
/* Retained health record. Startup code must neither copy nor clear it. */
|
||||
.mg_health (NOLOAD) : { KEEP(*(.mg_health .mg_health*)) } > health
|
||||
}
|
||||
+22
-75
@@ -1,16 +1,16 @@
|
||||
// Copyright (c) 2026 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include "mongoose.h"
|
||||
#include "hal.h"
|
||||
#include "mongoose.h"
|
||||
|
||||
#ifndef UART_DEBUG
|
||||
#define UART_DEBUG USART3
|
||||
#endif
|
||||
#define UART_DEBUG USART2 // ST-LINK virtual COM port, UM3616 7.12
|
||||
#define UART_DEBUG_TX_PIN PIN('A', 2)
|
||||
#define UART_DEBUG_RX_PIN PIN('A', 3)
|
||||
|
||||
#define LED1 PIN(0, 12) // Green
|
||||
#define LED2 PIN(0, 0) // Blue
|
||||
#define LED3 PIN(0, 1) // Red
|
||||
#define LED1 PIN('A', 5) // Green, on when high. UM3616 7.8
|
||||
#define LED2 PIN('G', 1) // Red, on when low
|
||||
#define LED3 PIN('G', 2) // Blue, on when low
|
||||
|
||||
static void log_fn(char ch, void *param) {
|
||||
hal_uart_write_buf(param, &ch, 1);
|
||||
@@ -18,8 +18,8 @@ static void log_fn(char ch, void *param) {
|
||||
|
||||
static void blink_task(void) {
|
||||
static uint64_t blink_timer = 0;
|
||||
if (hal_timer_expired(&blink_timer, 100, hal_get_tick())) {
|
||||
// hal_gpio_toggle(LED1);
|
||||
if (hal_timer_expired(&blink_timer, 500, hal_get_tick())) {
|
||||
hal_gpio_toggle(LED2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ uint64_t mg_millis(void) {
|
||||
return hal_get_tick();
|
||||
}
|
||||
|
||||
#if MG_ENABLE_CUSTOM_RANDOM
|
||||
bool mg_random(void *buf, size_t len) {
|
||||
for (size_t n = 0; n < len; n += sizeof(uint32_t)) {
|
||||
uint32_t r = hal_rng_read();
|
||||
@@ -35,7 +34,6 @@ bool mg_random(void *buf, size_t len) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Self-signed P-256 certificate and key for the HTTPS listener. Created by:
|
||||
// openssl ecparam -genkey -name prime256v1 -noout -out key.pem
|
||||
@@ -65,7 +63,7 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
struct mg_tls_opts opts = {.cert = mg_str(TLS_CERT),
|
||||
.key = mg_str(TLS_KEY)};
|
||||
mg_tls_init(c, &opts);
|
||||
} else if (ev == MG_EV_HTTP_MSG) { // New HTTP request received
|
||||
} else if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
if (mg_match(hm->uri, mg_str("/api/tick"), NULL)) {
|
||||
mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick());
|
||||
@@ -82,80 +80,29 @@ static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
}
|
||||
}
|
||||
|
||||
// Our address on the radio side, and the /64 prefix we advertise there.
|
||||
// fd12:3456:789a:1::1, a unique local address, RFC 4193
|
||||
#define RADIO_IP6 \
|
||||
"\xfd\x12\x34\x56\x78\x9a\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01"
|
||||
|
||||
// NAT64 prefix advertised to radio nodes: 64:ff9b::/96, RFC 6052. Nodes reach
|
||||
// IPv4 hosts as 64:ff9b::a.b.c.d, we translate to IPv4 on Ethernet
|
||||
#define NAT64_PREFIX "\x00\x64\xff\x9b\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
|
||||
int main(void) {
|
||||
MG_HEALTH_INIT();
|
||||
|
||||
hal_clock_init();
|
||||
hal_rng_init();
|
||||
hal_gpio_output(LED1);
|
||||
hal_gpio_write(LED1, true);
|
||||
hal_gpio_output(LED2);
|
||||
hal_gpio_write(LED2, true);
|
||||
hal_gpio_output(LED3);
|
||||
hal_gpio_write(LED3, true);
|
||||
hal_uart_init(UART_DEBUG, 115200);
|
||||
hal_uart_init(UART_DEBUG, UART_DEBUG_TX_PIN, UART_DEBUG_RX_PIN, 115200);
|
||||
mg_log_set_fn(log_fn, UART_DEBUG);
|
||||
|
||||
if (hal_radio_err) {
|
||||
MG_ERROR(("Radio firmware failed to start, step %d", hal_radio_err));
|
||||
}
|
||||
|
||||
// Two interfaces, each on its own manager: Ethernet and 802.15.4
|
||||
static struct mg_tcpip_driver_rw612_data eth_data = {
|
||||
.mdc_cr = MG_DRIVER_MDC_CR, .phy_addr = MG_TCPIP_PHY_ADDR};
|
||||
static struct mg_tcpip_if eth = {.driver = &mg_tcpip_driver_rw612,
|
||||
.driver_data = ð_data};
|
||||
static struct mg_tcpip_if radio = {.driver = &mg_tcpip_driver_rw612_154,
|
||||
.l2type = MG_TCPIP_L2_LOWPAN};
|
||||
struct mg_mgr mgr_eth, mgr_radio;
|
||||
uint8_t uid[FSL_OCOTP_UID_LENGTH], digest[32];
|
||||
uint32_t uid_len = sizeof(uid);
|
||||
|
||||
// Stable addresses, made from the chip unique ID: neighbours remember
|
||||
// them, e.g. radio nodes keep us as their default router
|
||||
memset(uid, 0, sizeof(uid));
|
||||
OCOTP_ReadUniqueID(uid, &uid_len);
|
||||
mg_sha256(digest, uid, sizeof(uid));
|
||||
memcpy(eth.mac, digest, 6);
|
||||
memcpy(radio.mac, digest + 6, 8);
|
||||
eth.mac[0] = radio.mac[0] = 0x02; // Locally administered, unicast
|
||||
hal_rng_init();
|
||||
hal_ethernet_init();
|
||||
mg_mgr_init(&mgr_eth);
|
||||
mg_tcpip_init(&mgr_eth, ð);
|
||||
radio.l2data.lowpan.channel = 15;
|
||||
radio.l2data.lowpan.pan_id = 0xabcd;
|
||||
memcpy(radio.ip6, RADIO_IP6, sizeof(radio.ip6)); // Static: we are router
|
||||
memcpy(radio.prefix, RADIO_IP6, sizeof(radio.prefix));
|
||||
radio.prefix_len = 64;
|
||||
memcpy(radio.nat64, NAT64_PREFIX, sizeof(radio.nat64)); // We do NAT64
|
||||
mg_mgr_init(&mgr_radio);
|
||||
mg_tcpip_init(&mgr_radio, &radio);
|
||||
mgr_radio.use_dns6 = true; // IPv6 only: no IPv4 over 6LoWPAN
|
||||
hal_gpio_output(LED1);
|
||||
hal_gpio_output(LED2);
|
||||
hal_gpio_output(LED3);
|
||||
|
||||
// Route IPv6 between the two: radio nodes get addresses from RADIO_IP6/64,
|
||||
// the Ethernet LAN gets a route to that prefix via us
|
||||
mg_tcpip_route(ð, &radio);
|
||||
|
||||
// Minimal web server on both interfaces
|
||||
mg_http_listen(&mgr_eth, "http://0.0.0.0:80", http_ev_handler, NULL);
|
||||
mg_http_listen(&mgr_eth, "https://0.0.0.0:443", http_ev_handler, NULL);
|
||||
mg_http_listen(&mgr_radio, "http://[::]:80", http_ev_handler, NULL);
|
||||
mg_http_listen(&mgr_radio, "https://[::]:443", http_ev_handler, NULL);
|
||||
struct mg_mgr mgr;
|
||||
mg_mgr_init(&mgr);
|
||||
mg_http_listen(&mgr, "http://0.0.0.0", http_ev_handler, NULL);
|
||||
mg_http_listen(&mgr, "https://0.0.0.0", http_ev_handler, NULL);
|
||||
|
||||
MG_INFO(("Initialised. CPU clock: %lu MHz", SystemCoreClock / 1000000));
|
||||
MG_INFO(("RAM used: %zu, free: %zu", hal_ram_used(), hal_ram_free()));
|
||||
MG_INFO(("Crash record: %M", mg_print_crash_record));
|
||||
|
||||
for (;;) {
|
||||
mg_mgr_poll(&mgr_eth, 0);
|
||||
mg_mgr_poll(&mgr_radio, 0);
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
blink_task();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include "hal.h"
|
||||
|
||||
// See https://mongoose.ws/docs/getting-started/build-environment/
|
||||
#define MG_ARCH MG_ARCH_ARMGCC
|
||||
#define MG_TLS MG_TLS_BUILTIN
|
||||
#define MG_OTA MG_OTA_STM32C5
|
||||
|
||||
#define MG_ENABLE_TCPIP 1
|
||||
#define MG_ENABLE_CUSTOM_MILLIS 1
|
||||
#define MG_ENABLE_CUSTOM_RANDOM 1
|
||||
#define MG_ENABLE_DRIVER_STM32C 1
|
||||
|
||||
// Construct MAC address from the MCU unique ID
|
||||
#define MGUID ((uint32_t *) UID_BASE)
|
||||
#define MG_SET_MAC_ADDRESS(mac) \
|
||||
do { \
|
||||
mac[0] = 2; \
|
||||
mac[1] = MGUID[0] & 255; \
|
||||
mac[2] = (MGUID[0] >> 10) & 255; \
|
||||
mac[3] = (MGUID[0] >> 19) & 255; \
|
||||
mac[4] = MGUID[1] & 255; \
|
||||
mac[5] = MGUID[2] & 255; \
|
||||
} while (0)
|
||||
|
||||
// Crash report support. Health record lives in the .mg_health region, see link.ld
|
||||
#define MG_ENABLE_HEALTH 1
|
||||
|
||||
// mdash.net device management service support
|
||||
#define MG_ENABLE_MDASH 0
|
||||
#define MG_MDASH_KEY "..." // Set this - mDash device token
|
||||
#define MG_FIRMWARE_VERSION "c5a3.1.0.0"
|
||||
Reference in new issue
Block a user