From d3368e4aadba0b650ff5d9b40373b0ad1542924f Mon Sep 17 00:00:00 2001 From: Sergey Lyubka Date: Sat, 11 Jun 2022 17:29:45 +0100 Subject: [PATCH] Move mip into mip/ --- Makefile | 8 +- drivers/README.md | 2 - drivers/mip_driver_stm32.h | 12 - .../stm32/nucleo-f746zg-baremetal/Makefile | 3 +- examples/stm32/nucleo-f746zg-baremetal/main.c | 8 +- examples/uart-bridge/net.c | 4 + mip/README.md | 9 + .../mip_driver_stm32.c => mip/driver_stm32.c | 55 +- .../mip_driver_w5500.h => mip/driver_w5500.h | 0 {src => mip}/mip.c | 0 {src => mip}/mip.h | 2 + mongoose.c | 1737 +++++++++-------- mongoose.h | 34 +- 13 files changed, 1016 insertions(+), 858 deletions(-) delete mode 100644 drivers/README.md delete mode 100644 drivers/mip_driver_stm32.h create mode 100644 mip/README.md rename drivers/mip_driver_stm32.c => mip/driver_stm32.c (73%) rename drivers/mip_driver_w5500.h => mip/driver_w5500.h (100%) rename {src => mip}/mip.c (100%) rename {src => mip}/mip.h (94%) diff --git a/Makefile b/Makefile index 040fdf17..9f806039 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ SRCS = mongoose.c test/unit_test.c test/packed_fs.c -HDRS = $(wildcard src/*.h) +HDRS = $(wildcard src/*.h) $(wildcard mip/*.h) DEFS ?= -DMG_MAX_HTTP_HEADERS=7 -DMG_ENABLE_LINES -DMG_ENABLE_PACKED_FS=1 -DMG_ENABLE_SSI=1 C_WARN ?= -Wmissing-prototypes -Wstrict-prototypes WARN ?= -pedantic -W -Wall -Werror -Wshadow -Wdouble-promotion -fno-common -Wconversion -Wundef $(C_WARN) @@ -149,11 +149,11 @@ install: linux-libs uninstall: rm -rf $(DESTDIR)$(PREFIX)/lib/libmongoose.a $(DESTDIR)$(PREFIX)/lib/libmongoose.so.$(VERSION) $(DESTDIR)$(PREFIX)/include/mongoose.h $(DESTDIR)$(PREFIX)/lib/libmongoose.so -mongoose.c: Makefile $(wildcard src/*) - (cat src/license.h; echo; echo '#include "mongoose.h"' ; (for F in src/*.c ; do echo; echo '#ifdef MG_ENABLE_LINES'; echo "#line 1 \"$$F\""; echo '#endif'; cat $$F | sed -e 's,#include ".*,,'; done))> $@ +mongoose.c: Makefile $(wildcard src/*) $(wildcard mip/*.c) + (cat src/license.h; echo; echo '#include "mongoose.h"' ; (for F in src/*.c mip/*.c ; do echo; echo '#ifdef MG_ENABLE_LINES'; echo "#line 1 \"$$F\""; echo '#endif'; cat $$F | sed -e 's,#include ".*,,'; done))> $@ mongoose.h: $(HDRS) Makefile - (cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/arch.h src/arch_*.h src/config.h src/str.h src/log.h src/timer.h src/fs.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h src/mip.h src/json.h | sed -e 's,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@ + (cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/arch.h src/arch_*.h src/config.h src/str.h src/log.h src/timer.h src/fs.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h src/json.h mip/mip.h | sed -e 's,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@ clean: rm -rf $(PROG) *.exe *.o *.dSYM unit_test* valgrind_unit_test* ut fuzzer *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb slow-unit* _CL_* infer-out data.txt crash-* test/packed_fs.c pack unpacked diff --git a/drivers/README.md b/drivers/README.md deleted file mode 100644 index 91f9c5c0..00000000 --- a/drivers/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# This is a collection of low-level drivers for the experimental -# built-in TCP/IP stack diff --git a/drivers/mip_driver_stm32.h b/drivers/mip_driver_stm32.h deleted file mode 100644 index 53417db2..00000000 --- a/drivers/mip_driver_stm32.h +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2022 Cesanta Software Limited -// All rights reserved - -#pragma once - -#include -#include - -void mip_driver_stm32_init(void *userdata); -bool mip_driver_stm32_status(void *); -void mip_driver_stm32_setrx(void (*rx)(void *, size_t, void *), void *); -size_t mip_driver_stm32_tx(const void *buf, size_t len, void *); diff --git a/examples/stm32/nucleo-f746zg-baremetal/Makefile b/examples/stm32/nucleo-f746zg-baremetal/Makefile index 8cc8ceac..640fa11d 100644 --- a/examples/stm32/nucleo-f746zg-baremetal/Makefile +++ b/examples/stm32/nucleo-f746zg-baremetal/Makefile @@ -10,8 +10,7 @@ CFLAGS ?= -W -Wall -Wextra -Werror -Wundef -Wshadow -Wdouble-promotion \ -mcpu=cortex-m7 -mthumb -mfloat-abi=softfp -mfpu=vfpv4 $(EXTRA) LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs \ -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map -SOURCES = boot.c main.c syscalls.c $(ROOT)/drivers/mip_driver_stm32.c \ - $(ROOT)/mongoose.c +SOURCES = boot.c main.c syscalls.c $(ROOT)/mongoose.c all build example: $(TARGET).bin diff --git a/examples/stm32/nucleo-f746zg-baremetal/main.c b/examples/stm32/nucleo-f746zg-baremetal/main.c index 9822a7bf..8bd7df48 100644 --- a/examples/stm32/nucleo-f746zg-baremetal/main.c +++ b/examples/stm32/nucleo-f746zg-baremetal/main.c @@ -2,8 +2,6 @@ // All rights reserved #include "mcu.h" - -#include "drivers/mip_driver_stm32.h" #include "mongoose.h" #define LED1 PIN('B', 0) // On-board LED pin (green) @@ -138,11 +136,7 @@ int main(void) { // For static configuration, specify IP/mask/GW in network byte order struct mip_ipcfg ipcfg = { .mac = {0xaa, 0xbb, 0xcc, 1, 2, 3}, .ip = 0, .mask = 0, .gw = 0}; - struct mip_driver stm32_driver = {.init = mip_driver_stm32_init, - .tx = mip_driver_stm32_tx, - .rxcb = mip_driver_stm32_setrx, - .status = mip_driver_stm32_status}; - mip_init(&mgr, &ipcfg, &stm32_driver); + mip_init(&mgr, &ipcfg, &mip_driver_stm32); MG_INFO(("Init done, starting main loop")); #if defined(DASH) diff --git a/examples/uart-bridge/net.c b/examples/uart-bridge/net.c index f2fcfedf..5f2f0dc7 100644 --- a/examples/uart-bridge/net.c +++ b/examples/uart-bridge/net.c @@ -11,6 +11,10 @@ static int s_rx = 4; static int s_tx = 5; static int s_baud = 115200; +void uart_init(int tx, int rx, int baud); +int uart_read(char *buf, size_t len); +void uart_write(const void *buf, size_t len); + // Let users define their own UART API. If they don't, use a dummy one #if defined(UART_API_IMPLEMENTED) #else diff --git a/mip/README.md b/mip/README.md new file mode 100644 index 00000000..35ac7c4e --- /dev/null +++ b/mip/README.md @@ -0,0 +1,9 @@ +# MIP - experimental TCP/IP stack for Mongoose Library + +This bare-metal embedded TCP/IP stack is designed specifically for Mongoose. +It does not implement BSD sockets interface - instead, it implements +an event-based API tailored for Mongoose Library. As a result, it does not +need to use synchronisation primitives. It is very fast, and can work +with or without RTOS. + +See example usage at [examples/stm32/nucleo-f746zg/baremetal](../examples/stm32/nucleo-f746zg/baremetal) diff --git a/drivers/mip_driver_stm32.c b/mip/driver_stm32.c similarity index 73% rename from drivers/mip_driver_stm32.c rename to mip/driver_stm32.c index 6b1320bc..691705ba 100644 --- a/drivers/mip_driver_stm32.c +++ b/mip/driver_stm32.c @@ -1,12 +1,11 @@ // Copyright (c) 2022 Cesanta Software Limited // All rights reserved -#include "mip_driver_stm32.h" -#include -#include -#include +#include "mip.h" -struct eth { +#if MG_ENABLE_MIP + +struct stm32_eth { 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, @@ -19,9 +18,9 @@ struct eth { DMAMFBOCR, DMARSWTR, RESERVED10[8], DMACHTDR, DMACHRDR, DMACHTBAR, DMACHRBAR; }; -#define ETH ((struct eth *) 0x40028000) +#define ETH ((struct stm32_eth *) (uintptr_t) 0x40028000) -#define BIT(x) (1UL << (x)) +#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) @@ -39,7 +38,7 @@ static inline void spin(volatile uint32_t count) { while (count--) asm("nop"); } -uint32_t eth_read_phy(uint8_t addr, uint8_t reg) { +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); @@ -47,7 +46,7 @@ uint32_t eth_read_phy(uint8_t addr, uint8_t reg) { return ETH->MACMIIDR; } -void eth_write_phy(uint8_t addr, uint8_t reg, uint32_t val) { +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); @@ -55,21 +54,23 @@ void eth_write_phy(uint8_t addr, uint8_t reg, uint32_t val) { while (ETH->MACMIIAR & BIT(0)) spin(1); } -void mip_driver_stm32_init(void *userdata) { +static void mip_driver_stm32_init(void *userdata) { s_userdata = userdata; // 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) s_rxbuf[i]; // Point to data buffer - s_rxdesc[i][3] = (uint32_t) s_rxdesc[(i + 1) % ETH_DESC_CNT]; // Chain + 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) s_txbuf[i]; // Buf pointer - s_txdesc[i][3] = (uint32_t) s_txdesc[(i + 1) % ETH_DESC_CNT]; // Chain + 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 @@ -83,20 +84,21 @@ void mip_driver_stm32_init(void *userdata) { 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) s_rxdesc; // RX descriptors - ETH->DMATDLAR = (uint32_t) s_txdesc; // RX descriptors - ETH->DMAIER = BIT(6) | BIT(16); // RIE, NISE + 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 } -void mip_driver_stm32_setrx(void (*rx)(void *, size_t, void *), void *rxdata) { +static void mip_driver_stm32_setrx(void (*rx)(void *, size_t, void *), + void *rxdata) { s_rx = rx; s_rxdata = rxdata; } static uint32_t s_txno; -size_t mip_driver_stm32_tx(const void *buf, size_t len, void *userdata) { +static size_t mip_driver_stm32_tx(const void *buf, size_t len, void *userdata) { if (len > sizeof(s_txbuf[s_txno])) { printf("%s: frame too big, %ld\n", __func__, (long) len); len = 0; // Frame is too big @@ -113,17 +115,18 @@ size_t mip_driver_stm32_tx(const void *buf, size_t len, void *userdata) { uint32_t sr = ETH->DMASR; if (sr & BIT(2)) ETH->DMASR = BIT(2), ETH->DMATPDR = 0; // Resume if (sr & BIT(5)) ETH->DMASR = BIT(5), ETH->DMATPDR = 0; // if busy - if (len == 0) printf("E: D0 %lx, DMASR %lx\n", s_txdesc[0][0], sr); + if (len == 0) printf("E: D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long) sr); return len; (void) userdata; } -bool mip_driver_stm32_status(void *userdata) { +static bool mip_driver_stm32_status(void *userdata) { uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR); return bsr & BIT(2) ? 1 : 0; (void) userdata; } +void ETH_IRQHandler(void); void ETH_IRQHandler(void) { volatile uint32_t sr = ETH->DMASR; if (sr & BIT(6)) { // Frame received, loop @@ -138,3 +141,9 @@ void ETH_IRQHandler(void) { if (sr & BIT(7)) ETH->DMARPDR = 0; // Resume RX ETH->DMASR = sr & ~(BIT(2) | BIT(7)); // Clear status } + +struct mip_driver mip_driver_stm32 = {.init = mip_driver_stm32_init, + .tx = mip_driver_stm32_tx, + .rxcb = mip_driver_stm32_setrx, + .status = mip_driver_stm32_status}; +#endif // MG_ENABLE_MIP diff --git a/drivers/mip_driver_w5500.h b/mip/driver_w5500.h similarity index 100% rename from drivers/mip_driver_w5500.h rename to mip/driver_w5500.h diff --git a/src/mip.c b/mip/mip.c similarity index 100% rename from src/mip.c rename to mip/mip.c diff --git a/src/mip.h b/mip/mip.h similarity index 94% rename from src/mip.h rename to mip/mip.h index 5f00c890..33b7ea55 100644 --- a/src/mip.h +++ b/mip/mip.h @@ -19,3 +19,5 @@ struct mip_ipcfg { }; void mip_init(struct mg_mgr *, struct mip_ipcfg *, struct mip_driver *); + +extern struct mip_driver mip_driver_stm32; diff --git a/mongoose.c b/mongoose.c index b5a82db9..1b8c695a 100644 --- a/mongoose.c +++ b/mongoose.c @@ -2532,798 +2532,6 @@ void mg_md5_final(mg_md5_ctx *ctx, unsigned char digest[16]) { } #endif -#ifdef MG_ENABLE_LINES -#line 1 "src/mip.c" -#endif - - - -#if MG_ENABLE_MIP -#include -#define MIP_ETHEMERAL_PORT 49152 -#define _packed __attribute__((packed)) -#define U16(ptr) ((((uint16_t) (ptr)[0]) << 8) | (ptr)[1]) -#define NET16(x) __builtin_bswap16(x) -#define NET32(x) __builtin_bswap32(x) -#define PDIFF(a, b) ((size_t) (((char *) (b)) - ((char *) (a)))) - -#ifndef MIP_ARP_ENTRIES -#define MIP_ARP_ENTRIES 5 // Number of ARP cache entries. Maximum 21 -#endif -#define MIP_ARP_CS (2 + 12 * MIP_ARP_ENTRIES) // ARP cache size - -struct str { - uint8_t *buf; - size_t len; -}; - -// Receive queue - single producer, single consumer queue. Interrupt-based -// drivers copy received frames to the queue in interrupt context. mip_poll() -// function runs in event loop context, reads from the queue -struct queue { - uint8_t *buf; - size_t len; - volatile _Atomic size_t tail, head; -}; - -// Network interface -struct mip_if { - uint8_t mac[6]; // MAC address. Must be set to a valid MAC - uint32_t ip, mask, gw; // IP address, mask, default gateway. Can be 0 - struct str rx; // Output (TX) buffer - struct str tx; // Input (RX) buffer - bool use_dhcp; // Enable DCHP - struct mip_driver *driver; // Low level driver - struct mg_mgr *mgr; // Mongoose event manager - - // Internal state, user can use it but should not change it - uint64_t curtime; // Last poll timestamp in millis - uint64_t timer; // Timer - uint8_t arp_cache[MIP_ARP_CS]; // Each entry is 12 bytes - uint16_t eport; // Next ephemeral port - int 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 - struct queue queue; // Receive queue -}; - -struct lcp { - uint8_t addr, ctrl, proto[2], code, id, len[2]; -} _packed; - -struct eth { - uint8_t dst[6]; // Destination MAC address - uint8_t src[6]; // Source MAC address - uint16_t type; // Ethernet type -} _packed; - -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 -} _packed; - -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 -} _packed; - -struct icmp { - uint8_t type; - uint8_t code; - uint16_t csum; -} _packed; - -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 -} _packed; - -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 -} _packed; - -struct udp { - uint16_t sport; // Source port - uint16_t dport; // Destination port - uint16_t len; // UDP length - uint16_t csum; // UDP checksum -} _packed; - -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]; -} _packed; - -struct pkt { - struct str raw; // Raw packet data - struct 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 i = 0, left = q->len - head; - for (; i < len && i < left; i++) q->buf[head + i] = buf[i]; - for (; i < len; i++) q->buf[i - left] = buf[i]; -} - -static void q_copyout(struct queue *q, uint8_t *buf, size_t len, size_t tail) { - size_t i = 0, left = q->len - tail; - for (; i < len && i < left; i++) buf[i] = q->buf[tail + i]; - for (; i < len; i++) buf[i] = q->buf[i - 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; - 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 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 str mkstr(void *buf, size_t len) { - struct str str = {(uint8_t *) buf, len}; - return str; -} - -static void mkpay(struct pkt *pkt, void *p) { - pkt->pay = mkstr(p, (size_t) (&pkt->raw.buf[pkt->raw.len] - (uint8_t *) 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 NET16(~sum & 0xffff); -} - -static uint16_t ipcsum(const void *buf, size_t len) { - uint32_t sum = csumup(0, buf, len); - return csumfin(sum); -} - -// ARP cache is organised as a doubly linked list. A successful cache lookup -// moves an entry to the head of the list. New entries are added by replacing -// the last entry in the list with a new IP/MAC. -// ARP cache format: | prev | next | Entry0 | Entry1 | .... | EntryN | -// ARP entry format: | prev | next | IP (4bytes) | MAC (6bytes) | -// prev and next are 1-byte offsets in the cache, so cache size is max 256 bytes -// ARP entry size is 12 bytes -static void arp_cache_init(uint8_t *p, int n, int size) { - for (int i = 0; i < n; i++) p[2 + i * size] = (uint8_t) (2 + (i - 1) * size); - for (int i = 0; i < n; i++) p[3 + i * size] = (uint8_t) (2 + (i + 1) * size); - p[0] = p[2] = (uint8_t) (2 + (n - 1) * size); - p[1] = p[3 + (n - 1) * size] = 2; -} - -static uint8_t *arp_cache_find(struct mip_if *ifp, uint32_t ip) { - uint8_t *p = ifp->arp_cache; - if (ip == 0) return NULL; - if (p[0] == 0 || p[1] == 0) arp_cache_init(p, MIP_ARP_ENTRIES, 12); - for (uint8_t i = 0, j = p[1]; i < MIP_ARP_ENTRIES; i++, j = p[j + 1]) { - if (memcmp(p + j + 2, &ip, sizeof(ip)) == 0) { - p[1] = j, p[0] = p[j]; // Found entry! Point list head to us - // MG_DEBUG(("ARP find: %#lx @ %x:%x:%x:%x:%x:%x\n", (long) ip, p[j + 6], - // p[j + 7], p[j + 8], p[j + 9], p[j + 10], p[j + 11])); - return p + j + 6; // And return MAC address - } - } - return NULL; -} - -static void arp_cache_add(struct mip_if *ifp, uint32_t ip, uint8_t mac[6]) { - uint8_t *p = ifp->arp_cache; - if (ip == 0 || ip == ~0U) return; // Bad IP - if (arp_cache_find(ifp, ip) != NULL) return; // Already exists, do nothing - memcpy(p + p[0] + 2, &ip, sizeof(ip)); // Replace last entry: IP address - memcpy(p + p[0] + 6, mac, 6); // And MAC address - p[1] = p[0], p[0] = p[p[1]]; // Point list head to us - // MG_DEBUG(("ARP cache: added %#lx @ %x:%x:%x:%x:%x:%x\n", (long) ip, mac[0], - // mac[1], mac[2], mac[3], mac[4], mac[5])); -} - -static void arp_ask(struct mip_if *ifp, uint32_t ip) { - struct eth *eth = (struct eth *) ifp->tx.buf; - struct arp *arp = (struct arp *) (eth + 1); - memset(eth->dst, 255, sizeof(eth->dst)); - memcpy(eth->src, ifp->mac, sizeof(eth->src)); - eth->type = NET16(0x806); - memset(arp, 0, sizeof(*arp)); - arp->fmt = NET16(1), arp->pro = NET16(0x800), arp->hlen = 6, arp->plen = 4; - arp->op = NET16(1), arp->tpa = ip, arp->spa = ifp->ip; - memcpy(arp->sha, ifp->mac, sizeof(arp->sha)); - ifp->driver->tx(eth, PDIFF(eth, arp + 1), ifp->driver->data); -} - -static void onstatechange(struct mip_if *ifp) { - if (ifp->state == MIP_STATE_READY) { - char buf[40]; - struct mg_addr addr = {.ip = ifp->ip}; - MG_INFO(("READY, IP: %s", mg_ntoa(&addr, buf, sizeof(buf)))); - arp_ask(ifp, ifp->gw); - } else if (ifp->state == MIP_STATE_UP) { - MG_ERROR(("Network up")); - } else if (ifp->state == MIP_STATE_DOWN) { - MG_ERROR(("Network down")); - } -} - -static struct ip *tx_ip(struct mip_if *ifp, uint8_t proto, uint32_t ip_src, - uint32_t ip_dst, size_t plen) { - struct eth *eth = (struct eth *) ifp->tx.buf; - struct ip *ip = (struct ip *) (eth + 1); - uint8_t *mac = arp_cache_find(ifp, ip_dst); // Dst IP in ARP cache ? - if (!mac) mac = arp_cache_find(ifp, ifp->gw); // No, use gateway - if (mac) memcpy(eth->dst, mac, sizeof(eth->dst)); // Found? Use it - if (!mac) memset(eth->dst, 255, sizeof(eth->dst)); // No? Use broadcast - memcpy(eth->src, ifp->mac, sizeof(eth->src)); // TODO(cpq): ARP lookup - eth->type = NET16(0x800); - memset(ip, 0, sizeof(*ip)); - ip->ver = 0x45; // Version 4, header length 5 words - ip->frag = 0x40; // Don't fragment - ip->len = NET16((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; -} - -void tx_udp(struct mip_if *ifp, 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, 17, ip_src, ip_dst, len + sizeof(struct udp)); - struct udp *udp = (struct udp *) (ip + 1); - udp->sport = sport; - udp->dport = dport; - udp->len = NET16((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 += ip->proto + sizeof(*udp) + len; - udp->csum = csumfin(cs); - memmove(udp + 1, buf, len); - // MG_DEBUG(("UDP LEN %d %d\n", (int) len, (int) ifp->frame_len)); - ifp->driver->tx(ifp->tx.buf, - sizeof(struct eth) + sizeof(*ip) + sizeof(*udp) + len, - ifp->driver->data); -} - -static void tx_dhcp(struct mip_if *ifp, uint32_t src, uint32_t dst, - uint8_t *opts, size_t optslen) { - struct dhcp dhcp = {.op = 1, - .htype = 1, - .hlen = 6, - .ciaddr = src, - .magic = NET32(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, src, NET16(68), dst, NET16(67), &dhcp, sizeof(dhcp)); -} - -static void tx_dhcp_request(struct mip_if *ifp, uint32_t src, uint32_t 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, &dst, sizeof(dst)); - memcpy(opts + 20, &src, sizeof(src)); - tx_dhcp(ifp, src, dst, opts, sizeof(opts)); -} - -static void tx_dhcp_discover(struct mip_if *ifp) { - uint8_t opts[] = { - 53, 1, 1, // Type: DHCP discover - 55, 2, 1, 3, // Parameters: ip, mask - 255 // End of options - }; - tx_dhcp(ifp, 0, 0xffffffff, opts, sizeof(opts)); -} - -static void rx_arp(struct mip_if *ifp, struct pkt *pkt) { - // MG_DEBUG(("ARP op %d %#x %#x\n", NET16(arp->op), arp->spa, arp->tpa)); - if (pkt->arp->op == NET16(1) && pkt->arp->tpa == ifp->ip) { - // ARP request. Make a response, then send - struct eth *eth = (struct eth *) ifp->tx.buf; - 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 = NET16(0x806); - *arp = *pkt->arp; - arp->op = NET16(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 response: we're %#lx", (long) ifp->ip)); - ifp->driver->tx(ifp->tx.buf, PDIFF(eth, arp + 1), ifp->driver->data); - } else if (pkt->arp->op == NET16(2)) { - if (memcmp(pkt->arp->tha, ifp->mac, sizeof(pkt->arp->tha)) != 0) return; - // MG_INFO(("ARP RESPONSE")); - arp_cache_add(ifp, pkt->arp->spa, pkt->arp->sha); - } -} - -static void rx_icmp(struct mip_if *ifp, struct pkt *pkt) { - // MG_DEBUG(("ICMP %d\n", (int) len)); - if (pkt->icmp->type == 8 && pkt->ip->dst == ifp->ip) { - struct ip *ip = tx_ip(ifp, 1, ifp->ip, pkt->ip->src, - sizeof(struct icmp) + pkt->pay.len); - struct icmp *icmp = (struct icmp *) (ip + 1); - memset(icmp, 0, sizeof(*icmp)); // Important - set csum to 0 - memcpy(icmp + 1, pkt->pay.buf, pkt->pay.len); - icmp->csum = ipcsum(icmp, sizeof(*icmp) + pkt->pay.len); - ifp->driver->tx(ifp->tx.buf, PDIFF(ifp->tx.buf, icmp + 1) + pkt->pay.len, - ifp->driver->data); - } -} - -static void rx_dhcp(struct mip_if *ifp, struct pkt *pkt) { - uint32_t ip = 0, gw = 0, mask = 0; - uint8_t *p = pkt->dhcp->options, *end = &pkt->raw.buf[pkt->raw.len]; - if (end < (uint8_t *) (pkt->dhcp + 1)) return; - // MG_DEBUG(("DHCP %u\n", (unsigned) pkt->raw.len)); - while (p < end && p[0] != 255) { - if (p[0] == 1 && p[1] == sizeof(ifp->mask)) { - memcpy(&mask, p + 2, sizeof(mask)); - // MG_DEBUG(("MASK %x\n", mask)); - } else if (p[0] == 3 && p[1] == sizeof(ifp->gw)) { - memcpy(&gw, p + 2, sizeof(gw)); - ip = pkt->dhcp->yiaddr; - // MG_DEBUG(("IP %x GW %x\n", ip, gw)); - } - p += p[1] + 2; - } - if (ip && mask && gw && ifp->ip == 0) { - // MG_DEBUG(("DHCP offer ip %#08lx mask %#08lx gw %#08lx\n", - // (long) ip, (long) mask, (long) gw)); - arp_cache_add(ifp, pkt->dhcp->siaddr, ((struct eth *) pkt->raw.buf)->src); - ifp->ip = ip, ifp->gw = gw, ifp->mask = mask; - ifp->state = MIP_STATE_READY; - onstatechange(ifp); - tx_dhcp_request(ifp, ip, pkt->dhcp->siaddr); - } -} - -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_udp(struct mip_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.buf, pkt->pay.len); - c->recv.len += pkt->pay.len; - struct mg_str evd = mg_str_n((char *) pkt->pay.buf, pkt->pay.len); - mg_call(c, MG_EV_READ, &evd); - } - } -} - -struct tcpstate { - uint32_t seq, ack; - time_t expire; -}; - -static size_t tx_tcp(struct mip_if *ifp, 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, 6, ifp->ip, dst_ip, sizeof(struct tcp) + len); - struct tcp *tcp = (struct tcp *) (ip + 1); - memset(tcp, 0, sizeof(*tcp)); - 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 ifp->driver->tx(ifp->tx.buf, PDIFF(ifp->tx.buf, tcp + 1) + len, - ifp->driver->data); -} - -static size_t tx_tcp_pkt(struct mip_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->ip->src, flags, pkt->tcp->dport, pkt->tcp->sport, seq, - mg_htonl(mg_ntohl(pkt->tcp->seq) + delta), buf, len); -} - -static struct mg_connection *accept_conn(struct mg_connection *lsn, - struct pkt *pkt) { - struct mg_connection *c = mg_alloc_conn(lsn->mgr); - struct tcpstate *s = (struct tcpstate *) (c + 1); - s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq); - c->rem.ip = pkt->ip->src; - c->rem.port = pkt->tcp->sport; - MG_DEBUG(("%lu accepted %lx:%hx", c->id, c->rem.ip, c->rem.port)); - LIST_ADD_HEAD(struct mg_connection, &lsn->mgr->conns, c); - c->fd = (void *) (size_t) mg_ntohl(pkt->tcp->ack); - 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; -} - -static void read_conn(struct mg_connection *c, struct pkt *pkt) { - struct tcpstate *s = (struct tcpstate *) (c + 1); - 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) { - } 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 if (mg_ntohl(pkt->tcp->seq) != s->ack) { - mg_error(c, "oob: %x %x", mg_ntohl(pkt->tcp->seq), s->ack); - } else { - s->ack = mg_htonl(pkt->tcp->seq) + pkt->pay.len; - memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len); - c->recv.len += pkt->pay.len; - struct mg_str evd = mg_str_n((char *) pkt->pay.buf, pkt->pay.len); - mg_call(c, MG_EV_READ, &evd); -#if 0 - // Send ACK immediately - tx_tcp(ifp, c->rem.ip, TH_ACK, c->loc.port, c->rem.port, mg_htonl(s->seq), - mg_htonl(s->ack), NULL, 0); -#endif - } -} - -static void rx_tcp(struct mip_if *ifp, struct pkt *pkt) { - struct mg_connection *c = getpeer(ifp->mgr, pkt, false); -#if 0 - MG_INFO(("%lu %hhu %d", c ? c->id : 0, pkt->tcp->flags, (int) pkt->pay.len)); -#endif - if (c != NULL) { -#if 0 - MG_DEBUG(("%lu %d %lx:%hx -> %lx:%hx", c->id, (int) pkt->raw.len, - pkt->ip->src, pkt->tcp->sport, pkt->ip->dst, pkt->tcp->dport)); - hexdump(pkt->pay.buf, pkt->pay.len); -#endif - read_conn(c, pkt); - } 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_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 { - // MG_DEBUG(("dropped silently..")); - } -} - -static void rx_ip(struct mip_if *ifp, struct pkt *pkt) { - // MG_DEBUG(("IP %d", (int) pkt->pay.len)); - 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; - // MG_DEBUG((" UDP %u %u -> %u\n", len, NET16(udp->sport), - // NET16(udp->dport))); - mkpay(pkt, pkt->udp + 1); - if (pkt->udp->dport == NET16(68)) { - pkt->dhcp = (struct dhcp *) (pkt->udp + 1); - mkpay(pkt, pkt->dhcp + 1); - rx_dhcp(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); - rx_tcp(ifp, pkt); - } -} - -static void rx_ip6(struct mip_if *ifp, struct pkt *pkt) { - // MG_DEBUG(("IP %d\n", (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->ip->proto == 17) { - pkt->udp = (struct udp *) (pkt->ip6 + 1); - if (pkt->pay.len < sizeof(*pkt->udp)) return; - // MG_DEBUG((" UDP %u %u -> %u\n", len, NET16(udp->sport), - // NET16(udp->dport))); - mkpay(pkt, pkt->udp + 1); - } -} - -static void mip_rx(struct mip_if *ifp, void *buf, size_t len) { - const uint8_t broadcast[] = {255, 255, 255, 255, 255, 255}; - struct pkt pkt = {.raw = {.buf = (uint8_t *) buf, .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 == NET16(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 == NET16(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 == NET16(0x800)) { - pkt.ip = (struct ip *) (pkt.eth + 1); - 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\n", NET16(pkt.eth->type))); - } -} - -static void mip_poll(struct mip_if *ifp, uint64_t uptime_ms) { - ifp->curtime = uptime_ms; - - if (ifp->ip == 0 && uptime_ms > ifp->timer) { - tx_dhcp_discover(ifp); // If IP not configured, send DHCP - ifp->timer = uptime_ms + 1000; // with some interval - } else if (ifp->use_dhcp == false && uptime_ms > ifp->timer && - arp_cache_find(ifp, ifp->gw) == NULL) { - arp_ask(ifp, ifp->gw); // If GW's MAC address in not in ARP cache - ifp->timer = uptime_ms + 1000; // send ARP who-has request - } - - // Handle physical interface up/down status - if (ifp->driver->status) { - bool up = ifp->driver->status(ifp->driver->data); - bool current = ifp->state != MIP_STATE_DOWN; - if (up != current) { - ifp->state = up == false ? MIP_STATE_DOWN - : ifp->use_dhcp ? MIP_STATE_UP - : MIP_STATE_READY; - if (!up && ifp->use_dhcp) ifp->ip = 0; - onstatechange(ifp); - } - } - - // Read data from the network - for (;;) { - size_t len = ifp->queue.len > 0 ? q_read(&ifp->queue, ifp->rx.buf) - : ifp->driver->rx(ifp->rx.buf, ifp->rx.len, - ifp->driver->data); - if (len == 0) break; - mip_rx(ifp, ifp->rx.buf, len); - } -} - -// 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 -static void on_rx(void *buf, size_t len, void *userdata) { - struct mip_if *ifp = (struct mip_if *) userdata; - if (!q_write(&ifp->queue, buf, len)) MG_ERROR(("dropped %d", (int) len)); -} - -void mip_init(struct mg_mgr *mgr, struct mip_ipcfg *ipcfg, - struct mip_driver *driver) { - size_t maxpktsize = 1500, qlen = driver->rxcb ? 1024 * 16 : 0; - struct mip_if *ifp = - (struct mip_if *) calloc(1, sizeof(*ifp) + 2 * maxpktsize + qlen); - memcpy(ifp->mac, ipcfg->mac, sizeof(ifp->mac)); - ifp->use_dhcp = ipcfg->ip == 0; - ifp->ip = ipcfg->ip, ifp->mask = ipcfg->mask, ifp->gw = ipcfg->gw; - ifp->rx.buf = (uint8_t *) (ifp + 1), ifp->rx.len = maxpktsize; - ifp->tx.buf = ifp->rx.buf + maxpktsize, ifp->tx.len = maxpktsize; - ifp->driver = driver; - ifp->mgr = mgr; - ifp->queue.buf = ifp->tx.buf + maxpktsize; - ifp->queue.len = qlen; - if (driver->init) driver->init(driver->data); - if (driver->rxcb) driver->rxcb(on_rx, ifp); - mgr->priv = ifp; - mgr->extraconnsize = sizeof(struct tcpstate); -} - -void mg_connect_resolved(struct mg_connection *c) { - struct mip_if *ifp = (struct mip_if *) c->mgr->priv; - if (ifp->eport < MIP_ETHEMERAL_PORT) ifp->eport = MIP_ETHEMERAL_PORT; - if (c->is_udp) { - c->loc.ip = ifp->ip; - c->loc.port = mg_htons(ifp->eport++); - MG_DEBUG(("%lu %08lx.%hu->%08lx.%hu", c->id, mg_ntohl(c->loc.ip), - mg_ntohs(c->loc.port), mg_ntohl(c->rem.ip), - mg_ntohs(c->rem.port))); - mg_call(c, MG_EV_RESOLVE, NULL); - mg_call(c, MG_EV_CONNECT, NULL); - } else { - mg_error(c, "Not implemented"); - } - c->is_resolving = 0; -} - -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) { - struct mip_if *ifp = (struct mip_if *) c->mgr->priv; - struct tcpstate *s = (struct tcpstate *) (c + 1); - size_t sent, n = c->send.len, hdrlen = 14 + 24 /*max IP*/ + 60 /*max TCP*/; - if (n + hdrlen > ifp->tx.len) n = ifp->tx.len - hdrlen; - sent = tx_tcp(ifp, c->rem.ip, TH_PUSH | TH_ACK, c->loc.port, c->rem.port, - mg_htonl(s->seq), mg_htonl(s->ack), c->send.buf, n); - if (sent > 0) { - mg_iobuf_del(&c->send, 0, n); - s->seq += n; - mg_call(c, MG_EV_WRITE, &n); - } -} - -static void fin_conn(struct mg_connection *c) { - struct mip_if *ifp = (struct mip_if *) c->mgr->priv; - struct tcpstate *s = (struct tcpstate *) (c + 1); - tx_tcp(ifp, c->rem.ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port, - mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); -} - -void mg_mgr_poll(struct mg_mgr *mgr, int ms) { - struct mg_connection *c, *tmp; - uint64_t now = mg_millis(); - mip_poll((struct mip_if *) mgr->priv, now); - mg_timer_poll(&mgr->timers, now); - for (c = mgr->conns; c != NULL; c = tmp) { - tmp = c->next; - if (c->send.len > 0) write_conn(c); - if (c->is_draining && c->send.len == 0) c->is_closing = 1; - if (c->is_closing) { - if (c->is_udp == false && c->is_listening == false) fin_conn(c); - mg_close_conn(c); - } - } - (void) ms; -} - -bool mg_send(struct mg_connection *c, const void *buf, size_t len) { - struct mip_if *ifp = (struct mip_if *) c->mgr->priv; - bool res = false; - if (ifp->ip == 0) { - mg_error(c, "net down"); - } else if (c->is_udp) { - tx_udp(ifp, ifp->ip, c->loc.port, c->rem.ip, c->rem.port, buf, len); - res = true; - } else { - // tx_tdp(ifp, ifp->ip, c->loc.port, c->rem.ip, c->rem.port, buf, len); - return mg_iobuf_add(&c->send, c->send.len, buf, len, MG_IO_SIZE); - } - return res; -} -#endif // MG_ENABLE_MIP - #ifdef MG_ENABLE_LINES #line 1 "src/mqtt.c" #endif @@ -6413,3 +5621,948 @@ size_t mg_ws_wrap(struct mg_connection *c, size_t len, int op) { return c->send.len; } + +#ifdef MG_ENABLE_LINES +#line 1 "mip/driver_stm32.c" +#endif +// Copyright (c) 2022 Cesanta Software Limited +// All rights reserved + + + +#if MG_ENABLE_MIP + +struct stm32_eth { + 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; +}; +#define ETH ((struct stm32_eth *) (uintptr_t) 0x40028000) + +#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 void (*s_rx)(void *, size_t, void *); // Recv callback +static void *s_rxdata; // Recv callback data +static void *s_userdata; // Driver data +enum { PHY_ADDR = 0, PHY_BCR = 0, PHY_BSR = 1 }; // PHY constants + +static inline void spin(volatile uint32_t count) { + while (count--) asm("nop"); +} + +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)) spin(1); + 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)) spin(1); +} + +static void mip_driver_stm32_init(void *userdata) { + s_userdata = userdata; + + // 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) spin(1); // Wait until done + // 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->MACMIIAR = 4 << 2; // MDC clock 150-216 MHz, 38.8.1 + 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 +} + +static void mip_driver_stm32_setrx(void (*rx)(void *, size_t, void *), + void *rxdata) { + s_rx = rx; + s_rxdata = rxdata; +} + +static uint32_t s_txno; +static size_t mip_driver_stm32_tx(const void *buf, size_t len, void *userdata) { + if (len > sizeof(s_txbuf[s_txno])) { + printf("%s: frame too big, %ld\n", __func__, (long) len); + len = 0; // Frame is too big + } else if ((s_txdesc[s_txno][0] & BIT(31))) { + printf("%s: no free descr\n", __func__); + 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; + } + uint32_t sr = ETH->DMASR; + if (sr & BIT(2)) ETH->DMASR = BIT(2), ETH->DMATPDR = 0; // Resume + if (sr & BIT(5)) ETH->DMASR = BIT(5), ETH->DMATPDR = 0; // if busy + if (len == 0) printf("E: D0 %lx SR %lx\n", (long) s_txdesc[0][0], (long) sr); + return len; + (void) userdata; +} + +static bool mip_driver_stm32_status(void *userdata) { + uint32_t bsr = eth_read_phy(PHY_ADDR, PHY_BSR); + return bsr & BIT(2) ? 1 : 0; + (void) userdata; +} + +void ETH_IRQHandler(void); +void ETH_IRQHandler(void) { + volatile uint32_t sr = ETH->DMASR; + if (sr & BIT(6)) { // Frame received, loop + for (uint32_t i = 0; i < ETH_DESC_CNT; i++) { + if (s_rxdesc[i][0] & BIT(31)) continue; + uint32_t len = ((s_rxdesc[i][0] >> 16) & (BIT(14) - 1)); + // printf("%lx %lu %lx %lx\n", i, len, s_rxdesc[i][0], sr); + if (s_rx != NULL) s_rx(s_rxbuf[i], len > 4 ? len - 4 : len, s_rxdata); + s_rxdesc[i][0] = BIT(31); + } + } + if (sr & BIT(7)) ETH->DMARPDR = 0; // Resume RX + ETH->DMASR = sr & ~(BIT(2) | BIT(7)); // Clear status +} + +struct mip_driver mip_driver_stm32 = {.init = mip_driver_stm32_init, + .tx = mip_driver_stm32_tx, + .rxcb = mip_driver_stm32_setrx, + .status = mip_driver_stm32_status}; +#endif // MG_ENABLE_MIP + +#ifdef MG_ENABLE_LINES +#line 1 "mip/mip.c" +#endif + + + +#if MG_ENABLE_MIP +#include +#define MIP_ETHEMERAL_PORT 49152 +#define _packed __attribute__((packed)) +#define U16(ptr) ((((uint16_t) (ptr)[0]) << 8) | (ptr)[1]) +#define NET16(x) __builtin_bswap16(x) +#define NET32(x) __builtin_bswap32(x) +#define PDIFF(a, b) ((size_t) (((char *) (b)) - ((char *) (a)))) + +#ifndef MIP_ARP_ENTRIES +#define MIP_ARP_ENTRIES 5 // Number of ARP cache entries. Maximum 21 +#endif +#define MIP_ARP_CS (2 + 12 * MIP_ARP_ENTRIES) // ARP cache size + +struct str { + uint8_t *buf; + size_t len; +}; + +// Receive queue - single producer, single consumer queue. Interrupt-based +// drivers copy received frames to the queue in interrupt context. mip_poll() +// function runs in event loop context, reads from the queue +struct queue { + uint8_t *buf; + size_t len; + volatile _Atomic size_t tail, head; +}; + +// Network interface +struct mip_if { + uint8_t mac[6]; // MAC address. Must be set to a valid MAC + uint32_t ip, mask, gw; // IP address, mask, default gateway. Can be 0 + struct str rx; // Output (TX) buffer + struct str tx; // Input (RX) buffer + bool use_dhcp; // Enable DCHP + struct mip_driver *driver; // Low level driver + struct mg_mgr *mgr; // Mongoose event manager + + // Internal state, user can use it but should not change it + uint64_t curtime; // Last poll timestamp in millis + uint64_t timer; // Timer + uint8_t arp_cache[MIP_ARP_CS]; // Each entry is 12 bytes + uint16_t eport; // Next ephemeral port + int 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 + struct queue queue; // Receive queue +}; + +struct lcp { + uint8_t addr, ctrl, proto[2], code, id, len[2]; +} _packed; + +struct eth { + uint8_t dst[6]; // Destination MAC address + uint8_t src[6]; // Source MAC address + uint16_t type; // Ethernet type +} _packed; + +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 +} _packed; + +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 +} _packed; + +struct icmp { + uint8_t type; + uint8_t code; + uint16_t csum; +} _packed; + +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 +} _packed; + +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 +} _packed; + +struct udp { + uint16_t sport; // Source port + uint16_t dport; // Destination port + uint16_t len; // UDP length + uint16_t csum; // UDP checksum +} _packed; + +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]; +} _packed; + +struct pkt { + struct str raw; // Raw packet data + struct 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 i = 0, left = q->len - head; + for (; i < len && i < left; i++) q->buf[head + i] = buf[i]; + for (; i < len; i++) q->buf[i - left] = buf[i]; +} + +static void q_copyout(struct queue *q, uint8_t *buf, size_t len, size_t tail) { + size_t i = 0, left = q->len - tail; + for (; i < len && i < left; i++) buf[i] = q->buf[tail + i]; + for (; i < len; i++) buf[i] = q->buf[i - 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; + 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 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 str mkstr(void *buf, size_t len) { + struct str str = {(uint8_t *) buf, len}; + return str; +} + +static void mkpay(struct pkt *pkt, void *p) { + pkt->pay = mkstr(p, (size_t) (&pkt->raw.buf[pkt->raw.len] - (uint8_t *) 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 NET16(~sum & 0xffff); +} + +static uint16_t ipcsum(const void *buf, size_t len) { + uint32_t sum = csumup(0, buf, len); + return csumfin(sum); +} + +// ARP cache is organised as a doubly linked list. A successful cache lookup +// moves an entry to the head of the list. New entries are added by replacing +// the last entry in the list with a new IP/MAC. +// ARP cache format: | prev | next | Entry0 | Entry1 | .... | EntryN | +// ARP entry format: | prev | next | IP (4bytes) | MAC (6bytes) | +// prev and next are 1-byte offsets in the cache, so cache size is max 256 bytes +// ARP entry size is 12 bytes +static void arp_cache_init(uint8_t *p, int n, int size) { + for (int i = 0; i < n; i++) p[2 + i * size] = (uint8_t) (2 + (i - 1) * size); + for (int i = 0; i < n; i++) p[3 + i * size] = (uint8_t) (2 + (i + 1) * size); + p[0] = p[2] = (uint8_t) (2 + (n - 1) * size); + p[1] = p[3 + (n - 1) * size] = 2; +} + +static uint8_t *arp_cache_find(struct mip_if *ifp, uint32_t ip) { + uint8_t *p = ifp->arp_cache; + if (ip == 0) return NULL; + if (p[0] == 0 || p[1] == 0) arp_cache_init(p, MIP_ARP_ENTRIES, 12); + for (uint8_t i = 0, j = p[1]; i < MIP_ARP_ENTRIES; i++, j = p[j + 1]) { + if (memcmp(p + j + 2, &ip, sizeof(ip)) == 0) { + p[1] = j, p[0] = p[j]; // Found entry! Point list head to us + // MG_DEBUG(("ARP find: %#lx @ %x:%x:%x:%x:%x:%x\n", (long) ip, p[j + 6], + // p[j + 7], p[j + 8], p[j + 9], p[j + 10], p[j + 11])); + return p + j + 6; // And return MAC address + } + } + return NULL; +} + +static void arp_cache_add(struct mip_if *ifp, uint32_t ip, uint8_t mac[6]) { + uint8_t *p = ifp->arp_cache; + if (ip == 0 || ip == ~0U) return; // Bad IP + if (arp_cache_find(ifp, ip) != NULL) return; // Already exists, do nothing + memcpy(p + p[0] + 2, &ip, sizeof(ip)); // Replace last entry: IP address + memcpy(p + p[0] + 6, mac, 6); // And MAC address + p[1] = p[0], p[0] = p[p[1]]; // Point list head to us + // MG_DEBUG(("ARP cache: added %#lx @ %x:%x:%x:%x:%x:%x\n", (long) ip, mac[0], + // mac[1], mac[2], mac[3], mac[4], mac[5])); +} + +static void arp_ask(struct mip_if *ifp, uint32_t ip) { + struct eth *eth = (struct eth *) ifp->tx.buf; + struct arp *arp = (struct arp *) (eth + 1); + memset(eth->dst, 255, sizeof(eth->dst)); + memcpy(eth->src, ifp->mac, sizeof(eth->src)); + eth->type = NET16(0x806); + memset(arp, 0, sizeof(*arp)); + arp->fmt = NET16(1), arp->pro = NET16(0x800), arp->hlen = 6, arp->plen = 4; + arp->op = NET16(1), arp->tpa = ip, arp->spa = ifp->ip; + memcpy(arp->sha, ifp->mac, sizeof(arp->sha)); + ifp->driver->tx(eth, PDIFF(eth, arp + 1), ifp->driver->data); +} + +static void onstatechange(struct mip_if *ifp) { + if (ifp->state == MIP_STATE_READY) { + char buf[40]; + struct mg_addr addr = {.ip = ifp->ip}; + MG_INFO(("READY, IP: %s", mg_ntoa(&addr, buf, sizeof(buf)))); + arp_ask(ifp, ifp->gw); + } else if (ifp->state == MIP_STATE_UP) { + MG_ERROR(("Network up")); + } else if (ifp->state == MIP_STATE_DOWN) { + MG_ERROR(("Network down")); + } +} + +static struct ip *tx_ip(struct mip_if *ifp, uint8_t proto, uint32_t ip_src, + uint32_t ip_dst, size_t plen) { + struct eth *eth = (struct eth *) ifp->tx.buf; + struct ip *ip = (struct ip *) (eth + 1); + uint8_t *mac = arp_cache_find(ifp, ip_dst); // Dst IP in ARP cache ? + if (!mac) mac = arp_cache_find(ifp, ifp->gw); // No, use gateway + if (mac) memcpy(eth->dst, mac, sizeof(eth->dst)); // Found? Use it + if (!mac) memset(eth->dst, 255, sizeof(eth->dst)); // No? Use broadcast + memcpy(eth->src, ifp->mac, sizeof(eth->src)); // TODO(cpq): ARP lookup + eth->type = NET16(0x800); + memset(ip, 0, sizeof(*ip)); + ip->ver = 0x45; // Version 4, header length 5 words + ip->frag = 0x40; // Don't fragment + ip->len = NET16((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; +} + +void tx_udp(struct mip_if *ifp, 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, 17, ip_src, ip_dst, len + sizeof(struct udp)); + struct udp *udp = (struct udp *) (ip + 1); + udp->sport = sport; + udp->dport = dport; + udp->len = NET16((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 += ip->proto + sizeof(*udp) + len; + udp->csum = csumfin(cs); + memmove(udp + 1, buf, len); + // MG_DEBUG(("UDP LEN %d %d\n", (int) len, (int) ifp->frame_len)); + ifp->driver->tx(ifp->tx.buf, + sizeof(struct eth) + sizeof(*ip) + sizeof(*udp) + len, + ifp->driver->data); +} + +static void tx_dhcp(struct mip_if *ifp, uint32_t src, uint32_t dst, + uint8_t *opts, size_t optslen) { + struct dhcp dhcp = {.op = 1, + .htype = 1, + .hlen = 6, + .ciaddr = src, + .magic = NET32(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, src, NET16(68), dst, NET16(67), &dhcp, sizeof(dhcp)); +} + +static void tx_dhcp_request(struct mip_if *ifp, uint32_t src, uint32_t 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, &dst, sizeof(dst)); + memcpy(opts + 20, &src, sizeof(src)); + tx_dhcp(ifp, src, dst, opts, sizeof(opts)); +} + +static void tx_dhcp_discover(struct mip_if *ifp) { + uint8_t opts[] = { + 53, 1, 1, // Type: DHCP discover + 55, 2, 1, 3, // Parameters: ip, mask + 255 // End of options + }; + tx_dhcp(ifp, 0, 0xffffffff, opts, sizeof(opts)); +} + +static void rx_arp(struct mip_if *ifp, struct pkt *pkt) { + // MG_DEBUG(("ARP op %d %#x %#x\n", NET16(arp->op), arp->spa, arp->tpa)); + if (pkt->arp->op == NET16(1) && pkt->arp->tpa == ifp->ip) { + // ARP request. Make a response, then send + struct eth *eth = (struct eth *) ifp->tx.buf; + 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 = NET16(0x806); + *arp = *pkt->arp; + arp->op = NET16(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 response: we're %#lx", (long) ifp->ip)); + ifp->driver->tx(ifp->tx.buf, PDIFF(eth, arp + 1), ifp->driver->data); + } else if (pkt->arp->op == NET16(2)) { + if (memcmp(pkt->arp->tha, ifp->mac, sizeof(pkt->arp->tha)) != 0) return; + // MG_INFO(("ARP RESPONSE")); + arp_cache_add(ifp, pkt->arp->spa, pkt->arp->sha); + } +} + +static void rx_icmp(struct mip_if *ifp, struct pkt *pkt) { + // MG_DEBUG(("ICMP %d\n", (int) len)); + if (pkt->icmp->type == 8 && pkt->ip->dst == ifp->ip) { + struct ip *ip = tx_ip(ifp, 1, ifp->ip, pkt->ip->src, + sizeof(struct icmp) + pkt->pay.len); + struct icmp *icmp = (struct icmp *) (ip + 1); + memset(icmp, 0, sizeof(*icmp)); // Important - set csum to 0 + memcpy(icmp + 1, pkt->pay.buf, pkt->pay.len); + icmp->csum = ipcsum(icmp, sizeof(*icmp) + pkt->pay.len); + ifp->driver->tx(ifp->tx.buf, PDIFF(ifp->tx.buf, icmp + 1) + pkt->pay.len, + ifp->driver->data); + } +} + +static void rx_dhcp(struct mip_if *ifp, struct pkt *pkt) { + uint32_t ip = 0, gw = 0, mask = 0; + uint8_t *p = pkt->dhcp->options, *end = &pkt->raw.buf[pkt->raw.len]; + if (end < (uint8_t *) (pkt->dhcp + 1)) return; + // MG_DEBUG(("DHCP %u\n", (unsigned) pkt->raw.len)); + while (p < end && p[0] != 255) { + if (p[0] == 1 && p[1] == sizeof(ifp->mask)) { + memcpy(&mask, p + 2, sizeof(mask)); + // MG_DEBUG(("MASK %x\n", mask)); + } else if (p[0] == 3 && p[1] == sizeof(ifp->gw)) { + memcpy(&gw, p + 2, sizeof(gw)); + ip = pkt->dhcp->yiaddr; + // MG_DEBUG(("IP %x GW %x\n", ip, gw)); + } + p += p[1] + 2; + } + if (ip && mask && gw && ifp->ip == 0) { + // MG_DEBUG(("DHCP offer ip %#08lx mask %#08lx gw %#08lx\n", + // (long) ip, (long) mask, (long) gw)); + arp_cache_add(ifp, pkt->dhcp->siaddr, ((struct eth *) pkt->raw.buf)->src); + ifp->ip = ip, ifp->gw = gw, ifp->mask = mask; + ifp->state = MIP_STATE_READY; + onstatechange(ifp); + tx_dhcp_request(ifp, ip, pkt->dhcp->siaddr); + } +} + +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_udp(struct mip_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.buf, pkt->pay.len); + c->recv.len += pkt->pay.len; + struct mg_str evd = mg_str_n((char *) pkt->pay.buf, pkt->pay.len); + mg_call(c, MG_EV_READ, &evd); + } + } +} + +struct tcpstate { + uint32_t seq, ack; + time_t expire; +}; + +static size_t tx_tcp(struct mip_if *ifp, 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, 6, ifp->ip, dst_ip, sizeof(struct tcp) + len); + struct tcp *tcp = (struct tcp *) (ip + 1); + memset(tcp, 0, sizeof(*tcp)); + 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 ifp->driver->tx(ifp->tx.buf, PDIFF(ifp->tx.buf, tcp + 1) + len, + ifp->driver->data); +} + +static size_t tx_tcp_pkt(struct mip_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->ip->src, flags, pkt->tcp->dport, pkt->tcp->sport, seq, + mg_htonl(mg_ntohl(pkt->tcp->seq) + delta), buf, len); +} + +static struct mg_connection *accept_conn(struct mg_connection *lsn, + struct pkt *pkt) { + struct mg_connection *c = mg_alloc_conn(lsn->mgr); + struct tcpstate *s = (struct tcpstate *) (c + 1); + s->seq = mg_ntohl(pkt->tcp->ack), s->ack = mg_ntohl(pkt->tcp->seq); + c->rem.ip = pkt->ip->src; + c->rem.port = pkt->tcp->sport; + MG_DEBUG(("%lu accepted %lx:%hx", c->id, c->rem.ip, c->rem.port)); + LIST_ADD_HEAD(struct mg_connection, &lsn->mgr->conns, c); + c->fd = (void *) (size_t) mg_ntohl(pkt->tcp->ack); + 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; +} + +static void read_conn(struct mg_connection *c, struct pkt *pkt) { + struct tcpstate *s = (struct tcpstate *) (c + 1); + 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) { + } 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 if (mg_ntohl(pkt->tcp->seq) != s->ack) { + mg_error(c, "oob: %x %x", mg_ntohl(pkt->tcp->seq), s->ack); + } else { + s->ack = mg_htonl(pkt->tcp->seq) + pkt->pay.len; + memcpy(&c->recv.buf[c->recv.len], pkt->pay.buf, pkt->pay.len); + c->recv.len += pkt->pay.len; + struct mg_str evd = mg_str_n((char *) pkt->pay.buf, pkt->pay.len); + mg_call(c, MG_EV_READ, &evd); +#if 0 + // Send ACK immediately + tx_tcp(ifp, c->rem.ip, TH_ACK, c->loc.port, c->rem.port, mg_htonl(s->seq), + mg_htonl(s->ack), NULL, 0); +#endif + } +} + +static void rx_tcp(struct mip_if *ifp, struct pkt *pkt) { + struct mg_connection *c = getpeer(ifp->mgr, pkt, false); +#if 0 + MG_INFO(("%lu %hhu %d", c ? c->id : 0, pkt->tcp->flags, (int) pkt->pay.len)); +#endif + if (c != NULL) { +#if 0 + MG_DEBUG(("%lu %d %lx:%hx -> %lx:%hx", c->id, (int) pkt->raw.len, + pkt->ip->src, pkt->tcp->sport, pkt->ip->dst, pkt->tcp->dport)); + hexdump(pkt->pay.buf, pkt->pay.len); +#endif + read_conn(c, pkt); + } 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_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 { + // MG_DEBUG(("dropped silently..")); + } +} + +static void rx_ip(struct mip_if *ifp, struct pkt *pkt) { + // MG_DEBUG(("IP %d", (int) pkt->pay.len)); + 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; + // MG_DEBUG((" UDP %u %u -> %u\n", len, NET16(udp->sport), + // NET16(udp->dport))); + mkpay(pkt, pkt->udp + 1); + if (pkt->udp->dport == NET16(68)) { + pkt->dhcp = (struct dhcp *) (pkt->udp + 1); + mkpay(pkt, pkt->dhcp + 1); + rx_dhcp(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); + rx_tcp(ifp, pkt); + } +} + +static void rx_ip6(struct mip_if *ifp, struct pkt *pkt) { + // MG_DEBUG(("IP %d\n", (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->ip->proto == 17) { + pkt->udp = (struct udp *) (pkt->ip6 + 1); + if (pkt->pay.len < sizeof(*pkt->udp)) return; + // MG_DEBUG((" UDP %u %u -> %u\n", len, NET16(udp->sport), + // NET16(udp->dport))); + mkpay(pkt, pkt->udp + 1); + } +} + +static void mip_rx(struct mip_if *ifp, void *buf, size_t len) { + const uint8_t broadcast[] = {255, 255, 255, 255, 255, 255}; + struct pkt pkt = {.raw = {.buf = (uint8_t *) buf, .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 == NET16(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 == NET16(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 == NET16(0x800)) { + pkt.ip = (struct ip *) (pkt.eth + 1); + 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\n", NET16(pkt.eth->type))); + } +} + +static void mip_poll(struct mip_if *ifp, uint64_t uptime_ms) { + ifp->curtime = uptime_ms; + + if (ifp->ip == 0 && uptime_ms > ifp->timer) { + tx_dhcp_discover(ifp); // If IP not configured, send DHCP + ifp->timer = uptime_ms + 1000; // with some interval + } else if (ifp->use_dhcp == false && uptime_ms > ifp->timer && + arp_cache_find(ifp, ifp->gw) == NULL) { + arp_ask(ifp, ifp->gw); // If GW's MAC address in not in ARP cache + ifp->timer = uptime_ms + 1000; // send ARP who-has request + } + + // Handle physical interface up/down status + if (ifp->driver->status) { + bool up = ifp->driver->status(ifp->driver->data); + bool current = ifp->state != MIP_STATE_DOWN; + if (up != current) { + ifp->state = up == false ? MIP_STATE_DOWN + : ifp->use_dhcp ? MIP_STATE_UP + : MIP_STATE_READY; + if (!up && ifp->use_dhcp) ifp->ip = 0; + onstatechange(ifp); + } + } + + // Read data from the network + for (;;) { + size_t len = ifp->queue.len > 0 ? q_read(&ifp->queue, ifp->rx.buf) + : ifp->driver->rx(ifp->rx.buf, ifp->rx.len, + ifp->driver->data); + if (len == 0) break; + mip_rx(ifp, ifp->rx.buf, len); + } +} + +// 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 +static void on_rx(void *buf, size_t len, void *userdata) { + struct mip_if *ifp = (struct mip_if *) userdata; + if (!q_write(&ifp->queue, buf, len)) MG_ERROR(("dropped %d", (int) len)); +} + +void mip_init(struct mg_mgr *mgr, struct mip_ipcfg *ipcfg, + struct mip_driver *driver) { + size_t maxpktsize = 1500, qlen = driver->rxcb ? 1024 * 16 : 0; + struct mip_if *ifp = + (struct mip_if *) calloc(1, sizeof(*ifp) + 2 * maxpktsize + qlen); + memcpy(ifp->mac, ipcfg->mac, sizeof(ifp->mac)); + ifp->use_dhcp = ipcfg->ip == 0; + ifp->ip = ipcfg->ip, ifp->mask = ipcfg->mask, ifp->gw = ipcfg->gw; + ifp->rx.buf = (uint8_t *) (ifp + 1), ifp->rx.len = maxpktsize; + ifp->tx.buf = ifp->rx.buf + maxpktsize, ifp->tx.len = maxpktsize; + ifp->driver = driver; + ifp->mgr = mgr; + ifp->queue.buf = ifp->tx.buf + maxpktsize; + ifp->queue.len = qlen; + if (driver->init) driver->init(driver->data); + if (driver->rxcb) driver->rxcb(on_rx, ifp); + mgr->priv = ifp; + mgr->extraconnsize = sizeof(struct tcpstate); +} + +void mg_connect_resolved(struct mg_connection *c) { + struct mip_if *ifp = (struct mip_if *) c->mgr->priv; + if (ifp->eport < MIP_ETHEMERAL_PORT) ifp->eport = MIP_ETHEMERAL_PORT; + if (c->is_udp) { + c->loc.ip = ifp->ip; + c->loc.port = mg_htons(ifp->eport++); + MG_DEBUG(("%lu %08lx.%hu->%08lx.%hu", c->id, mg_ntohl(c->loc.ip), + mg_ntohs(c->loc.port), mg_ntohl(c->rem.ip), + mg_ntohs(c->rem.port))); + mg_call(c, MG_EV_RESOLVE, NULL); + mg_call(c, MG_EV_CONNECT, NULL); + } else { + mg_error(c, "Not implemented"); + } + c->is_resolving = 0; +} + +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) { + struct mip_if *ifp = (struct mip_if *) c->mgr->priv; + struct tcpstate *s = (struct tcpstate *) (c + 1); + size_t sent, n = c->send.len, hdrlen = 14 + 24 /*max IP*/ + 60 /*max TCP*/; + if (n + hdrlen > ifp->tx.len) n = ifp->tx.len - hdrlen; + sent = tx_tcp(ifp, c->rem.ip, TH_PUSH | TH_ACK, c->loc.port, c->rem.port, + mg_htonl(s->seq), mg_htonl(s->ack), c->send.buf, n); + if (sent > 0) { + mg_iobuf_del(&c->send, 0, n); + s->seq += n; + mg_call(c, MG_EV_WRITE, &n); + } +} + +static void fin_conn(struct mg_connection *c) { + struct mip_if *ifp = (struct mip_if *) c->mgr->priv; + struct tcpstate *s = (struct tcpstate *) (c + 1); + tx_tcp(ifp, c->rem.ip, TH_FIN | TH_ACK, c->loc.port, c->rem.port, + mg_htonl(s->seq), mg_htonl(s->ack), NULL, 0); +} + +void mg_mgr_poll(struct mg_mgr *mgr, int ms) { + struct mg_connection *c, *tmp; + uint64_t now = mg_millis(); + mip_poll((struct mip_if *) mgr->priv, now); + mg_timer_poll(&mgr->timers, now); + for (c = mgr->conns; c != NULL; c = tmp) { + tmp = c->next; + if (c->send.len > 0) write_conn(c); + if (c->is_draining && c->send.len == 0) c->is_closing = 1; + if (c->is_closing) { + if (c->is_udp == false && c->is_listening == false) fin_conn(c); + mg_close_conn(c); + } + } + (void) ms; +} + +bool mg_send(struct mg_connection *c, const void *buf, size_t len) { + struct mip_if *ifp = (struct mip_if *) c->mgr->priv; + bool res = false; + if (ifp->ip == 0) { + mg_error(c, "net down"); + } else if (c->is_udp) { + tx_udp(ifp, ifp->ip, c->loc.port, c->rem.ip, c->rem.port, buf, len); + res = true; + } else { + // tx_tdp(ifp, ifp->ip, c->loc.port, c->rem.ip, c->rem.port, buf, len); + return mg_iobuf_add(&c->send, c->send.len, buf, len, MG_IO_SIZE); + } + return res; +} +#endif // MG_ENABLE_MIP diff --git a/mongoose.h b/mongoose.h index 0511b944..90fdd09b 100644 --- a/mongoose.h +++ b/mongoose.h @@ -1290,6 +1290,23 @@ size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs, +#ifndef MG_JSON_MAX_DEPTH +#define MG_JSON_MAX_DEPTH 30 +#endif + +// Error return values - negative. Successful returns are >= 0 +enum { MG_JSON_TOO_DEEP = -1, MG_JSON_INVALID = -2, MG_JSON_NOT_FOUND = -3 }; +int mg_json_get(const char *buf, int len, const char *path, int *toklen); + +bool mg_json_get_num(struct mg_str json, const char *path, double *v); +bool mg_json_get_bool(struct mg_str json, const char *path, bool *v); +char *mg_json_get_str(struct mg_str json, const char *path); +char *mg_json_get_hex(struct mg_str json, const char *path); + + + + + struct mip_driver { void *data; // Driver-specific data void (*init)(void *data); // Initialise driver @@ -1307,22 +1324,7 @@ struct mip_ipcfg { void mip_init(struct mg_mgr *, struct mip_ipcfg *, struct mip_driver *); - - - - -#ifndef MG_JSON_MAX_DEPTH -#define MG_JSON_MAX_DEPTH 30 -#endif - -// Error return values - negative. Successful returns are >= 0 -enum { MG_JSON_TOO_DEEP = -1, MG_JSON_INVALID = -2, MG_JSON_NOT_FOUND = -3 }; -int mg_json_get(const char *buf, int len, const char *path, int *toklen); - -bool mg_json_get_num(struct mg_str json, const char *path, double *v); -bool mg_json_get_bool(struct mg_str json, const char *path, bool *v); -char *mg_json_get_str(struct mg_str json, const char *path); -char *mg_json_get_hex(struct mg_str json, const char *path); +extern struct mip_driver mip_driver_stm32; #ifdef __cplusplus }