summaryrefslogtreecommitdiff
path: root/cmd/sbc_harness/hw/w5500.c
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/sbc_harness/hw/w5500.c')
-rw-r--r--cmd/sbc_harness/hw/w5500.c145
1 files changed, 83 insertions, 62 deletions
diff --git a/cmd/sbc_harness/hw/w5500.c b/cmd/sbc_harness/hw/w5500.c
index b95ad88..ab1ac19 100644
--- a/cmd/sbc_harness/hw/w5500.c
+++ b/cmd/sbc_harness/hw/w5500.c
@@ -122,7 +122,7 @@ static COROUTINE w5500_irq_cr(void *_chip) {
for (uint8_t socknum = 0; socknum < 8; socknum++) {
if (!(sockmask & (1<<socknum)))
continue;
- struct _w5500_listener *listener = &chip->listeners[socknum];
+ struct _w5500_tcp_listener *listener = &chip->listeners[socknum];
uint8_t sockintr = w5500ll_read_sock_reg(chip->spidev, socknum, interrupt);
@@ -144,19 +144,19 @@ static COROUTINE w5500_irq_cr(void *_chip) {
/* init() *********************************************************************/
-static implements_net_conn *w5500_accept(implements_net_listener *_listener);
-static ssize_t w5500_read(implements_net_conn *conn, void *buf, size_t count);
-static ssize_t w5500_write(implements_net_conn *conn, void *buf, size_t count);
-static int w5500_close(implements_net_conn *conn, bool rd, bool wr);
+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);
-static struct net_listener_vtable w5500_listener_vtable = {
- .accept = w5500_accept,
+static struct net_stream_listener_vtable w5500_tcp_listener_vtable = {
+ .accept = w5500_tcp_accept,
};
-static struct net_conn_vtable w5500_conn_vtable = {
- .read = w5500_read,
- .write = w5500_write,
- .close = w5500_close,
+static struct net_stream_conn_vtable w5500_tcp_conn_vtable = {
+ .read = w5500_tcp_read,
+ .write = w5500_tcp_write,
+ .close = w5500_tcp_close,
};
static struct w5500 *w5500_chips[CONFIG_W5500_NUM] = {0};
@@ -183,13 +183,13 @@ 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_listener){
+ chip->listeners[i] = (struct _w5500_tcp_listener){
/* const-after-init */
- .vtable = &w5500_listener_vtable,
+ .vtable = &w5500_tcp_listener_vtable,
.socknum = i,
.active_conn = {
/* const-after-init */
- .vtable = &w5500_conn_vtable,
+ .vtable = &w5500_tcp_conn_vtable,
/* mutable */
.read_open = false,
.write_open = false,
@@ -219,24 +219,44 @@ void _w5500_init(struct w5500 *chip,
/* chip methods ***************************************************************/
static inline void w5500_post_reset(struct w5500 *chip, struct net_eth_addr addr) {
+ /* The W5500 does not have a built-in MAC address, we must
+ * provide one. */
w5500ll_write_common_reg(chip->spidev, eth_addr, addr);
- /* The RP2040 needs a 1/sys_clk hysteresis between interrupts for us
- * to notice them. At the maximum-rated clock-rate of 133MHz, that
- * means 7.5ns (but the sbc-harness overclocks the RP2040, so we
- * could get away with even shorter).
+ /* The RP2040 needs a 1/sys_clk hysteresis between interrupts
+ * for us to notice them. At the maximum-rated clock-rate of
+ * 133MHz, that means 7.5ns (but the sbc-harness overclocks
+ * the RP2040, so we could get away with even shorter).
*
- * The hysteresis is (intlevel+1)*4/(150MHz) (if intlevel is
- * non-zero), or (intlevel+1)*26.7ns; so even the shortest-possible
- * hysteresis much larger than necessary for us. */
+ * If intlevel is non-zero, then the hysteresis is
+ * (intlevel+1)*4/(150MHz), or (intlevel+1)*26.7ns; so even
+ * the shortest-possible hysteresis much larger than necessary
+ * for us. */
w5500ll_write_common_reg(chip->spidev, intlevel, uint16be_marshal(1));
- /* This implementation does not care about any of the chip-level
- * interrupts. */
+ /* This implementation does not care about any of the
+ * chip-level interrupts. */
w5500ll_write_common_reg(chip->spidev, chip_interrupt_mask, 0);
- /* This implementation cares about interrupts for each socket. */
+ /* This implementation cares about interrupts for each
+ * socket. */
w5500ll_write_common_reg(chip->spidev, sock_interrupt_mask, 0xFF);
+
+ /* Configure retry/timeout.
+ *
+ * timeout_arp = 0.1ms * retry_time * (retry_count+1)
+ *
+ * retry_count
+ * timeout_tcp = 0.1ms * retry_time * Σ 2^min(n, floor(1+log_2(65535/retry_time)))
+ * n=0
+ *
+ * For retry_time=2000, retry_count=3, this means
+ *
+ * timeout_arp = 0.8s
+ * timeout_tcp = 3.0s
+ */
+ w5500ll_write_common_reg(chip->spidev, retry_time, uint16be_marshal(2000));
+ w5500ll_write_common_reg(chip->spidev, retry_count, 3);
}
void w5500_hard_reset(struct w5500 *chip, struct net_eth_addr addr) {
@@ -263,8 +283,8 @@ void w5500_netcfg(struct w5500 *chip, struct w5500_netcfg cfg) {
w5500ll_write_common_reg(chip->spidev, ip_addr, cfg.addr);
}
-implements_net_listener *w5500_listen(struct w5500 *chip, uint8_t socknum,
- uint16_t port) {
+implements_net_stream_listener *w5500_tcp_listen(struct w5500 *chip, uint8_t socknum,
+ uint16_t port) {
assert(chip);
assert(socknum < 8);
assert(port);
@@ -275,13 +295,13 @@ implements_net_listener *w5500_listen(struct w5500 *chip, uint8_t socknum,
return &chip->listeners[socknum];
}
-/* listener methods ***********************************************************/
+/* tcp_listener methods *******************************************************/
-static struct w5500 *w5500_listener_chip(struct _w5500_listener *listener) {
+static struct w5500 *w5500_tcp_listener_chip(struct _w5500_tcp_listener *listener) {
assert(listener);
assert(listener->socknum < 8);
- struct _w5500_listener *sock0 = &listener[-listener->socknum];
+ struct _w5500_tcp_listener *sock0 = &listener[-listener->socknum];
assert(sock0);
struct w5500 *chip =
((void *)sock0) - offsetof(struct w5500, listeners);
@@ -289,9 +309,9 @@ static struct w5500 *w5500_listener_chip(struct _w5500_listener *listener) {
return chip;
}
-static inline void w5500_listener_cmd(struct _w5500_listener *listener, uint8_t cmd) {
+static inline void w5500_tcp_listener_cmd(struct _w5500_tcp_listener *listener, uint8_t cmd) {
assert(listener);
- struct w5500 *chip = w5500_listener_chip(listener);
+ struct w5500 *chip = w5500_tcp_listener_chip(listener);
uint8_t socknum = listener->socknum;
cr_mutex_lock(&listener->cmd_mu);
@@ -301,12 +321,12 @@ static inline void w5500_listener_cmd(struct _w5500_listener *listener, uint8_t
cr_mutex_unlock(&listener->cmd_mu);
}
-static inline void w5500_listener_cmd_close(struct _w5500_listener *listener) {
+static inline void w5500_tcp_listener_cmd_close(struct _w5500_tcp_listener *listener) {
assert(listener);
- struct w5500 *chip = w5500_listener_chip(listener);
+ struct w5500 *chip = w5500_tcp_listener_chip(listener);
uint8_t socknum = listener->socknum;
- w5500_listener_cmd(listener, CMD_CLOSE);
+ 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();
@@ -314,27 +334,27 @@ static inline void w5500_listener_cmd_close(struct _w5500_listener *listener) {
listener->active_conn.read_open = listener->active_conn.write_open = false;
}
-#define ASSERT_LISTENER() \
- struct _w5500_listener *self = \
- VCALL_SELF(struct _w5500_listener, \
- implements_net_listener, _self); \
- struct w5500 *chip = w5500_listener_chip(self); \
+#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_conn *w5500_accept(implements_net_listener *_self) {
+static implements_net_stream_conn *w5500_tcp_accept(implements_net_stream_listener *_self) {
ASSERT_LISTENER();
restart:
/* Mimics socket.c:socket(). */
- w5500_listener_cmd_close(self);
+ w5500_tcp_listener_cmd_close(self);
w5500ll_write_sock_reg(chip->spidev, socknum, mode, SOCKMODE_TCP);
w5500ll_write_sock_reg(chip->spidev, socknum, local_port, uint16be_marshal(self->port));
- w5500_listener_cmd(self, CMD_OPEN);
+ w5500_tcp_listener_cmd(self, CMD_OPEN);
while (w5500ll_read_sock_reg(chip->spidev, socknum, state) != STATE_TCP_INIT)
cr_yield();
/* Mimics socket.c:listen(). */
- w5500_listener_cmd(self, CMD_LISTEN);
+ w5500_tcp_listener_cmd(self, CMD_LISTEN);
for (;;) {
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
switch (state) {
@@ -356,31 +376,32 @@ static implements_net_conn *w5500_accept(implements_net_listener *_self) {
/* conn methods ***************************************************************/
-static struct _w5500_listener *w5500_conn_listener(struct _w5500_conn *conn) {
+static struct _w5500_tcp_listener *w5500_tcp_conn_listener(struct _w5500_tcp_conn *conn) {
assert(conn);
- struct _w5500_listener *list =
- ((void *)conn) - offsetof(struct _w5500_listener, active_conn);
+ struct _w5500_tcp_listener *list =
+ ((void *)conn) - offsetof(struct _w5500_tcp_listener, active_conn);
return list;
}
#define ASSERT_CONN() \
- struct _w5500_conn *self = \
- VCALL_SELF(struct _w5500_conn, implements_net_conn, _self); \
- struct _w5500_listener *listener = w5500_conn_listener(self); \
- struct w5500 *chip = w5500_listener_chip(listener); \
+ 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_write(implements_net_conn *_self, void *buf, size_t count) {
+static ssize_t w5500_tcp_write(implements_net_stream_conn *_self, void *buf, size_t count) {
ASSERT_CONN();
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.
+ /* 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.
*
* Which means we basically have to busy-poll for space in the
* buffer becoming available.
@@ -416,13 +437,13 @@ static ssize_t w5500_write(implements_net_conn *_self, void *buf, size_t count)
w5500ll_write_sock_reg(chip->spidev, socknum, tx_write_pointer, uint16be_marshal(ptr+freesize));
/* Submit the queue. */
- w5500_listener_cmd(listener, CMD_SEND);
+ w5500_tcp_listener_cmd(listener, CMD_SEND);
done += freesize;
}
return done;
}
-static ssize_t w5500_read(implements_net_conn *_self, void *buf, size_t count) {
+static ssize_t w5500_tcp_read(implements_net_stream_conn *_self, void *buf, size_t count) {
ASSERT_CONN();
assert(buf);
assert(count);
@@ -452,20 +473,20 @@ static ssize_t w5500_read(implements_net_conn *_self, void *buf, size_t count) {
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_listener_cmd(listener, CMD_RECV);
+ w5500_tcp_listener_cmd(listener, CMD_RECV);
done += avail;
}
return done;
}
-static int w5500_close(implements_net_conn *_self, bool rd, bool wr) {
+static int w5500_tcp_close(implements_net_stream_conn *_self, bool rd, bool wr) {
ASSERT_CONN();
if (rd)
self->read_open = false;
if (wr && self->write_open) {
- w5500_listener_cmd(listener, CMD_DISCON);
+ w5500_tcp_listener_cmd(listener, CMD_DISCON);
while (self->write_open) {
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
switch (state) {
@@ -473,7 +494,7 @@ static int w5500_close(implements_net_conn *_self, bool rd, bool wr) {
self->write_open = false;
/* Can still read */
if (!self->read_open)
- w5500_listener_cmd_close(listener);
+ w5500_tcp_listener_cmd_close(listener);
break;
case STATE_CLOSED:
self->write_open = false;