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.c372
1 files changed, 224 insertions, 148 deletions
diff --git a/cmd/sbc_harness/hw/w5500.c b/cmd/sbc_harness/hw/w5500.c
index b6d2b33..5e418f1 100644
--- a/cmd/sbc_harness/hw/w5500.c
+++ b/cmd/sbc_harness/hw/w5500.c
@@ -59,6 +59,11 @@
#define UNUSED(name)
+#define ASSERT_SAMETYPE(a, b) \
+ static_assert(_Generic(a, typeof(b): 1, default: 0), \
+ "typeof("#a") != typeof("#b")")
+
+
/* Low-level protocol built on SPI frames. ***********************************/
/* A u8 control byte has 3 parts: block-ID, R/W, and
@@ -137,88 +142,109 @@ void w5500_spiframe_read(struct spi *spidev, uint16_t addr, uint8_t block, void
/* Offsets and sizes for use with that protocol. *****************************/
+struct uint16_be {
+ uint8_t octets[2];
+};
+
struct w5500_block_common_reg {
- uint8_t mode; /* MR */
- uint8_t ip_gateway_addr[4]; /* GAR0 ... GAR3 */
- uint8_t ip_subnet_mask[4]; /* SUBR0 ... SUBR3 */
- uint8_t eth_addr[6]; /* SHAR0 ... SHAR5 */
- uint8_t ip_addr[4]; /* SIPR0 ... SIPR3 */
-
- uint8_t intlevel_0; /* INTLEVEL0 */
- uint8_t intlevel_1; /* INTLEVEL1 */
- uint8_t interrupt; /* IR */
- uint8_t interrupt_mask; /* IMR */
- uint8_t sock_interrupt; /* SIR */
- uint8_t sock_interrupt_mask; /* SIMR */
- uint8_t retry_time_0; /* RTR0 */
- uint8_t retry_time_1; /* RTR0 */
- uint8_t retry_count; /* RCR */
-
- uint8_t ppp_lcp_request_timer; /* PTIMER */
- uint8_t ppp_lcp_magic_bumber; /* PMAGIC */
- uint8_t ppp_dst_eth_addr[6]; /* PHAR0 ... PHAR5 */
- uint8_t ppp_sess_id[2]; /* PSID0 ... PSID1 */
- uint8_t ppp_max_seg_size[2]; /* PMRU0 ... PMRU1 */
-
- uint8_t unreachable_ip_addr[4]; /* UIPR0 ... UIPR3 */
- uint8_t unreachable_port[2]; /* UPORTR0, UPORTR1 */
-
- uint8_t phy_cfg; /* PHYCFGR */
-
- uint8_t _reserved[10];
-
- uint8_t chip_version; /* VERSIONR */
+ uint8_t mode; /* MR; bitfield, see CHIPMODE_{x} below */
+ struct net_ip4_addr ip_gateway_addr; /* GAR0 ... GAR3 */
+ struct net_ip4_addr ip_subnet_mask; /* SUBR0 ... SUBR3 */
+ struct net_eth_addr eth_addr; /* SHAR0 ... SHAR5 */
+ struct net_ip4_addr ip_addr; /* SIPR0 ... SIPR3 */
+
+ struct uint16_be intlevel; /* INTLEVEL0, INTLEVEL1; minimum time before pin_intr can be pulled low again (time=(intlevel+1)*4/PLL) */
+ uint8_t chip_interrupt; /* IR; bitfield, see CHIPINTR_{x} below */
+ uint8_t chip_interrupt_mask; /* IMR; bitfield, see CHIPINTR_{x} below, 0=disable, 1=enable */
+ uint8_t sock_interrupt; /* SIR; bitfield of which sockets have their .interrupt set */
+ uint8_t sock_interrupt_mask; /* SIMR; bitfield of sockets, 0=disable, 1=enable */
+ struct uint16_be retry_time; /* RTR0, RTR0; configures re-transmission period, in units of 100µs */
+ uint8_t retry_count; /* RCR; configures max re-transmission count */
+
+ uint8_t ppp_lcp_request_timer; /* PTIMER */
+ uint8_t ppp_lcp_magic_bumber; /* PMAGIC */
+ struct net_eth_addr ppp_dst_eth_addr; /* PHAR0 ... PHAR5 */
+ struct uint16_be ppp_sess_id; /* PSID0 ... PSID1 */
+ struct uint16_be ppp_max_seg_size; /* PMRU0 ... PMRU1 */
+
+ struct net_ip4_addr unreachable_ip_addr; /* UIPR0 ... UIPR3 */
+ struct uint16_be unreachable_port; /* UPORTR0, UPORTR1 */
+
+ uint8_t phy_cfg; /* PHYCFGR */
+
+ uint8_t _reserved[10];
+
+ uint8_t chip_version; /* VERSIONR */
};
static_assert(sizeof(struct w5500_block_common_reg) == 0x3A);
+/* bitfield */
+#define CHIPMODE_RST ((uint8_t)(1<<7)); /* software reset */
+#define _CHIPMODE_UNUSED6 ((uint8_t)(1<<6));
+#define CHIPMODE_WOL ((uint8_t)(1<<5)); /* wake-on-lan */
+#define CHIPMODE_BLOCK_PING ((uint8_t)(1<<4));
+#define CHIPMODE_PPP ((uint8_t)(1<<3));
+#define _CHIPMODE_UNUSED2 ((uint8_t)(1<<2));
+#define CHIPMODE_FORCE_ARP ((uint8_t)(1<<1));
+#define _CHIPMODE_UNUSED0 ((uint8_t)(1<<0));
+
+#define CHIPINTR_CONFLICT ((uint8_t)(1<<7)); /* ARP says remote IP is self */
+#define CHIPINTR_UNREACH ((uint8_t)(1<<6));
+#define CHIPINTR_PPP_CLOSE ((uint8_t)(1<<6));
+#define CHIPINTR_WOL ((uint8_t)(1<<4));
+#define _CHIPINTR_UNUSED3 ((uint8_t)(1<<3));
+#define _CHIPINTR_UNUSED2 ((uint8_t)(1<<2));
+#define _CHIPINTR_UNUSED1 ((uint8_t)(1<<1));
+#define _CHIPINTR_UNUSED0 ((uint8_t)(1<<0));
+
struct w5500_block_sock_reg {
- uint8_t mode; /* Sn_MR; see MODE_{x} below */
- uint8_t command; /* Sn_CR; see CMD_{x} below */
- uint8_t interrupt; /* Sn_IR */
- uint8_t state; /* Sn_SR; see STATE_{x} below */
- uint8_t local_port[2]; /* Sn_PORT0, Sn_PORT1 */
- uint8_t remote_eth_addr[6]; /* Sn_DHAR0 ... SnDHAR5 */
- uint8_t remote_ip_addr[4]; /* Sn_DIPR0 ... Sn_DIP3 */
- uint8_t remote_port[2]; /* Sn_DPORT0 ... Sn_DPORT1 */
-
- uint8_t max_seg_size[2]; /* Sn_MSSR0, Sn_MSSR1 */
- uint8_t _reserved0[1];
- uint8_t ip_tos; /* Sn_TOS */
- uint8_t ip_ttl; /* Sn_TTL */
- uint8_t _reserved1[7];
-
- uint8_t rx_buf_size; /* Sn_RXBUF_SIZE */
- uint8_t tx_buf_size; /* Sn_TXBUF_SIZE */
- uint8_t tx_free_size[2]; /* Sn_TX_FSR0, Sn_TX_FSR1 */
- uint8_t tx_read_pointer[2]; /* Sn_TX_RD0, Sn_TX_RD1 */
- uint8_t tx_write_pointer[2]; /* Sn_TX_WR0, Sn_TX_WR1 */
- uint8_t rx_size[2]; /* Sn_RX_RSR0, Sn_RX_RSR1 */
- uint8_t rx_read_pointer[2]; /* Sn_RX_RD0, Sn_RX_RD1 */
- uint8_t rx_write_pointer[2]; /* Sn_RX_WR0, Sn_RX_WR1 */
-
- uint8_t interrupt_mask; /* Sn_IMR */
- uint8_t fragment_offset[2]; /* Sn_FRAG0, Sn_FRAG1 */
- uint8_t keepalive_timer; /* Sn_KPALVTR */
+ uint8_t mode; /* Sn_MR; see SOCKMODE_{x} below */
+ uint8_t command; /* Sn_CR; see CMD_{x} below */
+ uint8_t interrupt; /* Sn_IR; bitfield, see SOCKINTR_{x} below */
+ uint8_t state; /* Sn_SR; see STATE_{x} below */
+ struct uint16_be local_port; /* Sn_PORT0, Sn_PORT1 */
+ struct net_eth_addr remote_eth_addr; /* Sn_DHAR0 ... SnDHAR5 */
+ struct net_ip4_addr remote_ip_addr; /* Sn_DIPR0 ... Sn_DIP3 */
+ struct uint16_be remote_port; /* Sn_DPORT0 ... Sn_DPORT1 */
+
+ struct uint16_be max_seg_size; /* Sn_MSSR0, Sn_MSSR1 */
+ uint8_t _reserved0[1];
+ uint8_t ip_tos; /* Sn_TOS */
+ uint8_t ip_ttl; /* Sn_TTL */
+ uint8_t _reserved1[7];
+
+ uint8_t rx_buf_size; /* Sn_RXBUF_SIZE */
+ uint8_t tx_buf_size; /* Sn_TXBUF_SIZE */
+ struct uint16_be tx_free_size; /* Sn_TX_FSR0, Sn_TX_FSR1 */
+ struct uint16_be tx_read_pointer; /* Sn_TX_RD0, Sn_TX_RD1 */
+ struct uint16_be tx_write_pointer; /* Sn_TX_WR0, Sn_TX_WR1 */
+ struct uint16_be rx_size; /* Sn_RX_RSR0, Sn_RX_RSR1 */
+ struct uint16_be rx_read_pointer; /* Sn_RX_RD0, Sn_RX_RD1 */
+ struct uint16_be rx_write_pointer; /* Sn_RX_WR0, Sn_RX_WR1 */
+
+ uint8_t interrupt_mask; /* Sn_IMR */
+ struct uint16_be fragment_offset; /* Sn_FRAG0, Sn_FRAG1 */
+ uint8_t keepalive_timer; /* Sn_KPALVTR */
};
static_assert(sizeof(struct w5500_block_sock_reg) == 0x30);
/* low 4 bits are the main enum, high 4 bits are flags */
-#define MODE_CLOSED ((uint8_t)0b0000)
-#define MODE_TCP ((uint8_t)0b0001)
-#define MODE_UDP ((uint8_t)0b0010)
-#define MODE_MACRAW ((uint8_t)0b0100)
+#define SOCKMODE_CLOSED ((uint8_t)0b0000)
+#define SOCKMODE_TCP ((uint8_t)0b0001)
+#define SOCKMODE_UDP ((uint8_t)0b0010)
+#define SOCKMODE_MACRAW ((uint8_t)0b0100)
-#define MODE_FLAG_TCP_NODELAY_ACK ((uint8_t)(1<<5))
+#define SOCKMODE_FLAG_TCP_NODELAY_ACK ((uint8_t)(1<<5))
-#define MODE_FLAG_UDP_ENABLE_MULTICAST ((uint8_t)(1<<7))
-#define MODE_FLAG_UDP_BLOCK_BROADCAST ((uint8_t)(1<<6))
-#define MODE_FLAG_UDP_MULTICAST_DOWNGRADE ((uint8_t)(1<<5))
-#define MODE_FLAG_UDP_BLOCK_UNICAST ((uint8_t)(1<<4))
+#define SOCKMODE_FLAG_UDP_ENABLE_MULTICAST ((uint8_t)(1<<7))
+#define SOCKMODE_FLAG_UDP_BLOCK_BROADCAST ((uint8_t)(1<<6))
+#define SOCKMODE_FLAG_UDP_MULTICAST_DOWNGRADE ((uint8_t)(1<<5))
+#define SOCKMODE_FLAG_UDP_BLOCK_UNICAST ((uint8_t)(1<<4))
-#define MODE_FLAG_MACRAW_MAC_FILTERING ((uint8_t)(1<<7))
-#define MODE_FLAG_MACRAW_BLOCK_BROADCAST ((uint8_t)(1<<6))
-#define MODE_FLAG_MACRAW_BLOCK_MULTICAST ((uint8_t)(1<<5))
-#define MODE_FLAG_MACRAW_BLOCK_V6 ((uint8_t)(1<<4))
+#define SOCKMODE_FLAG_MACRAW_MAC_FILTERING ((uint8_t)(1<<7))
+#define SOCKMODE_FLAG_MACRAW_BLOCK_BROADCAST ((uint8_t)(1<<6))
+#define SOCKMODE_FLAG_MACRAW_BLOCK_MULTICAST ((uint8_t)(1<<5))
+#define SOCKMODE_FLAG_MACRAW_BLOCK_V6 ((uint8_t)(1<<4))
#define CMD_OPEN ((uint8_t)0x01)
#define CMD_LISTEN ((uint8_t)0x02) /* TCP-only */
@@ -230,6 +256,15 @@ static_assert(sizeof(struct w5500_block_sock_reg) == 0x30);
#define CMD_SEND_KEEP ((uint8_t)0x22) /* TCP-only: send a keepalive without any data */
#define CMD_RECV ((uint8_t)0x40)
+#define _SOCKINTR_SEND_UNUSED7 ((uint8_t)1<<7)
+#define _SOCKINTR_SEND_UNUSED6 ((uint8_t)1<<6)
+#define _SOCKINTR_SEND_UNUSED5 ((uint8_t)1<<5)
+#define SOCKINTR_SEND_FINISHED ((uint8_t)1<<4)
+#define SOCKINTR_TIMEOUT ((uint8_t)1<<3)
+#define SOCKINTR_RECV ((uint8_t)1<<2)
+#define SOCKINTR_FIN ((uint8_t)1<<1)
+#define SOCKINTR_CON ((uint8_t)1<<1) /* once for SYN, then once when SOCKMODE_ESTABLISHED */
+
#define STATE_CLOSED ((uint8_t)0x00)
/**
@@ -317,46 +352,47 @@ static_assert(sizeof(struct w5500_block_sock_reg) == 0x30);
#define STATE_MACRAW ((uint8_t)0x42)
-#define REGWRITE_COMMON(spidev, field, val) do { \
- assert(sizeof(val) == sizeof(((struct w5500_block_common_reg){}).field)); \
- typeof(val) lval = val; \
- w5500_spiframe_write(spidev, \
- offsetof(struct w5500_block_common_reg, field), \
- CTL_BLOCK_COMMON_REG, \
- &lval, \
- sizeof(lval)); \
+#define REGWRITE_COMMON(spidev, field, val) do { \
+ ASSERT_SAMETYPE(val, (struct w5500_block_common_reg){}.field); \
+ typeof(val) lval = val; \
+ w5500_spiframe_write(spidev, \
+ offsetof(struct w5500_block_common_reg, field), \
+ CTL_BLOCK_COMMON_REG, \
+ &lval, \
+ sizeof(lval)); \
} while (0)
-#define REGREAD_COMMON(spidev, field, typ) ({ \
- typ val; \
- assert(sizeof(val) == sizeof(((struct w5500_block_sock_reg){}).field)); \
- w5500_spiframe_read(spidev, \
- offsetof(struct w5500_block_sock_reg, field), \
- CTL_BLOCK_COMMON, \
- &(val), \
- sizeof(val)); \
- val; \
+
+#define REGREAD_COMMON(spidev, field, typ) ({ \
+ typ val; \
+ ASSERT_SAMETYPE(val, (struct w5500_block_common_reg){}.field); \
+ w5500_spiframe_read(spidev, \
+ offsetof(struct w5500_block_sock_reg, field), \
+ CTL_BLOCK_COMMON, \
+ &(val), \
+ sizeof(val)); \
+ val; \
})
-#define REGWRITE_SOCK(spidev, socknum, field, val) do { \
- assert(sizeof(val) == sizeof(((struct w5500_block_sock_reg){}).field)); \
- typeof(val) lval = val; \
- w5500_spiframe_write(spidev, \
- offsetof(struct w5500_block_sock_reg, field), \
- CTL_BLOCK_SOCK(socknum, REG), \
- &lval, \
- sizeof(lval)); \
+#define REGWRITE_SOCK(spidev, socknum, field, val) do { \
+ ASSERT_SAMETYPE(val, (struct w5500_block_sock_reg){}.field); \
+ typeof(val) lval = val; \
+ w5500_spiframe_write(spidev, \
+ offsetof(struct w5500_block_sock_reg, field), \
+ CTL_BLOCK_SOCK(socknum, REG), \
+ &lval, \
+ sizeof(lval)); \
} while (0)
-#define REGREAD_SOCK(spidev, socknum, field, typ) ({ \
- typ val; \
- assert(sizeof(val) == sizeof(((struct w5500_block_sock_reg){}).field)); \
- w5500_spiframe_read(spidev, \
- offsetof(struct w5500_block_sock_reg, field), \
- CTL_BLOCK_SOCK(socknum, REG), \
- &(val), \
- sizeof(val)); \
- val; \
+#define REGREAD_SOCK(spidev, socknum, field, typ) ({ \
+ typ val; \
+ ASSERT_SAMETYPE(val, (struct w5500_block_sock_reg){}.field); \
+ w5500_spiframe_read(spidev, \
+ offsetof(struct w5500_block_sock_reg, field), \
+ CTL_BLOCK_SOCK(socknum, REG), \
+ &(val), \
+ sizeof(val)); \
+ val; \
})
/* init() *********************************************************************/
@@ -376,32 +412,14 @@ static struct net_conn_vtable w5500_conn_vtable = {
.close = w5500_close,
};
-static uint16_t w5500_get_local_port(struct w5500 *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;
-}
-
-static struct w5500 *w5500_listener_chip(struct _w5500_listener *listener) {
- assert(listener);
- assert(listener->socknum < 8);
-
- struct _w5500_listener *sock0 = &listener[-listener->socknum];
- assert(sock0);
- struct w5500 *chip =
- ((void *)sock0) - offsetof(struct w5500, listeners);
- assert(chip);
- return chip;
-}
-
-static struct _w5500_listener *w5500_conn_listener(struct _w5500_conn *conn) {
- assert(conn);
-
- struct _w5500_listener *list =
- ((void *)conn) - offsetof(struct _w5500_listener, active_conn);
- return list;
-}
+/*
+ static uint16_t w5500_get_local_port(struct w5500 *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;
+ }
+*/
static void w5500_intrhandler(uint UNUSED(gpio), uint32_t UNUSED(event_mask)) {
/* TODO */
@@ -427,7 +445,11 @@ void _w5500_init(struct w5500 *chip,
.vtable = &w5500_listener_vtable,
.socknum = i,
.active_conn = {
+ /* const-after-init */
.vtable = &w5500_conn_vtable,
+ /* mutable */
+ .read_open = false,
+ .write_open = false,
},
/* mutable */
.port = 0,
@@ -440,6 +462,8 @@ void _w5500_init(struct w5500 *chip,
w5500_reset(chip, addr);
}
+/* chip methods ***************************************************************/
+
void w5500_reset(struct w5500 *chip, struct net_eth_addr addr) {
/* TODO: Replace blocking sleep_ms() with something libcr-friendly. */
gpio_put(chip->pin_reset, 0);
@@ -456,8 +480,6 @@ void w5500_netcfg(struct w5500 *chip, struct w5500_netcfg cfg) {
REGWRITE_COMMON(chip->spidev, ip_addr, cfg.addr);
}
-/* listen() *******************************************************************/
-
implements_net_listener *w5500_listen(struct w5500 *chip, uint8_t socknum,
uint16_t port) {
assert(chip);
@@ -470,13 +492,25 @@ implements_net_listener *w5500_listen(struct w5500 *chip, uint8_t socknum,
return &chip->listeners[socknum];
}
-/* accept() *******************************************************************/
+/* listener methods ***********************************************************/
-#define ASSERT_LISTENER() \
- struct _w5500_listener *self = \
- VCALL_SELF(struct _w5500_listener, \
+static struct w5500 *w5500_listener_chip(struct _w5500_listener *listener) {
+ assert(listener);
+ assert(listener->socknum < 8);
+
+ struct _w5500_listener *sock0 = &listener[-listener->socknum];
+ assert(sock0);
+ struct w5500 *chip =
+ ((void *)sock0) - offsetof(struct w5500, listeners);
+ assert(chip);
+ return chip;
+}
+
+#define ASSERT_LISTENER() \
+ struct _w5500_listener *self = \
+ VCALL_SELF(struct _w5500_listener, \
implements_net_listener, _self); \
- struct w5500 *chip = w5500_listener_chip(self); \
+ struct w5500 *chip = w5500_listener_chip(self); \
uint8_t socknum = self->socknum;
static void w5500_cmd_close(struct _w5500_listener *listener) {
@@ -496,9 +530,9 @@ static implements_net_conn *w5500_accept(implements_net_listener *_self) {
/* Mimics socket.c:socket(). */
w5500_cmd_close(self);
- REGWRITE_SOCK(chip->spidev, socknum, mode, MODE_TCP);
- struct { uint8_t octets[2]; } port_be = {{self->port>>8, self->port}};
- REGWRITE_SOCK(chip->spidev, socknum, local_port, port_be);
+ REGWRITE_SOCK(chip->spidev, socknum, mode, SOCKMODE_TCP);
+ struct uint16_be port = {{self->port>>8, self->port}};
+ REGWRITE_SOCK(chip->spidev, socknum, local_port, port);
REGWRITE_SOCK(chip->spidev, socknum, command, CMD_OPEN);
while (REGREAD_SOCK(chip->spidev, socknum, command, uint8_t) != 0x00)
cr_yield();
@@ -510,25 +544,35 @@ static implements_net_conn *w5500_accept(implements_net_listener *_self) {
while (REGREAD_SOCK(chip->spidev, socknum, command, uint8_t) != 0x00)
cr_yield();
for (;;) {
- uint8_t mode = REGREAD_SOCK(chip->spidev, socknum, state, uint8_t);
- switch (mode) {
+ uint8_t state = REGREAD_SOCK(chip->spidev, socknum, state, uint8_t);
+ switch (state) {
case STATE_TCP_LISTEN:
case STATE_TCP_SYNRECV:
cr_yield();
break;
default:
+ self->active_conn.read_open = true;
+ self->active_conn.write_open = true;
return &self->active_conn;
}
}
}
-/* write() ********************************************************************/
+/* conn methods ***************************************************************/
+
+static struct _w5500_listener *w5500_conn_listener(struct _w5500_conn *conn) {
+ assert(conn);
+
+ struct _w5500_listener *list =
+ ((void *)conn) - offsetof(struct _w5500_listener, active_conn);
+ return list;
+}
-#define ASSERT_CONN() \
- struct _w5500_conn *self = \
+#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_listener *listener = w5500_conn_listener(self); \
+ struct w5500 *chip = w5500_listener_chip(listener); \
uint8_t socknum = listener->socknum;
static ssize_t w5500_write(implements_net_conn *_self, void *buf, size_t count) {
@@ -536,7 +580,10 @@ static ssize_t w5500_write(implements_net_conn *_self, void *buf, size_t count)
assert(buf);
assert(count);
- // TODO
+ uint8_t state = REGREAD_SOCK(chip->spidev, socknum, state, uint8_t);
+ if (state != STATE_TCP_ESTABLISHED && state != STATE_TCP_CLOSE_WAIT)
+ return -1;
+
}
static ssize_t w5500_read(implements_net_conn *_self, void *buf, size_t count) {
@@ -546,3 +593,32 @@ static ssize_t w5500_read(implements_net_conn *_self, void *buf, size_t count) {
// TODO
}
+
+static int w5500_close(implements_net_conn *_self, bool rd, bool wr) {
+ ASSERT_CONN();
+
+ if (rd)
+ self->read_open = false;
+
+ if (wr && self->write_open) {
+ REGWRITE_SOCK(chip->spidev, socknum, command, CMD_DISCON);
+ while (REGREAD_SOCK(chip->spidev, socknum, command, uint8_t) != 0x00)
+ cr_yield();
+ while (self->write_open) {
+ uint8_t state = REGREAD_SOCK(chip->spidev, socknum, state, uint8_t);
+ switch (state) {
+ case STATE_TCP_FIN_WAIT:
+ self->write_open = false;
+ /* Can still read */
+ if (!self->read_open)
+ w5500_cmd_close(listener);
+ break;
+ case STATE_CLOSED:
+ self->write_open = false;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}