diff --git a/src/port/rtl8735b/README.md b/src/port/rtl8735b/README.md new file mode 100644 index 00000000..96974c22 --- /dev/null +++ b/src/port/rtl8735b/README.md @@ -0,0 +1,73 @@ +# wolfIP on RealTek RTL8735B (AmebaPro2) + +wolfIP TCP/IP stack running over the RTL8735B's native 10/100 Ethernet MAC (with its integrated Fast-Ethernet PHY), built inside the RealTek AmebaPro2 FreeRTOS SDK. Two demos, built from the same sources: + +1. **Milestone 1** - DHCP client + raw TCP echo server (port 7). Proves the driver and stack. +2. **Milestone 2** - a wolfSSL TLS 1.2/1.3 client over a real wolfIP socket (add `--tls`). + +## How it works + +The RTL8735B has a native Ethernet MAC + integrated FEPHY (no external PHY). The vendor SDK exposes a frame-level mbed HAL (`ethernet_init/write/send/receive/read`) that owns the DMA descriptor rings and the D-cache clean/invalidate internally. This port is a thin adapter that binds wolfIP's two driver callbacks onto that HAL - no register-level MAC/DMA/MDIO code: + +- wolfIP `send()` -> `ethernet_write()` + `ethernet_send()` +- wolfIP `poll()` -> `ethernet_receive()` + `ethernet_read()` + +The whole stack runs in a single FreeRTOS task, so no wolfIP locking is required. RX is polled (milestone 1/2); the MAC IRQ hook only latches link up/down. The vendor lwIP layer is not used - wolfIP owns the interface. + +## Files + +| File | Purpose | +|------|---------| +| `wolfip_rtl8735b.c` / `.h` | wolfIP poll/send driver glue over the mbed Ethernet HAL | +| `main.c` | App: single task - MAC init, link/DHCP wait, echo server, optional TLS client | +| `config.h` | wolfIP build config (DHCP, socket counts, static-IP fallback) | +| `user_settings.h` | wolfSSL config for the TLS client (software crypto, ECDHE-ECDSA/AES-GCM) | +| `tls_client.c` / `.h` | Board-agnostic wolfIP TLS client (shared with the other ports) | +| `wolfip_eth.cmake` | SDK build integration (append wolfIP + optional wolfSSL sources) | +| `wolfip_cycle.sh` | Build -> flash -> read-console helper | + +## Build and run + +Prerequisites: the AmebaPro2 FreeRTOS SDK (`~/GitHub/ameba-rtos-pro2`), the ASDK 10.3.0 toolchain, and the hardware bench (Pi4 GPIO 21 boot-mode, J-Link nRESET for the CHIP_EN power-on reset, console on `/dev/ttyUSB5`). The system `arm-none-eabi-gcc` will not work - use the ASDK toolchain. + +### SDK config + +No SDK source edits are required. The non-TrustZone (`ntz`) app build already compiles the mbed Ethernet HAL: `DEVICE_ETHERNET=1` (device.h non-secure branch) and `CONFIG_MII_EN=1` (platform_conf_ntz.h). This port calls that HAL directly, so the vendor `CONFIG_ETHERNET`/lwIP path stays disabled (`CONFIG_ETHERNET=0`) and no lwIP is linked in. + +### Hands-free cycle + +```bash +cd ~/GitHub/wolfip/src/port/rtl8735b +./wolfip_cycle.sh # milestone 1: DHCP + echo +./wolfip_cycle.sh --tls # milestone 2: also build/run the TLS client +``` + +### Manual build + +```bash +export PATH=~/ameba-pro2-workspace/asdk/asdk-10.3.0/linux/newlib/bin:$PATH +P=~/GitHub/ameba-rtos-pro2/project/realtek_amebapro2_v0_example +mkdir -p $P/component/example/wolfip_eth +cp main.c user_settings.h wolfip_eth.cmake $P/component/example/wolfip_eth/ +cp main.c $P/src/main.c +cd $P/GCC-RELEASE && mkdir -p build && cd build +cmake .. -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=../toolchain.cmake \ + -DBUILD_FPGA=OFF -DBUILD_PXP=OFF -DEXAMPLE=wolfip_eth \ + -DWOLFIP_ROOT=~/GitHub/wolfip +make flash -j8 # -> build/flash_ntz.bin +~/.claude/skills/amebapro2-flash/amebapro2_flash.sh build/flash_ntz.bin +uart-monitor tail GENERIC_UART_ttyUSB5 +``` + +For the TLS build add `-DWOLFIP_ENABLE_TLS=ON -DWOLFSSL_ROOT=~/GitHub/wolfssl`. + +## Verify + +- **M1:** the console prints the MAC, `Link up`, and `DHCP bound: ip=...`. From the host: `ping ` and `nc 7` (whatever you type is echoed back). +- **M2:** additionally `TLS: connecting to ...` and `TLS: received N bytes` with the server's response. The TLS target defaults to an ECDSA host; override with `-DTLS_TARGET_IP`/`-DTLS_TARGET_HOST`/`-DTLS_TARGET_PORT`. Certificate verification is disabled in the demo (no RTC / CA store). + +## Notes / limits + +- Certificate validation is off (`WOLFSSL_VERIFY_NONE`) - demo only. Add a CA store and an RTC/`NO_ASN_TIME` removal for production. +- RX is polled today. An IRQ-driven path (push frames via `wolfIP_recv_ex()` from the RX thread) is a planned follow-up. +- Descriptor rings are placed in the reserved `__sram_rev_start__` SRAM region and packet buffers in the vendor DMA heap, mirroring the SDK's own `ethernet_mii` driver. diff --git a/src/port/rtl8735b/config.h b/src/port/rtl8735b/config.h new file mode 100644 index 00000000..a5d19467 --- /dev/null +++ b/src/port/rtl8735b/config.h @@ -0,0 +1,71 @@ +/* config.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + * + * wolfIP is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfIP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + * + * wolfIP build configuration for the RTL8735B (AmebaPro2) port. + */ +#ifndef WOLF_CONFIG_H +#define WOLF_CONFIG_H + +#ifndef CONFIG_IPFILTER +#define CONFIG_IPFILTER 0 +#endif + +#define ETHERNET +#define LINK_MTU 1536 + +/* Socket configuration. The RTL8735B has ample DDR, but keep the stack's + * static footprint modest for the demo. */ +#define MAX_TCPSOCKETS 8 +#define MAX_UDPSOCKETS 2 +#define MAX_ICMPSOCKETS 1 +#define RXBUF_SIZE (LINK_MTU * 6) +#define TXBUF_SIZE (LINK_MTU * 6) + +#define MAX_NEIGHBORS 16 + +#ifndef WOLFIP_MAX_INTERFACES +#define WOLFIP_MAX_INTERFACES 1 +#endif + +#ifndef WOLFIP_ENABLE_FORWARDING +#define WOLFIP_ENABLE_FORWARDING 0 +#endif + +#ifndef WOLFIP_ENABLE_LOOPBACK +#define WOLFIP_ENABLE_LOOPBACK 0 +#endif + +#ifndef WOLFIP_ENABLE_DHCP +#define WOLFIP_ENABLE_DHCP 1 +#endif + +/* Static IP fallback (used when DHCP is disabled or times out). */ +#define WOLFIP_IP "192.168.1.100" +#define WOLFIP_NETMASK "255.255.255.0" +#define WOLFIP_GW "192.168.1.1" +#define WOLFIP_STATIC_DNS_IP "8.8.8.8" + +#if WOLFIP_ENABLE_DHCP +#define DHCP +#define DHCP_DISCOVER_RETRIES 6 +#define DHCP_REQUEST_RETRIES 6 +#endif + +#endif /* WOLF_CONFIG_H */ diff --git a/src/port/rtl8735b/main.c b/src/port/rtl8735b/main.c new file mode 100644 index 00000000..e10748a4 --- /dev/null +++ b/src/port/rtl8735b/main.c @@ -0,0 +1,292 @@ +/* main.c + * + * RTL8735B (AmebaPro2) wolfIP Echo Server / TLS Client demo. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + * + * wolfIP is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfIP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + * + * Built inside the RealTek AmebaPro2 FreeRTOS SDK (replaces src/main.c). A + * single FreeRTOS task owns the wolfIP stack: it brings up the RTL8735B MAC + * via the mbed HAL glue, waits for link + DHCP, serves a TCP echo server, and + * (when ENABLE_TLS_CLIENT is defined) runs a wolfSSL TLS client. Running the + * whole stack in one task means no wolfIP locking is required. + * + * Build: configure with -DEXAMPLE=wolfip_eth (see wolfip_eth.cmake). + */ + +#include +#include +#include + +#include "FreeRTOS.h" +#include "task.h" + +#include "config.h" +#include "wolfip.h" +#include "wolfip_rtl8735b.h" + +#ifdef ENABLE_TLS_CLIENT +#include "tls_client.h" +/* TLS target (DNS is not wired in, so use an IP). Override at build time with + * -DTLS_TARGET_IP=... -DTLS_TARGET_HOST=... -DTLS_TARGET_PORT=..., or point at + * a bench server, e.g. openssl s_server -tls1_3 -cert server-ecc.pem + * -key ecc-key.pem -accept 11111 -www. */ +#ifndef TLS_TARGET_IP +#define TLS_TARGET_IP "142.250.189.174" /* placeholder: www.google.com */ +#endif +#ifndef TLS_TARGET_HOST +#define TLS_TARGET_HOST "www.google.com" +#endif +#ifndef TLS_TARGET_PORT +#define TLS_TARGET_PORT 443 +#endif +#endif /* ENABLE_TLS_CLIENT */ + +/* SDK entry-point helpers (provided by the AmebaPro2 example project). The + * stock setup() (WLAN/FTL bring-up) is intentionally not used: this is a + * wired-Ethernet-only demo and does not need the Wi-Fi stack. */ +extern void console_init(void); +extern void voe_t2ff_prealloc(void); + +#ifndef ECHO_PORT +#define ECHO_PORT 7 +#endif +#ifndef WOLFIP_TASK_STACK_WORDS +#define WOLFIP_TASK_STACK_WORDS 4096 /* words; generous for the TLS path */ +#endif +#define WOLFIP_POLL_DELAY_MS 5 +#define PHY_MAINTAIN_MS 10000 + +static struct wolfIP *IPStack; +static int listen_fd = -1; +static int client_fd = -1; +static uint8_t rx_buf[1536]; + +/* Monotonic milliseconds for wolfIP_poll()'s "now". */ +static uint32_t now_ms(void) +{ + return (uint32_t)(xTaskGetTickCount() * portTICK_PERIOD_MS); +} + +/* Apply the static-IP fallback from config.h. */ +static void use_static_ip(void) +{ + wolfIP_ipconfig_set(IPStack, atoip4(WOLFIP_IP), atoip4(WOLFIP_NETMASK), + atoip4(WOLFIP_GW)); + printf("Static IP %s\r\n", WOLFIP_IP); +} + +/* TCP echo server: accept one client, echo everything it sends. */ +static void echo_cb(int fd, uint16_t event, void *arg) +{ + struct wolfIP *s = (struct wolfIP *)arg; + int ret; + + if ((fd == listen_fd) && (event & CB_EVENT_READABLE) && (client_fd == -1)) { + int c = wolfIP_sock_accept(s, listen_fd, NULL, NULL); + if (c > 0) { + client_fd = c; /* keep the -1 sentinel on a negative errno */ + wolfIP_register_callback(s, client_fd, echo_cb, s); + printf("echo: client connected (fd=%d)\r\n", client_fd); + } + return; + } + + if ((fd == client_fd) && (event & CB_EVENT_READABLE)) { + /* Drain all buffered data this event (recvfrom < 0 => nothing left). */ + do { + ret = wolfIP_sock_recvfrom(s, client_fd, rx_buf, sizeof(rx_buf), 0, + NULL, NULL); + if (ret > 0) + (void)wolfIP_sock_sendto(s, client_fd, rx_buf, (uint32_t)ret, 0, + NULL, 0); + } while (ret > 0); + if (ret == 0) { + wolfIP_sock_close(s, client_fd); + client_fd = -1; + printf("echo: client closed\r\n"); + } + } + + if ((fd == client_fd) && (event & CB_EVENT_CLOSED)) { + wolfIP_sock_close(s, client_fd); + client_fd = -1; + printf("echo: client closed\r\n"); + } +} + +#ifdef ENABLE_TLS_CLIENT +static void tls_response_cb(const char *data, int len, void *ctx) +{ + (void)ctx; + printf("TLS: received %d bytes\r\n", len); + if (len > 0) + printf("%.*s\r\n", len, data); +} + +static void tls_debug_cb(const char *msg) +{ + printf("%s", msg); +} +#endif + +static void wolfip_task(void *arg) +{ + struct wolfIP_ll_dev *ll; + struct wolfIP_sockaddr_in addr; + uint32_t last_phy_ms; + int ret; + (void)arg; + + printf("\r\nwolfIP RTL8735B demo starting\r\n"); + + wolfIP_init_static(&IPStack); + ll = wolfIP_getdev(IPStack); + + ret = rtl8735b_eth_init(ll, NULL); + if (ret != 0) { + printf("ERROR: rtl8735b_eth_init failed (%d)\r\n", ret); + vTaskDelete(NULL); + return; + } + printf("MAC: %02x:%02x:%02x:%02x:%02x:%02x\r\n", + (unsigned)ll->mac[0], (unsigned)ll->mac[1], (unsigned)ll->mac[2], + (unsigned)ll->mac[3], (unsigned)ll->mac[4], (unsigned)ll->mac[5]); + + /* Wait for PHY link (up to ~10s), polling the stack meanwhile. */ + printf("Waiting for Ethernet link...\r\n"); + { + int i; + for (i = 0; i < 2000 && !rtl8735b_eth_link_up(); i++) { + (void)wolfIP_poll(IPStack, now_ms()); + vTaskDelay(pdMS_TO_TICKS(WOLFIP_POLL_DELAY_MS)); + } + } + printf(rtl8735b_eth_link_up() ? "Link up\r\n" : "Link still down; continuing\r\n"); + +#ifdef DHCP + { + uint32_t start = now_ms(); + if (dhcp_client_init(IPStack) >= 0) { + printf("Waiting for DHCP...\r\n"); + while (!dhcp_bound(IPStack) && dhcp_client_is_running(IPStack) + && (now_ms() - start) <= 20000) { + (void)wolfIP_poll(IPStack, now_ms()); + vTaskDelay(pdMS_TO_TICKS(WOLFIP_POLL_DELAY_MS)); + } + } + if (dhcp_bound(IPStack)) { + ip4 ip = 0, nm = 0, gw = 0; + char ipstr[16]; + wolfIP_ipconfig_get(IPStack, &ip, &nm, &gw); + iptoa(ip, ipstr); + printf("DHCP bound: ip=%s\r\n", ipstr); + } + else { + printf("DHCP timeout; falling back to "); + use_static_ip(); + } + } +#else + use_static_ip(); +#endif + + /* TCP echo server. */ + listen_fd = wolfIP_sock_socket(IPStack, AF_INET, IPSTACK_SOCK_STREAM, 0); + if (listen_fd < 0) { + printf("ERROR: echo server socket() failed (%d)\r\n", listen_fd); + } + else { + wolfIP_register_callback(IPStack, listen_fd, echo_cb, IPStack); + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = ee16(ECHO_PORT); + addr.sin_addr.s_addr = 0; + (void)wolfIP_sock_bind(IPStack, listen_fd, + (struct wolfIP_sockaddr *)&addr, sizeof(addr)); + (void)wolfIP_sock_listen(IPStack, listen_fd, 1); + printf("TCP echo server listening on port %d\r\n", ECHO_PORT); + } + +#ifdef ENABLE_TLS_CLIENT + printf("Initializing TLS client...\r\n"); + if (tls_client_init(IPStack, tls_debug_cb) < 0) + printf("ERROR: TLS client init failed\r\n"); + else + tls_client_set_sni(TLS_TARGET_HOST); +#endif + + /* Main service loop. */ + last_phy_ms = now_ms(); +#ifdef ENABLE_TLS_CLIENT + { + int tls_started = 0; + int req_sent = 0; + static const char http_req[] = + "GET / HTTP/1.0\r\nHost: " TLS_TARGET_HOST "\r\n\r\n"; + for (;;) { + (void)wolfIP_poll(IPStack, now_ms()); + if (!tls_started && rtl8735b_eth_link_up()) { + printf("TLS: connecting to %s:%d\r\n", + TLS_TARGET_IP, TLS_TARGET_PORT); + if (tls_client_connect(TLS_TARGET_IP, TLS_TARGET_PORT, + tls_response_cb, NULL) == 0) + tls_started = 1; + } + (void)tls_client_poll(); + if (tls_started && !req_sent && tls_client_is_connected()) { + /* Retry if the TX buffer is momentarily full (send <= 0). */ + if (tls_client_send(http_req, (int)(sizeof(http_req) - 1)) > 0) { + printf("TLS: handshake done; request sent\r\n"); + req_sent = 1; + } + } + if ((now_ms() - last_phy_ms) >= PHY_MAINTAIN_MS) { + rtl8735b_eth_phy_maintain(); + last_phy_ms = now_ms(); + } + vTaskDelay(pdMS_TO_TICKS(WOLFIP_POLL_DELAY_MS)); + } + } +#else + for (;;) { + (void)wolfIP_poll(IPStack, now_ms()); + if ((now_ms() - last_phy_ms) >= PHY_MAINTAIN_MS) { + rtl8735b_eth_phy_maintain(); + last_phy_ms = now_ms(); + } + vTaskDelay(pdMS_TO_TICKS(WOLFIP_POLL_DELAY_MS)); + } +#endif +} + +int main(void) +{ + console_init(); + voe_t2ff_prealloc(); + + if (xTaskCreate(wolfip_task, "wolfip", WOLFIP_TASK_STACK_WORDS, NULL, + tskIDLE_PRIORITY + 4, NULL) != pdPASS) { + printf("ERROR: failed to create wolfip task\r\n"); + } + + vTaskStartScheduler(); + while (1) { + } + return 0; +} diff --git a/src/port/rtl8735b/tls_client.c b/src/port/rtl8735b/tls_client.c new file mode 100644 index 00000000..92e3b1c0 --- /dev/null +++ b/src/port/rtl8735b/tls_client.c @@ -0,0 +1,382 @@ +/* tls_client.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + * + * wolfIP is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfIP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include "tls_client.h" +#include "wolfip.h" + +#include +#include +#include + +/* Configuration */ +#ifndef TLS_CLIENT_BUF_SIZE +#define TLS_CLIENT_BUF_SIZE 2048 +#endif + +/* Client state */ +typedef enum { + TLS_CLIENT_STATE_IDLE = 0, + TLS_CLIENT_STATE_DNS_LOOKUP, + TLS_CLIENT_STATE_CONNECTING, + TLS_CLIENT_STATE_HANDSHAKE, + TLS_CLIENT_STATE_CONNECTED, + TLS_CLIENT_STATE_DONE, + TLS_CLIENT_STATE_ERROR +} tls_client_state_t; + +/* Client context */ +static struct { + struct wolfIP *stack; + WOLFSSL_CTX *ctx; + WOLFSSL *ssl; + int fd; + tls_client_state_t state; + tls_client_debug_cb debug_cb; + tls_client_response_cb response_cb; + void *user_ctx; + uint8_t rx_buf[TLS_CLIENT_BUF_SIZE]; + ip4 server_ip; + uint16_t server_port; + char sni_host[64]; + int got_response; + int connect_ready_count; + int connect_wait_count; +} client; + +/* External functions from wolfssl_io.c */ +extern int wolfSSL_SetIO_wolfIP_CTX(WOLFSSL_CTX *ctx, struct wolfIP *s); +extern int wolfSSL_SetIO_wolfIP(WOLFSSL *ssl, int fd); +extern void wolfSSL_CleanupIO_wolfIP(WOLFSSL *ssl); + +/* Max poll cycles to spend in TLS_CLIENT_STATE_CONNECTING before giving up on a + * refused/unreachable server (~10s at a few-ms poll cadence). */ +#ifndef TLS_CONNECT_MAX_POLLS +#define TLS_CONNECT_MAX_POLLS 2000 +#endif + +/* Debug output helper */ +static void debug_print(const char *msg) +{ + if (client.debug_cb) { + client.debug_cb(msg); + } +} + +/* Release the WOLFSSL object and its wolfIP IO-descriptor slot (idempotent). + * wolfSSL_CleanupIO_wolfIP must run before wolfSSL_free or the io_descs pool + * leaks one slot per session. */ +static void tls_client_teardown(void) +{ + if (client.ssl) { + wolfSSL_CleanupIO_wolfIP(client.ssl); + wolfSSL_free(client.ssl); + client.ssl = NULL; + } +} + +/* Enter a terminal state (DONE/ERROR), freeing the SSL object first. */ +static void tls_client_finish(tls_client_state_t st) +{ + tls_client_teardown(); + client.state = st; +} + +int tls_client_init(struct wolfIP *stack, tls_client_debug_cb debug) +{ + int ret; + + if (stack == NULL) { + if (debug) + debug("TLS Client: NULL stack\n"); + return -1; + } + + memset(&client, 0, sizeof(client)); + client.stack = stack; + client.debug_cb = debug; + client.fd = -1; + client.state = TLS_CLIENT_STATE_IDLE; + + debug_print("TLS Client: Initializing wolfSSL\n"); + + /* Initialize wolfSSL library (may already be done by server) */ + ret = wolfSSL_Init(); + if (ret != WOLFSSL_SUCCESS) { + debug_print("TLS Client: wolfSSL_Init failed\n"); + return -1; + } + + /* Create TLS 1.3 client context */ + client.ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); + if (client.ctx == NULL) { + debug_print("TLS Client: CTX_new failed\n"); + return -1; + } + + /* Don't verify server certificate (for testing without root CAs) */ + wolfSSL_CTX_set_verify(client.ctx, WOLFSSL_VERIFY_NONE, NULL); + + /* Register wolfIP I/O callbacks */ + wolfSSL_SetIO_wolfIP_CTX(client.ctx, stack); + + debug_print("TLS Client: Initialized\n"); + return 0; +} + +void tls_client_set_sni(const char *hostname) +{ + if (hostname) { + strncpy(client.sni_host, hostname, sizeof(client.sni_host) - 1); + client.sni_host[sizeof(client.sni_host) - 1] = '\0'; + } else { + client.sni_host[0] = '\0'; + } +} + +int tls_client_connect(const char *host, uint16_t port, + tls_client_response_cb response_cb, void *user_ctx) +{ + struct wolfIP_sockaddr_in addr; + int ret; + + if (client.state != TLS_CLIENT_STATE_IDLE) { + debug_print("TLS Client: Already busy\n"); + return -1; + } + + if (host == NULL || host[0] == '\0') { + debug_print("TLS Client: NULL/empty host\n"); + return -1; + } + + client.response_cb = response_cb; + client.user_ctx = user_ctx; + client.server_port = port; + + /* Try to parse as IP address first */ + client.server_ip = atoip4(host); + if (client.server_ip == 0) { + /* TODO: DNS lookup - for now require IP address */ + debug_print("TLS Client: DNS not implemented, use IP address\n"); + return -1; + } + + /* Create socket */ + client.fd = wolfIP_sock_socket(client.stack, AF_INET, IPSTACK_SOCK_STREAM, 0); + if (client.fd < 0) { + debug_print("TLS Client: Failed! socket() error\n"); + return -1; + } + + /* Connect to server */ + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = ee16(port); + addr.sin_addr.s_addr = ee32(client.server_ip); + + ret = wolfIP_sock_connect(client.stack, client.fd, + (struct wolfIP_sockaddr *)&addr, sizeof(addr)); + if (ret < 0 && ret != -WOLFIP_EAGAIN) { + debug_print("TLS Client: Failed! connect() error\n"); + wolfIP_sock_close(client.stack, client.fd); + client.fd = -1; + return -1; + } + + client.state = TLS_CLIENT_STATE_CONNECTING; + debug_print("TLS Client: Connecting...\n"); + return 0; +} + +/* Call this from main loop to drive the TLS client state machine */ +int tls_client_poll(void) +{ + int ret; + int err; + + switch (client.state) { + case TLS_CLIENT_STATE_IDLE: + case TLS_CLIENT_STATE_DONE: + case TLS_CLIENT_STATE_ERROR: + return 0; + + case TLS_CLIENT_STATE_CONNECTING: + /* Check if TCP connection is established by calling connect again */ + { + struct wolfIP_sockaddr_in addr; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = ee16(client.server_port); + addr.sin_addr.s_addr = ee32(client.server_ip); + + ret = wolfIP_sock_connect(client.stack, client.fd, + (struct wolfIP_sockaddr *)&addr, sizeof(addr)); + if (ret == -WOLFIP_EAGAIN) { + /* Still connecting. A refused/failed connect re-SYNs and + * keeps returning EAGAIN, so bound the attempt. */ + client.connect_ready_count = 0; + if (++client.connect_wait_count > TLS_CONNECT_MAX_POLLS) { + debug_print("TLS Client: Failed! connect timeout\n"); + tls_client_finish(TLS_CLIENT_STATE_ERROR); + return -1; + } + return 0; + } + if (ret < 0) { + debug_print("TLS Client: Failed! TCP connect error\n"); + tls_client_finish(TLS_CLIENT_STATE_ERROR); + return -1; + } + /* Connection established - wait a few poll cycles to let stack settle */ + client.connect_wait_count = 0; + client.connect_ready_count++; + if (client.connect_ready_count < 10) { + return 0; + } + client.connect_ready_count = 0; + } + debug_print("TLS Client: TLS handshake...\n"); + + /* Create SSL object */ + client.ssl = wolfSSL_new(client.ctx); + if (client.ssl == NULL) { + debug_print("TLS Client: Failed! SSL context error\n"); + tls_client_finish(TLS_CLIENT_STATE_ERROR); + return -1; + } + + /* Set SNI (Server Name Indication) - required by most servers */ + if (client.sni_host[0]) { + wolfSSL_UseSNI(client.ssl, WOLFSSL_SNI_HOST_NAME, + client.sni_host, (word16)strlen(client.sni_host)); + } + + /* Associate SSL with socket */ + ret = wolfSSL_SetIO_wolfIP(client.ssl, client.fd); + if (ret != 0) { + debug_print("TLS Client: Failed! I/O setup error\n"); + tls_client_finish(TLS_CLIENT_STATE_ERROR); + return -1; + } + client.state = TLS_CLIENT_STATE_HANDSHAKE; + __attribute__((fallthrough)); + + case TLS_CLIENT_STATE_HANDSHAKE: + ret = wolfSSL_connect(client.ssl); + if (ret == WOLFSSL_SUCCESS) { + debug_print("TLS Client: Connected!\n"); + client.state = TLS_CLIENT_STATE_CONNECTED; + } else { + err = wolfSSL_get_error(client.ssl, ret); + if (err == WOLFSSL_ERROR_WANT_READ || + err == WOLFSSL_ERROR_WANT_WRITE) { + /* Handshake in progress, continue polling */ + return 0; + } + (void)err; + debug_print("TLS Client: Failed! Handshake error\n"); + tls_client_finish(TLS_CLIENT_STATE_ERROR); + return -1; + } + break; + + case TLS_CLIENT_STATE_CONNECTED: + /* Try to read any response */ + ret = wolfSSL_read(client.ssl, client.rx_buf, + sizeof(client.rx_buf) - 1); + if (ret > 0) { + client.rx_buf[ret] = '\0'; + client.got_response = 1; + if (client.response_cb) { + client.response_cb((char *)client.rx_buf, ret, client.user_ctx); + } + } else { + err = wolfSSL_get_error(client.ssl, ret); + if (err == WOLFSSL_ERROR_ZERO_RETURN) { + /* Server closed connection - normal after sending response */ + if (client.got_response) { + debug_print("TLS Client: Passed! Received response from server\n"); + } else { + debug_print("TLS Client: Failed! Server closed connection (no data received)\n"); + } + tls_client_finish(TLS_CLIENT_STATE_DONE); + } else if (err != WOLFSSL_ERROR_WANT_READ) { + /* Connection closed/reset - check if we got data first */ + if (client.got_response) { + debug_print("TLS Client: Passed! Connection closed after response\n"); + tls_client_finish(TLS_CLIENT_STATE_DONE); + } else { + debug_print("TLS Client: Failed! Read error (no response received)\n"); + tls_client_finish(TLS_CLIENT_STATE_ERROR); + } + } + } + break; + + default: + break; + } + + return 0; +} + +int tls_client_send(const void *data, int len) +{ + int ret; + int err; + + if (client.state != TLS_CLIENT_STATE_CONNECTED) { + return -1; + } + + ret = wolfSSL_write(client.ssl, data, len); + if (ret <= 0) { + err = wolfSSL_get_error(client.ssl, ret); + if (err != WOLFSSL_ERROR_WANT_WRITE) { + debug_print("TLS Client: Write failed\n"); + return -1; + } + } + + return ret; +} + +void tls_client_close(void) +{ + if (client.ssl) { + wolfSSL_shutdown(client.ssl); + } + tls_client_teardown(); /* frees the SSL object and its IO slot */ + if (client.fd >= 0 && client.stack) { + wolfIP_sock_close(client.stack, client.fd); + client.fd = -1; + } + client.got_response = 0; + client.connect_ready_count = 0; + client.connect_wait_count = 0; + client.state = TLS_CLIENT_STATE_IDLE; +} + +int tls_client_is_connected(void) +{ + return (client.state == TLS_CLIENT_STATE_CONNECTED); +} diff --git a/src/port/rtl8735b/tls_client.h b/src/port/rtl8735b/tls_client.h new file mode 100644 index 00000000..95b7483f --- /dev/null +++ b/src/port/rtl8735b/tls_client.h @@ -0,0 +1,108 @@ +/* tls_client.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + * + * wolfIP is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfIP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef TLS_CLIENT_H +#define TLS_CLIENT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Forward declaration */ +struct wolfIP; + +/* Debug callback - receives status messages */ +typedef void (*tls_client_debug_cb)(const char *msg); + +/* Response callback - receives data from server */ +typedef void (*tls_client_response_cb)(const char *data, int len, void *ctx); + +/** + * Initialize the TLS client + * + * @param stack wolfIP stack instance + * @param debug Optional debug callback (can be NULL) + * + * @return 0 on success, negative on error + */ +int tls_client_init(struct wolfIP *stack, tls_client_debug_cb debug); + +/** + * Start TLS connection to a server + * + * @param host Server IP address (DNS not yet supported) + * @param port Server port (e.g., 443 for HTTPS) + * @param response_cb Callback for received data + * @param user_ctx User context passed to callback + * + * @return 0 on success, negative on error + * + * Example: + * tls_client_connect("142.250.80.46", 443, my_response_cb, NULL); + */ +int tls_client_connect(const char *host, uint16_t port, + tls_client_response_cb response_cb, void *user_ctx); + +/** + * Poll the TLS client state machine + * + * Call this regularly from main loop to drive handshake and receive data. + * + * @return 0 on success, negative on error + */ +int tls_client_poll(void); + +/** + * Send data to the server + * + * @param data Data to send + * @param len Length of data + * + * @return bytes sent, or negative on error + */ +int tls_client_send(const void *data, int len); + +/** + * Set SNI hostname for TLS connections (call before tls_client_connect) + * + * @param hostname Server hostname for SNI (e.g., "www.google.com") + */ +void tls_client_set_sni(const char *hostname); + +/** + * Close the TLS connection + */ +void tls_client_close(void); + +/** + * Check if client is connected + * + * @return 1 if connected, 0 otherwise + */ +int tls_client_is_connected(void); + +#ifdef __cplusplus +} +#endif + +#endif /* TLS_CLIENT_H */ diff --git a/src/port/rtl8735b/user_settings.h b/src/port/rtl8735b/user_settings.h new file mode 100644 index 00000000..a20e4eae --- /dev/null +++ b/src/port/rtl8735b/user_settings.h @@ -0,0 +1,109 @@ +/* user_settings.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + * + * wolfIP is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfIP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + * + * wolfSSL config for the RTL8735B (AmebaPro2) wolfIP TLS-client demo, built in + * the RealTek FreeRTOS SDK. Software crypto (no HUK offload for this port); the + * TLS client uses wolfIP sockets via WOLFSSL_USER_IO and the wolfssl_io.c + * bridge. Certificate verification is disabled in the demo (VERIFY_NONE), so no + * CA store or RTC is required. Crypto set mirrors the proven wolfcrypt_huk_tls + * example (ECDHE-ECDSA P-256, AES-GCM, SHA-2) so the TLS target should present + * an ECDSA certificate. Only compiled when ENABLE_TLS_CLIENT is set. + */ +#ifndef RTL8735B_WOLFIP_USER_SETTINGS_H +#define RTL8735B_WOLFIP_USER_SETTINGS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* ---- platform / RTOS ---- */ +#define WOLFSSL_GENERAL_ALIGNMENT 4 +#define SIZEOF_LONG_LONG 8 +#define SINGLE_THREADED /* wolfIP + TLS run in one FreeRTOS task */ +#define NO_FILESYSTEM +#define NO_WRITEV +#define NO_MAIN_DRIVER +#define WOLFSSL_USER_IO /* custom SetIORecv/Send; no BSD sockets */ +#define WOLFSSL_NO_SOCK +#define WOLFSSL_SMALL_STACK +#define WOLFSSL_IGNORE_FILE_WARN +#define NO_ERROR_STRINGS + +/* ---- TLS layer: TLS 1.2 + 1.3 client ---- */ +#define NO_OLD_TLS +#define WOLFSSL_TLS13 +#define HAVE_TLS_EXTENSIONS +#define HAVE_SUPPORTED_CURVES +#define HAVE_EXTENDED_MASTER +#define HAVE_ENCRYPT_THEN_MAC +#define HAVE_HKDF /* TLS 1.3 key schedule (kdf.c) */ +#define WOLFSSL_NO_TLS12_RENEGOTIATION +#define NO_SESSION_CACHE +#define WOLFSSL_AEAD_ONLY /* AES-GCM suites only (no CBC-HMAC) */ +#define HAVE_SNI /* wolfSSL_UseSNI in tls_client.c */ +#define NO_ASN_TIME /* no RTC -> demo uses VERIFY_NONE */ + +/* ---- symmetric: AES-GCM ---- */ +#define HAVE_AESGCM +#define WOLFSSL_AES_DIRECT +#define WOLFSSL_AES_256 +#define WOLFSSL_AES_128 +#define GCM_TABLE_4BIT + +/* ---- hashing + DRBG ---- */ +#undef NO_SHA256 +#define WOLFSSL_SHA256 +#define WOLFSSL_SHA384 +#define HAVE_HASHDRBG + +/* ---- ECC / ECDSA / ECDHE (P-256) ---- */ +#define HAVE_ECC +#define HAVE_ECC_SIGN +#define HAVE_ECC_VERIFY +#define HAVE_ECC_DHE +#define ECC_USER_CURVES +#define HAVE_ECC256 +#define ECC_TIMING_RESISTANT +#define WOLFSSL_SP_MATH_ALL + +/* ---- trims ---- */ +#define NO_RSA +#define NO_DSA +#define NO_DH +#define NO_DES3 +#define NO_RC4 +#define NO_MD4 +#define NO_MD5 +#define NO_PWDBASED +#define NO_PKCS12 +#define NO_PKCS8 + +/* ---- custom RNG seed hook (provided in wolfip_rtl8735b.c via the SDK TRNG) ---- */ +#define CUSTOM_RAND_GENERATE_SEED rtl8735b_rand_seed +#ifndef __ASSEMBLER__ + #include + int rtl8735b_rand_seed(unsigned char* output, unsigned int sz); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* RTL8735B_WOLFIP_USER_SETTINGS_H */ diff --git a/src/port/rtl8735b/wolfip_cycle.sh b/src/port/rtl8735b/wolfip_cycle.sh new file mode 100755 index 00000000..225c52d7 --- /dev/null +++ b/src/port/rtl8735b/wolfip_cycle.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash +# Build -> flash -> read the wolfIP RTL8735B (AmebaPro2) demo, hands-free. +# +# Syncs this port's sources into the RealTek FreeRTOS SDK, does a CLEAN cmake +# reconfigure (incremental rebuilds intermittently drop project/src/main.c's +# include path), `make flash`, flashes via amebapro2_flash.sh, then tails the +# console for a window (the demo runs a server forever; there is no done marker). +# +# Usage: +# wolfip_cycle.sh # milestone 1: DHCP + echo +# wolfip_cycle.sh --tls # milestone 2: also build the TLS client +# +# Env overrides (defaults shown): +# WOLFIP_ROOT=~/GitHub/wolfip WOLFSSL_ROOT=~/GitHub/wolfssl +# SDK=~/GitHub/ameba-rtos-pro2 +# ASDK_BIN=~/ameba-pro2-workspace/asdk/asdk-10.3.0/linux/newlib/bin +# LABEL=GENERIC_UART_ttyUSB5 WATCH_SECS=40 +# UART_LOG=/tmp/uart-monitor/latest/$LABEL.log +# FLASH=~/.claude/skills/amebapro2-flash/amebapro2_flash.sh +set -uo pipefail +TLS=OFF +[ "${1:-}" = "--tls" ] && TLS=ON + +: "${WOLFIP_ROOT:=$HOME/GitHub/wolfip}" +: "${WOLFSSL_ROOT:=$HOME/GitHub/wolfssl}" +: "${SDK:=$HOME/GitHub/ameba-rtos-pro2}" +: "${ASDK_BIN:=$HOME/ameba-pro2-workspace/asdk/asdk-10.3.0/linux/newlib/bin}" +: "${LABEL:=GENERIC_UART_ttyUSB5}" +: "${WATCH_SECS:=40}" +: "${UART_LOG:=/tmp/uart-monitor/latest/$LABEL.log}" +: "${FLASH:=$HOME/.claude/skills/amebapro2-flash/amebapro2_flash.sh}" +export PATH="$ASDK_BIN:$PATH" # ASDK 10.3.0; system arm-none-eabi-gcc FAILS + +EXAMPLE=wolfip_eth +SRC="$WOLFIP_ROOT/src/port/rtl8735b" +PROJ="$SDK/project/realtek_amebapro2_v0_example" +COMP="$SDK/component/example/$EXAMPLE" +BDIR="$PROJ/GCC-RELEASE/build_$EXAMPLE" +BIN="$BDIR/flash_ntz.bin" +L="$UART_LOG" + +# Sync sources into the SDK example dir + the project main.c. +mkdir -p "$COMP" +cp "$SRC/main.c" "$PROJ/src/main.c" +cp "$SRC/main.c" "$SRC/user_settings.h" "$SRC/wolfip_eth.cmake" "$COMP/" + +# Clean reconfigure every cycle (reliable; incremental drops the include path). +cd "$PROJ/GCC-RELEASE" +rm -rf "build_$EXAMPLE" && mkdir "build_$EXAMPLE" && cd "build_$EXAMPLE" +CMAKE_ARGS=(.. -G"Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=../toolchain.cmake + -DBUILD_FPGA=OFF -DBUILD_PXP=OFF -DEXAMPLE="$EXAMPLE" + -DWOLFIP_ROOT="$WOLFIP_ROOT" -DWOLFIP_ENABLE_TLS="$TLS") +[ "$TLS" = "ON" ] && CMAKE_ARGS+=(-DWOLFSSL_ROOT="$WOLFSSL_ROOT") +cmake "${CMAKE_ARGS[@]}" > /tmp/wolfip_cycle_cfg.log 2>&1 + +if ! make flash -j8 > /tmp/wolfip_cycle_build.log 2>&1; then + echo "=== BUILD FAIL (TLS=$TLS) ===" + grep -nE ": error:|undefined reference|fatal error" /tmp/wolfip_cycle_build.log \ + | grep -viE "video_dprintf|CHK_MSG|\"error|error: only|error:invalid|error:model|error: model" \ + | head -30 + exit 1 +fi +echo "=== build ok (TLS=$TLS); flashing ===" + +"$FLASH" "$BIN" > /tmp/wolfip_cycle_flash.log 2>&1 +grep -E "download mode|uartfwburn" /tmp/wolfip_cycle_flash.log | sed 's/\x1b\[[0-9;]*m//g' + +# Tail the console for a window; anchor on the last LOG CLEARED (boot POR). +CLEAR_MARK="--- LOG CLEARED" +run_slice() { + awk -v c="$CLEAR_MARK" ' + index($0,c){buf=""} + {buf = buf $0 ORS} + END{printf "%s", buf}' "$L" 2>/dev/null +} +for _ in $(seq 1 $((WATCH_SECS / 2))); do + run_slice | grep -qiE "DHCP bound|Link up|echo server listening" && break + sleep 2 +done +echo "=== UART (this run) ===" +run_slice | sed 's/\x1b\[[0-9;]*m//g' diff --git a/src/port/rtl8735b/wolfip_eth.cmake b/src/port/rtl8735b/wolfip_eth.cmake new file mode 100644 index 00000000..6ff37f94 --- /dev/null +++ b/src/port/rtl8735b/wolfip_eth.cmake @@ -0,0 +1,111 @@ +# wolfIP RTL8735B (AmebaPro2) Ethernet example -- RealTek FreeRTOS SDK wiring. +# +# Install at /component/example/wolfip_eth/ (the wolfip_cycle.sh helper +# copies main.c, user_settings.h and this .cmake there), then select with: +# cmake .. -DEXAMPLE=wolfip_eth -DWOLFIP_ROOT=/path/to/wolfip \ +# -DWOLFSSL_ROOT=/path/to/wolfssl ... +# (also copy main.c to /project/realtek_amebapro2_v0_example/src/main.c) +# +# Adds the wolfIP stack + the RTL8735B MAC glue to the SDK app build. When +# ENABLE_TLS_CLIENT is set it also pulls the wolfSSL TLS layer + the wolfIP TLS +# client. Set -DWOLFIP_ENABLE_TLS=ON to build the TLS-client demo (milestone 2); +# the default is the echo/DHCP demo (milestone 1). + +### ---- wolfIP root ---- ### +if(NOT DEFINED WOLFIP_ROOT OR WOLFIP_ROOT STREQUAL "") + if(DEFINED ENV{WOLFIP_ROOT}) + set(WOLFIP_ROOT $ENV{WOLFIP_ROOT}) + else() + set(WOLFIP_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../../../wolfip) + endif() +endif() +if(NOT EXISTS "${WOLFIP_ROOT}/src/wolfip.c") + message(FATAL_ERROR + "WOLFIP_ROOT='${WOLFIP_ROOT}' is not a wolfIP tree. " + "Pass -DWOLFIP_ROOT=/path/to/wolfip.") +endif() +message(STATUS "wolfIP RTL8735B example: WOLFIP_ROOT=${WOLFIP_ROOT}") + +set(WOLFIP_PORT_DIR ${WOLFIP_ROOT}/src/port/rtl8735b) + +### ---- header search paths (example dir first so our config.h wins) ---- ### +list(APPEND app_example_inc_path + ${CMAKE_CURRENT_LIST_DIR} # user_settings.h (this example dir) + ${WOLFIP_PORT_DIR} # config.h, rtl8735b_eth.h, tls_client.h + ${WOLFIP_ROOT} # wolfip.h +) + +### ---- wolfIP compile definitions ---- ### +# The SDK's lwIP posix-compat layer puts a foreign on the include +# path; tell wolfIP to skip probing for system socket headers and use its own +# POSIX type fallbacks (avoids iovec/msghdr redefinition and a send() macro). +list(APPEND app_example_flags + WOLFIP_NO_SYS_HEADERS +) + +### ---- wolfIP core + RTL8735B MAC glue ---- ### +list(APPEND app_example_sources + ${WOLFIP_ROOT}/src/wolfip.c + ${WOLFIP_PORT_DIR}/wolfip_rtl8735b.c +) + +### ---- optional TLS client (milestone 2) ---- ### +option(WOLFIP_ENABLE_TLS "Build the wolfIP TLS-client demo" OFF) +if(WOLFIP_ENABLE_TLS) + if(NOT DEFINED WOLFSSL_ROOT OR WOLFSSL_ROOT STREQUAL "") + if(DEFINED ENV{WOLFSSL_ROOT}) + set(WOLFSSL_ROOT $ENV{WOLFSSL_ROOT}) + else() + set(WOLFSSL_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../../../wolfssl) + endif() + endif() + if(NOT EXISTS "${WOLFSSL_ROOT}/wolfcrypt/src/aes.c") + message(FATAL_ERROR + "WOLFSSL_ROOT='${WOLFSSL_ROOT}' is not a wolfSSL tree. " + "Pass -DWOLFSSL_ROOT=/path/to/wolfssl.") + endif() + message(STATUS "wolfIP RTL8735B example: TLS on, WOLFSSL_ROOT=${WOLFSSL_ROOT}") + + list(APPEND app_example_inc_path ${WOLFSSL_ROOT}) + list(APPEND app_example_flags WOLFSSL_USER_SETTINGS ENABLE_TLS_CLIENT) + + list(APPEND app_example_sources + # --- wolfIP TLS glue + client --- + ${WOLFIP_ROOT}/src/port/wolfssl_io.c + ${WOLFIP_PORT_DIR}/tls_client.c + # --- wolfCrypt --- + ${WOLFSSL_ROOT}/wolfcrypt/src/aes.c + ${WOLFSSL_ROOT}/wolfcrypt/src/sha256.c + ${WOLFSSL_ROOT}/wolfcrypt/src/sha512.c + ${WOLFSSL_ROOT}/wolfcrypt/src/sha.c + ${WOLFSSL_ROOT}/wolfcrypt/src/hash.c + ${WOLFSSL_ROOT}/wolfcrypt/src/hmac.c + ${WOLFSSL_ROOT}/wolfcrypt/src/kdf.c + ${WOLFSSL_ROOT}/wolfcrypt/src/random.c + ${WOLFSSL_ROOT}/wolfcrypt/src/memory.c + ${WOLFSSL_ROOT}/wolfcrypt/src/wc_port.c + ${WOLFSSL_ROOT}/wolfcrypt/src/error.c + ${WOLFSSL_ROOT}/wolfcrypt/src/logging.c + ${WOLFSSL_ROOT}/wolfcrypt/src/wc_encrypt.c + ${WOLFSSL_ROOT}/wolfcrypt/src/ecc.c + ${WOLFSSL_ROOT}/wolfcrypt/src/asn.c + ${WOLFSSL_ROOT}/wolfcrypt/src/coding.c + ${WOLFSSL_ROOT}/wolfcrypt/src/sp_int.c + ${WOLFSSL_ROOT}/wolfcrypt/src/wolfmath.c + # --- wolfSSL TLS layer --- + ${WOLFSSL_ROOT}/src/internal.c + ${WOLFSSL_ROOT}/src/keys.c + ${WOLFSSL_ROOT}/src/tls.c + ${WOLFSSL_ROOT}/src/tls13.c + ${WOLFSSL_ROOT}/src/wolfio.c + ${WOLFSSL_ROOT}/src/ssl.c + ${WOLFSSL_ROOT}/src/ssl_load.c + ${WOLFSSL_ROOT}/src/ssl_certman.c + ${WOLFSSL_ROOT}/src/ssl_misc.c + ${WOLFSSL_ROOT}/src/ssl_sess.c + ${WOLFSSL_ROOT}/src/ssl_asn1.c + ${WOLFSSL_ROOT}/src/ssl_crypto.c + ${WOLFSSL_ROOT}/src/x509.c + ${WOLFSSL_ROOT}/src/x509_str.c + ) +endif() diff --git a/src/port/rtl8735b/wolfip_rtl8735b.c b/src/port/rtl8735b/wolfip_rtl8735b.c new file mode 100644 index 00000000..1492a24e --- /dev/null +++ b/src/port/rtl8735b/wolfip_rtl8735b.c @@ -0,0 +1,241 @@ +/* wolfip_rtl8735b.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + * + * wolfIP is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfIP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + * + * wolfIP <-> AmebaPro2 (RTL8735B) mbed Ethernet HAL glue. Polled RX: wolfIP's + * poll() drains the RX ring via ethernet_receive()/ethernet_read(); the MAC + * IRQ hook only latches link up/down. + */ + +#include +#include +#include "config.h" /* LINK_MTU */ +#include "wolfip.h" +#include "wolfip_rtl8735b.h" + +/* Vendor FreeRTOS SDK headers (AmebaPro2). Present only in the SDK build. */ +#include "ethernet_api.h" +#include "ethernet_ex_api.h" +#include "hal_trng_sec.h" + +/* TX/RX descriptor counts, matching the vendor ethernet_mii reference. */ +#ifndef RTL8735B_TX_DESC_NO +#define RTL8735B_TX_DESC_NO 8 +#endif +#ifndef RTL8735B_RX_DESC_NO +#define RTL8735B_RX_DESC_NO 8 +#endif + +/* MAC IRQ event codes (EthInt* enum in the vendor fwlib rtl8735b_eth.h), given + * numerically to avoid pulling in the deep SoC CMSIS header. */ +#define RTL_ETH_EVT_LINK_UP 7 +#define RTL_ETH_EVT_LINK_DOWN 8 + +/* Descriptor rings live in the reserved RAM_REV SRAM region (DMA-reachable, + * 32-byte aligned) exposed by the linker, as in the vendor driver. */ +extern uint32_t __sram_rev_start__[]; + +/* Vendor heap allocator; DMA packet buffers come from heap region index 1. */ +extern void *pvPortMallocExt(size_t size, int idx); +extern void vPortFree(void *pv); + +static uint8_t *tx_pkt_buf = NULL; +static uint8_t *rx_pkt_buf = NULL; +static volatile int link_up = 0; + +/* Release the DMA packet buffers (idempotent). */ +static void free_eth_bufs(void) +{ + if (tx_pkt_buf != NULL) { + vPortFree(tx_pkt_buf); + tx_pkt_buf = NULL; + } + if (rx_pkt_buf != NULL) { + vPortFree(rx_pkt_buf); + rx_pkt_buf = NULL; + } +} + +/* ethernet_write()/ethernet_read() require a 32-byte-aligned data buffer; + * wolfIP frame buffers are not, so stage every frame through these. */ +static uint8_t tx_bounce[ETH_PKT_BUF_SIZE] __attribute__((aligned(32))); +static uint8_t rx_bounce[ETH_PKT_BUF_SIZE] __attribute__((aligned(32))); + +static void rtl8735b_eth_irq(uint32_t event, uint32_t data) +{ + (void)data; + if (event == RTL_ETH_EVT_LINK_UP) + link_up = 1; + else if (event == RTL_ETH_EVT_LINK_DOWN) + link_up = 0; +} + +/* wolfIP RX pull: return one frame's length (>0), 0 if none. */ +static int eth_poll(struct wolfIP_ll_dev *ll, void *buf, uint32_t len) +{ + int sz; + (void)ll; + + sz = ethernet_receive(); + if (sz <= 0) + return 0; + if ((uint32_t)sz > sizeof(rx_bounce)) + sz = (int)sizeof(rx_bounce); + (void)ethernet_read((char *)rx_bounce, sz); + if ((uint32_t)sz > len) + sz = (int)len; + memcpy(buf, rx_bounce, (size_t)sz); + return sz; +} + +/* wolfIP TX: send one complete Ethernet frame (MAC auto-pads short frames). */ +static int eth_send(struct wolfIP_ll_dev *ll, void *buf, uint32_t len) +{ + (void)ll; + + if (buf == NULL || len == 0 || len > sizeof(tx_bounce)) + return -1; + memcpy(tx_bounce, buf, len); + if (ethernet_write((const char *)tx_bounce, (int)len) <= 0) + return -2; + /* ethernet_send() returns the transmitted frame size; <= 0 means the DMA + * did not accept it, so report the failure to wolfIP. */ + if (ethernet_send() <= 0) + return -3; + return (int)len; +} + +/* Read one word from the secure hardware TRNG (lazy init). Returns 0 on + * success, -1 if the TRNG is unavailable. Single source for both the stack + * entropy and the wolfCrypt seed so init lives in one place. */ +static int rtl8735b_trng_read(uint32_t *out) +{ + static int inited = 0; + + if (inited == 0 && hal_trng_sec_init() == 0) + inited = 1; + if (!inited) + return -1; + *out = (uint32_t)hal_trng_sec_get_rand(); + return 0; +} + +/* wolfIP entropy for TCP ISNs, ephemeral ports and DHCP xids. wolfIP_getrandom + * has no error channel, so on TRNG failure fall back to a non-constant LCG + * (weak, but avoids fixed values); the crypto seed path below fails closed + * instead. */ +uint32_t wolfIP_getrandom(void) +{ + static uint32_t lcg = 0x2545F491u; + uint32_t r; + + if (rtl8735b_trng_read(&r) == 0) + return r; + lcg = (lcg * 1664525u) + 1013904223u; + return lcg; +} + +/* wolfCrypt RNG seed hook (user_settings CUSTOM_RAND_GENERATE_SEED). Fails + * closed: if the TRNG is unavailable it returns an error rather than seeding + * the DRBG from a predictable fallback. Referenced only in the TLS build. */ +int rtl8735b_rand_seed(unsigned char *output, unsigned int sz) +{ + unsigned int i, n; + uint32_t r; + + for (i = 0; i < sz; ) { + if (rtl8735b_trng_read(&r) != 0) + return -1; + n = (sz - i) < 4u ? (sz - i) : 4u; + memcpy(output + i, &r, n); + i += n; + } + return 0; +} + +int rtl8735b_eth_link_up(void) +{ + return link_up; +} + +void rtl8735b_eth_phy_maintain(void) +{ + if (link_up) + ethernet_detect_phy_state(); +} + +int rtl8735b_eth_init(struct wolfIP_ll_dev *ll, const uint8_t *mac) +{ + uint8_t local_mac[6]; + uint8_t *tx_desc; + uint8_t *rx_desc; + + if (ll == NULL) + return -1; + + /* Descriptor rings in reserved SRAM: TX first, RX right after. */ + tx_desc = (uint8_t *)__sram_rev_start__; + rx_desc = tx_desc + (RTL8735B_TX_DESC_NO * ETH_TX_DESC_SIZE); + + /* DMA packet buffers from the vendor DMA-capable heap. */ + tx_pkt_buf = (uint8_t *)pvPortMallocExt(RTL8735B_TX_DESC_NO * ETH_PKT_BUF_SIZE, 1); + rx_pkt_buf = (uint8_t *)pvPortMallocExt(RTL8735B_RX_DESC_NO * ETH_PKT_BUF_SIZE, 1); + if (tx_pkt_buf == NULL || rx_pkt_buf == NULL) { + free_eth_bufs(); + return -2; + } + + memset(tx_desc, 0, RTL8735B_TX_DESC_NO * ETH_TX_DESC_SIZE); + memset(rx_desc, 0, RTL8735B_RX_DESC_NO * ETH_RX_DESC_SIZE); + memset(tx_pkt_buf, 0, RTL8735B_TX_DESC_NO * ETH_PKT_BUF_SIZE); + memset(rx_pkt_buf, 0, RTL8735B_RX_DESC_NO * ETH_PKT_BUF_SIZE); + + /* MAC: caller-supplied, else efuse, else locally-administered. */ + if (mac == NULL) { + ethernet_address((char *)local_mac); + if ((local_mac[0] | local_mac[1] | local_mac[2] | + local_mac[3] | local_mac[4] | local_mac[5]) == 0) { + local_mac[0] = 0x02; local_mac[1] = 0xE0; local_mac[2] = 0x4C; + local_mac[3] = 0x87; local_mac[4] = 0x35; local_mac[5] = 0xB0; + } + mac = local_mac; + } + + /* Hook link events, wire rings/buffers/MAC into the HAL, then start. */ + ethernet_irq_hook(rtl8735b_eth_irq); + ethernet_set_descnum(RTL8735B_TX_DESC_NO, RTL8735B_RX_DESC_NO); + ethernet_trx_pre_setting(tx_desc, rx_desc, tx_pkt_buf, rx_pkt_buf); + ethernet_set_address((char *)mac); + if (ethernet_init() != 0) { + free_eth_bufs(); + return -3; + } + if (ethernet_link()) + link_up = 1; + + memcpy(ll->mac, mac, sizeof(ll->mac)); + strncpy(ll->ifname, "eth0", sizeof(ll->ifname) - 1); + ll->ifname[sizeof(ll->ifname) - 1] = '\0'; + ll->non_ethernet = 0; + ll->mtu = LINK_MTU; /* full L2 frame buffer size, not the IP MTU */ + ll->poll = eth_poll; + ll->send = eth_send; + + return 0; +} diff --git a/src/port/rtl8735b/wolfip_rtl8735b.h b/src/port/rtl8735b/wolfip_rtl8735b.h new file mode 100644 index 00000000..c3c90719 --- /dev/null +++ b/src/port/rtl8735b/wolfip_rtl8735b.h @@ -0,0 +1,45 @@ +/* wolfip_rtl8735b.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfIP TCP/IP stack. + * + * wolfIP is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfIP is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + * + * wolfIP Ethernet driver for the RealTek RTL8735B (AmebaPro2). A thin adapter + * binding wolfIP's poll/send callbacks onto the vendor FreeRTOS SDK mbed + * Ethernet HAL, which owns the DMA rings and cache handling. Only compiles in + * the AmebaPro2 SDK build; wolfIP owns the stack (the vendor lwIP glue is not + * used). + */ +#ifndef WOLFIP_RTL8735B_H +#define WOLFIP_RTL8735B_H + +#include +#include "wolfip.h" + +/* Bring up the MAC + FEPHY and install poll/send on ll. mac may be NULL to use + * the chip efuse MAC (falling back to a locally-administered one if blank). + * Returns 0 on success, negative on error. */ +int rtl8735b_eth_init(struct wolfIP_ll_dev *ll, const uint8_t *mac); + +/* 1 if link is up, 0 if down (updated by the MAC IRQ hook). */ +int rtl8735b_eth_link_up(void); + +/* Periodic FEPHY software-patch check; call ~every 10s while linked (no-op when + * link is down). */ +void rtl8735b_eth_phy_maintain(void); + +#endif /* WOLFIP_RTL8735B_H */ diff --git a/wolfip.h b/wolfip.h index 1a5b8e7c..6700983a 100644 --- a/wolfip.h +++ b/wolfip.h @@ -294,7 +294,12 @@ typedef uint32_t socklen_t; #define WOLFIP_HAVE_POSIX_TYPES 1 #endif -#if defined(__has_include) +/* WOLFIP_NO_SYS_HEADERS: opt out of probing for system socket headers. Some + * bare-metal/RTOS SDKs (e.g. an lwIP posix-compat layer) put a + * on the include path that is NOT the host libc's -- it redefines iovec/msghdr + * and a send() macro that collide with wolfIP. Define WOLFIP_NO_SYS_HEADERS to + * skip the probe and use wolfIP's own POSIX type fallbacks below. */ +#if defined(__has_include) && !defined(WOLFIP_NO_SYS_HEADERS) #if __has_include() #include #if !defined(WOLFIP_HAVE_POSIX_TYPES) && __has_include()