summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libdhcp/dhcp_client.c11
-rw-r--r--libhw/host_net.c38
-rw-r--r--libhw/rp2040_include/libhw/w5500.h6
-rw-r--r--libhw/w5500.c45
-rw-r--r--libhw/w5500_ll.h8
-rw-r--r--libhw_generic/include/libhw/generic/net.h12
6 files changed, 81 insertions, 39 deletions
diff --git a/libdhcp/dhcp_client.c b/libdhcp/dhcp_client.c
index 34ce8ac..4f01f70 100644
--- a/libdhcp/dhcp_client.c
+++ b/libdhcp/dhcp_client.c
@@ -691,7 +691,8 @@ static ssize_t dhcp_client_recv(struct dhcp_client *client, struct dhcp_recv_msg
/** @return true if there's a conflict, false if the addr appears to be unused */
static bool dhcp_check_conflict(implements_net_packet_conn *sock, struct net_ip4_addr addr) {
assert(sock);
- return VCALL(sock, sendto, "CHECK_IP_CONFLICT", 17, addr, 5000) != NET_ETIMEDOUT;
+ ssize_t v = VCALL(sock, sendto, "CHECK_IP_CONFLICT", 17, addr, 5000);
+ return v != NET_EARP_TIMEOUT;
}
static void dhcp_client_take_lease(struct dhcp_client *client, struct dhcp_recv_msg *msg, bool ifup) {
@@ -768,7 +769,7 @@ static __attribute__((noreturn)) void dhcp_client_run(struct dhcp_client *client
/* ignore */
}
break;
- case -NET_ETIMEDOUT:
+ case -NET_ERECV_TIMEOUT:
dhcp_client_setstate(client, STATE_INIT, 0, NULL, scratch_msg);
break;
default:
@@ -807,7 +808,7 @@ static __attribute__((noreturn)) void dhcp_client_run(struct dhcp_client *client
case 0:
/* discard */
break;
- case -NET_ETIMEDOUT:
+ case -NET_ERECV_TIMEOUT:
dhcp_client_setstate(client, STATE_RENEWING, DHCP_MSGTYP_REQUEST, NULL, scratch_msg);
break;
default:
@@ -835,7 +836,7 @@ static __attribute__((noreturn)) void dhcp_client_run(struct dhcp_client *client
/* ignore */
}
break;
- case -NET_ETIMEDOUT:
+ case -NET_ERECV_TIMEOUT:
client->lease_server_id = net_ip4_addr_zero;
dhcp_client_setstate(client, STATE_REBINDING, DHCP_MSGTYP_REQUEST, NULL, scratch_msg);
break;
@@ -861,7 +862,7 @@ static __attribute__((noreturn)) void dhcp_client_run(struct dhcp_client *client
/* ignore */
}
break;
- case -NET_ETIMEDOUT:
+ case -NET_ERECV_TIMEOUT:
VCALL(client->iface, ifdown);
dhcp_client_setstate(client, STATE_BOUND, 0, NULL, scratch_msg);
break;
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)
diff --git a/libhw_generic/include/libhw/generic/net.h b/libhw_generic/include/libhw/generic/net.h
index def533c..150d199 100644
--- a/libhw_generic/include/libhw/generic/net.h
+++ b/libhw_generic/include/libhw/generic/net.h
@@ -14,11 +14,13 @@
/* Errnos *********************************************************************/
-#define NET_EOTHER 1
-#define NET_ETIMEDOUT 2
-#define NET_ETHREAD 3
-#define NET_ECLOSED 4
-#define NET_EMSGSIZE 5
+#define NET_EOTHER 1
+#define NET_EARP_TIMEOUT 2
+#define NET_EACK_TIMEOUT 3
+#define NET_ERECV_TIMEOUT 4
+#define NET_ETHREAD 5
+#define NET_ECLOSED 6
+#define NET_EMSGSIZE 7
/* Address types **************************************************************/