ether2ser 0.1.0
Ethernet <-> synchronous V.24 bridge firmware for RP2040 + W5500
Loading...
Searching...
No Matches
w5500_driver.c
Go to the documentation of this file.
1/*
2 * ether2ser — Ethernet <-> synchronous V.24 (RS-232/V.28) bridge
3 *
4 * File: src/drivers/w5500_driver.c
5 * Purpose: W5500 driver implementation and helpers.
6 *
7 * SPDX-License-Identifier: Apache-2.0
8 *
9 * Copyright (c) 2026 Florian <f.leuze@outlook.de>
10 */
11
12// Related headers
13#include "w5500_driver.h"
14
15// Standard library headers
16#include <stdbool.h>
17#include <stdint.h>
18#include <stdio.h>
19#include <string.h>
20
21// Library Headers
22#include "socket.h"
23#include "w5500.h"
24#include "wizchip_conf.h"
25#include "wizchip_qspi_pio.h"
26#include "wizchip_spi.h"
27
28// Project Headers
29#include "system/common.h"
30#include "system/error.h"
31
32// Generated headers
33
34// Default network configuration
35
36// Socket
37#define IP_SOCKET 0 // Socket number for IPRAW
38#define UDP_SOCKET 1
39#define UDP_MAX_PAYLOAD_BYTES 1472U
40#define UDP_W5500_OVERHEAD_BYTES 8U
41#define UDP_RX_REQUIRED_BYTES (UDP_MAX_PAYLOAD_BYTES + UDP_W5500_OVERHEAD_BYTES)
42
43void w5500_poll_udp_buffer_full_events(uint64_t* rx_full_enter_events,
44 uint64_t* tx_full_enter_events)
45{
46 static uint64_t rx_events = 0;
47 static uint64_t tx_events = 0;
48 static bool rx_was_full = false;
49 static bool tx_was_full = false;
50
51 uint16_t rx_used_bytes = getSn_RX_RSR(UDP_SOCKET);
52 uint16_t rx_cap_bytes = (uint16_t)getSn_RXBUF_SIZE(UDP_SOCKET) * 1024U;
53
54 uint16_t tx_free_bytes = getSn_TX_FSR(UDP_SOCKET);
55 uint16_t tx_cap_bytes = (uint16_t)getSn_TXBUF_SIZE(UDP_SOCKET) * 1024U;
56
57 // For UDP RX overload visibility, "full" means there is not enough free
58 // room for one max-size datagram (+ W5500 UDP metadata), which is when
59 // incoming drops can start even before 100% occupancy.
60 uint16_t rx_free_bytes =
61 (rx_used_bytes < rx_cap_bytes) ? (uint16_t)(rx_cap_bytes - rx_used_bytes) : (uint16_t)0;
62 bool rx_is_full = (rx_cap_bytes > 0U) && (rx_free_bytes < UDP_RX_REQUIRED_BYTES);
63 bool tx_is_full = (tx_cap_bytes > 0U) && (tx_free_bytes == 0U);
64
65 if (rx_is_full && !rx_was_full)
66 {
67 rx_events++;
68 }
69 if (tx_is_full && !tx_was_full)
70 {
71 tx_events++;
72 }
73
74 rx_was_full = rx_is_full;
75 tx_was_full = tx_is_full;
76
77 if (rx_full_enter_events)
78 {
79 *rx_full_enter_events = rx_events;
80 }
81 if (tx_full_enter_events)
82 {
83 *tx_full_enter_events = tx_events;
84 }
85}
86
87static void ipv4_calc_broadcast_u8(const uint8_t ip_addr[4], const uint8_t mask[4],
88 uint8_t bcast[4])
89{
90 for (int i = 0; i < 4; i++)
91 {
92 bcast[i] = (uint8_t)((ip_addr[i] & mask[i]) | (uint8_t)(~mask[i]));
93 }
94}
95
97{
98 uint8_t phy_cfg = getPHYCFGR(); // PHY config register
99 LOG_DEBUG("PHY CFG: 0x%02X (Link %s)\r\n", phy_cfg, (phy_cfg & 0x01) ? "UP" : "DOWN");
100
101 uint8_t mode = getSn_MR(IP_SOCKET); // Socket mode register
102 uint8_t status = getSn_SR(IP_SOCKET); // Socket status
103 LOG_DEBUG("Socket Mode: 0x%02X, Status: 0x%02X\r\n", mode, status);
104}
105
106void w5500_udp_tx(UDP_CONFIG_T* send_config, const UDP_FRAME_T* frame)
107{
108 int32_t sent_len = sendto(UDP_SOCKET, frame->payload, (uint16_t)frame->length,
109 send_config->ip_address, send_config->port);
110 LOG_DEBUG("TX %ld bytes to %u.%u.%u.%u:%u\r\n", (long)frame->length, send_config->ip_address[0],
111 send_config->ip_address[1], send_config->ip_address[2], send_config->ip_address[3],
112 send_config->port);
113 if (sent_len < 0)
114 {
115 LOG_DEBUG("sendto() error %ld\r\n", (long)sent_len);
116 }
117}
118
119bool w5500_poll_rx(UDP_CONFIG_T* send_config, UDP_FRAME_T* frame)
120{
121 // Clean up frame buffer first to ensure no data corruption
122 if (frame->length > 0)
123 {
124 memset(frame->payload, 0, frame->length);
125 frame->length = 0;
126 }
127 int32_t recv_len = recvfrom(UDP_SOCKET, frame->payload, RX_BUF_SIZE, send_config->ip_address,
128 &(send_config->port));
129 if (recv_len > 0)
130 {
131 frame->length = (size_t)recv_len;
132 LOG_DEBUG("RX %ld bytes from %u.%u.%u.%u:%u\r\n", (long)recv_len,
133 send_config->ip_address[0], send_config->ip_address[1],
134 send_config->ip_address[2], send_config->ip_address[3], send_config->port);
135
136 // Print first 16 bytes as hex (or less if packet is smaller)
137 LOG_DEBUG("Data: ");
138 int print_len = (recv_len < 16) ? recv_len : 16;
139 for (int i = 0; i < print_len; i++)
140 {
141 LOG_DEBUG("%02X ", frame->payload[i]);
142 }
143 LOG_DEBUG("\r\n");
144 return true;
145 }
146 return false;
147}
148
150{
151 int8_t ret = socket(IP_SOCKET, Sn_MR_IPRAW, 0, 0);
152 if (ret != IP_SOCKET)
153 {
154 LOG_ERROR("W5500: socket() failed, ret=%d\r\n", (int)ret);
156 }
157 LOG_DEBUG("W5500: IPRAW Socket opened successfully in blocking mode\r\n");
158 return E2S_OK;
159}
160
162{
163 LOG_DEBUG("Starting UDP echo on port %u...\r\n", config->port);
164
165 // socket() allocates one of the W5500's 8 hardware sockets
166 // Parameters: socket_number, protocol_mode, local_port, flags
167 // Returns: socket_number on success, negative on failure
168 int8_t ret = socket(UDP_SOCKET, Sn_MR_UDP, config->port, SF_IO_NONBLOCK);
169 if (ret != UDP_SOCKET)
170 {
171 LOG_ERROR("W5500: socket() failed, ret=%d\r\n", (int)ret);
173 }
174 LOG_DEBUG("W5500: Socket opened successfully in non blocking mode\r\n");
175 return E2S_OK;
176}
178{
179 close(UDP_SOCKET);
180 LOG_DEBUG("W5500: Socket closed\r\n");
181 return w5500_open_udp_socket(config);
182}
183
185{
186 ipv4_calc_broadcast_u8(config->net_info.ip, config->net_info.sn, config->broadcast_address);
187 LOG_DEBUG("Derived broadcast address %u.%u.%u.%u\r\n", config->broadcast_address[0],
188 config->broadcast_address[1], config->broadcast_address[2],
189 config->broadcast_address[3]);
190 network_initialize(config->net_info);
191 print_network_information(config->net_info);
192}
193
195{
196 // [0] TX map, [1] RX map, sockets S0..S7, units = KB
197 uint8_t memsize[2][8] = {
198 {1, 8, 1, 1, 1, 1, 1, 2},
199 {1, 8, 1, 1, 1, 1, 1, 2},
200 };
201
202 if (ctlwizchip(CW_INIT_WIZCHIP, (void*)memsize) == -1)
203 {
204 LOG_ERROR("W5500: failed to apply socket memory map\r\n");
206 }
207
208 LOG_INFO("W5500 socket1 buffers: RX=%uKB TX=%uKB\r\n", getSn_RXBUF_SIZE(UDP_SOCKET),
209 getSn_TXBUF_SIZE(UDP_SOCKET));
210 return E2S_OK;
211}
212
214{
215 // Configure network settings
216 config->net_info = (wiz_NetInfo){.mac = DEFAULT_MAC_ADDR,
217 .ip = DEFAULT_IP_ADDR,
220 .dns = DEFAULT_DNS_ADDR,
221 .dhcp = NETINFO_STATIC};
222
223 w5500_set_network(config);
224}
225
227{
228 LOG_DEBUG("W5500: Init PIO SPI\r\n");
229 wizchip_spi_initialize();
230
231 LOG_DEBUG("W5500: Init critical section\r\n");
232 wizchip_cris_initialize();
233
234 LOG_DEBUG("W5500: Reset chip\r\n");
235 wizchip_reset();
236
237 LOG_DEBUG("W5500: Initialize\r\n");
238 wizchip_initialize();
239
240 LOG_DEBUG("W5500: Apply socket memory map\r\n");
242 {
244 }
245
246 LOG_DEBUG("W5500: Verify chip\r\n");
247 wizchip_check();
248
249 LOG_DEBUG("W5500: Ready\r\n");
250}
#define LOG_INFO(...)
Definition common.h:164
#define LOG_DEBUG(...)
Definition common.h:165
#define LOG_ERROR(...)
Definition common.h:163
void fatal_panic(e2s_error_t reason)
Print error message and panic.
Definition error.c:30
e2s_error_t
Common error codes returned by ether2ser modules.
Definition error.h:27
@ E2S_ERR_W5500_INIT_FAILED
Definition error.h:70
@ E2S_ERR_W5500_SOCKET_OPEN_FAILED
Definition error.h:71
@ E2S_OK
Definition error.h:28
Network configuration wrapper used by the W5500 driver.
wiz_NetInfo net_info
uint8_t broadcast_address[4]
UDP endpoint configuration.
uint8_t ip_address[4]
uint16_t port
UDP payload container.
size_t length
uint8_t * payload
static e2s_error_t w5500_apply_socket_mem_map(void)
void w5500_set_network_defaults(NETWORK_CONFIG_T *config)
Fill network config with compile-time defaults.
#define IP_SOCKET
void w5500_poll_udp_buffer_full_events(uint64_t *rx_full_enter_events, uint64_t *tx_full_enter_events)
void w5500_udp_tx(UDP_CONFIG_T *send_config, const UDP_FRAME_T *frame)
Send one UDP frame through W5500.
void w5500_set_network(NETWORK_CONFIG_T *config)
Apply network configuration to W5500 hardware.
e2s_error_t w5500_open_ipraw_socket(void)
Open IPRAW socket mode.
void w5500_driver_init(void)
Initialize W5500 driver and low-level interface.
e2s_error_t w5500_open_udp_socket(UDP_CONFIG_T *config)
Open configured UDP socket.
bool w5500_poll_rx(UDP_CONFIG_T *send_config, UDP_FRAME_T *frame)
Poll W5500 for received UDP data.
e2s_error_t w5500_reconfigure_udp_socket(UDP_CONFIG_T *config)
Reconfigure UDP socket with new endpoint settings.
#define UDP_RX_REQUIRED_BYTES
void w5500_debug_status(void)
Print W5500 socket/PHY debug status.
static void ipv4_calc_broadcast_u8(const uint8_t ip_addr[4], const uint8_t mask[4], uint8_t bcast[4])
#define UDP_SOCKET
#define DEFAULT_DNS_ADDR
#define RX_BUF_SIZE
#define DEFAULT_SUBNET_MASK
#define DEFAULT_IP_ADDR
#define DEFAULT_MAC_ADDR
#define DEFAULT_GATEWAY_ADDR