mirror of
https://github.com/cesanta/mongoose.git
synced 2026-11-06 15:51:25 +00:00
237 lines
8.4 KiB
C
237 lines
8.4 KiB
C
#include "l2_mesh.h"
|
|
#include "log.h"
|
|
#include "net_builtin.h"
|
|
|
|
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_MCXW71) && \
|
|
MG_ENABLE_DRIVER_MCXW71
|
|
|
|
// ZLL, 802.15.4 link layer. See MCXW71 reference manual and NXP
|
|
// ieee_802_15_4 PHY sources: PhyPacketProcessor.c, PhyPlmeData.c, PhyISR.c
|
|
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;
|
|
};
|
|
|
|
// 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)
|
|
#else
|
|
#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
|
|
|
|
#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_CTRL_NO_RX_RECYCLE MG_BIT(5)
|
|
#define MGI_FRAME_FILTER 0x70bU // Beacon, data, command; versions 0..2
|
|
|
|
// PHY_CTRL[XCVSEQ]
|
|
enum { MGI_SEQ_IDLE = 0, MGI_SEQ_RX = 1, MGI_SEQ_TX = 2 };
|
|
#define MGI_SEND_TIMEOUT_MS 60
|
|
|
|
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_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;
|
|
}
|
|
|
|
// 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;
|
|
MGI_ZLL->IRQSTS = MGI_ZLL->IRQSTS;
|
|
MGI_ZLL->SAM_CTRL = 0;
|
|
MGI_ZLL->RX_FRAME_FILTER = MGI_FRAME_FILTER;
|
|
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;
|
|
}
|
|
|
|
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 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 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);
|
|
}
|
|
}
|
|
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 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 = {
|
|
mgi_mcxw71_init, mgi_mcxw71_tx, mgi_mcxw71_rx, mgi_mcxw71_poll};
|
|
#endif
|