diff options
Diffstat (limited to 'libhw/w5500.c')
-rw-r--r-- | libhw/w5500.c | 128 |
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; } |