mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-25 12:04:47 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f12e44b0b1 |
No files matched your search
+1737
-47
File diff suppressed because it is too large.
Load diff
+141
-2
@@ -1251,8 +1251,13 @@ 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
|
||||
@@ -1397,6 +1402,8 @@ 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
|
||||
@@ -1438,6 +1445,7 @@ 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.
|
||||
@@ -4227,6 +4235,7 @@ extern void mg_mqtt_poll(struct mg_mgr *);
|
||||
|
||||
|
||||
|
||||
|
||||
// Supported OTA targets
|
||||
#define MG_OTA_NONE 0 // No OTA support
|
||||
#define MG_OTA_STM32H5 1 // STM32 H5
|
||||
@@ -4242,6 +4251,7 @@ extern void mg_mqtt_poll(struct mg_mgr *);
|
||||
#define MG_OTA_RT1170 304 // IMXRT1170
|
||||
#define MG_OTA_RT1180 305 // IMXRT1180
|
||||
#define MG_OTA_MCXN 310 // MCXN947
|
||||
#define MG_OTA_MCXW71 311 // MCXW71
|
||||
#define MG_OTA_RW612 320 // FRDM-RW612
|
||||
#define MG_OTA_FLASH 900 // OTA via internal flash
|
||||
#define MG_OTA_ESP32 910 // ESP32 OTA implementation
|
||||
@@ -4271,6 +4281,19 @@ bool mg_ota_write(const void *buf, size_t len);
|
||||
// Ends the OTA process and commits the written firmware.
|
||||
bool mg_ota_end(void);
|
||||
|
||||
// Push-based OTA: mg_http_stream_body() callback that flashes the request
|
||||
//
|
||||
// ```c
|
||||
// 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");
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
bool mg_http_ota_cb(struct mg_http_message *hm, struct mg_str *data, void **p);
|
||||
|
||||
// MG_OTA_ROLLBACK: swap firmware banks and reset. On flash-based OTA, the
|
||||
// default calls swap_fn() (no-op on single-bank) then resets. Override in
|
||||
// mongoose_config.h for platforms without mg_flash (ESP32, PicoSDK, etc.).
|
||||
@@ -4583,7 +4606,13 @@ 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_USER };
|
||||
enum mg_l2type {
|
||||
MG_TCPIP_L2_ETH = 0,
|
||||
MG_TCPIP_L2_PPP,
|
||||
MG_TCPIP_L2_PPPoE,
|
||||
MG_TCPIP_L2_LOWPAN,
|
||||
MG_TCPIP_L2_USER
|
||||
};
|
||||
|
||||
#if defined(__DCC__)
|
||||
#pragma pack(1)
|
||||
@@ -4606,10 +4635,27 @@ 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__)
|
||||
@@ -4618,7 +4664,6 @@ union mg_l2data {
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
|
||||
enum mg_l2proto {
|
||||
MG_TCPIP_L2PROTO_IPV4 = 0,
|
||||
MG_TCPIP_L2PROTO_IPV6,
|
||||
@@ -4679,6 +4724,24 @@ 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.
|
||||
@@ -4713,6 +4776,8 @@ 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
|
||||
@@ -4744,6 +4809,13 @@ 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
|
||||
};
|
||||
|
||||
@@ -4757,6 +4829,15 @@ 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
|
||||
@@ -4789,6 +4870,8 @@ extern struct mg_tcpip_driver mg_tcpip_driver_nxp_wifi;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_st67w6;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_atcmd;
|
||||
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;
|
||||
|
||||
// SPI bus abstraction for SPI-attached network chips (e.g. W5500).
|
||||
// Populate and assign to the driver's driver_data field.
|
||||
@@ -5176,6 +5259,32 @@ struct mg_tcpip_driver_imxrt_data {
|
||||
#endif
|
||||
|
||||
|
||||
#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.
|
||||
// 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
|
||||
|
||||
#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")); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(MG_ENABLE_TCPIP) && MG_ENABLE_TCPIP && \
|
||||
defined(MG_ENABLE_DRIVER_NETC) && MG_ENABLE_DRIVER_NETC
|
||||
|
||||
@@ -5394,6 +5503,36 @@ struct mg_tcpip_driver_rw612_data {
|
||||
#endif
|
||||
|
||||
|
||||
#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
|
||||
|
||||
#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")); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_SAME54) && \
|
||||
MG_ENABLE_DRIVER_SAME54
|
||||
|
||||
|
||||
@@ -65,8 +65,13 @@
|
||||
#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
|
||||
@@ -211,6 +216,8 @@
|
||||
|
||||
#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
|
||||
@@ -248,3 +255,4 @@
|
||||
#ifndef MG_ENABLE_HEALTH
|
||||
#define MG_ENABLE_HEALTH 0
|
||||
#endif
|
||||
|
||||
+1
-16
@@ -533,21 +533,6 @@ 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;
|
||||
@@ -596,7 +581,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, otacb, NULL)) return;
|
||||
if (mg_http_stream_body(c, hm, mg_http_ota_cb, NULL)) return;
|
||||
}
|
||||
if (d->marker != '\0') mg_log_http_req(c, hm);
|
||||
} else if (ev == MG_EV_HTTP_MSG && d->marker != '\0') {
|
||||
|
||||
@@ -173,6 +173,15 @@ 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;
|
||||
@@ -196,6 +205,14 @@ 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
|
||||
@@ -314,7 +331,11 @@ 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;
|
||||
sendnsreq(c, &host, c->mgr->dnstimeout, dns, c->mgr->use_dns6);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
#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;
|
||||
};
|
||||
|
||||
#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_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
|
||||
|
||||
// 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();
|
||||
while (((mg_mcxw71_symbols() - start) & 0xffffffU) < MGI_TX_TIMEOUT) {
|
||||
if (MGI_ZLL->IRQSTS & MGI_IRQSTS_SEQIRQ) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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;
|
||||
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;
|
||||
}
|
||||
|
||||
// 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"));
|
||||
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->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
|
||||
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;
|
||||
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
|
||||
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_RX);
|
||||
if (!ok) MG_DEBUG(("TX failed, %d tries", tries));
|
||||
(void) ifp;
|
||||
return ok ? len : 0;
|
||||
}
|
||||
|
||||
static size_t mg_mcxw71_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
||||
uint32_t irqsts = MGI_ZLL->IRQSTS;
|
||||
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];
|
||||
}
|
||||
mg_mcxw71_idle();
|
||||
mg_mcxw71_start(MGI_SEQ_RX);
|
||||
(void) ifp;
|
||||
return n;
|
||||
}
|
||||
|
||||
static bool mg_mcxw71_poll(struct mg_tcpip_if *ifp, bool s1) {
|
||||
(void) ifp, (void) s1;
|
||||
return true; // Radio link is always up
|
||||
}
|
||||
|
||||
struct mg_tcpip_driver mg_tcpip_driver_mcxw71 = {mg_mcxw71_init, mg_mcxw71_tx,
|
||||
mg_mcxw71_rx, mg_mcxw71_poll};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,26 @@
|
||||
#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. Frames passed to tx() and
|
||||
// returned by rx() are raw 802.15.4 frames: MAC header and payload, no FCS.
|
||||
// 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
|
||||
|
||||
#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")); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,471 @@
|
||||
#include "net_builtin.h"
|
||||
|
||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_RW612_154) && \
|
||||
MG_ENABLE_DRIVER_RW612_154
|
||||
|
||||
// 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
|
||||
// messages, IMUMC messages carry HDLC framed Spinel. See NXP sources:
|
||||
// mcuxsdk-core drivers/imu/fsl_imu.c, mcux-component
|
||||
// imu_adapter/fsl_adapter_imu.c, connectivity framework
|
||||
// platform/rw61x/fwk_platform_hdlc.c; OpenThread src/lib/spinel/spinel.h
|
||||
|
||||
// IMU message FIFO registers, one block per direction. See fsl_imu.h
|
||||
struct mg_rw612_imu {
|
||||
volatile uint32_t WR_MSG, RD_MSG, STATUS, CNTL, RD_MSG_DBG;
|
||||
};
|
||||
#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_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_WATERMARK(x) ((uint32_t) (x) << 20)
|
||||
#define MGI_IMU_WATERMARK_MASK (15U << 20)
|
||||
|
||||
// IMU message: header, then payload words. See IMU_Msg_t
|
||||
struct mg_rw612_msg {
|
||||
uint8_t type, subtype, seq, len; // len: number of payload words
|
||||
uint32_t payload[8];
|
||||
};
|
||||
|
||||
// Mailbox in SMU2 shared memory, at the address CPU2 firmware expects
|
||||
struct mg_rw612_mbox {
|
||||
uint32_t magic[2]; // Sync pattern: [0] CPU2, [1] us
|
||||
struct mg_rw612_msg q[2][16]; // Message slots: [0] CPU2 -> us, [1] ours
|
||||
};
|
||||
#define MGI_MBOX ((volatile struct mg_rw612_mbox *) 0x443c0000)
|
||||
#define MGI_TXBUF 0x443c1508U // Our IMUMC buffers: 16 x 264 bytes
|
||||
#define MGI_TXBUF_CNT 16
|
||||
#define MGI_TXBUF_LEN 264U
|
||||
#define MGI_SLEEP_FLAG (*(volatile uint32_t *) 0x443c2588)
|
||||
#define MGI_SYNC_MAGIC 0xabcdef89U
|
||||
|
||||
// IMUMC message, carried in IMU payload words. See imumc_std_msg
|
||||
struct mg_rw612_imumc {
|
||||
uint32_t src, dst; // Endpoint addresses
|
||||
uint16_t rfu, idx; // Reserved
|
||||
uint16_t len; // Payload length, bytes
|
||||
uint16_t flags; // Message flags
|
||||
uint32_t data; // Payload buffer address
|
||||
};
|
||||
#define MGI_EPT_LOCAL 10 // Our 802.15.4 endpoint
|
||||
#define MGI_EPT_REMOTE 20 // Firmware 802.15.4 endpoint
|
||||
|
||||
enum { MGI_MSG_CONTROL = 0, MGI_MSG_IMUMC = 8 }; // IMU message types
|
||||
enum { // Control message subtypes
|
||||
MGI_CTL_SYNC = 1,
|
||||
MGI_CTL_SYNC_ACK = 2,
|
||||
MGI_CTL_ACK_FOR_UNLOCK = 3,
|
||||
MGI_CTL_CMD_BUF_ADDR = 6,
|
||||
MGI_CTL_EPT_QUERY = 11,
|
||||
MGI_CTL_EPT_QUERY_RSP = 12,
|
||||
MGI_CTL_BUF_FREE = 13
|
||||
};
|
||||
|
||||
// Spinel commands, properties and statuses we use
|
||||
enum { MGI_CMD_RESET = 1, MGI_CMD_GET = 2, MGI_CMD_SET = 3, MGI_CMD_IS = 6 };
|
||||
enum {
|
||||
MGI_PROP_LAST_STATUS = 0,
|
||||
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
|
||||
};
|
||||
#define MGI_RESET_STACK 2 // RESET argument: reset the RCP stack only
|
||||
#define MGI_STATUS_RESET_MIN 112 // LAST_STATUS: RCP has reset
|
||||
#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
|
||||
|
||||
// Driver state, one radio
|
||||
struct mg_rw612 {
|
||||
bool up; // IMU link synchronised
|
||||
bool ept_ready; // IMUMC endpoints connected
|
||||
bool reset; // RCP reported a reset
|
||||
uint8_t seq; // IMU message sequence number
|
||||
uint16_t free_bufs; // Free IMUMC buffers, bit per buffer
|
||||
uint8_t hdlc[300]; // HDLC frame being received, unescaped
|
||||
size_t hlen; // Bytes in hdlc
|
||||
bool esc; // Previous byte was an escape
|
||||
uint8_t tid; // Spinel transaction ID of the pending request
|
||||
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
|
||||
uint16_t pan_id, short_addr; // Addresses programmed in the RCP
|
||||
uint8_t eui[8];
|
||||
uint8_t channel;
|
||||
};
|
||||
static struct mg_rw612 s_rw612;
|
||||
|
||||
static void mg_rw612_imu_send(uint8_t type, uint8_t subtype,
|
||||
const uint32_t *payload, uint8_t len) {
|
||||
volatile struct mg_rw612_msg *m;
|
||||
uint64_t expire = mg_millis() + 100;
|
||||
uint8_t i;
|
||||
while ((MGI_IMU->STATUS & (MGI_IMU_ALMOST_FULL | MGI_IMU_LOCKED)) &&
|
||||
mg_millis() < expire) {
|
||||
(void) 0;
|
||||
}
|
||||
m = &MGI_MBOX->q[1][MGI_IMU_WR_PTR(MGI_IMU->STATUS)];
|
||||
m->type = type, m->subtype = subtype, m->seq = s_rw612.seq++, m->len = len;
|
||||
for (i = 0; i < len; i++) m->payload[i] = payload[i];
|
||||
MGI_IMU->WR_MSG = (uint32_t) (uintptr_t) m;
|
||||
}
|
||||
|
||||
static void mg_rw612_control(uint8_t subtype, uint32_t a, uint32_t b,
|
||||
uint8_t len) {
|
||||
uint32_t payload[2] = {a, b};
|
||||
mg_rw612_imu_send(MGI_MSG_CONTROL, subtype, payload, len);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Spinel packed unsigned integer: 7 bits per byte, LSB first
|
||||
static size_t mg_rw612_unpack(const uint8_t *p, size_t len, uint32_t *v) {
|
||||
size_t i;
|
||||
*v = 0;
|
||||
for (i = 0; i < len && i < 4; i++) {
|
||||
*v |= (uint32_t) (p[i] & 0x7f) << (7 * i);
|
||||
if ((p[i] & 0x80) == 0) return i + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Handle a received Spinel frame: header, command, property, value
|
||||
static void mg_rw612_spinel(const uint8_t *f, size_t len) {
|
||||
struct mg_rw612 *s = &s_rw612;
|
||||
uint32_t cmd, prop, status = 0;
|
||||
size_t n, ofs = 1;
|
||||
uint8_t tid = f[0] & 15;
|
||||
if (len < 3 || (n = mg_rw612_unpack(f + ofs, len - ofs, &cmd)) == 0) return;
|
||||
ofs += n;
|
||||
if ((n = mg_rw612_unpack(f + ofs, len - ofs, &prop)) == 0) return;
|
||||
ofs += n;
|
||||
if (cmd != MGI_CMD_IS) return;
|
||||
if (prop == MGI_PROP_STREAM_RAW && tid == 0 && len - ofs >= 2) {
|
||||
size_t flen = (size_t) (f[ofs] | (f[ofs + 1] << 8)); // Incl. FCS
|
||||
if (flen >= 2 && ofs + 2 + flen <= len) {
|
||||
mg_rw612_queue_rx(f + ofs + 2, flen - 2);
|
||||
}
|
||||
} else if (prop == MGI_PROP_LAST_STATUS) {
|
||||
mg_rw612_unpack(f + ofs, len - ofs, &status);
|
||||
if (tid == 0 && status >= MGI_STATUS_RESET_MIN &&
|
||||
status <= MGI_STATUS_RESET_MAX) {
|
||||
s->reset = true;
|
||||
}
|
||||
} else if (prop == MGI_PROP_NCP_VERSION) {
|
||||
n = len - ofs < sizeof(s->version) - 1 ? len - ofs : sizeof(s->version) - 1;
|
||||
memcpy(s->version, f + ofs, n);
|
||||
s->version[n] = '\0';
|
||||
}
|
||||
if (tid != 0 && tid == s->tid) s->rsp = true, s->rsp_status = status;
|
||||
}
|
||||
|
||||
// HDLC-lite: 0x7e delimits frames, 0x7d escapes, FCS-16 at the end
|
||||
static uint16_t mg_rw612_fcs(uint16_t fcs, uint8_t c) {
|
||||
int i;
|
||||
fcs ^= c;
|
||||
for (i = 0; i < 8; i++) fcs = (fcs & 1) ? (fcs >> 1) ^ 0x8408 : fcs >> 1;
|
||||
return fcs;
|
||||
}
|
||||
|
||||
static void mg_rw612_hdlc_rx(const uint8_t *p, size_t len) {
|
||||
struct mg_rw612 *s = &s_rw612;
|
||||
size_t i, j;
|
||||
for (i = 0; i < len; i++) {
|
||||
uint8_t c = p[i];
|
||||
if (c == 0x7e) {
|
||||
if (s->hlen >= 3) {
|
||||
uint16_t fcs = 0xffff;
|
||||
for (j = 0; j < s->hlen; j++) fcs = mg_rw612_fcs(fcs, s->hdlc[j]);
|
||||
if (fcs == 0xf0b8) mg_rw612_spinel(s->hdlc, s->hlen - 2);
|
||||
}
|
||||
s->hlen = 0, s->esc = false;
|
||||
} else if (c == 0x7d) {
|
||||
s->esc = true;
|
||||
} else if (s->hlen < sizeof(s->hdlc)) {
|
||||
s->hdlc[s->hlen++] = s->esc ? c ^ 0x20 : c;
|
||||
s->esc = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mg_rw612_control_rx(volatile struct mg_rw612_msg *m) {
|
||||
struct mg_rw612 *s = &s_rw612;
|
||||
switch (m->subtype) {
|
||||
case MGI_CTL_ACK_FOR_UNLOCK:
|
||||
MGI_IMU->CNTL &= ~MGI_IMU_WAIT_FOR_ACK;
|
||||
break;
|
||||
case MGI_CTL_SYNC: // As HAL_ImuCtrlHandler(): acknowledge twice
|
||||
mg_rw612_control(MGI_CTL_SYNC_ACK, 0, 0, 0);
|
||||
mg_rw612_control(MGI_CTL_SYNC_ACK, 0, 0, 0);
|
||||
s->up = true;
|
||||
break;
|
||||
case MGI_CTL_SYNC_ACK:
|
||||
case MGI_CTL_CMD_BUF_ADDR:
|
||||
s->up = true;
|
||||
break;
|
||||
case MGI_CTL_EPT_QUERY: { // Firmware asks if our endpoint exists
|
||||
bool ours = m->payload[0] == MGI_EPT_LOCAL;
|
||||
mg_rw612_control(MGI_CTL_EPT_QUERY_RSP, m->payload[0], ours, 2);
|
||||
if (ours) s->ept_ready = true;
|
||||
break;
|
||||
}
|
||||
case MGI_CTL_EPT_QUERY_RSP:
|
||||
if (m->payload[0] == MGI_EPT_REMOTE && m->payload[1] != 0) {
|
||||
s->ept_ready = true;
|
||||
}
|
||||
break;
|
||||
case MGI_CTL_BUF_FREE: { // Firmware is done with one of our buffers
|
||||
uint32_t i = (m->payload[0] - MGI_TXBUF) / MGI_TXBUF_LEN;
|
||||
if (i < MGI_TXBUF_CNT) s->free_bufs |= (uint16_t) MG_BIT(i);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Process all received IMU messages. See HAL_ImuReceive()
|
||||
static void mg_rw612_poll_imu(void) {
|
||||
bool unlock = false;
|
||||
while ((MGI_IMU_PEER->STATUS & MGI_IMU_EMPTY) == 0) {
|
||||
struct mg_rw612_msg m;
|
||||
volatile struct mg_rw612_msg *p;
|
||||
uint8_t i;
|
||||
MGI_SLEEP_FLAG &= ~1U;
|
||||
p = (volatile struct mg_rw612_msg *) (uintptr_t) MGI_IMU->RD_MSG;
|
||||
if (MGI_IMU_PEER->STATUS & MGI_IMU_LOCKED) unlock = true;
|
||||
if (p == NULL) continue;
|
||||
m.type = p->type, m.subtype = p->subtype, m.len = p->len;
|
||||
for (i = 0; i < 8; i++) m.payload[i] = p->payload[i];
|
||||
if (m.type == MGI_MSG_CONTROL) {
|
||||
mg_rw612_control_rx(&m);
|
||||
} else if (m.type == MGI_MSG_IMUMC) {
|
||||
struct mg_rw612_imumc im;
|
||||
memcpy(&im, m.payload, sizeof(im));
|
||||
if (im.dst == MGI_EPT_LOCAL) {
|
||||
mg_rw612_hdlc_rx((const uint8_t *) (uintptr_t) im.data, im.len);
|
||||
}
|
||||
mg_rw612_control(MGI_CTL_BUF_FREE, im.data, 0, 1);
|
||||
}
|
||||
}
|
||||
MGI_IMU->CNTL |= MGI_IMU_INT_CLR;
|
||||
MGI_SLEEP_FLAG |= 1U;
|
||||
if (unlock) mg_rw612_control(MGI_CTL_ACK_FOR_UNLOCK, 0, 0, 0);
|
||||
}
|
||||
|
||||
// Send bytes to the firmware endpoint, in IMUMC buffer sized chunks
|
||||
static bool mg_rw612_imumc_send(const uint8_t *p, size_t len) {
|
||||
struct mg_rw612 *s = &s_rw612;
|
||||
while (len > 0) {
|
||||
size_t n = len < MGI_TXBUF_LEN ? len : MGI_TXBUF_LEN;
|
||||
uint64_t expire = mg_millis() + 100;
|
||||
struct mg_rw612_imumc im;
|
||||
uint32_t i;
|
||||
while (s->free_bufs == 0) {
|
||||
mg_rw612_poll_imu();
|
||||
if (mg_millis() > expire) return false;
|
||||
}
|
||||
for (i = 0; (s->free_bufs & MG_BIT(i)) == 0; i++) (void) 0;
|
||||
s->free_bufs &= (uint16_t) ~MG_BIT(i);
|
||||
memset(&im, 0, sizeof(im));
|
||||
im.src = MGI_EPT_LOCAL, im.dst = MGI_EPT_REMOTE, im.len = (uint16_t) n;
|
||||
im.data = MGI_TXBUF + i * MGI_TXBUF_LEN;
|
||||
memcpy((void *) (uintptr_t) im.data, p, n);
|
||||
mg_rw612_imu_send(MGI_MSG_IMUMC, 0, (uint32_t *) &im, sizeof(im) / 4);
|
||||
p += n, len -= n;
|
||||
}
|
||||
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) {
|
||||
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;
|
||||
buf[n++] = 0x7e;
|
||||
for (i = 0; i < len + 2 && n + 3 < sizeof(buf); i++) {
|
||||
uint8_t c;
|
||||
if (i < len) {
|
||||
c = f[i], fcs = mg_rw612_fcs(fcs, c);
|
||||
} else {
|
||||
c = (uint8_t) (i == len ? ~fcs & 0xff : ~fcs >> 8);
|
||||
}
|
||||
if (c == 0x7e || c == 0x7d || c == 0x11 || c == 0x13 || c == 0xf8) {
|
||||
buf[n++] = 0x7d, c ^= 0x20;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
static bool mg_rw612_set(uint32_t prop, const uint8_t *val, size_t len) {
|
||||
uint8_t f[16];
|
||||
if (len > sizeof(f) - 3) return false;
|
||||
f[1] = MGI_CMD_SET, f[2] = (uint8_t) prop; // Properties used are < 0x80
|
||||
memcpy(f + 3, val, len);
|
||||
return mg_rw612_request(f, len + 3, MGI_RSP_TIMEOUT_MS) &&
|
||||
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));
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool mg_rw612_154_init(struct mg_tcpip_if *ifp) {
|
||||
struct mg_rw612 *s = &s_rw612;
|
||||
uint8_t f[8], one = 1;
|
||||
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"));
|
||||
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->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);
|
||||
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;
|
||||
mg_rw612_control(MGI_CTL_SYNC, 0, 0, 0);
|
||||
expire = mg_millis() + 1000;
|
||||
while (!s->up && mg_millis() < expire) mg_rw612_poll_imu();
|
||||
if (!s->up) return mg_rw612_fail("IMU sync timeout");
|
||||
expire = mg_millis() + 2000;
|
||||
while (!s->ept_ready && mg_millis() < expire) {
|
||||
uint64_t retry = mg_millis() + 100;
|
||||
mg_rw612_control(MGI_CTL_EPT_QUERY, MGI_EPT_REMOTE, 0, 1);
|
||||
while (!s->ept_ready && mg_millis() < retry) mg_rw612_poll_imu();
|
||||
}
|
||||
if (!s->ept_ready) return mg_rw612_fail("IMUMC endpoint timeout");
|
||||
// Reset the RCP stack, wait for its reset status, read its version
|
||||
f[1] = MGI_CMD_RESET, f[2] = MGI_RESET_STACK;
|
||||
mg_rw612_request(f, 3, MGI_RSP_TIMEOUT_MS);
|
||||
expire = mg_millis() + 1000;
|
||||
while (!s->reset && mg_millis() < expire) mg_rw612_poll_imu();
|
||||
if (!s->reset) return mg_rw612_fail("RCP reset timeout");
|
||||
f[1] = MGI_CMD_GET, f[2] = MGI_PROP_NCP_VERSION;
|
||||
mg_rw612_request(f, 3, MGI_RSP_TIMEOUT_MS);
|
||||
MG_INFO(("RCP: %s", s->version));
|
||||
if (!mg_rw612_set(MGI_PROP_PHY_ENABLED, &one, 1)) {
|
||||
return mg_rw612_fail("PHY enable failed");
|
||||
}
|
||||
mg_rw612_addrs(ifp);
|
||||
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) {
|
||||
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;
|
||||
f[n++] = 0, f[n++] = 0; // FCS, filled in by the radio
|
||||
f[n++] = ifp->l2data.lowpan.channel;
|
||||
f[n++] = 4; // Max CSMA backoffs
|
||||
f[n++] = 3; // Max frame retries, for frames with ACK request
|
||||
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;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
s->rxtail = (uint8_t) ((s->rxtail + 1) % MGI_RXQ_CNT);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static bool mg_rw612_154_poll(struct mg_tcpip_if *ifp, bool s1) {
|
||||
(void) ifp, (void) s1;
|
||||
return s_rw612.up;
|
||||
}
|
||||
|
||||
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};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#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
|
||||
|
||||
#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")); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -56,6 +56,19 @@ 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 *));
|
||||
@@ -71,25 +84,27 @@ 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_pppoe_init, mg_l2_lowpan_init};
|
||||
static const l2_poll_fn l2_poll[] = {mg_l2_eth_poll, mg_l2_ppp_poll,
|
||||
mg_l2_pppoe_poll};
|
||||
mg_l2_pppoe_poll, mg_l2_lowpan_poll};
|
||||
static const l2_header_fn l2_header[] = {mg_l2_eth_header, mg_l2_ppp_header,
|
||||
mg_l2_pppoe_header};
|
||||
mg_l2_pppoe_header,
|
||||
mg_l2_lowpan_header};
|
||||
static const l2_trailer_fn l2_trailer[] = {mg_l2_eth_trailer, mg_l2_ppp_trailer,
|
||||
mg_l2_pppoe_trailer};
|
||||
static const l2_rx_fn l2_rx[] = {mg_l2_eth_rx, mg_l2_ppp_rx, mg_l2_pppoe_rx};
|
||||
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};
|
||||
static const l2_getaddr_fn l2_getaddr[] = {mg_l2_eth_getaddr, mg_l2_ppp_getaddr,
|
||||
mg_l2_ppp_getaddr};
|
||||
mg_l2_ppp_getaddr, mg_l2_lowpan_getaddr};
|
||||
static const l2_mapip_fn l2_mapip[] = {mg_l2_eth_mapip, mg_l2_ppp_mapip,
|
||||
mg_l2_ppp_mapip};
|
||||
mg_l2_ppp_mapip, mg_l2_lowpan_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_ppp_genip6, mg_l2_lowpan_genip6};
|
||||
static const l2_ip6get_fn l2_ip6get[] = {mg_l2_eth_ip6get, mg_l2_ppp_ip6get,
|
||||
mg_l2_ppp_ip6get};
|
||||
mg_l2_ppp_ip6get, mg_l2_lowpan_ip6get};
|
||||
static const l2_ip6put_fn l2_ip6put[] = {mg_l2_eth_ip6put, mg_l2_ppp_ip6put,
|
||||
mg_l2_ppp_ip6put};
|
||||
mg_l2_ppp_ip6put, mg_l2_lowpan_ip6put};
|
||||
#endif
|
||||
|
||||
void mg_l2_init(struct mg_tcpip_if *ifp) {
|
||||
|
||||
@@ -6,7 +6,13 @@
|
||||
#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_USER };
|
||||
enum mg_l2type {
|
||||
MG_TCPIP_L2_ETH = 0,
|
||||
MG_TCPIP_L2_PPP,
|
||||
MG_TCPIP_L2_PPPoE,
|
||||
MG_TCPIP_L2_LOWPAN,
|
||||
MG_TCPIP_L2_USER
|
||||
};
|
||||
|
||||
#if defined(__DCC__)
|
||||
#pragma pack(1)
|
||||
@@ -29,10 +35,27 @@ 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__)
|
||||
@@ -41,7 +64,6 @@ union mg_l2data {
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
|
||||
enum mg_l2proto {
|
||||
MG_TCPIP_L2PROTO_IPV4 = 0,
|
||||
MG_TCPIP_L2PROTO_IPV6,
|
||||
|
||||
+551
@@ -0,0 +1,551 @@
|
||||
#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,7 +211,13 @@ 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) {
|
||||
} else if (mgr->ifp != NULL && mgr->ifp->state != MG_TCPIP_STATE_READY
|
||||
#if MG_ENABLE_IPV6
|
||||
// IPv6-only links: a link-local address is enough for local peers
|
||||
&& (mgr->ifp->state6 < MG_TCPIP_STATE_UP ||
|
||||
mgr->ifp->ip6ll[0] == 0)
|
||||
#endif
|
||||
) {
|
||||
MG_ERROR(("Network is down"));
|
||||
#endif
|
||||
} else if ((c = mg_alloc_conn(mgr)) == NULL) {
|
||||
|
||||
+262
-19
@@ -1,6 +1,7 @@
|
||||
#include "net_builtin.h"
|
||||
#include "bsd.h"
|
||||
#include "profile.h"
|
||||
#include "url.h"
|
||||
|
||||
#if MG_ENABLE_TCPIP
|
||||
#define MG_EPHEMERAL_PORT_BASE 32768
|
||||
@@ -969,7 +970,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_DEBUG(("NDP Router Solicitation sent"));
|
||||
MG_VERBOSE(("NDP Router Solicitation sent"));
|
||||
}
|
||||
|
||||
static void fill_prefix(uint8_t *dst, uint8_t *src, uint8_t len) {
|
||||
@@ -1008,9 +1009,10 @@ 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;
|
||||
uint8_t *prefix = NULL, prefix_len = 0, *dns = NULL;
|
||||
|
||||
if (pkt->pay.len < sizeof(*ra)) return;
|
||||
if (ifp->state6 == MG_TCPIP_STATE_UP) {
|
||||
@@ -1046,11 +1048,21 @@ 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;
|
||||
@@ -1076,6 +1088,208 @@ 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;
|
||||
@@ -1099,6 +1313,9 @@ 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;
|
||||
@@ -1360,9 +1577,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;
|
||||
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
|
||||
// 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;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
@@ -1879,6 +2096,9 @@ 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);
|
||||
@@ -1904,6 +2124,9 @@ 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));
|
||||
@@ -1921,6 +2144,8 @@ 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) {
|
||||
@@ -1930,7 +2155,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_INFO(("IPv6 extension header %d", (int) next));
|
||||
MG_DEBUG(("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));
|
||||
@@ -1963,8 +2188,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_DEBUG(("ICMPv6 %M -> %M len %u", mg_print_ip6, &pkt->ip6->src,
|
||||
mg_print_ip6, &pkt->ip6->dst, (int) pkt->pay.len));
|
||||
MG_VERBOSE(("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);
|
||||
@@ -1974,9 +2199,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_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));
|
||||
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));
|
||||
if (ifp->enable_dhcp6_client && pkt->udp->dport == mg_htons(546)) {
|
||||
pkt->dhcp6 = (struct dhcp6 *) (pkt->udp + 1);
|
||||
mkpay(pkt, pkt->dhcp6 + 1);
|
||||
@@ -1998,9 +2223,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_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));
|
||||
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));
|
||||
rx_tcp(ifp, pkt);
|
||||
} else {
|
||||
MG_DEBUG(("Unknown IPv6 next hdr %x", (int) next));
|
||||
@@ -2078,13 +2303,18 @@ 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 && l2_up && ifp->ip6ll[0] == 0 &&
|
||||
if ((ifp->l2type == MG_TCPIP_L2_ETH || ifp->l2type == MG_TCPIP_L2_LOWPAN) &&
|
||||
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
|
||||
@@ -2276,8 +2506,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->enable_dhcp_client = true;
|
||||
ifp->l2type != MG_TCPIP_L2_PPPoE && ifp->l2type != MG_TCPIP_L2_LOWPAN)
|
||||
ifp->enable_dhcp_client = true; // No IPv4 over 6LoWPAN
|
||||
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
|
||||
@@ -2299,8 +2529,18 @@ 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));
|
||||
@@ -2474,10 +2714,13 @@ 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;
|
||||
if (!c->loc.is_ip6 && (ifp->ip == 0 || ifp->state != MG_TCPIP_STATE_READY)) {
|
||||
// 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)) {
|
||||
proto_error(c, false, "net down");
|
||||
#if MG_ENABLE_IPV6
|
||||
} else if (c->loc.is_ip6 && ifp->state6 != MG_TCPIP_STATE_READY) {
|
||||
} else if (!c->is_resolving && 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)) {
|
||||
|
||||
@@ -44,6 +44,24 @@ 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.
|
||||
@@ -78,6 +96,8 @@ 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
|
||||
@@ -109,6 +129,13 @@ 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
|
||||
};
|
||||
|
||||
@@ -122,6 +149,15 @@ 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
|
||||
@@ -154,6 +190,8 @@ extern struct mg_tcpip_driver mg_tcpip_driver_nxp_wifi;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_st67w6;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_atcmd;
|
||||
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;
|
||||
|
||||
// SPI bus abstraction for SPI-attached network chips (e.g. W5500).
|
||||
// Populate and assign to the driver's driver_data field.
|
||||
|
||||
@@ -243,6 +243,21 @@ 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
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "arch.h"
|
||||
#include "http.h"
|
||||
#include "net.h"
|
||||
|
||||
// Supported OTA targets
|
||||
@@ -43,6 +44,7 @@
|
||||
#define MG_OTA_RT1170 304 // IMXRT1170
|
||||
#define MG_OTA_RT1180 305 // IMXRT1180
|
||||
#define MG_OTA_MCXN 310 // MCXN947
|
||||
#define MG_OTA_MCXW71 311 // MCXW71
|
||||
#define MG_OTA_RW612 320 // FRDM-RW612
|
||||
#define MG_OTA_FLASH 900 // OTA via internal flash
|
||||
#define MG_OTA_ESP32 910 // ESP32 OTA implementation
|
||||
@@ -72,6 +74,19 @@ bool mg_ota_write(const void *buf, size_t len);
|
||||
// Ends the OTA process and commits the written firmware.
|
||||
bool mg_ota_end(void);
|
||||
|
||||
// Push-based OTA: mg_http_stream_body() callback that flashes the request
|
||||
//
|
||||
// ```c
|
||||
// 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");
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
bool mg_http_ota_cb(struct mg_http_message *hm, struct mg_str *data, void **p);
|
||||
|
||||
// MG_OTA_ROLLBACK: swap firmware banks and reset. On flash-based OTA, the
|
||||
// default calls swap_fn() (no-op on single-bank) then resets. Override in
|
||||
// mongoose_config.h for platforms without mg_flash (ESP32, PicoSDK, etc.).
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
#include "flash.h"
|
||||
#include "log.h"
|
||||
#include "ota.h"
|
||||
|
||||
#if MG_OTA == MG_OTA_MCXW71
|
||||
|
||||
// MCXW71SRM, section 11.2: ROM flash API, 8 KB sectors, 16-byte phrases,
|
||||
// 128-byte pages. Only the CM33 application flash is used; RF flash and IFR
|
||||
// (including the write-once ROM configuration) are left alone.
|
||||
struct mg_mcxw71_flash_config {
|
||||
struct {
|
||||
uint32_t base, size, blocks;
|
||||
uint32_t ifr_base, ifr_size;
|
||||
} mem[2];
|
||||
};
|
||||
|
||||
struct mg_mcxw71_flash_api {
|
||||
uint32_t (*init)(struct mg_mcxw71_flash_config *);
|
||||
uint32_t (*erase)(struct mg_mcxw71_flash_config *, void *, uint32_t, uint32_t,
|
||||
uint32_t);
|
||||
uint32_t (*phrase)(struct mg_mcxw71_flash_config *, void *, uint32_t,
|
||||
uint32_t *, uint32_t);
|
||||
uint32_t (*page)(struct mg_mcxw71_flash_config *, void *, uint32_t,
|
||||
uint32_t *, uint32_t);
|
||||
};
|
||||
|
||||
// The flash API pointer is the second entry in the ROM API table.
|
||||
#define MG_MCXW71_FLASH_API \
|
||||
(*(const struct mg_mcxw71_flash_api *const *) 0x14816fe4U)
|
||||
#define MG_MCXW71_ERASE_KEY 0x6b65666cU // FOUR_CHAR_CODE('l', 'f', 'e', 'k')
|
||||
|
||||
static struct mg_mcxw71_flash_config s_mcxw71_config;
|
||||
static size_t s_mcxw71_size, s_mcxw71_received, s_mcxw71_pending;
|
||||
static uint32_t s_mcxw71_buf[4];
|
||||
static bool s_mcxw71_failed;
|
||||
|
||||
static bool mg_mcxw71_write(void *, const void *, size_t);
|
||||
static bool mg_mcxw71_swap(void);
|
||||
static struct mg_flash s_mg_flash_mcxw71 = {
|
||||
(void *) 0, 0x100000, 8192, 16, mg_mcxw71_write, mg_mcxw71_swap};
|
||||
struct mg_flash *mg_flash = &s_mg_flash_mcxw71;
|
||||
|
||||
// Everything called while flash is busy, or after the application has been
|
||||
// erased, must execute from RAM or ROM. Do not log or call libc here.
|
||||
MG_IRAM static bool mg_mcxw71_write(void *addr, const void *buf, size_t len) {
|
||||
uintptr_t dst = (uintptr_t) addr;
|
||||
const volatile uint8_t *src = (const volatile uint8_t *) buf;
|
||||
uint32_t irq, status = 0;
|
||||
if (dst >= s_mg_flash_mcxw71.size || len > s_mg_flash_mcxw71.size - dst ||
|
||||
(dst % 16) != 0 || (len % 16) != 0)
|
||||
return false;
|
||||
|
||||
irq = __get_PRIMASK();
|
||||
__disable_irq();
|
||||
while (len > 0 && status == 0) {
|
||||
uint32_t words[32]; // ROM needs a word-aligned source in RAM
|
||||
volatile uint8_t *p = (volatile uint8_t *) words;
|
||||
size_t i, n = (dst % 128) == 0 && len >= 128 ? 128 : 16;
|
||||
for (i = 0; i < n; i++) p[i] = src[i];
|
||||
|
||||
// Match the MCUXpresso ROM wrappers' cache maintenance. Interrupts stay
|
||||
// masked until the flash command completes and stale cache data is gone.
|
||||
MCM->CPCR2 |= MCM_CPCR2_CCBC_MASK;
|
||||
__DSB();
|
||||
__ISB();
|
||||
if ((dst % 8192) == 0) {
|
||||
status = MG_MCXW71_FLASH_API->erase(
|
||||
&s_mcxw71_config, FMU0, (uint32_t) dst, 8192, MG_MCXW71_ERASE_KEY);
|
||||
}
|
||||
if (status == 0) {
|
||||
if (n == 128) {
|
||||
status = MG_MCXW71_FLASH_API->page(&s_mcxw71_config, FMU0,
|
||||
(uint32_t) dst, words, 128);
|
||||
} else {
|
||||
status = MG_MCXW71_FLASH_API->phrase(&s_mcxw71_config, FMU0,
|
||||
(uint32_t) dst, words, 16);
|
||||
}
|
||||
}
|
||||
MCM->CPCR2 |= MCM_CPCR2_CCBC_MASK;
|
||||
__DSB();
|
||||
__ISB();
|
||||
dst += n, src += n, len -= n;
|
||||
}
|
||||
__set_PRIMASK(irq);
|
||||
return status == 0;
|
||||
}
|
||||
|
||||
static bool mg_mcxw71_swap(void) {
|
||||
// The common helper verifies the staged image before the RAM copy below.
|
||||
// A failed write must never commit, even if all expected bytes arrived.
|
||||
return !s_mcxw71_failed;
|
||||
}
|
||||
|
||||
MG_IRAM static void mg_mcxw71_apply(size_t size) {
|
||||
size_t ofs, half = s_mg_flash_mcxw71.size / 2;
|
||||
__disable_irq();
|
||||
SysTick->CTRL = 0;
|
||||
for (ofs = 0; ofs < size; ofs += 8192) {
|
||||
if (!mg_mcxw71_write((void *) (uintptr_t) ofs,
|
||||
(const void *) (uintptr_t) (half + ofs), 8192)) {
|
||||
// The application may already be erased. Stay in RAM for debugger
|
||||
// recovery rather than return to partially overwritten flash.
|
||||
for (;;) __NOP();
|
||||
}
|
||||
}
|
||||
__DSB();
|
||||
SCB->AIRCR = 0x05fa0004U;
|
||||
__DSB();
|
||||
for (;;) __NOP();
|
||||
}
|
||||
|
||||
bool mg_ota_begin(size_t new_firmware_size) {
|
||||
uint32_t status;
|
||||
if (s_mcxw71_size != 0 || new_firmware_size == 0) return false;
|
||||
status = MG_MCXW71_FLASH_API->init(&s_mcxw71_config);
|
||||
if (status != 0 || s_mcxw71_config.mem[0].base != 0 ||
|
||||
s_mcxw71_config.mem[0].size < 16384 ||
|
||||
(s_mcxw71_config.mem[0].size % 16384) != 0) {
|
||||
MG_ERROR(("Flash init failed: %lu", (unsigned long) status));
|
||||
return false;
|
||||
}
|
||||
s_mg_flash_mcxw71.size = s_mcxw71_config.mem[0].size;
|
||||
if (!mg_ota_flash_begin(new_firmware_size, &s_mg_flash_mcxw71)) return false;
|
||||
s_mcxw71_size = new_firmware_size;
|
||||
s_mcxw71_received = s_mcxw71_pending = 0;
|
||||
s_mcxw71_failed = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mg_ota_write(const void *buf, size_t len) {
|
||||
const uint8_t *p = (const uint8_t *) buf;
|
||||
uint8_t *pending = (uint8_t *) s_mcxw71_buf;
|
||||
if (s_mcxw71_size == 0 || s_mcxw71_failed) return false;
|
||||
if (len > s_mcxw71_size - s_mcxw71_received || (len > 0 && buf == NULL)) {
|
||||
s_mcxw71_failed = true;
|
||||
return false;
|
||||
}
|
||||
s_mcxw71_received += len;
|
||||
if (s_mcxw71_pending > 0) {
|
||||
size_t n = 16 - s_mcxw71_pending;
|
||||
if (n > len) n = len;
|
||||
if (n > 0) memcpy(pending + s_mcxw71_pending, p, n);
|
||||
s_mcxw71_pending += n;
|
||||
if (n > 0) p += n;
|
||||
len -= n;
|
||||
if (s_mcxw71_pending == 16) {
|
||||
s_mcxw71_failed = !mg_ota_flash_write(pending, 16, &s_mg_flash_mcxw71);
|
||||
s_mcxw71_pending = 0;
|
||||
}
|
||||
}
|
||||
if (!s_mcxw71_failed && len >= 16) {
|
||||
size_t n = MG_ROUND_DOWN(len, 16);
|
||||
s_mcxw71_failed = !mg_ota_flash_write(p, n, &s_mg_flash_mcxw71);
|
||||
p += n, len -= n;
|
||||
}
|
||||
if (!s_mcxw71_failed && len > 0) {
|
||||
memcpy(pending + s_mcxw71_pending, p, len);
|
||||
s_mcxw71_pending += len;
|
||||
}
|
||||
return !s_mcxw71_failed;
|
||||
}
|
||||
|
||||
bool mg_ota_end(void) {
|
||||
bool ok;
|
||||
size_t size = s_mcxw71_size;
|
||||
if (size == 0) return false;
|
||||
if (s_mcxw71_received != size) s_mcxw71_failed = true;
|
||||
if (!s_mcxw71_failed && s_mcxw71_pending > 0) {
|
||||
s_mcxw71_failed =
|
||||
!mg_ota_flash_write(s_mcxw71_buf, s_mcxw71_pending, &s_mg_flash_mcxw71);
|
||||
}
|
||||
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);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -138,6 +138,10 @@ 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/documentation/#build-options
|
||||
// See https://mongoose.ws/docs/getting-started/build-environment/
|
||||
|
||||
#define MG_ENABLE_TCPIP 1
|
||||
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
MGDIR = ../../../..
|
||||
|
||||
CFLAGS = -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion
|
||||
CFLAGS = -W -Wall -Wextra -Werror -Wundef -Wshadow
|
||||
CFLAGS += -Wformat-truncation -fno-common -Wno-conversion -Wno-sign-conversion
|
||||
CFLAGS += -g3 -Os -ffunction-sections -fdata-sections
|
||||
CFLAGS += -I. -I$(MGDIR) -Icmsis_core/CMSIS/Core/Include
|
||||
@@ -10,9 +10,11 @@ CFLAGS += -Icmsis_mcxw71/MCXW/MCXW716C -Icmsis_mcxw71/MCXW/periph2
|
||||
CFLAGS += -DCPU_MCXW716CMFTA
|
||||
CFLAGS += -D__START=main -D__STARTUP_CLEAR_BSS # For startup_MCXW716C.S
|
||||
CFLAGS += -mcpu=cortex-m33 -mthumb -mfloat-abi=hard -mfpu=fpv5-sp-d16 $(CFLAGS_EXTRA)
|
||||
LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs=nano.specs -lc -lgcc -Wl,--gc-sections
|
||||
|
||||
LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs=nano.specs -lc -lm -lgcc -Wl,--gc-sections
|
||||
|
||||
SOURCES = main.c hal.c $(MGDIR)/mongoose.c
|
||||
SOURCES += $(MGDIR)/tutorials/mqtt/mqtt-client/mongoose_mqtt.c
|
||||
SOURCES += cmsis_mcxw71/MCXW/MCXW716C/gcc/startup_MCXW716C.S
|
||||
|
||||
all build: firmware.bin
|
||||
|
||||
@@ -2,6 +2,61 @@
|
||||
// All rights reserved
|
||||
|
||||
#include "hal.h"
|
||||
#include "mongoose.h"
|
||||
|
||||
// Initialise the transceiver the way NXP XCVR_Init() does it: start the RF
|
||||
// oscillator, wait for the radio to wake up, enable its clocks and load the
|
||||
// 802.15.4 register setup. Then select the legacy 802.15.4 demodulator and
|
||||
// CCA from RX digital. See PhyHwInit() in NXP ieee_802_15_4 PHY sources,
|
||||
// PhyPacketProcessor.c
|
||||
bool hal_radio_init(void) {
|
||||
uint32_t stat = RFMC_RF2P4GHZ_STAT_RST_STAT_MASK |
|
||||
RFMC_RF2P4GHZ_STAT_LP_ACK_STAT_MASK;
|
||||
size_t i, n = 1000000;
|
||||
RFMC->CTRL &= ~RFMC_CTRL_RFMC_RST_MASK; // RFMC out of reset
|
||||
RFMC->RF2P4GHZ_CTRL |= RFMC_RF2P4GHZ_CTRL_XO_EN_MASK; // Start RF oscillator
|
||||
RFMC->RF2P4GHZ_CTRL &=
|
||||
~(RFMC_RF2P4GHZ_CTRL_LP_ENTER_MASK | RFMC_RF2P4GHZ_CTRL_RF_POR_MASK |
|
||||
RFMC_RF2P4GHZ_CTRL_RST_MASK); // Release radio reset, no low power
|
||||
while ((RFMC->RF2P4GHZ_STAT & stat) != 0 ||
|
||||
(RFMC->XO_STAT & RFMC_XO_STAT_XTAL_RDY_MASK) == 0) {
|
||||
if (--n == 0) return false; // Radio did not wake up
|
||||
}
|
||||
RADIO_CTRL->LL_CTRL = RADIO_CTRL_LL_CTRL_ACTIVE_LL_MASK;
|
||||
RFMC->RF2P4GHZ_CTRL |=
|
||||
RFMC_RF2P4GHZ_CTRL_BLE_LP_EN_MASK | RFMC_RF2P4GHZ_CTRL_BLE_WKUP_MASK;
|
||||
for (i = 0; i < sizeof(s_radio_regs) / sizeof(s_radio_regs[0]); i++) {
|
||||
*s_radio_regs[i].reg = s_radio_regs[i].val;
|
||||
}
|
||||
XCVR_MISC->XCVR_CTRL &= ~XCVR_MISC_XCVR_CTRL_DEMOD_SEL_MASK;
|
||||
XCVR_MISC->XCVR_CTRL |= XCVR_MISC_XCVR_CTRL_DEMOD_SEL(2);
|
||||
XCVR_ZBDEMOD->CCA_LQI_SRC |= XCVR_ZBDEMOD_CCA_LQI_SRC_CCA1_FROM_RX_DIG(1);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Random bytes for Mongoose, MG_ENABLE_CUSTOM_RANDOM. Entropy is radio noise:
|
||||
// receiver RSSI, sampled while the radio receives, mixed with SysTick by
|
||||
// SHA-256. Good enough for random addresses and nonces, not a certified TRNG
|
||||
bool mg_random(void *buf, size_t len) {
|
||||
static uint32_t counter;
|
||||
uint8_t digest[32], *p = (uint8_t *) buf;
|
||||
size_t i, n;
|
||||
while (len > 0) {
|
||||
mg_sha256_ctx ctx;
|
||||
mg_sha256_init(&ctx);
|
||||
counter++;
|
||||
mg_sha256_update(&ctx, (uint8_t *) &counter, sizeof(counter));
|
||||
for (i = 0; i < 64; i++) {
|
||||
uint32_t sample[2] = {XCVR_RX_DIG->NB_RSSI_RES0, SysTick->VAL};
|
||||
mg_sha256_update(&ctx, (uint8_t *) sample, sizeof(sample));
|
||||
for (n = 0; n < 50; n++) __NOP(); // Let the RSSI update
|
||||
}
|
||||
mg_sha256_final(digest, &ctx);
|
||||
n = len < sizeof(digest) ? len : sizeof(digest);
|
||||
memcpy(p, digest, n), p += n, len -= n;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
@@ -13,13 +13,18 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
uint64_t hal_get_tick(void);
|
||||
bool hal_timer_expired(volatile uint64_t *t, uint64_t period, uint64_t now);
|
||||
size_t hal_ram_used(void);
|
||||
size_t hal_ram_free(void);
|
||||
bool hal_radio_init(void); // Set up the 2.4 GHz transceiver for 802.15.4
|
||||
extern uint32_t SystemCoreClock; // CPU clock, Hz. Set by hal_clock_init()
|
||||
|
||||
|
||||
#define BIT(x) (1UL << (x))
|
||||
#define CLRSET(reg, clear, set) ((reg) = ((reg) & ~(clear)) | (set))
|
||||
|
||||
#define SIRC_FREQUENCY 6000000UL // Slow IRC (FRO6M), Hz
|
||||
|
||||
extern uint32_t SystemCoreClock; // CPU clock, Hz. Set by hal_clock_init()
|
||||
|
||||
// MRCC peripheral clock source, MRCC_xx[MUX], see fsl_clock.h
|
||||
enum { MRCC_SRC_FRO6M = 2, MRCC_SRC_FRO192M = 3 };
|
||||
|
||||
@@ -30,10 +35,43 @@ static inline uint32_t hal_firc_freq(void) {
|
||||
return freqs[SCG0->FIRCCFG & SCG_FIRCCFG_RANGE_MASK];
|
||||
}
|
||||
|
||||
// Work out the CPU clock from the SCG clock source and core divider. The
|
||||
// clock is left as configured at reset
|
||||
// Switch the system clock: source (2 SIRC, 3 FIRC), core and slow dividers
|
||||
// as divider - 1. Wait until the switch is done. See CLOCK_SetRunModeSysClkConfig
|
||||
static inline void hal_set_sysclk(uint32_t src, uint32_t divcore,
|
||||
uint32_t divslow) {
|
||||
SCG0->RCCR = SCG_RCCR_SCS(src) | SCG_RCCR_DIVCORE(divcore) |
|
||||
SCG_RCCR_DIVSLOW(divslow);
|
||||
while (((SCG0->CSR & SCG_CSR_SCS_MASK) >> SCG_CSR_SCS_SHIFT) != src) (void) 0;
|
||||
}
|
||||
|
||||
// Run the core at 96 MHz from FIRC, slow clock 24 MHz. Order as in NXP
|
||||
// BOARD_BootClockRUN(), clock_config.c: raise the core voltage and memory
|
||||
// timing first, then the clock. FIRC can only be changed when not in use
|
||||
static inline void hal_clock_96mhz(void) {
|
||||
while (SPC0->SC & SPC_SC_BUSY_MASK) (void) 0;
|
||||
CLRSET(SPC0->ACTIVE_CFG, SPC_ACTIVE_CFG_CORELDO_VDD_LVL_MASK,
|
||||
SPC_ACTIVE_CFG_CORELDO_VDD_LVL(2)); // Normal voltage
|
||||
while (SPC0->SC & SPC_SC_BUSY_MASK) (void) 0;
|
||||
CLRSET(FMU0->FCTRL, FMU_FCTRL_RWSC_MASK, FMU_FCTRL_RWSC(2)); // Wait states
|
||||
CLRSET(SPC0->SRAMCTL, SPC_SRAMCTL_VSM_MASK, SPC_SRAMCTL_VSM(2)); // 1.1 V
|
||||
SPC0->SRAMCTL |= SPC_SRAMCTL_REQ_MASK;
|
||||
while ((SPC0->SRAMCTL & SPC_SRAMCTL_ACK_MASK) == 0) (void) 0;
|
||||
SPC0->SRAMCTL &= ~SPC_SRAMCTL_REQ_MASK;
|
||||
hal_set_sysclk(2, 0, 3); // Run from SIRC meanwhile
|
||||
SCG0->FIRCCSR &= ~SCG_FIRCCSR_LK_MASK; // Unlock
|
||||
SCG0->FIRCCSR = SCG_FIRCCSR_FIRCERR_MASK; // Disable, clear error
|
||||
SCG0->FIRCCFG = SCG_FIRCCFG_RANGE(2); // 96 MHz
|
||||
SCG0->FIRCCSR |= SCG_FIRCCSR_FIRCEN_MASK;
|
||||
while ((SCG0->FIRCCSR & SCG_FIRCCSR_FIRCVLD_MASK) == 0) (void) 0;
|
||||
hal_set_sysclk(3, 0, 3); // FIRC, core / 1, slow / 4
|
||||
}
|
||||
|
||||
// Set the CPU clock to 96 MHz, then work out SystemCoreClock from the SCG
|
||||
// clock source and core divider
|
||||
static inline void hal_clock_init(void) {
|
||||
uint32_t csr = SCG0->CSR, freq = 0;
|
||||
uint32_t csr, freq = 0;
|
||||
hal_clock_96mhz();
|
||||
csr = SCG0->CSR;
|
||||
uint32_t src = (csr & SCG_CSR_SCS_MASK) >> SCG_CSR_SCS_SHIFT;
|
||||
uint32_t div = ((csr & SCG_CSR_DIVCORE_MASK) >> SCG_CSR_DIVCORE_SHIFT) + 1;
|
||||
if (src == 2) freq = SIRC_FREQUENCY; // Slow IRC
|
||||
@@ -108,7 +146,273 @@ static inline void hal_sys_init(void) {
|
||||
hal_wdog_disable();
|
||||
}
|
||||
|
||||
uint64_t hal_get_tick(void);
|
||||
bool hal_timer_expired(volatile uint64_t *t, uint64_t period, uint64_t now);
|
||||
size_t hal_ram_used(void);
|
||||
size_t hal_ram_free(void);
|
||||
// 802.15.4 (O-QPSK 250 kbps) radio register setup for the MCXW71 transceiver.
|
||||
// Values are taken from the NXP XCVR driver, release/25.06.00, configs/gen45:
|
||||
// XCVR_RadioGenRegSetup() and XCVR_RadioGenRBMESetup() with
|
||||
// xcvr_oqpsk_802p15p4_250kbps_full_config and xcvr_ble_uncoded_config.
|
||||
// https://github.com/nxp-mcuxpresso/mcuxsdk-middleware-xcvr
|
||||
// Copyright 2016 Freescale Semiconductor, Inc.
|
||||
// Copyright 2018-2023 NXP
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// One radio register write
|
||||
struct w71_radio_reg {
|
||||
volatile uint32_t *reg; // Register address
|
||||
uint32_t val; // Value to write
|
||||
};
|
||||
|
||||
// Register writes, in the order the NXP driver performs them
|
||||
static const struct w71_radio_reg s_radio_regs[] = {
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[0].DMD_WAVE_REG0, 0x0420b3d3},
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[0].DMD_WAVE_REG1, 0x31d78f00},
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[0].DMD_WAVE_REG2, 0x00025a6d},
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[1].DMD_WAVE_REG0, 0x0420b3d3},
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[1].DMD_WAVE_REG1, 0x38d78f00},
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[1].DMD_WAVE_REG2, 0x0000403c},
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[2].DMD_WAVE_REG0, 0x3c00420b},
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[2].DMD_WAVE_REG1, 0x082c8100},
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[2].DMD_WAVE_REG2, 0x0003c004},
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[3].DMD_WAVE_REG0, 0x3c00420b},
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[3].DMD_WAVE_REG1, 0x0f2c8100},
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[3].DMD_WAVE_REG2, 0x0001b5d3},
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[4].DMD_WAVE_REG0, 0x0403ce35},
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[4].DMD_WAVE_REG1, 0x31d78f00},
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[4].DMD_WAVE_REG2, 0x00025a6d},
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[5].DMD_WAVE_REG0, 0x0403ce35},
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[5].DMD_WAVE_REG1, 0x38d78f00},
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[5].DMD_WAVE_REG2, 0x0000403c},
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[6].DMD_WAVE_REG0, 0x3ce35c6d},
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[6].DMD_WAVE_REG1, 0x082c8100},
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[6].DMD_WAVE_REG2, 0x0003c004},
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[7].DMD_WAVE_REG0, 0x3ce35c6d},
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[7].DMD_WAVE_REG1, 0x0f2c8100},
|
||||
{&XCVR_2P4GHZ_PHY->DEMOD_WAVE[7].DMD_WAVE_REG2, 0x0001b5d3},
|
||||
{&XCVR_2P4GHZ_PHY->DMD_CTRL0, 0x6d940101},
|
||||
{&XCVR_2P4GHZ_PHY->DMD_CTRL1, 0xa8049008},
|
||||
{&XCVR_2P4GHZ_PHY->FSK_CFG0, 0x15001502},
|
||||
{&XCVR_2P4GHZ_PHY->FSK_CFG1, 0x0f080020},
|
||||
{&XCVR_2P4GHZ_PHY->FSK_CFG2, 0x40000000},
|
||||
{&XCVR_2P4GHZ_PHY->FSK_FAD_CFG, 0x00000000},
|
||||
{&XCVR_2P4GHZ_PHY->FSK_FAD_CTRL, 0x00000000},
|
||||
{&XCVR_2P4GHZ_PHY->FSK_PD_CFG0, 0x0000ff0c},
|
||||
{&XCVR_2P4GHZ_PHY->FSK_PD_CFG1, 0x00000055},
|
||||
{&XCVR_2P4GHZ_PHY->FSK_PD_CFG2, 0x00f600f6},
|
||||
{&XCVR_2P4GHZ_PHY->FSK_PD_PH[0], 0x00030503},
|
||||
{&XCVR_2P4GHZ_PHY->FSK_PD_PH[1], 0x003d3b3d},
|
||||
{&XCVR_2P4GHZ_PHY->FSK_PT, 0x00000048},
|
||||
{&XCVR_2P4GHZ_PHY->LR_AA_CFG, 0x06020696},
|
||||
{&XCVR_2P4GHZ_PHY->LR_PD_CFG, 0x0001019a},
|
||||
{&XCVR_2P4GHZ_PHY->LR_PD_PH[0], 0x3d383028},
|
||||
{&XCVR_2P4GHZ_PHY->LR_PD_PH[1], 0x20283038},
|
||||
{&XCVR_2P4GHZ_PHY->LR_PD_PH[2], 0x03081018},
|
||||
{&XCVR_2P4GHZ_PHY->LR_PD_PH[3], 0x20181008},
|
||||
{&XCVR_2P4GHZ_PHY->MISC, 0x03ff00a0},
|
||||
{&XCVR_2P4GHZ_PHY->PREPHY_MISC, 0x00000c10},
|
||||
{&XCVR_2P4GHZ_PHY->RTT_CTRL, 0x00004166},
|
||||
{&XCVR_2P4GHZ_PHY->RTT_REF, 0x00806348},
|
||||
{&XCVR_2P4GHZ_PHY->SM_CFG, 0x00a02432},
|
||||
{&RADIO_CTRL->LL_CTRL, 0x00000001},
|
||||
{&XCVR_ANALOG->LDO_0, 0x04440400},
|
||||
{&XCVR_ANALOG->LDO_1, 0x00000005},
|
||||
{&XCVR_ANALOG->PLL, 0x00000040},
|
||||
{&XCVR_ANALOG->RX_0, 0x01000002},
|
||||
{&XCVR_ANALOG->RX_1, 0x00022008},
|
||||
{&XCVR_ANALOG->TX_DAC_PA, 0x01000000},
|
||||
{&XCVR_ANALOG->XO_DIST, 0x00000000},
|
||||
{&XCVR_MISC->IPS_FO_ADDR[0], 0x00000370},
|
||||
{&XCVR_MISC->IPS_FO_ADDR[1], 0x0000002c},
|
||||
{&XCVR_MISC->IPS_FO_DRS0_DATA[0], 0x00000000},
|
||||
{&XCVR_MISC->IPS_FO_DRS0_DATA[1], 0x00000000},
|
||||
{&XCVR_MISC->IPS_FO_DRS1_DATA[0], 0x00000000},
|
||||
{&XCVR_MISC->IPS_FO_DRS1_DATA[1], 0x00000000},
|
||||
{&XCVR_MISC->LDO_TRIM_0, 0x00020000},
|
||||
{&XCVR_MISC->LDO_TRIM_1, 0x00000000},
|
||||
{&XCVR_MISC->XCVR_CTRL, 0x00130180},
|
||||
{&XCVR_PLL_DIG->CHAN_MAP, 0x00040000},
|
||||
{&XCVR_PLL_DIG->CHAN_MAP_EXT, 0x00100000},
|
||||
{&XCVR_PLL_DIG->DATA_RATE_OVRD_CTRL1, 0x00000089},
|
||||
{&XCVR_PLL_DIG->DATA_RATE_OVRD_CTRL2, 0x00180000},
|
||||
{&XCVR_PLL_DIG->DELAY_MATCH, 0x00050102},
|
||||
{&XCVR_PLL_DIG->HPMCAL_CTRL, 0x00002000},
|
||||
{&XCVR_PLL_DIG->HPM_BUMP, 0x00440144},
|
||||
{&XCVR_PLL_DIG->HPM_CAL_TIMING, 0x00000000},
|
||||
{&XCVR_PLL_DIG->HPM_CTRL, 0x00840000},
|
||||
{&XCVR_PLL_DIG->HPM_SDM_RES, 0x00ff0000},
|
||||
{&XCVR_PLL_DIG->LOCK_DETECT, 0x0e1a1800},
|
||||
{&XCVR_PLL_DIG->LPM_CTRL, 0x88880a00},
|
||||
{&XCVR_PLL_DIG->LPM_SDM_CTRL1, 0x00260000},
|
||||
{&XCVR_PLL_DIG->MOD_CTRL, 0x80000000},
|
||||
{&XCVR_PLL_DIG->PLL_DATARATE_CTRL, 0x04126644},
|
||||
{&XCVR_PLL_DIG->TUNING_CAP_RX_CTRL, 0x00000000},
|
||||
{&XCVR_PLL_DIG->TUNING_CAP_TX_CTRL, 0x00ffffa3},
|
||||
{&XCVR_RX_DIG->ACQ_FILT_0_3, 0x737f0502},
|
||||
{&XCVR_RX_DIG->ACQ_FILT_0_3_DRS, 0x0f7f3a01},
|
||||
{&XCVR_RX_DIG->ACQ_FILT_10_11, 0x00ef0061},
|
||||
{&XCVR_RX_DIG->ACQ_FILT_10_11_DRS, 0x012b0021},
|
||||
{&XCVR_RX_DIG->ACQ_FILT_4_7, 0xf8221478},
|
||||
{&XCVR_RX_DIG->ACQ_FILT_4_7_DRS, 0x3c06617f},
|
||||
{&XCVR_RX_DIG->ACQ_FILT_8_9, 0x01d501bc},
|
||||
{&XCVR_RX_DIG->ACQ_FILT_8_9_DRS, 0x019401f3},
|
||||
{&XCVR_RX_DIG->AGC_CTRL, 0x587c8d19},
|
||||
{&XCVR_RX_DIG->AGC_CTRL_STAT, 0x0000872c},
|
||||
{&XCVR_RX_DIG->AGC_IDX0_GAIN_CFG, 0x000000f2},
|
||||
{&XCVR_RX_DIG->AGC_IDX0_GAIN_VAL, 0x000003fd},
|
||||
{&XCVR_RX_DIG->AGC_IDX0_THR, 0x0000001e},
|
||||
{&XCVR_RX_DIG->AGC_IDX10_GAIN_CFG, 0x00004a03},
|
||||
{&XCVR_RX_DIG->AGC_IDX10_GAIN_VAL, 0x000000eb},
|
||||
{&XCVR_RX_DIG->AGC_IDX10_THR, 0x0096001e},
|
||||
{&XCVR_RX_DIG->AGC_IDX11_GAIN_CFG, 0x00007e03},
|
||||
{&XCVR_RX_DIG->AGC_IDX11_GAIN_VAL, 0x000000fa},
|
||||
{&XCVR_RX_DIG->AGC_IDX11_THR, 0x00780000},
|
||||
{&XCVR_RX_DIG->AGC_IDX1_GAIN_CFG, 0x000001b2},
|
||||
{&XCVR_RX_DIG->AGC_IDX1_GAIN_VAL, 0x0000000b},
|
||||
{&XCVR_RX_DIG->AGC_IDX1_THR, 0x015e001e},
|
||||
{&XCVR_RX_DIG->AGC_IDX2_GAIN_CFG, 0x00000123},
|
||||
{&XCVR_RX_DIG->AGC_IDX2_GAIN_VAL, 0x00000026},
|
||||
{&XCVR_RX_DIG->AGC_IDX2_THR, 0x00c8001e},
|
||||
{&XCVR_RX_DIG->AGC_IDX3_GAIN_CFG, 0x00000113},
|
||||
{&XCVR_RX_DIG->AGC_IDX3_GAIN_VAL, 0x0000003c},
|
||||
{&XCVR_RX_DIG->AGC_IDX3_THR, 0x00c8001e},
|
||||
{&XCVR_RX_DIG->AGC_IDX4_GAIN_CFG, 0x00000083},
|
||||
{&XCVR_RX_DIG->AGC_IDX4_GAIN_VAL, 0x00000057},
|
||||
{&XCVR_RX_DIG->AGC_IDX4_THR, 0x00c8001e},
|
||||
{&XCVR_RX_DIG->AGC_IDX5_GAIN_CFG, 0x00000183},
|
||||
{&XCVR_RX_DIG->AGC_IDX5_GAIN_VAL, 0x0000006d},
|
||||
{&XCVR_RX_DIG->AGC_IDX5_THR, 0x00fa001e},
|
||||
{&XCVR_RX_DIG->AGC_IDX6_GAIN_CFG, 0x00000203},
|
||||
{&XCVR_RX_DIG->AGC_IDX6_GAIN_VAL, 0x00000087},
|
||||
{&XCVR_RX_DIG->AGC_IDX6_THR, 0x012c001e},
|
||||
{&XCVR_RX_DIG->AGC_IDX7_GAIN_CFG, 0x00000603},
|
||||
{&XCVR_RX_DIG->AGC_IDX7_GAIN_VAL, 0x000000a0},
|
||||
{&XCVR_RX_DIG->AGC_IDX7_THR, 0x012c001e},
|
||||
{&XCVR_RX_DIG->AGC_IDX8_GAIN_CFG, 0x00000e03},
|
||||
{&XCVR_RX_DIG->AGC_IDX8_GAIN_VAL, 0x000000b7},
|
||||
{&XCVR_RX_DIG->AGC_IDX8_THR, 0x012c001e},
|
||||
{&XCVR_RX_DIG->AGC_IDX9_GAIN_CFG, 0x00002203},
|
||||
{&XCVR_RX_DIG->AGC_IDX9_GAIN_VAL, 0x000000d2},
|
||||
{&XCVR_RX_DIG->AGC_IDX9_THR, 0x0096001e},
|
||||
{&XCVR_RX_DIG->AGC_MIS_GAIN_CFG, 0x00000001},
|
||||
{&XCVR_RX_DIG->AGC_THR_FAST, 0x0190000f},
|
||||
{&XCVR_RX_DIG->AGC_THR_FAST_DRS, 0x0190001e},
|
||||
{&XCVR_RX_DIG->AGC_THR_MIS, 0x00960028},
|
||||
{&XCVR_RX_DIG->AGC_TIMING0, 0x04102398},
|
||||
{&XCVR_RX_DIG->AGC_TIMING0_DRS, 0x40002000},
|
||||
{&XCVR_RX_DIG->AGC_TIMING1, 0x190242a7},
|
||||
{&XCVR_RX_DIG->AGC_TIMING1_DRS, 0x000002a7},
|
||||
{&XCVR_RX_DIG->AGC_TIMING2, 0xe000007f},
|
||||
{&XCVR_RX_DIG->AGC_TIMING2_DRS, 0x0000007f},
|
||||
{&XCVR_RX_DIG->CTRL0, 0x68063083},
|
||||
{&XCVR_RX_DIG->CTRL0_DRS, 0x000030c0},
|
||||
{&XCVR_RX_DIG->CTRL1, 0x00000180},
|
||||
{&XCVR_RX_DIG->DCOC_CTRL0, 0x04332d28},
|
||||
{&XCVR_RX_DIG->DCOC_CTRL0_DRS, 0x00000114},
|
||||
{&XCVR_RX_DIG->DCOC_CTRL1, 0x00000000},
|
||||
{&XCVR_RX_DIG->DEMOD_FILT_0_1, 0x01fe01fd},
|
||||
{&XCVR_RX_DIG->DEMOD_FILT_0_1_DRS, 0x00ff0003},
|
||||
{&XCVR_RX_DIG->DEMOD_FILT_2_4, 0x0ba20824},
|
||||
{&XCVR_RX_DIG->DEMOD_FILT_2_4_DRS, 0x124221ff},
|
||||
{&XCVR_RX_DIG->NB_RSSI_CTRL0, 0xed472234},
|
||||
{&XCVR_RX_DIG->NB_RSSI_CTRL1, 0x00000000},
|
||||
{&XCVR_RX_DIG->RCCAL_CTRL0, 0x0000003a},
|
||||
{&XCVR_RX_DIG->RSSI_GLOBAL_CTRL, 0x80008330},
|
||||
{&XCVR_RX_DIG->WB_RSSI_CTRL, 0x00223342},
|
||||
{&XCVR_TSM->CTRL, 0xff000000},
|
||||
{&XCVR_TSM->END_OF_SEQ, 0x5c5a7270},
|
||||
{&XCVR_TSM->FAST_CTRL1, 0x24006f00},
|
||||
{&XCVR_TSM->FAST_CTRL2, 0x66284208},
|
||||
{&XCVR_TSM->FAST_CTRL3, 0x18001800},
|
||||
{&XCVR_TSM->RECYCLE_COUNT, 0x00001258},
|
||||
{&XCVR_TSM->TIMING00, 0xffffffff},
|
||||
{&XCVR_TSM->TIMING01, 0xffffffff},
|
||||
{&XCVR_TSM->TIMING02, 0xffffffff},
|
||||
{&XCVR_TSM->TIMING03, 0xffffffff},
|
||||
{&XCVR_TSM->TIMING04, 0xffffffff},
|
||||
{&XCVR_TSM->TIMING05, 0xffffffff},
|
||||
{&XCVR_TSM->TIMING06, 0xffffffff},
|
||||
{&XCVR_TSM->TIMING07, 0xffffffff},
|
||||
{&XCVR_TSM->TIMING08, 0xffffffff},
|
||||
{&XCVR_TSM->TIMING09, 0x5700ffff},
|
||||
{&XCVR_TSM->TIMING10, 0x11081108},
|
||||
{&XCVR_TSM->TIMING11, 0x5b137113},
|
||||
{&XCVR_TSM->TIMING12, 0x5b19714f},
|
||||
{&XCVR_TSM->TIMING13, 0x572dffff},
|
||||
{&XCVR_TSM->TIMING14, 0x0000716f},
|
||||
{&XCVR_TSM->TIMING15, 0x5b58716f},
|
||||
{&XCVR_TSM->TIMING16, 0x59580000},
|
||||
{&XCVR_TSM->TIMING17, 0x5b580000},
|
||||
{&XCVR_TSM->TIMING18, 0x5b580000},
|
||||
{&XCVR_TSM->TIMING19, 0x11061106},
|
||||
{&XCVR_TSM->TIMING20, 0x11001100},
|
||||
{&XCVR_TSM->TIMING21, 0x19001100},
|
||||
{&XCVR_TSM->TIMING22, 0x58006f00},
|
||||
{&XCVR_TSM->TIMING23, 0x58006f00},
|
||||
{&XCVR_TSM->TIMING24, 0x5b007100},
|
||||
{&XCVR_TSM->TIMING25, 0x5c007200},
|
||||
{&XCVR_TSM->TIMING26, 0x5c007200},
|
||||
{&XCVR_TSM->TIMING27, 0x5c007200},
|
||||
{&XCVR_TSM->TIMING28, 0x5c007200},
|
||||
{&XCVR_TSM->TIMING29, 0xffff7200},
|
||||
{&XCVR_TSM->TIMING30, 0x5c00ffff},
|
||||
{&XCVR_TSM->TIMING31, 0x5b087108},
|
||||
{&XCVR_TSM->TIMING32, 0x1911ffff},
|
||||
{&XCVR_TSM->TIMING33, 0x23115a11},
|
||||
{&XCVR_TSM->TIMING34, 0x5b117111},
|
||||
{&XCVR_TSM->TIMING35, 0x5b117111},
|
||||
{&XCVR_TSM->TIMING36, 0x5b117111},
|
||||
{&XCVR_TSM->TIMING37, 0x5b117111},
|
||||
{&XCVR_TSM->TIMING38, 0x5b117111},
|
||||
{&XCVR_TSM->TIMING39, 0x5b2b7111},
|
||||
{&XCVR_TSM->TIMING40, 0xffff7111},
|
||||
{&XCVR_TSM->TIMING41, 0xffff7111},
|
||||
{&XCVR_TSM->TIMING42, 0x5b127112},
|
||||
{&XCVR_TSM->TIMING43, 0x5b12ffff},
|
||||
{&XCVR_TSM->TIMING44, 0x5b12ffff},
|
||||
{&XCVR_TSM->TIMING45, 0xffff7112},
|
||||
{&XCVR_TSM->TIMING46, 0xffff716f},
|
||||
{&XCVR_TSM->TIMING47, 0x5b137157},
|
||||
{&XCVR_TSM->TIMING48, 0x5b23715a},
|
||||
{&XCVR_TSM->TIMING49, 0x5b23715a},
|
||||
{&XCVR_TSM->TIMING50, 0x5b2bffff},
|
||||
{&XCVR_TSM->TIMING51, 0x5b2bffff},
|
||||
{&XCVR_TSM->TIMING52, 0xffffffff},
|
||||
{&XCVR_TSM->WU_LATENCY, 0x00060006},
|
||||
{&XCVR_TX_DIG->DATARATE_CONFIG_FILTER_CTRL, 0x00010000},
|
||||
{&XCVR_TX_DIG->DATARATE_CONFIG_FSK_CTRL, 0x08001800},
|
||||
{&XCVR_TX_DIG->DATARATE_CONFIG_GFSK_CTRL, 0x00000804},
|
||||
{&XCVR_TX_DIG->DATA_PADDING_CTRL, 0x00001012},
|
||||
{&XCVR_TX_DIG->DATA_PADDING_CTRL_1, 0x00001600},
|
||||
{&XCVR_TX_DIG->DATA_PADDING_CTRL_2, 0x08001800},
|
||||
{&XCVR_TX_DIG->FSK_CTRL, 0x04001c00},
|
||||
{&XCVR_TX_DIG->GFSK_COEFF_0_1, 0x00040001},
|
||||
{&XCVR_TX_DIG->GFSK_COEFF_2_3, 0x0028000d},
|
||||
{&XCVR_TX_DIG->GFSK_COEFF_4_5, 0x00c00063},
|
||||
{&XCVR_TX_DIG->GFSK_COEFF_6_7, 0x0176012c},
|
||||
{&XCVR_TX_DIG->GFSK_CTRL, 0x000003fd},
|
||||
{&XCVR_TX_DIG->IMAGE_FILTER_CTRL, 0x00000000},
|
||||
{&XCVR_TX_DIG->PA_CTRL, 0x00020126},
|
||||
{&XCVR_TX_DIG->PA_RAMP_TBL0, 0x06040201},
|
||||
{&XCVR_TX_DIG->PA_RAMP_TBL1, 0x14100c09},
|
||||
{&XCVR_TX_DIG->PA_RAMP_TBL2, 0x26211c18},
|
||||
{&XCVR_TX_DIG->PA_RAMP_TBL3, 0x3c38322c},
|
||||
{&XCVR_TX_DIG->SWITCH_TX_CTRL, 0x00000000},
|
||||
{&XCVR_TX_DIG->TXDIG_CTRL, 0x00000007},
|
||||
{&XCVR_2P4GHZ_PHY->DMD_CTRL1, 0x8c049008},
|
||||
{&RBME->CRCW_CFG, 0x00000001},
|
||||
{&RBME->CRCW_CFG2, 0x00000000},
|
||||
{&RBME->CRCW_CFG3, 0x00000403},
|
||||
{&RBME->CRC_INIT, 0x55555500},
|
||||
{&RBME->CRC_PHR_SZ, 0x00000002},
|
||||
{&RBME->CRC_POLY, 0x00065b00},
|
||||
{&RBME->CRC_XOR_OUT, 0x00000000},
|
||||
{&RBME->FCP_CFG, 0x00000000},
|
||||
{&RBME->FEC_BSZ_OV_B4SP, 0x00000000},
|
||||
{&RBME->FEC_CFG1, 0x00000000},
|
||||
{&RBME->FEC_CFG2, 0x00000000},
|
||||
{&RBME->FRAME_OVER_SZ, 0x00000000},
|
||||
{&RBME->NPAYL_OVER_SZ, 0x00000000},
|
||||
{&RBME->PKT_SZ, 0x00244000},
|
||||
{&RBME->SPREAD_CFG, 0x00000000},
|
||||
{&RBME->WHITEN_CFG, 0x00410705},
|
||||
{&RBME->WHITEN_POLY, 0x00000004},
|
||||
{&RBME->WHITEN_SZ_THR, 0x00000000},
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
/* MCXW716C memory map, see MCUXpresso SDK gcc/mcxw716_flash.ld */
|
||||
ENTRY(Reset_Handler);
|
||||
MEMORY {
|
||||
flash(rx) : ORIGIN = 0x00000000, LENGTH = 1024k
|
||||
flash(rx) : ORIGIN = 0x00000000, LENGTH = 512k /* Upper half: OTA staging */
|
||||
sram(rwx) : ORIGIN = 0x20000000, LENGTH = 112k
|
||||
}
|
||||
__StackTop = ORIGIN(sram) + LENGTH(sram); /* Stack points here */
|
||||
@@ -16,6 +16,7 @@ SECTIONS {
|
||||
__data_start__ = .;
|
||||
_sdata = .;
|
||||
*(.first_data)
|
||||
*(.iram .iram.*) /* OTA flash operations, copied to RAM by startup */
|
||||
*(.data SORT(.data.*))
|
||||
. = ALIGN(4);
|
||||
__data_end__ = .;
|
||||
|
||||
@@ -1,5 +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
|
||||
|
||||
#include "hal.h"
|
||||
#include "mongoose.h"
|
||||
@@ -14,19 +19,79 @@ uint64_t mg_millis(void) {
|
||||
return hal_get_tick();
|
||||
}
|
||||
|
||||
// Self-signed P-256 certificate and key for the HTTPS listener. Created by:
|
||||
// openssl ecparam -genkey -name prime256v1 -noout -out key.pem
|
||||
// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg
|
||||
#define TLS_CERT \
|
||||
"-----BEGIN CERTIFICATE-----\n" \
|
||||
"MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \
|
||||
"FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \
|
||||
"MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \
|
||||
"SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \
|
||||
"uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \
|
||||
"MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \
|
||||
"BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \
|
||||
"lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \
|
||||
"BCjX++LI\n" \
|
||||
"-----END CERTIFICATE-----\n"
|
||||
|
||||
#define TLS_KEY \
|
||||
"-----BEGIN EC PRIVATE KEY-----\n" \
|
||||
"MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \
|
||||
"AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \
|
||||
"LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \
|
||||
"-----END EC PRIVATE KEY-----\n"
|
||||
|
||||
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener
|
||||
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)) {
|
||||
mg_http_reply(c, 200, "", "{%m:%llu}\n", MG_ESC("tick"), hal_get_tick());
|
||||
} else if (mg_match(hm->uri, mg_str("/api/crash"), NULL)) {
|
||||
__asm volatile("udf #0"); // Trigger HardFault
|
||||
} else {
|
||||
mg_http_reply(c, 200, "",
|
||||
"Hi from Mongoose!\n"
|
||||
"tick: %llu\n"
|
||||
"ram used: %zu\n"
|
||||
"ram free: %zu\n",
|
||||
hal_get_tick(), hal_ram_used(), hal_ram_free());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
hal_clock_init();
|
||||
hal_uart_init(UART_DEBUG, 115200);
|
||||
mg_log_set_fn(log_fn, UART_DEBUG);
|
||||
|
||||
if (!hal_radio_init()) MG_ERROR(("Radio init failed"));
|
||||
|
||||
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_mqtt_init(&mgr);
|
||||
|
||||
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 (;;) {
|
||||
static uint64_t timer;
|
||||
if (hal_timer_expired(&timer, 1000, hal_get_tick())) {
|
||||
MG_INFO(("Hi from Mongoose, tick %llu", hal_get_tick()));
|
||||
}
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
mg_mqtt_poll(&mgr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1,7 +1,23 @@
|
||||
#pragma once
|
||||
#include "MCXW716C.h"
|
||||
|
||||
// See https://mongoose.ws/documentation/#build-options
|
||||
// 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_MCXW71
|
||||
|
||||
#define MG_ENABLE_TCPIP 1 // Built-in TCP/IP stack. No network driver yet
|
||||
#define MG_ENABLE_TCPIP 1
|
||||
#define MG_ENABLE_DRIVER_MCXW71 1
|
||||
#define MG_ENABLE_CUSTOM_MILLIS 1
|
||||
#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]; \
|
||||
} while (0)
|
||||
@@ -0,0 +1,63 @@
|
||||
# Environment setup: https://mongoose.ws/documentation/tutorials/tools/
|
||||
|
||||
MGDIR = ../../../..
|
||||
|
||||
CFLAGS = -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion
|
||||
CFLAGS += -Wformat-truncation -fno-common -Wno-conversion -Wno-sign-conversion
|
||||
CFLAGS += -g3 -Os -ffunction-sections -fdata-sections
|
||||
CFLAGS += -Wno-cast-function-type -Wno-old-style-declaration
|
||||
CFLAGS += -Wno-unused-parameter
|
||||
CFLAGS += -I. -I$(MGDIR) -Imongoose -Icmsis_core/CMSIS/Core/Include/
|
||||
CFLAGS += $(addprefix -Icmsis_rw612/devices/RW612/drivers/romapi/,bootloader nboot otp iap flexspi)
|
||||
CFLAGS += -Icmsis_rw612/devices/RW612/ -Icmsis_rw612/devices/RW612/periph/ -Icmsis_rw612/devices/RW612/drivers/
|
||||
CFLAGS += -DCPU_RW612ETA2I -DCPU_RW612ETA2I_cm33_no_dsp
|
||||
CFLAGS += -DBOOT_HEADER_ENABLE=1
|
||||
CFLAGS += -DRADIO_FW='"$(RADIO_FW)"'
|
||||
CFLAGS += -mcpu=cortex-m33+nodsp -mthumb -mfloat-abi=hard -mfpu=fpv5-sp-d16 $(CFLAGS_EXTRA)
|
||||
LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs=nano.specs -lc -lgcc -Wl,--gc-sections
|
||||
|
||||
SOURCES = main.c hal.c radio.c startup.c flexspi.c flash_config.c $(MGDIR)/mongoose.c
|
||||
SOURCES += cmsis_rw612/devices/RW612/drivers/fsl_clock.c
|
||||
SOURCES += cmsis_rw612/devices/RW612/drivers/fsl_trng.c
|
||||
SOURCES += cmsis_rw612/devices/RW612/drivers/fsl_reset.c
|
||||
SOURCES += $(addprefix cmsis_rw612/devices/RW612/drivers/,fsl_power.c fsl_ocotp.c \
|
||||
romapi/bootloader/fsl_romapi.c romapi/nboot/fsl_romapi_nboot.c romapi/otp/fsl_romapi_otp.c)
|
||||
|
||||
# NXP BLE/802.15.4 combo firmware for CPU2, linked into our image. NXP license:
|
||||
# https://github.com/NXP/wifi_nb_fw/blob/nxp-v4.4.3/LICENSE.txt
|
||||
RADIO_FW = rw61x_sb_ble_15d4_combo_a2.bin
|
||||
RADIO_FW_URL = https://github.com/NXP/wifi_nb_fw/raw/nxp-v4.4.3/rw61x/$(RADIO_FW)
|
||||
RADIO_FW_SHA256 = 2efac0d771e371377d57159c40f0909c7ecc6f6b950ce69ed2a3e02ac7eb686a
|
||||
|
||||
all build: firmware.bin
|
||||
|
||||
firmware.bin: firmware.elf
|
||||
arm-none-eabi-objcopy -O binary $< $@
|
||||
arm-none-eabi-size --format=berkeley $<
|
||||
|
||||
firmware.elf: cmsis_core cmsis_rw612 $(RADIO_FW) $(SOURCES) $(wildcard *.h) link.ld Makefile mongoose_config.h
|
||||
arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@
|
||||
|
||||
cmsis_core:
|
||||
git clone -q -c advice.detachedHead=false --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@
|
||||
|
||||
$(RADIO_FW):
|
||||
curl -sL $(RADIO_FW_URL) -o $@.tmp
|
||||
echo "$(RADIO_FW_SHA256) $@.tmp" | shasum -a 256 -c - >/dev/null
|
||||
mv $@.tmp $@
|
||||
|
||||
cmsis_rw612:
|
||||
curl -sL https://mcuxpresso.nxp.com/cmsis_pack/repo/NXP.RW612_DFP.25.12.00.pack -o NXP.RW612_DFP.25.12.00.pack
|
||||
unzip -q NXP.RW612_DFP.25.12.00.pack -d $@
|
||||
rm NXP.RW612_DFP.25.12.00.pack
|
||||
|
||||
# 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 '
|
||||
|
||||
flash: firmware.bin
|
||||
(echo 'loadfile firmware.elf'; echo 'reset'; echo 'exit';) | JLinkExe -device RW612 -if SWD -speed 4000 -autoconnect 1 -NoGui 1 -ExitOnError 1
|
||||
# JRun --nogui --device RW612 --if SWD firmware.elf
|
||||
|
||||
clean:
|
||||
rm -rf firmware.* cmsis_* mongoose/mongoose.* $(RADIO_FW)
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2021-2024 NXP
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#include "flash_config.h"
|
||||
|
||||
/* Component ID definition, used by tools. */
|
||||
#ifndef FSL_COMPONENT_ID
|
||||
#define FSL_COMPONENT_ID "platform.drivers.flash_config"
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
* Code
|
||||
******************************************************************************/
|
||||
#if defined(BOOT_HEADER_ENABLE) && (BOOT_HEADER_ENABLE == 1)
|
||||
#if defined(__ARMCC_VERSION) || defined(__GNUC__)
|
||||
__attribute__((section(".flash_conf"), used))
|
||||
#elif defined(__ICCARM__)
|
||||
#pragma location = ".flash_conf"
|
||||
#endif
|
||||
const fc_flexspi_nor_config_t flexspi_config = {
|
||||
.memConfig =
|
||||
{
|
||||
.tag = FC_BLOCK_TAG,
|
||||
.version = FC_BLOCK_VERSION,
|
||||
.readSampleClkSrc = 1,
|
||||
.csHoldTime = 3,
|
||||
.csSetupTime = 3,
|
||||
.deviceModeCfgEnable = 1,
|
||||
.deviceModeSeq = {.seqNum = 1, .seqId = 2},
|
||||
.deviceModeArg = 0x02,
|
||||
.configCmdEnable = 0,
|
||||
.deviceType = 0x1,
|
||||
.sflashPadType = kSerialFlash_4Pads,
|
||||
.serialClkFreq = 5,
|
||||
.sflashA1Size = 0x4000000U,
|
||||
.sflashA2Size = 0,
|
||||
.sflashB1Size = 0,
|
||||
.sflashB2Size = 0,
|
||||
.lookupTable =
|
||||
{
|
||||
/* Read */
|
||||
[0] = FC_FLEXSPI_LUT_SEQ(FC_CMD_SDR, FC_FLEXSPI_1PAD, 0xEC, FC_RADDR_SDR, FC_FLEXSPI_4PAD, 0x20),
|
||||
[1] = FC_FLEXSPI_LUT_SEQ(FC_MODE8_SDR, FC_FLEXSPI_4PAD, 0xF0, FC_DUMMY_SDR, FC_FLEXSPI_4PAD, 0x04),
|
||||
[2] = FC_FLEXSPI_LUT_SEQ(FC_READ_SDR, FC_FLEXSPI_4PAD, 0x04, FC_STOP_EXE, FC_FLEXSPI_1PAD, 0x00),
|
||||
|
||||
/* Read Status */
|
||||
[4 * 1 + 0] =
|
||||
FC_FLEXSPI_LUT_SEQ(FC_CMD_SDR, FC_FLEXSPI_1PAD, 0x05, FC_READ_SDR, FC_FLEXSPI_1PAD, 0x04),
|
||||
|
||||
/* Write Status */
|
||||
[4 * 2 + 0] =
|
||||
FC_FLEXSPI_LUT_SEQ(FC_CMD_SDR, FC_FLEXSPI_1PAD, 0x31, FC_WRITE_SDR, FC_FLEXSPI_1PAD, 0x01),
|
||||
|
||||
/* Write Enable */
|
||||
[4 * 3 + 0] =
|
||||
FC_FLEXSPI_LUT_SEQ(FC_CMD_SDR, FC_FLEXSPI_1PAD, 0x06, FC_STOP_EXE, FC_FLEXSPI_1PAD, 0x00),
|
||||
|
||||
/* Sector erase */
|
||||
[4 * 5 + 0] =
|
||||
FC_FLEXSPI_LUT_SEQ(FC_CMD_SDR, FC_FLEXSPI_1PAD, 0x21, FC_RADDR_SDR, FC_FLEXSPI_1PAD, 0x20),
|
||||
|
||||
/* Block erase */
|
||||
[4 * 8 + 0] =
|
||||
FC_FLEXSPI_LUT_SEQ(FC_CMD_SDR, FC_FLEXSPI_1PAD, 0xDC, FC_RADDR_SDR, FC_FLEXSPI_1PAD, 0x20),
|
||||
|
||||
/* Page program */
|
||||
[4 * 9 + 0] =
|
||||
FC_FLEXSPI_LUT_SEQ(FC_CMD_SDR, FC_FLEXSPI_1PAD, 0x34, FC_RADDR_SDR, FC_FLEXSPI_1PAD, 0x20),
|
||||
[4 * 9 + 1] =
|
||||
FC_FLEXSPI_LUT_SEQ(FC_WRITE_SDR, FC_FLEXSPI_4PAD, 0x00, FC_STOP_EXE, FC_FLEXSPI_1PAD, 0x00),
|
||||
|
||||
/* chip erase */
|
||||
[4 * 11 + 0] =
|
||||
FC_FLEXSPI_LUT_SEQ(FC_CMD_SDR, FC_FLEXSPI_1PAD, 0xC7, FC_STOP_EXE, FC_FLEXSPI_1PAD, 0x00),
|
||||
},
|
||||
},
|
||||
.pageSize = 0x100,
|
||||
.sectorSize = 0x1000,
|
||||
.ipcmdSerialClkFreq = 0,
|
||||
.blockSize = 0x10000,
|
||||
.fcb_fill[0] = 0xFFFFFFFF,
|
||||
};
|
||||
#endif /* BOOT_HEADER_ENABLE */
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright 2021-2023 NXP
|
||||
* All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef __FLASH_CONFIG__
|
||||
#define __FLASH_CONFIG__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "fsl_common.h"
|
||||
|
||||
/*! @name Driver version */
|
||||
/*@{*/
|
||||
/*! @brief FLASH_CONFIG driver version 2.0.0. */
|
||||
#define FSL_FLASH_CONFIG_DRIVER_VERSION (MAKE_VERSION(2, 0, 0))
|
||||
/*@}*/
|
||||
|
||||
/*******************************************************************************
|
||||
* Definition
|
||||
******************************************************************************/
|
||||
|
||||
/* FLEXSPI memory config block related defintions */
|
||||
#define FC_BLOCK_TAG (0x42464346)
|
||||
#define FC_BLOCK_VERSION (0x00000000)
|
||||
|
||||
#define FC_CMD_SDR 0x01
|
||||
#define FC_CMD_DDR 0x21
|
||||
#define FC_RADDR_SDR 0x02
|
||||
#define FC_RADDR_DDR 0x22
|
||||
#define FC_CADDR_SDR 0x03
|
||||
#define FC_CADDR_DDR 0x23
|
||||
#define FC_MODE1_SDR 0x04
|
||||
#define FC_MODE1_DDR 0x24
|
||||
#define FC_MODE2_SDR 0x05
|
||||
#define FC_MODE2_DDR 0x25
|
||||
#define FC_MODE4_SDR 0x06
|
||||
#define FC_MODE4_DDR 0x26
|
||||
#define FC_MODE8_SDR 0x07
|
||||
#define FC_MODE8_DDR 0x27
|
||||
#define FC_WRITE_SDR 0x08
|
||||
#define FC_WRITE_DDR 0x28
|
||||
#define FC_READ_SDR 0x09
|
||||
#define FC_READ_DDR 0x29
|
||||
#define FC_LEARN_SDR 0x0A
|
||||
#define FC_LEARN_DDR 0x2A
|
||||
#define FC_DATSZ_SDR 0x0B
|
||||
#define FC_DATSZ_DDR 0x2B
|
||||
#define FC_DUMMY_SDR 0x0C
|
||||
#define FC_DUMMY_DDR 0x2C
|
||||
#define FC_DUMMY_RWDS_SDR 0x0D
|
||||
#define FC_DUMMY_RWDS_DDR 0x2D
|
||||
#define FC_JMP_ON_CS 0x1F
|
||||
#define FC_STOP_EXE 0
|
||||
|
||||
#define FC_FLEXSPI_1PAD 0
|
||||
#define FC_FLEXSPI_2PAD 1
|
||||
#define FC_FLEXSPI_4PAD 2
|
||||
#define FC_FLEXSPI_8PAD 3
|
||||
|
||||
#define FC_FLEXSPI_LUT_SEQ(cmd0, pad0, op0, cmd1, pad1, op1) \
|
||||
(FLEXSPI_LUT_OPERAND0(op0) | FLEXSPI_LUT_NUM_PADS0(pad0) | FLEXSPI_LUT_OPCODE0(cmd0) | FLEXSPI_LUT_OPERAND1(op1) | \
|
||||
FLEXSPI_LUT_NUM_PADS1(pad1) | FLEXSPI_LUT_OPCODE1(cmd1))
|
||||
|
||||
/* !@brief Data pad used in Read command */
|
||||
enum
|
||||
{
|
||||
kSerialFlash_1Pads = 1,
|
||||
kSerialFlash_2Pads = 2,
|
||||
kSerialFlash_4Pads = 4,
|
||||
kSerialFlash_8Pads = 8,
|
||||
};
|
||||
|
||||
/* !@brief FLEXSPI clock configuration - In High speed boot mode mode */
|
||||
enum
|
||||
{
|
||||
kFlexSpiSerialClk_30MHz = 1,
|
||||
kFlexSpiSerialClk_50MHz = 2,
|
||||
kFlexSpiSerialClk_60MHz = 3,
|
||||
kFlexSpiSerialClk_80MHz = 4,
|
||||
kFlexSpiSerialClk_100MHz = 5,
|
||||
kFlexSpiSerialClk_120MHz = 6,
|
||||
kFlexSpiSerialClk_133MHz = 7,
|
||||
kFlexSpiSerialClk_166MHz = 8,
|
||||
kFlexSpiSerialClk_200MHz = 9,
|
||||
};
|
||||
|
||||
/* !@brief FLEXSPI clock configuration - In Normal boot SDR mode */
|
||||
enum
|
||||
{
|
||||
kFlexSpiSerialClk_SDR_24MHz = 1,
|
||||
kFlexSpiSerialClk_SDR_48MHz = 2,
|
||||
};
|
||||
|
||||
/* !@brief FLEXSPI clock configuration - In Normal boot DDR mode */
|
||||
enum
|
||||
{
|
||||
kFlexSpiSerialClk_DDR_48MHz = 1,
|
||||
};
|
||||
|
||||
/* !@brief Misc feature bit definitions */
|
||||
enum
|
||||
{
|
||||
kFlexSpiMiscOffset_DiffClkEnable = 0, /* !< Bit for Differential clock enable */
|
||||
kFlexSpiMiscOffset_WordAddressableEnable = 3, /* !< Bit for Word Addressable enable */
|
||||
kFlexSpiMiscOffset_SafeConfigFreqEnable = 4, /* !< Bit for Safe Configuration Frequency enable */
|
||||
kFlexSpiMiscOffset_DdrModeEnable = 6, /* !< Bit for DDR clock confiuration indication. */
|
||||
};
|
||||
|
||||
/* !@brief Flash Configuration Command Type */
|
||||
enum
|
||||
{
|
||||
kDeviceConfigCmdType_Generic, /* !< Generic command, for example: configure dummy cycles, drive strength, etc */
|
||||
kDeviceConfigCmdType_QuadEnable, /* !< Quad Enable command */
|
||||
kDeviceConfigCmdType_Spi2Xpi, /* !< Switch from SPI to DPI/QPI/OPI mode */
|
||||
kDeviceConfigCmdType_Xpi2Spi, /* !< Switch from DPI/QPI/OPI to SPI mode */
|
||||
kDeviceConfigCmdType_Spi2NoCmd, /* !< Switch to 0-4-4/0-8-8 mode */
|
||||
kDeviceConfigCmdType_Reset, /* !< Reset device command */
|
||||
};
|
||||
|
||||
typedef struct _fc_flexspi_dll_time
|
||||
{
|
||||
uint8_t time_100ps; /* !< Data valid time, in terms of 100ps */
|
||||
uint8_t delay_cells; /* !< Data valid time, in terms of delay cells */
|
||||
} fc_flexspi_dll_time_t;
|
||||
|
||||
/* !@brief FlexSPI LUT Sequence structure */
|
||||
typedef struct _fc_flexspi_lut_seq
|
||||
{
|
||||
uint8_t seqNum; /* !< Sequence Number, valid number: 1-16 */
|
||||
uint8_t seqId; /* !< Sequence Index, valid number: 0-15 */
|
||||
uint16_t reserved;
|
||||
} fc_flexspi_lut_seq_t;
|
||||
|
||||
/* !@brief FlexSPI Memory Configuration Block */
|
||||
typedef struct _fc_flexspi_mem_config
|
||||
{
|
||||
uint32_t tag; /* !< [0x000-0x003] Tag, fixed value 0x42464346UL */
|
||||
uint32_t version; /* !< [0x004-0x007] Version,[31:24] -'V', [23:16] - Major, [15:8] - Minor, [7:0] - bugfix */
|
||||
uint32_t reserved0; /* !< [0x008-0x00b] Reserved for future use */
|
||||
uint8_t readSampleClkSrc; /* !< [0x00c-0x00c] Read Sample Clock Source, valid value: 0/1/3 */
|
||||
uint8_t csHoldTime; /* !< [0x00d-0x00d] CS hold time, default value: 3 */
|
||||
uint8_t csSetupTime; /* !< [0x00e-0x00e] CS setup time, default value: 3 */
|
||||
uint8_t columnAddressWidth; /* !< [0x00f-0x00f] Column Address with, for HyperBus protocol, it is fixed to 3, For
|
||||
Serial NAND, need to refer to datasheet */
|
||||
uint8_t deviceModeCfgEnable; /* !< [0x010-0x010] Device Mode Configure enable flag, 1 - Enable, 0 - Disable */
|
||||
uint8_t deviceModeType; /* !< [0x011-0x011] Specify the configuration command type:Quad Enable, DPI/QPI/OPI switch,
|
||||
Generic configuration, etc. */
|
||||
uint16_t waitTimeCfgCommands; /* !< [0x012-0x013] Wait time for all configuration commands, unit: 100us, Used for
|
||||
DPI/QPI/OPI switch or reset command */
|
||||
fc_flexspi_lut_seq_t deviceModeSeq; /* !< [0x014-0x017] Device mode sequence info, [7:0] - LUT sequence id, [15:8] -
|
||||
LUt sequence number, [31:16] Reserved */
|
||||
uint32_t deviceModeArg; /* !< [0x018-0x01b] Argument/Parameter for device configuration */
|
||||
uint8_t configCmdEnable; /* !< [0x01c-0x01c] Configure command Enable Flag, 1 - Enable, 0 - Disable */
|
||||
uint8_t configModeType[3]; /* !< [0x01d-0x01f] Configure Mode Type, similar as deviceModeTpe */
|
||||
fc_flexspi_lut_seq_t configCmdSeqs[3]; /* !< [0x020-0x02b] Sequence info for Device Configuration command, similar
|
||||
as deviceModeSeq */
|
||||
uint32_t reserved1; /* !< [0x02c-0x02f] Reserved for future use */
|
||||
uint32_t configCmdArgs[3]; /* !< [0x030-0x03b] Arguments/Parameters for device Configuration commands */
|
||||
uint32_t reserved2; /* !< [0x03c-0x03f] Reserved for future use */
|
||||
uint32_t controllerMiscOption; /* !< [0x040-0x043] Controller Misc Options, see Misc feature bit definitions for
|
||||
more details */
|
||||
uint8_t deviceType; /* !< [0x044-0x044] Device Type: See Flash Type Definition for more details */
|
||||
uint8_t sflashPadType; /* !< [0x045-0x045] Serial Flash Pad Type: 1 - Single, 2 - Dual, 4 - Quad, 8 - Octal */
|
||||
uint8_t serialClkFreq; /* !< [0x046-0x046] Serial Flash Frequencey, device specific definitions, See System Boot
|
||||
Chapter for more details */
|
||||
uint8_t lutCustomSeqEnable; /* !< [0x047-0x047] LUT customization Enable, it is required if the program/erase cannot
|
||||
be done using 1 LUT sequence, currently, only applicable to HyperFLASH */
|
||||
uint32_t reserved3[2]; /* !< [0x048-0x04f] Reserved for future use */
|
||||
uint32_t sflashA1Size; /* !< [0x050-0x053] Size of Flash connected to A1 */
|
||||
uint32_t sflashA2Size; /* !< [0x054-0x057] Size of Flash connected to A2 */
|
||||
uint32_t sflashB1Size; /* !< [0x058-0x05b] Size of Flash connected to B1 */
|
||||
uint32_t sflashB2Size; /* !< [0x05c-0x05f] Size of Flash connected to B2 */
|
||||
uint32_t csPadSettingOverride; /* !< [0x060-0x063] CS pad setting override value */
|
||||
uint32_t sclkPadSettingOverride; /* !< [0x064-0x067] SCK pad setting override value */
|
||||
uint32_t dataPadSettingOverride; /* !< [0x068-0x06b] data pad setting override value */
|
||||
uint32_t dqsPadSettingOverride; /* !< [0x06c-0x06f] DQS pad setting override value */
|
||||
uint32_t timeoutInMs; /* !< [0x070-0x073] Timeout threshold for read status command */
|
||||
uint32_t commandInterval; /* !< [0x074-0x077] CS deselect interval between two commands */
|
||||
fc_flexspi_dll_time_t dataValidTime[2]; /* !< [0x078-0x07b] CLK edge to data valid time for PORT A and PORT B */
|
||||
uint16_t busyOffset; /* !< [0x07c-0x07d] Busy offset, valid value: 0-31 */
|
||||
uint16_t busyBitPolarity; /* !< [0x07e-0x07f] Busy flag polarity, 0 - busy flag is 1 when flash device is busy, 1 -
|
||||
busy flag is 0 when flash device is busy */
|
||||
uint32_t lookupTable[64]; /* !< [0x080-0x17f] Lookup table holds Flash command sequences */
|
||||
fc_flexspi_lut_seq_t lutCustomSeq[12]; /* !< [0x180-0x1af] Customizable LUT Sequences */
|
||||
uint32_t reserved4[4]; /* !< [0x1b0-0x1bf] Reserved for future use */
|
||||
} fc_flexspi_mem_config_t;
|
||||
/*
|
||||
* Serial NOR configuration block
|
||||
*/
|
||||
typedef struct _fc_flexspi_nor_config
|
||||
{
|
||||
#if defined(__ARMCC_VERSION) || defined(__ICCARM__)
|
||||
uint8_t padding[0x400]; /* !< Padding for MDK and IAR */
|
||||
#endif
|
||||
fc_flexspi_mem_config_t memConfig; /* !< Common memory configuration info via FlexSPI */
|
||||
uint32_t pageSize; /* !< Page size of Serial NOR */
|
||||
uint32_t sectorSize; /* !< Sector size of Serial NOR */
|
||||
uint8_t ipcmdSerialClkFreq; /* !< Clock frequency for IP command */
|
||||
uint8_t isUniformBlockSize; /* !< Sector/Block size is the same */
|
||||
uint8_t isDataOrderSwapped; /* !< Data order (D0, D1, D2, D3) is swapped (D1,D0, D3, D2) */
|
||||
uint8_t reserved0[1]; /* !< Reserved for future use */
|
||||
uint8_t serialNorType; /* !< Serial NOR Flash type: 0/1/2/3 */
|
||||
uint8_t needExitNoCmdMode; /* !< Need to exit NoCmd mode before other IP command */
|
||||
uint8_t halfClkForNonReadCmd; /* !< Half the Serial Clock for non-read command: true/false */
|
||||
uint8_t needRestoreNoCmdMode; /* !< Need to Restore NoCmd mode after IP commmand execution */
|
||||
uint32_t blockSize; /* !< Block size */
|
||||
uint32_t flashStateCtx; /* !< Flash State Context */
|
||||
uint32_t reserve2[10]; /* !< Reserved for future use */
|
||||
uint32_t fcb_fill[0x280]; /* !< Fill */
|
||||
} fc_flexspi_nor_config_t;
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* __FLASH_CONFIG__ */
|
||||
@@ -0,0 +1,82 @@
|
||||
#include "RW612.h"
|
||||
#include "fsl_flexspi.h" // peripheral structures
|
||||
|
||||
__attribute__((section(".iram"))) static void deinit_flash(FLEXSPI_Type *base) {
|
||||
CLKCTL0->PSCCTL0_SET =
|
||||
CLKCTL0_PSCCTL0_SET_FLEXSPI0_MASK; // Enable FLEXSPI clock again
|
||||
base->MCR0 &= ~FLEXSPI_MCR0_MDIS_MASK; // Enable FLEXSPI module
|
||||
// Wait until FLEXSPI is not busy
|
||||
while (!((base->STS0 & FLEXSPI_STS0_ARBIDLE_MASK) &&
|
||||
(base->STS0 & FLEXSPI_STS0_SEQIDLE_MASK)))
|
||||
(void)0;
|
||||
base->MCR0 |=
|
||||
FLEXSPI_MCR0_MDIS_MASK; // Disable module during the reset procedure
|
||||
}
|
||||
|
||||
__attribute__((section(".iram"))) static void init_flash(FLEXSPI_Type *base) {
|
||||
uint32_t status;
|
||||
uint32_t lastStatus;
|
||||
uint32_t retry;
|
||||
|
||||
// Loopback from DQS pad can maximize RD board flash speed.
|
||||
if ((base->MCR0 & FLEXSPI_MCR0_RXCLKSRC_MASK) != FLEXSPI_MCR0_RXCLKSRC(1)) {
|
||||
base->MCR0 =
|
||||
(base->MCR0 & ~FLEXSPI_MCR0_RXCLKSRC_MASK) | FLEXSPI_MCR0_RXCLKSRC(1);
|
||||
}
|
||||
base->DLLCR[0] = 0x1U;
|
||||
base->MCR0 &= ~FLEXSPI_MCR0_MDIS_MASK; // Enable FLEXSPI module
|
||||
base->MCR0 |= FLEXSPI_MCR0_SWRESET_MASK;
|
||||
while (base->MCR0 & FLEXSPI_MCR0_SWRESET_MASK)
|
||||
(void)0;
|
||||
|
||||
// Need to wait DLL locked if DLL enabled
|
||||
if (0U != (base->DLLCR[0] & FLEXSPI_DLLCR_DLLEN_MASK)) {
|
||||
lastStatus = base->STS2;
|
||||
retry = 10;
|
||||
// Wait slave delay line locked and slave reference delay line locked.
|
||||
do {
|
||||
status = base->STS2;
|
||||
if ((status &
|
||||
(FLEXSPI_STS2_AREFLOCK_MASK | FLEXSPI_STS2_ASLVLOCK_MASK)) ==
|
||||
(FLEXSPI_STS2_AREFLOCK_MASK | FLEXSPI_STS2_ASLVLOCK_MASK)) {
|
||||
// Locked
|
||||
retry = 100;
|
||||
break;
|
||||
} else if (status == lastStatus) {
|
||||
retry--;
|
||||
} else {
|
||||
retry = 10;
|
||||
lastStatus = status;
|
||||
}
|
||||
} while (retry > 0);
|
||||
// According to ERR011377, need to delay at least 100 NOPs to ensure the DLL
|
||||
// is locked
|
||||
for (; retry > 0U; retry--)
|
||||
(void)0;
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((section(".iram"))) void
|
||||
set_flexspi_clock(FLEXSPI_Type *base, uint32_t src, uint32_t divider) {
|
||||
if ((CLKCTL0->FLEXSPIFCLKSEL != CLKCTL0_FLEXSPIFCLKSEL_SEL(src)) ||
|
||||
((CLKCTL0->FLEXSPIFCLKDIV & CLKCTL0_FLEXSPIFCLKDIV_DIV_MASK) !=
|
||||
(divider - 1))) {
|
||||
// Always deinit FLEXSPI and init FLEXSPI for the flash to make sure the
|
||||
// flash works correctly after the FLEXSPI root clock changed as the default
|
||||
// FLEXSPI configuration may does not work for the new root clock frequency.
|
||||
deinit_flash(base);
|
||||
|
||||
// Disable clock before changing clock source
|
||||
CLKCTL0->PSCCTL0_CLR = CLKCTL0_PSCCTL0_CLR_FLEXSPI0_MASK;
|
||||
// Update flexspi clock.
|
||||
CLKCTL0->FLEXSPIFCLKSEL = CLKCTL0_FLEXSPIFCLKSEL_SEL(src);
|
||||
CLKCTL0->FLEXSPIFCLKDIV |=
|
||||
CLKCTL0_FLEXSPIFCLKDIV_RESET_MASK; // Reset the divider counter
|
||||
CLKCTL0->FLEXSPIFCLKDIV = CLKCTL0_FLEXSPIFCLKDIV_DIV(divider - 1);
|
||||
while ((CLKCTL0->FLEXSPIFCLKDIV) & CLKCTL0_FLEXSPIFCLKDIV_REQFLAG_MASK)
|
||||
(void)0;
|
||||
// Enable FLEXSPI clock again
|
||||
CLKCTL0->PSCCTL0_SET = CLKCTL0_PSCCTL0_SET_FLEXSPI0_MASK;
|
||||
init_flash(base);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
// 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 = SYS_FREQUENCY;
|
||||
int hal_radio_err; // Result of hal_radio_init()
|
||||
void SystemInit(void) { // Called automatically by startup code
|
||||
hal_system_init(); // Enable FPU
|
||||
hal_radio_err = hal_radio_init(); // At the boot clock, see radio.c
|
||||
hal_clock_init();
|
||||
SysTick_Config(SystemCoreClock / 1000); // Sys tick every 1ms
|
||||
}
|
||||
|
||||
void ExitRun0Mode(void) {
|
||||
}
|
||||
|
||||
struct stat;
|
||||
__attribute__((weak)) int _fstat(int fd, struct stat *st) {
|
||||
(void) fd, (void) st;
|
||||
return -1;
|
||||
}
|
||||
|
||||
extern unsigned char _end[]; // End of data section, start of heap. See link.ld
|
||||
static unsigned char *s_current_heap_end = _end;
|
||||
|
||||
size_t hal_ram_used(void) {
|
||||
return (size_t) (s_current_heap_end - _end);
|
||||
}
|
||||
|
||||
size_t hal_ram_free(void) {
|
||||
unsigned char endofstack;
|
||||
return (size_t) (&endofstack - s_current_heap_end);
|
||||
}
|
||||
|
||||
void *_sbrk(int incr) {
|
||||
unsigned char *prev_heap;
|
||||
unsigned char *heap_end = (unsigned char *) ((size_t) &heap_end - 256);
|
||||
prev_heap = s_current_heap_end;
|
||||
// Check how much space we got from the heap end to the stack end
|
||||
if (s_current_heap_end + incr > heap_end) return (void *) -1;
|
||||
s_current_heap_end += incr;
|
||||
return prev_heap;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _open(const char *path) {
|
||||
(void) path;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _close(int fd) {
|
||||
(void) fd;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _isatty(int fd) {
|
||||
(void) fd;
|
||||
return 1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _lseek(int fd, int ptr, int dir) {
|
||||
(void) fd, (void) ptr, (void) dir;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((weak)) void _exit(int status) {
|
||||
(void) status;
|
||||
for (;;) asm volatile("BKPT #0");
|
||||
}
|
||||
|
||||
__attribute__((weak)) void _kill(int pid, int sig) {
|
||||
(void) pid, (void) sig;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _getpid(void) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _write(int fd, char *ptr, int len) {
|
||||
(void) fd, (void) ptr, (void) len;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _read(int fd, char *ptr, int len) {
|
||||
(void) fd, (void) ptr, (void) len;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _link(const char *a, const char *b) {
|
||||
(void) a, (void) b;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _unlink(const char *a) {
|
||||
(void) a;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int _stat(const char *path, struct stat *st) {
|
||||
(void) path, (void) st;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int mkdir(const char *path, int mode) {
|
||||
(void) path, (void) mode;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) void _init(void) {
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
// Copyright (c) 2023 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "RW612.h"
|
||||
#include "fsl_clock.h"
|
||||
#include "fsl_ocotp.h"
|
||||
#include "fsl_power.h"
|
||||
#include "fsl_io_mux.h"
|
||||
#include "fsl_reset.h"
|
||||
#include "fsl_trng.h"
|
||||
|
||||
int hal_radio_init(void); // Start the 802.15.4 radio firmware. 0: success
|
||||
extern int hal_radio_err; // hal_radio_init() result, called by SystemInit()
|
||||
|
||||
#define BIT(x) (1UL << (x))
|
||||
#define CLRSET(reg, clear, set) ((reg) = ((reg) & ~(clear)) | (set))
|
||||
#define PIN(bank, num) ((bank << 8) | (num))
|
||||
#define PINNO(pin) (pin & 255)
|
||||
#define PINBANK(pin) (pin >> 8)
|
||||
|
||||
void hal_init(void);
|
||||
size_t hal_ram_free(void);
|
||||
size_t hal_ram_used(void);
|
||||
bool hal_timer_expired(volatile uint64_t *t, uint64_t period, uint64_t now);
|
||||
uint64_t hal_get_tick(void);
|
||||
|
||||
static inline void spin(volatile uint32_t count) {
|
||||
while (count--)
|
||||
(void)0;
|
||||
}
|
||||
|
||||
#define SYS_FREQUENCY 260000000UL
|
||||
#define FRG_PLL_CLKDIV 13
|
||||
|
||||
enum { HAL_GPIO_MODE_INPUT, HAL_GPIO_MODE_OUTPUT };
|
||||
enum {
|
||||
HAL_GPIO_TYPE_NOPULL_WEAKEST = 0x0,
|
||||
HAL_GPIO_TYPE_NOPULL_WEAK = 0x4,
|
||||
HAL_GPIO_TYPE_NOPULL_STRONG = 0x8,
|
||||
HAL_GPIO_TYPE_NOPULL_STRONGEST = 0xC,
|
||||
HAL_GPIO_TYPE_PULLUP_WEAKEST = 0x1,
|
||||
HAL_GPIO_TYPE_PULLUP_WEAK = 0x5,
|
||||
HAL_GPIO_TYPE_PULLUP_STRONG = 0x9,
|
||||
HAL_GPIO_TYPE_PULLUP_STRONGEST = 0xD,
|
||||
HAL_GPIO_TYPE_PULLDOWN_WEAKEST = 0x2,
|
||||
HAL_GPIO_TYPE_PULLDOWN_WEAK = 0x6,
|
||||
HAL_GPIO_TYPE_PULLDOWN_STRONG = 0xA,
|
||||
HAL_GPIO_TYPE_PULLDOWN_STRONGEST = 0xE,
|
||||
HAL_GPIO_TYPE_NOPULL = HAL_GPIO_TYPE_NOPULL_STRONGEST,
|
||||
HAL_GPIO_TYPE_PULLUP = HAL_GPIO_TYPE_PULLUP_STRONGEST,
|
||||
HAL_GPIO_TYPE_PULLDOWN = HAL_GPIO_TYPE_PULLDOWN_STRONGEST,
|
||||
};
|
||||
|
||||
// Wrapper structure over the IO_MUX_* macros in fsl_io_mux.h
|
||||
// To use those macros with our gpio functions,
|
||||
// wrap them inside this structure as shown in the example from the
|
||||
// ethernet_init function.
|
||||
typedef struct
|
||||
{
|
||||
uint32_t pinLowMask, pinHighMask, gpioFcSetMask,
|
||||
gpioFcClrMask, fselSetMask, fselClrMask, ctimerSetMask,
|
||||
ctimerClrMask, sctimerSetMask, sctimerClrMask;
|
||||
} io_mux_config_t;
|
||||
|
||||
static inline void hal_gpio_init(uint16_t pin, uint8_t mode, uint8_t type,
|
||||
io_mux_config_t *mux_config) {
|
||||
uint8_t bank = PINBANK(pin);
|
||||
uint8_t pin_no = PINNO(pin);
|
||||
if (mode == HAL_GPIO_MODE_INPUT) {
|
||||
GPIO->DIR[bank] &= ~(1 << pin_no);
|
||||
} else {
|
||||
GPIO->DIR[bank] |= (1 << pin_no);
|
||||
}
|
||||
|
||||
// configure pull
|
||||
pin_no += bank * 32;
|
||||
uint32_t shift = (pin_no % 16) * 2;
|
||||
uint32_t pull_val = (uint32_t)type & 3;
|
||||
uint32_t drive_val = ((uint32_t)type >> 2) & 3;
|
||||
volatile uint32_t *pull_reg, *drive_reg;
|
||||
if (pin_no < 22 || pin_no > 27) {
|
||||
pull_reg = &SOCCTRL->PAD_PU_PD_EN0 + pin_no / 16;
|
||||
drive_reg = &SOCCTRL->SR_CONFIG0 + pin_no / 16;
|
||||
} else {
|
||||
pull_reg = &AON_SOC_CIU->PAD_PU_PD_EN1;
|
||||
drive_reg = &AON_SOC_CIU->SR_CONFIG1;
|
||||
}
|
||||
*pull_reg = (*pull_reg & ~(3 << shift)) | (pull_val << shift);
|
||||
*drive_reg = (*drive_reg & ~(3 << shift)) | (drive_val << shift);
|
||||
|
||||
if (mux_config) {
|
||||
IO_MUX_SetPinMux(mux_config->pinLowMask, mux_config->pinHighMask,
|
||||
mux_config->gpioFcSetMask, mux_config->gpioFcClrMask,
|
||||
mux_config->fselSetMask, mux_config->fselClrMask,
|
||||
mux_config->ctimerSetMask, mux_config->ctimerClrMask,
|
||||
mux_config->sctimerSetMask, mux_config->sctimerClrMask
|
||||
);
|
||||
} else {
|
||||
// if no mux config is specified, assume default gpio settings and
|
||||
// no special function and just enable the IO
|
||||
IO_MUX_SetPinMux(bank ? 0 : BIT(PINNO(pin)), !bank ? 0 : BIT(PINNO(pin)),
|
||||
1 << 16, 0, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hal_gpio_input(uint16_t pin) {
|
||||
hal_gpio_init(pin, HAL_GPIO_MODE_INPUT, HAL_GPIO_TYPE_PULLUP_STRONGEST, NULL);
|
||||
}
|
||||
static inline void hal_gpio_output(uint16_t pin) {
|
||||
hal_gpio_init(pin, HAL_GPIO_MODE_OUTPUT, HAL_GPIO_TYPE_NOPULL_STRONGEST, NULL);
|
||||
}
|
||||
|
||||
static inline bool hal_gpio_read(uint16_t pin) {
|
||||
return GPIO->PIN[PINBANK(pin)] & (1 << PINNO(pin));
|
||||
}
|
||||
|
||||
static inline void hal_gpio_write(uint16_t pin, bool value) {
|
||||
if (value) {
|
||||
GPIO->SET[PINBANK(pin)] |= (1 << PINNO(pin));
|
||||
} else {
|
||||
GPIO->CLR[PINBANK(pin)] |= (1 << PINNO(pin));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void hal_gpio_toggle(uint16_t pin) {
|
||||
hal_gpio_read(pin) ? hal_gpio_write(pin, false) : hal_gpio_write(pin, true);
|
||||
}
|
||||
|
||||
#if defined(__ZEPHYR__)
|
||||
#undef MG_HAL_DISABLE_SYSTEM_INIT
|
||||
#define MG_HAL_DISABLE_SYSTEM_INIT 1
|
||||
#undef MG_HAL_DISABLE_NEWLIB
|
||||
#define MG_HAL_DISABLE_NEWLIB 1
|
||||
#undef MG_HAL_DISABLE_CLOCK
|
||||
#define MG_HAL_DISABLE_CLOCK 1
|
||||
#undef MG_HAL_DISABLE_RANDOM
|
||||
#define MG_HAL_DISABLE_RANDOM 1
|
||||
#undef MG_HAL_DISABLE_MILLIS
|
||||
#define MG_HAL_DISABLE_MILLIS 1
|
||||
#undef MG_HAL_DISABLE_UART
|
||||
#define MG_HAL_DISABLE_UART 1
|
||||
#undef MG_HAL_DISABLE_ETHERNET
|
||||
#define MG_HAL_DISABLE_ETHERNET 1
|
||||
#undef MG_HAL_SYSTICK
|
||||
#define MG_HAL_SYSTICK MG_HAL_SYSTICK_NONE
|
||||
#endif
|
||||
|
||||
static inline void hal_uart_init(USART_Type *uart, unsigned long baud) {
|
||||
if (uart == USART0) {
|
||||
IO_MUX_SetPinMux(IO_MUX_FC0_USART_DATA);
|
||||
} else if (uart == USART1) {
|
||||
IO_MUX_SetPinMux(IO_MUX_FC1_USART_DATA);
|
||||
} else if (uart == USART2) {
|
||||
IO_MUX_SetPinMux(IO_MUX_FC2_USART_DATA);
|
||||
} else if (uart == USART3) {
|
||||
IO_MUX_SetPinMux(IO_MUX_FC3_USART_DATA);
|
||||
} else if (uart == USART14) {
|
||||
IO_MUX_SetPinMux(IO_MUX_FC14_USART_DATA);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t uart_index = ((uint32_t)uart - USART0_BASE) / 0x1000;
|
||||
|
||||
// set FRG clk
|
||||
CLKCTL1->FLEXCOMM[uart_index].FRGCLKSEL = kCLOCK_FrgPllDiv;
|
||||
CLKCTL1->FLEXCOMM[uart_index].FRGCTL =
|
||||
(CLKCTL1_FRGCTL_MULT(0) | CLKCTL1_FRGCTL_DIV(255));
|
||||
CLOCK_AttachClk(CLKCTL1_TUPLE_MUXA(0x508 + uart_index * 0x20, 4));
|
||||
|
||||
// set FLEXCOMM to USART mode
|
||||
CLOCK_EnableClock(CLK_GATE_DEFINE(CLK_CTL1_PSCCTL0, uart_index + 8));
|
||||
uint32_t mask = BIT(uart_index + 8);
|
||||
RSTCTL1->PRSTCTL0_SET = mask;
|
||||
while ((RSTCTL1->PRSTCTL0 & mask) == 0)
|
||||
spin(1);
|
||||
RSTCTL1->PRSTCTL0_CLR = mask;
|
||||
while ((RSTCTL1->PRSTCTL0 & mask) == mask)
|
||||
spin(1);
|
||||
((FLEXCOMM_Type *)uart)->PSELID = 1;
|
||||
|
||||
uart->FIFOCFG |= USART_FIFOCFG_EMPTYTX_MASK |
|
||||
USART_FIFOCFG_ENABLETX_MASK; // empty and enable txFIFO
|
||||
uart->CFG = BIT(2) | BIT(0); // 8 bit data length and enable
|
||||
|
||||
// compute baud
|
||||
uint32_t uart_freq = SYS_FREQUENCY / FRG_PLL_CLKDIV;
|
||||
uint32_t best_diff = (uint32_t)-1, best_osr = 0xF, best_brg = (uint32_t)-1;
|
||||
uint32_t osr, brg, diff, current_baud;
|
||||
for (osr = 15; osr >= 4; osr--) {
|
||||
brg = (((uart_freq * 10) / ((osr + 1) * baud)) - 5) / 10;
|
||||
if (brg > 0xFFFF)
|
||||
continue;
|
||||
current_baud = uart_freq / ((osr + 1) * (brg + 1));
|
||||
diff =
|
||||
(baud < current_baud) ? (current_baud - baud) : (baud - current_baud);
|
||||
if (diff < best_diff) {
|
||||
best_diff = diff;
|
||||
best_osr = osr;
|
||||
best_brg = brg;
|
||||
}
|
||||
}
|
||||
uart->OSR = best_osr;
|
||||
uart->BRG = best_brg;
|
||||
}
|
||||
|
||||
static inline void hal_uart_write_byte(USART_Type *uart, uint8_t byte) {
|
||||
while ((uart->FIFOSTAT & USART_FIFOSTAT_TXNOTFULL_MASK) == 0)
|
||||
spin(1);
|
||||
uart->FIFOWR = byte;
|
||||
}
|
||||
static inline void hal_uart_write_buf(USART_Type *uart, char *buf, size_t len) {
|
||||
while (len-- > 0)
|
||||
hal_uart_write_byte(uart, *(uint8_t *)buf++);
|
||||
}
|
||||
|
||||
static inline void hal_rng_init(void) {
|
||||
trng_config_t config;
|
||||
TRNG_GetDefaultConfig(&config);
|
||||
TRNG_Init(TRNG, &config);
|
||||
}
|
||||
|
||||
static inline uint32_t hal_rng_read(void) {
|
||||
uint32_t value;
|
||||
TRNG_GetRandomData(TRNG, &value, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline void hal_ethernet_init(void) {
|
||||
CLOCK_EnableClock(kCLOCK_TddrMciEnetClk);
|
||||
hal_gpio_init(PIN(0, 25), HAL_GPIO_MODE_OUTPUT, IO_MUX_PinConfigPullUpDriveWeak,
|
||||
&(io_mux_config_t){ IO_MUX_ENET_CLK }); // ENET_CLK on GPIO_25
|
||||
IO_MUX_SetPinMux(
|
||||
IO_MUX_ENET_RX); // ENET_RX on pin GPIO_62, GPIO_63, GPIO_22, GPIO_23
|
||||
IO_MUX_SetPinMux(IO_MUX_ENET_TX); // ENET_TX on pin GPIO_58, GPIO_59, GPIO_60
|
||||
IO_MUX_SetPinMux(IO_MUX_ENET_MDIO); // ENET_MDIO on pin GPIO_56, GPIO_57
|
||||
|
||||
// reset ENET peripheral
|
||||
RSTCTL0->PRSTCTL1_SET = BIT(25);
|
||||
while ((RSTCTL0->PRSTCTL1 & BIT(25)) == 0)
|
||||
spin(1);
|
||||
RSTCTL0->PRSTCTL1_CLR = BIT(25);
|
||||
while ((RSTCTL0->PRSTCTL1 & BIT(25)) == BIT(25))
|
||||
spin(1);
|
||||
RSTCTL0->PRSTCTL1_SET = BIT(26);
|
||||
while ((RSTCTL0->PRSTCTL1 & BIT(26)) == 0)
|
||||
spin(1);
|
||||
RSTCTL0->PRSTCTL1_CLR = BIT(26);
|
||||
while ((RSTCTL0->PRSTCTL1 & BIT(26)) == BIT(26))
|
||||
spin(1);
|
||||
|
||||
// Enable clocks
|
||||
CLOCK_EnableClock(kCLOCK_EnetIpg);
|
||||
CLOCK_EnableClock(kCLOCK_EnetIpgS);
|
||||
|
||||
NVIC_EnableIRQ(ENET_IRQn);
|
||||
}
|
||||
extern void set_flexspi_clock(FLEXSPI_Type *base, uint32_t src,
|
||||
uint32_t divider);
|
||||
static inline void hal_clock_init(void) {
|
||||
const clock_avpll_config_t avpllConfig_BOARD_BootClockRUN = {
|
||||
.ch1Freq =
|
||||
kCLOCK_AvPllChFreq12p288m, // AVPLL channel frequency 12.288 MHz
|
||||
.ch2Freq = kCLOCK_AvPllChFreq64m, // AVPLL channel frequency 64 MHz
|
||||
.enableCali = true, // AVPLL calibration is enabled
|
||||
};
|
||||
// As NXP BOARD_BootClockRUN(): the ROM leaves the ELS glitch detector and
|
||||
// voltage sensors armed, trimmed for the boot clock. Disable them before
|
||||
// changing the clock, or the ROM resets the chip when it next uses ELS
|
||||
POWER_DisableGDetVSensors();
|
||||
if ((PMU->CAU_SLP_CTRL & PMU_CAU_SLP_CTRL_SOC_SLP_RDY_MASK) == 0) {
|
||||
CLOCK_EnableClock(
|
||||
kCLOCK_RefClkCauSlp); // Enable the CAU sleep clock for PMU
|
||||
}
|
||||
if ((SYSCTL2->SOURCE_CLK_GATE & SYSCTL2_SOURCE_CLK_GATE_REFCLK_SYS_CG_MASK) !=
|
||||
0) {
|
||||
CLOCK_EnableClock(kCLOCK_RefClkSys); // Enable the REFCLK_SYS clock
|
||||
}
|
||||
CLOCK_InitT3RefClk(kCLOCK_T3MciIrc48m); // Initialize T3 PLL and enable
|
||||
// outputs that are not clock gated
|
||||
CLOCK_EnableClock(
|
||||
kCLOCK_T3PllMciIrcClk); // Enable FFRO - T3 PLL 48/60 MHz IRC clock output
|
||||
CLOCK_EnableClock(
|
||||
kCLOCK_T3PllMci256mClk); // Enable T3 PLL 256 MHz clock output
|
||||
|
||||
// Move FLEXSPI clock source to T3 256M / 4 to avoid instruction/data fetch
|
||||
// issue in XIP when updating PLL and main clock.
|
||||
set_flexspi_clock(FLEXSPI, 6, 4);
|
||||
CLOCK_AttachClk(kSYSOSC_to_MAIN_CLK); // Set core clock to safe system
|
||||
// oscillator for initialization
|
||||
CLOCK_SetClkDiv(kCLOCK_DivSysCpuAhbClk,
|
||||
1); // Set system CPU AHB clock divider to 1
|
||||
CLOCK_InitTcpuRefClk(
|
||||
3120000000UL,
|
||||
kCLOCK_TcpuFlexspiDiv10); // Initialize TCPU PLL and enable outputs
|
||||
CLOCK_EnableClock(kCLOCK_TcpuMciClk); // Enable TCPU PLL MCI clock output
|
||||
CLOCK_InitTddrRefClk(
|
||||
kCLOCK_TddrFlexspiDiv10); // Initialize TDDR PLL and enable outputs
|
||||
CLOCK_EnableClock(
|
||||
kCLOCK_TddrMciFlexspiClk); // Enable TDDR PLL FlexSPI clock output
|
||||
CLOCK_InitAvPll(&avpllConfig_BOARD_BootClockRUN); // Initialize AVPLL and
|
||||
// enable both channels
|
||||
CLOCK_AttachClk(kRC32K_to_CLK32K); // Switch CLK32K to RC32K
|
||||
CLOCK_AttachClk(
|
||||
kSYSTICK_DIV_to_SYSTICK_CLK); // Switch SYSTICK_CLK to SYSTICK_DIV
|
||||
CLOCK_SetClkDiv(kCLOCK_DivAudioPllClk, 1U); // Set AUDIOPLLCLKDIV divider to 1
|
||||
CLOCK_SetClkDiv(kCLOCK_DivPllFrgClk,
|
||||
FRG_PLL_CLKDIV); // Set FRGPLLCLKDIV divider
|
||||
CLOCK_SetClkDiv(kCLOCK_DivMainPllClk, 1U); // Set MAINPLLCLKDIV divider to 1
|
||||
CLOCK_SetClkDiv(kCLOCK_DivAux0PllClk, 1U); // Set AUX0PLLCLKDIV divider to 1
|
||||
CLOCK_SetClkDiv(kCLOCK_DivSystickClk, 1U); // Set SYSTICKFCLKDIV divider to 1
|
||||
CLOCK_SetClkDiv(kCLOCK_DivPmuFclk, 5U); // Set PMUFCLKDIV divider to 5
|
||||
CLOCK_AttachClk(
|
||||
kMAIN_PLL_to_MAIN_CLK); // Select the main clock source for system clock
|
||||
set_flexspi_clock(FLEXSPI, 2U, 2U); // set_flexspi_clock
|
||||
|
||||
CLOCK_EnableClock(kCLOCK_HsGpio0);
|
||||
RSTCTL1->PRSTCTL1_SET = BIT(0); // Reset GPIO_0
|
||||
while ((RSTCTL1->PRSTCTL1 & BIT(0)) == 0)
|
||||
spin(1);
|
||||
RSTCTL1->PRSTCTL1_CLR = BIT(0);
|
||||
while ((RSTCTL1->PRSTCTL1 & BIT(0)) == BIT(0))
|
||||
spin(1);
|
||||
|
||||
CLOCK_EnableClock(kCLOCK_HsGpio1);
|
||||
RSTCTL1->PRSTCTL1_SET = BIT(1); // Reset GPIO_1
|
||||
while ((RSTCTL1->PRSTCTL1 & BIT(1)) == 0)
|
||||
spin(1);
|
||||
RSTCTL1->PRSTCTL1_CLR = BIT(1);
|
||||
while ((RSTCTL1->PRSTCTL1 & BIT(1)) == BIT(1))
|
||||
spin(1);
|
||||
}
|
||||
|
||||
#if defined(__MCUXPRESSO)
|
||||
#undef MG_HAL_DISABLE_SYSTEM_INIT
|
||||
#define MG_HAL_DISABLE_SYSTEM_INIT 1
|
||||
#undef MG_HAL_SBRK_NONE
|
||||
#define MG_HAL_SBRK_NONE 1
|
||||
#endif
|
||||
|
||||
static inline void hal_system_init(void) {
|
||||
SCB->CPACR |=
|
||||
(3UL << 0 * 2) | (3UL << 1 * 2) | // Enable PowerQuad (CPO/CP1)
|
||||
(3UL << 10 * 2) | (3UL << 11 * 2); // Enable FPU
|
||||
SCB->NSACR |= ((3UL << 0) | (3UL << 10));
|
||||
CACHE64_CTRL0->CCR = CACHE64_CTRL_CCR_INVW1_MASK | CACHE64_CTRL_CCR_INVW0_MASK;
|
||||
CACHE64_CTRL0->CCR |= CACHE64_CTRL_CCR_GO_MASK;
|
||||
while ((CACHE64_CTRL0->CCR & CACHE64_CTRL_CCR_GO_MASK) != 0) spin(1);
|
||||
// Enable cache, enable write buffer
|
||||
CACHE64_CTRL0->CCR = (CACHE64_CTRL_CCR_ENWRBUF_MASK | CACHE64_CTRL_CCR_ENCACHE_MASK);
|
||||
|
||||
// Set whole FlexSPI0 space to write through
|
||||
CACHE64_POLSEL0->REG0_TOP = 0x07FFFC00U;
|
||||
CACHE64_POLSEL0->REG1_TOP = 0x0U;
|
||||
CACHE64_POLSEL0->POLSEL = 0x1U;
|
||||
|
||||
__DSB();
|
||||
__ISB();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
ENTRY(Reset_Handler);
|
||||
MEMORY {
|
||||
flash(rx) : ORIGIN = 0x08000000, LENGTH = 0x4000000
|
||||
sram(rwx) : ORIGIN = 0x20000000, LENGTH = 0x123000 /* Top is reserved, as in NXP wireless linker scripts */
|
||||
}
|
||||
__StackTop = ORIGIN(sram) + LENGTH(sram);
|
||||
|
||||
SECTIONS {
|
||||
.boot_hdr : ALIGN(4) { FILL(0xFF); . = 0x400; __FLASH_BASE = .; KEEP(*(.flash_conf)) . = 0x1000; } > flash
|
||||
.vectors : { FILL(256) KEEP(*(.isr_vector)) } > flash
|
||||
.text : { *(.text.* *.text.*) } > flash
|
||||
.rodata : { *(.rodata*) } > flash
|
||||
|
||||
.iram : {
|
||||
_siram = .;
|
||||
*(.iram)
|
||||
*(CodeQuickAccess) /* NXP SDK drivers, e.g. fsl_power.c */
|
||||
*(DataQuickAccess)
|
||||
_eiram = .;
|
||||
} > sram AT > flash
|
||||
_siiram = LOADADDR(.iram);
|
||||
|
||||
.data : {
|
||||
_sdata = .;
|
||||
*(.first_data)
|
||||
*(.data SORT(.data.*))
|
||||
_edata = .;
|
||||
} > sram AT > flash
|
||||
_sidata = LOADADDR(.data);
|
||||
|
||||
.bss : { _sbss = .; *(.bss SORT(.bss.*) COMMON) _ebss = .; } > sram
|
||||
. = ALIGN(8);
|
||||
_end = .;
|
||||
|
||||
_image_start = LOADADDR(.text);
|
||||
_image_end = LOADADDR(.data) + SIZEOF(.data);
|
||||
_image_size = _image_end - _image_start;
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
// Copyright (c) 2026 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include "mongoose.h"
|
||||
#include "hal.h"
|
||||
|
||||
#ifndef UART_DEBUG
|
||||
#define UART_DEBUG USART3
|
||||
#endif
|
||||
|
||||
#define LED1 PIN(0, 12) // Green
|
||||
#define LED2 PIN(0, 0) // Blue
|
||||
#define LED3 PIN(0, 1) // Red
|
||||
|
||||
static void log_fn(char ch, void *param) {
|
||||
hal_uart_write_buf(param, &ch, 1);
|
||||
}
|
||||
|
||||
static void blink_task(void) {
|
||||
static uint64_t blink_timer = 0;
|
||||
if (hal_timer_expired(&blink_timer, 100, hal_get_tick())) {
|
||||
// hal_gpio_toggle(LED1);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r));
|
||||
}
|
||||
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
|
||||
// openssl req -x509 -key key.pem -out cert.pem -days 3650 -subj /CN=mg
|
||||
#define TLS_CERT \
|
||||
"-----BEGIN CERTIFICATE-----\n" \
|
||||
"MIIBgjCCASegAwIBAgIUIJOq77Qn7lHbH6WseMemeqorR50wCgYIKoZIzj0EAwIw\n" \
|
||||
"FjEUMBIGA1UEAwwLbnVjbGVvLWg3MjMwHhcNMjYxMDEyMTUwMjE5WhcNMzYxMDA5\n" \
|
||||
"MTUwMjE5WjAWMRQwEgYDVQQDDAtudWNsZW8taDcyMzBZMBMGByqGSM49AgEGCCqG\n" \
|
||||
"SM49AwEHA0IABB6IQJNmzrsdQL+2BnZYS7GYOaj7sz8f6cjBmz3n8D9Em19XodlU\n" \
|
||||
"uy4ybIvyhr766lL0ERoTmMUivb/lGmzx9C+jUzBRMB0GA1UdDgQWBBQqxMuc4gj7\n" \
|
||||
"MTMzreWlRMNI3QMqTzAfBgNVHSMEGDAWgBQqxMuc4gj7MTMzreWlRMNI3QMqTzAP\n" \
|
||||
"BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDY4xaJpuxq6b3rHK3b\n" \
|
||||
"lOIQyfkQX+orCRqQUkseplqpqwIhAKswZFNn+2zUNRRNY8+cHmdIwmdpsmxtoWYP\n" \
|
||||
"BCjX++LI\n" \
|
||||
"-----END CERTIFICATE-----\n"
|
||||
|
||||
#define TLS_KEY \
|
||||
"-----BEGIN EC PRIVATE KEY-----\n" \
|
||||
"MHcCAQEEIJ/GBl0TlD52q9PMg1KWaNIdXqp8b/HyIsABmBpqC+O1oAoGCCqGSM49\n" \
|
||||
"AwEHoUQDQgAEHohAk2bOux1Av7YGdlhLsZg5qPuzPx/pyMGbPefwP0SbX1eh2VS7\n" \
|
||||
"LjJsi/KGvvrqUvQRGhOYxSK9v+UabPH0Lw==\n" \
|
||||
"-----END EC PRIVATE KEY-----\n"
|
||||
|
||||
static void http_ev_handler(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_ACCEPT && c->is_tls) { // Accepted on the https:// listener
|
||||
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
|
||||
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());
|
||||
} else if (mg_match(hm->uri, mg_str("/api/crash"), NULL)) {
|
||||
__asm volatile("udf #0"); // Trigger HardFault
|
||||
} else {
|
||||
mg_http_reply(c, 200, "",
|
||||
"Hi from Mongoose!\n"
|
||||
"tick: %llu\n"
|
||||
"ram used: %zu\n"
|
||||
"ram free: %zu\n",
|
||||
hal_get_tick(), hal_ram_used(), hal_ram_free());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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);
|
||||
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_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
|
||||
|
||||
// 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);
|
||||
|
||||
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);
|
||||
blink_task();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#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,236 @@
|
||||
// Load NXP BLE/802.15.4 combo firmware to CPU2 and start it.
|
||||
// Port of NXP mcux-component conn_fwloader: fsl_loader_utils.c (SB3 loader,
|
||||
// load_service_monolithic) and nboot_hal.c (manifest parameters).
|
||||
// Copyright 2024 NXP
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#include "hal.h"
|
||||
#include "fsl_ocotp.h"
|
||||
#include "fsl_power.h"
|
||||
#include "fsl_romapi_nboot.h"
|
||||
#include "fsl_romapi_otp.h"
|
||||
#include "fsl_kb_api.h"
|
||||
#include "fsl_sbloader_v3.h"
|
||||
|
||||
// The firmware image, linked into our flash. Signed SB3 container, see
|
||||
// https://github.com/NXP/wifi_nb_fw, rw61x/rw61x_sb_ble_15d4_combo_a2.bin
|
||||
__asm__(
|
||||
".section .rodata.radio_fw,\"a\"\n"
|
||||
".balign 16\n"
|
||||
"radio_fw:\n"
|
||||
".incbin \"" RADIO_FW "\"\n"
|
||||
".balign 4\n"
|
||||
"radio_fw_end:\n"
|
||||
".previous\n");
|
||||
extern const uint8_t radio_fw[], radio_fw_end[];
|
||||
|
||||
#define CIU2_RST_SW3 (*(volatile uint32_t *) 0x4424011cU)
|
||||
#define CIU2_RST_SW3_CPU2_RST BIT(4)
|
||||
#define CPU2_MAGIC (*(volatile uint32_t *) 0x443c0000U) // Set by firmware
|
||||
#define CPU2_MAGIC_VALUE 0xabcdef89U
|
||||
#define OTP_LCS_FUSE_IDX 45U // Life cycle state, and its redundant copy
|
||||
#define OTP_PCK_BLOB_FUSE_IDX 116U // Part common key blob, 48 bytes
|
||||
|
||||
// SB3 loader state. See fsl_ldr_Context_v3_t
|
||||
struct sb3 {
|
||||
nboot_context_t nboot; // ROM nboot context
|
||||
uint8_t buf[MAX(NBOOT_SB3_MANIFEST_MAX_SIZE_IN_BYTES,
|
||||
NBOOT_SB3_BLOCK_MAX_SIZE_IN_BYTES)]; // Block being read
|
||||
uint32_t buf_size; // Bytes needed for the current block
|
||||
uint32_t buf_pos; // Bytes in buf so far
|
||||
bool header_done; // Manifest (block 0) processed
|
||||
uint32_t data_offset; // Data offset within a block
|
||||
uint32_t blocks; // Data blocks processed
|
||||
uint8_t *data; // Data of the current block
|
||||
uint32_t pos; // Position in data
|
||||
bool in_section; // Inside a data section
|
||||
uint32_t section_done; // Bytes of the section handled
|
||||
sb3_section_header_t section;
|
||||
bool in_range; // Inside a data range
|
||||
uint32_t range_gap; // Padding after the range, 16 byte alignment
|
||||
sb3_data_range_header_t range;
|
||||
bool has_expansion; // Range expansion read
|
||||
bool executed; // Execute command seen: firmware loaded
|
||||
};
|
||||
static struct sb3 s_sb3;
|
||||
|
||||
static uint32_t antipole(uint32_t v) {
|
||||
return (v & 0xffffU) | (~(v & 0xffffU) << 16);
|
||||
}
|
||||
|
||||
// Manifest parameters: root of trust for NXP signed images, rev A2 chips.
|
||||
// See nboot_hal_get_sb3_manifest_params(), nboot_hal.c
|
||||
static bool manifest_params(nboot_sb3_load_manifest_parms_t *p) {
|
||||
static const uint32_t rkh_a2[8] = {0xE7C7E9BB, 0x12C8C535, 0x37E61148,
|
||||
0x2BE7F18C, 0x8F0E3094, 0xB2BA7F32,
|
||||
0xEC9B4ECB, 0xAD9FC941};
|
||||
uint32_t i, lcs = OCOTP->OTP_SHADOW[OTP_LCS_FUSE_IDX];
|
||||
memset(p, 0, sizeof(*p));
|
||||
if ((lcs & 0xff) != ((lcs >> 8) & 0xff)) return false; // Corrupt LCS
|
||||
for (i = 0; i < NBOOT_ROOT_CERT_COUNT; i++) {
|
||||
p->soc_RoTNVM.soc_rootKeyRevocation[i] = kNBOOT_RootKey_Enabled;
|
||||
}
|
||||
memcpy(p->soc_RoTNVM.soc_rkh, rkh_a2, sizeof(rkh_a2));
|
||||
p->soc_RoTNVM.soc_numberOfRootKeys = 1;
|
||||
for (i = 1; i < NBOOT_ROOT_CERT_COUNT; i++) {
|
||||
p->soc_RoTNVM.soc_rootKeyUsage[i] = kNBOOT_RootKeyUsage_Unused;
|
||||
}
|
||||
p->soc_RoTNVM.soc_rootKeyTypeAndLength = kNBOOT_RootKey_Ecdsa_P256;
|
||||
p->soc_RoTNVM.soc_lifecycle = (nboot_soc_lifecycle_t) antipole(lcs & 0xff);
|
||||
for (i = 0; i < sizeof(p->pckBlob) / 4; i++) {
|
||||
if (OCOTP_OtpFuseRead(OTP_PCK_BLOB_FUSE_IDX + i,
|
||||
(uint32_t *) &p->pckBlob[4 * i]) != kStatus_Success) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Manifest (block 0): verify, learn the block layout. See ldr_DoHeader_v3()
|
||||
static bool sb3_header(struct sb3 *s) {
|
||||
nboot_sb3_header_t *h = (nboot_sb3_header_t *) s->buf;
|
||||
nboot_sb3_load_manifest_parms_t parms;
|
||||
if (s->buf_size == sizeof(nboot_sb3_header_t)) { // Header only so far
|
||||
s->buf_size = h->imageTotalLength;
|
||||
s->buf_pos = sizeof(nboot_sb3_header_t);
|
||||
return s->buf_size <= sizeof(s->buf);
|
||||
}
|
||||
if (!manifest_params(&parms)) return false;
|
||||
// Protected status: the status code is in the low 32 bits
|
||||
if ((nboot_status_t) nboot_sb3_load_manifest(&s->nboot, (uint32_t *) h,
|
||||
&parms) != kStatus_NBOOT_Success) {
|
||||
return false;
|
||||
}
|
||||
s->data_offset = 4 + h->certificateBlockOffset - sizeof(nboot_sb3_header_t);
|
||||
s->buf_size = s->data_offset + NBOOT_SB3_CHUNK_SIZE_IN_BYTES;
|
||||
s->header_done = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Copy (part of) the current load range to its destination
|
||||
static void sb3_load(struct sb3 *s) {
|
||||
uint32_t avail = NBOOT_SB3_CHUNK_SIZE_IN_BYTES - s->pos;
|
||||
uint32_t n = s->range.length < avail ? s->range.length : avail;
|
||||
memcpy((void *) s->range.startAddress, &s->data[s->pos], n);
|
||||
s->pos += n, s->section_done += n;
|
||||
s->range.startAddress += n, s->range.length -= n;
|
||||
if (s->range.length == 0) { // Range done, skip alignment padding
|
||||
s->in_range = false;
|
||||
s->pos += s->range_gap, s->section_done += s->range_gap;
|
||||
}
|
||||
}
|
||||
|
||||
// Process one decrypted data block. See ldr_DoBlock()
|
||||
static bool sb3_block(struct sb3 *s) {
|
||||
while (s->pos < NBOOT_SB3_CHUNK_SIZE_IN_BYTES) {
|
||||
if (!s->in_section) { // New section
|
||||
memcpy(&s->section, &s->data[s->pos], sizeof(s->section));
|
||||
if (s->section.sectionType == kSectionNone) break; // Rest is unused
|
||||
if (s->section.sectionType != kSectionDataRange) return false;
|
||||
s->in_section = true, s->section_done = 0, s->in_range = false;
|
||||
s->pos += sizeof(sb3_section_header_t);
|
||||
}
|
||||
if (!s->in_range) { // New range
|
||||
memcpy(&s->range, &s->data[s->pos], sizeof(s->range));
|
||||
if (s->range.tag != SB3_DATA_RANGE_HEADER_TAG) return false;
|
||||
s->in_range = true, s->has_expansion = false, s->range_gap = 0;
|
||||
s->pos += sizeof(sb3_data_range_header_t);
|
||||
s->section_done += sizeof(sb3_data_range_header_t);
|
||||
if (s->range.cmd == kSB3_CmdLoad) {
|
||||
s->range_gap = (SB3_DATA_ALIGNMENT_SIZE_IN_BYTE -
|
||||
(s->range.length & 15U)) & 15U;
|
||||
}
|
||||
}
|
||||
if (s->range.cmd == kSB3_CmdLoad) {
|
||||
if (!s->has_expansion) {
|
||||
if (s->pos == NBOOT_SB3_CHUNK_SIZE_IN_BYTES) break; // Next block
|
||||
s->has_expansion = true;
|
||||
s->pos += sizeof(sb3_data_range_expansion_t);
|
||||
s->section_done += sizeof(sb3_data_range_expansion_t);
|
||||
}
|
||||
if (s->pos < NBOOT_SB3_CHUNK_SIZE_IN_BYTES) sb3_load(s);
|
||||
} else if (s->range.cmd == kSB3_CmdExecute) {
|
||||
s->executed = true;
|
||||
return true;
|
||||
} else {
|
||||
s->in_range = false; // Other commands: nothing to do
|
||||
}
|
||||
if (s->section_done == s->section.length) s->in_section = false;
|
||||
if (s->section_done > s->section.length) return false;
|
||||
}
|
||||
s->pos = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Feed image bytes, process complete blocks. See fsl_sbloader_pump()
|
||||
static bool sb3_pump(struct sb3 *s, const uint8_t *p, uint32_t len) {
|
||||
while (len > 0 && !s->executed) {
|
||||
uint32_t n = s->buf_size - s->buf_pos;
|
||||
if (n > len) n = len;
|
||||
memcpy(&s->buf[s->buf_pos], p, n);
|
||||
s->buf_pos += n, p += n, len -= n;
|
||||
if (s->buf_pos < s->buf_size) break;
|
||||
s->buf_pos = 0;
|
||||
if (!s->header_done) {
|
||||
if (!sb3_header(s)) return false;
|
||||
} else {
|
||||
if (s->blocks >= s->nboot.totalBlocks) return false;
|
||||
if ((nboot_status_t) nboot_sb3_load_block(
|
||||
&s->nboot, (uint32_t *) s->buf) != kStatus_NBOOT_Success) {
|
||||
return false;
|
||||
}
|
||||
s->blocks++;
|
||||
s->data = &s->buf[s->data_offset];
|
||||
if (!sb3_block(s)) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Authenticate the image with the ROM, copy it to CPU2 memory.
|
||||
// See loader_process_sb_file()
|
||||
static bool sb3_process(const uint8_t *img, size_t len) {
|
||||
struct sb3 *s = &s_sb3;
|
||||
bool ok = false;
|
||||
memset(s, 0, sizeof(*s));
|
||||
s->buf_size = sizeof(nboot_sb3_header_t);
|
||||
// Start ELS only if it is off: the ROM keeps keys in it after boot
|
||||
if ((CLKCTL0->PSCCTL0 & CLKCTL0_PSCCTL0_ELS_MASK) == 0 ||
|
||||
(CLKCTL0->PSCCTL1 & CLKCTL0_PSCCTL1_ELS_APB_MASK) == 0 ||
|
||||
(RSTCTL0->PRSTCTL0 & RSTCTL0_PRSTCTL0_ELS_MASK) != 0) {
|
||||
CLOCK_EnableClock(kCLOCK_Els);
|
||||
CLOCK_EnableClock(kCLOCK_ElsApb);
|
||||
RESET_PeripheralReset(kELS_RST_SHIFT_RSTn);
|
||||
}
|
||||
if (nboot_context_init(&s->nboot) == kStatus_NBOOT_Success) {
|
||||
ok = sb3_pump(s, img, (uint32_t) len) && s->executed;
|
||||
(void) nboot_context_deinit(&s->nboot);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
// Load the radio firmware to CPU2 and start it. See sb3_fw_reset() and
|
||||
// load_service_monolithic(), fsl_loader_utils.c. Call before changing the
|
||||
// boot clock: the ROM trims the ELS glitch detector for it, and after the
|
||||
// clock change ELS reports glitch events. Interrupts may be off.
|
||||
// Return 0 on success, or the failed step: 1 chip revision, 2 OTP, 3 image
|
||||
// load, 4 firmware start
|
||||
int hal_radio_init(void) {
|
||||
uint32_t n = 100000000; // Firmware start timeout, loop iterations
|
||||
bool ok;
|
||||
if ((SOCCTRL->CHIP_INFO & 15U) != 2U) return 1; // Image is for rev A2
|
||||
POWER_PowerOffBle();
|
||||
spin(100000);
|
||||
if (otp_init(0) != kStatus_Success) return 2;
|
||||
POWER_PowerOnBle();
|
||||
spin(100000);
|
||||
CPU2_MAGIC = 0;
|
||||
ok = sb3_process(radio_fw, (size_t) (radio_fw_end - radio_fw));
|
||||
(void) otp_deinit();
|
||||
if (!ok) return 3;
|
||||
CIU2_RST_SW3 |= CIU2_RST_SW3_CPU2_RST;
|
||||
while (CPU2_MAGIC != CPU2_MAGIC_VALUE) {
|
||||
if (--n == 0) return 4; // Firmware did not start
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
#include "RW612.h"
|
||||
|
||||
void Reset_Handler(void); // Defined below
|
||||
void Dummy_Handler(void); // Defined below
|
||||
void SysTick_Handler(void); // Defined in main.c
|
||||
void SystemInit(void); // Defined in main.c, called by reset handler
|
||||
void __StackTop(void); // Defined in link.ld
|
||||
void _image_size(void);
|
||||
|
||||
#define WEAK __attribute__((weak, alias("Default_Handler")))
|
||||
|
||||
void Default_Handler(void);
|
||||
WEAK void NMI_Handler(void);
|
||||
WEAK void HardFault_Handler(void);
|
||||
WEAK void MemManage_Handler(void);
|
||||
WEAK void BusFault_Handler(void);
|
||||
WEAK void UsageFault_Handler(void);
|
||||
WEAK void SecureFault_Handler(void);
|
||||
WEAK void SVC_Handler(void);
|
||||
WEAK void DebugMon_Handler(void);
|
||||
WEAK void PendSV_Handler(void);
|
||||
WEAK void SysTick_Handler(void);
|
||||
WEAK void IntDefaultHandler(void);
|
||||
WEAK void WDT0_IRQHandler(void);
|
||||
WEAK void DMA0_IRQHandler(void);
|
||||
WEAK void GPIO_INTA_IRQHandler(void);
|
||||
WEAK void GPIO_INTB_IRQHandler(void);
|
||||
WEAK void PIN_INT0_IRQHandler(void);
|
||||
WEAK void PIN_INT1_IRQHandler(void);
|
||||
WEAK void PIN_INT2_IRQHandler(void);
|
||||
WEAK void PIN_INT3_IRQHandler(void);
|
||||
WEAK void UTICK_IRQHandler(void);
|
||||
WEAK void MRT_IRQHandler(void);
|
||||
WEAK void CTIMER0_IRQHandler(void);
|
||||
WEAK void CTIMER1_IRQHandler(void);
|
||||
WEAK void SCT0_IRQHandler(void);
|
||||
WEAK void CTIMER3_IRQHandler(void);
|
||||
WEAK void FLEXCOMM0_IRQHandler(void);
|
||||
WEAK void FLEXCOMM1_IRQHandler(void);
|
||||
WEAK void FLEXCOMM2_IRQHandler(void);
|
||||
WEAK void FLEXCOMM3_IRQHandler(void);
|
||||
WEAK void Reserved34_IRQHandler(void);
|
||||
WEAK void Reserved35_IRQHandler(void);
|
||||
WEAK void FLEXCOMM14_IRQHandler(void);
|
||||
WEAK void Reserved37_IRQHandler(void);
|
||||
WEAK void Reserved38_IRQHandler(void);
|
||||
WEAK void GFMRT_IRQHandler(void);
|
||||
WEAK void Reserved40_IRQHandler(void);
|
||||
WEAK void DMIC_IRQHandler(void);
|
||||
WEAK void WKDEEPSLEEP_IRQHandler(void);
|
||||
WEAK void HYPERVISOR_IRQHandler(void);
|
||||
WEAK void SECUREVIOLATION_IRQHandler(void);
|
||||
WEAK void HWVAD_IRQHandler(void);
|
||||
WEAK void Reserved46_IRQHandler(void);
|
||||
WEAK void Reserved47_IRQHandler(void);
|
||||
WEAK void RTC_IRQHandler(void);
|
||||
WEAK void Reserved49_IRQHandler(void);
|
||||
WEAK void Reserved50_IRQHandler(void);
|
||||
WEAK void PIN_INT4_IRQHandler(void);
|
||||
WEAK void PIN_INT5_IRQHandler(void);
|
||||
WEAK void PIN_INT6_IRQHandler(void);
|
||||
WEAK void PIN_INT7_IRQHandler(void);
|
||||
WEAK void CTIMER2_IRQHandler(void);
|
||||
WEAK void CTIMER4_IRQHandler(void);
|
||||
WEAK void OS_EVENT_TIMER_IRQHandler(void);
|
||||
WEAK void FLEXSPI_IRQHandler(void);
|
||||
WEAK void Reserved59_IRQHandler(void);
|
||||
WEAK void Reserved60_IRQHandler(void);
|
||||
WEAK void Reserved61_IRQHandler(void);
|
||||
WEAK void SDIO_IRQHandler(void);
|
||||
WEAK void SGPIO_INTA_IRQHandler(void);
|
||||
WEAK void SGPIO_INTB_IRQHandler(void);
|
||||
WEAK void Reserved65_IRQHandler(void);
|
||||
WEAK void USB_IRQHandler(void);
|
||||
WEAK void Reserved67_IRQHandler(void);
|
||||
WEAK void Reserved68_IRQHandler(void);
|
||||
WEAK void Reserved69_IRQHandler(void);
|
||||
WEAK void DMA1_IRQHandler(void);
|
||||
WEAK void PUF_IRQHandler(void);
|
||||
WEAK void POWERQUAD_IRQHandler(void);
|
||||
WEAK void Reserved73_IRQHandler(void);
|
||||
WEAK void Reserved74_IRQHandler(void);
|
||||
WEAK void Reserved75_IRQHandler(void);
|
||||
WEAK void Reserved76_IRQHandler(void);
|
||||
WEAK void LCD_IRQHandler(void);
|
||||
WEAK void CAPTIMER_IRQHandler(void);
|
||||
WEAK void Reserved79_IRQHandler(void);
|
||||
WEAK void W2MWKUP_DONE0_IRQHandler(void);
|
||||
WEAK void W2MWKUP_DONE1_IRQHandler(void);
|
||||
WEAK void W2MWKUP_DONE2_IRQHandler(void);
|
||||
WEAK void W2MWKUP_DONE3_IRQHandler(void);
|
||||
WEAK void W2MWKUP_DONE4_IRQHandler(void);
|
||||
WEAK void W2MWKUP_DONE5_IRQHandler(void);
|
||||
WEAK void W2MWKUP_DONE6_IRQHandler(void);
|
||||
WEAK void W2MWKUP_DONE7_IRQHandler(void);
|
||||
WEAK void W2MWKUP0_IRQHandler(void);
|
||||
WEAK void W2MWKUP1_IRQHandler(void);
|
||||
WEAK void WL_MCI_INT0_IRQHandler(void);
|
||||
WEAK void WL_MCI_INT1_IRQHandler(void);
|
||||
WEAK void WL_MCI_INT2_IRQHandler(void);
|
||||
WEAK void WL_MCI_INT3_IRQHandler(void);
|
||||
WEAK void WL_MCI_INT4_IRQHandler(void);
|
||||
WEAK void WL_MCI_INT5_IRQHandler(void);
|
||||
WEAK void WL_MCI_INT6_IRQHandler(void);
|
||||
WEAK void WL_MCI_INT7_IRQHandler(void);
|
||||
WEAK void B2MWKUP_DONE0_IRQHandler(void);
|
||||
WEAK void B2MWKUP_DONE1_IRQHandler(void);
|
||||
WEAK void B2MWKUP_DONE2_IRQHandler(void);
|
||||
WEAK void B2MWKUP_DONE3_IRQHandler(void);
|
||||
WEAK void B2MWKUP_DONE4_IRQHandler(void);
|
||||
WEAK void B2MWKUP_DONE5_IRQHandler(void);
|
||||
WEAK void B2MWKUP_DONE6_IRQHandler(void);
|
||||
WEAK void B2MWKUP_DONE7_IRQHandler(void);
|
||||
WEAK void B2MWKUP0_IRQHandler(void);
|
||||
WEAK void B2MWKUP1_IRQHandler(void);
|
||||
WEAK void BLE_MCI_INT0_IRQHandler(void);
|
||||
WEAK void BLE_MCI_INT1_IRQHandler(void);
|
||||
WEAK void BLE_MCI_INT2_IRQHandler(void);
|
||||
WEAK void BLE_MCI_INT3_IRQHandler(void);
|
||||
WEAK void BLE_MCI_INT4_IRQHandler(void);
|
||||
WEAK void BLE_MCI_INT5_IRQHandler(void);
|
||||
WEAK void BLE_MCI_INT6_IRQHandler(void);
|
||||
WEAK void BLE_MCI_INT7_IRQHandler(void);
|
||||
WEAK void PIN0_INT_IRQHandler(void);
|
||||
WEAK void PIN1_INT_IRQHandler(void);
|
||||
WEAK void ELS_IRQHandler(void);
|
||||
WEAK void ELS_GDET_IRQHandler(void);
|
||||
WEAK void ELS_GDET_UM_IRQHandler(void);
|
||||
WEAK void PKC_INT_IRQHandler(void);
|
||||
WEAK void PKC_ERR_IRQHandler(void);
|
||||
WEAK void CDOG_INT_IRQHandler(void);
|
||||
WEAK void GAU_DAC_IRQHandler(void);
|
||||
WEAK void GAU_ACOMP_WKUP_IRQHandler(void);
|
||||
WEAK void GAU_ACOMP_IRQHandler(void);
|
||||
WEAK void GAU_ADC1_IRQHandler(void);
|
||||
WEAK void GAU_ADC0_IRQHandler(void);
|
||||
WEAK void USIM_IRQHandler(void);
|
||||
WEAK void OTP_IRQHandler(void);
|
||||
WEAK void ENET_IRQHandler(void);
|
||||
WEAK void ENET_TIMER_IRQHandler(void);
|
||||
WEAK void PMIP_IRQHandler(void);
|
||||
WEAK void PMIP_CHANGE_IRQHandler(void);
|
||||
WEAK void ITRC_IRQHandler(void);
|
||||
WEAK void Reserved136_IRQHandler(void);
|
||||
WEAK void Reserved137_IRQHandler(void);
|
||||
WEAK void Reserved138_IRQHandler(void);
|
||||
WEAK void TRNG_IRQHandler(void);
|
||||
WEAK void ACC_C_INT_IRQHandler(void);
|
||||
WEAK void ACC_S_INT_IRQHandler(void);
|
||||
WEAK void WACC_IRQHandler(void);
|
||||
WEAK void BACC_IRQHandler(void);
|
||||
WEAK void GDMA_IRQHandler(void);
|
||||
|
||||
|
||||
#define WEAK_ALIAS __attribute__((weak, alias("Default_Handler")))
|
||||
|
||||
|
||||
__attribute__((section(".vectors"))) void (*const tab[16 + 156])(void) = {
|
||||
&__StackTop, // The initial stack pointer
|
||||
Reset_Handler, // The reset handler
|
||||
NMI_Handler, // NMI Handler
|
||||
HardFault_Handler, // Hard Fault Handler
|
||||
MemManage_Handler, // MPU Fault Handler
|
||||
BusFault_Handler, // Bus Fault Handler
|
||||
UsageFault_Handler, // Usage Fault Handler
|
||||
SecureFault_Handler, // Secure Fault Handler
|
||||
&_image_size, // Image size
|
||||
0, // Image type
|
||||
0, // Reserved
|
||||
SVC_Handler, // SVCall Handler
|
||||
DebugMon_Handler, // Debug Monitor Handler
|
||||
(void (*)())tab, // Image load address
|
||||
PendSV_Handler, // PendSV Handler
|
||||
SysTick_Handler, // SysTick Handler
|
||||
|
||||
// Chip Level - RW612
|
||||
WDT0_IRQHandler, // 16 : Windowed watchdog timer 0 (WDT0)(Cortex-M33 watchdog)
|
||||
DMA0_IRQHandler, // 17 : Direct memory access (DMA) controller 0 (secure or Cortex-M33 DMA)
|
||||
GPIO_INTA_IRQHandler, // 18 : GPIO interrupt A
|
||||
GPIO_INTB_IRQHandler, // 19 : GPIO interrupt B
|
||||
PIN_INT0_IRQHandler, // 20 : Pin interrupt 0 or pattern match engine slice 0
|
||||
PIN_INT1_IRQHandler, // 21 : Pin interrupt 1 or pattern match engine slice 1
|
||||
PIN_INT2_IRQHandler, // 22 : Pin interrupt 2 or pattern match engine slice 2
|
||||
PIN_INT3_IRQHandler, // 23 : Pin interrupt 3 or pattern match engine slice 3
|
||||
UTICK_IRQHandler, // 24 : Micro-tick Timer (UTICK)
|
||||
MRT_IRQHandler, // 25 : Multi-Rate Timer (MRT). Global MRT interrupts
|
||||
CTIMER0_IRQHandler, // 26 : Standard counter/timer CTIMER0
|
||||
CTIMER1_IRQHandler, // 27 : Standard counter/timer CTIMER1
|
||||
SCT0_IRQHandler, // 28 : SCTimer/PWM
|
||||
CTIMER3_IRQHandler, // 29 : Standard counter/timer CTIMER3
|
||||
FLEXCOMM0_IRQHandler, // 30 : Flexcomm Interface 0 (USART, SPI, I2C, I2S)
|
||||
FLEXCOMM1_IRQHandler, // 31 : Flexcomm Interface 1 (USART, SPI, I2C, I2S)
|
||||
FLEXCOMM2_IRQHandler, // 32 : Flexcomm Interface 2 (USART, SPI, I2C, I2S)
|
||||
FLEXCOMM3_IRQHandler, // 33 : Flexcomm Interface 3 (USART, SPI, I2C, I2S)
|
||||
Reserved34_IRQHandler, // 34 : Reserved interrupt
|
||||
Reserved35_IRQHandler, // 35 : Reserved interrupt
|
||||
FLEXCOMM14_IRQHandler, // 36 : Flexcomm Interface 14 (USART, SPI, I2C, I2S)
|
||||
Reserved37_IRQHandler, // 37 : Reserved interrupt
|
||||
Reserved38_IRQHandler, // 38 : Reserved interrupt
|
||||
GFMRT_IRQHandler, // 39 : Free Multi-rate timer (GFMRT). Global MRT interrupts
|
||||
Reserved40_IRQHandler, // 40 : Reserved interrupt
|
||||
DMIC_IRQHandler, // 41 : Digital microphone (DMIC) and DMIC subsystem
|
||||
WKDEEPSLEEP_IRQHandler, // 42 : Wake-up from deep sleep
|
||||
HYPERVISOR_IRQHandler, // 43 : Hypervisor service software interrupt
|
||||
SECUREVIOLATION_IRQHandler, // 44 : Secure violation
|
||||
HWVAD_IRQHandler, // 45 : Hardware Voice Activity Detector
|
||||
Reserved46_IRQHandler, // 46 : Reserved interrupt
|
||||
Reserved47_IRQHandler, // 47 : Reserved interrupt
|
||||
RTC_IRQHandler, // 48 : RTC alarm and wake-up
|
||||
Reserved49_IRQHandler, // 49 : Reserved interrupt
|
||||
Reserved50_IRQHandler, // 50 : Reserved interrupt
|
||||
PIN_INT4_IRQHandler, // 51 : Pin interrupt 4 or pattern match engine slice 4
|
||||
PIN_INT5_IRQHandler, // 52 : Pin interrupt 5 or pattern match engine slice 5
|
||||
PIN_INT6_IRQHandler, // 53 : Pin interrupt 6 or pattern match engine slice 6
|
||||
PIN_INT7_IRQHandler, // 54 : Pin interrupt 7 or pattern match engine slice 7
|
||||
CTIMER2_IRQHandler, // 55 : Standard counter/timer CTIMER2
|
||||
CTIMER4_IRQHandler, // 56 : Standard counter/timer CTIMER4
|
||||
OS_EVENT_TIMER_IRQHandler, // 57 : OS event timer 0
|
||||
FLEXSPI_IRQHandler, // 58 : FLEXSPI interface
|
||||
Reserved59_IRQHandler, // 59 : Reserved interrupt
|
||||
Reserved60_IRQHandler, // 60 : Reserved interrupt
|
||||
Reserved61_IRQHandler, // 61 : Reserved interrupt
|
||||
SDIO_IRQHandler, // 62 : The secure digital interface
|
||||
SGPIO_INTA_IRQHandler, // 63 : Secure GPIO interrupt A
|
||||
SGPIO_INTB_IRQHandler, // 64 : Secure GPIO interrupt B
|
||||
Reserved65_IRQHandler, // 65 : Reserved interrupt
|
||||
USB_IRQHandler, // 66 : High-speed USB device/host
|
||||
Reserved67_IRQHandler, // 67 : Reserved interrupt
|
||||
Reserved68_IRQHandler, // 68 : Reserved interrupt
|
||||
Reserved69_IRQHandler, // 69 : Reserved interrupt
|
||||
DMA1_IRQHandler, // 70 : DMA controller 1 (non-secure or HiFi 4 DMA)
|
||||
PUF_IRQHandler, // 71 : Physical Unclonable Function
|
||||
POWERQUAD_IRQHandler, // 72 : PowerQuad math coprocessor
|
||||
Reserved73_IRQHandler, // 73 : Reserved interrupt
|
||||
Reserved74_IRQHandler, // 74 : Reserved interrupt
|
||||
Reserved75_IRQHandler, // 75 : Reserved interrupt
|
||||
Reserved76_IRQHandler, // 76 : Reserved interrupt
|
||||
LCD_IRQHandler, // 77 : LCDIC
|
||||
CAPTIMER_IRQHandler, // 78 : Capture timer
|
||||
Reserved79_IRQHandler, // 79 : Reserved interrupt
|
||||
W2MWKUP_DONE0_IRQHandler, // 80 : Wi-Fi to MCU, wakeup done 0
|
||||
W2MWKUP_DONE1_IRQHandler, // 81 : Wi-Fi to MCU, wakeup done 1
|
||||
W2MWKUP_DONE2_IRQHandler, // 82 : Wi-Fi to MCU, wakeup done 2
|
||||
W2MWKUP_DONE3_IRQHandler, // 83 : Wi-Fi to MCU, wakeup done 3
|
||||
W2MWKUP_DONE4_IRQHandler, // 84 : Wi-Fi to MCU, wakeup done 4
|
||||
W2MWKUP_DONE5_IRQHandler, // 85 : Wi-Fi to MCU, wakeup done 5
|
||||
W2MWKUP_DONE6_IRQHandler, // 86 : Wi-Fi to MCU, wakeup done 6
|
||||
W2MWKUP_DONE7_IRQHandler, // 87 : Wi-Fi to MCU, wakeup done 7
|
||||
W2MWKUP0_IRQHandler, // 88 : Wi-Fi to MCU, wakeup signal 0
|
||||
W2MWKUP1_IRQHandler, // 89 : Wi-Fi to MCU, wakueup signal 1
|
||||
WL_MCI_INT0_IRQHandler, // 90 : Wi-Fi to MCU interrupt 0
|
||||
WL_MCI_INT1_IRQHandler, // 91 : Reserved for Wi-Fi to MCU
|
||||
WL_MCI_INT2_IRQHandler, // 92 : Reserved for Wi-Fi to MCU
|
||||
WL_MCI_INT3_IRQHandler, // 93 : Reserved for Wi-Fi to MCU
|
||||
WL_MCI_INT4_IRQHandler, // 94 : Reserved for Wi-Fi to MCU
|
||||
WL_MCI_INT5_IRQHandler, // 95 : Reserved for Wi-Fi to MCU
|
||||
WL_MCI_INT6_IRQHandler, // 96 : Reserved for Wi-Fi to MCU
|
||||
WL_MCI_INT7_IRQHandler, // 97 : Reserved for Wi-Fi to MCU
|
||||
B2MWKUP_DONE0_IRQHandler, // 98 : Bluetooth LE/802.15.4 radio to MCU, wakeup done 0
|
||||
B2MWKUP_DONE1_IRQHandler, // 99 : Bluetooth LE/802.15.4 radio to MCU, wakeup done 1
|
||||
B2MWKUP_DONE2_IRQHandler, // 100: Bluetooth LE/802.15.4 radio to MCU, wakeup done 2
|
||||
B2MWKUP_DONE3_IRQHandler, // 101: Bluetooth LE/802.15.4 radio to MCU, wakeup done 3
|
||||
B2MWKUP_DONE4_IRQHandler, // 102: Bluetooth LE/802.15.4 radio to MCU, wakeup done 4
|
||||
B2MWKUP_DONE5_IRQHandler, // 103: Bluetooth LE/802.15.4 radio to MCU, wakeup done 5
|
||||
B2MWKUP_DONE6_IRQHandler, // 104: Bluetooth LE/802.15.4 radio to MCU, wakeup done 6
|
||||
B2MWKUP_DONE7_IRQHandler, // 105: Bluetooth LE/802.15.4 radio to MCU, wakeup done 7
|
||||
B2MWKUP0_IRQHandler, // 106: Bluetooth LE/802.15.4 radio to MCU, wakeup signal 0
|
||||
B2MWKUP1_IRQHandler, // 107: Bluetooth LE/802.15.4 radio to MCU, wakeup signal 1
|
||||
BLE_MCI_INT0_IRQHandler, // 108: Bluetooth LE/802.15.4 radio to MCU interrupt 0
|
||||
BLE_MCI_INT1_IRQHandler, // 109: Reserved for Bluetooth LE/802.15.4 radio to MCU
|
||||
BLE_MCI_INT2_IRQHandler, // 110: Reserved for Bluetooth LE/802.15.4 radio to MCU
|
||||
BLE_MCI_INT3_IRQHandler, // 111: Reserved for Bluetooth LE/802.15.4 radio to MCU
|
||||
BLE_MCI_INT4_IRQHandler, // 112: Reserved for Bluetooth LE/802.15.4 radio to MCU
|
||||
BLE_MCI_INT5_IRQHandler, // 113: Reserved for Bluetooth LE/802.15.4 radio to MCU
|
||||
BLE_MCI_INT6_IRQHandler, // 114: Reserved for Bluetooth LE/802.15.4 radio to MCU
|
||||
BLE_MCI_INT7_IRQHandler, // 115: Reserved for Bluetooth LE/802.15.4 radio to MCU
|
||||
PIN0_INT_IRQHandler, // 116: From AON GPIO
|
||||
PIN1_INT_IRQHandler, // 117: From AON GPIO
|
||||
ELS_IRQHandler, // 118: EdgeLock subsystem (ELS)
|
||||
ELS_GDET_IRQHandler, // 119: ELS IRQ line for GDET error
|
||||
ELS_GDET_UM_IRQHandler, // 120: ELS un-gated latched error
|
||||
PKC_INT_IRQHandler, // 121: Public key crypto-processor (PKC) interrupt
|
||||
PKC_ERR_IRQHandler, // 122: PKC error
|
||||
CDOG_INT_IRQHandler, // 123: Code watch dog timmer interrupt
|
||||
GAU_DAC_IRQHandler, // 124: General analog unit (GAU) digital to analog converter (DAC)
|
||||
GAU_ACOMP_WKUP_IRQHandler, // 125: GAU analog comparator (ACOMP) wake-up
|
||||
GAU_ACOMP_IRQHandler, // 126: GAU analog comparator
|
||||
GAU_ADC1_IRQHandler, // 127: GAU analog to digital converter 1 (ADC1)
|
||||
GAU_ADC0_IRQHandler, // 128: GAU analog to digital converter 0 (ADC0)
|
||||
USIM_IRQHandler, // 129: Universal subscriber identity module (USIM) interface
|
||||
OTP_IRQHandler, // 130: One time programmable (OTP) memory interrupt
|
||||
ENET_IRQHandler, // 131: Etheret interrupt
|
||||
ENET_TIMER_IRQHandler, // 132: Ethernet timer interrupt
|
||||
PMIP_IRQHandler, // 133: Power management IP (PMIP)
|
||||
PMIP_CHANGE_IRQHandler, // 134: PMIP change from 1 to 0
|
||||
ITRC_IRQHandler, // 135: Intrusion and tamper response controller (ITRC) interrupt request
|
||||
Reserved136_IRQHandler, // 136: Reserved interrupt
|
||||
Reserved137_IRQHandler, // 137: Reserved interrupt
|
||||
Reserved138_IRQHandler, // 138: Reserved interrupt
|
||||
TRNG_IRQHandler, // 139: TRNG interrupt request
|
||||
ACC_C_INT_IRQHandler, // 140: AHB memory access checker - Cortex-M33 code bus
|
||||
ACC_S_INT_IRQHandler, // 141: AHB memory access checker - Cortex-M33 sys bus
|
||||
WACC_IRQHandler, // 142: Wi-Fi accessed during power off
|
||||
BACC_IRQHandler, // 143: Bluetooth LE/802.15.4 radio accessed during power off
|
||||
GDMA_IRQHandler, // 144: General purpose direct memory access (GDMA) interrupt
|
||||
};
|
||||
|
||||
extern unsigned char _end[]; // End of data section, start of heap. See link.ld
|
||||
__attribute__((naked, noreturn)) void Reset_Handler(void) {
|
||||
__asm("cpsid i"); // Disable interrupts
|
||||
// Config VTOR & MSPLIM register
|
||||
__asm("LDR R0, =0xE000ED08 \n"
|
||||
"STR %0, [R0] \n"
|
||||
"LDR R1, [%0] \n"
|
||||
"MSR MSP, R1 \n"
|
||||
"MSR MSPLIM, %1 \n"
|
||||
:
|
||||
: "r"(tab), "r"(_end)
|
||||
: "r0", "r1");
|
||||
extern long _sbss, _ebss, _sdata, _edata, _sidata, _siram, _siiram, _eiram;
|
||||
for (long *dst = &_sbss; dst < &_ebss; dst++) *dst = 0;
|
||||
for (long *dst = &_sdata, *src = &_sidata; dst < &_edata;) *dst++ = *src++;
|
||||
for (long *dst = &_siram, *src = &_siiram; dst < &_eiram;) *dst++ = *src++;
|
||||
SystemInit();
|
||||
__asm("cpsie i"); // Reenable interrupts
|
||||
// Call main()
|
||||
extern void main(void);
|
||||
main();
|
||||
for (;;) (void) 0; // Infinite loop
|
||||
}
|
||||
|
||||
void SystemCoreClockUpdate (void) {
|
||||
|
||||
}
|
||||
|
||||
void Default_Handler(void) {
|
||||
for (;;) (void) 0;
|
||||
}
|
||||
Reference in new issue
Block a user