diff options
Diffstat (limited to 'cmd/sbc_harness/hw/w5500.c')
-rw-r--r-- | cmd/sbc_harness/hw/w5500.c | 145 |
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; |