summaryrefslogtreecommitdiff
path: root/libhw/w5500.c
diff options
context:
space:
mode:
Diffstat (limited to 'libhw/w5500.c')
-rw-r--r--libhw/w5500.c172
1 files changed, 55 insertions, 117 deletions
diff --git a/libhw/w5500.c b/libhw/w5500.c
index cebcf8e..dfe169f 100644
--- a/libhw/w5500.c
+++ b/libhw/w5500.c
@@ -1,6 +1,6 @@
/* libhw/w5500.c - <libhw/generic/net.h> implementation for the WIZnet W5500 chip
*
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* -----------------------------------------------------------------------------
@@ -74,7 +74,6 @@
#include <hardware/gpio.h> /* pico-sdk:hardware_gpio */
#include <libcr/coroutine.h> /* for cr_yield() */
-#include <libmisc/vcall.h> /* for VCALL_SELF() */
#include <libhw/generic/alarmclock.h> /* for sleep_*() */
@@ -124,61 +123,12 @@ static const char *w5500_state_str(uint8_t state) {
}
}
-/* vtables ********************************************************************/
-
-/* iface */
-static struct net_eth_addr w5500_if_hwaddr (implements_net_iface *);
-static void w5500_if_up (implements_net_iface *, struct net_iface_config);
-static void w5500_if_down (implements_net_iface *);
-static implements_net_stream_listener *w5500_if_tcp_listen (implements_net_iface *, uint16_t local_port);
-static implements_net_stream_conn *w5500_if_tcp_dial (implements_net_iface *, struct net_ip4_addr, uint16_t remote_port);
-static implements_net_packet_conn *w5500_if_udp_conn (implements_net_iface *, uint16_t local_port);
-
-/* stream_listener */
-static implements_net_stream_conn *w5500_tcplist_accept(implements_net_stream_listener *);
-static int w5500_tcplist_close (implements_net_stream_listener *);
-
-/* stream_conn */
-static void w5500_tcp_set_read_deadline (implements_net_stream_conn *, uint64_t ns);
-static ssize_t w5500_tcp_read (implements_net_stream_conn *, void *, size_t);
-static ssize_t w5500_tcp_write (implements_net_stream_conn *, void *, size_t);
-static int w5500_tcp_close (implements_net_stream_conn *, bool rd, bool wr);
-
-/* packet_conn */
-static void w5500_udp_set_read_deadline (implements_net_packet_conn *, uint64_t ns);
-static ssize_t w5500_udp_recvfrom (implements_net_packet_conn *, void *, size_t, struct net_ip4_addr *, uint16_t *);
-static ssize_t w5500_udp_sendto (implements_net_packet_conn *, void *, size_t, struct net_ip4_addr, uint16_t);
-static int w5500_udp_close (implements_net_packet_conn *);
-
-/* tables */
-
-static struct net_iface_vtable w5500_iface_vtable = {
- .hwaddr = w5500_if_hwaddr,
- .ifup = w5500_if_up,
- .ifdown = w5500_if_down,
- .tcp_listen = w5500_if_tcp_listen,
- .tcp_dial = w5500_if_tcp_dial,
- .udp_conn = w5500_if_udp_conn,
-};
-
-static struct net_stream_listener_vtable w5500_tcp_listener_vtable = {
- .accept = w5500_tcplist_accept,
- .close = w5500_tcplist_close,
-};
-
-static struct net_stream_conn_vtable w5500_tcp_conn_vtable = {
- .set_read_deadline = w5500_tcp_set_read_deadline,
- .read = w5500_tcp_read,
- .write = w5500_tcp_write,
- .close = w5500_tcp_close,
-};
-
-static struct net_packet_conn_vtable w5500_udp_conn_vtable = {
- .set_read_deadline = w5500_udp_set_read_deadline,
- .recvfrom = w5500_udp_recvfrom,
- .sendto = w5500_udp_sendto,
- .close = w5500_udp_close,
-};
+/* libobj *********************************************************************/
+
+LO_IMPLEMENTATION_C(net_stream_listener, struct _w5500_socket, w5500_tcplist, static)
+LO_IMPLEMENTATION_C(net_stream_conn, struct _w5500_socket, w5500_tcp, static)
+LO_IMPLEMENTATION_C(net_packet_conn, struct _w5500_socket, w5500_udp, static)
+LO_IMPLEMENTATION_C(net_iface, struct w5500, w5500_if, static)
/* mid-level utilities ********************************************************/
@@ -311,9 +261,6 @@ static inline void w5500_socket_close(struct _w5500_socket *socket) {
}
#define ASSERT_SELF(_iface, _mode) \
- struct _w5500_socket *socket = \
- VCALL_SELF(struct _w5500_socket, \
- implements_net_##_iface, _socket); \
assert(socket); \
uint8_t socknum = socket->socknum; \
assert(socknum < 8); \
@@ -333,15 +280,14 @@ static void w5500_intrhandler(uint gpio, uint32_t LM_UNUSED(event_mask)) {
}
void _w5500_init(struct w5500 *chip,
- implements_spi *spi, uint pin_intr, uint pin_reset,
+ lo_interface spi spi, uint pin_intr, uint pin_reset,
struct net_eth_addr addr) {
assert(chip);
- assert(spi);
+ assert(!LO_IS_NULL(spi));
/* Initialize the data structures. */
*chip = (struct w5500){
/* const-after-init */
- .implements_net_iface = { .vtable = &w5500_iface_vtable },
.spidev = spi,
.pin_intr = pin_intr,
.pin_reset = pin_reset,
@@ -353,9 +299,6 @@ void _w5500_init(struct w5500 *chip,
for (uint8_t i = 0; i < 8; i++) {
chip->sockets[i] = (struct _w5500_socket){
/* const-after-init */
- .implements_net_stream_listener = { .vtable = &w5500_tcp_listener_vtable },
- .implements_net_stream_conn = { .vtable = &w5500_tcp_conn_vtable },
- .implements_net_packet_conn = { .vtable = &w5500_udp_conn_vtable },
.socknum = i,
/* mutable */
.next_free = (i + 1 < 8) ? &chip->sockets[i+1] : NULL,
@@ -449,15 +392,13 @@ void w5500_soft_reset(struct w5500 *chip) {
cr_mutex_unlock(&chip->mu);
}
-static struct net_eth_addr w5500_if_hwaddr(implements_net_iface *_chip) {
- struct w5500 *chip = VCALL_SELF(struct w5500, implements_net_iface, _chip);
+static struct net_eth_addr w5500_if_hwaddr(struct w5500 *chip) {
assert(chip);
return chip->hwaddr;
}
-static void _w5500_if_up(implements_net_iface *_chip, struct net_iface_config cfg) {
- struct w5500 *chip = VCALL_SELF(struct w5500, implements_net_iface, _chip);
+static void _w5500_if_up(struct w5500 *chip, struct net_iface_config cfg) {
assert(chip);
cr_mutex_lock(&chip->mu);
@@ -469,27 +410,26 @@ static void _w5500_if_up(implements_net_iface *_chip, struct net_iface_config cf
cr_mutex_unlock(&chip->mu);
}
-static void w5500_if_up(implements_net_iface *_chip, struct net_iface_config cfg) {
+static void w5500_if_ifup(struct w5500 *chip, struct net_iface_config cfg) {
debugf("if_up()");
debugf(":: addr = "PRI_net_ip4_addr, ARG_net_ip4_addr(cfg.addr));
debugf(":: gateway_addr = "PRI_net_ip4_addr, ARG_net_ip4_addr(cfg.gateway_addr));
debugf(":: subnet_mask = "PRI_net_ip4_addr, ARG_net_ip4_addr(cfg.subnet_mask));
- _w5500_if_up(_chip, cfg);
+ _w5500_if_up(chip, cfg);
}
-static void w5500_if_down(implements_net_iface *_chip) {
+static void w5500_if_ifdown(struct w5500 *chip) {
debugf("if_down()");
- _w5500_if_up(_chip, (struct net_iface_config){0});
+ _w5500_if_up(chip, (struct net_iface_config){0});
}
-static implements_net_stream_listener *w5500_if_tcp_listen(implements_net_iface *_chip, uint16_t local_port) {
- struct w5500 *chip = VCALL_SELF(struct w5500, implements_net_iface, _chip);
+static lo_interface net_stream_listener w5500_if_tcp_listen(struct w5500 *chip, uint16_t local_port) {
assert(chip);
struct _w5500_socket *sock = w5500_alloc_socket(chip);
if (!sock) {
debugf("tcp_listen() => no sock");
- return NULL;
+ return LO_NULL(net_stream_listener);
}
debugf("tcp_listen() => sock[%"PRIu8"]", sock->socknum);
@@ -502,12 +442,11 @@ static implements_net_stream_listener *w5500_if_tcp_listen(implements_net_iface
sock->read_deadline_ns = 0;
sock->list_open = true;
- return &sock->implements_net_stream_listener;
+ return lo_box_w5500_tcplist_as_net_stream_listener(sock);
}
-static implements_net_stream_conn *w5500_if_tcp_dial(implements_net_iface *_chip,
- struct net_ip4_addr node, uint16_t port) {
- struct w5500 *chip = VCALL_SELF(struct w5500, implements_net_iface, _chip);
+static lo_interface net_stream_conn w5500_if_tcp_dial(struct w5500 *chip,
+ struct net_ip4_addr node, uint16_t port) {
assert(chip);
assert(memcmp(node.octets, net_ip4_addr_zero.octets, 4));
assert(memcmp(node.octets, net_ip4_addr_broadcast.octets, 4));
@@ -516,7 +455,7 @@ static implements_net_stream_conn *w5500_if_tcp_dial(implements_net_iface *_chip
struct _w5500_socket *socket = w5500_alloc_socket(chip);
if (!socket) {
debugf("tcp_dial() => no sock");
- return NULL;
+ return LO_NULL(net_stream_conn);
}
uint8_t socknum = socket->socknum;
debugf("tcp_dial() => sock[%"PRIu8"]", socknum);
@@ -553,21 +492,20 @@ static implements_net_stream_conn *w5500_if_tcp_dial(implements_net_iface *_chip
cr_yield();
break;
case STATE_TCP_ESTABLISHED:
- return &socket->implements_net_stream_conn;
+ return lo_box_w5500_tcp_as_net_stream_conn(socket);
default:
goto restart;
}
}
}
-static implements_net_packet_conn *w5500_if_udp_conn(implements_net_iface *_chip, uint16_t local_port) {
- struct w5500 *chip = VCALL_SELF(struct w5500, implements_net_iface, _chip);
+static lo_interface net_packet_conn w5500_if_udp_conn(struct w5500 *chip, uint16_t local_port) {
assert(chip);
struct _w5500_socket *socket = w5500_alloc_socket(chip);
if (!socket) {
debugf("udp_conn() => no sock");
- return NULL;
+ return LO_NULL(net_packet_conn);
}
uint8_t socknum = socket->socknum;
debugf("udp_conn() => sock[%"PRIu8"]", socknum);
@@ -590,18 +528,18 @@ static implements_net_packet_conn *w5500_if_udp_conn(implements_net_iface *_chip
cr_yield();
cr_mutex_unlock(&chip->mu);
- return &socket->implements_net_packet_conn;
+ return lo_box_w5500_udp_as_net_packet_conn(socket);
}
/* tcp_listener methods *******************************************************/
-static implements_net_stream_conn *w5500_tcplist_accept(implements_net_stream_listener *_socket) {
+static lo_interface net_stream_conn w5500_tcplist_accept(struct _w5500_socket *socket) {
ASSERT_SELF(stream_listener, TCP);
restart:
if (!socket->list_open) {
debugf("tcp_listener.accept() => already closed");
- return NULL;
+ return LO_NULL(net_stream_conn);
}
cr_mutex_lock(&chip->mu);
@@ -630,14 +568,14 @@ static implements_net_stream_conn *w5500_tcplist_accept(implements_net_stream_li
/* fall-through */
case STATE_TCP_CLOSE_WAIT:
socket->write_open = true;
- return &socket->implements_net_stream_conn;
+ return lo_box_w5500_tcp_as_net_stream_conn(socket);
default:
goto restart;
}
}
}
-static int w5500_tcplist_close(implements_net_stream_listener *_socket) {
+static int w5500_tcplist_close(struct _w5500_socket *socket) {
debugf("tcp_listener.close()");
ASSERT_SELF(stream_listener, TCP);
@@ -648,7 +586,7 @@ static int w5500_tcplist_close(implements_net_stream_listener *_socket) {
/* tcp_conn methods ***********************************************************/
-static ssize_t w5500_tcp_write(implements_net_stream_conn *_socket, void *buf, size_t count) {
+static ssize_t w5500_tcp_write(struct _w5500_socket *socket, void *buf, size_t count) {
debugf("tcp_conn.write(%zu)", count);
ASSERT_SELF(stream_conn, TCP);
assert(buf);
@@ -725,7 +663,7 @@ static ssize_t w5500_tcp_write(implements_net_stream_conn *_socket, void *buf, s
return done;
}
-static void w5500_tcp_set_read_deadline(implements_net_stream_conn *_socket, uint64_t ns) {
+static void w5500_tcp_set_read_deadline(struct _w5500_socket *socket, uint64_t ns) {
debugf("tcp_conn.set_read_deadline(%"PRIu64")", ns);
ASSERT_SELF(stream_conn, TCP);
socket->read_deadline_ns = ns;
@@ -736,7 +674,7 @@ static void w5500_tcp_alarm_handler(void *_arg) {
cr_sema_signal_from_intrhandler(&socket->read_sema);
}
-static ssize_t w5500_tcp_read(implements_net_stream_conn *_socket, void *buf, size_t count) {
+static ssize_t w5500_tcp_read(struct _w5500_socket *socket, void *buf, size_t count) {
debugf("tcp_conn.read()");
ASSERT_SELF(stream_conn, TCP);
assert(buf);
@@ -744,21 +682,21 @@ static ssize_t w5500_tcp_read(implements_net_stream_conn *_socket, void *buf, si
struct alarmclock_trigger trigger = {0};
if (socket->read_deadline_ns)
- VCALL(bootclock, add_trigger, &trigger,
- socket->read_deadline_ns,
- w5500_tcp_alarm_handler,
- socket);
+ LO_CALL(bootclock, add_trigger, &trigger,
+ socket->read_deadline_ns,
+ w5500_tcp_alarm_handler,
+ socket);
/* Wait until there is data to read. */
uint16_t avail = 0;
for (;;) {
if (!socket->read_open) {
- VCALL(bootclock, del_trigger, &trigger);
+ LO_CALL(bootclock, del_trigger, &trigger);
debugf(" => soft closed");
return -NET_ECLOSED;
}
- if (socket->read_deadline_ns && socket->read_deadline_ns <= VCALL(bootclock, get_time_ns)) {
- VCALL(bootclock, del_trigger, &trigger);
+ if (socket->read_deadline_ns && socket->read_deadline_ns <= LO_CALL(bootclock, get_time_ns)) {
+ LO_CALL(bootclock, del_trigger, &trigger);
debugf(" => recv timeout");
return -NET_ERECV_TIMEOUT;
}
@@ -770,7 +708,7 @@ static ssize_t w5500_tcp_read(implements_net_stream_conn *_socket, void *buf, si
case STATE_TCP_FIN_WAIT:
break; /* OK */
default:
- VCALL(bootclock, del_trigger, &trigger);
+ LO_CALL(bootclock, del_trigger, &trigger);
cr_mutex_unlock(&chip->mu);
debugf(" => hard closed");
return -NET_ECLOSED;
@@ -781,7 +719,7 @@ static ssize_t w5500_tcp_read(implements_net_stream_conn *_socket, void *buf, si
/* We have data to read. */
break;
if (state == STATE_TCP_CLOSE_WAIT) {
- VCALL(bootclock, del_trigger, &trigger);
+ LO_CALL(bootclock, del_trigger, &trigger);
cr_mutex_unlock(&chip->mu);
debugf(" => EOF");
return 0;
@@ -801,12 +739,12 @@ static ssize_t w5500_tcp_read(implements_net_stream_conn *_socket, void *buf, si
w5500ll_write_sock_reg(chip->spidev, socknum, rx_read_pointer, uint16be_marshal(ptr+avail));
w5500_socket_cmd(socket, CMD_RECV);
/* Return. */
- VCALL(bootclock, del_trigger, &trigger);
+ LO_CALL(bootclock, del_trigger, &trigger);
cr_mutex_unlock(&chip->mu);
return avail;
}
-static int w5500_tcp_close(implements_net_stream_conn *_socket, bool rd, bool wr) {
+static int w5500_tcp_close(struct _w5500_socket *socket, bool rd, bool wr) {
debugf("tcp_conn.close(rd=%s, wr=%s)", rd ? "true" : "false", wr ? "true" : "false");
ASSERT_SELF(stream_conn, TCP);
@@ -839,7 +777,7 @@ static int w5500_tcp_close(implements_net_stream_conn *_socket, bool rd, bool wr
/* udp_conn methods ***********************************************************/
-static ssize_t w5500_udp_sendto(implements_net_packet_conn *_socket, void *buf, size_t count,
+static ssize_t w5500_udp_sendto(struct _w5500_socket *socket, void *buf, size_t count,
struct net_ip4_addr node, uint16_t port) {
debugf("udp_conn.sendto()");
ASSERT_SELF(packet_conn, UDP);
@@ -896,7 +834,7 @@ static ssize_t w5500_udp_sendto(implements_net_packet_conn *_socket, void *buf,
}
}
-static void w5500_udp_set_read_deadline(implements_net_packet_conn *_socket, uint64_t ns) {
+static void w5500_udp_set_read_deadline(struct _w5500_socket *socket, uint64_t ns) {
debugf("udp_conn.set_read_deadline(%"PRIu64")", ns);
ASSERT_SELF(packet_conn, UDP);
socket->read_deadline_ns = ns;
@@ -907,7 +845,7 @@ static void w5500_udp_alarm_handler(void *_arg) {
cr_sema_signal_from_intrhandler(&socket->read_sema);
}
-static ssize_t w5500_udp_recvfrom(implements_net_packet_conn *_socket, void *buf, size_t count,
+static ssize_t w5500_udp_recvfrom(struct _w5500_socket *socket, void *buf, size_t count,
struct net_ip4_addr *ret_node, uint16_t *ret_port) {
debugf("udp_conn.recvfrom()");
ASSERT_SELF(packet_conn, UDP);
@@ -916,23 +854,23 @@ static ssize_t w5500_udp_recvfrom(implements_net_packet_conn *_socket, void *buf
struct alarmclock_trigger trigger = {0};
if (socket->read_deadline_ns)
- VCALL(bootclock, add_trigger, &trigger,
- socket->read_deadline_ns,
- w5500_udp_alarm_handler,
- socket);
+ LO_CALL(bootclock, add_trigger, &trigger,
+ socket->read_deadline_ns,
+ w5500_udp_alarm_handler,
+ socket);
/* Wait until there is data to read. */
uint16_t avail = 0;
for (;;) {
- if (socket->read_deadline_ns && socket->read_deadline_ns <= VCALL(bootclock, get_time_ns)) {
- VCALL(bootclock, del_trigger, &trigger);
+ if (socket->read_deadline_ns && socket->read_deadline_ns <= LO_CALL(bootclock, get_time_ns)) {
+ LO_CALL(bootclock, del_trigger, &trigger);
debugf(" => recv timeout");
return -NET_ERECV_TIMEOUT;
}
cr_mutex_lock(&chip->mu);
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
if (state != STATE_UDP) {
- VCALL(bootclock, del_trigger, &trigger);
+ LO_CALL(bootclock, del_trigger, &trigger);
debugf(" => hard closed");
return -NET_ECLOSED;
}
@@ -970,12 +908,12 @@ static ssize_t w5500_udp_recvfrom(implements_net_packet_conn *_socket, void *buf
w5500ll_write_sock_reg(chip->spidev, socknum, rx_read_pointer, uint16be_marshal(ptr+8+len));
w5500_socket_cmd(socket, CMD_RECV);
/* Return. */
- VCALL(bootclock, del_trigger, &trigger);
+ LO_CALL(bootclock, del_trigger, &trigger);
cr_mutex_unlock(&chip->mu);
return len;
}
-static int w5500_udp_close(implements_net_packet_conn *_socket) {
+static int w5500_udp_close(struct _w5500_socket *socket) {
debugf("udp_conn.close()");
ASSERT_SELF(packet_conn, UDP);