Add radio simulation

This commit is contained in:
cpq committed 2026-11-05 22:23:06 +00:00
1 parent 9337a78cd0
commit d39519b92f
66 files changed
+5659 -1000

No files matched your search

+171 -77
View File
@@ -1,3 +1,5 @@
#include "l2_mesh.h"
#include "log.h"
#include "net_builtin.h"
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_MCXW71) && \
@@ -9,12 +11,12 @@ struct mg_zll {
volatile uint32_t IRQSTS, PHY_CTRL, EVENT_TMR, TIMESTAMP, T1CMP, T2CMP,
T2PRIMECMP, T3CMP, T4CMP, PA_PWR, CHANNEL_NUM0, LQI_AND_RSSI,
MACSHORTADDRS0, MACLONGADDRS0_LSB, MACLONGADDRS0_MSB, RX_FRAME_FILTER,
CCA_LQI_CTRL, CCA2_CTRL, RESERVED_0, DSM_CTRL, RESERVED_1,
MACSHORTADDRS1, MACLONGADDRS1_LSB, MACLONGADDRS1_MSB, DUAL_PAN_CTRL,
CHANNEL_NUM1, SAM_CTRL, SAM_TABLE, SAM_MATCH, SAM_FREE_IDX,
SEQ_CTRL_STS, ACKDELAY, FILTERFAIL_CODE, RX_WTR_MARK, RESERVED_2,
SLOT_PRELOAD, SEQ_STATE, TMR_PRESCALE, LENIENCY_LSB, LENIENCY_MSB,
PART_ID, COEX_CTRL, COEX_PRIORITY, ENHACK_CTRL0;
CCA_LQI_CTRL, CCA2_CTRL, RESERVED_0, DSM_CTRL, RESERVED_1, MACSHORTADDRS1,
MACLONGADDRS1_LSB, MACLONGADDRS1_MSB, DUAL_PAN_CTRL, CHANNEL_NUM1,
SAM_CTRL, SAM_TABLE, SAM_MATCH, SAM_FREE_IDX, SEQ_CTRL_STS, ACKDELAY,
FILTERFAIL_CODE, RX_WTR_MARK, RESERVED_2, SLOT_PRELOAD, SEQ_STATE,
TMR_PRESCALE, LENIENCY_LSB, LENIENCY_MSB, PART_ID, COEX_CTRL,
COEX_PRIORITY, ENHACK_CTRL0;
};
// TrustZone: code built for the secure state (-mcmse) uses the secure alias
@@ -32,111 +34,203 @@ struct mg_zll {
#define MGI_IRQSTS_SEQIRQ MG_BIT(0)
#define MGI_IRQSTS_RXIRQ MG_BIT(2)
#define MGI_IRQSTS_FILTERFAIL MG_BIT(5) // Frame not for us
#define MGI_IRQSTS_CRCVALID MG_BIT(15)
#define MGI_IRQSTS_RX_LEN(x) (((x) >> 24) & 0x7f) // Frame length incl. FCS
#define MGI_PHY_CTRL_XCVSEQ 7U
#define MGI_PHY_CTRL_ALL_IRQ_MASKS 0x8007ff00U // Bits 8..18, 31: mask all IRQs
#define MGI_PHY_CTRL_SEQMSK MG_BIT(8) // Mask the sequence end IRQ
#define MGI_PHY_CTRL_TRCV_MSK MG_BIT(31) // Mask all transceiver IRQs
#define MGI_IRQN 53 // RF_802_15_4_IRQn
#define MGI_NVIC_ISER ((volatile uint32_t *) 0xE000E100) // Enable IRQ
#define MGI_NVIC_ICER ((volatile uint32_t *) 0xE000E180) // Disable IRQ
#define MGI_SEQ_STATE_MASK 0x1fU
#define MGI_SEQ_STATE_RX_WU 0x10U // RX warm up
#define MGI_SEQ_STATE_RX_WU 0x10U // RX warm up
#define MGI_SEQ_CTRL_NO_RX_RECYCLE MG_BIT(5)
#define MGI_FRAME_FILTER 0x70bU // Beacon, data, command; versions 0..2
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
// PHY_CTRL[XCVSEQ]
enum { MGI_SEQ_IDLE = 0, MGI_SEQ_RX = 1, MGI_SEQ_TX = 2 };
#define MGI_SEND_TIMEOUT_MS 60
// Symbol timer, 16 us, 24 bits
static uint32_t mg_mcxw71_symbols(void) {
return MGI_ZLL->EVENT_TMR >> 8;
}
// Wait for the sequence to end, up to MGI_TX_TIMEOUT symbols
static bool mg_mcxw71_wait(void) {
uint32_t start = mg_mcxw71_symbols();
while (((mg_mcxw71_symbols() - start) & 0xffffffU) < MGI_TX_TIMEOUT) {
if (MGI_ZLL->IRQSTS & MGI_IRQSTS_SEQIRQ) return true;
}
return false;
}
static struct mg_tcpip_if *s_mcxw71_ifp;
static struct mg_queue s_mcxw71_rx;
static char s_mcxw71_queue[2048];
static volatile bool s_mcxw71_txing, s_mcxw71_done;
static uint64_t s_mcxw71_sendstart;
// Stop the current sequence, wait for the sequencer to go idle. See PhyAbort()
static void mg_mcxw71_idle(void) {
static void mg_mcxw71_abort(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;
MGI_ZLL->PHY_CTRL &= ~MGI_PHY_CTRL_XCVSEQ;
while (MGI_ZLL->SEQ_STATE & MGI_SEQ_STATE_MASK) (void) 0;
}
MGI_ZLL->IRQSTS = MGI_ZLL->IRQSTS; // Clear all flags, write 1 to clear
}
// Start a new sequence: MGI_SEQ_RX or MGI_SEQ_TX
static void mg_mcxw71_start(uint32_t seq) {
MGI_ZLL->IRQSTS = MGI_ZLL->IRQSTS;
MGI_ZLL->PHY_CTRL |= seq;
}
static bool mg_mcxw71_init(struct mg_tcpip_if *ifp) {
struct mg_l2_802154_data *d =
(struct mg_l2_802154_data *) ifp->driver_data;
if (d == NULL || d->channel < 11 || d->channel > 26) {
MG_ERROR(("Set driver_data, channel 11..26"));
return false;
// Start receiving, with the sequence end IRQ on, see RF_802_15_4_IRQHandler()
static void mg_mcxw71_listen(void) {
MGI_ZLL->IRQSTS = MGI_ZLL->IRQSTS;
MGI_ZLL->PHY_CTRL |= MGI_SEQ_RX;
MGI_ZLL->PHY_CTRL &= ~(MGI_PHY_CTRL_SEQMSK | MGI_PHY_CTRL_TRCV_MSK);
MGI_NVIC_ISER[MGI_IRQN / 32] = MG_BIT(MGI_IRQN % 32);
}
// Keep the IRQ handler out while we change the radio. The NVIC disable is
// atomic, so PHY_CTRL updates here and there do not race
static void mg_mcxw71_lock(void) {
MGI_NVIC_ICER[MGI_IRQN / 32] = MG_BIT(MGI_IRQN % 32);
__asm volatile("dsb; isb" ::: "memory");
}
// Start the TX sequence for the loaded frame. Its end: see the IRQ handler
static void mg_mcxw71_go(void) {
MGI_ZLL->PHY_CTRL |= MGI_PHY_CTRL_TRCV_MSK | MGI_PHY_CTRL_SEQMSK;
mg_mcxw71_abort();
MGI_ZLL->IRQSTS = MGI_ZLL->IRQSTS;
MGI_ZLL->PHY_CTRL |= MGI_SEQ_TX;
MGI_ZLL->PHY_CTRL &= ~(MGI_PHY_CTRL_SEQMSK | MGI_PHY_CTRL_TRCV_MSK);
}
void RF_802_15_4_IRQHandler(void);
void RF_802_15_4_IRQHandler(void) {
uint32_t irqsts = MGI_ZLL->IRQSTS;
if ((irqsts & MGI_IRQSTS_SEQIRQ) != 0) {
mg_mcxw71_abort();
if (s_mcxw71_txing) {
s_mcxw71_txing = false;
s_mcxw71_done = true;
} else if ((irqsts & (MGI_IRQSTS_RXIRQ | MGI_IRQSTS_FILTERFAIL |
MGI_IRQSTS_CRCVALID)) ==
(MGI_IRQSTS_RXIRQ | MGI_IRQSTS_CRCVALID)) {
uint8_t frame[125];
size_t i, n = MGI_IRQSTS_RX_LEN(irqsts);
n = n >= 2 ? n - 2 : 0;
if (n > 0 && n <= sizeof(frame)) {
for (i = 0; i < n; i++) frame[i] = MGI_RX_PB[i];
{
char *dst;
if (mg_queue_book(&s_mcxw71_rx, &dst, n) >= n) {
memcpy(dst, frame, n);
mg_queue_add(&s_mcxw71_rx, n);
} else {
s_mcxw71_ifp->ndrop++;
}
}
}
}
mg_mcxw71_listen();
}
}
static size_t mgi_mcxw71_radio_tx(struct mg_tcpip_if *ifp, const void *buf,
size_t len) {
size_t i;
(void) ifp;
if (len > 125 || s_mcxw71_txing) return 0;
mg_mcxw71_lock();
MGI_TX_PB[0] = (uint8_t) (len + 2); // FCS is added by hardware
for (i = 0; i < len; i++)
MGI_TX_PB[i + 1] = ((const uint8_t *) buf)[i]; // 8-bit access only
s_mcxw71_done = false;
s_mcxw71_txing = true;
s_mcxw71_sendstart = mg_millis();
mg_mcxw71_go();
MGI_NVIC_ISER[MGI_IRQN / 32] = MG_BIT(MGI_IRQN % 32);
return len;
}
static bool mgi_mcxw71_radio_init(struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_mcxw71_data *data = ifp->driver_data;
struct mg_mesh_node *node = data->mesh;
s_mcxw71_ifp = ifp;
MGI_ZLL->SEQ_CTRL_STS |= MGI_SEQ_CTRL_NO_RX_RECYCLE;
MGI_ZLL->PHY_CTRL = MGI_PHY_CTRL_ALL_IRQ_MASKS; // Mask all IRQs, we poll
MGI_ZLL->PHY_CTRL = MGI_PHY_CTRL_ALL_IRQ_MASKS;
MGI_ZLL->IRQSTS = MGI_ZLL->IRQSTS;
MGI_ZLL->SAM_CTRL = 0;
MGI_ZLL->RX_FRAME_FILTER = MGI_FRAME_FILTER;
MGI_ZLL->TMR_PRESCALE = 5; // 16 us timebase, one symbol
MGI_ZLL->CHANNEL_NUM0 = d->channel;
MGI_ZLL->MACSHORTADDRS0 = (0xfffeU << 16) | d->pan_id; // Filter by PAN ID
MGI_ZLL->PA_PWR = 0x3f; // PA slices, max output power
mg_mcxw71_start(MGI_SEQ_RX);
MGI_ZLL->CHANNEL_NUM0 = MG_802154_CHANNEL;
MGI_ZLL->MACSHORTADDRS0 =
((uint32_t) node->radio_address << 16) | MG_802154_PAN_ID;
MGI_ZLL->PA_PWR = 0x3f;
mg_mcxw71_listen();
return true;
}
// Send one radio frame: MAC header and payload, no FCS
static size_t mg_mcxw71_send(const uint8_t *p, size_t len,
struct mg_tcpip_if *ifp) {
size_t i;
bool ok;
mg_mcxw71_idle();
MGI_TX_PB[0] = (uint8_t) (len + 2); // FCS is added by hardware
for (i = 0; i < len; i++) MGI_TX_PB[i + 1] = p[i]; // 8-bit access only
mg_mcxw71_start(MGI_SEQ_TX);
ok = mg_mcxw71_wait();
mg_mcxw71_idle();
mg_mcxw71_start(MGI_SEQ_RX);
(void) ifp;
return ok ? len : 0;
static size_t mgi_mcxw71_tx(const void *buf, size_t len,
struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_mcxw71_data *data = ifp->driver_data;
return data->mesh->strategy->tx(data->mesh, buf, len);
}
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 mgi_mcxw71_rx(void *buf, size_t cap, struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_mcxw71_data *data = ifp->driver_data;
size_t len = data->mesh->rx_len;
if (len > cap) return 0;
if (len != 0 && buf != ifp->recv_queue.buf)
memcpy(buf, ifp->recv_queue.buf, len);
data->mesh->rx_len = 0;
return len;
}
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;
if ((irqsts & MGI_IRQSTS_SEQIRQ) == 0) return 0; // Still receiving
if (irqsts & MGI_IRQSTS_RXIRQ) {
n = MGI_IRQSTS_RX_LEN(irqsts);
n = n >= 2 ? n - 2 : 0; // Strip FCS
if (n > sizeof(f)) n = sizeof(f);
for (i = 0; i < n; i++) f[i] = MGI_RX_PB[i];
static bool mgi_mcxw71_poll(struct mg_tcpip_if *ifp, bool s1) {
struct mg_tcpip_driver_mcxw71_data *data = ifp->driver_data;
struct mg_mesh_node *node = data->mesh;
uint64_t now = mg_micros();
char *buf;
size_t len;
(void) s1;
if (s_mcxw71_txing &&
mg_millis() > s_mcxw71_sendstart + MGI_SEND_TIMEOUT_MS) {
mg_mcxw71_lock();
if (s_mcxw71_txing) {
mg_mcxw71_abort();
s_mcxw71_txing = false;
s_mcxw71_done = true;
mg_mcxw71_listen();
} else {
MGI_NVIC_ISER[MGI_IRQN / 32] = MG_BIT(MGI_IRQN % 32);
}
}
mg_mcxw71_idle();
mg_mcxw71_start(MGI_SEQ_RX);
return mg_l2_802154_rx(ifp, f, n, buf, len);
if (s_mcxw71_done) {
s_mcxw71_done = false;
node->tx_at = now + node->strategy->radio->ifs_us;
}
node->now = now;
while (node->rx_len == 0 && (len = mg_queue_next(&s_mcxw71_rx, &buf)) != 0) {
node->strategy->rx(node, buf, len);
mg_queue_del(&s_mcxw71_rx, len);
}
node->strategy->tick(node, now);
return node->joined;
}
static bool mg_mcxw71_poll(struct mg_tcpip_if *ifp, bool s1) {
(void) ifp, (void) s1;
return true; // Radio link is always up
static bool mgi_mcxw71_init(struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_mcxw71_data *data = ifp->driver_data;
const struct mg_mesh *strategy = data->strategy;
uint16_t address;
address =
(uint16_t) (2 + ((unsigned) ifp->mac[4] * 256 + ifp->mac[5]) % 65532u);
if (strategy == NULL) strategy = &mg_mesh_default;
data->mesh = calloc(1, sizeof(*data->mesh));
if (data->mesh == NULL) return false;
if (!strategy->init(data->mesh, ifp, NULL, address)) {
free(data->mesh);
data->mesh = NULL;
return false;
}
data->mesh->strategy = strategy;
data->mesh->tx = mgi_mcxw71_radio_tx;
mg_queue_init(&s_mcxw71_rx, s_mcxw71_queue, sizeof(s_mcxw71_queue));
if (mgi_mcxw71_radio_init(ifp)) return true;
strategy->free(data->mesh);
free(data->mesh);
data->mesh = NULL;
return false;
}
struct mg_tcpip_driver mg_tcpip_driver_mcxw71 = {mg_mcxw71_init, mg_mcxw71_tx,
mg_mcxw71_rx, mg_mcxw71_poll};
struct mg_tcpip_driver mg_tcpip_driver_mcxw71 = {
mgi_mcxw71_init, mgi_mcxw71_tx, mgi_mcxw71_rx, mgi_mcxw71_poll};
#endif
+26 -16
View File
@@ -1,25 +1,35 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
#pragma once
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_MCXW71) && \
MG_ENABLE_DRIVER_MCXW71
// NXP MCXW71 IEEE 802.15.4 radio, ZLL link layer, as an Ethernet interface:
// Ethernet frames are split into radio frames, see mg_l2_802154_tx().
// The transceiver (XCVR) must be set up for 802.15.4 before mg_tcpip_init(),
// see hal_radio_init() in the FRDM-MCXW71 tutorial.
// driver_data: struct mg_l2_802154_data, channel and PAN ID set
struct mg_tcpip_driver_mcxw71_data {
struct mg_mesh_node *mesh; // Driver-owned runtime state
const struct mg_mesh *strategy; // NULL selects mg_mesh_default
};
#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)); \
extern struct mg_tcpip_driver mg_tcpip_driver_mcxw71;
#define MG_TCPIP_DRIVER_MCXW71_INIT(manager) \
do { \
static struct mg_tcpip_driver_mcxw71_data driver_data_; \
static struct mg_tcpip_if mif_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.mgr = (manager); \
mif_.driver = &mg_tcpip_driver_mcxw71; \
mif_.driver_data = &driver_data_; \
mg_tcpip_init((manager), &mif_); \
MG_INFO(("Driver: mcxw71, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#ifndef MG_TCPIP_DRIVER_INIT
#define MG_TCPIP_DRIVER_INIT(x) MG_TCPIP_DRIVER_MCXW71_INIT(x)
#endif
#endif
+183
View File
@@ -0,0 +1,183 @@
#include "net_builtin.h"
#include "l2_mesh.h"
#include "log.h"
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_MG24) && MG_ENABLE_DRIVER_MG24
#include <rail.h>
#include <rail_ieee802154.h>
// RAIL delivers raw frames and transmit completion from interrupt context.
#define MG_MG24_TX_TIMEOUT_US 60000
static struct mg_tcpip_if *s_mg24_ifp;
static RAIL_Handle_t s_mg24_rail;
static struct mg_queue s_mg24_rx;
static char s_mg24_rxbuf[2048];
static volatile bool s_mg24_txing, s_mg24_tx_done;
static RAIL_Time_t s_mg24_txstart; // Send start, us
static uint8_t s_mg24_txfifo[256] __attribute__((aligned(4))); // Power of 2
// Copy a received PSDU, stripping the PHR and any stored FCS.
static void mg_mg24_rx_frame(RAIL_Handle_t h) {
RAIL_RxPacketInfo_t info;
uint8_t f[128];
size_t n;
char *dst;
if (RAIL_GetRxPacketInfo(h, RAIL_RX_PACKET_HANDLE_NEWEST, &info) ==
RAIL_RX_PACKET_HANDLE_INVALID ||
info.packetStatus != RAIL_RX_PACKET_READY_SUCCESS ||
info.packetBytes <= 1 || info.packetBytes > sizeof(f)) {
return;
}
RAIL_CopyRxPacket(f, &info);
n = info.packetBytes - 1U; // Strip PHR
if (n == f[0]) n -= 2; // FCS stored, strip it
if (mg_queue_book(&s_mg24_rx, &dst, n) >= n) {
memcpy(dst, f + 1, n);
mg_queue_add(&s_mg24_rx, n);
} else {
s_mg24_ifp->ndrop++;
}
}
// RAIL event callback, runs in interrupt context
static void mg_mg24_events(RAIL_Handle_t h, RAIL_Events_t events) {
if (events & RAIL_EVENT_CAL_NEEDED) {
RAIL_Calibrate(h, NULL, RAIL_CAL_ALL_PENDING);
}
if (events & RAIL_EVENT_RX_PACKET_RECEIVED) mg_mg24_rx_frame(h);
if (events & RAIL_EVENTS_TX_COMPLETION) s_mg24_tx_done = true;
}
// RAIL adds the FCS.
static size_t mgi_mg24_radio_tx(struct mg_tcpip_if *ifp, const void *buf,
size_t len) {
uint8_t phr = (uint8_t) (len + 2); // FCS is added by hardware
(void) ifp;
if (len > 125 || s_mg24_txing) return 0;
RAIL_WriteTxFifo(s_mg24_rail, &phr, 1, true); // Reset FIFO, write PHR
RAIL_WriteTxFifo(s_mg24_rail, (const uint8_t *) buf, (uint16_t) len, false);
s_mg24_tx_done = false;
s_mg24_txing = true, s_mg24_txstart = RAIL_GetTime();
if (RAIL_StartTx(s_mg24_rail, MG_802154_CHANNEL, RAIL_TX_OPTIONS_DEFAULT,
NULL) != RAIL_STATUS_NO_ERROR) {
s_mg24_txing = false;
return 0;
}
return len;
}
static bool mgi_mg24_radio_init(struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_mg24_data *data = ifp->driver_data;
struct mg_mesh_node *node = data->mesh;
RAIL_Config_t cfg = {0};
RAIL_IEEE802154_Config_t cfg154 = {0};
RAIL_TxPowerConfig_t pwr;
RAIL_StateTransitions_t to_rx = {RAIL_RF_STATE_RX, RAIL_RF_STATE_RX};
RAIL_Events_t events = RAIL_EVENT_CAL_NEEDED | RAIL_EVENT_RX_PACKET_RECEIVED |
RAIL_EVENTS_TX_COMPLETION;
cfg.eventsCallback = mg_mg24_events;
if ((s_mg24_rail = RAIL_Init(&cfg, NULL)) == NULL) {
MG_ERROR(("RAIL_Init failed"));
return false;
}
cfg154.ackConfig.enable = false;
cfg154.ackConfig.ackTimeout = 864;
cfg154.ackConfig.rxTransitions = to_rx;
cfg154.ackConfig.txTransitions = to_rx;
cfg154.timings.idleToRx = 100, cfg154.timings.idleToTx = 100;
cfg154.timings.rxToTx = 192, cfg154.timings.txToRx = 182;
cfg154.framesMask = RAIL_IEEE802154_ACCEPT_STANDARD_FRAMES;
cfg154.promiscuousMode = false; // Filter by PAN ID and our short address
pwr.mode = RAIL_TX_POWER_MODE_2P4GIG_HP, pwr.voltage = 3300;
pwr.rampTime = 10;
if (RAIL_ConfigCal(s_mg24_rail, RAIL_CAL_ALL) != RAIL_STATUS_NO_ERROR ||
RAIL_ConfigEvents(s_mg24_rail, RAIL_EVENTS_ALL, events) !=
RAIL_STATUS_NO_ERROR ||
RAIL_IEEE802154_Config2p4GHzRadio(s_mg24_rail) != RAIL_STATUS_NO_ERROR ||
RAIL_IEEE802154_Init(s_mg24_rail, &cfg154) != RAIL_STATUS_NO_ERROR ||
RAIL_IEEE802154_SetPanId(s_mg24_rail, MG_802154_PAN_ID, 0) !=
RAIL_STATUS_NO_ERROR ||
RAIL_IEEE802154_SetShortAddress(s_mg24_rail, node->radio_address, 0) !=
RAIL_STATUS_NO_ERROR ||
RAIL_SetTxTransitions(s_mg24_rail, &to_rx) != RAIL_STATUS_NO_ERROR ||
RAIL_SetRxTransitions(s_mg24_rail, &to_rx) != RAIL_STATUS_NO_ERROR ||
RAIL_ConfigTxPower(s_mg24_rail, &pwr) != RAIL_STATUS_NO_ERROR ||
RAIL_SetTxPower(s_mg24_rail, RAIL_TX_POWER_LEVEL_2P4_HP_MAX) !=
RAIL_STATUS_NO_ERROR ||
RAIL_SetTxFifo(s_mg24_rail, s_mg24_txfifo, 0, sizeof(s_mg24_txfifo)) !=
sizeof(s_mg24_txfifo)) {
MG_ERROR(("RAIL setup failed"));
return false;
}
s_mg24_ifp = ifp;
return RAIL_StartRx(s_mg24_rail, MG_802154_CHANNEL, NULL) ==
RAIL_STATUS_NO_ERROR;
}
static size_t mgi_mg24_tx(const void *buf, size_t len,
struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_mg24_data *data = ifp->driver_data;
return data->mesh->strategy->tx(data->mesh, buf, len);
}
static size_t mgi_mg24_rx(void *buf, size_t cap, struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_mg24_data *data = ifp->driver_data;
size_t len = data->mesh->rx_len;
if (len > cap) return 0;
if (len != 0 && buf != ifp->recv_queue.buf)
memcpy(buf, ifp->recv_queue.buf, len);
data->mesh->rx_len = 0;
return len;
}
static bool mgi_mg24_poll(struct mg_tcpip_if *ifp, bool s1) {
struct mg_tcpip_driver_mg24_data *data = ifp->driver_data;
struct mg_mesh_node *node = data->mesh;
uint64_t now = mg_micros();
char *buf;
size_t len;
(void) s1;
node->now = now;
if (s_mg24_txing && (s_mg24_tx_done || RAIL_GetTime() - s_mg24_txstart >
MG_MG24_TX_TIMEOUT_US)) {
if (!s_mg24_tx_done) {
RAIL_Idle(s_mg24_rail, RAIL_IDLE_ABORT, true);
RAIL_StartRx(s_mg24_rail, MG_802154_CHANNEL, NULL);
}
s_mg24_txing = false;
node->tx_at = now + node->strategy->radio->ifs_us;
}
while (node->rx_len == 0 && (len = mg_queue_next(&s_mg24_rx, &buf)) != 0) {
node->strategy->rx(node, buf, len);
mg_queue_del(&s_mg24_rx, len);
}
node->strategy->tick(node, now);
return node->joined;
}
static bool mgi_mg24_init(struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_mg24_data *data = ifp->driver_data;
const struct mg_mesh *strategy = data->strategy;
uint16_t address =
(uint16_t) (2 + ((unsigned) ifp->mac[4] * 256 + ifp->mac[5]) % 65532u);
if (strategy == NULL) strategy = &mg_mesh_default;
data->mesh = calloc(1, sizeof(*data->mesh));
if (data->mesh == NULL) return false;
if (!strategy->init(data->mesh, ifp, NULL, address)) {
free(data->mesh);
data->mesh = NULL;
return false;
}
data->mesh->strategy = strategy;
data->mesh->tx = mgi_mg24_radio_tx;
mg_queue_init(&s_mg24_rx, s_mg24_rxbuf, sizeof(s_mg24_rxbuf));
if (mgi_mg24_radio_init(ifp)) return true;
data->mesh->strategy->free(data->mesh);
free(data->mesh);
data->mesh = NULL;
return false;
}
struct mg_tcpip_driver mg_tcpip_driver_mg24 = {mgi_mg24_init, mgi_mg24_tx,
mgi_mg24_rx, mgi_mg24_poll};
#endif
+34
View File
@@ -0,0 +1,34 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
#pragma once
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_MG24) && MG_ENABLE_DRIVER_MG24
struct mg_tcpip_driver_mg24_data {
struct mg_mesh_node *mesh; // Driver-owned runtime state
const struct mg_mesh *strategy; // NULL selects mg_mesh_default
};
extern struct mg_tcpip_driver mg_tcpip_driver_mg24;
#define MG_TCPIP_DRIVER_MG24_INIT(manager) \
do { \
static struct mg_tcpip_driver_mg24_data driver_data_; \
static struct mg_tcpip_if mif_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.mgr = (manager); \
mif_.driver = &mg_tcpip_driver_mg24; \
mif_.driver_data = &driver_data_; \
mg_tcpip_init((manager), &mif_); \
MG_INFO(("Driver: mg24, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#ifndef MG_TCPIP_DRIVER_INIT
#define MG_TCPIP_DRIVER_INIT(x) MG_TCPIP_DRIVER_MG24_INIT(x)
#endif
#endif
+179
View File
@@ -0,0 +1,179 @@
#include "net_builtin.h"
#include "l2_mesh.h"
#include "log.h"
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_NRF52) && MG_ENABLE_DRIVER_NRF52
#include <nrf.h>
static struct mg_tcpip_if *s_nrf52_ifp;
static struct mg_queue s_nrf52_rx;
static char s_nrf52_queue[2048];
static uint8_t s_nrf52_rxbuf[128] __attribute__((aligned(4)));
static uint8_t s_nrf52_txbuf[128] __attribute__((aligned(4)));
static volatile bool s_nrf52_txing, s_nrf52_done;
static uint64_t s_nrf52_started;
static void mgi_nrf52_listen(void) {
NRF_RADIO->PACKETPTR = (uint32_t) s_nrf52_rxbuf;
NRF_RADIO->SHORTS =
RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk;
NRF_RADIO->EVENTS_FRAMESTART = 0;
NRF_RADIO->EVENTS_END = 0;
NRF_RADIO->TASKS_RXEN = 1;
}
static void mgi_nrf52_disable(void) {
NRF_RADIO->SHORTS = 0;
NRF_RADIO->EVENTS_DISABLED = 0;
NRF_RADIO->TASKS_DISABLE = 1;
while (!NRF_RADIO->EVENTS_DISABLED) (void) 0;
NRF_RADIO->EVENTS_DISABLED = 0;
}
void RADIO_IRQHandler(void);
void RADIO_IRQHandler(void) {
if (!NRF_RADIO->EVENTS_DISABLED) return;
NRF_RADIO->EVENTS_DISABLED = 0;
if (s_nrf52_txing) {
s_nrf52_txing = false;
s_nrf52_done = true;
} else if (NRF_RADIO->CRCSTATUS) {
struct mg_tcpip_driver_nrf52_data *data = s_nrf52_ifp->driver_data;
struct mg_mesh_node *node = data->mesh;
size_t len = s_nrf52_rxbuf[0] >= 2 ? s_nrf52_rxbuf[0] - 2U : 0;
uint8_t *frame = s_nrf52_rxbuf + 1;
uint16_t dst, src;
if (len <= 125 &&
mg_radio_802154.parse_hdr(&mg_radio_802154, frame, len, &dst, &src) !=
0 &&
(dst == 0xffffu || dst == node->radio_address)) {
char *dst;
if (mg_queue_book(&s_nrf52_rx, &dst, len) >= len) {
memcpy(dst, frame, len);
mg_queue_add(&s_nrf52_rx, len);
} else {
s_nrf52_ifp->ndrop++;
}
}
}
mgi_nrf52_listen();
}
static size_t mgi_nrf52_radio_tx(struct mg_tcpip_if *ifp, const void *buf,
size_t len) {
(void) ifp;
if (len > 125) return 0;
NVIC_DisableIRQ(RADIO_IRQn);
if (s_nrf52_txing || NRF_RADIO->EVENTS_FRAMESTART) {
NVIC_EnableIRQ(RADIO_IRQn);
return 0;
}
mgi_nrf52_disable();
s_nrf52_txbuf[0] = (uint8_t) (len + 2);
memcpy(s_nrf52_txbuf + 1, buf, len);
s_nrf52_done = false;
s_nrf52_txing = true;
s_nrf52_started = mg_micros();
NRF_RADIO->PACKETPTR = (uint32_t) s_nrf52_txbuf;
NRF_RADIO->SHORTS =
RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk;
NRF_RADIO->TASKS_TXEN = 1;
NVIC_EnableIRQ(RADIO_IRQn);
return len;
}
static bool mgi_nrf52_radio_init(struct mg_tcpip_if *ifp) {
s_nrf52_ifp = ifp;
NRF_RADIO->POWER = 1;
NRF_RADIO->MODE = RADIO_MODE_MODE_Ieee802154_250Kbit;
NRF_RADIO->PCNF0 = (8UL << RADIO_PCNF0_LFLEN_Pos) |
(RADIO_PCNF0_PLEN_32bitZero << RADIO_PCNF0_PLEN_Pos) |
RADIO_PCNF0_CRCINC_Msk;
NRF_RADIO->PCNF1 = 127;
NRF_RADIO->CRCCNF = 0x202;
NRF_RADIO->CRCPOLY = 0x11021;
NRF_RADIO->CRCINIT = 0;
NRF_RADIO->MODECNF0 = (RADIO_MODECNF0_DTX_Center << RADIO_MODECNF0_DTX_Pos) |
RADIO_MODECNF0_RU_Fast;
NRF_RADIO->SFD = 0xa7;
NRF_RADIO->FREQUENCY = 5 * (MG_802154_CHANNEL - 10);
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Pos8dBm;
// Correlator threshold 45 matches the RADIO reset value. A threshold of
// 20 caused false busy detections and persistent L2 retries.
NRF_RADIO->CCACTRL =
(RADIO_CCACTRL_CCAMODE_CarrierMode << RADIO_CCACTRL_CCAMODE_Pos) |
(2UL << RADIO_CCACTRL_CCACORRCNT_Pos) |
(45UL << RADIO_CCACTRL_CCACORRTHRES_Pos);
NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk;
NVIC_SetPriority(RADIO_IRQn, 0);
NVIC_EnableIRQ(RADIO_IRQn);
mgi_nrf52_listen();
return true;
}
static size_t mgi_nrf52_tx(const void *buf, size_t len,
struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_nrf52_data *data = ifp->driver_data;
return data->mesh->strategy->tx(data->mesh, buf, len);
}
static size_t mgi_nrf52_rx(void *buf, size_t cap, struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_nrf52_data *data = ifp->driver_data;
size_t len = data->mesh->rx_len;
if (len > cap) return 0;
if (len != 0 && buf != ifp->recv_queue.buf)
memcpy(buf, ifp->recv_queue.buf, len);
data->mesh->rx_len = 0;
return len;
}
static bool mgi_nrf52_poll(struct mg_tcpip_if *ifp, bool s1) {
struct mg_tcpip_driver_nrf52_data *data = ifp->driver_data;
struct mg_mesh_node *node = data->mesh;
uint64_t now = mg_micros();
char *buf;
size_t len;
(void) s1;
if (s_nrf52_txing && mg_micros() - s_nrf52_started > 60000) {
NVIC_DisableIRQ(RADIO_IRQn);
mgi_nrf52_disable();
s_nrf52_txing = false;
s_nrf52_done = true;
mgi_nrf52_listen();
NVIC_EnableIRQ(RADIO_IRQn);
}
if (s_nrf52_done) {
s_nrf52_done = false;
node->tx_at = now + node->strategy->radio->ifs_us;
}
node->now = now;
while (node->rx_len == 0 && (len = mg_queue_next(&s_nrf52_rx, &buf)) != 0) {
node->strategy->rx(node, buf, len);
mg_queue_del(&s_nrf52_rx, len);
}
node->strategy->tick(node, now);
return node->joined;
}
static bool mgi_nrf52_init(struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_nrf52_data *data = ifp->driver_data;
const struct mg_mesh *strategy = data->strategy;
uint16_t address;
address =
(uint16_t) (2 + ((unsigned) ifp->mac[4] * 256 + ifp->mac[5]) % 65532u);
if (strategy == NULL) strategy = &mg_mesh_default;
data->mesh = calloc(1, sizeof(*data->mesh));
if (data->mesh == NULL) return false;
if (!strategy->init(data->mesh, ifp, NULL, address)) {
free(data->mesh);
data->mesh = NULL;
return false;
}
data->mesh->strategy = strategy;
data->mesh->tx = mgi_nrf52_radio_tx;
mg_queue_init(&s_nrf52_rx, s_nrf52_queue, sizeof(s_nrf52_queue));
if (mgi_nrf52_radio_init(ifp)) return true;
strategy->free(data->mesh);
free(data->mesh);
data->mesh = NULL;
return false;
}
struct mg_tcpip_driver mg_tcpip_driver_nrf52 = {mgi_nrf52_init, mgi_nrf52_tx,
mgi_nrf52_rx, mgi_nrf52_poll};
#endif
+34
View File
@@ -0,0 +1,34 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
#pragma once
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_NRF52) && MG_ENABLE_DRIVER_NRF52
struct mg_tcpip_driver_nrf52_data {
struct mg_mesh_node *mesh; // Driver-owned runtime state
const struct mg_mesh *strategy; // NULL selects mg_mesh_default
};
extern struct mg_tcpip_driver mg_tcpip_driver_nrf52;
#define MG_TCPIP_DRIVER_NRF52_INIT(manager) \
do { \
static struct mg_tcpip_driver_nrf52_data driver_data_; \
static struct mg_tcpip_if mif_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.mgr = (manager); \
mif_.driver = &mg_tcpip_driver_nrf52; \
mif_.driver_data = &driver_data_; \
mg_tcpip_init((manager), &mif_); \
MG_INFO(("Driver: nrf52, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#ifndef MG_TCPIP_DRIVER_INIT
#define MG_TCPIP_DRIVER_INIT(x) MG_TCPIP_DRIVER_NRF52_INIT(x)
#endif
#endif
+2
View File
@@ -27,6 +27,7 @@ struct mg_tcpip_driver_rw612_data {
#define MG_DRIVER_MDC_HOLDTIME 3
#endif
#ifndef MG_TCPIP_DRIVER_INIT
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_tcpip_driver_rw612_data driver_data_; \
@@ -42,5 +43,6 @@ struct mg_tcpip_driver_rw612_data {
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: rw612, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#endif
#endif
-29
View File
@@ -1,29 +0,0 @@
#pragma once
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RW612_154) && \
MG_ENABLE_DRIVER_RW612_154
// NXP RW612 IEEE 802.15.4 radio, as an Ethernet interface: Ethernet frames
// are split into radio frames, see mg_l2_802154_tx(). The radio runs on
// CPU2: NXP BLE/802.15.4 combo firmware, an OpenThread radio co-processor
// (RCP). The firmware must be loaded and running before mg_tcpip_init(), see
// the FRDM-RW612 eth_radio_bridge tutorial. The driver talks to it over the
// CPU2 <-> CPU3 IMU mailbox: IMUMC messages carrying HDLC framed Spinel.
// driver_data: struct mg_l2_802154_data, channel and PAN ID set
#ifndef MG_TCPIP_DRIVER_INIT
#define MG_TCPIP_DRIVER_INIT(mgr) \
do { \
static struct mg_l2_802154_data driver_data_; \
static struct mg_tcpip_if mif_; \
driver_data_.channel = MG_L2_802154_CHANNEL; \
driver_data_.pan_id = MG_L2_802154_PAN_ID; \
mif_.driver = &mg_tcpip_driver_rw612_154; \
mif_.driver_data = &driver_data_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mg_tcpip_init(mgr, &mif_); \
MG_INFO(("Driver: rw612_154, channel %d", MG_L2_802154_CHANNEL)); \
} while (0)
#endif
#endif
+144 -81
View File
@@ -1,7 +1,16 @@
#include "net_builtin.h"
#include "l2_mesh.h"
#include "log.h"
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RW612_154) && \
MG_ENABLE_DRIVER_RW612_154
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RW612W) && \
MG_ENABLE_DRIVER_RW612W
#include "sha256.h"
#ifdef ENET
#undef ENET
#endif
#include <fsl_ocotp.h>
// The 802.15.4 radio runs NXP combo firmware on CPU2, an OpenThread RCP. We
// are CPU3. Messages go through the IMU mailbox: IMU messages carry IMUMC
@@ -16,13 +25,13 @@ struct mg_rw612_imu {
};
#define MGI_IMU ((struct mg_rw612_imu *) 0x443ff804) // CPU3 -> CPU2
#define MGI_IMU_PEER ((struct mg_rw612_imu *) 0x443ff818) // CPU2 -> CPU3
#define MGI_IMU_LOCKED MG_BIT(0) // STATUS: FIFO locked
#define MGI_IMU_ALMOST_FULL MG_BIT(1) // STATUS: FIFO almost full
#define MGI_IMU_EMPTY MG_BIT(3) // STATUS: FIFO empty
#define MGI_IMU_LOCKED MG_BIT(0) // STATUS: FIFO locked
#define MGI_IMU_ALMOST_FULL MG_BIT(1) // STATUS: FIFO almost full
#define MGI_IMU_EMPTY MG_BIT(3) // STATUS: FIFO empty
#define MGI_IMU_WR_PTR(s) (((s) >> 16) & 15U)
#define MGI_IMU_INT_CLR MG_BIT(0) // CNTL: clear message ready IRQ
#define MGI_IMU_FLUSH MG_BIT(16) // CNTL: flush send FIFO
#define MGI_IMU_WAIT_FOR_ACK MG_BIT(17) // CNTL: lock send FIFO
#define MGI_IMU_INT_CLR MG_BIT(0) // CNTL: clear message ready IRQ
#define MGI_IMU_FLUSH MG_BIT(16) // CNTL: flush send FIFO
#define MGI_IMU_WAIT_FOR_ACK MG_BIT(17) // CNTL: lock send FIFO
#define MGI_IMU_WATERMARK(x) ((uint32_t) (x) << 20)
#define MGI_IMU_WATERMARK_MASK (15U << 20)
@@ -73,8 +82,10 @@ enum {
MGI_PROP_NCP_VERSION = 2,
MGI_PROP_PHY_ENABLED = 0x20,
MGI_PROP_PHY_CHAN = 0x21,
MGI_PROP_SADDR = 0x35, // Short address, little endian
MGI_PROP_PANID = 0x36,
MGI_PROP_RAW_STREAM = 0x37,
MGI_PROP_PROMISCUOUS = 0x38,
MGI_PROP_STREAM_RAW = 0x71
};
#define MGI_RESET_STACK 2 // RESET argument: reset the RCP stack only
@@ -82,7 +93,6 @@ enum {
#define MGI_STATUS_RESET_MAX 127
#define MGI_TX_TIMEOUT_MS 200 // Wait for transmit done, incl. retries
#define MGI_RSP_TIMEOUT_MS 500
#define MGI_RXQ_CNT 16 // A whole Ethernet frame, 15 radio frames
// Driver state, one radio
struct mg_rw612 {
@@ -98,11 +108,13 @@ struct mg_rw612 {
bool rsp; // Response to the pending request received
uint32_t rsp_status; // Its status, if LAST_STATUS, else 0
char version[64]; // RCP version string
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
bool txing; // A send is on, see mg_rw612_start()
uint64_t txstart; // Its start, ms
struct mg_tcpip_if *ifp; // Our interface, for the L2 receive pool
};
static struct mg_rw612 s_rw612;
static struct mg_queue s_rw612w_rx;
static char s_rw612w_queue[2048];
static void mg_rw612_imu_send(uint8_t type, uint8_t subtype,
const uint32_t *payload, uint8_t len) {
@@ -126,12 +138,15 @@ static void mg_rw612_control(uint8_t subtype, uint32_t a, uint32_t b,
}
static void mg_rw612_queue_rx(const uint8_t *frame, size_t len) {
struct mg_rw612 *s = &s_rw612;
uint8_t next = (uint8_t) ((s->rxhead + 1) % MGI_RXQ_CNT);
if (next == s->rxtail || len > sizeof(s->rxq[0])) return; // Full: drop
memcpy(s->rxq[s->rxhead], frame, len);
s->rxlen[s->rxhead] = (uint8_t) len;
s->rxhead = next;
{
char *dst;
if (mg_queue_book(&s_rw612w_rx, &dst, len) >= len) {
memcpy(dst, frame, len);
mg_queue_add(&s_rw612w_rx, len);
} else {
s_rw612.ifp->ndrop++;
}
}
}
// Spinel packed unsigned integer: 7 bits per byte, LSB first
@@ -293,14 +308,13 @@ static bool mg_rw612_imumc_send(const uint8_t *p, size_t len) {
return true;
}
// Send a Spinel frame, HDLC encoded, and wait for the response with the
// same transaction ID. Return false on timeout
static bool mg_rw612_request(uint8_t *f, size_t len, uint32_t timeout_ms) {
// Send a Spinel frame, HDLC encoded, with a new transaction ID. Its
// response sets rsp, see mg_rw612_spinel()
static bool mg_rw612_post(uint8_t *f, size_t len) {
struct mg_rw612 *s = &s_rw612;
uint8_t buf[2 * 160 + 6];
uint16_t fcs = 0xffff;
size_t i, n = 0;
uint64_t expire = mg_millis() + timeout_ms;
s->tid = (uint8_t) (s->tid % 15 + 1);
f[0] = (uint8_t) (0x80 | s->tid); // Header: flag, interface 0, TID
s->rsp = false;
@@ -318,9 +332,15 @@ static bool mg_rw612_request(uint8_t *f, size_t len, uint32_t timeout_ms) {
buf[n++] = c;
}
buf[n++] = 0x7e;
if (!mg_rw612_imumc_send(buf, n)) return false;
while (!s->rsp && mg_millis() < expire) mg_rw612_poll_imu();
return s->rsp;
return mg_rw612_imumc_send(buf, n);
}
// Send a Spinel frame and wait for its response. Return false on timeout
static bool mg_rw612_request(uint8_t *f, size_t len, uint32_t timeout_ms) {
uint64_t expire = mg_millis() + timeout_ms;
if (!mg_rw612_post(f, len)) return false;
while (!s_rw612.rsp && mg_millis() < expire) mg_rw612_poll_imu();
return s_rw612.rsp;
}
static bool mg_rw612_set(uint32_t prop, const uint8_t *val, size_t len) {
@@ -338,27 +358,43 @@ static bool mg_rw612_fail(const char *msg) {
return false;
}
static bool mg_rw612_154_init(struct mg_tcpip_if *ifp) {
// Start one raw transmission. The RCP adds the FCS and reports completion.
static size_t mgi_rw612w_radio_tx(struct mg_tcpip_if *ifp, const void *buf,
size_t len) {
uint8_t f[3 + 2 + 127 + 4];
size_t n = 3;
if (len > 125 || s_rw612.txing) return 0;
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;
f[n++] = 0, f[n++] = 0; // FCS, filled in by the radio
(void) ifp;
f[n++] = MG_802154_CHANNEL;
f[n++] = 0; // Max CSMA backoffs
f[n++] = 0; // Max frame retries
f[n++] = 0; // CSMA disabled for scheduled turns
if (!mg_rw612_post(f, n)) return 0;
s_rw612.txing = true, s_rw612.txstart = mg_millis();
return len;
}
static bool mgi_rw612w_radio_init(struct mg_tcpip_if *ifp) {
struct mg_rw612 *s = &s_rw612;
struct mg_l2_802154_data *d =
(struct mg_l2_802154_data *) ifp->driver_data;
uint8_t f[8], one = 1, pan[2];
struct mg_tcpip_driver_rw612w_data *data = ifp->driver_data;
struct mg_mesh_node *node = data->mesh;
uint8_t f[8], one = 1, zero = 0, pan[2], addr[2], channel = MG_802154_CHANNEL;
uint64_t expire;
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) {
MG_ERROR(("Radio firmware is not running on CPU2"));
return false;
}
memset(s, 0, sizeof(*s));
s->free_bufs = 0xffff;
s->free_bufs = 0xffff, s->ifp = ifp;
// 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);
MGI_IMU->CNTL = (MGI_IMU->CNTL & ~MGI_IMU_WATERMARK_MASK) |
MGI_IMU_WATERMARK(15);
MGI_IMU->CNTL =
(MGI_IMU->CNTL & ~MGI_IMU_WATERMARK_MASK) | MGI_IMU_WATERMARK(15);
while ((MGI_IMU_PEER->STATUS & MGI_IMU_EMPTY) == 0) (void) MGI_IMU->RD_MSG;
// Synchronise the IMU link, then connect the IMUMC endpoints
MGI_MBOX->magic[1] = MGI_SYNC_MAGIC;
@@ -385,10 +421,15 @@ static bool mg_rw612_154_init(struct mg_tcpip_if *ifp) {
if (!mg_rw612_set(MGI_PROP_PHY_ENABLED, &one, 1)) {
return mg_rw612_fail("PHY enable failed");
}
pan[0] = (uint8_t) d->pan_id, pan[1] = (uint8_t) (d->pan_id >> 8);
if (!mg_rw612_set(MGI_PROP_PHY_CHAN, &d->channel, 1) ||
!mg_rw612_set(MGI_PROP_PANID, pan, 2)) {
return mg_rw612_fail("Channel or PAN ID set failed");
pan[0] = (uint8_t) MG_802154_PAN_ID;
pan[1] = (uint8_t) (MG_802154_PAN_ID >> 8);
addr[0] = (uint8_t) node->radio_address;
addr[1] = (uint8_t) (node->radio_address >> 8);
if (!mg_rw612_set(MGI_PROP_PHY_CHAN, &channel, 1) ||
!mg_rw612_set(MGI_PROP_PANID, pan, 2) ||
!mg_rw612_set(MGI_PROP_SADDR, addr, 2) ||
!mg_rw612_set(MGI_PROP_PROMISCUOUS, &zero, 1)) {
return mg_rw612_fail("Channel, PAN ID or address set failed");
}
if (!mg_rw612_set(MGI_PROP_RAW_STREAM, &one, 1)) {
return mg_rw612_fail("Raw stream enable failed");
@@ -396,55 +437,77 @@ static bool mg_rw612_154_init(struct mg_tcpip_if *ifp) {
return true;
}
// Send one radio frame: MAC header and payload, no FCS
static size_t mg_rw612_send(const uint8_t *p, size_t len,
static size_t mgi_rw612w_tx(const void *buf, 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;
f[1] = MGI_CMD_SET, f[2] = MGI_PROP_STREAM_RAW;
f[n++] = (uint8_t) (len + 2), f[n++] = 0; // PSDU length, incl. FCS
memcpy(f + n, p, len), n += len;
f[n++] = 0, f[n++] = 0; // FCS, filled in by the radio
f[n++] = d->channel;
f[n++] = 4; // Max CSMA backoffs
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"));
return 0;
}
if (s_rw612.rsp_status != 0) {
MG_VERBOSE(("TX failed, status %lu", (unsigned long) s_rw612.rsp_status));
return 0;
}
struct mg_tcpip_driver_rw612w_data *data = ifp->driver_data;
return data->mesh->strategy->tx(data->mesh, buf, len);
}
static size_t mgi_rw612w_rx(void *buf, size_t cap, struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_rw612w_data *data = ifp->driver_data;
size_t len = data->mesh->rx_len;
if (len > cap) return 0;
if (len != 0 && buf != ifp->recv_queue.buf)
memcpy(buf, ifp->recv_queue.buf, len);
data->mesh->rx_len = 0;
return len;
}
static size_t mg_rw612_154_tx(const void *buf, size_t len,
struct mg_tcpip_if *ifp) {
return mg_l2_802154_tx(ifp, buf, len, mg_rw612_send);
}
static size_t mg_rw612_154_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
struct mg_rw612 *s = &s_rw612;
size_t res = 0;
static bool mgi_rw612w_poll(struct mg_tcpip_if *ifp, bool s1) {
struct mg_tcpip_driver_rw612w_data *data = ifp->driver_data;
struct mg_mesh_node *node = data->mesh;
uint64_t now = mg_micros();
char *buf;
size_t len;
(void) s1;
bool was_sending = s_rw612.txing;
mg_rw612_poll_imu();
while (s->rxtail != s->rxhead && res == 0) {
res = mg_l2_802154_rx(ifp, s->rxq[s->rxtail], s->rxlen[s->rxtail], buf,
len);
s->rxtail = (uint8_t) ((s->rxtail + 1) % MGI_RXQ_CNT);
if (s_rw612.txing &&
(s_rw612.rsp || mg_millis() > s_rw612.txstart + MGI_TX_TIMEOUT_MS)) {
if (!s_rw612.rsp) MG_ERROR(("TX timeout"));
s_rw612.txing = false;
}
return res;
if (was_sending && !s_rw612.txing)
node->tx_at = now + node->strategy->radio->ifs_us;
node->now = now;
while (node->rx_len == 0 && (len = mg_queue_next(&s_rw612w_rx, &buf)) != 0) {
node->strategy->rx(node, buf, len);
mg_queue_del(&s_rw612w_rx, len);
}
node->strategy->tick(node, now);
return node->joined;
}
static bool mg_rw612_154_poll(struct mg_tcpip_if *ifp, bool s1) {
(void) ifp, (void) s1;
return s_rw612.up;
static bool mgi_rw612w_init(struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_rw612w_data *data = ifp->driver_data;
const struct mg_mesh *strategy = data->strategy;
uint8_t uid[FSL_OCOTP_UID_LENGTH] = {0}, digest[32];
uint32_t uid_len = sizeof(uid);
uint16_t address;
OCOTP_ReadUniqueID(uid, &uid_len);
mg_sha256(digest, uid, sizeof(uid));
memcpy(ifp->mac, digest, 6);
ifp->mac[0] = 0x02;
address =
(uint16_t) (2 + ((unsigned) ifp->mac[4] * 256 + ifp->mac[5]) % 65532u);
if (strategy == NULL) strategy = &mg_mesh_default;
data->mesh = calloc(1, sizeof(*data->mesh));
if (data->mesh == NULL) return false;
if (!strategy->init(data->mesh, ifp, NULL, address)) {
free(data->mesh);
data->mesh = NULL;
return false;
}
data->mesh->strategy = strategy;
data->mesh->tx = mgi_rw612w_radio_tx;
mg_queue_init(&s_rw612w_rx, s_rw612w_queue, sizeof(s_rw612w_queue));
if (mgi_rw612w_radio_init(ifp)) return true;
strategy->free(data->mesh);
free(data->mesh);
data->mesh = NULL;
return false;
}
struct mg_tcpip_driver mg_tcpip_driver_rw612_154 = {
mg_rw612_154_init, mg_rw612_154_tx, mg_rw612_154_rx, mg_rw612_154_poll};
struct mg_tcpip_driver mg_tcpip_driver_rw612w = {
mgi_rw612w_init, mgi_rw612w_tx, mgi_rw612w_rx, mgi_rw612w_poll};
#endif
+35
View File
@@ -0,0 +1,35 @@
// Copyright (c) 2026 Cesanta Software Limited
// All rights reserved
#pragma once
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RW612W) && \
MG_ENABLE_DRIVER_RW612W
struct mg_tcpip_driver_rw612w_data {
struct mg_mesh_node *mesh; // Driver-owned runtime state
const struct mg_mesh *strategy; // NULL selects mg_mesh_default
};
extern struct mg_tcpip_driver mg_tcpip_driver_rw612w;
#define MG_TCPIP_DRIVER_RW612W_INIT(manager) \
do { \
static struct mg_tcpip_driver_rw612w_data driver_data_; \
static struct mg_tcpip_if mif_; \
MG_SET_MAC_ADDRESS(mif_.mac); \
mif_.ip = MG_TCPIP_IP; \
mif_.mask = MG_TCPIP_MASK; \
mif_.gw = MG_TCPIP_GW; \
mif_.mgr = (manager); \
mif_.driver = &mg_tcpip_driver_rw612w; \
mif_.driver_data = &driver_data_; \
mg_tcpip_init((manager), &mif_); \
MG_INFO(("Driver: rw612w, MAC: %M", mg_print_mac, mif_.mac)); \
} while (0)
#ifndef MG_TCPIP_DRIVER_INIT
#define MG_TCPIP_DRIVER_INIT(x) MG_TCPIP_DRIVER_RW612W_INIT(x)
#endif
#endif