mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-12 10:13:29 +00:00
Add L2 PPP and PPPoE
This commit is contained in:
+727
-438
File diff suppressed because it is too large
Load Diff
+21
-17
@@ -3209,7 +3209,7 @@ bool mg_wifi_ap_stop(void);
|
||||
#if MG_ENABLE_TCPIP
|
||||
|
||||
// no config defaults to 0 => Ethernet
|
||||
enum mg_l2type { MG_TCPIP_L2_ETH = 0, MG_TCPIP_L2_PPP }; // MG_TCPIP_L2_PPPoE
|
||||
enum mg_l2type { MG_TCPIP_L2_ETH = 0, MG_TCPIP_L2_PPP, MG_TCPIP_L2_PPPoE};
|
||||
|
||||
#if defined(__DCC__)
|
||||
#pragma pack(1)
|
||||
@@ -3340,10 +3340,11 @@ struct mg_tcpip_if {
|
||||
volatile uint32_t nerr; // Number of driver errors
|
||||
uint8_t state; // Current link and IPv4 state
|
||||
#define MG_TCPIP_STATE_DOWN 0 // Interface is down
|
||||
#define MG_TCPIP_STATE_UP 1 // Interface is up
|
||||
#define MG_TCPIP_STATE_REQ 2 // Interface is up, DHCP REQUESTING state
|
||||
#define MG_TCPIP_STATE_IP 3 // Interface is up and has an IP assigned
|
||||
#define MG_TCPIP_STATE_READY 4 // Interface has fully come up, ready to work
|
||||
#define MG_TCPIP_STATE_LINK_UP 1 // Driver reports link state is up
|
||||
#define MG_TCPIP_STATE_UP 2 // Interface is up --> LINK_READY
|
||||
#define MG_TCPIP_STATE_REQ 3 // Interface is up, DHCP REQUESTING state
|
||||
#define MG_TCPIP_STATE_IP 4 // Interface is up and has an IP assigned
|
||||
#define MG_TCPIP_STATE_READY 5 // Interface has fully come up, ready to work
|
||||
bool gw_ready; // We've got a hw address for the router
|
||||
#if MG_ENABLE_IPV6
|
||||
uint8_t gw6mac[sizeof(struct mg_l2addr)]; // IPV6 Router's hw address
|
||||
@@ -3369,12 +3370,12 @@ extern struct mg_tcpip_driver mg_tcpip_driver_cmsis;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_ra;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_xmc;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_xmc7;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_ppp;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_pico_w;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_rw612;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_cyw;
|
||||
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;
|
||||
|
||||
// Drivers that require SPI, can use this SPI abstraction
|
||||
struct mg_tcpip_spi {
|
||||
@@ -3424,6 +3425,20 @@ struct mg_tcpip_spi {
|
||||
#endif
|
||||
|
||||
|
||||
struct mg_tcpip_driver_atcmd_data {
|
||||
void *usart; // Opaque UART/SPI descriptor
|
||||
void (*reset)(void *); // Modem hardware reset
|
||||
void (*tx)(void *, uint8_t); // USART transmit single byte
|
||||
int (*rx)(void *); // USART receive single byte
|
||||
const char **script; // List of AT commands and expected replies
|
||||
int script_index; // Index of the current AT command in the list
|
||||
uint64_t deadline; // AT command deadline in ms
|
||||
bool no_byte_stuff; // Do not perform byte stuffing/unstuffing
|
||||
bool link; // internal use: done with AT processing
|
||||
bool unstuffing; // internal use: unstuffing state
|
||||
};
|
||||
|
||||
|
||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_CMSIS) && MG_ENABLE_DRIVER_CMSIS
|
||||
|
||||
#include "Driver_ETH_MAC.h" // keep this include
|
||||
@@ -3608,17 +3623,6 @@ struct mg_tcpip_driver_pico_w_data {
|
||||
#endif
|
||||
|
||||
|
||||
struct mg_tcpip_driver_ppp_data {
|
||||
void *uart; // Opaque UART bus descriptor
|
||||
void (*reset)(void *); // Modem hardware reset
|
||||
void (*tx)(void *, uint8_t); // UART transmit single byte
|
||||
int (*rx)(void *); // UART receive single byte
|
||||
const char **script; // List of AT commands and expected replies
|
||||
int script_index; // Index of the current AT command in the list
|
||||
uint64_t deadline; // AT command deadline in ms
|
||||
};
|
||||
|
||||
|
||||
#if MG_ENABLE_TCPIP && \
|
||||
(defined(MG_ENABLE_DRIVER_RA6) && MG_ENABLE_DRIVER_RA6) || \
|
||||
(defined(MG_ENABLE_DRIVER_RA8) && MG_ENABLE_DRIVER_RA8)
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
#include "net_builtin.h"
|
||||
|
||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_ATCMD) && MG_ENABLE_DRIVER_ATCMD
|
||||
|
||||
#define MG_HDLC_FLAG 0x7e // HDLC frame delimiter
|
||||
#define MG_HDLC_ESC 0x7d // HDLC escape byte for byte stuffing
|
||||
|
||||
#define MG_ATCMD_AT_TIMEOUT 2000
|
||||
|
||||
static size_t print_atcmd(void (*out)(char, void *), void *arg, va_list *ap) {
|
||||
struct mg_str s = va_arg(*ap, struct mg_str);
|
||||
for (size_t i = 0; i < s.len; i++) out(s.buf[i] < 0x20 ? '.' : s.buf[i], arg);
|
||||
return s.len;
|
||||
}
|
||||
|
||||
static void mg_atcmd_reset(struct mg_tcpip_driver_atcmd_data *dd) {
|
||||
dd->script_index = 0;
|
||||
dd->deadline = 0;
|
||||
if (dd->reset) dd->reset(dd->usart);
|
||||
}
|
||||
|
||||
static bool mg_atcmd_handle(struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_driver_atcmd_data *dd = (struct mg_tcpip_driver_atcmd_data *) ifp->driver_data;
|
||||
if (dd->script == NULL || dd->script_index < 0) return true;
|
||||
if (dd->deadline == 0) dd->deadline = mg_millis() + MG_ATCMD_AT_TIMEOUT;
|
||||
for (;;) {
|
||||
if (dd->script_index % 2 == 0) { // send AT command
|
||||
const char *cmd = dd->script[dd->script_index];
|
||||
MG_DEBUG(("send AT[%d]: %M", dd->script_index, print_atcmd, mg_str(cmd)));
|
||||
while (*cmd) dd->tx(dd->usart, *cmd++);
|
||||
dd->script_index++;
|
||||
ifp->recv_queue.head = 0;
|
||||
} else { // check AT command response
|
||||
const char *expect = dd->script[dd->script_index];
|
||||
struct mg_queue *q = &ifp->recv_queue;
|
||||
for (;;) {
|
||||
int c;
|
||||
bool is_timeout = dd->deadline > 0 && mg_millis() > dd->deadline;
|
||||
bool is_overflow = q->head >= q->size - 1;
|
||||
if (is_timeout || is_overflow) {
|
||||
MG_ERROR(("AT error: %s, retrying...", is_timeout ? "timeout" : "overflow"));
|
||||
mg_atcmd_reset(dd);
|
||||
return false; // FAIL
|
||||
}
|
||||
if ((c = dd->rx(dd->usart)) < 0) return false; // no data
|
||||
q->buf[q->head++] = c;
|
||||
if (mg_match(mg_str_n(q->buf, q->head), mg_str(expect), NULL)) {
|
||||
MG_DEBUG(("recv AT[%d]: %M", dd->script_index, print_atcmd,
|
||||
mg_str_n(q->buf, q->head)));
|
||||
dd->script_index++;
|
||||
q->head = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dd->script[dd->script_index] == NULL) {
|
||||
MG_DEBUG(("finished AT script"));
|
||||
dd->script_index = -1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void byte_stuff(struct mg_tcpip_driver_atcmd_data *dd, uint8_t b);
|
||||
static bool byte_unstuff(struct mg_tcpip_driver_atcmd_data *dd, uint8_t *p);
|
||||
|
||||
static size_t mg_atcmd_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_driver_atcmd_data *dd = (struct mg_tcpip_driver_atcmd_data *) ifp->driver_data;
|
||||
struct mg_queue *q = &ifp->recv_queue;
|
||||
|
||||
if (!dd->link) return 0;
|
||||
|
||||
while (q->head < q->size) { // read as many bytes as possible
|
||||
uint8_t b;
|
||||
int c = dd->rx(dd->usart);
|
||||
if (c < 0) return 0; // no more bytes, exit
|
||||
b = (uint8_t) c;
|
||||
if (b == MG_HDLC_FLAG) {
|
||||
if (q->head == 0) { // first flag: skip
|
||||
dd->unstuffing = false;
|
||||
continue;
|
||||
} else { // last flag: end of frame, exit loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!dd->no_byte_stuff && !byte_unstuff(dd, &b)) continue;
|
||||
q->buf[q->head++] = b;
|
||||
}
|
||||
len = (q->head <= len) ? q->head : 0;
|
||||
memmove(buf, q->buf, len);
|
||||
q->head = 0;
|
||||
return len;
|
||||
}
|
||||
|
||||
static size_t mg_atcmd_tx(const void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_driver_atcmd_data *dd = (struct mg_tcpip_driver_atcmd_data *) ifp->driver_data;
|
||||
uint8_t *p = (uint8_t *) buf;
|
||||
size_t n = len;
|
||||
|
||||
if (!dd->link) return 0;
|
||||
|
||||
dd->tx(dd->usart, MG_HDLC_FLAG);
|
||||
while (n--) {
|
||||
if (dd->no_byte_stuff) {
|
||||
dd->tx(dd->usart, *p++);
|
||||
} else {
|
||||
byte_stuff(dd, *p++);
|
||||
}
|
||||
}
|
||||
dd->tx(dd->usart, MG_HDLC_FLAG);
|
||||
return len;
|
||||
}
|
||||
|
||||
static bool mg_atcmd_init(struct mg_tcpip_if *ifp) {
|
||||
ifp->recv_queue.size = 1506; // PPP MTU=1500 + header + HDLC w/ no flags
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool mg_atcmd_poll(struct mg_tcpip_if *ifp, bool s1) {
|
||||
struct mg_tcpip_driver_atcmd_data *dd = (struct mg_tcpip_driver_atcmd_data *) ifp->driver_data;
|
||||
dd->link = mg_atcmd_handle(ifp);
|
||||
return s1 ? dd->link :false;
|
||||
}
|
||||
|
||||
|
||||
struct mg_tcpip_driver mg_tcpip_driver_atcmd = {mg_atcmd_init, mg_atcmd_tx, mg_atcmd_rx, mg_atcmd_poll};
|
||||
|
||||
|
||||
|
||||
static void byte_stuff(struct mg_tcpip_driver_atcmd_data *dd, uint8_t b) {
|
||||
if ((b < 0x20) || (b == MG_HDLC_ESC) || (b == MG_HDLC_FLAG)) {
|
||||
dd->tx(dd->usart, MG_HDLC_ESC);
|
||||
dd->tx(dd->usart, b ^ 0x20);
|
||||
} else {
|
||||
dd->tx(dd->usart, b);
|
||||
}
|
||||
}
|
||||
|
||||
static bool byte_unstuff(struct mg_tcpip_driver_atcmd_data *dd, uint8_t *p) {
|
||||
if (!dd->unstuffing) {
|
||||
if (*p == MG_HDLC_ESC) {
|
||||
dd->unstuffing = true;
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
dd->unstuffing = false;
|
||||
*p ^= 0x20;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
struct mg_tcpip_driver_atcmd_data {
|
||||
void *usart; // Opaque UART/SPI descriptor
|
||||
void (*reset)(void *); // Modem hardware reset
|
||||
void (*tx)(void *, uint8_t); // USART transmit single byte
|
||||
int (*rx)(void *); // USART receive single byte
|
||||
const char **script; // List of AT commands and expected replies
|
||||
int script_index; // Index of the current AT command in the list
|
||||
uint64_t deadline; // AT command deadline in ms
|
||||
bool no_byte_stuff; // Do not perform byte stuffing/unstuffing
|
||||
bool link; // internal use: done with AT processing
|
||||
bool unstuffing; // internal use: unstuffing state
|
||||
};
|
||||
@@ -1,328 +0,0 @@
|
||||
#include "net_builtin.h"
|
||||
|
||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_PPP) && MG_ENABLE_DRIVER_PPP
|
||||
|
||||
#define MG_PPP_FLAG 0x7e // PPP frame delimiter
|
||||
#define MG_PPP_ESC 0x7d // PPP escape byte for special characters
|
||||
#define MG_PPP_ADDR 0xff
|
||||
#define MG_PPP_CTRL 0x03
|
||||
|
||||
#define MG_PPP_PROTO_IP 0x0021
|
||||
#define MG_PPP_PROTO_LCP 0xc021
|
||||
#define MG_PPP_PROTO_IPCP 0x8021
|
||||
|
||||
#define MG_PPP_IPCP_REQ 1
|
||||
#define MG_PPP_IPCP_ACK 2
|
||||
#define MG_PPP_IPCP_NACK 3
|
||||
#define MG_PPP_IPCP_IPADDR 3
|
||||
|
||||
#define MG_PPP_LCP_CFG_REQ 1
|
||||
#define MG_PPP_LCP_CFG_ACK 2
|
||||
#define MG_PPP_LCP_CFG_NACK 3
|
||||
#define MG_PPP_LCP_CFG_REJECT 4
|
||||
#define MG_PPP_LCP_CFG_TERM_REQ 5
|
||||
#define MG_PPP_LCP_CFG_TERM_ACK 6
|
||||
|
||||
#define MG_PPP_AT_TIMEOUT 2000
|
||||
|
||||
static size_t print_atcmd(void (*out)(char, void *), void *arg, va_list *ap) {
|
||||
struct mg_str s = va_arg(*ap, struct mg_str);
|
||||
for (size_t i = 0; i < s.len; i++) out(s.buf[i] < 0x20 ? '.' : s.buf[i], arg);
|
||||
return s.len;
|
||||
}
|
||||
|
||||
static void mg_ppp_reset(struct mg_tcpip_driver_ppp_data *dd) {
|
||||
dd->script_index = 0;
|
||||
dd->deadline = 0;
|
||||
if (dd->reset) dd->reset(dd->uart);
|
||||
}
|
||||
|
||||
static bool mg_ppp_atcmd_handle(struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_driver_ppp_data *dd =
|
||||
(struct mg_tcpip_driver_ppp_data *) ifp->driver_data;
|
||||
if (dd->script == NULL || dd->script_index < 0) return true;
|
||||
if (dd->deadline == 0) dd->deadline = mg_millis() + MG_PPP_AT_TIMEOUT;
|
||||
for (;;) {
|
||||
if (dd->script_index % 2 == 0) { // send AT command
|
||||
const char *cmd = dd->script[dd->script_index];
|
||||
MG_DEBUG(("send AT[%d]: %M", dd->script_index, print_atcmd, mg_str(cmd)));
|
||||
while (*cmd) dd->tx(dd->uart, *cmd++);
|
||||
dd->script_index++;
|
||||
ifp->recv_queue.head = 0;
|
||||
} else { // check AT command response
|
||||
const char *expect = dd->script[dd->script_index];
|
||||
struct mg_queue *q = &ifp->recv_queue;
|
||||
for (;;) {
|
||||
int c;
|
||||
int is_timeout = dd->deadline > 0 && mg_millis() > dd->deadline;
|
||||
int is_overflow = q->head >= q->size - 1;
|
||||
if (is_timeout || is_overflow) {
|
||||
MG_ERROR(("AT error: %s, retrying...",
|
||||
is_timeout ? "timeout" : "overflow"));
|
||||
mg_ppp_reset(dd);
|
||||
return false; // FAIL: timeout
|
||||
}
|
||||
if ((c = dd->rx(dd->uart)) < 0) return false; // no data
|
||||
q->buf[q->head++] = c;
|
||||
if (mg_match(mg_str_n(q->buf, q->head), mg_str(expect), NULL)) {
|
||||
MG_DEBUG(("recv AT[%d]: %M", dd->script_index, print_atcmd,
|
||||
mg_str_n(q->buf, q->head)));
|
||||
dd->script_index++;
|
||||
q->head = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dd->script[dd->script_index] == NULL) {
|
||||
MG_DEBUG(("finished AT script"));
|
||||
dd->script_index = -1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool mg_ppp_init(struct mg_tcpip_if *ifp) {
|
||||
ifp->recv_queue.size = 3000; // MTU=1500, worst case escaping = 2x
|
||||
return true;
|
||||
}
|
||||
|
||||
// Calculate FCS/CRC for PPP frames. Could be implemented faster using lookup
|
||||
// tables.
|
||||
static uint32_t fcs_do(uint32_t fcs, uint8_t x) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
fcs = ((fcs ^ x) & 1) ? (fcs >> 1) ^ 0x8408 : fcs >> 1;
|
||||
x >>= 1;
|
||||
}
|
||||
return fcs;
|
||||
}
|
||||
|
||||
static bool mg_ppp_poll(struct mg_tcpip_if *ifp, bool s1) {
|
||||
(void) s1;
|
||||
return ifp->driver_data != NULL;
|
||||
}
|
||||
|
||||
// Transmit a single byte as part of the PPP frame (escaped, if needed)
|
||||
static void mg_ppp_tx_byte(struct mg_tcpip_driver_ppp_data *dd, uint8_t b) {
|
||||
if ((b < 0x20) || (b == MG_PPP_ESC) || (b == MG_PPP_FLAG)) {
|
||||
dd->tx(dd->uart, MG_PPP_ESC);
|
||||
dd->tx(dd->uart, b ^ 0x20);
|
||||
} else {
|
||||
dd->tx(dd->uart, b);
|
||||
}
|
||||
}
|
||||
|
||||
// Transmit a single PPP frame for the given protocol
|
||||
static void mg_ppp_tx_frame(struct mg_tcpip_driver_ppp_data *dd, uint16_t proto,
|
||||
uint8_t *data, size_t datasz) {
|
||||
uint16_t crc;
|
||||
uint32_t fcs = 0xffff;
|
||||
|
||||
dd->tx(dd->uart, MG_PPP_FLAG);
|
||||
mg_ppp_tx_byte(dd, MG_PPP_ADDR);
|
||||
mg_ppp_tx_byte(dd, MG_PPP_CTRL);
|
||||
mg_ppp_tx_byte(dd, proto >> 8);
|
||||
mg_ppp_tx_byte(dd, proto & 0xff);
|
||||
fcs = fcs_do(fcs, MG_PPP_ADDR);
|
||||
fcs = fcs_do(fcs, MG_PPP_CTRL);
|
||||
fcs = fcs_do(fcs, proto >> 8);
|
||||
fcs = fcs_do(fcs, proto & 0xff);
|
||||
for (unsigned int i = 0; i < datasz; i++) {
|
||||
mg_ppp_tx_byte(dd, data[i]);
|
||||
fcs = fcs_do(fcs, data[i]);
|
||||
}
|
||||
crc = fcs & 0xffff;
|
||||
mg_ppp_tx_byte(dd, ~crc); // send CRC, note the byte order
|
||||
mg_ppp_tx_byte(dd, ~crc >> 8);
|
||||
dd->tx(dd->uart, MG_PPP_FLAG); // end of frame
|
||||
}
|
||||
|
||||
// Send Ethernet frame as PPP frame
|
||||
static size_t mg_ppp_tx(const void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_driver_ppp_data *dd =
|
||||
(struct mg_tcpip_driver_ppp_data *) ifp->driver_data;
|
||||
if (ifp->state != MG_TCPIP_STATE_READY) return 0;
|
||||
// XXX: what if not an IP protocol?
|
||||
mg_ppp_tx_frame(dd, MG_PPP_PROTO_IP, (uint8_t *) buf + 14, len - 14);
|
||||
return len;
|
||||
}
|
||||
|
||||
// Given a full PPP frame, unescape it in place and verify FCS, returns actual
|
||||
// data size on success or 0 on error.
|
||||
static size_t mg_ppp_verify_frame(uint8_t *buf, size_t bufsz) {
|
||||
int unpack = 0;
|
||||
uint16_t crc;
|
||||
size_t pktsz = 0;
|
||||
uint32_t fcs = 0xffff;
|
||||
for (unsigned int i = 0; i < bufsz; i++) {
|
||||
if (unpack == 0) {
|
||||
if (buf[i] == 0x7d) {
|
||||
unpack = 1;
|
||||
} else {
|
||||
buf[pktsz] = buf[i];
|
||||
fcs = fcs_do(fcs, buf[pktsz]);
|
||||
pktsz++;
|
||||
}
|
||||
} else {
|
||||
unpack = 0;
|
||||
buf[pktsz] = buf[i] ^ 0x20;
|
||||
fcs = fcs_do(fcs, buf[pktsz]);
|
||||
pktsz++;
|
||||
}
|
||||
}
|
||||
crc = fcs & 0xffff;
|
||||
if (crc != 0xf0b8) {
|
||||
MG_DEBUG(("bad crc: %04x", crc));
|
||||
return 0;
|
||||
}
|
||||
if (pktsz < 6 || buf[0] != MG_PPP_ADDR || buf[1] != MG_PPP_CTRL) {
|
||||
return 0;
|
||||
}
|
||||
return pktsz - 2; // strip FCS
|
||||
}
|
||||
|
||||
// fetch as much data as we can, until a single PPP frame is received
|
||||
static size_t mg_ppp_rx_frame(struct mg_tcpip_driver_ppp_data *dd,
|
||||
struct mg_queue *q) {
|
||||
while (q->head < q->size) {
|
||||
int c;
|
||||
if ((c = dd->rx(dd->uart)) < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (c == MG_PPP_FLAG) {
|
||||
if (q->head > 0) {
|
||||
break;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
q->buf[q->head++] = c;
|
||||
}
|
||||
|
||||
size_t n = mg_ppp_verify_frame((uint8_t *) q->buf, q->head);
|
||||
if (n == 0) {
|
||||
MG_DEBUG(("invalid PPP frame of %d bytes", q->head));
|
||||
q->head = 0;
|
||||
return 0;
|
||||
}
|
||||
q->head = n;
|
||||
return q->head;
|
||||
}
|
||||
|
||||
static void mg_ppp_handle_lcp(struct mg_tcpip_if *ifp, uint8_t *lcp,
|
||||
size_t lcpsz) {
|
||||
uint8_t id;
|
||||
uint16_t len;
|
||||
struct mg_tcpip_driver_ppp_data *dd =
|
||||
(struct mg_tcpip_driver_ppp_data *) ifp->driver_data;
|
||||
if (lcpsz < 4) return;
|
||||
id = lcp[1];
|
||||
len = (((uint16_t) lcp[2]) << 8) | (lcp[3]);
|
||||
switch (lcp[0]) {
|
||||
case MG_PPP_LCP_CFG_REQ: {
|
||||
if (len == 4) {
|
||||
MG_DEBUG(("LCP config request of %d bytes, acknowledging...", len));
|
||||
lcp[0] = MG_PPP_LCP_CFG_ACK;
|
||||
mg_ppp_tx_frame(dd, MG_PPP_PROTO_LCP, lcp, len);
|
||||
lcp[0] = MG_PPP_LCP_CFG_REQ;
|
||||
mg_ppp_tx_frame(dd, MG_PPP_PROTO_LCP, lcp, len);
|
||||
} else {
|
||||
MG_DEBUG(("LCP config request of %d bytes, rejecting...", len));
|
||||
lcp[0] = MG_PPP_LCP_CFG_REJECT;
|
||||
mg_ppp_tx_frame(dd, MG_PPP_PROTO_LCP, lcp, len);
|
||||
}
|
||||
} break;
|
||||
case MG_PPP_LCP_CFG_TERM_REQ: {
|
||||
uint8_t ack[4] = {MG_PPP_LCP_CFG_TERM_ACK, id, 0, 4};
|
||||
MG_DEBUG(("LCP termination request, acknowledging..."));
|
||||
mg_ppp_tx_frame(dd, MG_PPP_PROTO_LCP, ack, sizeof(ack));
|
||||
mg_ppp_reset(dd);
|
||||
ifp->state = MG_TCPIP_STATE_UP;
|
||||
if (dd->reset) dd->reset(dd->uart);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
static void mg_ppp_handle_ipcp(struct mg_tcpip_if *ifp, uint8_t *ipcp,
|
||||
size_t ipcpsz) {
|
||||
struct mg_tcpip_driver_ppp_data *dd =
|
||||
(struct mg_tcpip_driver_ppp_data *) ifp->driver_data;
|
||||
uint16_t len;
|
||||
uint8_t id;
|
||||
uint8_t req[] = {
|
||||
MG_PPP_IPCP_REQ, 0, 0, 10, MG_PPP_IPCP_IPADDR, 6, 0, 0, 0, 0};
|
||||
if (ipcpsz < 4) return;
|
||||
id = ipcp[1];
|
||||
len = (((uint16_t) ipcp[2]) << 8) | (ipcp[3]);
|
||||
switch (ipcp[0]) {
|
||||
case MG_PPP_IPCP_REQ:
|
||||
MG_DEBUG(("got IPCP config request, acknowledging..."));
|
||||
if (len >= 10 && ipcp[4] == MG_PPP_IPCP_IPADDR) {
|
||||
uint8_t *ip = ipcp + 6;
|
||||
MG_DEBUG(("host ip: %d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]));
|
||||
}
|
||||
ipcp[0] = MG_PPP_IPCP_ACK;
|
||||
mg_ppp_tx_frame(dd, MG_PPP_PROTO_IPCP, ipcp, len);
|
||||
req[1] = id;
|
||||
// Request IP address 0.0.0.0
|
||||
mg_ppp_tx_frame(dd, MG_PPP_PROTO_IPCP, req, sizeof(req));
|
||||
break;
|
||||
case MG_PPP_IPCP_ACK:
|
||||
// This usually does not happen, as our "preferred" IP address is invalid
|
||||
MG_DEBUG(("got IPCP config ack, link is online now"));
|
||||
ifp->state = MG_TCPIP_STATE_READY;
|
||||
break;
|
||||
case MG_PPP_IPCP_NACK:
|
||||
MG_DEBUG(("got IPCP config nack"));
|
||||
// NACK contains our "suggested" IP address, use it
|
||||
if (len >= 10 && ipcp[4] == MG_PPP_IPCP_IPADDR) {
|
||||
uint8_t *ip = ipcp + 6;
|
||||
MG_DEBUG(("ipcp ack, ip: %d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]));
|
||||
ipcp[0] = MG_PPP_IPCP_REQ;
|
||||
mg_ppp_tx_frame(dd, MG_PPP_PROTO_IPCP, ipcp, len);
|
||||
ifp->ip = ifp->mask = MG_IPV4(ip[0], ip[1], ip[2], ip[3]);
|
||||
ifp->state = MG_TCPIP_STATE_READY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t mg_ppp_rx(void *ethbuf, size_t ethlen, struct mg_tcpip_if *ifp) {
|
||||
uint8_t *eth = ethbuf;
|
||||
size_t ethsz = 0;
|
||||
struct mg_tcpip_driver_ppp_data *dd =
|
||||
(struct mg_tcpip_driver_ppp_data *) ifp->driver_data;
|
||||
uint8_t *buf = (uint8_t *) ifp->recv_queue.buf;
|
||||
|
||||
if (!mg_ppp_atcmd_handle(ifp)) return 0;
|
||||
|
||||
size_t bufsz = mg_ppp_rx_frame(dd, &ifp->recv_queue);
|
||||
if (!bufsz) return 0;
|
||||
uint16_t proto = (((uint16_t) buf[2]) << 8) | (uint16_t) buf[3];
|
||||
switch (proto) {
|
||||
case MG_PPP_PROTO_LCP: mg_ppp_handle_lcp(ifp, buf + 4, bufsz - 4); break;
|
||||
case MG_PPP_PROTO_IPCP: mg_ppp_handle_ipcp(ifp, buf + 4, bufsz - 4); break;
|
||||
case MG_PPP_PROTO_IP:
|
||||
MG_VERBOSE(("got IP packet of %d bytes", bufsz - 4));
|
||||
memmove(eth + 14, buf + 4, bufsz - 4);
|
||||
memmove(eth, ifp->mac, 6);
|
||||
memmove(eth + 6, "\xff\xff\xff\xff\xff\xff", 6);
|
||||
eth[12] = 0x08;
|
||||
eth[13] = 0x00;
|
||||
ethsz = bufsz - 4 + 14;
|
||||
ifp->recv_queue.head = 0;
|
||||
return ethsz;
|
||||
#if 0
|
||||
default:
|
||||
MG_DEBUG(("unknown PPP frame:"));
|
||||
mg_hexdump(ppp->buf, ppp->bufsz);
|
||||
#endif
|
||||
}
|
||||
ifp->recv_queue.head = 0;
|
||||
return 0;
|
||||
(void) ethlen;
|
||||
}
|
||||
|
||||
struct mg_tcpip_driver mg_tcpip_driver_ppp = {mg_ppp_init, mg_ppp_tx, mg_ppp_rx,
|
||||
mg_ppp_poll};
|
||||
|
||||
#endif
|
||||
@@ -1,11 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
struct mg_tcpip_driver_ppp_data {
|
||||
void *uart; // Opaque UART bus descriptor
|
||||
void (*reset)(void *); // Modem hardware reset
|
||||
void (*tx)(void *, uint8_t); // UART transmit single byte
|
||||
int (*rx)(void *); // UART receive single byte
|
||||
const char **script; // List of AT commands and expected replies
|
||||
int script_index; // Index of the current AT command in the list
|
||||
uint64_t deadline; // AT command deadline in ms
|
||||
};
|
||||
@@ -5,11 +5,11 @@
|
||||
#if MG_ENABLE_TCPIP
|
||||
|
||||
// L2 API
|
||||
void mg_l2_init(enum mg_l2type type, uint8_t *addr, uint16_t *mtu,
|
||||
uint16_t *framesize);
|
||||
void mg_l2_init(struct mg_tcpip_if *ifp);
|
||||
bool mg_l2_poll(struct mg_tcpip_if *ifp, bool expired_1000ms);
|
||||
uint8_t *mg_l2_header(enum mg_l2type type, enum mg_l2proto proto, uint8_t *src,
|
||||
uint8_t *dst, uint8_t *frame);
|
||||
size_t mg_l2_footer(enum mg_l2type type, size_t len, uint8_t *frame);
|
||||
size_t mg_l2_footer(enum mg_l2type type, size_t len, uint8_t *cur);
|
||||
bool mg_l2_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
|
||||
struct mg_str *pay, struct mg_str *raw);
|
||||
// TODO(): ? bool mg_l2_rx(enum mg_l2type type, struct mg_l2opts *opts, uint8_t
|
||||
@@ -17,24 +17,31 @@ bool mg_l2_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
|
||||
uint8_t *mg_l2_getaddr(enum mg_l2type type, uint8_t *frame);
|
||||
uint8_t *mg_l2_mapip(enum mg_l2type type, enum mg_l2addrtype addrtype,
|
||||
struct mg_addr *ip);
|
||||
#if MG_ENABLE_IPV6
|
||||
bool mg_l2_genip6(enum mg_l2type type, uint64_t *ip6, uint8_t prefix_len,
|
||||
uint8_t *addr);
|
||||
bool mg_l2_ip6get(enum mg_l2type type, uint8_t *addr, uint8_t *opts,
|
||||
uint8_t len);
|
||||
uint8_t mg_l2_ip6put(enum mg_l2type type, uint8_t *addr, uint8_t *opts);
|
||||
#endif
|
||||
size_t mg_l2_driver_output(struct mg_tcpip_if *ifp, size_t len);
|
||||
|
||||
// clang-format off
|
||||
extern void mg_l2_eth_init(struct mg_l2addr *, uint16_t *, uint16_t *);
|
||||
extern void mg_l2_eth_init(struct mg_tcpip_if *);
|
||||
extern bool mg_l2_eth_poll(struct mg_tcpip_if *, bool);
|
||||
extern uint8_t *mg_l2_eth_header(enum mg_l2proto, struct mg_l2addr *, struct mg_l2addr *, uint8_t *);
|
||||
extern size_t mg_l2_eth_footer(size_t, uint8_t *);
|
||||
extern bool mg_l2_eth_rx(struct mg_tcpip_if *, enum mg_l2proto *, struct mg_str *, struct mg_str *);
|
||||
extern struct mg_l2addr *mg_l2_eth_getaddr(uint8_t *);
|
||||
extern struct mg_l2addr *mg_l2_eth_mapip(enum mg_l2addrtype, struct mg_addr *);
|
||||
#if MG_ENABLE_IPV6
|
||||
extern bool mg_l2_eth_genip6(uint64_t *, uint8_t, struct mg_l2addr *);
|
||||
extern bool mg_l2_eth_ip6get(struct mg_l2addr *, uint8_t *, uint8_t);
|
||||
extern uint8_t mg_l2_eth_ip6put(struct mg_l2addr *, uint8_t *);
|
||||
#endif
|
||||
|
||||
extern void mg_l2_ppp_init(struct mg_l2addr *, uint16_t *, uint16_t *);
|
||||
extern void mg_l2_ppp_init(struct mg_tcpip_if *);
|
||||
extern bool mg_l2_ppp_poll(struct mg_tcpip_if *, bool);
|
||||
extern uint8_t *mg_l2_ppp_header(enum mg_l2proto, struct mg_l2addr *, struct mg_l2addr *, uint8_t *);
|
||||
extern size_t mg_l2_ppp_footer(size_t, uint8_t *);
|
||||
extern bool mg_l2_ppp_rx(struct mg_tcpip_if *, enum mg_l2proto *, struct mg_str *, struct mg_str *);
|
||||
@@ -45,8 +52,14 @@ extern bool mg_l2_ppp_genip6(uint64_t *, uint8_t, struct mg_l2addr *);
|
||||
extern bool mg_l2_ppp_ip6get(struct mg_l2addr *, uint8_t *, uint8_t);
|
||||
extern uint8_t mg_l2_ppp_ip6put(struct mg_l2addr *, uint8_t *);
|
||||
#endif
|
||||
extern void mg_l2_pppoe_init(struct mg_tcpip_if *);
|
||||
extern bool mg_l2_pppoe_poll(struct mg_tcpip_if *, bool);
|
||||
extern uint8_t *mg_l2_pppoe_header(enum mg_l2proto, struct mg_l2addr *, struct mg_l2addr *, uint8_t *);
|
||||
extern size_t mg_l2_pppoe_footer(size_t, uint8_t *);
|
||||
extern bool mg_l2_pppoe_rx(struct mg_tcpip_if *, enum mg_l2proto *, struct mg_str *, struct mg_str *);
|
||||
|
||||
typedef void (*l2_init_fn)(struct mg_l2addr *, uint16_t *, uint16_t *);
|
||||
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)(enum mg_l2proto, struct mg_l2addr *, struct mg_l2addr *, uint8_t *));
|
||||
typedef size_t (*l2_footer_fn)(size_t, uint8_t *);
|
||||
typedef bool (*l2_rx_fn)(struct mg_tcpip_if *, enum mg_l2proto *, struct mg_str *, struct mg_str *);
|
||||
@@ -59,22 +72,25 @@ typedef uint8_t (*l2_ip6put_fn)(struct mg_l2addr *, uint8_t *);
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
static const l2_init_fn l2_init[] = {mg_l2_eth_init, mg_l2_ppp_init};
|
||||
static const l2_header_fn l2_header[] = {mg_l2_eth_header, mg_l2_ppp_header};
|
||||
static const l2_footer_fn l2_footer[] = {mg_l2_eth_footer, mg_l2_ppp_footer};
|
||||
static const l2_rx_fn l2_rx[] = {mg_l2_eth_rx, mg_l2_ppp_rx};
|
||||
static const l2_getaddr_fn l2_getaddr[] = {mg_l2_eth_getaddr,
|
||||
mg_l2_ppp_getaddr};
|
||||
static const l2_mapip_fn l2_mapip[] = {mg_l2_eth_mapip, mg_l2_ppp_mapip};
|
||||
static const l2_init_fn l2_init[] = {mg_l2_eth_init, mg_l2_ppp_init, mg_l2_pppoe_init};
|
||||
static const l2_poll_fn l2_poll[] = {mg_l2_eth_poll, mg_l2_ppp_poll, mg_l2_pppoe_poll};
|
||||
static const l2_header_fn l2_header[] = {mg_l2_eth_header, mg_l2_ppp_header, mg_l2_pppoe_header};
|
||||
static const l2_footer_fn l2_footer[] = {mg_l2_eth_footer, mg_l2_ppp_footer, mg_l2_pppoe_footer};
|
||||
static const l2_rx_fn l2_rx[] = {mg_l2_eth_rx, mg_l2_ppp_rx, mg_l2_pppoe_rx};
|
||||
static const l2_getaddr_fn l2_getaddr[] = {mg_l2_eth_getaddr, mg_l2_ppp_getaddr, mg_l2_ppp_getaddr};
|
||||
static const l2_mapip_fn l2_mapip[] = {mg_l2_eth_mapip, mg_l2_ppp_mapip, mg_l2_ppp_mapip};
|
||||
#if MG_ENABLE_IPV6
|
||||
static const l2_genip6_fn l2_genip6[] = {mg_l2_eth_genip6, mg_l2_ppp_genip6};
|
||||
static const l2_ip6get_fn l2_ip6get[] = {mg_l2_eth_ip6get, mg_l2_ppp_ip6get};
|
||||
static const l2_ip6put_fn l2_ip6put[] = {mg_l2_eth_ip6put, mg_l2_ppp_ip6put};
|
||||
static const l2_genip6_fn l2_genip6[] = {mg_l2_eth_genip6, mg_l2_ppp_genip6, mg_l2_ppp_genip6};
|
||||
static const l2_ip6get_fn l2_ip6get[] = {mg_l2_eth_ip6get, mg_l2_ppp_ip6get, mg_l2_ppp_ip6get};
|
||||
static const l2_ip6put_fn l2_ip6put[] = {mg_l2_eth_ip6put, mg_l2_ppp_ip6put, mg_l2_ppp_ip6put};
|
||||
#endif
|
||||
|
||||
void mg_l2_init(enum mg_l2type type, uint8_t *addr, uint16_t *mtu,
|
||||
uint16_t *framesize) {
|
||||
l2_init[type]((struct mg_l2addr *) addr, mtu, framesize);
|
||||
void mg_l2_init(struct mg_tcpip_if *ifp) {
|
||||
l2_init[ifp->l2type](ifp);
|
||||
}
|
||||
|
||||
bool mg_l2_poll(struct mg_tcpip_if *ifp, bool expired_1000ms) {
|
||||
return l2_poll[ifp->l2type](ifp, expired_1000ms);
|
||||
}
|
||||
|
||||
uint8_t *mg_l2_header(enum mg_l2type type, enum mg_l2proto proto, uint8_t *src,
|
||||
@@ -118,4 +134,10 @@ uint8_t mg_l2_ip6put(enum mg_l2type type, uint8_t *addr, uint8_t *opts) {
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t mg_l2_driver_output(struct mg_tcpip_if *ifp, size_t len) {
|
||||
size_t n = ifp->driver->tx(ifp->tx.buf, len, ifp);
|
||||
if (n == len) ifp->nsent++;
|
||||
return n;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#if MG_ENABLE_TCPIP
|
||||
|
||||
// no config defaults to 0 => Ethernet
|
||||
enum mg_l2type { MG_TCPIP_L2_ETH = 0, MG_TCPIP_L2_PPP }; // MG_TCPIP_L2_PPPoE
|
||||
enum mg_l2type { MG_TCPIP_L2_ETH = 0, MG_TCPIP_L2_PPP, MG_TCPIP_L2_PPPoE};
|
||||
|
||||
#if defined(__DCC__)
|
||||
#pragma pack(1)
|
||||
|
||||
+15
-8
@@ -34,8 +34,8 @@ static const uint16_t eth_types[] = {
|
||||
0x8864 // PPPoE Session Stage
|
||||
};
|
||||
|
||||
void mg_l2_eth_init(struct mg_l2addr *l2addr, uint16_t *mtu,
|
||||
uint16_t *framesize) {
|
||||
void mg_l2_eth_init(struct mg_tcpip_if *ifp) {
|
||||
struct mg_l2addr *l2addr = (struct mg_l2addr *)ifp->mac;
|
||||
// If MAC is not set, make a random one
|
||||
if (l2addr->addr.mac[0] == 0 && l2addr->addr.mac[1] == 0 &&
|
||||
l2addr->addr.mac[2] == 0 && l2addr->addr.mac[3] == 0 &&
|
||||
@@ -45,8 +45,14 @@ void mg_l2_eth_init(struct mg_l2addr *l2addr, uint16_t *mtu,
|
||||
MG_INFO(
|
||||
("MAC not set. Generated random: %M", mg_print_mac, l2addr->addr.mac));
|
||||
}
|
||||
*mtu = 1500;
|
||||
*framesize = 1540;
|
||||
ifp->mtu = 1500;
|
||||
ifp->framesize = 1540;
|
||||
}
|
||||
|
||||
bool mg_l2_eth_poll(struct mg_tcpip_if *ifp, bool expired_1000ms) {
|
||||
(void) ifp;
|
||||
(void) expired_1000ms;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t *mg_l2_eth_header(enum mg_l2proto proto, struct mg_l2addr *src,
|
||||
@@ -58,10 +64,11 @@ uint8_t *mg_l2_eth_header(enum mg_l2proto proto, struct mg_l2addr *src,
|
||||
return (uint8_t *) (eth + 1);
|
||||
}
|
||||
|
||||
size_t mg_l2_eth_footer(size_t len, uint8_t *frame) {
|
||||
struct eth *eth = (struct eth *) frame;
|
||||
// nothing to do; there is no len field in Ethernet, CRC is hw-calculated
|
||||
return len + sizeof(*eth);
|
||||
size_t mg_l2_eth_footer(size_t len, uint8_t *cur) {
|
||||
// there is no len field in Ethernet, CRC is hw-calculated; pad to 64 - CRC
|
||||
size_t ethlen = len + sizeof(struct eth);
|
||||
(void) cur;
|
||||
return ethlen >= 60 ? ethlen : 60;
|
||||
}
|
||||
|
||||
struct mg_l2addr *mg_l2_eth_mapip(enum mg_l2addrtype addrtype,
|
||||
|
||||
+449
-33
@@ -12,22 +12,35 @@
|
||||
#else
|
||||
#pragma pack(push, 1)
|
||||
#endif
|
||||
// all in network order
|
||||
|
||||
struct ppp { // RFC-1662
|
||||
uint8_t addr, ctrl;
|
||||
struct ppp { // RFC-1661
|
||||
uint16_t proto;
|
||||
};
|
||||
|
||||
struct lcp { // RFC-1661
|
||||
uint8_t code, id, len[2];
|
||||
uint8_t code, id;
|
||||
uint16_t len;
|
||||
};
|
||||
|
||||
struct ipcp { // RFC-1332
|
||||
uint8_t code;
|
||||
struct ipcp { // RFC-1332
|
||||
uint8_t code, id;
|
||||
uint16_t len;
|
||||
};
|
||||
|
||||
struct ipv6cp { // RFC-5072
|
||||
uint8_t code;
|
||||
struct ipv6cp { // RFC-5072
|
||||
uint8_t code, id;
|
||||
uint16_t len;
|
||||
};
|
||||
|
||||
struct hdlc_ { // RFC-1662, "PPP in HDLC-like Framing"
|
||||
uint8_t addr, ctrl;
|
||||
};
|
||||
|
||||
struct pppoe { // RFC-2516, "A Method for Transmitting PPP Over Ethernet
|
||||
// (PPPoE)"
|
||||
uint8_t vertype, code;
|
||||
uint16_t id, len;
|
||||
};
|
||||
|
||||
#if defined(__DCC__)
|
||||
@@ -36,52 +49,361 @@ struct ipv6cp { // RFC-5072
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
#define MG_PPP_ADDR 0xff
|
||||
#define MG_PPP_CTRL 0x03
|
||||
|
||||
void mg_l2_ppp_init(struct mg_l2addr *addr, uint16_t *mtu,
|
||||
uint16_t *framesize) {
|
||||
(void) addr;
|
||||
*mtu = 1500; // 1492 for PPPoE
|
||||
*framesize = 1540; // *** TODO(scaprile): actual value, check for PPPoE too
|
||||
#define MG_PPP_PROTO_IP 0x0021
|
||||
#define MG_PPP_PROTO_IPV6 0x0057
|
||||
#define MG_PPP_PROTO_LCP 0xc021
|
||||
#define MG_PPP_PROTO_IPCP 0x8021
|
||||
#define MG_PPP_PROTO_IPV6CP 0x8057
|
||||
#define MG_PPP_PROTO_PAP 0xc023
|
||||
#define MG_PPP_PROTO_CHAP 0xc223
|
||||
|
||||
#define MG_PPP_IPCP_REQ 1
|
||||
#define MG_PPP_IPCP_ACK 2
|
||||
#define MG_PPP_IPCP_NACK 3
|
||||
#define MG_PPP_IPCP_REJECT 4
|
||||
#define MG_PPP_IPCP_IPADDR 3
|
||||
|
||||
#define MG_PPP_LCP_CFG_REQ 1
|
||||
#define MG_PPP_LCP_CFG_ACK 2
|
||||
#define MG_PPP_LCP_CFG_NACK 3
|
||||
#define MG_PPP_LCP_CFG_REJECT 4
|
||||
#define MG_PPP_LCP_CFG_TERM_REQ 5
|
||||
#define MG_PPP_LCP_CFG_TERM_ACK 6
|
||||
#define MG_PPP_LCP_REJECT 8
|
||||
#define MG_PPP_LCP_ECHO_REQ 9
|
||||
#define MG_PPP_LCP_ECHO_REPLY 10
|
||||
|
||||
#define MG_PPPoE_PADI 0x09
|
||||
#define MG_PPPoE_PADO 0x07
|
||||
#define MG_PPPoE_PADR 0x19
|
||||
#define MG_PPPoE_PADS 0x65
|
||||
#define MG_PPPoE_PADT 0xa7
|
||||
|
||||
#define MG_PPPoE_ST_DISC 0 // Discovery phase, see what servers are out there
|
||||
#define MG_PPPoE_ST_REQ 1 // Chose a server, request a session and wait
|
||||
#define MG_PPPoE_ST_SESS 2 // Session established, PPP traffic is exchanged
|
||||
|
||||
#define PDIFF(a, b) ((size_t) (((char *) (b)) - ((char *) (a))))
|
||||
|
||||
static bool s_link = false; // *******************************************
|
||||
static uint8_t s_state = MG_PPPoE_ST_DISC;
|
||||
static uint16_t s_id;
|
||||
|
||||
void mg_l2_ppp_init(struct mg_tcpip_if *ifp) {
|
||||
ifp->mtu = 1500;
|
||||
ifp->framesize = 1500 + sizeof(struct ppp) + sizeof(struct hdlc_);
|
||||
}
|
||||
|
||||
extern void mg_l2_eth_init(struct mg_tcpip_if *);
|
||||
|
||||
void mg_l2_pppoe_init(struct mg_tcpip_if *ifp) {
|
||||
mg_l2_eth_init(ifp);
|
||||
ifp->mtu = ifp->mtu - (uint16_t) (sizeof(struct pppoe) +
|
||||
sizeof(struct ppp)); // 1500 --> 1492
|
||||
}
|
||||
|
||||
bool mg_l2_ppp_poll(struct mg_tcpip_if *ifp, bool expired_1000ms) {
|
||||
if (expired_1000ms && ifp->state == MG_TCPIP_STATE_DOWN) s_link = false;
|
||||
return s_link;
|
||||
}
|
||||
|
||||
static uint8_t *hdlc_header(uint8_t *p) {
|
||||
struct hdlc_ *hdlc = (struct hdlc_ *) p;
|
||||
hdlc->addr = MG_PPP_ADDR;
|
||||
hdlc->ctrl = MG_PPP_CTRL;
|
||||
return (uint8_t *) (hdlc + 1);
|
||||
}
|
||||
|
||||
static uint8_t *ppp_header(uint16_t proto, uint8_t *p) {
|
||||
struct ppp *ppp = (struct ppp *) p;
|
||||
ppp->proto = mg_htons(proto);
|
||||
return (uint8_t *) (ppp + 1);
|
||||
}
|
||||
|
||||
static uint8_t *l2_ppp_header(enum mg_l2proto proto, uint8_t *p) {
|
||||
uint16_t ppp_proto = proto == MG_TCPIP_L2PROTO_IPV4 ? MG_PPP_PROTO_IP
|
||||
: proto == MG_TCPIP_L2PROTO_IPV4 ? MG_PPP_PROTO_IPV6
|
||||
: 0;
|
||||
return ppp_header(ppp_proto, p);
|
||||
}
|
||||
|
||||
uint8_t *mg_l2_ppp_header(enum mg_l2proto proto, struct mg_l2addr *src,
|
||||
struct mg_l2addr *dst, uint8_t *frame) {
|
||||
(void) src;
|
||||
(void) dst;
|
||||
(void) proto;
|
||||
return frame;
|
||||
return l2_ppp_header(proto, hdlc_header(frame));
|
||||
}
|
||||
|
||||
size_t mg_l2_ppp_footer(size_t len, uint8_t *frame) {
|
||||
(void) frame;
|
||||
return len;
|
||||
extern uint8_t *mg_l2_eth_header(enum mg_l2proto proto, struct mg_l2addr *src,
|
||||
struct mg_l2addr *dst, uint8_t *frame);
|
||||
|
||||
static uint8_t *pppoe_header(enum mg_l2proto proto, uint8_t code, uint16_t id,
|
||||
struct mg_l2addr *src, struct mg_l2addr *dst,
|
||||
uint8_t *frame) {
|
||||
struct pppoe *pppoe =
|
||||
(struct pppoe *) mg_l2_eth_header(proto, src, dst, frame);
|
||||
pppoe->vertype = 0x11;
|
||||
pppoe->code = code;
|
||||
pppoe->id = id;
|
||||
return (uint8_t *) (pppoe + 1);
|
||||
}
|
||||
|
||||
uint8_t *mg_l2_pppoe_header(enum mg_l2proto proto, struct mg_l2addr *src,
|
||||
struct mg_l2addr *dst, uint8_t *frame) {
|
||||
(void) dst;
|
||||
return l2_ppp_header(proto, pppoe_header(MG_TCPIP_L2PROTO_PPPoE_SESS, 0, s_id,
|
||||
src, dst, frame));
|
||||
}
|
||||
|
||||
// Calculate FCS/CRC for PPP frames. Could be implemented faster using lookup
|
||||
// tables.
|
||||
static uint16_t fcs_do(uint8_t *frame, size_t len) {
|
||||
uint32_t fcs = 0xffff;
|
||||
unsigned int j;
|
||||
for (j = 0; j < len; j++) {
|
||||
unsigned int i;
|
||||
uint8_t x = frame[j];
|
||||
for (i = 0; i < 8; i++) {
|
||||
fcs = ((fcs ^ x) & 1) ? (fcs >> 1) ^ 0x8408 : fcs >> 1;
|
||||
x >>= 1;
|
||||
}
|
||||
}
|
||||
return (uint16_t) (fcs & 0xffff);
|
||||
}
|
||||
|
||||
size_t mg_l2_ppp_footer(size_t len, uint8_t *cur) {
|
||||
uint16_t crc;
|
||||
uint8_t *frame;
|
||||
len += sizeof(struct ppp) + sizeof(struct hdlc_);
|
||||
frame = cur - len;
|
||||
crc = fcs_do(frame, len);
|
||||
*cur++ = (uint8_t) ~crc; // add CRC, note the byte order
|
||||
*cur++ = (uint8_t) (~crc >> 8);
|
||||
// there is no len field in PPP
|
||||
return len + 2;
|
||||
}
|
||||
|
||||
extern size_t mg_l2_eth_footer(size_t len, uint8_t *cur);
|
||||
|
||||
static size_t pppoe_footer(size_t len, uint8_t *cur) {
|
||||
struct pppoe *pppoe = (struct pppoe *) (cur - len - sizeof(struct pppoe));
|
||||
pppoe->len = mg_htons((uint16_t) len);
|
||||
return mg_l2_eth_footer(PDIFF(pppoe, cur), cur);
|
||||
}
|
||||
|
||||
size_t mg_l2_pppoe_footer(size_t len, uint8_t *cur) {
|
||||
return pppoe_footer(len + sizeof(struct ppp), cur);
|
||||
}
|
||||
|
||||
size_t mg_l2_driver_output(struct mg_tcpip_if *ifp, size_t len);
|
||||
|
||||
static uint8_t *ppp_tx_frame_header(struct mg_tcpip_if *ifp, uint16_t proto) {
|
||||
uint8_t *l2p = (uint8_t *) ifp->tx.buf;
|
||||
if (ifp->l2type == MG_TCPIP_L2_PPP)
|
||||
return ppp_header(proto, hdlc_header(l2p));
|
||||
return ppp_header(proto, pppoe_header(MG_TCPIP_L2PROTO_PPPoE_SESS, 0, s_id,
|
||||
(struct mg_l2addr *) ifp->mac,
|
||||
(struct mg_l2addr *) ifp->gwmac, l2p));
|
||||
}
|
||||
static size_t ppp_tx_frame_footer(struct mg_tcpip_if *ifp, size_t len,
|
||||
uint8_t *cur) {
|
||||
return mg_l2_driver_output(ifp, ifp->l2type == MG_TCPIP_L2_PPPoE
|
||||
? mg_l2_pppoe_footer(len, cur)
|
||||
: mg_l2_ppp_footer(len, cur));
|
||||
}
|
||||
|
||||
// Transmit a single PPP frame for the given protocol
|
||||
static size_t ppp_tx_frame(struct mg_tcpip_if *ifp, uint16_t proto,
|
||||
uint8_t *data, size_t datasz) {
|
||||
uint8_t *pay = ppp_tx_frame_header(ifp, proto);
|
||||
memcpy(pay, data, datasz);
|
||||
return ppp_tx_frame_footer(ifp, datasz, pay + datasz);
|
||||
}
|
||||
|
||||
static void ppp_handle_lcp(struct mg_tcpip_if *ifp, uint8_t *lcpp,
|
||||
size_t lcpsz) {
|
||||
uint8_t id;
|
||||
uint16_t len;
|
||||
struct lcp *lcp = (struct lcp *) lcpp;
|
||||
if (lcpsz < sizeof(*lcp)) return;
|
||||
id = lcp->id;
|
||||
len = mg_ntohs(lcp->len);
|
||||
switch (lcp->code) {
|
||||
case MG_PPP_LCP_CFG_REQ: {
|
||||
if (len == sizeof(*lcp)) {
|
||||
MG_DEBUG(("LCP config request of %d bytes, acknowledging...", len));
|
||||
lcp->code = MG_PPP_LCP_CFG_ACK;
|
||||
ppp_tx_frame(ifp, MG_PPP_PROTO_LCP, lcpp, len);
|
||||
lcp->code = MG_PPP_LCP_CFG_REQ;
|
||||
ppp_tx_frame(ifp, MG_PPP_PROTO_LCP, lcpp, len);
|
||||
} else {
|
||||
MG_DEBUG(("LCP config request of %d bytes, rejecting...", len));
|
||||
lcp->code = MG_PPP_LCP_CFG_REJECT;
|
||||
ppp_tx_frame(ifp, MG_PPP_PROTO_LCP, lcpp, len);
|
||||
}
|
||||
} break;
|
||||
case MG_PPP_LCP_CFG_ACK:
|
||||
s_link = true;
|
||||
break;
|
||||
case MG_PPP_LCP_CFG_TERM_REQ: {
|
||||
uint8_t ack[4] = {MG_PPP_LCP_CFG_TERM_ACK, id, 0, 4};
|
||||
MG_DEBUG(("LCP termination request, acknowledging..."));
|
||||
ppp_tx_frame(ifp, MG_PPP_PROTO_LCP, ack, sizeof(ack));
|
||||
s_link = false;
|
||||
} break;
|
||||
case MG_PPP_LCP_ECHO_REQ: // RFC-1661 5.8: must respond
|
||||
MG_DEBUG(("LCP echo request of %d bytes, replying...", len));
|
||||
lcp->code = MG_PPP_LCP_ECHO_REPLY;
|
||||
ppp_tx_frame(ifp, MG_PPP_PROTO_LCP, lcpp, len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool find_opt(const uint8_t opt, const uint8_t optlen,
|
||||
const uint8_t *opts, size_t optslen, uint8_t *dest) {
|
||||
uint8_t *p = (uint8_t *) opts;
|
||||
while (optslen >= 2) { // parse options for requested one
|
||||
if (p[1] > optslen) return false; // truncated / malformed
|
||||
if (p[0] == opt && p[1] == optlen) {
|
||||
memcpy(dest, p + 2, optlen - 2);
|
||||
return true;
|
||||
}
|
||||
optslen -= p[1];
|
||||
p += p[1];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ppp_handle_ipcp(struct mg_tcpip_if *ifp, uint8_t *ipcpp,
|
||||
size_t ipcpsz) {
|
||||
uint16_t len;
|
||||
uint8_t id;
|
||||
struct ipcp *ipcp = (struct ipcp *) ipcpp;
|
||||
uint8_t req[] = {
|
||||
MG_PPP_IPCP_REQ, 0, 0, 10, MG_PPP_IPCP_IPADDR, 6, 0, 0, 0, 0};
|
||||
if (ipcpsz < sizeof(*ipcp)) return;
|
||||
id = ipcp->id;
|
||||
len = mg_ntohs(ipcp->len);
|
||||
switch (ipcp->code) {
|
||||
case MG_PPP_IPCP_REQ:
|
||||
MG_VERBOSE(("got IPCP config request, acknowledging..."));
|
||||
if (len >= 10 &&
|
||||
find_opt(MG_PPP_IPCP_IPADDR, 6, (const uint8_t *) (ipcp + 1),
|
||||
len - sizeof(*ipcp), (uint8_t *) &ifp->gw))
|
||||
MG_DEBUG(("IPCP ack, GW IP: %M", mg_print_ip4, &ifp->gw));
|
||||
ipcp->code = MG_PPP_IPCP_ACK;
|
||||
ppp_tx_frame(ifp, MG_PPP_PROTO_IPCP, ipcpp, len);
|
||||
req[1] = id;
|
||||
memcpy(req + 6, &ifp->ip, 4); // Request config IP address or 0.0.0.0
|
||||
ppp_tx_frame(ifp, MG_PPP_PROTO_IPCP, req, sizeof(req));
|
||||
break;
|
||||
case MG_PPP_IPCP_ACK:
|
||||
// Our peer accepted our static IP address (unlikely if 0.0.0.0)
|
||||
MG_VERBOSE(("got IPCP config ack"));
|
||||
ifp->mask = 0xffffffff; // send to gw
|
||||
ifp->state = MG_TCPIP_STATE_IP;
|
||||
ifp->gw_ready = true;
|
||||
break;
|
||||
case MG_PPP_IPCP_NACK:
|
||||
MG_VERBOSE(("got IPCP config nack"));
|
||||
// NACK contains our "suggested" IP address, use it
|
||||
if (len >= 10 &&
|
||||
find_opt(MG_PPP_IPCP_IPADDR, 6, (const uint8_t *) (ipcp + 1),
|
||||
len - sizeof(*ipcp), (uint8_t *) &ifp->ip)) {
|
||||
ifp->mask = 0xffffffff; // send to gw
|
||||
ifp->state = MG_TCPIP_STATE_IP;
|
||||
ifp->gw_ready = true;
|
||||
MG_DEBUG(("IPCP ack, IP: %M", mg_print_ip4, &ifp->ip));
|
||||
ipcp->code = MG_PPP_IPCP_REQ;
|
||||
ppp_tx_frame(ifp, MG_PPP_PROTO_IPCP, ipcpp, len);
|
||||
}
|
||||
break;
|
||||
case MG_PPP_IPCP_REJECT:
|
||||
MG_ERROR(("Peer rejected our IP address, need to properly set ifp->ip"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if MG_ENABLE_IPV6
|
||||
#endif
|
||||
|
||||
static bool ppp_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
|
||||
struct mg_str *pay, struct mg_str *raw) {
|
||||
struct ppp *ppp = (struct ppp *) pay->buf;
|
||||
if (pay->len < sizeof(*ppp)) return false; // Truncated
|
||||
pay->buf = (char *) (ppp + 1);
|
||||
pay->len = pay->len - sizeof(*ppp);
|
||||
switch (mg_ntohs(ppp->proto)) {
|
||||
case MG_PPP_PROTO_LCP:
|
||||
ppp_handle_lcp(ifp, (uint8_t *) pay->buf, pay->len);
|
||||
return false;
|
||||
case MG_PPP_PROTO_IPCP:
|
||||
if (s_link) ppp_handle_ipcp(ifp, (uint8_t *) pay->buf, pay->len);
|
||||
return false;
|
||||
case MG_PPP_PROTO_IP:
|
||||
if (!s_link) return false;
|
||||
MG_VERBOSE(("got IP packet of %d bytes", pay->len));
|
||||
*proto = MG_TCPIP_L2PROTO_IPV4;
|
||||
break;
|
||||
#if 0 && MG_ENABLE_IPV6
|
||||
case MG_PPP_PROTO_IPV6CP:
|
||||
if (s_link) ppp_handle_ipv6cp(ifp, (uint8_t *) pay->buf, pay->len);
|
||||
return false;
|
||||
case MG_PPP_PROTO_IPV6:
|
||||
if (!s_link) return false;
|
||||
MG_VERBOSE(("got IPv6 packet of %d bytes", pay->len));
|
||||
*proto = MG_TCPIP_L2PROTO_IPV6;
|
||||
break;
|
||||
#endif
|
||||
default: {
|
||||
struct lcp rej;
|
||||
uint8_t *p;
|
||||
size_t msglen;
|
||||
MG_DEBUG(("unknown %u-byte PPP frame with proto 0x%04x:",
|
||||
pay->len + sizeof(*ppp), mg_ntohs(ppp->proto)));
|
||||
if (mg_log_level >= MG_LL_DEBUG) mg_hexdump(ppp, sizeof(*ppp) + 20);
|
||||
if (!s_link) return false; // RFC-1661 5.7: must reject on link up
|
||||
if (pay->len > (size_t) (ifp->mtu - 20))
|
||||
pay->len = (size_t) (ifp->mtu - 20); // truncate to some safe limit
|
||||
rej.code = MG_PPP_LCP_REJECT;
|
||||
mg_random(&rej.id, sizeof(rej.id));
|
||||
msglen = pay->len + sizeof(*ppp);
|
||||
rej.len = mg_htons((uint16_t) (msglen + sizeof(rej)));
|
||||
p = ppp_tx_frame_header(ifp, MG_PPP_PROTO_LCP);
|
||||
memmove(p, &rej, sizeof(rej)); // LCP reject
|
||||
memmove(p + sizeof(rej), ppp, msglen); // rejected PPP message
|
||||
ppp_tx_frame_footer(ifp, msglen + sizeof(rej), p + msglen + sizeof(rej));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
(void) raw;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mg_l2_ppp_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
|
||||
struct mg_str *pay, struct mg_str *raw) {
|
||||
#if 0
|
||||
if (ppp->addr == MG_PPP_ADDR && ppp->ctrl == MG_PPP_CTRL) {
|
||||
code = ntohs(ppp->proto);
|
||||
payload = (uint8_t *) (ppp + 1);
|
||||
} else { // Address-and-Control-Field-Compressed PPP header
|
||||
uint16_t *cppp = (uint16_t *) ppp;
|
||||
code = ntohs(*cppp);
|
||||
payload = (uint8_t *) (cppp + 1);
|
||||
}
|
||||
#endif
|
||||
*pay = *raw;
|
||||
*proto = MG_TCPIP_L2PROTO_IPV4;
|
||||
(void) ifp;
|
||||
return true;
|
||||
struct hdlc_ *hdlc = (struct hdlc_ *) raw->buf;
|
||||
if (raw->len < sizeof(*hdlc) + 2) return false; // Truncated
|
||||
if (hdlc->addr == MG_PPP_ADDR && hdlc->ctrl == MG_PPP_CTRL) {
|
||||
pay->buf = (char *) (hdlc + 1);
|
||||
pay->len = raw->len - sizeof(*hdlc) - 2;
|
||||
} else { // Address-and-Control-Field-Compressed PPP header
|
||||
pay->buf = (char *) raw->buf;
|
||||
pay->len = raw->len - 2;
|
||||
}
|
||||
return ppp_rx(ifp, proto, pay, raw);
|
||||
}
|
||||
|
||||
extern struct mg_l2addr s_mapip;
|
||||
|
||||
struct mg_l2addr *mg_l2_ppp_getaddr(uint8_t *frame) {
|
||||
(void) frame;
|
||||
return &s_mapip; // bogus
|
||||
}
|
||||
|
||||
extern struct mg_l2addr s_mapip;
|
||||
|
||||
struct mg_l2addr *mg_l2_ppp_mapip(enum mg_l2addrtype addrtype,
|
||||
struct mg_addr *addr) {
|
||||
(void) addrtype;
|
||||
@@ -112,4 +434,98 @@ uint8_t mg_l2_ppp_ip6put(struct mg_l2addr *addr, uint8_t *opts) {
|
||||
}
|
||||
#endif
|
||||
|
||||
// Transmit a single PPPoE frame for the discovery phase
|
||||
static size_t pppoe_tx_frame(struct mg_tcpip_if *ifp, uint8_t code, uint16_t id,
|
||||
uint8_t *data, size_t datasz,
|
||||
struct mg_l2addr *dst) {
|
||||
uint8_t *l2p = (uint8_t *) ifp->tx.buf;
|
||||
uint8_t *p = pppoe_header(MG_TCPIP_L2PROTO_PPPoE_DISC, code, id,
|
||||
(struct mg_l2addr *) ifp->mac, dst, l2p);
|
||||
memmove(p, data, datasz);
|
||||
return mg_l2_driver_output(ifp, pppoe_footer(datasz, p + datasz));
|
||||
}
|
||||
|
||||
extern struct mg_l2addr *mg_l2_eth_mapip(enum mg_l2addrtype, struct mg_addr *);
|
||||
|
||||
bool mg_l2_pppoe_poll(struct mg_tcpip_if *ifp, bool expired_1000ms) {
|
||||
if (expired_1000ms && s_state == MG_PPPoE_ST_DISC &&
|
||||
ifp->state == MG_TCPIP_STATE_LINK_UP) {
|
||||
uint16_t tags[2];
|
||||
tags[0] = mg_htons(0x0101); // Service Request
|
||||
tags[1] = mg_htons(0x0000); // Any
|
||||
pppoe_tx_frame(ifp, MG_PPPoE_PADI, 0, (uint8_t *) tags, sizeof(tags),
|
||||
mg_l2_eth_mapip(MG_TCPIP_L2ADDR_BCAST, NULL));
|
||||
MG_DEBUG(("Sent PADI"));
|
||||
} else if (expired_1000ms && (s_state != MG_PPPoE_ST_SESS ||
|
||||
ifp->state == MG_TCPIP_STATE_DOWN)) {
|
||||
s_state = MG_PPPoE_ST_DISC;
|
||||
}
|
||||
return mg_l2_ppp_poll(ifp, expired_1000ms);
|
||||
}
|
||||
|
||||
extern bool mg_l2_eth_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
|
||||
struct mg_str *pay, struct mg_str *raw);
|
||||
extern struct mg_l2addr *mg_l2_eth_getaddr(uint8_t *);
|
||||
|
||||
bool mg_l2_pppoe_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
|
||||
struct mg_str *pay, struct mg_str *raw) {
|
||||
enum mg_l2proto eth_proto; // raw is handled by eth
|
||||
struct pppoe *pppoe;
|
||||
if (!mg_l2_eth_rx(ifp, ð_proto, pay, raw)) return false;
|
||||
pppoe = (struct pppoe *) pay->buf; // here we handle pay, not raw
|
||||
if (pay->len < sizeof(*pppoe)) return false; // Truncated
|
||||
if (eth_proto == MG_TCPIP_L2PROTO_PPPoE_DISC) {
|
||||
MG_VERBOSE(("PPPoE_DISC"));
|
||||
if (s_state == MG_PPPoE_ST_DISC && pppoe->code == MG_PPPoE_PADO &&
|
||||
pppoe->id == 0) {
|
||||
uint16_t tags[2];
|
||||
bool has_cookie = false;
|
||||
size_t len = pay->len - sizeof(*pppoe);
|
||||
uint8_t *p = (uint8_t *) (pppoe + 1);
|
||||
uint16_t taglen;
|
||||
while (len >= 4) { // parse tags for a possible AC-Cookie
|
||||
uint16_t curtag = *((uint16_t *) p);
|
||||
taglen = mg_ntohs(*(((uint16_t *) p) + 1));
|
||||
if (taglen > len - 4) return false; // truncated / malformed
|
||||
if (curtag == mg_htons(0x0104)) {
|
||||
has_cookie = true;
|
||||
break;
|
||||
}
|
||||
len -= 4 + taglen;
|
||||
p += 4 + taglen;
|
||||
}
|
||||
tags[0] = mg_htons(0x0101); // Service Request
|
||||
tags[1] = mg_htons(0x0000); // Any
|
||||
if (has_cookie) { // copy tags to before AC-Cookie tag in rx buffer
|
||||
memcpy(p -= sizeof(tags), tags, sizeof(tags)); // point to all tags
|
||||
taglen += 4 + sizeof(tags); // account for tags
|
||||
} else {
|
||||
p = (uint8_t *) tags;
|
||||
taglen = sizeof(tags);
|
||||
}
|
||||
pppoe_tx_frame(ifp, MG_PPPoE_PADR, 0, p, taglen,
|
||||
mg_l2_eth_getaddr((uint8_t *) raw->buf));
|
||||
MG_DEBUG(("Sent PADR"));
|
||||
s_state = MG_PPPoE_ST_REQ;
|
||||
} else if (s_state == MG_PPPoE_ST_REQ && pppoe->code == MG_PPPoE_PADS) {
|
||||
s_id = pppoe->id;
|
||||
memcpy(&ifp->gwmac, mg_l2_eth_getaddr((uint8_t *) raw->buf)->addr.mac, 6);
|
||||
MG_DEBUG(("PPPoE session 0x%04x started", mg_ntohs(s_id)));
|
||||
s_state = MG_PPPoE_ST_SESS;
|
||||
} else if (s_state == MG_PPPoE_ST_SESS && pppoe->code == MG_PPPoE_PADT &&
|
||||
pppoe->id == s_id) {
|
||||
MG_ERROR(("Got PADT"));
|
||||
s_id = 0;
|
||||
s_link = false;
|
||||
s_state = MG_PPPoE_ST_DISC;
|
||||
}
|
||||
} else if (eth_proto == MG_TCPIP_L2PROTO_PPPoE_SESS &&
|
||||
s_state == MG_PPPoE_ST_SESS) {
|
||||
pay->buf = (char *) (pppoe + 1);
|
||||
pay->len = pay->len - sizeof(*pppoe);
|
||||
return ppp_rx(ifp, proto, pay, raw);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+65
-46
@@ -191,11 +191,10 @@ struct pkt {
|
||||
};
|
||||
|
||||
// L2 API
|
||||
void mg_l2_init(enum mg_l2type type, uint8_t *addr, uint16_t *mtu,
|
||||
uint16_t *framesize);
|
||||
void mg_l2_init(struct mg_tcpip_if *ifp);
|
||||
uint8_t *mg_l2_header(enum mg_l2type type, enum mg_l2proto proto, uint8_t *src,
|
||||
uint8_t *dst, uint8_t *frame);
|
||||
size_t mg_l2_footer(enum mg_l2type type, size_t len, uint8_t *frame);
|
||||
size_t mg_l2_footer(enum mg_l2type type, size_t len, uint8_t *cur);
|
||||
bool mg_l2_rx(struct mg_tcpip_if *ifp, enum mg_l2proto *proto,
|
||||
struct mg_str *pay, struct mg_str *raw);
|
||||
uint8_t *mg_l2_getaddr(enum mg_l2type type, uint8_t *frame);
|
||||
@@ -208,6 +207,7 @@ bool mg_l2_ip6get(enum mg_l2type type, uint8_t *addr, uint8_t *opts,
|
||||
uint8_t len);
|
||||
uint8_t mg_l2_ip6put(enum mg_l2type type, uint8_t *addr, uint8_t *opts);
|
||||
#endif
|
||||
bool mg_l2_poll(struct mg_tcpip_if *ifp, bool expired_1000ms);
|
||||
|
||||
static void mg_tcpip_call(struct mg_tcpip_if *ifp, int ev, void *ev_data) {
|
||||
#if MG_ENABLE_PROFILE
|
||||
@@ -263,9 +263,9 @@ static uint16_t pcsum(void *d, void *p, size_t plen) {
|
||||
uint32_t sum;
|
||||
struct ip *ip = (struct ip *) d;
|
||||
#if defined(__DCC__)
|
||||
volatile /* Makes PPC & Diab4.3 happy */
|
||||
volatile /* Makes PPC & Diab4.3 happy */
|
||||
#endif
|
||||
struct pseudoip pip;
|
||||
struct pseudoip pip;
|
||||
pip.src = ip->src;
|
||||
pip.dst = ip->dst;
|
||||
pip.zero = 0;
|
||||
@@ -294,9 +294,9 @@ static uint16_t p6csum(void *d, void *p, size_t plen) {
|
||||
uint32_t sum;
|
||||
struct ip6 *ip6 = (struct ip6 *) d;
|
||||
#if defined(__DCC__)
|
||||
volatile /* Makes PPC & Diab4.3 happy */
|
||||
volatile /* Makes PPC & Diab4.3 happy */
|
||||
#endif
|
||||
struct pseudoip6 pip6;
|
||||
struct pseudoip6 pip6;
|
||||
pip6.src[0] = ip6->src[0], pip6.src[1] = ip6->src[1];
|
||||
pip6.dst[0] = ip6->dst[0], pip6.dst[1] = ip6->dst[1];
|
||||
pip6.zero[0] = 0, pip6.zero[1] = 0, pip6.zero[2] = 0;
|
||||
@@ -376,17 +376,22 @@ void mg_tcpip_arp_request(struct mg_tcpip_if *ifp, uint32_t ip, uint8_t *mac) {
|
||||
arp->op = mg_htons(1), arp->tpa = ip, arp->spa = ifp->ip;
|
||||
memcpy(arp->sha, ifp->mac, sizeof(arp->sha));
|
||||
if (mac != NULL) memcpy(arp->tha, mac, sizeof(arp->tha));
|
||||
driver_output(ifp, mg_l2_footer(ifp->l2type, PDIFF(l2p, arp + 1), l2p));
|
||||
driver_output(ifp,
|
||||
mg_l2_footer(ifp->l2type, sizeof(*arp), (uint8_t *) (arp + 1)));
|
||||
}
|
||||
|
||||
static void onstatechange(struct mg_tcpip_if *ifp) {
|
||||
if (ifp->state == MG_TCPIP_STATE_IP &&
|
||||
(ifp->l2type == MG_TCPIP_L2_PPP || ifp->l2type == MG_TCPIP_L2_PPPoE))
|
||||
ifp->state = MG_TCPIP_STATE_READY;
|
||||
if (ifp->state == MG_TCPIP_STATE_READY) {
|
||||
MG_INFO(("READY, IP: %M", mg_print_ip4, &ifp->ip));
|
||||
MG_INFO((" GW: %M", mg_print_ip4, &ifp->gw));
|
||||
if (ifp->l2type == MG_TCPIP_L2_ETH) // TODO(): print other l2
|
||||
if (ifp->l2type == MG_TCPIP_L2_ETH ||
|
||||
ifp->l2type == MG_TCPIP_L2_PPPoE) // TODO(): print other l2
|
||||
MG_INFO((" MAC: %M", mg_print_mac, ifp->mac));
|
||||
} else if (ifp->state == MG_TCPIP_STATE_IP) {
|
||||
if (ifp->gw != 0)
|
||||
if (ifp->gw != 0 && ifp->l2type == MG_TCPIP_L2_ETH)
|
||||
mg_tcpip_arp_request(ifp, ifp->gw, NULL); // unsolicited GW ARP request
|
||||
} else if (ifp->state == MG_TCPIP_STATE_UP) {
|
||||
srand((unsigned int) mg_millis());
|
||||
@@ -423,7 +428,7 @@ static struct ip6 *tx_ip6(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
|
||||
static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
|
||||
struct mg_addr *ip_src, struct mg_addr *ip_dst,
|
||||
const void *buf, size_t len) {
|
||||
uint8_t *l2p = (uint8_t *) ifp->tx.buf;
|
||||
uint8_t *l3p;
|
||||
size_t l2_len;
|
||||
struct ip *ip = NULL;
|
||||
struct udp *udp;
|
||||
@@ -434,6 +439,7 @@ static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
|
||||
len + sizeof(struct udp));
|
||||
udp = (struct udp *) (ip6 + 1);
|
||||
l2_len = sizeof(*ip6) + sizeof(*udp) + len;
|
||||
l3p = (uint8_t *) ip6;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
@@ -441,6 +447,7 @@ static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
|
||||
len + sizeof(struct udp));
|
||||
udp = (struct udp *) (ip + 1);
|
||||
l2_len = sizeof(*ip) + sizeof(*udp) + len;
|
||||
l3p = (uint8_t *) ip;
|
||||
}
|
||||
udp->sport = ip_src->port;
|
||||
udp->dport = ip_dst->port;
|
||||
@@ -455,7 +462,7 @@ static bool tx_udp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
|
||||
{
|
||||
udp->csum = pcsum(ip, udp, sizeof(*udp) + len);
|
||||
}
|
||||
l2_len = mg_l2_footer(ifp->l2type, l2_len, l2p);
|
||||
l2_len = mg_l2_footer(ifp->l2type, l2_len, l3p + l2_len);
|
||||
return (driver_output(ifp, l2_len) == l2_len);
|
||||
}
|
||||
|
||||
@@ -584,7 +591,8 @@ static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
arp->spa = ifp->ip;
|
||||
MG_DEBUG(("ARP: tell %M we're %M", mg_print_ip4, &arp->tpa, mg_print_mac,
|
||||
ifp->mac));
|
||||
driver_output(ifp, mg_l2_footer(ifp->l2type, PDIFF(l2p, arp + 1), l2p));
|
||||
driver_output(
|
||||
ifp, mg_l2_footer(ifp->l2type, sizeof(*arp), (uint8_t *) (arp + 1)));
|
||||
} else if (pkt->arp->op == mg_htons(2)) {
|
||||
if (memcmp(pkt->arp->tha, ifp->mac, sizeof(pkt->arp->tha)) != 0) return;
|
||||
// MG_VERBOSE(("ARP resp from %M", mg_print_ip4, &pkt->arp->spa));
|
||||
@@ -611,7 +619,6 @@ static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
}
|
||||
|
||||
static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
uint8_t *l2p = (uint8_t *) ifp->tx.buf;
|
||||
size_t plen = pkt->pay.len;
|
||||
if (!icmpcsum_ok(pkt->icmp, sizeof(struct icmp) + plen)) return;
|
||||
if (pkt->icmp->type == 8 && pkt->ip != NULL && pkt->ip->dst == ifp->ip) {
|
||||
@@ -632,7 +639,8 @@ static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
memset(icmp, 0, sizeof(*icmp)); // Set csum, type, code to 0
|
||||
memcpy(icmp + 1, pkt->pay.buf, plen); // Copy RX payload to TX
|
||||
icmp->csum = ipcsum(icmp, sizeof(*icmp) + plen);
|
||||
driver_output(ifp, mg_l2_footer(ifp->l2type, hlen + plen, l2p));
|
||||
driver_output(ifp, mg_l2_footer(ifp->l2type, hlen + plen,
|
||||
(uint8_t *) ip + hlen + plen));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -772,7 +780,6 @@ static struct ip6 *tx_ip6(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
|
||||
static void tx_icmp6(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint64_t *ip_src,
|
||||
uint64_t *ip_dst, uint8_t type, uint8_t code,
|
||||
const void *buf, size_t len) {
|
||||
uint8_t *l2p = (uint8_t *) ifp->tx.buf;
|
||||
struct ip6 *ip6;
|
||||
struct icmp6 *icmp6;
|
||||
ip6 = tx_ip6(ifp, l2_dst, 58, ip_src, ip_dst, sizeof(*icmp6) + len);
|
||||
@@ -783,8 +790,9 @@ static void tx_icmp6(struct mg_tcpip_if *ifp, uint8_t *l2_dst, uint64_t *ip_src,
|
||||
memcpy(icmp6 + 1, buf, len); // Copy payload
|
||||
icmp6->csum = 0; // RFC-4443 2.3, RFC-8200 8.1
|
||||
icmp6->csum = p6csum(ip6, icmp6, sizeof(*icmp6) + len);
|
||||
driver_output(
|
||||
ifp, mg_l2_footer(ifp->l2type, sizeof(*ip6) + sizeof(*icmp6) + len, l2p));
|
||||
driver_output(ifp,
|
||||
mg_l2_footer(ifp->l2type, sizeof(*ip6) + sizeof(*icmp6) + len,
|
||||
(uint8_t *) (ip6 + 1) + sizeof(*icmp6) + len));
|
||||
}
|
||||
|
||||
// Neighbor Discovery Protocol, RFC-4861
|
||||
@@ -1047,7 +1055,7 @@ static void onstate6change(struct mg_tcpip_if *ifp) {
|
||||
} else if (ifp->state6 == MG_TCPIP_STATE_UP) {
|
||||
MG_INFO(("IP: %M", mg_print_ip6, &ifp->ip6ll));
|
||||
}
|
||||
if (ifp->state6 != MG_TCPIP_STATE_UP && ifp->state6 != MG_TCPIP_STATE_DOWN)
|
||||
if (ifp->state6 > MG_TCPIP_STATE_UP)
|
||||
mg_tcpip_call(ifp, MG_TCPIP_EV_ST6_CHG, &ifp->state6);
|
||||
}
|
||||
#endif
|
||||
@@ -1134,7 +1142,7 @@ static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
|
||||
struct mg_addr *ip_src, struct mg_addr *ip_dst,
|
||||
uint8_t flags, uint32_t seq, uint32_t ack, const void *buf,
|
||||
size_t len) {
|
||||
uint8_t *l2p = (uint8_t *) ifp->tx.buf;
|
||||
uint8_t *l3p;
|
||||
struct ip *ip = NULL;
|
||||
struct tcp *tcp;
|
||||
uint16_t opts[4 / 2];
|
||||
@@ -1161,11 +1169,13 @@ static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
|
||||
ip6 =
|
||||
tx_ip6(ifp, l2_dst, 6, ip_src->addr.ip6, ip_dst->addr.ip6, hlen + len);
|
||||
tcp = (struct tcp *) (ip6 + 1);
|
||||
l3p = (uint8_t *) ip6;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
ip = tx_ip(ifp, l2_dst, 6, ip_src->addr.ip4, ip_dst->addr.ip4, hlen + len);
|
||||
tcp = (struct tcp *) (ip + 1);
|
||||
l3p = (uint8_t *) ip;
|
||||
}
|
||||
memset(tcp, 0, sizeof(*tcp));
|
||||
memmove(tcp + 1, opts, hlen - sizeof(*tcp)); // copy opts if any
|
||||
@@ -1188,7 +1198,8 @@ static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *l2_dst,
|
||||
MG_VERBOSE(("TCP %M -> %M fl %x len %u", mg_print_ip_port, ip_src,
|
||||
mg_print_ip_port, ip_dst, tcp->flags, len));
|
||||
return driver_output(
|
||||
ifp, mg_l2_footer(ifp->l2type, PDIFF(l2p, tcp) + hlen + len, l2p));
|
||||
ifp, mg_l2_footer(ifp->l2type, PDIFF(l3p, (uint8_t *) tcp + hlen + len),
|
||||
(uint8_t *) tcp + hlen + len));
|
||||
}
|
||||
|
||||
static size_t tx_tcp_ctrlresp(struct mg_tcpip_if *ifp, struct pkt *pkt,
|
||||
@@ -1771,6 +1782,7 @@ static void mg_tcpip_rx(struct mg_tcpip_if *ifp, void *buf, size_t len) {
|
||||
pkt.raw.len = len;
|
||||
pkt.l2 = (uint8_t *) pkt.raw.buf;
|
||||
if (!mg_l2_rx(ifp, &proto, &pkt.pay, &pkt.raw)) return;
|
||||
if (ifp->state < MG_TCPIP_STATE_UP) return; // discard while L2 is not up
|
||||
if (proto == MG_TCPIP_L2PROTO_ARP) {
|
||||
pkt.arp = (struct arp *) (pkt.pay.buf);
|
||||
if (pkt.pay.len < sizeof(*pkt.arp)) return; // Truncated
|
||||
@@ -1786,7 +1798,7 @@ static void mg_tcpip_rx(struct mg_tcpip_if *ifp, void *buf, size_t len) {
|
||||
}
|
||||
|
||||
static void mg_ip_poll(struct mg_tcpip_if *ifp, bool s1) {
|
||||
if (ifp->state == MG_TCPIP_STATE_DOWN) return;
|
||||
if (ifp->state < MG_TCPIP_STATE_UP) return;
|
||||
// DHCP RFC-2131 (4.4)
|
||||
if (ifp->enable_dhcp_client && s1) {
|
||||
if (ifp->state == MG_TCPIP_STATE_UP) {
|
||||
@@ -1806,11 +1818,13 @@ static void mg_ip_poll(struct mg_tcpip_if *ifp, bool s1) {
|
||||
}
|
||||
}
|
||||
}
|
||||
static void mg_ip_link(struct mg_tcpip_if *ifp, bool up) {
|
||||
bool current = ifp->state != MG_TCPIP_STATE_DOWN;
|
||||
if (!up && ifp->enable_dhcp_client) ifp->ip = 0;
|
||||
if (up != current) { // link state has changed
|
||||
ifp->state = up == false ? MG_TCPIP_STATE_DOWN
|
||||
static void mg_ip_link(struct mg_tcpip_if *ifp, bool drv_up, bool l2_up) {
|
||||
bool cur_drv = (ifp->state != MG_TCPIP_STATE_DOWN);
|
||||
bool cur_l2 = (ifp->state >= MG_TCPIP_STATE_UP);
|
||||
if (!l2_up && ifp->enable_dhcp_client) ifp->ip = 0;
|
||||
if (drv_up != cur_drv || l2_up != cur_l2) { // link/L2 state has changed
|
||||
ifp->state = !drv_up ? MG_TCPIP_STATE_DOWN
|
||||
: !l2_up ? MG_TCPIP_STATE_LINK_UP
|
||||
: ifp->enable_dhcp_client || ifp->ip == 0 ? MG_TCPIP_STATE_UP
|
||||
: MG_TCPIP_STATE_IP;
|
||||
onstatechange(ifp);
|
||||
@@ -1823,15 +1837,17 @@ static void mg_ip_link(struct mg_tcpip_if *ifp, bool up) {
|
||||
|
||||
#if MG_ENABLE_IPV6
|
||||
static void mg_ip6_poll(struct mg_tcpip_if *ifp, bool s1) {
|
||||
if (ifp->state6 == MG_TCPIP_STATE_DOWN) return;
|
||||
if (ifp->state6 < MG_TCPIP_STATE_UP) return;
|
||||
if (ifp->enable_slaac && s1 && ifp->state6 == MG_TCPIP_STATE_UP)
|
||||
tx_ndp_rs(ifp);
|
||||
}
|
||||
static void mg_ip6_link(struct mg_tcpip_if *ifp, bool up) {
|
||||
bool current = ifp->state6 != MG_TCPIP_STATE_DOWN;
|
||||
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);
|
||||
if (!up && ifp->enable_slaac) ifp->ip6[0] = ifp->ip6[1] = 0;
|
||||
if (up != current) { // link state has changed
|
||||
ifp->state6 = !up ? MG_TCPIP_STATE_DOWN
|
||||
if (drv_up != cur_drv || l2_up != cur_l2) { // link/L2 state has changed
|
||||
ifp->state6 = !drv_up ? MG_TCPIP_STATE_DOWN
|
||||
: !l2_up ? MG_TCPIP_STATE_LINK_UP
|
||||
: ifp->enable_slaac || ifp->ip6[0] == 0 ? MG_TCPIP_STATE_UP
|
||||
: MG_TCPIP_STATE_IP;
|
||||
onstate6change(ifp);
|
||||
@@ -1843,7 +1859,7 @@ static void mg_ip6_link(struct mg_tcpip_if *ifp, bool up) {
|
||||
}
|
||||
#else
|
||||
#define mg_ip6_poll(x, y)
|
||||
#define mg_ip6_link(x, y)
|
||||
#define mg_ip6_link(x, y, z)
|
||||
#endif
|
||||
|
||||
static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
|
||||
@@ -1853,7 +1869,7 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
|
||||
|
||||
if (expired_1000ms) {
|
||||
#if MG_ENABLE_TCPIP_PRINT_DEBUG_STATS
|
||||
const char *names[] = {"down", "up", "req", "ip", "ready"};
|
||||
const char *names[] = {"down", "lnk up", "lnk rdy", "req", "ip", "ready"};
|
||||
size_t max = sizeof(names) / sizeof(char *);
|
||||
unsigned int state = ifp->state >= max ? max - 1 : ifp->state;
|
||||
MG_INFO(("Status: %s, IP: %M, rx:%u, tx:%u, dr:%u, er:%u", names[state],
|
||||
@@ -1886,14 +1902,15 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
|
||||
tx_ndp_ns(ifp, ifp->gw6, NULL); // retry GW hwaddr resolution
|
||||
#endif
|
||||
|
||||
// poll driver
|
||||
if (ifp->driver->poll) {
|
||||
bool up = ifp->driver->poll(ifp, expired_1000ms);
|
||||
// Handle physical interface up/down status, ifp->state rules over state6
|
||||
if (expired_1000ms) {
|
||||
mg_ip_link(ifp, up); // Handle IPv4
|
||||
mg_ip6_link(ifp, up); // Handle IPv6
|
||||
if (ifp->state == MG_TCPIP_STATE_DOWN) MG_ERROR(("Network is down"));
|
||||
{ // poll driver and let L2 do its work, if any
|
||||
// Handle physical interface up/down status
|
||||
bool drv_up, l2_up;
|
||||
drv_up = ifp->driver->poll ? ifp->driver->poll(ifp, expired_1000ms) : true;
|
||||
l2_up = mg_l2_poll(ifp, expired_1000ms); // Handle L2 up/down link status;
|
||||
if (expired_1000ms) { // ifp->state rules over state6
|
||||
mg_ip_link(ifp, drv_up, l2_up); // Handle IPv4
|
||||
mg_ip6_link(ifp, drv_up, l2_up); // Handle IPv6
|
||||
if (ifp->state < MG_TCPIP_STATE_UP) MG_ERROR(("Network is down"));
|
||||
mg_tcpip_call(ifp, MG_TCPIP_EV_TIMER_1S, NULL);
|
||||
}
|
||||
}
|
||||
@@ -1901,8 +1918,7 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
|
||||
mg_ip_poll(ifp, expired_1000ms); // Handle IPv4
|
||||
mg_ip6_poll(ifp, expired_1000ms); // Handle IPv6
|
||||
|
||||
if (ifp->state == MG_TCPIP_STATE_DOWN) return;
|
||||
// Read data from the network
|
||||
// Read data from the network (mg_tcpip_rx() will discard for us)
|
||||
if (ifp->driver->rx != NULL) { // Simple polling driver, returns one frame
|
||||
size_t len =
|
||||
ifp->driver->rx(ifp->recv_queue.buf, ifp->recv_queue.size, ifp);
|
||||
@@ -1918,6 +1934,7 @@ static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t now) {
|
||||
mg_queue_del(&ifp->recv_queue, len);
|
||||
}
|
||||
}
|
||||
if (ifp->state < MG_TCPIP_STATE_UP) return; // need to let L2 do its job
|
||||
|
||||
// Process timeouts
|
||||
for (c = ifp->mgr->conns; c != NULL; c = c->next) {
|
||||
@@ -1970,7 +1987,7 @@ void mg_tcpip_qwrite(void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
||||
|
||||
void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) {
|
||||
// If L2 address is not set, make a random one; fill MTU
|
||||
mg_l2_init(ifp->l2type, ifp->mac, &ifp->mtu, &ifp->framesize);
|
||||
mg_l2_init(ifp);
|
||||
|
||||
if (ifp->dhcp_name[0] == '\0') // If DHCP name is not set, use "mip"
|
||||
memcpy(ifp->dhcp_name, "mip", 4);
|
||||
@@ -1988,7 +2005,9 @@ void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) {
|
||||
mgr->ifp = ifp;
|
||||
ifp->mgr = mgr;
|
||||
mgr->extraconnsize = sizeof(struct connstate);
|
||||
if (ifp->ip == 0) ifp->enable_dhcp_client = true;
|
||||
if (ifp->ip == 0 && ifp->l2type != MG_TCPIP_L2_PPP &&
|
||||
ifp->l2type != MG_TCPIP_L2_PPPoE)
|
||||
ifp->enable_dhcp_client = true;
|
||||
mg_random(&ifp->eport, sizeof(ifp->eport)); // Random from 0 to 65535
|
||||
ifp->eport |= MG_EPHEMERAL_PORT_BASE; // Random from
|
||||
// MG_EPHEMERAL_PORT_BASE to 65535
|
||||
|
||||
+6
-5
@@ -82,10 +82,11 @@ struct mg_tcpip_if {
|
||||
volatile uint32_t nerr; // Number of driver errors
|
||||
uint8_t state; // Current link and IPv4 state
|
||||
#define MG_TCPIP_STATE_DOWN 0 // Interface is down
|
||||
#define MG_TCPIP_STATE_UP 1 // Interface is up
|
||||
#define MG_TCPIP_STATE_REQ 2 // Interface is up, DHCP REQUESTING state
|
||||
#define MG_TCPIP_STATE_IP 3 // Interface is up and has an IP assigned
|
||||
#define MG_TCPIP_STATE_READY 4 // Interface has fully come up, ready to work
|
||||
#define MG_TCPIP_STATE_LINK_UP 1 // Driver reports link state is up
|
||||
#define MG_TCPIP_STATE_UP 2 // Interface is up --> LINK_READY
|
||||
#define MG_TCPIP_STATE_REQ 3 // Interface is up, DHCP REQUESTING state
|
||||
#define MG_TCPIP_STATE_IP 4 // Interface is up and has an IP assigned
|
||||
#define MG_TCPIP_STATE_READY 5 // Interface has fully come up, ready to work
|
||||
bool gw_ready; // We've got a hw address for the router
|
||||
#if MG_ENABLE_IPV6
|
||||
uint8_t gw6mac[sizeof(struct mg_l2addr)]; // IPV6 Router's hw address
|
||||
@@ -111,12 +112,12 @@ extern struct mg_tcpip_driver mg_tcpip_driver_cmsis;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_ra;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_xmc;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_xmc7;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_ppp;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_pico_w;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_rw612;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_cyw;
|
||||
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;
|
||||
|
||||
// Drivers that require SPI, can use this SPI abstraction
|
||||
struct mg_tcpip_spi {
|
||||
|
||||
@@ -106,14 +106,14 @@ void setup() {
|
||||
mg_log_set(MG_LL_DEBUG); // Set debug log level
|
||||
mg_log_set_fn([](char c, void *) { Serial.print(c); }, NULL); // Log serial
|
||||
|
||||
mif.driver = &mg_tcpip_driver_ppp; // Initialise built-in TCP/IP stack
|
||||
mif.driver_data = &driver_data; // with the cellular driver
|
||||
mif.driver = &mg_tcpip_driver_atcmd; // Initialise built-in TCP/IP stack
|
||||
mif.l2type = MG_TCPIP_L2_PPP; // with the cellular driver
|
||||
mif.driver_data = &driver_data;
|
||||
driver_data.script = script;
|
||||
driver_data.reset = [](void *) { digitalWrite(RST_PIN, LOW); delay(1); digitalWrite(RST_PIN, HIGH); },
|
||||
driver_data.tx = [](void *, uint8_t c) { ModemSerial.write(c); },
|
||||
driver_data.rx = [](void *) { return ModemSerial.available() ? ModemSerial.read() : -1; },
|
||||
mg_tcpip_init(&mgr, &mif);
|
||||
mif.enable_dhcp_client = false;
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
CFLAGS = -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion
|
||||
CFLAGS += -Wformat-truncation -fno-common -Wconversion -Wno-sign-conversion
|
||||
CFLAGS += -g3 -Os -ffunction-sections -fdata-sections
|
||||
CFLAGS += -I. -Icmsis_core/CMSIS/Core/Include -Icmsis_f7/Include
|
||||
CFLAGS += -mcpu=cortex-m7 -mthumb -mfloat-abi=hard -mfpu=fpv5-sp-d16 $(CFLAGS_EXTRA)
|
||||
LDFLAGS ?= -Tlink.ld -nostdlib -nostartfiles --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map
|
||||
|
||||
SOURCES = main.c syscalls.c sysinit.c
|
||||
SOURCES += cmsis_f7/Source/Templates/gcc/startup_stm32f746xx.s # ST startup file. Compiler-dependent!
|
||||
|
||||
# Mongoose options are defined in mongoose_config.h
|
||||
SOURCES += mongoose.c
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
RM = cmd /C del /Q /F /S
|
||||
else
|
||||
RM = rm -rf
|
||||
endif
|
||||
|
||||
all build example: firmware.bin
|
||||
|
||||
firmware.bin: firmware.elf
|
||||
arm-none-eabi-objcopy -O binary $< $@
|
||||
|
||||
firmware.elf: cmsis_core cmsis_f7 $(SOURCES) hal.h link.ld
|
||||
arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(LDFLAGS) -o $@
|
||||
|
||||
flash: firmware.bin
|
||||
st-flash --reset write $< 0x8000000
|
||||
|
||||
cmsis_core: # ARM CMSIS core headers
|
||||
git clone --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@
|
||||
cmsis_f7: # ST CMSIS headers for STM32F7 series
|
||||
git clone --depth 1 -b v1.2.8 https://github.com/STMicroelectronics/cmsis_device_f7 $@
|
||||
|
||||
clean:
|
||||
$(RM) firmware.* cmsis_core cmsis_f7
|
||||
@@ -0,0 +1,87 @@
|
||||
# PPPoE on NUCLEO-F746ZG
|
||||
|
||||
This example shows a device with a very simple web server (the NUCLEO board) that connects to a PPPoE server. The device web server can then be accessed from the computer running the web server. For more complete scenarios, proper server setting and routing would be needed, thing a network admin will do.
|
||||
|
||||
## Configure and start your PPPoE server
|
||||
|
||||
This is just a test example in Linux, we use _pppoe-server_ for this, as it is easy to configure and serves our purpose. For production scenarios, ask your network admin.
|
||||
|
||||
```sh
|
||||
$ cat /etc/ppp/pppoe-server-options
|
||||
# PPP options for the PPPoE server
|
||||
# LIC: GPL
|
||||
#require-pap
|
||||
#login
|
||||
lcp-echo-interval 10
|
||||
lcp-echo-failure 2
|
||||
$ sudo pppoe-server -F -I yourethernetinterface
|
||||
```
|
||||
|
||||
## Flash your NUCLEO
|
||||
|
||||
```sh
|
||||
$ make
|
||||
$ make flash
|
||||
```
|
||||
|
||||
## Check and test
|
||||
|
||||
```sh
|
||||
$ ifconfig ppp0
|
||||
ppp0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1492
|
||||
inet 10.0.0.1 netmask 255.255.255.255 destination 10.67.15.1
|
||||
ppp txqueuelen 3 (Point-to-Point Protocol)
|
||||
RX packets 3 bytes 36 (36.0 B)
|
||||
RX errors 0 dropped 0 overruns 0 frame 0
|
||||
TX packets 28 bytes 7003 (6.8 KiB)
|
||||
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
|
||||
|
||||
$ route
|
||||
Kernel IP routing table
|
||||
Destination Gateway Genmask Flags Metric Ref Use Iface
|
||||
default _gateway 0.0.0.0 UG 0 0 0 virbr0
|
||||
10.67.15.1 0.0.0.0 255.255.255.255 UH 0 0 0 ppp0
|
||||
|
||||
$ curl http://10.67.15.1/
|
||||
hi
|
||||
$
|
||||
```
|
||||
|
||||
```sh
|
||||
$ minicom -D /dev/ttyACM0
|
||||
```
|
||||
|
||||
```text
|
||||
0 3 mongoose.c:5306:mg_mgr_init MG_IO_SIZE: 512, TLS: builtin
|
||||
6 2 main.c:48:main Init TCP/IP stack ...
|
||||
b 3 mongoose.c:27645:guess_mdc_cr HCLK: 216000000, CR: 4
|
||||
11 2 mongoose.c:25494:mg_phy_init PHY ID: 0x07 0xc131 (LAN87x)
|
||||
18 3 mongoose.c:5223:mg_listen 1 0 tcp://0.0.0.0:80
|
||||
1d 2 main.c:57:main Init done, starting main loop ...
|
||||
7e7 3 mongoose.c:27757:mg_tcpip_driv Link is 100M full-duplex
|
||||
7ec 3 mongoose.c:4035:mg_l2_pppoe_po Sent PADI
|
||||
7f1 1 mongoose.c:7220:mg_tcpip_poll Network is down
|
||||
7f6 3 mongoose.c:4083:mg_l2_pppoe_rx Sent PADR
|
||||
7fa 3 mongoose.c:4088:mg_l2_pppoe_rx PPPoE session 0x0001 started
|
||||
bcf 3 mongoose.c:27757:mg_tcpip_driv Link is 100M full-duplex
|
||||
bd4 1 mongoose.c:7220:mg_tcpip_poll Network is down
|
||||
c0d 3 mongoose.c:3821:ppp_handle_lcp LCP config request of 14 bytes, rejecting...
|
||||
c15 3 mongoose.c:3815:ppp_handle_lcp LCP config request of 4 bytes, acknowledging...
|
||||
c1e 3 mongoose.c:3836:ppp_handle_lcp LCP echo request of 8 bytes, replying...
|
||||
c26 3 mongoose.c:3943:ppp_rx unknown 40-byte PPP frame with proto 0x80fd:
|
||||
0000 80 fd 01 01 00 0f 1a 04 78 00 18 04 78 00 15 03 ........x...x...
|
||||
0010 2f 00 00 00 00 00 /.....
|
||||
c3a 3 mongoose.c:3874:ppp_handle_ipc IPCP ack, GW IP: 10.0.0.1
|
||||
c40 3 mongoose.c:3897:ppp_handle_ipc IPCP ack, IP: 10.67.15.1
|
||||
fb7 2 mongoose.c:5712:onstatechange READY, IP: 10.67.15.1
|
||||
fbc 2 mongoose.c:5713:onstatechange GW: 10.0.0.1
|
||||
fc1 2 mongoose.c:5715:onstatechange MAC: 02:35:42:56:22:2e
|
||||
33d3 3 mongoose.c:3836:ppp_handle_lcp LCP echo request of 8 bytes, replying...
|
||||
5b8b 3 mongoose.c:3836:ppp_handle_lcp LCP echo request of 8 bytes, replying...
|
||||
8348 3 mongoose.c:3836:ppp_handle_lcp LCP echo request of 8 bytes, replying...
|
||||
aafa 3 mongoose.c:3836:ppp_handle_lcp LCP echo request of 8 bytes, replying...
|
||||
d2b7 3 mongoose.c:3836:ppp_handle_lcp LCP echo request of 8 bytes, replying...
|
||||
fa74 3 mongoose.c:3836:ppp_handle_lcp LCP echo request of 8 bytes, replying...
|
||||
12196 3 mongoose.c:6583:accept_conn 2 accepted 10.0.0.1:50846
|
||||
1219d 3 mongoose.c:5159:mg_close_conn 2 0 closed
|
||||
```
|
||||
@@ -0,0 +1,175 @@
|
||||
// Copyright (c) 2022 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
// https://www.st.com/resource/en/reference_manual/dm00124865-stm32f75xxx-and-stm32f74xxx-advanced-arm-based-32-bit-mcus-stmicroelectronics.pdf
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stm32f746xx.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define BIT(x) (1UL << (x))
|
||||
#define SETBITS(R, CLEARMASK, SETMASK) (R) = ((R) & ~(CLEARMASK)) | (SETMASK)
|
||||
#define PIN(bank, num) ((((bank) - 'A') << 8) | (num))
|
||||
#define PINNO(pin) (pin & 255)
|
||||
#define PINBANK(pin) (pin >> 8)
|
||||
|
||||
#define LED1 PIN('B', 0) // On-board LED pin (green)
|
||||
#define LED2 PIN('B', 7) // On-board LED pin (blue)
|
||||
#define LED3 PIN('B', 14) // On-board LED pin (red)
|
||||
|
||||
#define LED LED2 // Use blue LED for blinking
|
||||
|
||||
/* System clock
|
||||
5.3.3: APB1 clock <= 54MHz; APB2 clock <= 108MHz
|
||||
3.3.2, Table 5: configure flash latency (WS) in accordance to clock freq
|
||||
38.4: The AHB clock frequency must be at least 25 MHz when the Ethernet
|
||||
controller is used */
|
||||
enum { APB1_PRE = 5 /* AHB clock / 4 */, APB2_PRE = 4 /* AHB clock / 2 */ };
|
||||
enum { PLL_HSI = 16, PLL_M = 8, PLL_N = 216, PLL_P = 2, PLL_Q = 9 };
|
||||
#define FLASH_LATENCY 7
|
||||
#define SYS_FREQUENCY ((PLL_HSI * PLL_N / PLL_M / PLL_P) * 1000000) // Core 216 MHz, USB 48 MHz
|
||||
#define APB2_FREQUENCY (SYS_FREQUENCY / (BIT(APB2_PRE - 3)))
|
||||
#define APB1_FREQUENCY (SYS_FREQUENCY / (BIT(APB1_PRE - 3)))
|
||||
|
||||
static inline void spin(volatile uint32_t count) {
|
||||
while (count--) (void) 0;
|
||||
}
|
||||
|
||||
enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT, GPIO_MODE_AF, GPIO_MODE_ANALOG };
|
||||
enum { GPIO_OTYPE_PUSH_PULL, GPIO_OTYPE_OPEN_DRAIN };
|
||||
enum { GPIO_SPEED_LOW, GPIO_SPEED_MEDIUM, GPIO_SPEED_HIGH, GPIO_SPEED_INSANE };
|
||||
enum { GPIO_PULL_NONE, GPIO_PULL_UP, GPIO_PULL_DOWN };
|
||||
#define GPIO(N) ((GPIO_TypeDef *) (0x40020000 + 0x400 * (N)))
|
||||
|
||||
static GPIO_TypeDef *gpio_bank(uint16_t pin) { return GPIO(PINBANK(pin)); }
|
||||
static inline void gpio_toggle(uint16_t pin) {
|
||||
GPIO_TypeDef *gpio = gpio_bank(pin);
|
||||
uint32_t mask = BIT(PINNO(pin));
|
||||
gpio->BSRR = mask << (gpio->ODR & mask ? 16 : 0);
|
||||
}
|
||||
static inline int gpio_read(uint16_t pin) {
|
||||
return gpio_bank(pin)->IDR & BIT(PINNO(pin)) ? 1 : 0;
|
||||
}
|
||||
static inline void gpio_write(uint16_t pin, bool val) {
|
||||
GPIO_TypeDef *gpio = gpio_bank(pin);
|
||||
gpio->BSRR = BIT(PINNO(pin)) << (val ? 0 : 16);
|
||||
}
|
||||
static inline void gpio_init(uint16_t pin, uint8_t mode, uint8_t type,
|
||||
uint8_t speed, uint8_t pull, uint8_t af) {
|
||||
GPIO_TypeDef *gpio = gpio_bank(pin);
|
||||
uint8_t n = (uint8_t) (PINNO(pin));
|
||||
RCC->AHB1ENR |= BIT(PINBANK(pin)); // Enable GPIO clock
|
||||
SETBITS(gpio->OTYPER, 1UL << n, ((uint32_t) type) << n);
|
||||
SETBITS(gpio->OSPEEDR, 3UL << (n * 2), ((uint32_t) speed) << (n * 2));
|
||||
SETBITS(gpio->PUPDR, 3UL << (n * 2), ((uint32_t) pull) << (n * 2));
|
||||
SETBITS(gpio->AFR[n >> 3], 15UL << ((n & 7) * 4),
|
||||
((uint32_t) af) << ((n & 7) * 4));
|
||||
SETBITS(gpio->MODER, 3UL << (n * 2), ((uint32_t) mode) << (n * 2));
|
||||
}
|
||||
static inline void gpio_input(uint16_t pin) {
|
||||
gpio_init(pin, GPIO_MODE_INPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH,
|
||||
GPIO_PULL_NONE, 0);
|
||||
}
|
||||
static inline void gpio_output(uint16_t pin) {
|
||||
gpio_init(pin, GPIO_MODE_OUTPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH,
|
||||
GPIO_PULL_NONE, 0);
|
||||
}
|
||||
|
||||
static inline void irq_exti_attach(uint16_t pin) {
|
||||
uint8_t bank = (uint8_t) (PINBANK(pin)), n = (uint8_t) (PINNO(pin));
|
||||
SYSCFG->EXTICR[n / 4] &= ~(15UL << ((n % 4) * 4));
|
||||
SYSCFG->EXTICR[n / 4] |= (uint32_t) (bank << ((n % 4) * 4));
|
||||
EXTI->IMR |= BIT(n);
|
||||
EXTI->RTSR |= BIT(n);
|
||||
EXTI->FTSR |= BIT(n);
|
||||
int irqvec = n < 5 ? 6 + n : n < 10 ? 23 : 40; // IRQ vector index, 10.1.2
|
||||
NVIC_SetPriority(irqvec, 3);
|
||||
NVIC_EnableIRQ(irqvec);
|
||||
}
|
||||
|
||||
#ifndef UART_DEBUG
|
||||
#define UART_DEBUG USART3
|
||||
#endif
|
||||
|
||||
static inline void uart_init(USART_TypeDef *uart, unsigned long baud) {
|
||||
// https://www.st.com/resource/en/datasheet/stm32f746zg.pdf
|
||||
uint8_t af = 7; // Alternate function
|
||||
uint16_t rx = 0, tx = 0; // pins
|
||||
uint32_t freq = 0; // Bus frequency. UART1 is on APB2, rest on APB1
|
||||
|
||||
if (uart == USART1) freq = APB2_FREQUENCY, RCC->APB2ENR |= BIT(4);
|
||||
if (uart == USART2) freq = APB1_FREQUENCY, RCC->APB1ENR |= BIT(17);
|
||||
if (uart == USART3) freq = APB1_FREQUENCY, RCC->APB1ENR |= BIT(18);
|
||||
|
||||
if (uart == USART1) tx = PIN('A', 9), rx = PIN('A', 10);
|
||||
if (uart == USART2) tx = PIN('A', 2), rx = PIN('A', 3);
|
||||
if (uart == USART3) tx = PIN('D', 8), rx = PIN('D', 9);
|
||||
|
||||
gpio_init(tx, GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, 0, af);
|
||||
gpio_init(rx, GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH, 0, af);
|
||||
uart->CR1 = 0; // Disable this UART
|
||||
uart->BRR = freq / baud; // Set baud rate
|
||||
uart->CR1 |= BIT(0) | BIT(2) | BIT(3); // Set UE, RE, TE
|
||||
}
|
||||
static inline void uart_write_byte(USART_TypeDef *uart, uint8_t byte) {
|
||||
uart->TDR = byte;
|
||||
while ((uart->ISR & BIT(7)) == 0) spin(1);
|
||||
}
|
||||
static inline void uart_write_buf(USART_TypeDef *uart, char *buf, size_t len) {
|
||||
while (len-- > 0) uart_write_byte(uart, *(uint8_t *) buf++);
|
||||
}
|
||||
static inline int uart_read_ready(USART_TypeDef *uart) {
|
||||
return uart->ISR & BIT(5); // If RXNE bit is set, data is ready
|
||||
}
|
||||
static inline uint8_t uart_read_byte(USART_TypeDef *uart) {
|
||||
return (uint8_t) (uart->RDR & 255);
|
||||
}
|
||||
|
||||
static inline void rng_init(void) {
|
||||
RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN;
|
||||
RNG->CR |= RNG_CR_RNGEN;
|
||||
}
|
||||
static inline uint32_t rng_read(void) {
|
||||
while ((RNG->SR & RNG_SR_DRDY) == 0) (void) 0;
|
||||
return RNG->DR;
|
||||
}
|
||||
|
||||
// Hw pull-ups on PHY RXD0,1,DV to enable autonegotiation
|
||||
static inline void ethernet_init(void) {
|
||||
// Initialise Ethernet. Enable MAC GPIO pins, see
|
||||
// https://www.farnell.com/datasheets/2014265.pdf section 6.10
|
||||
uint16_t pins[] = {
|
||||
PIN('A', 1), /* ETH_REF_CLK */ \
|
||||
PIN('A', 2), /* ETH_MDIO */ \
|
||||
PIN('A', 7), /* ETH_CRS_DV */ \
|
||||
PIN('B', 13), /* ETH_TXD1 */ \
|
||||
PIN('C', 1), /* ETH_MDC */ \
|
||||
PIN('C', 4), /* ETH_RXD0 */ \
|
||||
PIN('C', 5), /* ETH_RXD1 */ \
|
||||
PIN('G', 11), /* ETH_TX_EN */ \
|
||||
PIN('G', 13) /* ETH_TXD0 */
|
||||
};
|
||||
for (size_t i = 0; i < sizeof(pins) / sizeof(pins[0]); i++) {
|
||||
gpio_init(pins[i], GPIO_MODE_AF, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_INSANE,
|
||||
GPIO_PULL_NONE, 11); // 11 is the Ethernet function
|
||||
}
|
||||
NVIC_EnableIRQ(ETH_IRQn); // Setup Ethernet IRQ handler
|
||||
SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL; // Use RMII. Goes first!
|
||||
RCC->AHB1ENR |=
|
||||
RCC_AHB1ENR_ETHMACEN | RCC_AHB1ENR_ETHMACTXEN | RCC_AHB1ENR_ETHMACRXEN;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define UUID ((uint8_t *) UID_BASE) // Unique 96-bit chip ID. TRM 41.1
|
||||
|
||||
// Helper macro for MAC generation
|
||||
#define GENERATE_LOCALLY_ADMINISTERED_MAC() \
|
||||
{ \
|
||||
2, UUID[0] ^ UUID[1], UUID[2] ^ UUID[3], UUID[4] ^ UUID[5], \
|
||||
UUID[6] ^ UUID[7] ^ UUID[8], UUID[9] ^ UUID[10] ^ UUID[11] \
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
ENTRY(Reset_Handler);
|
||||
MEMORY {
|
||||
flash(rx) : ORIGIN = 0x08000000, LENGTH = 1024k
|
||||
sram(rwx) : ORIGIN = 0x20000000, LENGTH = 320k
|
||||
}
|
||||
_estack = ORIGIN(sram) + LENGTH(sram); /* stack points to end of SRAM */
|
||||
|
||||
SECTIONS {
|
||||
.vectors : { KEEP(*(.isr_vector)) } > flash
|
||||
.text : { *(.text* .text.*) } > flash
|
||||
.rodata : { *(.rodata*) } > flash
|
||||
|
||||
.data : {
|
||||
_sdata = .; /* for init_ram() */
|
||||
*(.first_data)
|
||||
*(.data SORT(.data.*))
|
||||
_edata = .; /* for init_ram() */
|
||||
} > sram AT > flash
|
||||
_sidata = LOADADDR(.data);
|
||||
|
||||
.bss : {
|
||||
_sbss = .; /* for init_ram() */
|
||||
*(.bss SORT(.bss.*) COMMON)
|
||||
_ebss = .; /* for init_ram() */
|
||||
} > sram
|
||||
|
||||
. = ALIGN(8);
|
||||
_end = .; /* for cmsis_gcc.h and init_ram() */
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright (c) 2026 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
|
||||
#include "hal.h"
|
||||
#include "mongoose.h"
|
||||
|
||||
static void blink_cb(void *arg) { // Blink periodically
|
||||
gpio_toggle(LED);
|
||||
(void) arg;
|
||||
}
|
||||
|
||||
static volatile uint64_t s_ticks; // Milliseconds since boot
|
||||
void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms
|
||||
s_ticks++;
|
||||
}
|
||||
|
||||
uint64_t mg_millis(void) { // Let Mongoose use our uptime function
|
||||
return s_ticks; // Return number of milliseconds since boot
|
||||
}
|
||||
|
||||
bool mg_random(void *buf, size_t len) { // Use on-board RNG
|
||||
for (size_t n = 0; n < len; n += sizeof(uint32_t)) {
|
||||
uint32_t r = rng_read();
|
||||
memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
if (ev == MG_EV_HTTP_MSG) {
|
||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||
(void) hm;
|
||||
mg_http_reply(c, 200, "", "hi\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
gpio_output(LED); // Setup blue LED
|
||||
uart_init(UART_DEBUG, 115200); // Initialise debug printf
|
||||
ethernet_init();
|
||||
|
||||
struct mg_mgr mgr; // Initialise
|
||||
mg_mgr_init(&mgr); // Mongoose event manager
|
||||
mg_log_set(MG_LL_DEBUG); // Set log level
|
||||
|
||||
|
||||
MG_INFO(("Init TCP/IP stack ..."));
|
||||
struct mg_tcpip_if mif = {.mac = GENERATE_LOCALLY_ADMINISTERED_MAC(),
|
||||
.driver = &mg_tcpip_driver_stm32f,
|
||||
.l2type = MG_TCPIP_L2_PPPoE,
|
||||
.recv_queue.size = 4096};
|
||||
mg_tcpip_init(&mgr, &mif);
|
||||
mg_timer_add(&mgr, 500, MG_TIMER_REPEAT, blink_cb, &mgr);
|
||||
mg_http_listen(&mgr, "tcp://0.0.0.0:80", fn, &mgr);
|
||||
|
||||
MG_INFO(("Init done, starting main loop ..."));
|
||||
for (;;) {
|
||||
mg_mgr_poll(&mgr, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
../../../mongoose.c
|
||||
@@ -0,0 +1 @@
|
||||
../../../mongoose.h
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
// See https://mongoose.ws/documentation/#build-options
|
||||
#define MG_ARCH MG_ARCH_ARMGCC
|
||||
#define MG_TLS MG_TLS_BUILTIN
|
||||
|
||||
#define MG_ENABLE_TCPIP 1
|
||||
#define MG_ENABLE_CUSTOM_MILLIS 1
|
||||
#define MG_ENABLE_CUSTOM_RANDOM 1
|
||||
#define MG_ENABLE_DRIVER_STM32F 1
|
||||
#define MG_ENABLE_TCPIP_DRIVER_INIT 0
|
||||
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "hal.h"
|
||||
|
||||
int _fstat(int fd, struct stat *st) {
|
||||
if (fd < 0) return -1;
|
||||
st->st_mode = S_IFCHR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *_sbrk(int incr) {
|
||||
extern char _end;
|
||||
static unsigned char *heap = NULL;
|
||||
unsigned char *prev_heap;
|
||||
unsigned char x = 0, *heap_end = (unsigned char *)((size_t) &x - 512);
|
||||
(void) x;
|
||||
if (heap == NULL) heap = (unsigned char *) &_end;
|
||||
prev_heap = heap;
|
||||
if (heap + incr > heap_end) return (void *) -1;
|
||||
heap += incr;
|
||||
return prev_heap;
|
||||
}
|
||||
|
||||
int _open(const char *path) {
|
||||
(void) path;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _close(int fd) {
|
||||
(void) fd;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _isatty(int fd) {
|
||||
(void) fd;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int _lseek(int fd, int ptr, int dir) {
|
||||
(void) fd, (void) ptr, (void) dir;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _exit(int status) {
|
||||
(void) status;
|
||||
for (;;) asm volatile("BKPT #0");
|
||||
}
|
||||
|
||||
void _kill(int pid, int sig) {
|
||||
(void) pid, (void) sig;
|
||||
}
|
||||
|
||||
int _getpid(void) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _write(int fd, char *ptr, int len) {
|
||||
(void) fd, (void) ptr, (void) len;
|
||||
if (fd == 1) uart_write_buf(UART_DEBUG, ptr, (size_t) len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _read(int fd, char *ptr, int len) {
|
||||
(void) fd, (void) ptr, (void) len;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _link(const char *a, const char *b) {
|
||||
(void) a, (void) b;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _unlink(const char *a) {
|
||||
(void) a;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _stat(const char *path, struct stat *st) {
|
||||
(void) path, (void) st;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int mkdir(const char *path, mode_t mode) {
|
||||
(void) path, (void) mode;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void _init(void) {}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2023 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
//
|
||||
// This file contains essentials required by the CMSIS:
|
||||
// uint32_t SystemCoreClock - holds the system core clock value
|
||||
// SystemInit() - initialises the system, e.g. sets up clocks
|
||||
|
||||
#include "hal.h"
|
||||
|
||||
uint32_t SystemCoreClock = SYS_FREQUENCY;
|
||||
|
||||
void SystemInit(void) { // Called automatically by startup code
|
||||
SCB->CPACR |= ((3UL << 10 * 2) | (3UL << 11 * 2)); // Enable FPU
|
||||
asm("DSB");
|
||||
asm("ISB");
|
||||
FLASH->ACR |= FLASH_LATENCY | BIT(8) | BIT(9); // Flash latency, prefetch
|
||||
RCC->PLLCFGR &= ~((BIT(17) - 1) | (0xF << 24)); // Clear PLL multipliers
|
||||
RCC->PLLCFGR |= (((PLL_P - 2) / 2) & 3) << 16; // Set PLL_P
|
||||
RCC->PLLCFGR |= PLL_M | (PLL_N << 6) | (PLL_Q << 24); // Set PLL_M _N _Q
|
||||
RCC->CR |= BIT(24); // Enable PLL
|
||||
while ((RCC->CR & BIT(25)) == 0) spin(1); // Wait until done
|
||||
RCC->CFGR = (APB1_PRE << 10) | (APB2_PRE << 13); // Set prescalers
|
||||
RCC->CFGR |= 2; // Set clock source to PLL
|
||||
while ((RCC->CFGR & 12) == 0) spin(1); // Wait until done
|
||||
|
||||
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; // Enable SYSCFG
|
||||
rng_init(); // Initialise random number generator
|
||||
SysTick_Config(SystemCoreClock / 1000); // Sys tick every 1ms
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user