summaryrefslogtreecommitdiff
path: root/libhw/w5500.c
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2024-12-09 07:56:02 -0700
committerLuke T. Shumaker <lukeshu@lukeshu.com>2024-12-09 12:24:43 -0700
commit13277cbe391c4502422c7203d333caf2b0ba3f11 (patch)
tree7eeb3589b9abdafc5bb5053280e7628742c049d4 /libhw/w5500.c
parentf9d571d233711c4a1a65c4d5756e09bc9960cc0a (diff)
w5500: Rethink locking wrt interrupt handling
Diffstat (limited to 'libhw/w5500.c')
-rw-r--r--libhw/w5500.c128
1 files changed, 85 insertions, 43 deletions
diff --git a/libhw/w5500.c b/libhw/w5500.c
index 7a5ba01..3d3e9ae 100644
--- a/libhw/w5500.c
+++ b/libhw/w5500.c
@@ -225,50 +225,55 @@ static COROUTINE w5500_irq_cr(void *_chip) {
cr_begin();
for (;;) {
- while (!gpio_get(chip->pin_intr)) {
- debugf("w5500_irq_cr(): gpio low");
-
- uint8_t chipintr = w5500ll_read_common_reg(chip->spidev, chip_interrupt);
- if (chipintr)
- w5500ll_write_common_reg(chip->spidev, chip_interrupt, 0xFF);
-
- uint8_t sockmask = w5500ll_read_common_reg(chip->spidev, sock_interrupt);
- for (uint8_t socknum = 0; socknum < 8; socknum++) {
- if (!(sockmask & (1<<socknum)))
- continue;
- struct _w5500_socket *socket = &chip->sockets[socknum];
-
- uint8_t sockintr = w5500ll_read_sock_reg(chip->spidev, socknum, interrupt);
- debugf("w5500_irq_cr(): sockintr=%"PRIu8, sockintr);
-
- switch (socket->mode) {
- case W5500_MODE_NONE:
- break;
- case W5500_MODE_TCP: case W5500_MODE_UDP:
- uint8_t listen_bits = sockintr & SOCKINTR_CONN,
- send_bits = sockintr & (SOCKINTR_SEND_OK|SOCKINTR_SEND_TIMEOUT),
- recv_bits = sockintr & (SOCKINTR_RECV_DAT|SOCKINTR_RECV_FIN);
-
- if (listen_bits) {
- debugf("w5500_irq_cr(): signal sock[%"PRIu8"]->listen_sema", socknum);
- cr_sema_signal(&socket->listen_sema);
- }
- if (recv_bits) {
- debugf("w5500_irq_cr(): signal sock[%"PRIu8"]->read_sema", socknum);
- cr_sema_signal(&socket->read_sema);
- }
- if (send_bits) {
- debugf("w5500_irq_cr(): signal sock[%"PRIu8"]->write_ch", socknum);
- _w5500_sockintr_ch_send(&socket->write_ch, send_bits);
- }
- break;
- }
+ cr_mutex_lock(&chip->mu);
+ bool had_intr = false;
+
+ uint8_t chipintr = w5500ll_read_common_reg(chip->spidev, chip_interrupt);
+ debugf("w5500_irq_cr(): chipintr=%"PRIu8, chipintr);
+ had_intr = had_intr || (chipintr != 0);
+ if (chipintr)
+ w5500ll_write_common_reg(chip->spidev, chip_interrupt, 0xFF);
+
+ for (uint8_t socknum = 0; socknum < 8; socknum++) {
+ struct _w5500_socket *socket = &chip->sockets[socknum];
- w5500ll_write_sock_reg(chip->spidev, socknum, interrupt, sockintr);
+ uint8_t sockintr = w5500ll_read_sock_reg(chip->spidev, socknum, interrupt);
+ debugf("w5500_irq_cr(): sockintr[%"PRIu8"]=%"PRIu8, socknum, sockintr);
+ had_intr = had_intr || (sockintr != 0);
+
+ switch (socket->mode) {
+ case W5500_MODE_NONE:
+ break;
+ case W5500_MODE_TCP: case W5500_MODE_UDP:
+ uint8_t listen_bits = sockintr & SOCKINTR_CONN,
+ send_bits = sockintr & (SOCKINTR_SEND_OK|SOCKINTR_SEND_TIMEOUT),
+ recv_bits = sockintr & (SOCKINTR_RECV_DAT|SOCKINTR_RECV_FIN);
+
+ if (listen_bits) {
+ debugf("w5500_irq_cr(): signal sock[%"PRIu8"]->listen_sema", socknum);
+ cr_sema_signal(&socket->listen_sema);
+ }
+ if (recv_bits) {
+ debugf("w5500_irq_cr(): signal sock[%"PRIu8"]->read_sema", socknum);
+ cr_sema_signal(&socket->read_sema);
+ }
+ if (send_bits) {
+ debugf("w5500_irq_cr(): signal sock[%"PRIu8"]->write_ch", socknum);
+ _w5500_sockintr_ch_send(&socket->write_ch, send_bits);
+ }
+ break;
}
+
+ w5500ll_write_sock_reg(chip->spidev, socknum, interrupt, sockintr);
}
- cr_sema_wait(&chip->intr);
- debugf("w5500_irq_cr(): sema signalled");
+
+ cr_mutex_unlock(&chip->mu);
+
+ if (!had_intr && gpio_get(chip->pin_intr)) {
+ debugf("w5500_irq_cr(): looks like all interrupts have been processed, sleeping...");
+ cr_sema_wait(&chip->intr);
+ } else
+ cr_yield();
}
cr_end();
@@ -291,11 +296,9 @@ static inline void w5500_socket_cmd(struct _w5500_socket *socket, uint8_t cmd) {
struct w5500 *chip = w5500_socket_chip(socket);
uint8_t socknum = socket->socknum;
- 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) {
@@ -425,20 +428,28 @@ static void w5500_post_reset(struct w5500 *chip) {
}
void w5500_hard_reset(struct w5500 *chip) {
+ cr_mutex_lock(&chip->mu);
+
gpio_put(chip->pin_reset, 0);
sleep_for_ms(1); /* minimum of 500us */
gpio_put(chip->pin_reset, 1);
sleep_for_ms(2); /* minimum of 1ms */
w5500_post_reset(chip);
+
+ cr_mutex_unlock(&chip->mu);
}
void w5500_soft_reset(struct w5500 *chip) {
+ cr_mutex_lock(&chip->mu);
+
w5500ll_write_common_reg(chip->spidev, mode, CHIPMODE_RST);
while (w5500ll_read_common_reg(chip->spidev, mode) & CHIPMODE_RST)
cr_yield();
w5500_post_reset(chip);
+
+ cr_mutex_unlock(&chip->mu);
}
static struct net_eth_addr w5500_if_hwaddr(implements_net_iface *_chip) {
@@ -452,9 +463,13 @@ static void _w5500_if_up(implements_net_iface *_chip, struct net_iface_config cf
struct w5500 *chip = VCALL_SELF(struct w5500, implements_net_iface, _chip);
assert(chip);
+ cr_mutex_lock(&chip->mu);
+
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);
+
+ cr_mutex_unlock(&chip->mu);
}
static void w5500_if_up(implements_net_iface *_chip, struct net_iface_config cfg) {
@@ -518,6 +533,8 @@ static implements_net_stream_conn *w5500_if_tcp_dial(implements_net_iface *_chip
socket->read_open = socket->write_open = true;
restart:
+ cr_mutex_lock(&chip->mu);
+
/* Mimics socket.c:socket(). */
w5500_socket_close(socket);
w5500ll_write_sock_reg(chip->spidev, socknum, mode, SOCKMODE_TCP);
@@ -530,6 +547,7 @@ static implements_net_stream_conn *w5500_if_tcp_dial(implements_net_iface *_chip
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);
+ cr_mutex_unlock(&chip->mu);
for (;;) {
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
debugf("tcp_dial(): state=%s", w5500_state_str(state));
@@ -566,12 +584,14 @@ static implements_net_packet_conn *w5500_if_udp_conn(implements_net_iface *_chip
socket->read_deadline_ns = 0;
/* Mimics socket.c:socket(). */
+ cr_mutex_lock(&chip->mu);
w5500_socket_close(socket);
w5500ll_write_sock_reg(chip->spidev, socknum, mode, SOCKMODE_UDP);
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_UDP)
cr_yield();
+ cr_mutex_unlock(&chip->mu);
return &socket->implements_net_packet_conn;
}
@@ -587,6 +607,8 @@ static implements_net_stream_conn *w5500_tcplist_accept(implements_net_stream_li
return NULL;
}
+ cr_mutex_lock(&chip->mu);
+
/* Mimics socket.c:socket(). */
w5500_socket_close(socket);
w5500ll_write_sock_reg(chip->spidev, socknum, mode, SOCKMODE_TCP);
@@ -597,6 +619,7 @@ static implements_net_stream_conn *w5500_tcplist_accept(implements_net_stream_li
/* Mimics socket.c:listen(). */
w5500_socket_cmd(socket, CMD_LISTEN);
+ cr_mutex_unlock(&chip->mu);
for (;;) {
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
debugf("tcp_listener.accept() => state=%s", w5500_state_str(state));
@@ -660,8 +683,10 @@ static ssize_t w5500_tcp_write(implements_net_stream_conn *_socket, void *buf, s
debugf(" => soft closed");
return -NET_ECLOSED;
}
+ cr_mutex_lock(&chip->mu);
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
if (state != STATE_TCP_ESTABLISHED && state != STATE_TCP_CLOSE_WAIT) {
+ cr_mutex_unlock(&chip->mu);
debugf(" => hard closed");
return -NET_ECLOSED;
}
@@ -669,6 +694,7 @@ static ssize_t w5500_tcp_write(implements_net_stream_conn *_socket, void *buf, s
uint16_t freesize = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, tx_free_size));
if (freesize < count-done && freesize < min_free_space) {
/* Wait for more buffer space. */
+ cr_mutex_unlock(&chip->mu);
cr_yield();
continue;
}
@@ -682,6 +708,8 @@ static ssize_t w5500_tcp_write(implements_net_stream_conn *_socket, void *buf, s
/* Submit the queue. */
w5500_socket_cmd(socket, CMD_SEND);
+
+ cr_mutex_unlock(&chip->mu);
switch (_w5500_sockintr_ch_recv(&socket->write_ch)) {
case SOCKINTR_SEND_OK:
debugf(" => sent %zu", freesize);
@@ -737,6 +765,7 @@ static ssize_t w5500_tcp_read(implements_net_stream_conn *_socket, void *buf, si
debugf(" => recv timeout");
return -NET_ERECV_TIMEOUT;
}
+ cr_mutex_lock(&chip->mu);
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
switch (state) {
case STATE_TCP_CLOSE_WAIT:
@@ -745,6 +774,7 @@ static ssize_t w5500_tcp_read(implements_net_stream_conn *_socket, void *buf, si
break; /* OK */
default:
VCALL(bootclock, del_trigger, &trigger);
+ cr_mutex_unlock(&chip->mu);
debugf(" => hard closed");
return -NET_ECLOSED;
}
@@ -755,10 +785,12 @@ static ssize_t w5500_tcp_read(implements_net_stream_conn *_socket, void *buf, si
break;
if (state == STATE_TCP_CLOSE_WAIT) {
VCALL(bootclock, del_trigger, &trigger);
+ cr_mutex_unlock(&chip->mu);
debugf(" => EOF");
return 0;
}
+ cr_mutex_unlock(&chip->mu);
cr_sema_wait(&socket->read_sema);
}
assert(avail);
@@ -773,6 +805,7 @@ static ssize_t w5500_tcp_read(implements_net_stream_conn *_socket, void *buf, si
w5500_socket_cmd(socket, CMD_RECV);
/* Return. */
VCALL(bootclock, del_trigger, &trigger);
+ cr_mutex_unlock(&chip->mu);
return avail;
}
@@ -784,6 +817,7 @@ static int w5500_tcp_close(implements_net_stream_conn *_socket, bool rd, bool wr
socket->read_open = false;
if (wr && socket->write_open) {
+ cr_mutex_lock(&chip->mu);
w5500_socket_cmd(socket, CMD_DISCON);
while (socket->write_open) {
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
@@ -799,6 +833,7 @@ static int w5500_tcp_close(implements_net_stream_conn *_socket, bool rd, bool wr
break;
}
}
+ cr_mutex_unlock(&chip->mu);
}
w5500_tcp_maybe_free(chip, socket);
@@ -821,8 +856,10 @@ static ssize_t w5500_udp_sendto(implements_net_packet_conn *_socket, void *buf,
}
for (;;) {
+ cr_mutex_lock(&chip->mu);
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
if (state != STATE_UDP) {
+ cr_mutex_unlock(&chip->mu);
debugf(" => closed");
return -NET_ECLOSED;
}
@@ -833,6 +870,7 @@ static ssize_t w5500_udp_sendto(implements_net_packet_conn *_socket, void *buf,
break;
/* Wait for more buffer space. */
+ cr_mutex_unlock(&chip->mu);
cr_yield();
}
@@ -846,6 +884,7 @@ static ssize_t w5500_udp_sendto(implements_net_packet_conn *_socket, void *buf,
/* Submit the queue. */
w5500_socket_cmd(socket, CMD_SEND);
+ cr_mutex_unlock(&chip->mu);
switch (_w5500_sockintr_ch_recv(&socket->write_ch)) {
case SOCKINTR_SEND_OK:
debugf(" => sent");
@@ -893,6 +932,7 @@ static ssize_t w5500_udp_recvfrom(implements_net_packet_conn *_socket, void *buf
debugf(" => recv timeout");
return -NET_ERECV_TIMEOUT;
}
+ cr_mutex_lock(&chip->mu);
uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
if (state != STATE_UDP) {
VCALL(bootclock, del_trigger, &trigger);
@@ -905,6 +945,7 @@ static ssize_t w5500_udp_recvfrom(implements_net_packet_conn *_socket, void *buf
/* We have data to read. */
break;
+ cr_mutex_unlock(&chip->mu);
cr_sema_wait(&socket->read_sema);
}
assert(avail >= 8);
@@ -933,6 +974,7 @@ static ssize_t w5500_udp_recvfrom(implements_net_packet_conn *_socket, void *buf
w5500_socket_cmd(socket, CMD_RECV);
/* Return. */
VCALL(bootclock, del_trigger, &trigger);
+ cr_mutex_unlock(&chip->mu);
return len;
}