From 15fea3a1ff9b7437e0fed54bd4a16c1708faf321 Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Thu, 5 May 2022 12:32:22 +1000 Subject: [PATCH] rp2: Integrate lwIP network stack. Signed-off-by: Andrew Leech --- extmod/extmod.cmake | 56 +++++++++++++++++++ extmod/modnetwork.h | 1 + ports/rp2/CMakeLists.txt | 23 ++++++++ ports/rp2/lwip_inc/arch/cc.h | 10 ++++ ports/rp2/lwip_inc/arch/sys_arch.h | 1 + ports/rp2/lwip_inc/lwipopts.h | 56 +++++++++++++++++++ ports/rp2/main.c | 27 ++++++++- ports/rp2/mpconfigport.h | 8 +++ ports/rp2/mpnetworkport.c | 90 ++++++++++++++++++++++++++++++ 9 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 ports/rp2/lwip_inc/arch/cc.h create mode 100644 ports/rp2/lwip_inc/arch/sys_arch.h create mode 100644 ports/rp2/lwip_inc/lwipopts.h create mode 100644 ports/rp2/mpnetworkport.c diff --git a/extmod/extmod.cmake b/extmod/extmod.cmake index adaa04f131..2d943f814c 100644 --- a/extmod/extmod.cmake +++ b/extmod/extmod.cmake @@ -191,3 +191,59 @@ if(EXISTS "${MICROPY_LIB_MBEDTLS_DIR}/library/aes.c") "${MICROPY_LIB_MBEDTLS_DIR}/include" ) endif() + +# Library for lwIP network stack + +set(MICROPY_LIB_LWIP_DIR "${MICROPY_DIR}/lib/lwip/src") + +if(EXISTS "${MICROPY_LIB_LWIP_DIR}/core/def.c") + add_library(micropy_lib_lwip INTERFACE) + + target_include_directories(micropy_lib_lwip INTERFACE + ${MICROPY_LIB_LWIP_DIR}/include + ) + + target_sources(micropy_lib_lwip INTERFACE + ${MICROPY_DIR}/shared/netutils/netutils.c + ${MICROPY_LIB_LWIP_DIR}/apps/mdns/mdns.c + ${MICROPY_LIB_LWIP_DIR}/core/def.c + ${MICROPY_LIB_LWIP_DIR}/core/dns.c + ${MICROPY_LIB_LWIP_DIR}/core/inet_chksum.c + ${MICROPY_LIB_LWIP_DIR}/core/init.c + ${MICROPY_LIB_LWIP_DIR}/core/ip.c + ${MICROPY_LIB_LWIP_DIR}/core/ipv4/autoip.c + ${MICROPY_LIB_LWIP_DIR}/core/ipv4/dhcp.c + ${MICROPY_LIB_LWIP_DIR}/core/ipv4/etharp.c + ${MICROPY_LIB_LWIP_DIR}/core/ipv4/icmp.c + ${MICROPY_LIB_LWIP_DIR}/core/ipv4/igmp.c + ${MICROPY_LIB_LWIP_DIR}/core/ipv4/ip4.c + ${MICROPY_LIB_LWIP_DIR}/core/ipv4/ip4_addr.c + ${MICROPY_LIB_LWIP_DIR}/core/ipv4/ip4_frag.c + ${MICROPY_LIB_LWIP_DIR}/core/ipv6/dhcp6.c + ${MICROPY_LIB_LWIP_DIR}/core/ipv6/ethip6.c + ${MICROPY_LIB_LWIP_DIR}/core/ipv6/icmp6.c + ${MICROPY_LIB_LWIP_DIR}/core/ipv6/inet6.c + ${MICROPY_LIB_LWIP_DIR}/core/ipv6/ip6.c + ${MICROPY_LIB_LWIP_DIR}/core/ipv6/ip6_addr.c + ${MICROPY_LIB_LWIP_DIR}/core/ipv6/ip6_frag.c + ${MICROPY_LIB_LWIP_DIR}/core/ipv6/mld6.c + ${MICROPY_LIB_LWIP_DIR}/core/ipv6/nd6.c + ${MICROPY_LIB_LWIP_DIR}/core/mem.c + ${MICROPY_LIB_LWIP_DIR}/core/memp.c + ${MICROPY_LIB_LWIP_DIR}/core/netif.c + ${MICROPY_LIB_LWIP_DIR}/core/pbuf.c + ${MICROPY_LIB_LWIP_DIR}/core/raw.c + ${MICROPY_LIB_LWIP_DIR}/core/stats.c + ${MICROPY_LIB_LWIP_DIR}/core/sys.c + ${MICROPY_LIB_LWIP_DIR}/core/tcp.c + ${MICROPY_LIB_LWIP_DIR}/core/tcp_in.c + ${MICROPY_LIB_LWIP_DIR}/core/tcp_out.c + ${MICROPY_LIB_LWIP_DIR}/core/timeouts.c + ${MICROPY_LIB_LWIP_DIR}/core/udp.c + ${MICROPY_LIB_LWIP_DIR}/netif/ethernet.c + ) + + list(APPEND MICROPY_INC_CORE + ${MICROPY_LIB_LWIP_DIR}/include + ) +endif() diff --git a/extmod/modnetwork.h b/extmod/modnetwork.h index a4d86a71c1..0177df67d4 100644 --- a/extmod/modnetwork.h +++ b/extmod/modnetwork.h @@ -49,6 +49,7 @@ #if MICROPY_PY_LWIP struct netif; +void mod_network_lwip_init(void); void mod_network_lwip_poll_wrapper(uint32_t ticks_ms); mp_obj_t mod_network_nic_ifconfig(struct netif *netif, size_t n_args, const mp_obj_t *args); #else diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index f25725f881..d9e485f702 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -70,6 +70,7 @@ set(MICROPY_SOURCE_LIB ${MICROPY_DIR}/lib/oofatfs/ff.c ${MICROPY_DIR}/lib/oofatfs/ffunicode.c ${MICROPY_DIR}/shared/netutils/netutils.c + ${MICROPY_DIR}/shared/netutils/trace.c ${MICROPY_DIR}/shared/readline/readline.c ${MICROPY_DIR}/shared/runtime/gchelper_m0.s ${MICROPY_DIR}/shared/runtime/gchelper_native.c @@ -169,6 +170,28 @@ set(PICO_SDK_COMPONENTS tinyusb_device ) +if (MICROPY_PY_LWIP) + target_link_libraries(${MICROPY_TARGET} micropy_lib_lwip) + + target_include_directories(${MICROPY_TARGET} PRIVATE + lwip_inc + ) + target_compile_definitions(${MICROPY_TARGET} PRIVATE + MICROPY_PY_LWIP=1 + ) + + list(APPEND MICROPY_SOURCE_PORT + ${MICROPY_EXTMOD_DIR}/modlwip.c + mpnetworkport.c + ) + + list(APPEND MICROPY_SOURCE_QSTR + ${MICROPY_EXTMOD_DIR}/modlwip.c + ) + + string(CONCAT GIT_SUBMODULES "${GIT_SUBMODULES} " lib/lwip) +endif() + if(MICROPY_PY_BLUETOOTH) list(APPEND MICROPY_SOURCE_PORT mpbthciport.c) target_compile_definitions(${MICROPY_TARGET} PRIVATE diff --git a/ports/rp2/lwip_inc/arch/cc.h b/ports/rp2/lwip_inc/arch/cc.h new file mode 100644 index 0000000000..c31e54e9d6 --- /dev/null +++ b/ports/rp2/lwip_inc/arch/cc.h @@ -0,0 +1,10 @@ +#ifndef MICROPY_INCLUDED_RP2_LWIP_ARCH_CC_H +#define MICROPY_INCLUDED_RP2_LWIP_ARCH_CC_H + +#include +#define LWIP_PLATFORM_DIAG(x) +#define LWIP_PLATFORM_ASSERT(x) { assert(1); } + +#define LWIP_NO_CTYPE_H 1 + +#endif // MICROPY_INCLUDED_RP2_LWIP_ARCH_CC_H diff --git a/ports/rp2/lwip_inc/arch/sys_arch.h b/ports/rp2/lwip_inc/arch/sys_arch.h new file mode 100644 index 0000000000..8b1a393741 --- /dev/null +++ b/ports/rp2/lwip_inc/arch/sys_arch.h @@ -0,0 +1 @@ +// empty diff --git a/ports/rp2/lwip_inc/lwipopts.h b/ports/rp2/lwip_inc/lwipopts.h new file mode 100644 index 0000000000..55926af7e5 --- /dev/null +++ b/ports/rp2/lwip_inc/lwipopts.h @@ -0,0 +1,56 @@ +#ifndef MICROPY_INCLUDED_RP2_LWIP_LWIPOPTS_H +#define MICROPY_INCLUDED_RP2_LWIP_LWIPOPTS_H + +#include + +// This protection is not needed, instead protect lwIP code with flags +#define SYS_ARCH_DECL_PROTECT(lev) do { } while (0) +#define SYS_ARCH_PROTECT(lev) do { } while (0) +#define SYS_ARCH_UNPROTECT(lev) do { } while (0) + +#define NO_SYS 1 +#define SYS_LIGHTWEIGHT_PROT 1 +#define MEM_ALIGNMENT 4 + +#define LWIP_CHKSUM_ALGORITHM 3 +// The checksum flags are set in eth.c +#define LWIP_CHECKSUM_CTRL_PER_NETIF 1 + +#define LWIP_ARP 1 +#define LWIP_ETHERNET 1 +#define LWIP_RAW 1 +#define LWIP_NETCONN 0 +#define LWIP_SOCKET 0 +#define LWIP_STATS 0 +#define LWIP_NETIF_HOSTNAME 1 +#define LWIP_NETIF_EXT_STATUS_CALLBACK 1 + +#define LWIP_IPV6 0 +#define LWIP_DHCP 1 +#define LWIP_DHCP_CHECK_LINK_UP 1 +#define DHCP_DOES_ARP_CHECK 0 // to speed DHCP up +#define LWIP_DNS 1 +#define LWIP_DNS_SUPPORT_MDNS_QUERIES 1 +#define LWIP_MDNS_RESPONDER 1 +#define LWIP_IGMP 1 + +#define LWIP_NUM_NETIF_CLIENT_DATA LWIP_MDNS_RESPONDER +#define MEMP_NUM_UDP_PCB (4 + LWIP_MDNS_RESPONDER) +#define MEMP_NUM_SYS_TIMEOUT (LWIP_NUM_SYS_TIMEOUT_INTERNAL + LWIP_MDNS_RESPONDER) + +#define SO_REUSE 1 +#define TCP_LISTEN_BACKLOG 1 + +extern uint32_t rosc_random_u32(void); +#define LWIP_RAND() rosc_random_u32() + +// lwip takes 26700 bytes +#define MEM_SIZE (8000) +#define TCP_MSS (800) +#define TCP_WND (8 * TCP_MSS) +#define TCP_SND_BUF (8 * TCP_MSS) +#define MEMP_NUM_TCP_SEG (32) + +typedef uint32_t sys_prot_t; + +#endif // MICROPY_INCLUDED_RP2_LWIP_LWIPOPTS_H diff --git a/ports/rp2/main.c b/ports/rp2/main.c index 02bfb6dae2..da21e0b394 100644 --- a/ports/rp2/main.c +++ b/ports/rp2/main.c @@ -48,9 +48,21 @@ #include "pico/binary_info.h" #include "hardware/rtc.h" #include "hardware/structs/rosc.h" +#if MICROPY_PY_LWIP +#include "lwip/init.h" +#include "lwip/apps/mdns.h" +#endif + +#ifndef MICROPY_GC_HEAP_SIZE +#if MICROPY_PY_LWIP +#define MICROPY_GC_HEAP_SIZE 166 * 1024 +#else +#define MICROPY_GC_HEAP_SIZE 192 * 1024 +#endif +#endif extern uint8_t __StackTop, __StackBottom; -static char gc_heap[192 * 1024]; +static char gc_heap[MICROPY_GC_HEAP_SIZE]; // Embed version info in the binary in machine readable form bi_decl(bi_program_version_string(MICROPY_GIT_TAG)); @@ -96,6 +108,16 @@ int main(int argc, char **argv) { mp_stack_set_limit(&__StackTop - &__StackBottom - 256); gc_init(&gc_heap[0], &gc_heap[MP_ARRAY_SIZE(gc_heap)]); + #if MICROPY_PY_LWIP + // lwIP doesn't allow to reinitialise itself by subsequent calls to this function + // because the system timeout list (next_timeout) is only ever reset by BSS clearing. + // So for now we only init the lwIP stack once on power-up. + lwip_init(); + #if LWIP_MDNS_RESPONDER + mdns_resp_init(); + #endif + #endif + for (;;) { // Initialise MicroPython runtime. @@ -114,6 +136,9 @@ int main(int argc, char **argv) { #if MICROPY_PY_NETWORK mod_network_init(); #endif + #if MICROPY_PY_LWIP + mod_network_lwip_init(); + #endif // Execute _boot.py to set up the filesystem. #if MICROPY_VFS_FAT && MICROPY_HW_USB_MSC diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 2ce8b88f3f..4cadc26174 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -106,6 +106,7 @@ #define MICROPY_VFS_LFS2 (1) #define MICROPY_VFS_FAT (1) #define MICROPY_SSL_MBEDTLS (1) +#define MICROPY_PY_LWIP_SOCK_RAW (MICROPY_PY_LWIP) // fatfs configuration #define MICROPY_FATFS_ENABLE_LFN (1) @@ -196,6 +197,11 @@ extern const struct _mod_network_nic_type_t mod_network_nic_type_nina; #define MICROPY_BEGIN_ATOMIC_SECTION() save_and_disable_interrupts() #define MICROPY_END_ATOMIC_SECTION(state) restore_interrupts(state) +// Prevent the "lwIP task" from running when unsafe to do so. +#define MICROPY_PY_LWIP_ENTER lwip_lock_acquire(); +#define MICROPY_PY_LWIP_REENTER lwip_lock_acquire(); +#define MICROPY_PY_LWIP_EXIT lwip_lock_release(); + #if MICROPY_HW_ENABLE_USBDEV #define MICROPY_HW_USBDEV_TASK_HOOK extern void tud_task(void); tud_task(); #define MICROPY_VM_HOOK_COUNT (10) @@ -233,3 +239,5 @@ typedef intptr_t mp_off_t; #define MICROPY_FROZEN_LIST_ITEM(name, file) bi_decl(bi_string(BINARY_INFO_TAG_MICROPYTHON, BINARY_INFO_ID_MP_FROZEN, name)) extern uint32_t rosc_random_u32(void); +extern void lwip_lock_acquire(void); +extern void lwip_lock_release(void); diff --git a/ports/rp2/mpnetworkport.c b/ports/rp2/mpnetworkport.c new file mode 100644 index 0000000000..1f59e28ea5 --- /dev/null +++ b/ports/rp2/mpnetworkport.c @@ -0,0 +1,90 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/runtime.h" +#include "py/mphal.h" + +#if MICROPY_PY_LWIP + +#include "lwip/timeouts.h" +#include "pico/time.h" + +// Poll lwIP every 64ms by default +#define LWIP_TICK_RATE_MS 64 + +static alarm_id_t lwip_alarm_id = -1; +static bool lwip_can_poll = true; +static bool lwip_poll_pending = false; + +u32_t sys_now(void) { + // Used by LwIP + return mp_hal_ticks_ms(); +} + +STATIC uint32_t lwip_poll(void) { + // Run the lwIP internal updates + sys_check_timeouts(); + + return MAX(5, MIN(sys_timeouts_sleeptime(), LWIP_TICK_RATE_MS)); +} + +void lwip_lock_acquire(void) { + lwip_can_poll = false; +} + +void lwip_lock_release(void) { + lwip_can_poll = false; + if (lwip_poll_pending) { + lwip_poll(); + lwip_poll_pending = false; + } + lwip_can_poll = true; +} + +uint32_t lwip_try_poll(void) { + uint32_t ret = LWIP_TICK_RATE_MS; + if (lwip_can_poll) { + lwip_can_poll = false; + ret = lwip_poll(); + lwip_can_poll = true; + } else { + lwip_poll_pending = true; + } + return ret; +} + +STATIC int64_t alarm_callback(alarm_id_t id, void *user_data) { + return lwip_try_poll(); +} + +void mod_network_lwip_init(void) { + if (lwip_alarm_id != -1) { + cancel_alarm(lwip_alarm_id); + } + lwip_alarm_id = add_alarm_in_ms(LWIP_TICK_RATE_MS, alarm_callback, mp_const_true, true); +} + +#endif // MICROPY_PY_LWIP