diff options
Diffstat (limited to 'libhw')
-rw-r--r-- | libhw/host_net.c | 38 | ||||
-rw-r--r-- | libhw/rp2040_include/libhw/w5500.h | 6 | ||||
-rw-r--r-- | libhw/w5500.c | 45 | ||||
-rw-r--r-- | libhw/w5500_ll.h | 8 |
4 files changed, 68 insertions, 29 deletions
diff --git a/libhw/host_net.c b/libhw/host_net.c index 330ce85..92a94df 100644 --- a/libhw/host_net.c +++ b/libhw/host_net.c @@ -75,12 +75,28 @@ static inline bool RUN_PTHREAD(void *(*fn)(void *), void *args) { return false; } -static inline ssize_t hostnet_map_negerrno(ssize_t v) { +enum hostnet_timeout_op { + OP_NONE, + OP_SEND, + OP_RECV, +}; + +static inline ssize_t hostnet_map_negerrno(ssize_t v, enum hostnet_timeout_op op) { if (v >= 0) return v; switch (v) { + case -EHOSTUNREACH: + return -NET_EARP_TIMEOUT; case -ETIMEDOUT: - return -NET_ETIMEDOUT; + switch (op) { + case OP_NONE: + assert_notreached("impossible ETIMEDOUT"); + case OP_SEND: + return -NET_EACK_TIMEOUT; + case OP_RECV: + return -NET_ERECV_TIMEOUT; + } + assert_notreached("invalid timeout op"); case -EBADF: return -NET_ECLOSED; case -EMSGSIZE: @@ -186,7 +202,7 @@ static int hostnet_tcplist_close(implements_net_stream_listener *_listener) { VCALL_SELF(struct hostnet_tcp_listener, implements_net_stream_listener, _listener); assert(listener); - return hostnet_map_negerrno(close(listener->fd) ? -errno : 0); + return hostnet_map_negerrno(close(listener->fd) ? -errno : 0, OP_NONE); } /* TCP read() *****************************************************************/ @@ -225,7 +241,7 @@ static void *hostnet_pthread_read(void *_args) { end: if (*(args->ret) < 0) - *(args->ret) = hostnet_map_negerrno(-errno); + *(args->ret) = hostnet_map_negerrno(-errno, OP_SEND); WAKE_COROUTINE(args); return NULL; } @@ -249,7 +265,7 @@ static ssize_t hostnet_tcp_read(implements_net_stream_conn *_conn, void *buf, si if (conn->read_deadline_ns) { uint64_t now_ns = VCALL(bootclock, get_time_ns); if (conn->read_deadline_ns < now_ns) - return -NET_ETIMEDOUT; + return -NET_ERECV_TIMEOUT; args.timeout = ns_to_host_us_time(conn->read_deadline_ns-now_ns); } else { args.timeout = (host_us_time_t){0}; @@ -279,7 +295,7 @@ static void *hostnet_pthread_write(void *_args) { while (done < args->count) { ssize_t r = write(args->connfd, args->buf, args->count); if (r < 0) { - hostnet_map_negerrno(-errno); + hostnet_map_negerrno(-errno, OP_RECV); break; } done += r; @@ -327,7 +343,7 @@ static int hostnet_tcp_close(implements_net_stream_conn *_conn, bool rd, bool wr how = SHUT_WR; else assert_notreached("invalid arguments to stream_conn.close()"); - return hostnet_map_negerrno(shutdown(conn->fd, how) ? -errno : 0); + return hostnet_map_negerrno(shutdown(conn->fd, how) ? -errno : 0, OP_NONE); } /* UDP init() *****************************************************************/ @@ -402,7 +418,7 @@ static void *hostnet_pthread_sendto(void *_args) { addr.in.sin_port = htons(args->port); *(args->ret) = sendto(args->connfd, args->buf, args->count, 0, &addr.gen, sizeof(addr)); if (*(args->ret) < 0) - *(args->ret) = hostnet_map_negerrno(-errno); + *(args->ret) = hostnet_map_negerrno(-errno, OP_SEND); WAKE_COROUTINE(args); return NULL; } @@ -489,7 +505,7 @@ static void *hostnet_pthread_recvfrom(void *_args) { end: if (*(args->ret_size) < 0) - *(args->ret_size) = hostnet_map_negerrno(-errno); + *(args->ret_size) = hostnet_map_negerrno(-errno, OP_RECV); WAKE_COROUTINE(args); return NULL; } @@ -516,7 +532,7 @@ static ssize_t hostnet_udp_recvfrom(implements_net_packet_conn *_conn, void *buf if (conn->read_deadline_ns) { uint64_t now_ns = VCALL(bootclock, get_time_ns); if (conn->read_deadline_ns < now_ns) - return -NET_ETIMEDOUT; + return -NET_ERECV_TIMEOUT; args.timeout = ns_to_host_us_time(conn->read_deadline_ns-now_ns); } else { args.timeout = (host_us_time_t){0}; @@ -534,5 +550,5 @@ static int hostnet_udp_close(implements_net_packet_conn *_conn) { VCALL_SELF(struct hostnet_udp_conn, implements_net_packet_conn, _conn); assert(conn); - return hostnet_map_negerrno(close(conn->fd) ? -errno : 0); + return hostnet_map_negerrno(close(conn->fd) ? -errno : 0, OP_NONE); } diff --git a/libhw/rp2040_include/libhw/w5500.h b/libhw/rp2040_include/libhw/w5500.h index 7538bbc..80366a0 100644 --- a/libhw/rp2040_include/libhw/w5500.h +++ b/libhw/rp2040_include/libhw/w5500.h @@ -9,13 +9,16 @@ #include <pico/binary_info.h> /* for bi_* */ -#include <libcr_ipc/sema.h> +#include <libcr_ipc/chan.h> #include <libcr_ipc/mutex.h> +#include <libcr_ipc/sema.h> #include <libmisc/private.h> #include <libhw/generic/net.h> #include <libhw/generic/spi.h> +CR_CHAN_DECLARE(_w5500_sockintr_ch, uint8_t) + struct _w5500_socket { /* const-after-init */ implements_net_stream_listener implements_net_stream_listener; @@ -35,6 +38,7 @@ struct _w5500_socket { uint64_t read_deadline_ns; /* MODE_{TCP,UDP} */ cr_sema_t listen_sema; /* MODE_TCP */ cr_sema_t read_sema; /* MODE_{TCP,UDP} */ + _w5500_sockintr_ch_t write_ch; /* MODE_{TCP,UDP} */ bool list_open, read_open, write_open; /* MODE_TCP */ cr_mutex_t cmd_mu; diff --git a/libhw/w5500.c b/libhw/w5500.c index d94a88d..c039226 100644 --- a/libhw/w5500.c +++ b/libhw/w5500.c @@ -221,18 +221,18 @@ static COROUTINE w5500_irq_cr(void *_chip) { switch (socket->mode) { case W5500_MODE_NONE: break; - case W5500_MODE_TCP: - /* SOCKINTR_SEND_OK is useless; just count on the write methods to - * poll instead of waiting on notification from here. */ - uint8_t listen_bits = sockintr & (SOCKINTR_TIMEOUT|SOCKINTR_CONN), - read_bits = sockintr & (SOCKINTR_TIMEOUT|SOCKINTR_RECV|SOCKINTR_FIN); + 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) cr_sema_signal(&socket->listen_sema); - /* fallthrough */ - case W5500_MODE_UDP: - if (read_bits) + if (recv_bits) cr_sema_signal(&socket->read_sema); + if (send_bits) + _w5500_sockintr_ch_send(&socket->write_ch, send_bits); + break; } w5500ll_write_sock_reg(chip->spidev, socknum, interrupt, sockintr); @@ -630,7 +630,17 @@ static ssize_t w5500_tcp_write(implements_net_stream_conn *_socket, void *buf, s /* Submit the queue. */ w5500_socket_cmd(socket, CMD_SEND); - done += freesize; + switch (_w5500_sockintr_ch_recv(&socket->write_ch)) { + case SOCKINTR_SEND_OK: + done += freesize; + break; + case SOCKINTR_SEND_TIMEOUT: + return -NET_EACK_TIMEOUT; + case SOCKINTR_SEND_OK|SOCKINTR_SEND_TIMEOUT: + assert_notreached("send both OK and timed out?"); + default: + assert_notreached("invalid write_ch bits"); + } } return done; } @@ -666,7 +676,7 @@ static ssize_t w5500_tcp_read(implements_net_stream_conn *_socket, void *buf, si } if (socket->read_deadline_ns && socket->read_deadline_ns <= VCALL(bootclock, get_time_ns)) { VCALL(bootclock, del_trigger, &trigger); - return -NET_ETIMEDOUT; + return -NET_ERECV_TIMEOUT; } uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state); switch (state) { @@ -768,7 +778,16 @@ static ssize_t w5500_udp_sendto(implements_net_packet_conn *_socket, void *buf, /* Submit the queue. */ w5500_socket_cmd(socket, CMD_SEND); - return count; + switch (_w5500_sockintr_ch_recv(&socket->write_ch)) { + case SOCKINTR_SEND_OK: + return count; + case SOCKINTR_SEND_TIMEOUT: + return -NET_EARP_TIMEOUT; + case SOCKINTR_SEND_OK|SOCKINTR_SEND_TIMEOUT: + assert_notreached("send both OK and timed out?"); + default: + assert_notreached("invalid write_ch bits"); + } } static void w5500_udp_set_read_deadline(implements_net_packet_conn *_socket, uint64_t ns) { @@ -799,7 +818,7 @@ static ssize_t w5500_udp_recvfrom(implements_net_packet_conn *_socket, void *buf for (;;) { if (socket->read_deadline_ns && socket->read_deadline_ns <= VCALL(bootclock, get_time_ns)) { VCALL(bootclock, del_trigger, &trigger); - return -NET_ETIMEDOUT; + return -NET_ERECV_TIMEOUT; } uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state); if (state != STATE_UDP) { @@ -807,7 +826,7 @@ static ssize_t w5500_udp_recvfrom(implements_net_packet_conn *_socket, void *buf return -NET_ECLOSED; } - uint16_t avail = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, rx_size)); + avail = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, rx_size)); if (avail) /* We have data to read. */ break; diff --git a/libhw/w5500_ll.h b/libhw/w5500_ll.h index 9098dd1..c70da0d 100644 --- a/libhw/w5500_ll.h +++ b/libhw/w5500_ll.h @@ -218,10 +218,10 @@ static_assert(sizeof(struct w5500ll_block_sock_reg) == 0x30); #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_OK ((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_SEND_OK ((uint8_t)1<<4) /* TODO: determine precise meaning */ +#define SOCKINTR_SEND_TIMEOUT ((uint8_t)1<<3) /* ARP or TCP */ +#define SOCKINTR_RECV_DAT ((uint8_t)1<<2) /* received data */ +#define SOCKINTR_RECV_FIN ((uint8_t)1<<1) /* received FIN */ #define SOCKINTR_CONN ((uint8_t)1<<1) /* first for SYN, then when SOCKMODE_ESTABLISHED */ #define STATE_CLOSED ((uint8_t)0x00) |