diff options
Diffstat (limited to 'libhw/w5500.c')
-rw-r--r-- | libhw/w5500.c | 172 |
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); |