diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-11-10 22:08:02 -0700 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2024-11-10 22:08:02 -0700 |
commit | d84daf84d2ced072782ef3c61e5088b06d950939 (patch) | |
tree | fff4e2f44f01113dff27328e9e74caf4a12f962a /libhw | |
parent | 1e8897a61addec5c2067ffbab38122d6c236ced3 (diff) |
libhw: net.h: Add listener close(), add 'iface'
Diffstat (limited to 'libhw')
-rw-r--r-- | libhw/common_include/libhw/generic/net.h | 32 | ||||
-rw-r--r-- | libhw/host_net.c | 20 | ||||
-rw-r--r-- | libhw/rp2040_include/libhw/w5500.h | 35 | ||||
-rw-r--r-- | libhw/w5500.c | 228 |
4 files changed, 241 insertions, 74 deletions
diff --git a/libhw/common_include/libhw/generic/net.h b/libhw/common_include/libhw/generic/net.h index e23dcda..def533c 100644 --- a/libhw/common_include/libhw/generic/net.h +++ b/libhw/common_include/libhw/generic/net.h @@ -52,6 +52,14 @@ struct net_stream_listener_vtable { * connection is still open. */ implements_net_stream_conn *(*accept)(implements_net_stream_listener *self); + + /** + * The net_stream_conn returned from accept() may still be + * valid after the listener is closed. + * + * Return 0 on success, -errno on error. + */ + int (*close)(implements_net_stream_listener *self); }; struct net_stream_conn_vtable { @@ -119,4 +127,28 @@ struct net_packet_conn_vtable { int (*close )(implements_net_packet_conn *self); }; +/* Interfaces *****************************************************************/ + +struct net_iface_config { + struct net_ip4_addr addr; + struct net_ip4_addr gateway_addr; + struct net_ip4_addr subnet_mask; +}; + +struct net_iface_vtable; + +typedef struct { + struct net_iface_vtable *vtable; +} implements_net_iface; + +struct net_iface_vtable { + struct net_eth_addr (*hwaddr )(implements_net_iface *); + void (*ifup )(implements_net_iface *, struct net_iface_config); + void (*ifdown )(implements_net_iface *); + + implements_net_stream_listener *(*tcp_listen)(implements_net_iface *, uint16_t local_port); + implements_net_stream_conn *(*tcp_dial )(implements_net_iface *, struct net_ip4_addr remote_node, uint16_t remote_port); + implements_net_packet_conn *(*udp_conn )(implements_net_iface *, uint16_t local_port); +}; + #endif /* _LIBHW_GENERIC_NET_H_ */ diff --git a/libhw/host_net.c b/libhw/host_net.c index 75486aa..88bda49 100644 --- a/libhw/host_net.c +++ b/libhw/host_net.c @@ -92,14 +92,16 @@ static inline ssize_t hostnet_map_negerrno(ssize_t v) { /* TCP init() ( AKA socket(3) + listen(3) )************************************/ -static implements_net_stream_conn *hostnet_tcp_accept(implements_net_stream_listener *_listener); +static implements_net_stream_conn *hostnet_tcplist_accept(implements_net_stream_listener *); +static int hostnet_tcplist_close(implements_net_stream_listener *); static void hostnet_tcp_set_read_deadline(implements_net_stream_conn *conn, uint64_t ts_ns); static ssize_t hostnet_tcp_read(implements_net_stream_conn *conn, void *buf, size_t count); static ssize_t hostnet_tcp_write(implements_net_stream_conn *conn, void *buf, size_t count); static int hostnet_tcp_close(implements_net_stream_conn *conn, bool rd, bool wr); static struct net_stream_listener_vtable hostnet_tcp_listener_vtable = { - .accept = hostnet_tcp_accept, + .accept = hostnet_tcplist_accept, + .close = hostnet_tcplist_close, }; static struct net_stream_conn_vtable hostnet_tcp_conn_vtable = { @@ -136,7 +138,7 @@ void hostnet_tcp_listener_init(struct hostnet_tcp_listener *self, uint16_t port) self->fd = listenerfd; } -/* TCP accept() ***************************************************************/ +/* TCP listener accept() ******************************************************/ struct hostnet_pthread_accept_args { pthread_t cr_thread; @@ -156,7 +158,7 @@ static void *hostnet_pthread_accept(void *_args) { return NULL; } -static implements_net_stream_conn *hostnet_tcp_accept(implements_net_stream_listener *_listener) { +static implements_net_stream_conn *hostnet_tcplist_accept(implements_net_stream_listener *_listener) { struct hostnet_tcp_listener *listener = VCALL_SELF(struct hostnet_tcp_listener, implements_net_stream_listener, _listener); assert(listener); @@ -176,6 +178,16 @@ static implements_net_stream_conn *hostnet_tcp_accept(implements_net_stream_list return &listener->active_conn; } +/* TCP listener close() *******************************************************/ + +static int hostnet_tcplist_close(implements_net_stream_listener *_listener) { + struct hostnet_tcp_listener *listener = + VCALL_SELF(struct hostnet_tcp_listener, implements_net_stream_listener, _listener); + assert(listener); + + return hostnet_map_negerrno(close(listener->fd) ? -errno : 0); +} + /* TCP read() *****************************************************************/ static void hostnet_tcp_set_read_deadline(implements_net_stream_conn *_conn, uint64_t ts_ns) { diff --git a/libhw/rp2040_include/libhw/w5500.h b/libhw/rp2040_include/libhw/w5500.h index 16f88cc..7538bbc 100644 --- a/libhw/rp2040_include/libhw/w5500.h +++ b/libhw/rp2040_include/libhw/w5500.h @@ -25,16 +25,17 @@ struct _w5500_socket { uint8_t socknum; /* mutable */ + struct _w5500_socket *next_free; enum { W5500_MODE_NONE = 0, W5500_MODE_TCP, W5500_MODE_UDP, } mode; - uint16_t port; /* MODE_{TCP,UDP} */ - uint64_t read_deadline_ns; /* MODE_{TCP,UDP} */ - cr_sema_t listen_sema; /* MODE_TCP */ - cr_sema_t read_sema; /* MODE_{TCP,UDP} */ - bool read_open, write_open; /* MODE_TCP */ + uint16_t port; /* MODE_{TCP,UDP} */ + uint64_t read_deadline_ns; /* MODE_{TCP,UDP} */ + cr_sema_t listen_sema; /* MODE_TCP */ + cr_sema_t read_sema; /* MODE_{TCP,UDP} */ + bool list_open, read_open, write_open; /* MODE_TCP */ cr_mutex_t cmd_mu; END_PRIVATE(LIBHW_W5500_H) @@ -42,8 +43,9 @@ struct _w5500_socket { struct w5500 { /* const-after-init */ - implements_spi *spidev; + implements_net_iface; BEGIN_PRIVATE(LIBHW_W5500_H) + implements_spi *spidev; uint pin_intr; uint pin_reset; struct net_eth_addr hwaddr; @@ -51,6 +53,7 @@ struct w5500 { /* mutable */ uint16_t next_local_port; struct _w5500_socket sockets[8]; + struct _w5500_socket *free; cr_sema_t intr; END_PRIVATE(LIBHW_W5500_H) }; @@ -88,24 +91,4 @@ void w5500_hard_reset(struct w5500 *self); */ void w5500_soft_reset(struct w5500 *self); -struct w5500_netcfg { - struct net_ip4_addr gateway_addr; - struct net_ip4_addr subnet_mask; - struct net_ip4_addr addr; -}; - -/** - * TODO. - */ -void w5500_netcfg(struct w5500 *self, struct w5500_netcfg cfg); - -implements_net_stream_listener *w5500_tcp_listen(struct w5500 *self, uint8_t socknum, - uint16_t port); - -/** - * TODO. - */ -implements_net_packet_conn *w5500_udp_conn(struct w5500 *self, uint8_t socknum, - uint16_t port); - #endif /* _LIBHW_W5500_H_ */ 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; } |