summaryrefslogtreecommitdiff
path: root/libhw
diff options
context:
space:
mode:
Diffstat (limited to 'libhw')
-rw-r--r--libhw/common_include/libhw/generic/net.h32
-rw-r--r--libhw/host_net.c20
-rw-r--r--libhw/rp2040_include/libhw/w5500.h35
-rw-r--r--libhw/w5500.c228
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;
}