summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libhw/common_include/libhw/generic/net.h2
-rw-r--r--libhw/host_net.c24
-rw-r--r--libhw/rp2040_include/libhw/w5500.h34
-rw-r--r--libhw/w5500.c466
4 files changed, 350 insertions, 176 deletions
diff --git a/libhw/common_include/libhw/generic/net.h b/libhw/common_include/libhw/generic/net.h
index 08b411f..8dedaea 100644
--- a/libhw/common_include/libhw/generic/net.h
+++ b/libhw/common_include/libhw/generic/net.h
@@ -17,6 +17,8 @@
#define NET_EOTHER 1
#define NET_ETIMEDOUT 2
#define NET_ETHREAD 3
+#define NET_ECLOSED 4
+#define NET_EMSGSIZE 5
/* Address types **************************************************************/
diff --git a/libhw/host_net.c b/libhw/host_net.c
index 20b72f1..27df2e2 100644
--- a/libhw/host_net.c
+++ b/libhw/host_net.c
@@ -75,14 +75,18 @@ static inline bool RUN_PTHREAD(void *(*fn)(void *), void *args) {
return false;
}
-static inline ssize_t hostnet_map_errno(ssize_t v) {
+static inline ssize_t hostnet_map_negerrno(ssize_t v) {
if (v >= 0)
return v;
switch (v) {
- case ETIMEDOUT:
- return NET_ETIMEDOUT;
+ case -ETIMEDOUT:
+ return -NET_ETIMEDOUT;
+ case -EBADF:
+ return -NET_ECLOSED;
+ case -EMSGSIZE:
+ return -NET_EMSGSIZE;
default:
- return NET_EOTHER;
+ return -NET_EOTHER;
}
}
@@ -208,7 +212,7 @@ static void *hostnet_pthread_read(void *_args) {
end:
if (*(args->ret) < 0)
- *(args->ret) = hostnet_map_errno(-errno);
+ *(args->ret) = hostnet_map_negerrno(-errno);
WAKE_COROUTINE(args);
return NULL;
}
@@ -262,7 +266,7 @@ static void *hostnet_pthread_write(void *_args) {
while (done < args->count) {
ssize_t r = write(args->connfd, args->buf, args->count);
if (r < 0) {
- hostnet_map_errno(-errno);
+ hostnet_map_negerrno(-errno);
break;
}
done += r;
@@ -310,7 +314,7 @@ static int hostnet_tcp_close(implements_net_stream_conn *_conn, bool rd, bool wr
how = SHUT_WR;
else
assert(false);
- return hostnet_map_errno(shutdown(conn->fd, how) ? -errno : 0);
+ return hostnet_map_negerrno(shutdown(conn->fd, how) ? -errno : 0);
}
/* UDP init() *****************************************************************/
@@ -384,7 +388,7 @@ static void *hostnet_pthread_sendto(void *_args) {
addr.in.sin_port = htons(args->port);
*(args->ret) = sendto(args->connfd, args->buf, args->count, 0, &addr.gen, sizeof(addr));
if (*(args->ret) < 0)
- *(args->ret) = hostnet_map_errno(-errno);
+ *(args->ret) = hostnet_map_negerrno(-errno);
WAKE_COROUTINE(args);
return NULL;
}
@@ -470,7 +474,7 @@ static void *hostnet_pthread_recvfrom(void *_args) {
end:
if (*(args->ret_size) < 0)
- *(args->ret_size) = hostnet_map_errno(-errno);
+ *(args->ret_size) = hostnet_map_negerrno(-errno);
WAKE_COROUTINE(args);
return NULL;
}
@@ -515,5 +519,5 @@ static int hostnet_udp_close(implements_net_packet_conn *_conn) {
VCALL_SELF(struct hostnet_udp_conn, implements_net_packet_conn, _conn);
assert(conn);
- return hostnet_map_errno(close(conn->fd) ? -errno : 0);
+ return hostnet_map_negerrno(close(conn->fd) ? -errno : 0);
}
diff --git a/libhw/rp2040_include/libhw/w5500.h b/libhw/rp2040_include/libhw/w5500.h
index 037d4f1..16f88cc 100644
--- a/libhw/rp2040_include/libhw/w5500.h
+++ b/libhw/rp2040_include/libhw/w5500.h
@@ -7,6 +7,8 @@
#ifndef _LIBHW_W5500_H_
#define _LIBHW_W5500_H_
+#include <pico/binary_info.h> /* for bi_* */
+
#include <libcr_ipc/sema.h>
#include <libcr_ipc/mutex.h>
#include <libmisc/private.h>
@@ -14,27 +16,27 @@
#include <libhw/generic/net.h>
#include <libhw/generic/spi.h>
-struct _w5500_tcp_conn {
- /* const-after-init */
- implements_net_stream_conn;
- /* mutable */
- BEGIN_PRIVATE(LIBHW_W5500_H)
- bool read_open;
- bool write_open;
- END_PRIVATE(LIBHW_W5500_H)
-};
-
-struct _w5500_tcp_listener {
+struct _w5500_socket {
/* const-after-init */
- implements_net_stream_listener;
+ implements_net_stream_listener implements_net_stream_listener;
+ implements_net_stream_conn implements_net_stream_conn;
+ implements_net_packet_conn implements_net_packet_conn;
BEGIN_PRIVATE(LIBHW_W5500_H)
uint8_t socknum;
- struct _w5500_tcp_conn active_conn;
/* mutable */
- uint16_t port;
+ 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 */
+
cr_mutex_t cmd_mu;
- cr_sema_t listen_sema, read_sema;
END_PRIVATE(LIBHW_W5500_H)
};
@@ -48,7 +50,7 @@ struct w5500 {
/* mutable */
uint16_t next_local_port;
- struct _w5500_tcp_listener listeners[8];
+ struct _w5500_socket sockets[8];
cr_sema_t intr;
END_PRIVATE(LIBHW_W5500_H)
};
diff --git a/libhw/w5500.c b/libhw/w5500.c
index 603863a..1923a17 100644
--- a/libhw/w5500.c
+++ b/libhw/w5500.c
@@ -105,6 +105,40 @@
#define UNUSED(name)
#define ARRAY_LEN(ary) (sizeof(ary)/sizeof((ary)[0]))
+/* 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 *);
+
+static struct net_stream_listener_vtable w5500_tcp_listener_vtable = {
+ .accept = w5500_tcp_accept,
+};
+
+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,
+};
+
/* mid-level utilities ********************************************************/
#if 0
@@ -129,18 +163,26 @@ static COROUTINE w5500_irq_cr(void *_chip) {
for (uint8_t socknum = 0; socknum < 8; socknum++) {
if (!(sockmask & (1<<socknum)))
continue;
- struct _w5500_tcp_listener *listener = &chip->listeners[socknum];
+ struct _w5500_socket *socket = &chip->sockets[socknum];
uint8_t sockintr = w5500ll_read_sock_reg(chip->spidev, socknum, interrupt);
- /* SOCKINTR_SEND_OK is useless. */
- uint8_t listen_bits = sockintr & (SOCKINTR_TIMEOUT|SOCKINTR_CONN),
- read_bits = sockintr & (SOCKINTR_TIMEOUT|SOCKINTR_RECV|SOCKINTR_FIN);
-
- if (listen_bits)
- cr_sema_signal(&listener->listen_sema);
- if (read_bits)
- cr_sema_signal(&listener->read_sema);
+ switch (socket->mode) {
+ case W5500_MODE_NONE:
+ break;
+ case W5500_MODE_TCP:
+ /* SOCKINTR_SEND_OK is useless; just count on the write methods to
+ * poll instead of waiting on notification from here. */
+ uint8_t listen_bits = sockintr & (SOCKINTR_TIMEOUT|SOCKINTR_CONN),
+ read_bits = sockintr & (SOCKINTR_TIMEOUT|SOCKINTR_RECV|SOCKINTR_FIN);
+
+ if (listen_bits)
+ cr_sema_signal(&socket->listen_sema);
+ /* fallthrough */
+ case W5500_MODE_UDP:
+ if (read_bits)
+ cr_sema_signal(&socket->read_sema);
+ }
w5500ll_write_sock_reg(chip->spidev, socknum, interrupt, sockintr);
}
@@ -149,22 +191,60 @@ static COROUTINE w5500_irq_cr(void *_chip) {
cr_end();
}
-/* init() *********************************************************************/
+static struct w5500 *w5500_socket_chip(struct _w5500_socket *socket) {
+ assert(socket);
+ assert(socket->socknum < 8);
-static implements_net_stream_conn *w5500_tcp_accept(implements_net_stream_listener *listener);
-static ssize_t w5500_tcp_read(implements_net_stream_conn *conn, void *buf, size_t count);
-static ssize_t w5500_tcp_write(implements_net_stream_conn *conn, void *buf, size_t count);
-static int w5500_tcp_close(implements_net_stream_conn *conn, bool rd, bool wr);
+ struct _w5500_socket *sock0 = &socket[-(socket->socknum)];
+ assert(sock0);
+ struct w5500 *chip =
+ ((void *)sock0) - offsetof(struct w5500, sockets);
+ assert(chip);
+ return chip;
+}
-static struct net_stream_listener_vtable w5500_tcp_listener_vtable = {
- .accept = w5500_tcp_accept,
-};
+static inline void w5500_socket_cmd(struct _w5500_socket *socket, uint8_t cmd) {
+ assert(socket);
+ struct w5500 *chip = w5500_socket_chip(socket);
+ uint8_t socknum = socket->socknum;
-static struct net_stream_conn_vtable w5500_tcp_conn_vtable = {
- .read = w5500_tcp_read,
- .write = w5500_tcp_write,
- .close = w5500_tcp_close,
-};
+ cr_mutex_lock(&socket->cmd_mu);
+ w5500ll_write_sock_reg(chip->spidev, socknum, command, cmd);
+ while (w5500ll_read_sock_reg(chip->spidev, socknum, command) != 0x00)
+ cr_yield();
+ cr_mutex_unlock(&socket->cmd_mu);
+}
+
+static inline void w5500_socket_close(struct _w5500_socket *socket) {
+ assert(socket);
+ struct w5500 *chip = w5500_socket_chip(socket);
+ uint8_t socknum = socket->socknum;
+
+ /* Send CMD_CLOSE. */
+ w5500_socket_cmd(socket, CMD_CLOSE);
+ /* Wait for it to transition to STATE_CLOSED. */
+ while (w5500ll_read_sock_reg(chip->spidev, socknum, state) != STATE_CLOSED)
+ cr_yield();
+
+ /* Update our MCU-side bookkeeping. */
+ socket->mode = W5500_MODE_NONE;
+ socket->port = 0;
+ socket->read_deadline_ns = 0;
+ socket->read_open = socket->write_open = false;
+}
+
+#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); \
+ assert(socket->mode == W5500_MODE_##_mode); \
+ struct w5500 *chip = w5500_socket_chip(socket); \
+ assert(chip);
+
+/* init() *********************************************************************/
static struct w5500 *w5500_chips[CONFIG_W5500_NUM] = {0};
@@ -191,19 +271,14 @@ void _w5500_init(struct w5500 *chip,
.next_local_port = CONFIG_W5500_LOCAL_PORT_MIN,
};
for (uint8_t i = 0; i < 8; i++) {
- chip->listeners[i] = (struct _w5500_tcp_listener){
+ chip->sockets[i] = (struct _w5500_socket){
/* const-after-init */
- .vtable = &w5500_tcp_listener_vtable,
+ .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,
- .active_conn = {
- /* const-after-init */
- .vtable = &w5500_tcp_conn_vtable,
- /* mutable */
- .read_open = false,
- .write_open = false,
- },
/* mutable */
- .port = 0,
+ /* these all get initialized to the zero values */
};
}
@@ -296,99 +371,57 @@ implements_net_stream_listener *w5500_tcp_listen(struct w5500 *chip, uint8_t soc
assert(socknum < 8);
assert(port);
- assert(chip->listeners[socknum].port == 0);
- chip->listeners[socknum].port = 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;
- return &chip->listeners[socknum];
+ return &chip->sockets[socknum].implements_net_stream_listener;
}
-/*
implements_net_packet_conn *w5500_udp_conn(struct w5500 *chip, uint8_t socknum,
uint16_t port) {
assert(chip);
assert(socknum < 8);
assert(port);
- assert(chip->listeners[socknum].port == 0);
- chip->listeners[socknum].port = 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;
- return &chip->listeners[socknum];
+ return &chip->sockets[socknum].implements_net_packet_conn;
}
-*/
/* tcp_listener methods *******************************************************/
-static struct w5500 *w5500_tcp_listener_chip(struct _w5500_tcp_listener *listener) {
- assert(listener);
- assert(listener->socknum < 8);
-
- struct _w5500_tcp_listener *sock0 = &listener[-listener->socknum];
- assert(sock0);
- struct w5500 *chip =
- ((void *)sock0) - offsetof(struct w5500, listeners);
- assert(chip);
- return chip;
-}
-
-static inline void w5500_tcp_listener_cmd(struct _w5500_tcp_listener *listener, uint8_t cmd) {
- assert(listener);
- struct w5500 *chip = w5500_tcp_listener_chip(listener);
- uint8_t socknum = listener->socknum;
-
- cr_mutex_lock(&listener->cmd_mu);
- w5500ll_write_sock_reg(chip->spidev, socknum, command, cmd);
- while (w5500ll_read_sock_reg(chip->spidev, socknum, command) != 0x00)
- cr_yield();
- cr_mutex_unlock(&listener->cmd_mu);
-}
-
-static inline void w5500_tcp_listener_cmd_close(struct _w5500_tcp_listener *listener) {
- assert(listener);
- struct w5500 *chip = w5500_tcp_listener_chip(listener);
- uint8_t socknum = listener->socknum;
-
- w5500_tcp_listener_cmd(listener, CMD_CLOSE);
- w5500ll_write_sock_reg(chip->spidev, socknum, interrupt, 0xFF);
- while (w5500ll_read_sock_reg(chip->spidev, socknum, state) != STATE_CLOSED)
- cr_yield();
-
- listener->active_conn.read_open = listener->active_conn.write_open = false;
-}
-
-#define ASSERT_LISTENER() \
- struct _w5500_tcp_listener *self = \
- VCALL_SELF(struct _w5500_tcp_listener, \
- implements_net_stream_listener, _self); \
- struct w5500 *chip = w5500_tcp_listener_chip(self); \
- uint8_t socknum = self->socknum;
-
-static implements_net_stream_conn *w5500_tcp_accept(implements_net_stream_listener *_self) {
- ASSERT_LISTENER();
+static implements_net_stream_conn *w5500_tcp_accept(implements_net_stream_listener *_socket) {
+ ASSERT_SELF(stream_listener, TCP);
restart:
/* Mimics socket.c:socket(). */
- w5500_tcp_listener_cmd_close(self);
+ 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(self->port));
- w5500_tcp_listener_cmd(self, CMD_OPEN);
+ 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:listen(). */
- w5500_tcp_listener_cmd(self, CMD_LISTEN);
+ w5500_socket_cmd(socket, CMD_LISTEN);
for (;;) {
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
switch (state) {
case STATE_TCP_LISTEN:
case STATE_TCP_SYNRECV:
- cr_sema_wait(&self->listen_sema);
+ cr_sema_wait(&socket->listen_sema);
break;
case STATE_TCP_ESTABLISHED:
- self->active_conn.read_open = true;
+ socket->read_open = true;
/* fall-through */
case STATE_TCP_CLOSE_WAIT:
- self->active_conn.write_open = true;
- return &self->active_conn;
+ socket->write_open = true;
+ return &socket->implements_net_stream_conn;
default:
goto restart;
}
@@ -397,32 +430,17 @@ static implements_net_stream_conn *w5500_tcp_accept(implements_net_stream_listen
/* tcp_conn methods ***********************************************************/
-static struct _w5500_tcp_listener *w5500_tcp_conn_listener(struct _w5500_tcp_conn *conn) {
- assert(conn);
-
- struct _w5500_tcp_listener *list =
- ((void *)conn) - offsetof(struct _w5500_tcp_listener, active_conn);
- return list;
-}
-
-#define ASSERT_CONN() \
- struct _w5500_tcp_conn *self = \
- VCALL_SELF(struct _w5500_tcp_conn, implements_net_stream_conn, _self); \
- struct _w5500_tcp_listener *listener = w5500_tcp_conn_listener(self); \
- struct w5500 *chip = w5500_tcp_listener_chip(listener); \
- uint8_t socknum = listener->socknum;
-
-static ssize_t w5500_tcp_write(implements_net_stream_conn *_self, void *buf, size_t count) {
- ASSERT_CONN();
+static ssize_t w5500_tcp_write(implements_net_stream_conn *_socket, void *buf, size_t count) {
+ ASSERT_SELF(stream_conn, TCP);
assert(buf);
assert(count);
/* What we really want is to pause until we receive an ACK for
* some data we just queued, so that we can line up some new
* data to keep the buffer full. But that's not what
- * SEND_FINIAIUI, the SEND_FINISHED interrupt doesn't fire
- * until we receive the *last* ACK for the data, when the
- * buffer is entirely empty.
+ * SEND_FINISHED does AIUI, the SEND_FINISHED interrupt
+ * doesn't fire until we receive the *last* ACK for the data,
+ * when the buffer is entirely empty.
*
* Which means we basically have to busy-poll for space in the
* buffer becoming available.
@@ -439,11 +457,11 @@ static ssize_t w5500_tcp_write(implements_net_stream_conn *_self, void *buf, siz
size_t done = 0;
while (done < count) {
- if (!self->write_open)
- return -1;
+ if (!socket->write_open)
+ return -NET_ECLOSED;
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
if (state != STATE_TCP_ESTABLISHED && state != STATE_TCP_CLOSE_WAIT)
- return -1;
+ return -NET_ECLOSED;
uint16_t freesize = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, tx_free_size));
if (freesize < count-done && freesize < min_free_space) {
@@ -453,72 +471,107 @@ static ssize_t w5500_tcp_write(implements_net_stream_conn *_self, void *buf, siz
}
/* Queue data to be sent. */
+ if ((size_t)freesize > count-done)
+ freesize = count-done;
uint16_t ptr = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, tx_write_pointer));
w5500ll_write(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, TX), &((char *)buf)[done], freesize);
w5500ll_write_sock_reg(chip->spidev, socknum, tx_write_pointer, uint16be_marshal(ptr+freesize));
/* Submit the queue. */
- w5500_tcp_listener_cmd(listener, CMD_SEND);
+ w5500_socket_cmd(socket, CMD_SEND);
done += freesize;
}
return done;
}
-static ssize_t w5500_tcp_read(implements_net_stream_conn *_self, void *buf, size_t count) {
- ASSERT_CONN();
+static void w5500_tcp_set_read_deadline(implements_net_stream_conn *_socket, uint64_t ns) {
+ ASSERT_SELF(stream_conn, TCP);
+ socket->read_deadline_ns = ns;
+}
+
+static void w5500_tcp_alarm_handler(void *_arg) {
+ struct _w5500_socket *socket = _arg;
+ cr_sema_signal(&socket->read_sema);
+}
+
+static ssize_t w5500_tcp_read(implements_net_stream_conn *_socket, void *buf, size_t count) {
+ ASSERT_SELF(stream_conn, TCP);
assert(buf);
assert(count);
- size_t done = 0;
- while (!done) {
- if (!self->read_open)
- return -1;
+ struct alarmclock_trigger trigger = {0};
+ if (socket->read_deadline_ns)
+ VCALL(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);
+ return -NET_ECLOSED;
+ }
+ if (socket->read_deadline_ns && socket->read_deadline_ns >= VCALL(bootclock, get_time_ns)) {
+ VCALL(bootclock, del_trigger, &trigger);
+ return -NET_ETIMEDOUT;
+ }
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
switch (state) {
case STATE_TCP_CLOSE_WAIT:
- return 0; /* EOF */
- case STATE_TCP_ESTABLISHED: case STATE_TCP_FIN_WAIT:
+ case STATE_TCP_ESTABLISHED:
+ case STATE_TCP_FIN_WAIT:
break; /* OK */
default:
- return -1;
+ VCALL(bootclock, del_trigger, &trigger);
+ return -NET_ECLOSED;
}
- uint16_t avail = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, rx_size));
- if (!avail) {
- cr_sema_wait(&listener->read_sema);
- continue;
+ avail = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, rx_size));
+ if (avail)
+ /* We have data to read. */
+ break;
+ if (state == STATE_TCP_CLOSE_WAIT) {
+ VCALL(bootclock, del_trigger, &trigger);
+ return 0; /* EOF */
}
- if ((size_t)avail > count)
- avail = count;
- uint16_t ptr = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, rx_read_pointer));
- w5500ll_read(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, RX), &((char *)buf)[done], avail);
- w5500ll_write_sock_reg(chip->spidev, socknum, rx_read_pointer, uint16be_marshal(ptr+avail));
-
- w5500_tcp_listener_cmd(listener, CMD_RECV);
- done += avail;
+
+ cr_sema_wait(&socket->read_sema);
}
- return done;
+ assert(avail);
+ uint16_t ptr = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, rx_read_pointer));
+ /* Read the data. */
+ if ((size_t)avail > count)
+ avail = count;
+ w5500ll_read(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, RX), buf, avail);
+ /* Tell the chip that we read the data. */
+ 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);
+ return avail;
}
-static int w5500_tcp_close(implements_net_stream_conn *_self, bool rd, bool wr) {
- ASSERT_CONN();
+static int w5500_tcp_close(implements_net_stream_conn *_socket, bool rd, bool wr) {
+ ASSERT_SELF(stream_conn, TCP);
if (rd)
- self->read_open = false;
+ socket->read_open = false;
- if (wr && self->write_open) {
- w5500_tcp_listener_cmd(listener, CMD_DISCON);
- while (self->write_open) {
+ if (wr && socket->write_open) {
+ w5500_socket_cmd(socket, CMD_DISCON);
+ while (socket->write_open) {
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
switch (state) {
case STATE_TCP_FIN_WAIT:
- self->write_open = false;
+ socket->write_open = false;
/* Can still read */
- if (!self->read_open)
- w5500_tcp_listener_cmd_close(listener);
+ if (!socket->read_open)
+ w5500_socket_close(socket);
break;
case STATE_CLOSED:
- self->write_open = false;
+ socket->write_open = false;
break;
}
}
@@ -528,3 +581,116 @@ static int w5500_tcp_close(implements_net_stream_conn *_self, bool rd, bool wr)
}
/* udp_conn methods ***********************************************************/
+
+static ssize_t w5500_udp_sendto(implements_net_packet_conn *_socket, void *buf, size_t count,
+ struct net_ip4_addr node, uint16_t port) {
+ ASSERT_SELF(packet_conn, UDP);
+ assert(buf);
+ assert(count);
+
+ uint16_t bufsize = ((uint16_t)w5500ll_read_sock_reg(chip->spidev, socknum, tx_buf_size))*1024;
+ if (count < bufsize)
+ return -NET_EMSGSIZE;
+
+ for (;;) {
+ uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
+ if (state != STATE_UDP)
+ return -NET_ECLOSED;
+
+ uint16_t freesize = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, tx_free_size));
+ if (freesize >= count)
+ /* We can send. */
+ break;
+
+ /* Wait for more buffer space. */
+ cr_yield();
+ }
+
+ /* Where we're sending it. */
+ w5500ll_write_sock_reg(chip->spidev, socknum, remote_ip_addr, node);
+ w5500ll_write_sock_reg(chip->spidev, socknum, remote_port, uint16be_marshal(port));
+ /* Queue data to be sent. */
+ uint16_t ptr = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, tx_write_pointer));
+ w5500ll_write(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, TX), buf, count);
+ w5500ll_write_sock_reg(chip->spidev, socknum, tx_write_pointer, uint16be_marshal(ptr+count));
+ /* Submit the queue. */
+ w5500_socket_cmd(socket, CMD_SEND);
+
+ return count;
+}
+
+static void w5500_udp_set_read_deadline(implements_net_packet_conn *_socket, uint64_t ns) {
+ ASSERT_SELF(packet_conn, UDP);
+ socket->read_deadline_ns = ns;
+}
+
+static void w5500_udp_alarm_handler(void *_arg) {
+ struct _w5500_socket *socket = _arg;
+ cr_sema_signal(&socket->read_sema);
+}
+
+static ssize_t w5500_udp_recvfrom(implements_net_packet_conn *_socket, void *buf, size_t count,
+ struct net_ip4_addr *node, uint16_t *port) {
+ ASSERT_SELF(packet_conn, UDP);
+ assert(buf);
+ assert(count);
+
+ struct alarmclock_trigger trigger = {0};
+ if (socket->read_deadline_ns)
+ VCALL(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);
+ return -NET_ETIMEDOUT;
+ }
+ uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
+ if (state != STATE_UDP) {
+ VCALL(bootclock, del_trigger, &trigger);
+ return -NET_ECLOSED;
+ }
+
+ uint16_t avail = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, rx_size));
+ if (avail)
+ /* We have data to read. */
+ break;
+
+ cr_sema_wait(&socket->read_sema);
+ }
+ assert(avail >= 8);
+ uint16_t ptr = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, rx_read_pointer));
+ /* Read a munged form of the UDP packet header. I
+ * can't find in the datasheet where it describes
+ * 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]);
+ uint16_t len = uint16be_decode(&hdr[6]);
+ /* Now read the actual data. */
+ if (count > len)
+ count = len;
+ w5500ll_read(chip->spidev, ptr+8, CTL_BLOCK_SOCK(socknum, RX), buf, len);
+ /* Tell the chip that we read the data. */
+ 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);
+ return len;
+}
+
+static int w5500_udp_close(implements_net_packet_conn *_socket) {
+ ASSERT_SELF(packet_conn, UDP);
+
+ w5500_socket_close(socket);
+
+ return 0;
+}