Move mip/ -> src/tcpip/, rename mip_ -> mg_tcpip_

This commit is contained in:
cpq committed 2023-02-07 21:35:37 +00:00
1 parent efc54375dd
commit 2c62e58dbd
64 files changed
+515 -516

No files matched your search

+3 -3
View File
@@ -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
+218
View File
@@ -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
+16
View File
@@ -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
};
+276
View File
@@ -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
+16
View File
@@ -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
};
+251
View File
@@ -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
+14
View File
@@ -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
};
+89
View File
@@ -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
+974
View File
@@ -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
+79
View File
@@ -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