mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-22 11:36:21 +00:00
Move mip/ -> src/tcpip/, rename mip_ -> mg_tcpip_
This commit is contained in:
1 parent
efc54375dd
commit
2c62e58dbd
64 files changed
+515
-516
No files matched your search
+3
-3
@@ -4,8 +4,8 @@
|
||||
#define MG_ENABLE_LOG 1
|
||||
#endif
|
||||
|
||||
#ifndef MG_ENABLE_MIP
|
||||
#define MG_ENABLE_MIP 0 // Mongoose built-in network stack
|
||||
#ifndef MG_ENABLE_TCPIP
|
||||
#define MG_ENABLE_TCPIP 0 // Mongoose built-in network stack
|
||||
#endif
|
||||
|
||||
#ifndef MG_ENABLE_LWIP
|
||||
@@ -21,7 +21,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef MG_ENABLE_SOCKET
|
||||
#define MG_ENABLE_SOCKET !MG_ENABLE_MIP
|
||||
#define MG_ENABLE_SOCKET !MG_ENABLE_TCPIP
|
||||
#endif
|
||||
|
||||
#ifndef MG_ENABLE_POLL
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
#include "mip.h"
|
||||
|
||||
#if MG_ENABLE_TCPIP && MG_ENABLE_DRIVER_STM32
|
||||
struct stm32_eth {
|
||||
volatile uint32_t MACCR, MACFFR, MACHTHR, MACHTLR, MACMIIAR, MACMIIDR, MACFCR,
|
||||
MACVLANTR, RESERVED0[2], MACRWUFFR, MACPMTCSR, RESERVED1, MACDBGR, MACSR,
|
||||
MACIMR, MACA0HR, MACA0LR, MACA1HR, MACA1LR, MACA2HR, MACA2LR, MACA3HR,
|
||||
MACA3LR, RESERVED2[40], MMCCR, MMCRIR, MMCTIR, MMCRIMR, MMCTIMR,
|
||||
RESERVED3[14], MMCTGFSCCR, MMCTGFMSCCR, RESERVED4[5], MMCTGFCR,
|
||||
RESERVED5[10], MMCRFCECR, MMCRFAECR, RESERVED6[10], MMCRGUFCR,
|
||||
RESERVED7[334], PTPTSCR, PTPSSIR, PTPTSHR, PTPTSLR, PTPTSHUR, PTPTSLUR,
|
||||
PTPTSAR, PTPTTHR, PTPTTLR, RESERVED8, PTPTSSR, PTPPPSCR, RESERVED9[564],
|
||||
DMABMR, DMATPDR, DMARPDR, DMARDLAR, DMATDLAR, DMASR, DMAOMR, DMAIER,
|
||||
DMAMFBOCR, DMARSWTR, RESERVED10[8], DMACHTDR, DMACHRDR, DMACHTBAR,
|
||||
DMACHRBAR;
|
||||
};
|
||||
#undef ETH
|
||||
#define ETH ((struct stm32_eth *) (uintptr_t) 0x40028000)
|
||||
|
||||
#undef BIT
|
||||
#define BIT(x) ((uint32_t) 1 << (x))
|
||||
#define ETH_PKT_SIZE 1540 // Max frame size
|
||||
#define ETH_DESC_CNT 4 // Descriptors count
|
||||
#define ETH_DS 4 // Descriptor size (words)
|
||||
|
||||
static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
|
||||
static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
|
||||
static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
|
||||
static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
|
||||
static uint8_t s_txno; // Current TX descriptor
|
||||
static uint8_t s_rxno; // Current RX descriptor
|
||||
|
||||
static struct mg_tcpip_if *s_ifp; // MIP interface
|
||||
enum { PHY_ADDR = 0, PHY_BCR = 0, PHY_BSR = 1, PHY_CSCR = 31 };
|
||||
|
||||
static uint32_t eth_read_phy(uint8_t addr, uint8_t reg) {
|
||||
ETH->MACMIIAR &= (7 << 2);
|
||||
ETH->MACMIIAR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6);
|
||||
ETH->MACMIIAR |= BIT(0);
|
||||
while (ETH->MACMIIAR & BIT(0)) (void) 0;
|
||||
return ETH->MACMIIDR;
|
||||
}
|
||||
|
||||
static void eth_write_phy(uint8_t addr, uint8_t reg, uint32_t val) {
|
||||
ETH->MACMIIDR = val;
|
||||
ETH->MACMIIAR &= (7 << 2);
|
||||
ETH->MACMIIAR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6) | BIT(1);
|
||||
ETH->MACMIIAR |= BIT(0);
|
||||
while (ETH->MACMIIAR & BIT(0)) (void) 0;
|
||||
}
|
||||
|
||||
static uint32_t get_hclk(void) {
|
||||
struct rcc {
|
||||
volatile uint32_t CR, PLLCFGR, CFGR;
|
||||
} *rcc = (struct rcc *) 0x40023800;
|
||||
uint32_t clk = 0, hsi = 16000000 /* 16 MHz */, hse = 8000000 /* 8MHz */;
|
||||
|
||||
if (rcc->CFGR & (1 << 2)) {
|
||||
clk = hse;
|
||||
} else if (rcc->CFGR & (1 << 3)) {
|
||||
uint32_t vco, m, n, p;
|
||||
m = (rcc->PLLCFGR & (0x3f << 0)) >> 0;
|
||||
n = (rcc->PLLCFGR & (0x1ff << 6)) >> 6;
|
||||
p = (((rcc->PLLCFGR & (3 << 16)) >> 16) + 1) * 2;
|
||||
clk = (rcc->PLLCFGR & (1 << 22)) ? hse : hsi;
|
||||
vco = (uint32_t) ((uint64_t) clk * n / m);
|
||||
clk = vco / p;
|
||||
} else {
|
||||
clk = hsi;
|
||||
}
|
||||
uint32_t hpre = (rcc->CFGR & (15 << 4)) >> 4;
|
||||
if (hpre < 8) return clk;
|
||||
|
||||
uint8_t ahbptab[8] = {1, 2, 3, 4, 6, 7, 8, 9}; // log2(div)
|
||||
return ((uint32_t) clk) >> ahbptab[hpre - 8];
|
||||
}
|
||||
|
||||
// Guess CR from HCLK. MDC clock is generated from HCLK (AHB); as per 802.3,
|
||||
// it must not exceed 2.5MHz As the AHB clock can be (and usually is) derived
|
||||
// from the HSI (internal RC), and it can go above specs, the datasheets
|
||||
// specify a range of frequencies and activate one of a series of dividers to
|
||||
// keep the MDC clock safely below 2.5MHz. We guess a divider setting based on
|
||||
// HCLK with a +5% drift. If the user uses a different clock from our
|
||||
// defaults, needs to set the macros on top Valid for STM32F74xxx/75xxx
|
||||
// (38.8.1) and STM32F42xxx/43xxx (33.8.1) (both 4.5% worst case drift)
|
||||
static int guess_mdc_cr(void) {
|
||||
uint8_t crs[] = {2, 3, 0, 1, 4, 5}; // ETH->MACMIIAR::CR values
|
||||
uint8_t div[] = {16, 26, 42, 62, 102, 124}; // Respective HCLK dividers
|
||||
uint32_t hclk = get_hclk(); // Guess system HCLK
|
||||
int result = -1; // Invalid CR value
|
||||
if (hclk < 25000000) {
|
||||
MG_ERROR(("HCLK too low"));
|
||||
} else {
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (hclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) {
|
||||
result = crs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result < 0) MG_ERROR(("HCLK too high"));
|
||||
}
|
||||
MG_DEBUG(("HCLK: %u, CR: %d", hclk, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool mg_tcpip_driver_stm32_init(struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_driver_stm32_data *d =
|
||||
(struct mg_tcpip_driver_stm32_data *) ifp->driver_data;
|
||||
s_ifp = ifp;
|
||||
|
||||
// Init RX descriptors
|
||||
for (int i = 0; i < ETH_DESC_CNT; i++) {
|
||||
s_rxdesc[i][0] = BIT(31); // Own
|
||||
s_rxdesc[i][1] = sizeof(s_rxbuf[i]) | BIT(14); // 2nd address chained
|
||||
s_rxdesc[i][2] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer
|
||||
s_rxdesc[i][3] =
|
||||
(uint32_t) (uintptr_t) s_rxdesc[(i + 1) % ETH_DESC_CNT]; // Chain
|
||||
}
|
||||
|
||||
// Init TX descriptors
|
||||
for (int i = 0; i < ETH_DESC_CNT; i++) {
|
||||
s_txdesc[i][2] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
|
||||
s_txdesc[i][3] =
|
||||
(uint32_t) (uintptr_t) s_txdesc[(i + 1) % ETH_DESC_CNT]; // Chain
|
||||
}
|
||||
|
||||
ETH->DMABMR |= BIT(0); // Software reset
|
||||
while ((ETH->DMABMR & BIT(0)) != 0) (void) 0; // Wait until done
|
||||
|
||||
// Set MDC clock divider. If user told us the value, use it. Otherwise, guess
|
||||
int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr;
|
||||
ETH->MACMIIAR = ((uint32_t) cr & 7) << 2;
|
||||
|
||||
// NOTE(cpq): we do not use extended descriptor bit 7, and do not use
|
||||
// hardware checksum. Therefore, descriptor size is 4, not 8
|
||||
// ETH->DMABMR = BIT(13) | BIT(16) | BIT(22) | BIT(23) | BIT(25);
|
||||
ETH->MACIMR = BIT(3) | BIT(9); // Mask timestamp & PMT IT
|
||||
ETH->MACFCR = BIT(7); // Disable zero quarta pause
|
||||
// ETH->MACFFR = BIT(31); // Receive all
|
||||
eth_write_phy(PHY_ADDR, PHY_BCR, BIT(15)); // Reset PHY
|
||||
eth_write_phy(PHY_ADDR, PHY_BCR, BIT(12)); // Set autonegotiation
|
||||
ETH->DMARDLAR = (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors
|
||||
ETH->DMATDLAR = (uint32_t) (uintptr_t) s_txdesc; // RX descriptors
|
||||
ETH->DMAIER = BIT(6) | BIT(16); // RIE, NISE
|
||||
ETH->MACCR = BIT(2) | BIT(3) | BIT(11) | BIT(14); // RE, TE, Duplex, Fast
|
||||
ETH->DMAOMR = BIT(1) | BIT(13) | BIT(21) | BIT(25); // SR, ST, TSF, RSF
|
||||
|
||||
// MAC address filtering
|
||||
ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
|
||||
ETH->MACA0LR = (uint32_t) (ifp->mac[3] << 24) |
|
||||
((uint32_t) ifp->mac[2] << 16) |
|
||||
((uint32_t) ifp->mac[1] << 8) | ifp->mac[0];
|
||||
if (ifp->queue.len == 0) ifp->queue.len = 8192;
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t mg_tcpip_driver_stm32_tx(const void *buf, size_t len,
|
||||
struct mg_tcpip_if *ifp) {
|
||||
if (len > sizeof(s_txbuf[s_txno])) {
|
||||
MG_ERROR(("Frame too big, %ld", (long) len));
|
||||
len = 0; // Frame is too big
|
||||
} else if ((s_txdesc[s_txno][0] & BIT(31))) {
|
||||
ifp->nerr++;
|
||||
MG_ERROR(("No free descriptors"));
|
||||
// printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long) ETH->DMASR);
|
||||
len = 0; // All descriptors are busy, fail
|
||||
} else {
|
||||
memcpy(s_txbuf[s_txno], buf, len); // Copy data
|
||||
s_txdesc[s_txno][1] = (uint32_t) len; // Set data len
|
||||
s_txdesc[s_txno][0] = BIT(20) | BIT(28) | BIT(29) | BIT(30); // Chain,FS,LS
|
||||
s_txdesc[s_txno][0] |= BIT(31); // Set OWN bit - let DMA take over
|
||||
if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
|
||||
}
|
||||
ETH->DMASR = BIT(2) | BIT(5); // Clear any prior TBUS/TUS
|
||||
ETH->DMATPDR = 0; // and resume
|
||||
return len;
|
||||
}
|
||||
|
||||
static bool mg_tcpip_driver_stm32_up(struct mg_tcpip_if *ifp) {
|
||||
uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR);
|
||||
bool up = bsr & BIT(2) ? 1 : 0;
|
||||
if ((ifp->state == MIP_STATE_DOWN) && up) { // link state just went up
|
||||
uint32_t scsr = eth_read_phy(PHY_ADDR, PHY_CSCR);
|
||||
uint32_t maccr = ETH->MACCR | BIT(14) | BIT(11); // 100M, Full-duplex
|
||||
if ((scsr & BIT(3)) == 0) maccr &= ~BIT(14); // 10M
|
||||
if ((scsr & BIT(4)) == 0) maccr &= ~BIT(11); // Half-duplex
|
||||
ETH->MACCR = maccr; // IRQ handler does not fiddle with this register
|
||||
MG_DEBUG(("Link is %uM %s-duplex", maccr & BIT(14) ? 100 : 10,
|
||||
maccr & BIT(11) ? "full" : "half"));
|
||||
}
|
||||
return up;
|
||||
}
|
||||
|
||||
void ETH_IRQHandler(void);
|
||||
void ETH_IRQHandler(void) {
|
||||
if (ETH->DMASR & BIT(6)) { // Frame received, loop
|
||||
ETH->DMASR = BIT(16) | BIT(6); // Clear flag
|
||||
for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
|
||||
if (s_rxdesc[s_rxno][0] & BIT(31)) break; // exit when done
|
||||
if (((s_rxdesc[s_rxno][0] & (BIT(8) | BIT(9))) == (BIT(8) | BIT(9))) &&
|
||||
!(s_rxdesc[s_rxno][0] & BIT(15))) { // skip partial/errored frames
|
||||
uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (BIT(14) - 1));
|
||||
// printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0],
|
||||
// ETH->DMASR);
|
||||
mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
|
||||
}
|
||||
s_rxdesc[s_rxno][0] = BIT(31);
|
||||
if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
|
||||
}
|
||||
}
|
||||
ETH->DMASR = BIT(7); // Clear possible RBUS while processing
|
||||
ETH->DMARPDR = 0; // and resume RX
|
||||
}
|
||||
|
||||
struct mg_tcpip_driver mg_tcpip_driver_stm32 = {
|
||||
mg_tcpip_driver_stm32_init, mg_tcpip_driver_stm32_tx, mg_tcpip_driver_rx,
|
||||
mg_tcpip_driver_stm32_up};
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
struct mg_tcpip_driver_stm32_data {
|
||||
// MDC clock divider. MDC clock is derived from HCLK, must not exceed 2.5MHz
|
||||
// HCLK range DIVIDER mdc_cr VALUE
|
||||
// -------------------------------------
|
||||
// -1 <-- tell driver to guess the value
|
||||
// 60-100 MHz HCLK/42 0
|
||||
// 100-150 MHz HCLK/62 1
|
||||
// 20-35 MHz HCLK/16 2
|
||||
// 35-60 MHz HCLK/26 3
|
||||
// 150-216 MHz HCLK/102 4 <-- value for Nucleo-F* on max speed
|
||||
// 216-310 MHz HCLK/124 5
|
||||
// 110, 111 Reserved
|
||||
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
|
||||
};
|
||||
@@ -0,0 +1,276 @@
|
||||
#include "mip.h"
|
||||
|
||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_STM32H) && \
|
||||
MG_ENABLE_DRIVER_STM32H
|
||||
struct stm32h_eth {
|
||||
volatile uint32_t MACCR, MACECR, MACPFR, MACWTR, MACHT0R, MACHT1R,
|
||||
RESERVED1[14], MACVTR, RESERVED2, MACVHTR, RESERVED3, MACVIR, MACIVIR,
|
||||
RESERVED4[2], MACTFCR, RESERVED5[7], MACRFCR, RESERVED6[7], MACISR,
|
||||
MACIER, MACRXTXSR, RESERVED7, MACPCSR, MACRWKPFR, RESERVED8[2], MACLCSR,
|
||||
MACLTCR, MACLETR, MAC1USTCR, RESERVED9[12], MACVR, MACDR, RESERVED10,
|
||||
MACHWF0R, MACHWF1R, MACHWF2R, RESERVED11[54], MACMDIOAR, MACMDIODR,
|
||||
RESERVED12[2], MACARPAR, RESERVED13[59], MACA0HR, MACA0LR, MACA1HR,
|
||||
MACA1LR, MACA2HR, MACA2LR, MACA3HR, MACA3LR, RESERVED14[248], MMCCR,
|
||||
MMCRIR, MMCTIR, MMCRIMR, MMCTIMR, RESERVED15[14], MMCTSCGPR, MMCTMCGPR,
|
||||
RESERVED16[5], MMCTPCGR, RESERVED17[10], MMCRCRCEPR, MMCRAEPR,
|
||||
RESERVED18[10], MMCRUPGR, RESERVED19[9], MMCTLPIMSTR, MMCTLPITCR,
|
||||
MMCRLPIMSTR, MMCRLPITCR, RESERVED20[65], MACL3L4C0R, MACL4A0R,
|
||||
RESERVED21[2], MACL3A0R0R, MACL3A1R0R, MACL3A2R0R, MACL3A3R0R,
|
||||
RESERVED22[4], MACL3L4C1R, MACL4A1R, RESERVED23[2], MACL3A0R1R,
|
||||
MACL3A1R1R, MACL3A2R1R, MACL3A3R1R, RESERVED24[108], MACTSCR, MACSSIR,
|
||||
MACSTSR, MACSTNR, MACSTSUR, MACSTNUR, MACTSAR, RESERVED25, MACTSSR,
|
||||
RESERVED26[3], MACTTSSNR, MACTTSSSR, RESERVED27[2], MACACR, RESERVED28,
|
||||
MACATSNR, MACATSSR, MACTSIACR, MACTSEACR, MACTSICNR, MACTSECNR,
|
||||
RESERVED29[4], MACPPSCR, RESERVED30[3], MACPPSTTSR, MACPPSTTNR, MACPPSIR,
|
||||
MACPPSWR, RESERVED31[12], MACPOCR, MACSPI0R, MACSPI1R, MACSPI2R, MACLMIR,
|
||||
RESERVED32[11], MTLOMR, RESERVED33[7], MTLISR, RESERVED34[55], MTLTQOMR,
|
||||
MTLTQUR, MTLTQDR, RESERVED35[8], MTLQICSR, MTLRQOMR, MTLRQMPOCR, MTLRQDR,
|
||||
RESERVED36[177], DMAMR, DMASBMR, DMAISR, DMADSR, RESERVED37[60], DMACCR,
|
||||
DMACTCR, DMACRCR, RESERVED38[2], DMACTDLAR, RESERVED39, DMACRDLAR,
|
||||
DMACTDTPR, RESERVED40, DMACRDTPR, DMACTDRLR, DMACRDRLR, DMACIER,
|
||||
DMACRIWTR, DMACSFCSR, RESERVED41, DMACCATDR, RESERVED42, DMACCARDR,
|
||||
RESERVED43, DMACCATBR, RESERVED44, DMACCARBR, DMACSR, RESERVED45[2],
|
||||
DMACMFCR;
|
||||
};
|
||||
#undef ETH
|
||||
#define ETH \
|
||||
((struct stm32h_eth *) (uintptr_t) (0x40000000UL + 0x00020000UL + 0x8000UL))
|
||||
|
||||
#undef BIT
|
||||
#define BIT(x) ((uint32_t) 1 << (x))
|
||||
#define ETH_PKT_SIZE 1540 // Max frame size
|
||||
#define ETH_DESC_CNT 4 // Descriptors count
|
||||
#define ETH_DS 4 // Descriptor size (words)
|
||||
|
||||
static volatile uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
|
||||
static volatile uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
|
||||
static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
|
||||
static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
|
||||
static struct mg_tcpip_if *s_ifp; // MIP interface
|
||||
enum {
|
||||
PHY_ADDR = 0,
|
||||
PHY_BCR = 0,
|
||||
PHY_BSR = 1,
|
||||
PHY_CSCR = 31
|
||||
}; // PHY constants
|
||||
|
||||
static uint32_t eth_read_phy(uint8_t addr, uint8_t reg) {
|
||||
ETH->MACMDIOAR &= (0xF << 8);
|
||||
ETH->MACMDIOAR |= ((uint32_t) addr << 21) | ((uint32_t) reg << 16) | 3 << 2;
|
||||
ETH->MACMDIOAR |= BIT(0);
|
||||
while (ETH->MACMDIOAR & BIT(0)) (void) 0;
|
||||
return ETH->MACMDIODR;
|
||||
}
|
||||
|
||||
static void eth_write_phy(uint8_t addr, uint8_t reg, uint32_t val) {
|
||||
ETH->MACMDIODR = val;
|
||||
ETH->MACMDIOAR &= (0xF << 8);
|
||||
ETH->MACMDIOAR |= ((uint32_t) addr << 21) | ((uint32_t) reg << 16) | 1 << 2;
|
||||
ETH->MACMDIOAR |= BIT(0);
|
||||
while (ETH->MACMDIOAR & BIT(0)) (void) 0;
|
||||
}
|
||||
|
||||
static uint32_t get_hclk(void) {
|
||||
struct rcc {
|
||||
volatile uint32_t CR, HSICFGR, CRRCR, CSICFGR, CFGR, RESERVED1, D1CFGR,
|
||||
D2CFGR, D3CFGR, RESERVED2, PLLCKSELR, PLLCFGR, PLL1DIVR, PLL1FRACR,
|
||||
PLL2DIVR, PLL2FRACR, PLL3DIVR, PLL3FRACR, RESERVED3, D1CCIPR, D2CCIP1R,
|
||||
D2CCIP2R, D3CCIPR, RESERVED4, CIER, CIFR, CICR, RESERVED5, BDCR, CSR,
|
||||
RESERVED6, AHB3RSTR, AHB1RSTR, AHB2RSTR, AHB4RSTR, APB3RSTR, APB1LRSTR,
|
||||
APB1HRSTR, APB2RSTR, APB4RSTR, GCR, RESERVED8, D3AMR, RESERVED11[9],
|
||||
RSR, AHB3ENR, AHB1ENR, AHB2ENR, AHB4ENR, APB3ENR, APB1LENR, APB1HENR,
|
||||
APB2ENR, APB4ENR, RESERVED12, AHB3LPENR, AHB1LPENR, AHB2LPENR,
|
||||
AHB4LPENR, APB3LPENR, APB1LLPENR, APB1HLPENR, APB2LPENR, APB4LPENR,
|
||||
RESERVED13[4];
|
||||
} *rcc = ((struct rcc *) (0x40000000 + 0x18020000 + 0x4400));
|
||||
uint32_t clk = 0, hsi = 64000000 /* 64 MHz */, hse = 8000000 /* 8MHz */,
|
||||
csi = 4000000 /* 4MHz */;
|
||||
unsigned int sel = (rcc->CFGR & (7 << 3)) >> 3;
|
||||
|
||||
if (sel == 1) {
|
||||
clk = csi;
|
||||
} else if (sel == 2) {
|
||||
clk = hse;
|
||||
} else if (sel == 3) {
|
||||
uint32_t vco, m, n, p;
|
||||
unsigned int src = (rcc->PLLCKSELR & (3 << 0)) >> 0;
|
||||
m = ((rcc->PLLCKSELR & (0x3F << 4)) >> 4);
|
||||
n = ((rcc->PLL1DIVR & (0x1FF << 0)) >> 0) + 1 +
|
||||
((rcc->PLLCFGR & BIT(0)) ? 1 : 0); // round-up in fractional mode
|
||||
p = ((rcc->PLL1DIVR & (0x7F << 9)) >> 9) + 1;
|
||||
if (src == 1) {
|
||||
clk = csi;
|
||||
} else if (src == 2) {
|
||||
clk = hse;
|
||||
} else {
|
||||
clk = hsi;
|
||||
clk >>= ((rcc->CR & 3) >> 3);
|
||||
}
|
||||
vco = (uint32_t) ((uint64_t) clk * n / m);
|
||||
clk = vco / p;
|
||||
} else {
|
||||
clk = hsi;
|
||||
clk >>= ((rcc->CR & 3) >> 3);
|
||||
}
|
||||
const uint8_t cptab[12] = {1, 2, 3, 4, 6, 7, 8, 9}; // log2(div)
|
||||
uint32_t d1cpre = (rcc->D1CFGR & (0x0F << 8)) >> 8;
|
||||
if (d1cpre >= 8) clk >>= cptab[d1cpre - 8];
|
||||
MG_DEBUG(("D1 CLK: %u", clk));
|
||||
uint32_t hpre = (rcc->D1CFGR & (0x0F << 0)) >> 0;
|
||||
if (hpre < 8) return clk;
|
||||
return ((uint32_t) clk) >> cptab[hpre - 8];
|
||||
}
|
||||
|
||||
// Guess CR from AHB1 clock. MDC clock is generated from the ETH peripheral
|
||||
// clock (AHB1); as per 802.3, it must not exceed 2. As the AHB clock can
|
||||
// be derived from HSI or CSI (internal RC) clocks, and those can go above
|
||||
// specs, the datasheets specify a range of frequencies and activate one of a
|
||||
// series of dividers to keep the MDC clock safely below 2.5MHz. We guess a
|
||||
// divider setting based on HCLK with some drift. If the user uses a different
|
||||
// clock from our defaults, needs to set the macros on top. Valid for
|
||||
// STM32H74xxx/75xxx (58.11.4)(4.5% worst case drift)(CSI clock has a 7.5 %
|
||||
// worst case drift @ max temp)
|
||||
static int guess_mdc_cr(void) {
|
||||
const uint8_t crs[] = {2, 3, 0, 1, 4, 5}; // ETH->MACMDIOAR::CR values
|
||||
const uint8_t div[] = {16, 26, 42, 62, 102, 124}; // Respective HCLK dividers
|
||||
uint32_t hclk = get_hclk(); // Guess system HCLK
|
||||
int result = -1; // Invalid CR value
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (hclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) {
|
||||
result = crs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result < 0) MG_ERROR(("HCLK too high"));
|
||||
MG_DEBUG(("HCLK: %u, CR: %d", hclk, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool mg_tcpip_driver_stm32h_init(struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_driver_stm32h_data *d =
|
||||
(struct mg_tcpip_driver_stm32h_data *) ifp->driver_data;
|
||||
s_ifp = ifp;
|
||||
|
||||
// Init RX descriptors
|
||||
for (int i = 0; i < ETH_DESC_CNT; i++) {
|
||||
s_rxdesc[i][0] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer
|
||||
s_rxdesc[i][3] = BIT(31) | BIT(30) | BIT(24); // OWN, IOC, BUF1V
|
||||
}
|
||||
|
||||
// Init TX descriptors
|
||||
for (int i = 0; i < ETH_DESC_CNT; i++) {
|
||||
s_txdesc[i][0] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
|
||||
}
|
||||
|
||||
ETH->DMAMR |= BIT(0); // Software reset
|
||||
while ((ETH->DMAMR & BIT(0)) != 0) (void) 0; // Wait until done
|
||||
|
||||
// Set MDC clock divider. If user told us the value, use it. Otherwise, guess
|
||||
int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr;
|
||||
ETH->MACMDIOAR = ((uint32_t) cr & 0xF) << 8;
|
||||
|
||||
// NOTE(scaprile): We do not use timing facilities so the DMA engine does not
|
||||
// re-write buffer address
|
||||
ETH->DMAMR = 0 << 16; // use interrupt mode 0 (58.8.1) (reset value)
|
||||
ETH->DMASBMR |= BIT(12); // AAL NOTE(scaprile): is this actually needed
|
||||
ETH->MACIER = 0; // Do not enable additional irq sources (reset value)
|
||||
ETH->MACTFCR = BIT(7); // Disable zero-quanta pause
|
||||
// ETH->MACPFR = BIT(31); // Receive all
|
||||
eth_write_phy(PHY_ADDR, PHY_BCR, BIT(15)); // Reset PHY
|
||||
eth_write_phy(PHY_ADDR, PHY_BCR, BIT(12)); // Set autonegotiation
|
||||
ETH->DMACRDLAR =
|
||||
(uint32_t) (uintptr_t) s_rxdesc; // RX descriptors start address
|
||||
ETH->DMACRDRLR = ETH_DESC_CNT - 1; // ring length
|
||||
ETH->DMACRDTPR =
|
||||
(uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT -
|
||||
1]; // last valid descriptor address
|
||||
ETH->DMACTDLAR =
|
||||
(uint32_t) (uintptr_t) s_txdesc; // TX descriptors start address
|
||||
ETH->DMACTDRLR = ETH_DESC_CNT - 1; // ring length
|
||||
ETH->DMACTDTPR =
|
||||
(uint32_t) (uintptr_t) s_txdesc; // first available descriptor address
|
||||
ETH->DMACCR = 0; // DSL = 0 (contiguous descriptor table) (reset value)
|
||||
ETH->DMACIER = BIT(6) | BIT(15); // RIE, NIE
|
||||
ETH->MACCR = BIT(0) | BIT(1) | BIT(13) | BIT(14) |
|
||||
BIT(15); // RE, TE, Duplex, Fast, Reserved
|
||||
ETH->MTLTQOMR |= BIT(1); // TSF
|
||||
ETH->MTLRQOMR |= BIT(5); // RSF
|
||||
ETH->DMACTCR |= BIT(0); // ST
|
||||
ETH->DMACRCR |= BIT(0); // SR
|
||||
|
||||
// MAC address filtering
|
||||
ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
|
||||
ETH->MACA0LR = (uint32_t) (ifp->mac[3] << 24) |
|
||||
((uint32_t) ifp->mac[2] << 16) |
|
||||
((uint32_t) ifp->mac[1] << 8) | ifp->mac[0];
|
||||
if (ifp->queue.len == 0) ifp->queue.len = 8192;
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint32_t s_txno;
|
||||
static size_t mg_tcpip_driver_stm32h_tx(const void *buf, size_t len,
|
||||
struct mg_tcpip_if *ifp) {
|
||||
if (len > sizeof(s_txbuf[s_txno])) {
|
||||
MG_ERROR(("Frame too big, %ld", (long) len));
|
||||
len = 0; // Frame is too big
|
||||
} else if ((s_txdesc[s_txno][3] & BIT(31))) {
|
||||
MG_ERROR(("No free descriptors: %u %08X %08X %08X", s_txno,
|
||||
s_txdesc[s_txno][3], ETH->DMACSR, ETH->DMACTCR));
|
||||
for (int i = 0; i < ETH_DESC_CNT; i++) MG_ERROR(("%08X", s_txdesc[i][3]));
|
||||
len = 0; // All descriptors are busy, fail
|
||||
} else {
|
||||
memcpy(s_txbuf[s_txno], buf, len); // Copy data
|
||||
s_txdesc[s_txno][2] = (uint32_t) len; // Set data len
|
||||
s_txdesc[s_txno][3] = BIT(28) | BIT(29); // FD, LD
|
||||
s_txdesc[s_txno][3] |= BIT(31); // Set OWN bit - let DMA take over
|
||||
if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
|
||||
}
|
||||
ETH->DMACSR |= BIT(2) | BIT(1); // Clear any prior TBU, TPS
|
||||
ETH->DMACTDTPR = (uint32_t) (uintptr_t) &s_txdesc[s_txno]; // and resume
|
||||
return len;
|
||||
(void) ifp;
|
||||
}
|
||||
|
||||
static bool mg_tcpip_driver_stm32h_up(struct mg_tcpip_if *ifp) {
|
||||
uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR);
|
||||
bool up = bsr & BIT(2) ? 1 : 0;
|
||||
if ((ifp->state == MIP_STATE_DOWN) && up) { // link state just went up
|
||||
uint32_t scsr = eth_read_phy(PHY_ADDR, PHY_CSCR);
|
||||
uint32_t maccr = ETH->MACCR | BIT(14) | BIT(13); // 100M, Full-duplex
|
||||
if ((scsr & BIT(3)) == 0) maccr &= ~BIT(14); // 10M
|
||||
if ((scsr & BIT(4)) == 0) maccr &= ~BIT(13); // Half-duplex
|
||||
ETH->MACCR = maccr; // IRQ handler does not fiddle with this register
|
||||
MG_DEBUG(("Link is %uM %s-duplex", maccr & BIT(14) ? 100 : 10,
|
||||
maccr & BIT(13) ? "full" : "half"));
|
||||
}
|
||||
return up;
|
||||
}
|
||||
|
||||
void ETH_IRQHandler(void);
|
||||
static uint32_t s_rxno;
|
||||
void ETH_IRQHandler(void) {
|
||||
if (ETH->DMACSR & BIT(6)) { // Frame received, loop
|
||||
ETH->DMACSR = BIT(15) | BIT(6); // Clear flag
|
||||
for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
|
||||
if (s_rxdesc[s_rxno][3] & BIT(31)) break; // exit when done
|
||||
if (((s_rxdesc[s_rxno][3] & (BIT(28) | BIT(29))) ==
|
||||
(BIT(28) | BIT(29))) &&
|
||||
!(s_rxdesc[s_rxno][3] & BIT(15))) { // skip partial/errored frames
|
||||
uint32_t len = s_rxdesc[s_rxno][3] & (BIT(15) - 1);
|
||||
// MG_DEBUG(("%lx %lu %lx %08lx", s_rxno, len, s_rxdesc[s_rxno][3],
|
||||
// ETH->DMACSR));
|
||||
mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
|
||||
}
|
||||
s_rxdesc[s_rxno][3] = BIT(31) | BIT(30) | BIT(24); // OWN, IOC, BUF1V
|
||||
if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
|
||||
}
|
||||
}
|
||||
ETH->DMACSR = BIT(7) | BIT(8); // Clear possible RBU RPS while processing
|
||||
ETH->DMACRDTPR =
|
||||
(uint32_t) (uintptr_t) &s_rxdesc[ETH_DESC_CNT - 1]; // and resume RX
|
||||
}
|
||||
|
||||
struct mg_tcpip_driver mg_tcpip_driver_stm32h = {
|
||||
mg_tcpip_driver_stm32h_init, mg_tcpip_driver_stm32h_tx, mg_tcpip_driver_rx,
|
||||
mg_tcpip_driver_stm32h_up};
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
struct mg_tcpip_driver_stm32h_data {
|
||||
// MDC clock divider. MDC clock is derived from HCLK, must not exceed 2.5MHz
|
||||
// HCLK range DIVIDER mdc_cr VALUE
|
||||
// -------------------------------------
|
||||
// -1 <-- tell driver to guess the value
|
||||
// 60-100 MHz HCLK/42 0
|
||||
// 100-150 MHz HCLK/62 1
|
||||
// 20-35 MHz HCLK/16 2
|
||||
// 35-60 MHz HCLK/26 3
|
||||
// 150-250 MHz HCLK/102 4 <-- value for Nucleo-H* on max speed driven by HSI
|
||||
// 250-300 MHz HCLK/124 5 <-- value for Nucleo-H* on max speed driven by CSI
|
||||
// 110, 111 Reserved
|
||||
int mdc_cr; // Valid values: -1, 0, 1, 2, 3, 4, 5
|
||||
};
|
||||
@@ -0,0 +1,251 @@
|
||||
#include "mip.h"
|
||||
|
||||
#if MG_ENABLE_TCPIP && defined(MG_ENABLE_DRIVER_TM4C) && MG_ENABLE_DRIVER_TM4C
|
||||
struct tm4c_emac {
|
||||
volatile uint32_t EMACCFG, EMACFRAMEFLTR, EMACHASHTBLH, EMACHASHTBLL,
|
||||
EMACMIIADDR, EMACMIIDATA, EMACFLOWCTL, EMACVLANTG, RESERVED0, EMACSTATUS,
|
||||
EMACRWUFF, EMACPMTCTLSTAT, RESERVED1[2], EMACRIS, EMACIM, EMACADDR0H,
|
||||
EMACADDR0L, EMACADDR1H, EMACADDR1L, EMACADDR2H, EMACADDR2L, EMACADDR3H,
|
||||
EMACADDR3L, RESERVED2[31], EMACWDOGTO, RESERVED3[8], EMACMMCCTRL,
|
||||
EMACMMCRXRIS, EMACMMCTXRIS, EMACMMCRXIM, EMACMMCTXIM, RESERVED4,
|
||||
EMACTXCNTGB, RESERVED5[12], EMACTXCNTSCOL, EMACTXCNTMCOL, RESERVED6[4],
|
||||
EMACTXOCTCNTG, RESERVED7[6], EMACRXCNTGB, RESERVED8[4], EMACRXCNTCRCERR,
|
||||
EMACRXCNTALGNERR, RESERVED9[10], EMACRXCNTGUNI, RESERVED10[239],
|
||||
EMACVLNINCREP, EMACVLANHASH, RESERVED11[93], EMACTIMSTCTRL, EMACSUBSECINC,
|
||||
EMACTIMSEC, EMACTIMNANO, EMACTIMSECU, EMACTIMNANOU, EMACTIMADD,
|
||||
EMACTARGSEC, EMACTARGNANO, EMACHWORDSEC, EMACTIMSTAT, EMACPPSCTRL,
|
||||
RESERVED12[12], EMACPPS0INTVL, EMACPPS0WIDTH, RESERVED13[294],
|
||||
EMACDMABUSMOD, EMACTXPOLLD, EMACRXPOLLD, EMACRXDLADDR, EMACTXDLADDR,
|
||||
EMACDMARIS, EMACDMAOPMODE, EMACDMAIM, EMACMFBOC, EMACRXINTWDT,
|
||||
RESERVED14[8], EMACHOSTXDESC, EMACHOSRXDESC, EMACHOSTXBA, EMACHOSRXBA,
|
||||
RESERVED15[218], EMACPP, EMACPC, EMACCC, RESERVED16, EMACEPHYRIS,
|
||||
EMACEPHYIM, EMACEPHYIMSC;
|
||||
};
|
||||
#undef EMAC
|
||||
#define EMAC ((struct tm4c_emac *) (uintptr_t) 0x400EC000)
|
||||
|
||||
#undef BIT
|
||||
#define BIT(x) ((uint32_t) 1 << (x))
|
||||
#define ETH_PKT_SIZE 1540 // Max frame size
|
||||
#define ETH_DESC_CNT 4 // Descriptors count
|
||||
#define ETH_DS 4 // Descriptor size (words)
|
||||
|
||||
static uint32_t s_rxdesc[ETH_DESC_CNT][ETH_DS]; // RX descriptors
|
||||
static uint32_t s_txdesc[ETH_DESC_CNT][ETH_DS]; // TX descriptors
|
||||
static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // RX ethernet buffers
|
||||
static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE]; // TX ethernet buffers
|
||||
static struct mg_tcpip_if *s_ifp; // MIP interface
|
||||
enum {
|
||||
EPHY_ADDR = 0,
|
||||
EPHYBMCR = 0,
|
||||
EPHYBMSR = 1,
|
||||
EPHYSTS = 16
|
||||
}; // PHY constants
|
||||
|
||||
static inline void tm4cspin(volatile uint32_t count) {
|
||||
while (count--) (void) 0;
|
||||
}
|
||||
|
||||
static uint32_t emac_read_phy(uint8_t addr, uint8_t reg) {
|
||||
EMAC->EMACMIIADDR &= (0xf << 2);
|
||||
EMAC->EMACMIIADDR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6);
|
||||
EMAC->EMACMIIADDR |= BIT(0);
|
||||
while (EMAC->EMACMIIADDR & BIT(0)) tm4cspin(1);
|
||||
return EMAC->EMACMIIDATA;
|
||||
}
|
||||
|
||||
static void emac_write_phy(uint8_t addr, uint8_t reg, uint32_t val) {
|
||||
EMAC->EMACMIIDATA = val;
|
||||
EMAC->EMACMIIADDR &= (0xf << 2);
|
||||
EMAC->EMACMIIADDR |= ((uint32_t) addr << 11) | ((uint32_t) reg << 6) | BIT(1);
|
||||
EMAC->EMACMIIADDR |= BIT(0);
|
||||
while (EMAC->EMACMIIADDR & BIT(0)) tm4cspin(1);
|
||||
}
|
||||
|
||||
static uint32_t get_sysclk(void) {
|
||||
struct sysctl {
|
||||
volatile uint32_t DONTCARE0[44], RSCLKCFG, DONTCARE1[43], PLLFREQ0,
|
||||
PLLFREQ1;
|
||||
} *sysctl = (struct sysctl *) 0x400FE000;
|
||||
uint32_t clk = 0, piosc = 16000000 /* 16 MHz */, mosc = 25000000 /* 25MHz */;
|
||||
if (sysctl->RSCLKCFG & (1 << 28)) { // USEPLL
|
||||
uint32_t fin, vco, mdiv, n, q, psysdiv;
|
||||
uint32_t pllsrc = (sysctl->RSCLKCFG & (0xf << 24)) >> 24;
|
||||
if (pllsrc == 0) {
|
||||
clk = piosc;
|
||||
} else if (pllsrc == 3) {
|
||||
clk = mosc;
|
||||
} else {
|
||||
MG_ERROR(("Unsupported clock source"));
|
||||
}
|
||||
q = (sysctl->PLLFREQ1 & (0x1f << 8)) >> 8;
|
||||
n = (sysctl->PLLFREQ1 & (0x1f << 0)) >> 0;
|
||||
fin = clk / ((q + 1) * (n + 1));
|
||||
mdiv = (sysctl->PLLFREQ0 & (0x3ff << 0)) >>
|
||||
0; // mint + (mfrac / 1024); MFRAC not supported
|
||||
psysdiv = (sysctl->RSCLKCFG & (0x3f << 0)) >> 0;
|
||||
vco = (uint32_t) ((uint64_t) fin * mdiv);
|
||||
return vco / (psysdiv + 1);
|
||||
}
|
||||
uint32_t oscsrc = (sysctl->RSCLKCFG & (0xf << 20)) >> 20;
|
||||
if (oscsrc == 0) {
|
||||
clk = piosc;
|
||||
} else if (oscsrc == 3) {
|
||||
clk = mosc;
|
||||
} else {
|
||||
MG_ERROR(("Unsupported clock source"));
|
||||
}
|
||||
uint32_t osysdiv = (sysctl->RSCLKCFG & (0xf << 16)) >> 16;
|
||||
return clk / (osysdiv + 1);
|
||||
}
|
||||
|
||||
// Guess CR from SYSCLK. MDC clock is generated from SYSCLK (AHB); as per
|
||||
// 802.3, it must not exceed 2.5MHz (also 20.4.2.6) As the AHB clock can be
|
||||
// derived from the PIOSC (internal RC), and it can go above specs, the
|
||||
// datasheets specify a range of frequencies and activate one of a series of
|
||||
// dividers to keep the MDC clock safely below 2.5MHz. We guess a divider
|
||||
// setting based on SYSCLK with a +5% drift. If the user uses a different clock
|
||||
// from our defaults, needs to set the macros on top Valid for TM4C129x (20.7)
|
||||
// (4.5% worst case drift)
|
||||
// The PHY receives the main oscillator (MOSC) (20.3.1)
|
||||
static int guess_mdc_cr(void) {
|
||||
uint8_t crs[] = {2, 3, 0, 1}; // EMAC->MACMIIAR::CR values
|
||||
uint8_t div[] = {16, 26, 42, 62}; // Respective HCLK dividers
|
||||
uint32_t sysclk = get_sysclk(); // Guess system SYSCLK
|
||||
int result = -1; // Invalid CR value
|
||||
if (sysclk < 25000000) {
|
||||
MG_ERROR(("SYSCLK too low"));
|
||||
} else {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (sysclk / div[i] <= 2375000UL /* 2.5MHz - 5% */) {
|
||||
result = crs[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result < 0) MG_ERROR(("SYSCLK too high"));
|
||||
}
|
||||
MG_DEBUG(("SYSCLK: %u, CR: %d", sysclk, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool mg_tcpip_driver_tm4c_init(struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_driver_tm4c_data *d =
|
||||
(struct mg_tcpip_driver_tm4c_data *) ifp->driver_data;
|
||||
s_ifp = ifp;
|
||||
|
||||
// Init RX descriptors
|
||||
for (int i = 0; i < ETH_DESC_CNT; i++) {
|
||||
s_rxdesc[i][0] = BIT(31); // Own
|
||||
s_rxdesc[i][1] = sizeof(s_rxbuf[i]) | BIT(14); // 2nd address chained
|
||||
s_rxdesc[i][2] = (uint32_t) (uintptr_t) s_rxbuf[i]; // Point to data buffer
|
||||
s_rxdesc[i][3] =
|
||||
(uint32_t) (uintptr_t) s_rxdesc[(i + 1) % ETH_DESC_CNT]; // Chain
|
||||
// MG_DEBUG(("%d %p", i, s_rxdesc[i]));
|
||||
}
|
||||
|
||||
// Init TX descriptors
|
||||
for (int i = 0; i < ETH_DESC_CNT; i++) {
|
||||
s_txdesc[i][2] = (uint32_t) (uintptr_t) s_txbuf[i]; // Buf pointer
|
||||
s_txdesc[i][3] =
|
||||
(uint32_t) (uintptr_t) s_txdesc[(i + 1) % ETH_DESC_CNT]; // Chain
|
||||
}
|
||||
|
||||
EMAC->EMACDMABUSMOD |= BIT(0); // Software reset
|
||||
while ((EMAC->EMACDMABUSMOD & BIT(0)) != 0) tm4cspin(1); // Wait until done
|
||||
|
||||
// Set MDC clock divider. If user told us the value, use it. Otherwise, guess
|
||||
int cr = (d == NULL || d->mdc_cr < 0) ? guess_mdc_cr() : d->mdc_cr;
|
||||
EMAC->EMACMIIADDR = ((uint32_t) cr & 0xf) << 2;
|
||||
|
||||
// NOTE(cpq): we do not use extended descriptor bit 7, and do not use
|
||||
// hardware checksum. Therefore, descriptor size is 4, not 8
|
||||
// EMAC->EMACDMABUSMOD = BIT(13) | BIT(16) | BIT(22) | BIT(23) | BIT(25);
|
||||
EMAC->EMACIM = BIT(3) | BIT(9); // Mask timestamp & PMT IT
|
||||
EMAC->EMACFLOWCTL = BIT(7); // Disable zero-quanta pause
|
||||
// EMAC->EMACFRAMEFLTR = BIT(31); // Receive all
|
||||
// EMAC->EMACPC defaults to internal PHY (EPHY) in MMI mode
|
||||
emac_write_phy(EPHY_ADDR, EPHYBMCR, BIT(15)); // Reset internal PHY (EPHY)
|
||||
emac_write_phy(EPHY_ADDR, EPHYBMCR, BIT(12)); // Set autonegotiation
|
||||
EMAC->EMACRXDLADDR = (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors
|
||||
EMAC->EMACTXDLADDR = (uint32_t) (uintptr_t) s_txdesc; // TX descriptors
|
||||
EMAC->EMACDMAIM = BIT(6) | BIT(16); // RIE, NIE
|
||||
EMAC->EMACCFG = BIT(2) | BIT(3) | BIT(11) | BIT(14); // RE, TE, Duplex, Fast
|
||||
EMAC->EMACDMAOPMODE =
|
||||
BIT(1) | BIT(13) | BIT(21) | BIT(25); // SR, ST, TSF, RSF
|
||||
EMAC->EMACADDR0H = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
|
||||
EMAC->EMACADDR0L = (uint32_t) (ifp->mac[3] << 24) |
|
||||
((uint32_t) ifp->mac[2] << 16) |
|
||||
((uint32_t) ifp->mac[1] << 8) | ifp->mac[0];
|
||||
// NOTE(scaprile) There are 3 additional slots for filtering, disabled by
|
||||
// default. This also applies to the STM32 driver (at least for F7)
|
||||
|
||||
if (ifp->queue.len == 0) ifp->queue.len = 8192;
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint32_t s_txno;
|
||||
static size_t mg_tcpip_driver_tm4c_tx(const void *buf, size_t len,
|
||||
struct mg_tcpip_if *ifp) {
|
||||
if (len > sizeof(s_txbuf[s_txno])) {
|
||||
MG_ERROR(("Frame too big, %ld", (long) len));
|
||||
len = 0; // fail
|
||||
} else if ((s_txdesc[s_txno][0] & BIT(31))) {
|
||||
MG_ERROR(("No descriptors available"));
|
||||
// printf("D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long)
|
||||
// EMAC->EMACDMARIS);
|
||||
len = 0; // fail
|
||||
} else {
|
||||
memcpy(s_txbuf[s_txno], buf, len); // Copy data
|
||||
s_txdesc[s_txno][1] = (uint32_t) len; // Set data len
|
||||
s_txdesc[s_txno][0] =
|
||||
BIT(20) | BIT(28) | BIT(29) | BIT(30); // Chain,FS,LS,IC
|
||||
s_txdesc[s_txno][0] |= BIT(31); // Set OWN bit - let DMA take over
|
||||
if (++s_txno >= ETH_DESC_CNT) s_txno = 0;
|
||||
}
|
||||
EMAC->EMACDMARIS = BIT(2) | BIT(5); // Clear any prior TU/UNF
|
||||
EMAC->EMACTXPOLLD = 0; // and resume
|
||||
return len;
|
||||
(void) ifp;
|
||||
}
|
||||
|
||||
static bool mg_tcpip_driver_tm4c_up(struct mg_tcpip_if *ifp) {
|
||||
uint32_t bmsr = emac_read_phy(EPHY_ADDR, EPHYBMSR);
|
||||
bool up = (bmsr & BIT(2)) ? 1 : 0;
|
||||
if ((ifp->state == MIP_STATE_DOWN) && up) { // link state just went up
|
||||
uint32_t sts = emac_read_phy(EPHY_ADDR, EPHYSTS);
|
||||
uint32_t emaccfg = EMAC->EMACCFG | BIT(14) | BIT(11); // 100M, Full-duplex
|
||||
if (sts & BIT(1)) emaccfg &= ~BIT(14); // 10M
|
||||
if ((sts & BIT(2)) == 0) emaccfg &= ~BIT(11); // Half-duplex
|
||||
EMAC->EMACCFG = emaccfg; // IRQ handler does not fiddle with this register
|
||||
MG_DEBUG(("Link is %uM %s-duplex", emaccfg & BIT(14) ? 100 : 10,
|
||||
emaccfg & BIT(11) ? "full" : "half"));
|
||||
}
|
||||
return up;
|
||||
}
|
||||
|
||||
void EMAC0_IRQHandler(void);
|
||||
static uint32_t s_rxno;
|
||||
void EMAC0_IRQHandler(void) {
|
||||
if (EMAC->EMACDMARIS & BIT(6)) { // Frame received, loop
|
||||
EMAC->EMACDMARIS = BIT(16) | BIT(6); // Clear flag
|
||||
for (uint32_t i = 0; i < 10; i++) { // read as they arrive but not forever
|
||||
if (s_rxdesc[s_rxno][0] & BIT(31)) break; // exit when done
|
||||
if (((s_rxdesc[s_rxno][0] & (BIT(8) | BIT(9))) == (BIT(8) | BIT(9))) &&
|
||||
!(s_rxdesc[s_rxno][0] & BIT(15))) { // skip partial/errored frames
|
||||
uint32_t len = ((s_rxdesc[s_rxno][0] >> 16) & (BIT(14) - 1));
|
||||
// printf("%lx %lu %lx %.8lx\n", s_rxno, len, s_rxdesc[s_rxno][0],
|
||||
// EMAC->EMACDMARIS);
|
||||
mg_tcpip_qwrite(s_rxbuf[s_rxno], len > 4 ? len - 4 : len, s_ifp);
|
||||
}
|
||||
s_rxdesc[s_rxno][0] = BIT(31);
|
||||
if (++s_rxno >= ETH_DESC_CNT) s_rxno = 0;
|
||||
}
|
||||
}
|
||||
EMAC->EMACDMARIS = BIT(7); // Clear possible RU while processing
|
||||
EMAC->EMACRXPOLLD = 0; // and resume RX
|
||||
}
|
||||
|
||||
struct mg_tcpip_driver mg_tcpip_driver_tm4c = {
|
||||
mg_tcpip_driver_tm4c_init, mg_tcpip_driver_tm4c_tx, mg_tcpip_driver_rx,
|
||||
mg_tcpip_driver_tm4c_up};
|
||||
#endif
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
struct mg_tcpip_driver_tm4c_data {
|
||||
// MDC clock divider. MDC clock is derived from SYSCLK, must not exceed 2.5MHz
|
||||
// SYSCLK range DIVIDER mdc_cr VALUE
|
||||
// -------------------------------------
|
||||
// -1 <-- tell driver to guess the value
|
||||
// 60-100 MHz SYSCLK/42 0
|
||||
// 100-150 MHz SYSCLK/62 1 <-- value for EK-TM4C129* on max speed
|
||||
// 20-35 MHz SYSCLK/16 2
|
||||
// 35-60 MHz SYSCLK/26 3
|
||||
// 0x4-0xF Reserved
|
||||
int mdc_cr; // Valid values: -1, 0, 1, 2, 3
|
||||
};
|
||||
@@ -0,0 +1,89 @@
|
||||
#include "mip.h"
|
||||
|
||||
#if MG_ENABLE_TCPIP
|
||||
|
||||
enum { W5500_CR = 0, W5500_S0 = 1, W5500_TX0 = 2, W5500_RX0 = 3 };
|
||||
|
||||
static void w5500_txn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, bool wr,
|
||||
void *buf, size_t len) {
|
||||
uint8_t *p = (uint8_t *) buf;
|
||||
uint8_t cmd[] = {(uint8_t) (addr >> 8), (uint8_t) (addr & 255),
|
||||
(uint8_t) ((block << 3) | (wr ? 4 : 0))};
|
||||
s->begin(s->spi);
|
||||
for (size_t i = 0; i < sizeof(cmd); i++) s->txn(s->spi, cmd[i]);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
uint8_t r = s->txn(s->spi, p[i]);
|
||||
if (!wr) p[i] = r;
|
||||
}
|
||||
s->end(s->spi);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
static void w5500_wn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, void *buf, size_t len) { w5500_txn(s, block, addr, true, buf, len); }
|
||||
static void w5500_w1(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, uint8_t val) { w5500_wn(s, block, addr, &val, 1); }
|
||||
static void w5500_w2(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, uint16_t val) { uint8_t buf[2] = {(uint8_t) (val >> 8), (uint8_t) (val & 255)}; w5500_wn(s, block, addr, buf, sizeof(buf)); }
|
||||
static void w5500_rn(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr, void *buf, size_t len) { w5500_txn(s, block, addr, false, buf, len); }
|
||||
static uint8_t w5500_r1(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr) { uint8_t r = 0; w5500_rn(s, block, addr, &r, 1); return r; }
|
||||
static uint16_t w5500_r2(struct mg_tcpip_spi *s, uint8_t block, uint16_t addr) { uint8_t buf[2] = {0, 0}; w5500_rn(s, block, addr, buf, sizeof(buf)); return (uint16_t) ((buf[0] << 8) | buf[1]); }
|
||||
// clang-format on
|
||||
|
||||
static size_t w5500_rx(void *buf, size_t buflen, struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data;
|
||||
uint16_t r = 0, n = 0, len = (uint16_t) buflen, n2; // Read recv len
|
||||
while ((n2 = w5500_r2(s, W5500_S0, 0x26)) > n) n = n2; // Until it is stable
|
||||
// printf("RSR: %d\n", (int) n);
|
||||
if (n > 0) {
|
||||
uint16_t ptr = w5500_r2(s, W5500_S0, 0x28); // Get read pointer
|
||||
n = w5500_r2(s, W5500_RX0, ptr); // Read frame length
|
||||
if (n <= len + 2 && n > 1) {
|
||||
r = (uint16_t) (n - 2);
|
||||
w5500_rn(s, W5500_RX0, (uint16_t) (ptr + 2), buf, r);
|
||||
}
|
||||
w5500_w2(s, W5500_S0, 0x28, (uint16_t) (ptr + n)); // Advance read pointer
|
||||
w5500_w1(s, W5500_S0, 1, 0x40); // Sock0 CR -> RECV
|
||||
// printf(" RX_RD: tot=%u n=%u r=%u\n", n2, n, r);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static size_t w5500_tx(const void *buf, size_t buflen, struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data;
|
||||
uint16_t n = 0, len = (uint16_t) buflen;
|
||||
while (n < len) n = w5500_r2(s, W5500_S0, 0x20); // Wait for space
|
||||
uint16_t ptr = w5500_r2(s, W5500_S0, 0x24); // Get write pointer
|
||||
w5500_wn(s, W5500_TX0, ptr, (void *) buf, len); // Write data
|
||||
w5500_w2(s, W5500_S0, 0x24, (uint16_t) (ptr + len)); // Advance write pointer
|
||||
w5500_w1(s, W5500_S0, 1, 0x20); // Sock0 CR -> SEND
|
||||
for (int i = 0; i < 40; i++) {
|
||||
uint8_t ir = w5500_r1(s, W5500_S0, 2); // Read S0 IR
|
||||
if (ir == 0) continue;
|
||||
// printf("IR %d, len=%d, free=%d, ptr %d\n", ir, (int) len, (int) n, ptr);
|
||||
w5500_w1(s, W5500_S0, 2, ir); // Write S0 IR: clear it!
|
||||
if (ir & 8) len = 0; // Timeout. Report error
|
||||
if (ir & (16 | 8)) break; // Stop on SEND_OK or timeout
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static bool w5500_init(struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_spi *s = (struct mg_tcpip_spi *) ifp->driver_data;
|
||||
s->end(s->spi);
|
||||
w5500_w1(s, W5500_CR, 0, 0x80); // Reset chip: CR -> 0x80
|
||||
w5500_w1(s, W5500_CR, 0x2e, 0); // CR PHYCFGR -> reset
|
||||
w5500_w1(s, W5500_CR, 0x2e, 0xf8); // CR PHYCFGR -> set
|
||||
// w5500_wn(s, W5500_CR, 9, s->mac, 6); // Set source MAC
|
||||
w5500_w1(s, W5500_S0, 0x1e, 16); // Sock0 RX buf size
|
||||
w5500_w1(s, W5500_S0, 0x1f, 16); // Sock0 TX buf size
|
||||
w5500_w1(s, W5500_S0, 0, 4); // Sock0 MR -> MACRAW
|
||||
w5500_w1(s, W5500_S0, 1, 1); // Sock0 CR -> OPEN
|
||||
return w5500_r1(s, W5500_S0, 3) == 0x42; // Sock0 SR == MACRAW
|
||||
}
|
||||
|
||||
static bool w5500_up(struct mg_tcpip_if *ifp) {
|
||||
struct mg_tcpip_spi *spi = (struct mg_tcpip_spi *) ifp->driver_data;
|
||||
uint8_t phycfgr = w5500_r1(spi, W5500_CR, 0x2e);
|
||||
return phycfgr & 1; // Bit 0 of PHYCFGR is LNK (0 - down, 1 - up)
|
||||
}
|
||||
|
||||
struct mg_tcpip_driver mg_tcpip_driver_w5500 = {w5500_init, w5500_tx, w5500_rx, w5500_up};
|
||||
#endif
|
||||
@@ -0,0 +1,974 @@
|
||||
#include "tcpip.h"
|
||||
|
||||
#if MG_ENABLE_TCPIP
|
||||
|
||||
#define MIP_EPHEMERAL_PORT 49152
|
||||
#define PDIFF(a, b) ((size_t) (((char *) (b)) - ((char *) (a))))
|
||||
|
||||
#ifndef MIP_TCP_KEEPALIVE_MS
|
||||
#define MIP_TCP_KEEPALIVE_MS 45000 // TCP keep-alive period, ms
|
||||
#endif
|
||||
|
||||
#define MIP_TCP_ACK_MS 150 // Timeout for ACKing
|
||||
|
||||
struct connstate {
|
||||
uint32_t seq, ack; // TCP seq/ack counters
|
||||
uint64_t timer; // TCP keep-alive / ACK timer
|
||||
uint8_t mac[6]; // Peer MAC address
|
||||
uint8_t ttype; // Timer type. 0: ack, 1: keep-alive
|
||||
#define MIP_TTYPE_KEEPALIVE 0 // Connection is idle for long, send keepalive
|
||||
#define MIP_TTYPE_ACK 1 // Peer sent us data, we have to ack it soon
|
||||
uint8_t tmiss; // Number of keep-alive misses
|
||||
struct mg_iobuf raw; // For TLS only. Incoming raw data
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct lcp {
|
||||
uint8_t addr, ctrl, proto[2], code, id, len[2];
|
||||
};
|
||||
|
||||
struct eth {
|
||||
uint8_t dst[6]; // Destination MAC address
|
||||
uint8_t src[6]; // Source MAC address
|
||||
uint16_t type; // Ethernet type
|
||||
};
|
||||
|
||||
struct ip {
|
||||
uint8_t ver; // Version
|
||||
uint8_t tos; // Unused
|
||||
uint16_t len; // Length
|
||||
uint16_t id; // Unused
|
||||
uint16_t frag; // Fragmentation
|
||||
uint8_t ttl; // Time to live
|
||||
uint8_t proto; // Upper level protocol
|
||||
uint16_t csum; // Checksum
|
||||
uint32_t src; // Source IP
|
||||
uint32_t dst; // Destination IP
|
||||
};
|
||||
|
||||
struct ip6 {
|
||||
uint8_t ver; // Version
|
||||
uint8_t opts[3]; // Options
|
||||
uint16_t len; // Length
|
||||
uint8_t proto; // Upper level protocol
|
||||
uint8_t ttl; // Time to live
|
||||
uint8_t src[16]; // Source IP
|
||||
uint8_t dst[16]; // Destination IP
|
||||
};
|
||||
|
||||
struct icmp {
|
||||
uint8_t type;
|
||||
uint8_t code;
|
||||
uint16_t csum;
|
||||
};
|
||||
|
||||
struct arp {
|
||||
uint16_t fmt; // Format of hardware address
|
||||
uint16_t pro; // Format of protocol address
|
||||
uint8_t hlen; // Length of hardware address
|
||||
uint8_t plen; // Length of protocol address
|
||||
uint16_t op; // Operation
|
||||
uint8_t sha[6]; // Sender hardware address
|
||||
uint32_t spa; // Sender protocol address
|
||||
uint8_t tha[6]; // Target hardware address
|
||||
uint32_t tpa; // Target protocol address
|
||||
};
|
||||
|
||||
struct tcp {
|
||||
uint16_t sport; // Source port
|
||||
uint16_t dport; // Destination port
|
||||
uint32_t seq; // Sequence number
|
||||
uint32_t ack; // Acknowledgement number
|
||||
uint8_t off; // Data offset
|
||||
uint8_t flags; // TCP flags
|
||||
#define TH_FIN 0x01
|
||||
#define TH_SYN 0x02
|
||||
#define TH_RST 0x04
|
||||
#define TH_PUSH 0x08
|
||||
#define TH_ACK 0x10
|
||||
#define TH_URG 0x20
|
||||
#define TH_ECE 0x40
|
||||
#define TH_CWR 0x80
|
||||
uint16_t win; // Window
|
||||
uint16_t csum; // Checksum
|
||||
uint16_t urp; // Urgent pointer
|
||||
};
|
||||
|
||||
struct udp {
|
||||
uint16_t sport; // Source port
|
||||
uint16_t dport; // Destination port
|
||||
uint16_t len; // UDP length
|
||||
uint16_t csum; // UDP checksum
|
||||
};
|
||||
|
||||
struct dhcp {
|
||||
uint8_t op, htype, hlen, hops;
|
||||
uint32_t xid;
|
||||
uint16_t secs, flags;
|
||||
uint32_t ciaddr, yiaddr, siaddr, giaddr;
|
||||
uint8_t hwaddr[208];
|
||||
uint32_t magic;
|
||||
uint8_t options[32];
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
struct pkt {
|
||||
struct mg_str raw; // Raw packet data
|
||||
struct mg_str pay; // Payload data
|
||||
struct eth *eth;
|
||||
struct llc *llc;
|
||||
struct arp *arp;
|
||||
struct ip *ip;
|
||||
struct ip6 *ip6;
|
||||
struct icmp *icmp;
|
||||
struct tcp *tcp;
|
||||
struct udp *udp;
|
||||
struct dhcp *dhcp;
|
||||
};
|
||||
|
||||
static void q_copyin(struct queue *q, const uint8_t *buf, size_t len,
|
||||
size_t head) {
|
||||
size_t left = q->len - head;
|
||||
memcpy(&q->buf[head], buf, left < len ? left : len);
|
||||
if (left < len) memcpy(q->buf, &buf[left], len - left);
|
||||
}
|
||||
|
||||
static void q_copyout(struct queue *q, uint8_t *buf, size_t len, size_t tail) {
|
||||
size_t left = q->len - tail;
|
||||
memcpy(buf, &q->buf[tail], left < len ? left : len);
|
||||
if (left < len) memcpy(&buf[left], q->buf, len - left);
|
||||
}
|
||||
|
||||
static bool q_write(struct queue *q, const void *buf, size_t len) {
|
||||
bool success = false;
|
||||
size_t left = (q->len - q->head + q->tail - 1) % q->len;
|
||||
if (len + sizeof(size_t) <= left) {
|
||||
q_copyin(q, (uint8_t *) &len, sizeof(len), q->head);
|
||||
q_copyin(q, (uint8_t *) buf, len, (q->head + sizeof(size_t)) % q->len);
|
||||
q->head = (q->head + sizeof(len) + len) % q->len;
|
||||
success = true;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
static inline size_t q_avail(struct queue *q) {
|
||||
size_t n = 0;
|
||||
if (q->tail != q->head) q_copyout(q, (uint8_t *) &n, sizeof(n), q->tail);
|
||||
return n;
|
||||
}
|
||||
|
||||
static size_t q_read(struct queue *q, void *buf) {
|
||||
size_t n = q_avail(q);
|
||||
if (n > 0) {
|
||||
q_copyout(q, (uint8_t *) buf, n, (q->tail + sizeof(n)) % q->len);
|
||||
q->tail = (q->tail + sizeof(n) + n) % q->len;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static struct mg_str mkstr(void *buf, size_t len) {
|
||||
struct mg_str str = {(char *) buf, len};
|
||||
return str;
|
||||
}
|
||||
|
||||
static void mkpay(struct pkt *pkt, void *p) {
|
||||
pkt->pay = mkstr(p, (size_t) (&pkt->raw.ptr[pkt->raw.len] - (char *) p));
|
||||
}
|
||||
|
||||
static uint32_t csumup(uint32_t sum, const void *buf, size_t len) {
|
||||
const uint8_t *p = (const uint8_t *) buf;
|
||||
for (size_t i = 0; i < len; i++) sum += i & 1 ? p[i] : (uint32_t) (p[i] << 8);
|
||||
return sum;
|
||||
}
|
||||
|
||||
static uint16_t csumfin(uint32_t sum) {
|
||||
while (sum >> 16) sum = (sum & 0xffff) + (sum >> 16);
|
||||
return mg_htons(~sum & 0xffff);
|
||||
}
|
||||
|
||||
static uint16_t ipcsum(const void *buf, size_t len) {
|
||||
uint32_t sum = csumup(0, buf, len);
|
||||
return csumfin(sum);
|
||||
}
|
||||
|
||||
static size_t ether_output(struct mg_tcpip_if *ifp, size_t len) {
|
||||
// size_t min = 64; // Pad short frames to 64 bytes (minimum Ethernet size)
|
||||
// if (len < min) memset(ifp->tx.ptr + len, 0, min - len), len = min;
|
||||
// mg_hexdump(ifp->tx.ptr, len);
|
||||
size_t n = ifp->driver->tx(ifp->tx.ptr, len, ifp);
|
||||
if (n == len) ifp->nsent++;
|
||||
return n;
|
||||
}
|
||||
|
||||
static void arp_ask(struct mg_tcpip_if *ifp, uint32_t ip) {
|
||||
struct eth *eth = (struct eth *) ifp->tx.ptr;
|
||||
struct arp *arp = (struct arp *) (eth + 1);
|
||||
memset(eth->dst, 255, sizeof(eth->dst));
|
||||
memcpy(eth->src, ifp->mac, sizeof(eth->src));
|
||||
eth->type = mg_htons(0x806);
|
||||
memset(arp, 0, sizeof(*arp));
|
||||
arp->fmt = mg_htons(1), arp->pro = mg_htons(0x800), arp->hlen = 6,
|
||||
arp->plen = 4;
|
||||
arp->op = mg_htons(1), arp->tpa = ip, arp->spa = ifp->ip;
|
||||
memcpy(arp->sha, ifp->mac, sizeof(arp->sha));
|
||||
ether_output(ifp, PDIFF(eth, arp + 1));
|
||||
}
|
||||
|
||||
static void onstatechange(struct mg_tcpip_if *ifp) {
|
||||
if (ifp->state == MIP_STATE_READY) {
|
||||
MG_INFO(("READY, IP: %M", mg_print_ip4, &ifp->ip));
|
||||
MG_INFO((" GW: %M", mg_print_ip4, &ifp->gw));
|
||||
if (ifp->lease_expire > ifp->now) {
|
||||
MG_INFO(
|
||||
(" Lease: %lld sec", (ifp->lease_expire - ifp->now) / 1000));
|
||||
}
|
||||
arp_ask(ifp, ifp->gw);
|
||||
} else if (ifp->state == MIP_STATE_UP) {
|
||||
MG_ERROR(("Link up"));
|
||||
} else if (ifp->state == MIP_STATE_DOWN) {
|
||||
MG_ERROR(("Link down"));
|
||||
}
|
||||
}
|
||||
|
||||
static struct ip *tx_ip(struct mg_tcpip_if *ifp, uint8_t *mac_dst,
|
||||
uint8_t proto, uint32_t ip_src, uint32_t ip_dst,
|
||||
size_t plen) {
|
||||
struct eth *eth = (struct eth *) ifp->tx.ptr;
|
||||
struct ip *ip = (struct ip *) (eth + 1);
|
||||
memcpy(eth->dst, mac_dst, sizeof(eth->dst));
|
||||
memcpy(eth->src, ifp->mac, sizeof(eth->src)); // Use our MAC
|
||||
eth->type = mg_htons(0x800);
|
||||
memset(ip, 0, sizeof(*ip));
|
||||
ip->ver = 0x45; // Version 4, header length 5 words
|
||||
ip->frag = 0x40; // Don't fragment
|
||||
ip->len = mg_htons((uint16_t) (sizeof(*ip) + plen));
|
||||
ip->ttl = 64;
|
||||
ip->proto = proto;
|
||||
ip->src = ip_src;
|
||||
ip->dst = ip_dst;
|
||||
ip->csum = ipcsum(ip, sizeof(*ip));
|
||||
return ip;
|
||||
}
|
||||
|
||||
static void tx_udp(struct mg_tcpip_if *ifp, uint8_t *mac_dst, uint32_t ip_src,
|
||||
uint16_t sport, uint32_t ip_dst, uint16_t dport,
|
||||
const void *buf, size_t len) {
|
||||
struct ip *ip =
|
||||
tx_ip(ifp, mac_dst, 17, ip_src, ip_dst, len + sizeof(struct udp));
|
||||
struct udp *udp = (struct udp *) (ip + 1);
|
||||
// MG_DEBUG(("UDP XX LEN %d %d", (int) len, (int) ifp->tx.len));
|
||||
udp->sport = sport;
|
||||
udp->dport = dport;
|
||||
udp->len = mg_htons((uint16_t) (sizeof(*udp) + len));
|
||||
udp->csum = 0;
|
||||
uint32_t cs = csumup(0, udp, sizeof(*udp));
|
||||
cs = csumup(cs, buf, len);
|
||||
cs = csumup(cs, &ip->src, sizeof(ip->src));
|
||||
cs = csumup(cs, &ip->dst, sizeof(ip->dst));
|
||||
cs += (uint32_t) (ip->proto + sizeof(*udp) + len);
|
||||
udp->csum = csumfin(cs);
|
||||
memmove(udp + 1, buf, len);
|
||||
// MG_DEBUG(("UDP LEN %d %d", (int) len, (int) ifp->frame_len));
|
||||
ether_output(ifp, sizeof(struct eth) + sizeof(*ip) + sizeof(*udp) + len);
|
||||
}
|
||||
|
||||
static void tx_dhcp(struct mg_tcpip_if *ifp, uint8_t *mac_dst, uint32_t ip_src,
|
||||
uint32_t ip_dst, uint8_t *opts, size_t optslen) {
|
||||
struct dhcp dhcp = {1, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}};
|
||||
dhcp.magic = mg_htonl(0x63825363);
|
||||
memcpy(&dhcp.hwaddr, ifp->mac, sizeof(ifp->mac));
|
||||
memcpy(&dhcp.xid, ifp->mac + 2, sizeof(dhcp.xid));
|
||||
memcpy(&dhcp.options, opts, optslen);
|
||||
tx_udp(ifp, mac_dst, ip_src, mg_htons(68), ip_dst, mg_htons(67), &dhcp,
|
||||
sizeof(dhcp));
|
||||
}
|
||||
|
||||
static void tx_dhcp_request(struct mg_tcpip_if *ifp, uint8_t *mac_dst,
|
||||
uint32_t ip_src, uint32_t ip_dst) {
|
||||
uint8_t opts[] = {
|
||||
53, 1, 3, // Type: DHCP request
|
||||
55, 2, 1, 3, // GW and mask
|
||||
12, 3, 'm', 'i', 'p', // Host name: "mip"
|
||||
54, 4, 0, 0, 0, 0, // DHCP server ID
|
||||
50, 4, 0, 0, 0, 0, // Requested IP
|
||||
255 // End of options
|
||||
};
|
||||
memcpy(opts + 14, &ip_dst, sizeof(ip_dst));
|
||||
memcpy(opts + 20, &ip_src, sizeof(ip_src));
|
||||
tx_dhcp(ifp, mac_dst, ip_src, ip_dst, opts, sizeof(opts));
|
||||
}
|
||||
|
||||
static void tx_dhcp_discover(struct mg_tcpip_if *ifp) {
|
||||
uint8_t mac[6] = {255, 255, 255, 255, 255, 255};
|
||||
uint8_t opts[] = {
|
||||
53, 1, 1, // Type: DHCP discover
|
||||
55, 2, 1, 3, // Parameters: ip, mask
|
||||
255 // End of options
|
||||
};
|
||||
tx_dhcp(ifp, mac, 0, 0xffffffff, opts, sizeof(opts));
|
||||
MG_DEBUG(("DHCP discover sent"));
|
||||
}
|
||||
|
||||
static struct mg_connection *getpeer(struct mg_mgr *mgr, struct pkt *pkt,
|
||||
bool lsn) {
|
||||
struct mg_connection *c = NULL;
|
||||
for (c = mgr->conns; c != NULL; c = c->next) {
|
||||
if (c->is_udp && pkt->udp && c->loc.port == pkt->udp->dport) break;
|
||||
if (!c->is_udp && pkt->tcp && c->loc.port == pkt->tcp->dport &&
|
||||
lsn == c->is_listening && (lsn || c->rem.port == pkt->tcp->sport))
|
||||
break;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
static void rx_arp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
if (pkt->arp->op == mg_htons(1) && pkt->arp->tpa == ifp->ip) {
|
||||
// ARP request. Make a response, then send
|
||||
// MG_DEBUG(("ARP op %d %M: %M", mg_ntohs(pkt->arp->op), mg_print_ip4,
|
||||
// &pkt->arp->spa, mg_print_ip4, &pkt->arp->tpa));
|
||||
struct eth *eth = (struct eth *) ifp->tx.ptr;
|
||||
struct arp *arp = (struct arp *) (eth + 1);
|
||||
memcpy(eth->dst, pkt->eth->src, sizeof(eth->dst));
|
||||
memcpy(eth->src, ifp->mac, sizeof(eth->src));
|
||||
eth->type = mg_htons(0x806);
|
||||
*arp = *pkt->arp;
|
||||
arp->op = mg_htons(2);
|
||||
memcpy(arp->tha, pkt->arp->sha, sizeof(pkt->arp->tha));
|
||||
memcpy(arp->sha, ifp->mac, sizeof(pkt->arp->sha));
|
||||
arp->tpa = pkt->arp->spa;
|
||||
arp->spa = ifp->ip;
|
||||
MG_DEBUG(("ARP: tell %M we're %M", mg_print_ip4, &arp->tpa, mg_print_ip4,
|
||||
&ifp->ip));
|
||||
ether_output(ifp, PDIFF(eth, arp + 1));
|
||||
} else if (pkt->arp->op == mg_htons(2)) {
|
||||
if (memcmp(pkt->arp->tha, ifp->mac, sizeof(pkt->arp->tha)) != 0) return;
|
||||
if (pkt->arp->spa == ifp->gw) {
|
||||
// Got response for the GW ARP request. Set ifp->gwmac
|
||||
memcpy(ifp->gwmac, pkt->arp->sha, sizeof(ifp->gwmac));
|
||||
} else {
|
||||
struct mg_connection *c = getpeer(ifp->mgr, pkt, false);
|
||||
if (c != NULL && c->is_arplooking) {
|
||||
struct connstate *s = (struct connstate *) (c + 1);
|
||||
memcpy(s->mac, pkt->arp->sha, sizeof(s->mac));
|
||||
MG_DEBUG(("%lu ARP resolved %M -> %M", c->id, mg_print_ip4, &c->rem.ip,
|
||||
mg_print_mac, s->mac));
|
||||
c->is_arplooking = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rx_icmp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
// MG_DEBUG(("ICMP %d", (int) len));
|
||||
if (pkt->icmp->type == 8 && pkt->ip != NULL && pkt->ip->dst == ifp->ip) {
|
||||
size_t hlen = sizeof(struct eth) + sizeof(struct ip) + sizeof(struct icmp);
|
||||
size_t space = ifp->tx.len - hlen, plen = pkt->pay.len;
|
||||
if (plen > space) plen = space;
|
||||
struct ip *ip = tx_ip(ifp, pkt->eth->src, 1, ifp->ip, pkt->ip->src,
|
||||
sizeof(struct icmp) + plen);
|
||||
struct icmp *icmp = (struct icmp *) (ip + 1);
|
||||
memset(icmp, 0, sizeof(*icmp)); // Set csum to 0
|
||||
memcpy(icmp + 1, pkt->pay.ptr, plen); // Copy RX payload to TX
|
||||
icmp->csum = ipcsum(icmp, sizeof(*icmp) + plen);
|
||||
ether_output(ifp, hlen + plen);
|
||||
}
|
||||
}
|
||||
|
||||
static void rx_dhcp_client(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
uint32_t ip = 0, gw = 0, mask = 0;
|
||||
uint8_t *p = pkt->dhcp->options,
|
||||
*end = (uint8_t *) &pkt->raw.ptr[pkt->raw.len];
|
||||
if (end < (uint8_t *) (pkt->dhcp + 1)) return;
|
||||
while (p + 1 < end && p[0] != 255) { // Parse options
|
||||
if (p[0] == 1 && p[1] == sizeof(ifp->mask) && p + 6 < end) { // Mask
|
||||
memcpy(&mask, p + 2, sizeof(mask));
|
||||
} else if (p[0] == 3 && p[1] == sizeof(ifp->gw) && p + 6 < end) { // GW
|
||||
memcpy(&gw, p + 2, sizeof(gw));
|
||||
ip = pkt->dhcp->yiaddr;
|
||||
} else if (p[0] == 51 && p[1] == 4 && p + 6 < end) { // Lease
|
||||
uint32_t lease = 0;
|
||||
memcpy(&lease, p + 2, sizeof(lease));
|
||||
ifp->lease_expire = ifp->now + mg_ntohl(lease) * 1000;
|
||||
}
|
||||
p += p[1] + 2;
|
||||
}
|
||||
if (ip && mask && gw && ifp->ip == 0) {
|
||||
memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac));
|
||||
ifp->ip = ip, ifp->gw = gw, ifp->mask = mask;
|
||||
ifp->state = MIP_STATE_READY;
|
||||
onstatechange(ifp);
|
||||
tx_dhcp_request(ifp, pkt->eth->src, ip, pkt->dhcp->siaddr);
|
||||
}
|
||||
}
|
||||
|
||||
// Simple DHCP server that assigns a next IP address: ifp->ip + 1
|
||||
static void rx_dhcp_server(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
uint8_t op = 0, *p = pkt->dhcp->options,
|
||||
*end = (uint8_t *) &pkt->raw.ptr[pkt->raw.len];
|
||||
if (end < (uint8_t *) (pkt->dhcp + 1)) return;
|
||||
// struct dhcp *req = pkt->dhcp;
|
||||
struct dhcp res = {2, 1, 6, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, {0}};
|
||||
res.yiaddr = ifp->ip;
|
||||
((uint8_t *) (&res.yiaddr))[3]++; // Offer our IP + 1
|
||||
while (p + 1 < end && p[0] != 255) { // Parse options
|
||||
if (p[0] == 53 && p[1] == 1 && p + 2 < end) { // Message type
|
||||
op = p[2];
|
||||
}
|
||||
p += p[1] + 2;
|
||||
}
|
||||
if (op == 1 || op == 3) { // DHCP Discover or DHCP Request
|
||||
uint8_t msg = op == 1 ? 2 : 5; // Message type: DHCP OFFER or DHCP ACK
|
||||
uint8_t opts[] = {
|
||||
53, 1, msg, // Message type
|
||||
1, 4, 0, 0, 0, 0, // Subnet mask
|
||||
54, 4, 0, 0, 0, 0, // Server ID
|
||||
12, 3, 'm', 'i', 'p', // Host name: "mip"
|
||||
51, 4, 255, 255, 255, 255, // Lease time
|
||||
255 // End of options
|
||||
};
|
||||
memcpy(&res.hwaddr, pkt->dhcp->hwaddr, 6);
|
||||
memcpy(opts + 5, &ifp->mask, sizeof(ifp->mask));
|
||||
memcpy(opts + 11, &ifp->ip, sizeof(ifp->ip));
|
||||
memcpy(&res.options, opts, sizeof(opts));
|
||||
res.magic = pkt->dhcp->magic;
|
||||
res.xid = pkt->dhcp->xid;
|
||||
// memcpy(ifp->gwmac, pkt->eth->src, sizeof(ifp->gwmac));
|
||||
tx_udp(ifp, pkt->eth->src, ifp->ip, mg_htons(67),
|
||||
op == 1 ? ~0U : res.yiaddr, mg_htons(68), &res, sizeof(res));
|
||||
}
|
||||
}
|
||||
|
||||
static void rx_udp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
struct mg_connection *c = getpeer(ifp->mgr, pkt, true);
|
||||
if (c == NULL) {
|
||||
// No UDP listener on this port. Should send ICMP, but keep silent.
|
||||
} else if (c != NULL) {
|
||||
c->rem.port = pkt->udp->sport;
|
||||
c->rem.ip = pkt->ip->src;
|
||||
if (c->recv.len >= MG_MAX_RECV_SIZE) {
|
||||
mg_error(c, "max_recv_buf_size reached");
|
||||
} else if (c->recv.size - c->recv.len < pkt->pay.len &&
|
||||
!mg_iobuf_resize(&c->recv, c->recv.len + pkt->pay.len)) {
|
||||
mg_error(c, "oom");
|
||||
} else {
|
||||
memcpy(&c->recv.buf[c->recv.len], pkt->pay.ptr, pkt->pay.len);
|
||||
c->recv.len += pkt->pay.len;
|
||||
mg_call(c, MG_EV_READ, &pkt->pay.len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static size_t tx_tcp(struct mg_tcpip_if *ifp, uint8_t *dst_mac, uint32_t dst_ip,
|
||||
uint8_t flags, uint16_t sport, uint16_t dport,
|
||||
uint32_t seq, uint32_t ack, const void *buf, size_t len) {
|
||||
struct ip *ip =
|
||||
tx_ip(ifp, dst_mac, 6, ifp->ip, dst_ip, sizeof(struct tcp) + len);
|
||||
struct tcp *tcp = (struct tcp *) (ip + 1);
|
||||
memset(tcp, 0, sizeof(*tcp));
|
||||
if (buf != NULL && len) memmove(tcp + 1, buf, len);
|
||||
tcp->sport = sport;
|
||||
tcp->dport = dport;
|
||||
tcp->seq = seq;
|
||||
tcp->ack = ack;
|
||||
tcp->flags = flags;
|
||||
tcp->win = mg_htons(8192);
|
||||
tcp->off = (uint8_t) (sizeof(*tcp) / 4 << 4);
|
||||
uint32_t cs = 0;
|
||||
uint16_t n = (uint16_t) (sizeof(*tcp) + len);
|
||||
uint8_t pseudo[] = {0, ip->proto, (uint8_t) (n >> 8), (uint8_t) (n & 255)};
|
||||
cs = csumup(cs, tcp, n);
|
||||
cs = csumup(cs, &ip->src, sizeof(ip->src));
|
||||
cs = csumup(cs, &ip->dst, sizeof(ip->dst));
|
||||
cs = csumup(cs, pseudo, sizeof(pseudo));
|
||||
tcp->csum = csumfin(cs);
|
||||
return ether_output(ifp, PDIFF(ifp->tx.ptr, tcp + 1) + len);
|
||||
}
|
||||
|
||||
static size_t tx_tcp_pkt(struct mg_tcpip_if *ifp, struct pkt *pkt,
|
||||
uint8_t flags, uint32_t seq, const void *buf,
|
||||
size_t len) {
|
||||
uint32_t delta = (pkt->tcp->flags & (TH_SYN | TH_FIN)) ? 1 : 0;
|
||||
return tx_tcp(ifp, pkt->eth->src, pkt->ip->src, flags, pkt->tcp->dport,
|
||||
pkt->tcp->sport, seq, mg_htonl(mg_ntohl(pkt->tcp->seq) + delta),
|
||||
buf, len);
|
||||
}
|
||||
|
||||
static void settmout(struct mg_connection *c, uint8_t type) {
|
||||
struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
|
||||
struct connstate *s = (struct connstate *) (c + 1);
|
||||
unsigned n = type == MIP_TTYPE_ACK ? MIP_TCP_ACK_MS : MIP_TCP_KEEPALIVE_MS;
|
||||
s->timer = ifp->now + n;
|
||||
s->ttype = type;
|
||||
MG_VERBOSE(("%lu %d -> %llx", c->id, type, s->timer));
|
||||
}
|
||||
|
||||
static struct mg_connection *accept_conn(struct mg_connection *lsn,
|
||||
struct pkt *pkt) {
|
||||
struct mg_connection *c = mg_alloc_conn(lsn->mgr);
|
||||
struct connstate *s = (struct connstate *) (c + 1);
|
||||
s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq);
|
||||
memcpy(s->mac, pkt->eth->src, sizeof(s->mac));
|
||||
settmout(c, MIP_TTYPE_KEEPALIVE);
|
||||
c->rem.ip = pkt->ip->src;
|
||||
c->rem.port = pkt->tcp->sport;
|
||||
MG_DEBUG(("%lu accepted %M", c->id, mg_print_ip_port, &c->rem));
|
||||
LIST_ADD_HEAD(struct mg_connection, &lsn->mgr->conns, c);
|
||||
c->is_accepted = 1;
|
||||
c->is_hexdumping = lsn->is_hexdumping;
|
||||
c->pfn = lsn->pfn;
|
||||
c->loc = lsn->loc;
|
||||
c->pfn_data = lsn->pfn_data;
|
||||
c->fn = lsn->fn;
|
||||
c->fn_data = lsn->fn_data;
|
||||
mg_call(c, MG_EV_OPEN, NULL);
|
||||
mg_call(c, MG_EV_ACCEPT, NULL);
|
||||
return c;
|
||||
}
|
||||
|
||||
long mg_io_send(struct mg_connection *c, const void *buf, size_t len) {
|
||||
struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
|
||||
struct connstate *s = (struct connstate *) (c + 1);
|
||||
size_t max_headers_len = 14 + 24 /* max IP */ + 60 /* max TCP */;
|
||||
if (len + max_headers_len > ifp->tx.len) len = ifp->tx.len - max_headers_len;
|
||||
if (tx_tcp(ifp, s->mac, c->rem.ip, TH_PUSH | TH_ACK, c->loc.port, c->rem.port,
|
||||
mg_htonl(s->seq), mg_htonl(s->ack), buf, len) > 0) {
|
||||
s->seq += (uint32_t) len;
|
||||
if (s->ttype == MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_KEEPALIVE);
|
||||
} else {
|
||||
return MG_IO_ERR;
|
||||
}
|
||||
return (long) len;
|
||||
}
|
||||
|
||||
long mg_io_recv(struct mg_connection *c, void *buf, size_t len) {
|
||||
struct connstate *s = (struct connstate *) (c + 1);
|
||||
if (s->raw.len == 0) return MG_IO_WAIT;
|
||||
if (len > s->raw.len) len = s->raw.len;
|
||||
memcpy(buf, s->raw.buf, len);
|
||||
mg_iobuf_del(&s->raw, 0, len);
|
||||
MG_DEBUG(("%lu", len));
|
||||
return (long) len;
|
||||
}
|
||||
|
||||
static void read_conn(struct mg_connection *c, struct pkt *pkt) {
|
||||
struct connstate *s = (struct connstate *) (c + 1);
|
||||
struct mg_iobuf *io = c->is_tls ? &s->raw : &c->recv;
|
||||
uint32_t seq = mg_ntohl(pkt->tcp->seq);
|
||||
s->raw.align = c->recv.align;
|
||||
if (pkt->tcp->flags & TH_FIN) {
|
||||
s->ack = mg_htonl(pkt->tcp->seq) + 1, s->seq = mg_htonl(pkt->tcp->ack);
|
||||
c->is_closing = 1;
|
||||
} else if (pkt->pay.len == 0) {
|
||||
// TODO(cpq): handle this peer's ACK
|
||||
} else if (seq != s->ack) {
|
||||
uint32_t ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len);
|
||||
if (s->ack == ack) {
|
||||
MG_VERBOSE(("ignoring duplicate pkt"));
|
||||
} else {
|
||||
// TODO(cpq): peer sent us SEQ which we don't expect. Retransmit rather
|
||||
// than close this connection
|
||||
mg_error(c, "SEQ != ACK: %x %x %x", seq, s->ack, ack);
|
||||
}
|
||||
} else if (io->size - io->len < pkt->pay.len &&
|
||||
!mg_iobuf_resize(io, io->len + pkt->pay.len)) {
|
||||
mg_error(c, "oom");
|
||||
} else {
|
||||
// Copy TCP payload into the IO buffer. If the connection is plain text, we
|
||||
// copy to c->recv. If the connection is TLS, this data is encrypted,
|
||||
// therefore we copy that encrypted data to the s->raw iobuffer instead,
|
||||
// and then call mg_tls_recv() to decrypt it. NOTE: mg_tls_recv() will
|
||||
// call back mg_io_recv() which grabs raw data from s->raw
|
||||
memcpy(&io->buf[io->len], pkt->pay.ptr, pkt->pay.len);
|
||||
io->len += pkt->pay.len;
|
||||
|
||||
MG_DEBUG(("%lu SEQ %x -> %x", c->id, mg_htonl(pkt->tcp->seq), s->ack));
|
||||
// Advance ACK counter
|
||||
s->ack = (uint32_t) (mg_htonl(pkt->tcp->seq) + pkt->pay.len);
|
||||
#if 0
|
||||
// Send ACK immediately
|
||||
MG_DEBUG((" imm ACK", c->id, mg_htonl(pkt->tcp->seq), s->ack));
|
||||
tx_tcp((struct mg_tcpip_if *) c->mgr->priv, c->rem.ip, TH_ACK, c->loc.port,
|
||||
c->rem.port, mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
|
||||
#else
|
||||
// if not already running, setup a timer to send an ACK later
|
||||
if (s->ttype != MIP_TTYPE_ACK) settmout(c, MIP_TTYPE_ACK);
|
||||
#endif
|
||||
|
||||
if (c->is_tls) {
|
||||
// TLS connection. Make room for decrypted data in c->recv
|
||||
io = &c->recv;
|
||||
if (io->size - io->len < pkt->pay.len &&
|
||||
!mg_iobuf_resize(io, io->len + pkt->pay.len)) {
|
||||
mg_error(c, "oom");
|
||||
} else {
|
||||
// Decrypt data directly into c->recv
|
||||
long n = mg_tls_recv(c, &io->buf[io->len], io->size - io->len);
|
||||
if (n == MG_IO_ERR) {
|
||||
mg_error(c, "TLS recv error");
|
||||
} else if (n > 0) {
|
||||
// Decrypted successfully - trigger MG_EV_READ
|
||||
io->len += (size_t) n;
|
||||
mg_call(c, MG_EV_READ, &n);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Plain text connection, data is already in c->recv, trigger MG_EV_READ
|
||||
mg_call(c, MG_EV_READ, &pkt->pay.len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rx_tcp(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
struct mg_connection *c = getpeer(ifp->mgr, pkt, false);
|
||||
struct connstate *s = c == NULL ? NULL : (struct connstate *) (c + 1);
|
||||
#if 0
|
||||
MG_INFO(("%lu %hhu %d", c ? c->id : 0, pkt->tcp->flags, (int) pkt->pay.len));
|
||||
#endif
|
||||
if (c != NULL && c->is_connecting && pkt->tcp->flags & (TH_SYN | TH_ACK)) {
|
||||
s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq) + 1;
|
||||
tx_tcp_pkt(ifp, pkt, TH_ACK, pkt->tcp->ack, NULL, 0);
|
||||
c->is_connecting = 0; // Client connected
|
||||
settmout(c, MIP_TTYPE_KEEPALIVE);
|
||||
mg_call(c, MG_EV_CONNECT, NULL); // Let user know
|
||||
} else if (c != NULL && c->is_connecting) {
|
||||
tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
|
||||
} else if (c != NULL && pkt->tcp->flags & TH_RST) {
|
||||
mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
|
||||
} else if (c != NULL) {
|
||||
#if 0
|
||||
MG_DEBUG(("%lu %d %M:%hu -> %M:%hu", c->id, (int) pkt->raw.len,
|
||||
mg_print_ip4, &pkt->ip->src, mg_ntohs(pkt->tcp->sport),
|
||||
mg_print_ip4, &pkt->ip->dst, mg_ntohs(pkt->tcp->dport)));
|
||||
mg_hexdump(pkt->pay.buf, pkt->pay.len);
|
||||
#endif
|
||||
s->tmiss = 0; // Reset missed keep-alive counter
|
||||
if (s->ttype == MIP_TTYPE_KEEPALIVE) // Advance keep-alive timer
|
||||
settmout(c,
|
||||
MIP_TTYPE_KEEPALIVE); // unless a former ACK timeout is pending
|
||||
read_conn(c, pkt); // Override timer with ACK timeout if needed
|
||||
} else if ((c = getpeer(ifp->mgr, pkt, true)) == NULL) {
|
||||
tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
|
||||
} else if (pkt->tcp->flags & TH_RST) {
|
||||
if (c->is_accepted) mg_error(c, "peer RST"); // RFC-1122 4.2.2.13
|
||||
// ignore RST if not connected
|
||||
} else if (pkt->tcp->flags & TH_SYN) {
|
||||
// Use peer's source port as ISN, in order to recognise the handshake
|
||||
uint32_t isn = mg_htonl((uint32_t) mg_ntohs(pkt->tcp->sport));
|
||||
tx_tcp_pkt(ifp, pkt, TH_SYN | TH_ACK, isn, NULL, 0);
|
||||
} else if (pkt->tcp->flags & TH_FIN) {
|
||||
tx_tcp_pkt(ifp, pkt, TH_FIN | TH_ACK, pkt->tcp->ack, NULL, 0);
|
||||
} else if (mg_htonl(pkt->tcp->ack) == mg_htons(pkt->tcp->sport) + 1U) {
|
||||
accept_conn(c, pkt);
|
||||
} else if (!c->is_accepted) { // no peer
|
||||
tx_tcp_pkt(ifp, pkt, TH_RST | TH_ACK, pkt->tcp->ack, NULL, 0);
|
||||
} else {
|
||||
// MG_DEBUG(("dropped silently.."));
|
||||
}
|
||||
}
|
||||
|
||||
static void rx_ip(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
if (pkt->ip->proto == 1) {
|
||||
pkt->icmp = (struct icmp *) (pkt->ip + 1);
|
||||
if (pkt->pay.len < sizeof(*pkt->icmp)) return;
|
||||
mkpay(pkt, pkt->icmp + 1);
|
||||
rx_icmp(ifp, pkt);
|
||||
} else if (pkt->ip->proto == 17) {
|
||||
pkt->udp = (struct udp *) (pkt->ip + 1);
|
||||
if (pkt->pay.len < sizeof(*pkt->udp)) return;
|
||||
mkpay(pkt, pkt->udp + 1);
|
||||
MG_DEBUG(("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 (pkt->udp->dport == mg_htons(68)) {
|
||||
pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
|
||||
mkpay(pkt, pkt->dhcp + 1);
|
||||
rx_dhcp_client(ifp, pkt);
|
||||
} else if (ifp->enable_dhcp_server && pkt->udp->dport == mg_htons(67)) {
|
||||
pkt->dhcp = (struct dhcp *) (pkt->udp + 1);
|
||||
mkpay(pkt, pkt->dhcp + 1);
|
||||
rx_dhcp_server(ifp, pkt);
|
||||
} else {
|
||||
rx_udp(ifp, pkt);
|
||||
}
|
||||
} else if (pkt->ip->proto == 6) {
|
||||
pkt->tcp = (struct tcp *) (pkt->ip + 1);
|
||||
if (pkt->pay.len < sizeof(*pkt->tcp)) return;
|
||||
mkpay(pkt, pkt->tcp + 1);
|
||||
uint16_t iplen = mg_ntohs(pkt->ip->len);
|
||||
uint16_t off = (uint16_t) (sizeof(*pkt->ip) + ((pkt->tcp->off >> 4) * 4U));
|
||||
if (iplen >= off) pkt->pay.len = (size_t) (iplen - off);
|
||||
MG_DEBUG(("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));
|
||||
rx_tcp(ifp, pkt);
|
||||
}
|
||||
}
|
||||
|
||||
static void rx_ip6(struct mg_tcpip_if *ifp, struct pkt *pkt) {
|
||||
// MG_DEBUG(("IP %d", (int) len));
|
||||
if (pkt->ip6->proto == 1 || pkt->ip6->proto == 58) {
|
||||
pkt->icmp = (struct icmp *) (pkt->ip6 + 1);
|
||||
if (pkt->pay.len < sizeof(*pkt->icmp)) return;
|
||||
mkpay(pkt, pkt->icmp + 1);
|
||||
rx_icmp(ifp, pkt);
|
||||
} else if (pkt->ip6->proto == 17) {
|
||||
pkt->udp = (struct udp *) (pkt->ip6 + 1);
|
||||
if (pkt->pay.len < sizeof(*pkt->udp)) return;
|
||||
// MG_DEBUG((" UDP %u %u -> %u", len, mg_htons(udp->sport),
|
||||
// mg_htons(udp->dport)));
|
||||
mkpay(pkt, pkt->udp + 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void mg_tcpip_rx(struct mg_tcpip_if *ifp, void *buf, size_t len) {
|
||||
const uint8_t broadcast[] = {255, 255, 255, 255, 255, 255};
|
||||
struct pkt pkt;
|
||||
memset(&pkt, 0, sizeof(pkt));
|
||||
pkt.raw.ptr = (char *) buf;
|
||||
pkt.raw.len = len;
|
||||
pkt.eth = (struct eth *) buf;
|
||||
if (pkt.raw.len < sizeof(*pkt.eth)) return; // Truncated - runt?
|
||||
if (memcmp(pkt.eth->dst, ifp->mac, sizeof(pkt.eth->dst)) != 0 &&
|
||||
memcmp(pkt.eth->dst, broadcast, sizeof(pkt.eth->dst)) != 0) {
|
||||
// Not for us. Drop silently
|
||||
} else if (pkt.eth->type == mg_htons(0x806)) {
|
||||
pkt.arp = (struct arp *) (pkt.eth + 1);
|
||||
if (sizeof(*pkt.eth) + sizeof(*pkt.arp) > pkt.raw.len) return; // Truncated
|
||||
rx_arp(ifp, &pkt);
|
||||
} else if (pkt.eth->type == mg_htons(0x86dd)) {
|
||||
pkt.ip6 = (struct ip6 *) (pkt.eth + 1);
|
||||
if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip6)) return; // Truncated
|
||||
if ((pkt.ip6->ver >> 4) != 0x6) return; // Not IP
|
||||
mkpay(&pkt, pkt.ip6 + 1);
|
||||
rx_ip6(ifp, &pkt);
|
||||
} else if (pkt.eth->type == mg_htons(0x800)) {
|
||||
pkt.ip = (struct ip *) (pkt.eth + 1);
|
||||
if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip)) return; // Truncated
|
||||
// Truncate frame to what IP header tells us
|
||||
if ((size_t) mg_ntohs(pkt.ip->len) + sizeof(struct eth) < pkt.raw.len) {
|
||||
pkt.raw.len = (size_t) mg_ntohs(pkt.ip->len) + sizeof(struct eth);
|
||||
}
|
||||
if (pkt.raw.len < sizeof(*pkt.eth) + sizeof(*pkt.ip)) return; // Truncated
|
||||
if ((pkt.ip->ver >> 4) != 4) return; // Not IP
|
||||
mkpay(&pkt, pkt.ip + 1);
|
||||
rx_ip(ifp, &pkt);
|
||||
} else {
|
||||
MG_DEBUG((" Unknown eth type %x", mg_htons(pkt.eth->type)));
|
||||
}
|
||||
}
|
||||
|
||||
static void mg_tcpip_poll(struct mg_tcpip_if *ifp, uint64_t uptime_ms) {
|
||||
if (ifp == NULL || ifp->driver == NULL) return;
|
||||
bool expired_1000ms = mg_timer_expired(&ifp->timer_1000ms, 1000, uptime_ms);
|
||||
ifp->now = uptime_ms;
|
||||
|
||||
// Handle physical interface up/down status
|
||||
if (expired_1000ms && ifp->driver->up) {
|
||||
bool up = ifp->driver->up(ifp);
|
||||
bool current = ifp->state != MIP_STATE_DOWN;
|
||||
if (up != current) {
|
||||
ifp->state = up == false ? MIP_STATE_DOWN
|
||||
: ifp->enable_dhcp_client ? MIP_STATE_UP
|
||||
: MIP_STATE_READY;
|
||||
if (!up && ifp->enable_dhcp_client) ifp->ip = 0;
|
||||
onstatechange(ifp);
|
||||
}
|
||||
}
|
||||
if (ifp->state == MIP_STATE_DOWN) return;
|
||||
|
||||
// If IP not configured, send DHCP
|
||||
if (ifp->ip == 0 && expired_1000ms) tx_dhcp_discover(ifp);
|
||||
|
||||
// Read data from the network
|
||||
size_t len = ifp->driver->rx((void *) ifp->rx.ptr, ifp->rx.len, ifp);
|
||||
if (len) {
|
||||
mg_tcpip_rx(ifp, (void *) ifp->rx.ptr, len);
|
||||
}
|
||||
|
||||
// Process timeouts
|
||||
for (struct mg_connection *c = ifp->mgr->conns; c != NULL; c = c->next) {
|
||||
if (c->is_udp || c->is_listening) continue;
|
||||
if (c->is_connecting || c->is_resolving) continue;
|
||||
struct connstate *s = (struct connstate *) (c + 1);
|
||||
if (uptime_ms > s->timer) {
|
||||
if (s->ttype == MIP_TTYPE_ACK) {
|
||||
MG_DEBUG(("%lu ack %x %x", c->id, s->seq, s->ack));
|
||||
tx_tcp(ifp, s->mac, c->rem.ip, TH_ACK, c->loc.port, c->rem.port,
|
||||
mg_htonl(s->seq), mg_htonl(s->ack), "", 0);
|
||||
} else {
|
||||
if (s->tmiss++ > 2) {
|
||||
mg_error(c, "keepalive");
|
||||
} else {
|
||||
MG_DEBUG(("%lu keepalive", c->id));
|
||||
tx_tcp(ifp, s->mac, c->rem.ip, TH_ACK, c->loc.port, c->rem.port,
|
||||
mg_htonl(s->seq - 1), mg_htonl(s->ack), "", 0);
|
||||
}
|
||||
}
|
||||
settmout(c, MIP_TTYPE_KEEPALIVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This function executes in interrupt context, thus it should copy data
|
||||
// somewhere fast. Note that newlib's malloc is not thread safe, thus use
|
||||
// our lock-free queue with preallocated buffer to copy data and return asap
|
||||
void mg_tcpip_qwrite(void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
||||
if (q_write(&ifp->queue, buf, len)) {
|
||||
ifp->nrecv++;
|
||||
} else {
|
||||
ifp->ndrop++;
|
||||
}
|
||||
}
|
||||
|
||||
size_t mg_tcpip_qread(void *buf, struct mg_tcpip_if *ifp) {
|
||||
return q_read(&ifp->queue, buf);
|
||||
}
|
||||
|
||||
size_t mg_tcpip_driver_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
||||
return mg_tcpip_qread((void *) ifp->rx.ptr, ifp);
|
||||
(void) len, (void) buf;
|
||||
}
|
||||
|
||||
void mg_tcpip_init(struct mg_mgr *mgr, struct mg_tcpip_if *ifp) {
|
||||
// If MAC address is not set, make a random one
|
||||
if (ifp->mac[0] == 0 && ifp->mac[1] == 0 && ifp->mac[2] == 0 &&
|
||||
ifp->mac[3] == 0 && ifp->mac[4] == 0 && ifp->mac[5] == 0) {
|
||||
mg_random(ifp->mac, sizeof(ifp->mac));
|
||||
ifp->mac[0] &= (uint8_t) ~1; // Clear bit 0. 1st byte is even (unicast)
|
||||
MG_INFO(("MAC not set. Generated random: %M", mg_print_mac, ifp->mac));
|
||||
}
|
||||
|
||||
if (ifp->driver->init && !ifp->driver->init(ifp)) {
|
||||
MG_ERROR(("driver init failed"));
|
||||
} else {
|
||||
size_t maxpktsize = 1540;
|
||||
ifp->rx.ptr = (char *) calloc(1, maxpktsize), ifp->rx.len = maxpktsize;
|
||||
ifp->tx.ptr = (char *) calloc(1, maxpktsize), ifp->tx.len = maxpktsize;
|
||||
if (ifp->queue.len) ifp->queue.buf = (uint8_t *) calloc(1, ifp->queue.len);
|
||||
ifp->timer_1000ms = mg_millis();
|
||||
mgr->priv = ifp;
|
||||
ifp->mgr = mgr;
|
||||
mgr->extraconnsize = sizeof(struct connstate);
|
||||
if (ifp->ip == 0) ifp->enable_dhcp_client = true;
|
||||
memset(ifp->gwmac, 255, sizeof(ifp->gwmac)); // Set to broadcast
|
||||
|
||||
// Randomise initial ephemeral port
|
||||
uint16_t jitter;
|
||||
mg_random(&jitter, sizeof(jitter));
|
||||
ifp->eport = MIP_EPHEMERAL_PORT +
|
||||
(uint16_t) (jitter % (0xffffu - MIP_EPHEMERAL_PORT));
|
||||
}
|
||||
}
|
||||
|
||||
void mg_tcpip_free(struct mg_tcpip_if *ifp) {
|
||||
free((char *) ifp->rx.ptr);
|
||||
free((char *) ifp->tx.ptr);
|
||||
}
|
||||
|
||||
int mg_mkpipe(struct mg_mgr *m, mg_event_handler_t fn, void *d, bool udp) {
|
||||
(void) m, (void) fn, (void) d, (void) udp;
|
||||
MG_ERROR(("Not implemented"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
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));
|
||||
struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
|
||||
tx_tcp(ifp, s->mac, c->rem.ip, TH_SYN, c->loc.port, c->rem.port, isn, 0, NULL,
|
||||
0);
|
||||
}
|
||||
|
||||
void mg_connect_resolved(struct mg_connection *c) {
|
||||
struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
|
||||
c->is_resolving = 0;
|
||||
if (ifp->eport < MIP_EPHEMERAL_PORT) ifp->eport = MIP_EPHEMERAL_PORT;
|
||||
c->loc.ip = ifp->ip;
|
||||
c->loc.port = mg_htons(ifp->eport++);
|
||||
MG_DEBUG(("%lu %M -> %M", c->id, mg_print_ip_port, &c->loc, mg_print_ip_port,
|
||||
&c->rem));
|
||||
mg_call(c, MG_EV_RESOLVE, NULL);
|
||||
if (((c->rem.ip & ifp->mask) == (ifp->ip & ifp->mask))) {
|
||||
// If we're in the same LAN, fire an ARP lookup. TODO(cpq): handle this!
|
||||
MG_DEBUG(("%lu ARP lookup...", c->id));
|
||||
arp_ask(ifp, c->rem.ip);
|
||||
c->is_arplooking = 1;
|
||||
} else {
|
||||
struct connstate *s = (struct connstate *) (c + 1);
|
||||
memcpy(s->mac, ifp->gwmac, sizeof(ifp->gwmac));
|
||||
if (c->is_udp) {
|
||||
mg_call(c, MG_EV_CONNECT, NULL);
|
||||
} else {
|
||||
send_syn(c);
|
||||
c->is_connecting = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool mg_open_listener(struct mg_connection *c, const char *url) {
|
||||
c->loc.port = mg_htons(mg_url_port(url));
|
||||
return true;
|
||||
}
|
||||
|
||||
static void write_conn(struct mg_connection *c) {
|
||||
long len = c->is_tls ? mg_tls_send(c, c->send.buf, c->send.len)
|
||||
: mg_io_send(c, c->send.buf, c->send.len);
|
||||
if (len > 0) {
|
||||
mg_iobuf_del(&c->send, 0, (size_t) len);
|
||||
mg_call(c, MG_EV_WRITE, &len);
|
||||
}
|
||||
}
|
||||
|
||||
static void close_conn(struct mg_connection *c) {
|
||||
struct connstate *s = (struct connstate *) (c + 1);
|
||||
mg_iobuf_free(&s->raw); // For TLS connections, release raw data
|
||||
if (c->is_udp == false && c->is_listening == false) { // For TCP conns,
|
||||
struct mg_tcpip_if *ifp =
|
||||
(struct mg_tcpip_if *) c->mgr->priv; // send TCP FIN
|
||||
tx_tcp(ifp, s->mac, c->rem.ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port,
|
||||
mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0);
|
||||
}
|
||||
mg_close_conn(c);
|
||||
}
|
||||
|
||||
static bool can_write(struct mg_connection *c) {
|
||||
return c->is_connecting == 0 && c->is_resolving == 0 && c->send.len > 0 &&
|
||||
c->is_tls_hs == 0 && c->is_arplooking == 0;
|
||||
}
|
||||
|
||||
void mg_mgr_poll(struct mg_mgr *mgr, int ms) {
|
||||
struct mg_connection *c, *tmp;
|
||||
uint64_t now = mg_millis();
|
||||
mg_tcpip_poll((struct mg_tcpip_if *) mgr->priv, now);
|
||||
mg_timer_poll(&mgr->timers, now);
|
||||
for (c = mgr->conns; c != NULL; c = tmp) {
|
||||
tmp = c->next;
|
||||
mg_call(c, MG_EV_POLL, &now);
|
||||
MG_VERBOSE(("%lu .. %c%c%c%c%c", c->id, c->is_tls ? 'T' : 't',
|
||||
c->is_connecting ? 'C' : 'c', c->is_tls_hs ? 'H' : 'h',
|
||||
c->is_resolving ? 'R' : 'r', c->is_closing ? 'C' : 'c'));
|
||||
if (c->is_tls_hs) mg_tls_handshake(c);
|
||||
if (can_write(c)) write_conn(c);
|
||||
if (c->is_draining && c->send.len == 0) c->is_closing = 1;
|
||||
if (c->is_closing) close_conn(c);
|
||||
}
|
||||
(void) ms;
|
||||
}
|
||||
|
||||
bool mg_send(struct mg_connection *c, const void *buf, size_t len) {
|
||||
struct mg_tcpip_if *ifp = (struct mg_tcpip_if *) c->mgr->priv;
|
||||
bool res = false;
|
||||
if (ifp->ip == 0 || ifp->state != MIP_STATE_READY) {
|
||||
mg_error(c, "net down");
|
||||
} else if (c->is_udp) {
|
||||
struct connstate *s = (struct connstate *) (c + 1);
|
||||
tx_udp(ifp, s->mac, ifp->ip, c->loc.port, c->rem.ip, c->rem.port, buf, len);
|
||||
res = true;
|
||||
} else {
|
||||
res = mg_iobuf_add(&c->send, c->send.len, buf, len);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
#endif // MG_ENABLE_TCPIP
|
||||
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include "arch.h"
|
||||
#include "net.h"
|
||||
|
||||
struct mg_tcpip_if; // MIP network interface
|
||||
|
||||
struct mg_tcpip_driver {
|
||||
bool (*init)(struct mg_tcpip_if *); // Init driver
|
||||
size_t (*tx)(const void *, size_t, struct mg_tcpip_if *); // Transmit frame
|
||||
size_t (*rx)(void *buf, size_t len, struct mg_tcpip_if *); // Receive frame
|
||||
bool (*up)(struct mg_tcpip_if *); // Up/down status
|
||||
};
|
||||
|
||||
// Receive queue - single producer, single consumer queue. Interrupt-based
|
||||
// drivers copy received frames to the queue in interrupt context.
|
||||
// mg_tcpip_poll() function runs in event loop context, reads from the queue
|
||||
struct queue {
|
||||
uint8_t *buf;
|
||||
size_t len;
|
||||
volatile size_t tail, head;
|
||||
};
|
||||
|
||||
// Network interface
|
||||
struct mg_tcpip_if {
|
||||
uint8_t mac[6]; // MAC address. Must be set to a valid MAC
|
||||
uint32_t ip, mask, gw; // IP address, mask, default gateway
|
||||
struct mg_str rx; // Output (TX) buffer
|
||||
struct mg_str tx; // Input (RX) buffer
|
||||
bool enable_dhcp_client; // Enable DCHP client
|
||||
bool enable_dhcp_server; // Enable DCHP server
|
||||
struct mg_tcpip_driver *driver; // Low level driver
|
||||
void *driver_data; // Driver-specific data
|
||||
struct mg_mgr *mgr; // Mongoose event manager
|
||||
struct queue queue; // Set queue.len for interrupt based drivers
|
||||
|
||||
// Internal state, user can use it but should not change it
|
||||
uint8_t gwmac[6]; // Router's MAC
|
||||
uint64_t now; // Current time
|
||||
uint64_t timer_1000ms; // 1000 ms timer: for DHCP and link state
|
||||
uint64_t lease_expire; // Lease expiration time
|
||||
uint16_t eport; // Next ephemeral port
|
||||
volatile uint32_t ndrop; // Number of received, but dropped frames
|
||||
volatile uint32_t nrecv; // Number of received frames
|
||||
volatile uint32_t nsent; // Number of transmitted frames
|
||||
volatile uint32_t nerr; // Number of driver errors
|
||||
uint8_t state; // Current state
|
||||
#define MIP_STATE_DOWN 0 // Interface is down
|
||||
#define MIP_STATE_UP 1 // Interface is up
|
||||
#define MIP_STATE_READY 2 // Interface is up and has IP
|
||||
};
|
||||
|
||||
void mg_tcpip_init(struct mg_mgr *, struct mg_tcpip_if *);
|
||||
void mg_tcpip_free(struct mg_tcpip_if *);
|
||||
void mg_tcpip_qwrite(void *buf, size_t len, struct mg_tcpip_if *ifp);
|
||||
size_t mg_tcpip_qread(void *buf, struct mg_tcpip_if *ifp);
|
||||
// conveniency rx function for IRQ-driven drivers
|
||||
size_t mg_tcpip_driver_rx(void *buf, size_t len, struct mg_tcpip_if *ifp);
|
||||
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_stm32;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_w5500;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_tm4c;
|
||||
extern struct mg_tcpip_driver mg_tcpip_driver_stm32h;
|
||||
|
||||
// Drivers that require SPI, can use this SPI abstraction
|
||||
struct mg_tcpip_spi {
|
||||
void *spi; // Opaque SPI bus descriptor
|
||||
void (*begin)(void *); // SPI begin: slave select low
|
||||
void (*end)(void *); // SPI end: slave select high
|
||||
uint8_t (*txn)(void *, uint8_t); // SPI transaction: write 1 byte, read reply
|
||||
};
|
||||
|
||||
#if MG_ENABLE_TCPIP
|
||||
#if !defined(MG_ENABLE_DRIVER_STM32H) && !defined(MG_ENABLE_DRIVER_TM4C)
|
||||
#define MG_ENABLE_DRIVER_STM32 1
|
||||
#else
|
||||
#define MG_ENABLE_DRIVER_STM32 0
|
||||
#endif
|
||||
#endif
|
||||
Reference in new issue
Block a user