summaryrefslogtreecommitdiff
path: root/libhw/w5500.c
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2024-11-10 22:08:02 -0700
committerLuke T. Shumaker <lukeshu@lukeshu.com>2024-11-10 22:08:02 -0700
commitd84daf84d2ced072782ef3c61e5088b06d950939 (patch)
treefff4e2f44f01113dff27328e9e74caf4a12f962a /libhw/w5500.c
parent1e8897a61addec5c2067ffbab38122d6c236ced3 (diff)
libhw: net.h: Add listener close(), add 'iface'
Diffstat (limited to 'libhw/w5500.c')
-rw-r--r--libhw/w5500.c228
1 files changed, 184 insertions, 44 deletions
diff --git a/libhw/w5500.c b/libhw/w5500.c
index 1923a17..ae48a61 100644
--- a/libhw/w5500.c
+++ b/libhw/w5500.c
@@ -107,22 +107,44 @@
/* vtables ********************************************************************/
-static implements_net_stream_conn *w5500_tcp_accept(implements_net_stream_listener *);
-
-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);
-
-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 *);
+/* 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_tcp_accept,
+ .accept = w5500_tcplist_accept,
+ .close = w5500_tcplist_close,
};
static struct net_stream_conn_vtable w5500_tcp_conn_vtable = {
@@ -141,14 +163,40 @@ static struct net_packet_conn_vtable w5500_udp_conn_vtable = {
/* mid-level utilities ********************************************************/
-#if 0
-static uint16_t w5500_get_local_port(struct w5500 *self) {
+static uint16_t w5500_alloc_local_port(struct w5500 *self) {
+ assert(self);
uint16_t ret = self->next_local_port++;
if (self->next_local_port > CONFIG_W5500_LOCAL_PORT_MAX)
self->next_local_port = CONFIG_W5500_LOCAL_PORT_MIN;
return ret;
}
-#endif
+
+static struct _w5500_socket *w5500_alloc_socket(struct w5500 *self) {
+ assert(self);
+ struct _w5500_socket *sock = self->free;
+ if (!sock)
+ return NULL;
+ self->free = sock->next_free;
+ sock->next_free = NULL;
+ assert(sock->mode == W5500_MODE_NONE);
+ return sock;
+}
+
+static void w5500_free_socket(struct w5500 *self, struct _w5500_socket *sock) {
+ assert(self);
+ assert(sock);
+ sock->mode = W5500_MODE_NONE;
+ sock->next_free = self->free;
+ self->free = sock;
+}
+
+static void w5500_tcp_maybe_free(struct w5500 *chip, struct _w5500_socket *sock) {
+ assert(chip);
+ assert(sock);
+ assert(sock->mode == W5500_MODE_TCP);
+ if (!sock->list_open && !sock->read_open && !sock->write_open)
+ w5500_free_socket(chip, sock);
+}
static COROUTINE w5500_irq_cr(void *_chip) {
struct w5500 *chip = _chip;
@@ -263,6 +311,7 @@ void _w5500_init(struct w5500 *chip,
/* 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,
@@ -270,6 +319,7 @@ void _w5500_init(struct w5500 *chip,
/* mutable */
.next_local_port = CONFIG_W5500_LOCAL_PORT_MIN,
};
+ chip->free = &chip->sockets[0];
for (uint8_t i = 0; i < 8; i++) {
chip->sockets[i] = (struct _w5500_socket){
/* const-after-init */
@@ -278,7 +328,9 @@ void _w5500_init(struct w5500 *chip,
.implements_net_packet_conn = { .vtable = &w5500_udp_conn_vtable },
.socknum = i,
/* mutable */
- /* these all get initialized to the zero values */
+ .next_free = (i + 1 < 8) ? &chip->sockets[i+1] : NULL,
+ /* the rest of the mutable members get
+ * initialized to the zero values */
};
}
@@ -359,46 +411,122 @@ void w5500_soft_reset(struct w5500 *chip) {
w5500_post_reset(chip);
}
-void w5500_netcfg(struct w5500 *chip, struct w5500_netcfg cfg) {
+static struct net_eth_addr w5500_if_hwaddr(implements_net_iface *_chip) {
+ struct w5500 *chip = VCALL_SELF(struct w5500, implements_net_iface, _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);
+ assert(chip);
+
w5500ll_write_common_reg(chip->spidev, ip_gateway_addr, cfg.gateway_addr);
w5500ll_write_common_reg(chip->spidev, ip_subnet_mask, cfg.subnet_mask);
w5500ll_write_common_reg(chip->spidev, ip_addr, cfg.addr);
}
-implements_net_stream_listener *w5500_tcp_listen(struct w5500 *chip, uint8_t socknum,
- uint16_t port) {
+static void w5500_if_down(implements_net_iface *_chip) {
+ 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);
assert(chip);
- assert(socknum < 8);
- assert(port);
- assert(chip->sockets[socknum].mode == W5500_MODE_NONE);
- chip->sockets[socknum].mode = W5500_MODE_TCP;
- chip->sockets[socknum].port = port;
- chip->sockets[socknum].read_deadline_ns = 0;
+ struct _w5500_socket *sock = w5500_alloc_socket(chip);
+ if (!sock)
+ return NULL;
- return &chip->sockets[socknum].implements_net_stream_listener;
+ if (!local_port)
+ local_port = w5500_alloc_local_port(chip);
+
+ assert(sock->mode == W5500_MODE_NONE);
+ sock->mode = W5500_MODE_TCP;
+ sock->port = local_port;
+ sock->read_deadline_ns = 0;
+ sock->list_open = true;
+
+ return &sock->implements_net_stream_listener;
}
-implements_net_packet_conn *w5500_udp_conn(struct w5500 *chip, uint8_t socknum,
- uint16_t port) {
+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);
assert(chip);
- assert(socknum < 8);
+ assert(memcmp(node.octets, net_ip4_addr_zero.octets, 4));
+ assert(memcmp(node.octets, net_ip4_addr_broadcast.octets, 4));
assert(port);
- assert(chip->sockets[socknum].mode == W5500_MODE_NONE);
- chip->sockets[socknum].mode = W5500_MODE_UDP;
- chip->sockets[socknum].port = port;
- chip->sockets[socknum].read_deadline_ns = 0;
+ struct _w5500_socket *socket = w5500_alloc_socket(chip);
+ if (!socket)
+ return NULL;
+ uint8_t socknum = socket->socknum;
+
+ uint16_t local_port = w5500_alloc_local_port(chip);
+
+ assert(socket->mode == W5500_MODE_NONE);
+ socket->mode = W5500_MODE_TCP;
+ socket->port = local_port;
+ socket->read_deadline_ns = 0;
+ socket->read_open = socket->write_open = true;
+
+ restart:
+ /* Mimics socket.c:socket(). */
+ w5500_socket_close(socket);
+ w5500ll_write_sock_reg(chip->spidev, socknum, mode, SOCKMODE_TCP);
+ w5500ll_write_sock_reg(chip->spidev, socknum, local_port, uint16be_marshal(socket->port));
+ w5500_socket_cmd(socket, CMD_OPEN);
+ while (w5500ll_read_sock_reg(chip->spidev, socknum, state) != STATE_TCP_INIT)
+ cr_yield();
+
+ /* Mimics socket.c:connect(). */
+ w5500ll_write_sock_reg(chip->spidev, socknum, remote_ip_addr, node);
+ w5500ll_write_sock_reg(chip->spidev, socknum, remote_port, uint16be_marshal(port));
+ w5500_socket_cmd(socket, CMD_CONNECT);
+ for (;;) {
+ uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
+ switch (state) {
+ case STATE_TCP_SYNSENT:
+ cr_yield();
+ break;
+ case STATE_TCP_ESTABLISHED:
+ return &socket->implements_net_stream_conn;
+ default:
+ goto restart;
+ }
+ }
+}
+
+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);
+ assert(chip);
+
+ struct _w5500_socket *socket = w5500_alloc_socket(chip);
+ if (!socket)
+ return NULL;
+
+ if (!local_port)
+ local_port = w5500_alloc_local_port(chip);
+
+ assert(socket->mode == W5500_MODE_NONE);
+ socket->mode = W5500_MODE_UDP;
+ socket->port = local_port;
+ socket->read_deadline_ns = 0;
- return &chip->sockets[socknum].implements_net_packet_conn;
+ return &socket->implements_net_packet_conn;
}
/* tcp_listener methods *******************************************************/
-static implements_net_stream_conn *w5500_tcp_accept(implements_net_stream_listener *_socket) {
+static implements_net_stream_conn *w5500_tcplist_accept(implements_net_stream_listener *_socket) {
ASSERT_SELF(stream_listener, TCP);
restart:
+ if (!socket->list_open)
+ return NULL;
+
/* Mimics socket.c:socket(). */
w5500_socket_close(socket);
w5500ll_write_sock_reg(chip->spidev, socknum, mode, SOCKMODE_TCP);
@@ -428,6 +556,14 @@ static implements_net_stream_conn *w5500_tcp_accept(implements_net_stream_listen
}
}
+static int w5500_tcplist_close(implements_net_stream_listener *_socket) {
+ ASSERT_SELF(stream_listener, TCP);
+
+ socket->list_open = false;
+ w5500_tcp_maybe_free(chip, socket);
+ return 0;
+}
+
/* tcp_conn methods ***********************************************************/
static ssize_t w5500_tcp_write(implements_net_stream_conn *_socket, void *buf, size_t count) {
@@ -577,6 +713,7 @@ static int w5500_tcp_close(implements_net_stream_conn *_socket, bool rd, bool wr
}
}
+ w5500_tcp_maybe_free(chip, socket);
return 0;
}
@@ -630,7 +767,7 @@ static void w5500_udp_alarm_handler(void *_arg) {
}
static ssize_t w5500_udp_recvfrom(implements_net_packet_conn *_socket, void *buf, size_t count,
- struct net_ip4_addr *node, uint16_t *port) {
+ struct net_ip4_addr *ret_node, uint16_t *ret_port) {
ASSERT_SELF(packet_conn, UDP);
assert(buf);
assert(count);
@@ -669,11 +806,14 @@ static ssize_t w5500_udp_recvfrom(implements_net_packet_conn *_socket, void *buf
* this; this is based off of socket.c:recvfrom(). */
uint8_t hdr[8];
w5500ll_read(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, RX), hdr, sizeof(hdr));
- node->octets[0] = hdr[0];
- node->octets[1] = hdr[1];
- node->octets[2] = hdr[2];
- node->octets[3] = hdr[3];
- *port = uint16be_decode(&hdr[4]);
+ if (ret_node) {
+ ret_node->octets[0] = hdr[0];
+ ret_node->octets[1] = hdr[1];
+ ret_node->octets[2] = hdr[2];
+ ret_node->octets[3] = hdr[3];
+ }
+ if (ret_port)
+ *ret_port = uint16be_decode(&hdr[4]);
uint16_t len = uint16be_decode(&hdr[6]);
/* Now read the actual data. */
if (count > len)
@@ -691,6 +831,6 @@ static int w5500_udp_close(implements_net_packet_conn *_socket) {
ASSERT_SELF(packet_conn, UDP);
w5500_socket_close(socket);
-
+ w5500_free_socket(chip, socket);
return 0;
}