summaryrefslogtreecommitdiff
path: root/libhw
diff options
context:
space:
mode:
Diffstat (limited to 'libhw')
-rw-r--r--libhw/CMakeLists.txt3
-rw-r--r--libhw/common_include/libhw/generic/bootclock.h22
-rw-r--r--libhw/common_include/libhw/generic/net.h16
-rw-r--r--libhw/host_bootclock.c20
-rw-r--r--libhw/host_include/libhw/host_net.h4
-rw-r--r--libhw/host_net.c118
-rw-r--r--libhw/rp2040_bootclock.c13
7 files changed, 172 insertions, 24 deletions
diff --git a/libhw/CMakeLists.txt b/libhw/CMakeLists.txt
index 521e697..47ffbdd 100644
--- a/libhw/CMakeLists.txt
+++ b/libhw/CMakeLists.txt
@@ -12,11 +12,13 @@ target_link_libraries(libhw INTERFACE
if (PICO_PLATFORM STREQUAL "rp2040")
target_include_directories(libhw SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/rp2040_include)
target_sources(libhw INTERFACE
+ rp2040_bootclock.c
rp2040_hwspi.c
w5500.c
)
target_link_libraries(libhw INTERFACE
pico_time
+ hardware_timer
hardware_gpio
hardware_spi
)
@@ -25,6 +27,7 @@ endif()
if (PICO_PLATFORM STREQUAL "host")
target_include_directories(libhw SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/host_include)
target_sources(libhw INTERFACE
+ host_bootclock.c
host_net.c
)
endif()
diff --git a/libhw/common_include/libhw/generic/bootclock.h b/libhw/common_include/libhw/generic/bootclock.h
new file mode 100644
index 0000000..bea5338
--- /dev/null
+++ b/libhw/common_include/libhw/generic/bootclock.h
@@ -0,0 +1,22 @@
+/* libhw/generic/bootclock.h - Device-independent clock definitions
+ *
+ * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIBHW_GENERIC_BOOTCLOCK_H_
+#define _LIBHW_GENERIC_BOOTCLOCK_H_
+
+#include <stdint.h> /* for uint{n}_t and UINT{n}_C */
+
+#define NS_PER_S UINT64_C(1000000000) /* needs at least log₂(10⁹) ≈ 29.9 bits */
+#define US_PER_S UINT64_C(1000000) /* needs at least log₂(10⁶) ≈ 19.9 bits */
+#define MS_PER_S UINT64_C(1000) /* needs at least log₂(10³) ≈ 9.9 bits */
+
+/**
+ * (2⁶⁴-1 nanoseconds is more than 500 years; there is little
+ * risk of this overflowing)
+ */
+uint64_t bootclock_get_ns(void);
+
+#endif /* _LIBHW_GENERIC_BOOTCLOCK_H_ */
diff --git a/libhw/common_include/libhw/generic/net.h b/libhw/common_include/libhw/generic/net.h
index 419a8f2..af8844f 100644
--- a/libhw/common_include/libhw/generic/net.h
+++ b/libhw/common_include/libhw/generic/net.h
@@ -61,6 +61,20 @@ struct net_stream_conn_vtable {
void *buf, size_t count);
/**
+ * Set a timestamp after which calls to read() will return
+ * NET_ETIMEDOUT. The timestamp is in nanoseconds on the
+ * system monotonic clock, which is usually (on pico-sdk and
+ * on the Linux kernel) nanoseconds-since-boot.
+ *
+ * A zero value disables the deadline.
+ *
+ * (2⁶⁴-1 nanoseconds is more than 500 years; there is little
+ * risk of this overflowing)
+ */
+ void (*set_read_deadline)(implements_net_stream_conn *self,
+ uint64_t ns_since_boot);
+
+ /**
* Return `count` on success, -errno on error; a short write *is* an
* error.
*
@@ -93,6 +107,8 @@ struct net_packet_conn_vtable {
ssize_t (*recvfrom)(implements_net_packet_conn *self,
void *buf, size_t len,
struct net_ip4_addr *ret_node, uint16_t *ret_port);
+ void (*set_read_deadline)(implements_net_packet_conn *self,
+ uint64_t ns_since_boot);
int (*close )(implements_net_packet_conn *self);
};
diff --git a/libhw/host_bootclock.c b/libhw/host_bootclock.c
new file mode 100644
index 0000000..ca9565c
--- /dev/null
+++ b/libhw/host_bootclock.c
@@ -0,0 +1,20 @@
+/* libhw/host_bootclock.c - <libhw/generic/bootclock.h> implementation for POSIX hosts
+ *
+ * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <assert.h>
+#include <time.h> /* for clock_gettime(), CLOCK_MONOTONIC, struct timespec */
+
+#include <libhw/generic/bootclock.h>
+
+uint64_t bootclock_get_ns(void) {
+ struct timespec ts;
+ int r;
+
+ r = clock_gettime(CLOCK_MONOTONIC, &ts);
+ assert(r == 0);
+
+ return (((uint64_t)ts.tv_sec) * NS_PER_S) + (uint64_t)ts.tv_nsec;
+}
diff --git a/libhw/host_include/libhw/host_net.h b/libhw/host_include/libhw/host_net.h
index 7a0dff4..1b1ba7a 100644
--- a/libhw/host_include/libhw/host_net.h
+++ b/libhw/host_include/libhw/host_net.h
@@ -15,13 +15,14 @@ struct _hostnet_tcp_conn {
implements_net_stream_conn;
int fd;
+ uint64_t read_deadline_ns;
};
struct hostnet_tcp_listener {
implements_net_stream_listener;
int fd;
- struct _hostnet_tcp_conn active_conn;
+ struct _hostnet_tcp_conn active_conn;
};
void hostnet_tcp_listener_init(struct hostnet_tcp_listener *self, uint16_t port);
@@ -30,6 +31,7 @@ struct hostnet_udp_conn {
implements_net_packet_conn;
int fd;
+ uint64_t read_deadline_ns;
};
void hostnet_udp_conn_init(struct hostnet_udp_conn *self, uint16_t port);
diff --git a/libhw/host_net.c b/libhw/host_net.c
index fcdac57..1563ced 100644
--- a/libhw/host_net.c
+++ b/libhw/host_net.c
@@ -22,6 +22,7 @@
#include <libcr/coroutine.h>
#include <libmisc/vcall.h>
+#include <libhw/generic/bootclock.h>
#include <libhw/host_net.h>
/* common *********************************************************************/
@@ -86,18 +87,20 @@ static inline ssize_t hostnet_map_errno(ssize_t v) {
/* TCP init() ( AKA socket(3) + listen(3) )************************************/
static implements_net_stream_conn *hostnet_tcp_accept(implements_net_stream_listener *_listener);
+static void hostnet_tcp_set_read_deadline(implements_net_stream_conn *conn, uint64_t ts_ns);
static ssize_t hostnet_tcp_read(implements_net_stream_conn *conn, void *buf, size_t count);
static ssize_t hostnet_tcp_write(implements_net_stream_conn *conn, void *buf, size_t count);
-static int hostnet_tcp_close(implements_net_stream_conn *conn, bool rd, bool wr);
+static int hostnet_tcp_close(implements_net_stream_conn *conn, bool rd, bool wr);
static struct net_stream_listener_vtable hostnet_tcp_listener_vtable = {
.accept = hostnet_tcp_accept,
};
static struct net_stream_conn_vtable hostnet_tcp_conn_vtable = {
- .read = hostnet_tcp_read,
- .write = hostnet_tcp_write,
- .close = hostnet_tcp_close,
+ .set_read_deadline = hostnet_tcp_set_read_deadline,
+ .read = hostnet_tcp_read,
+ .write = hostnet_tcp_write,
+ .close = hostnet_tcp_close,
};
void hostnet_tcp_listener_init(struct hostnet_tcp_listener *self, uint16_t port) {
@@ -145,7 +148,7 @@ static void *hostnet_pthread_accept(void *_args) {
*(args->ret_connfd) = -errno;
WAKE_COROUTINE(args);
return NULL;
-};
+}
static implements_net_stream_conn *hostnet_tcp_accept(implements_net_stream_listener *_listener) {
struct hostnet_tcp_listener *listener =
@@ -169,11 +172,20 @@ static implements_net_stream_conn *hostnet_tcp_accept(implements_net_stream_list
/* TCP read() *****************************************************************/
+static void hostnet_tcp_set_read_deadline(implements_net_stream_conn *_conn, uint64_t ts_ns) {
+ struct _hostnet_tcp_conn *conn =
+ VCALL_SELF(struct _hostnet_tcp_conn, implements_net_stream_conn, _conn);
+ assert(conn);
+
+ conn->read_deadline_ns = ts_ns;
+}
+
struct hostnet_pthread_read_args {
pthread_t cr_thread;
cid_t cr_coroutine;
int connfd;
+ struct timeval timeout;
void *buf;
size_t count;
@@ -182,12 +194,22 @@ struct hostnet_pthread_read_args {
static void *hostnet_pthread_read(void *_args) {
struct hostnet_pthread_read_args *args = _args;
+
+ *(args->ret) = setsockopt(args->connfd, SOL_SOCKET, SO_RCVTIMEO,
+ &args->timeout, sizeof(args->timeout));
+ if (*(args->ret) < 0)
+ goto end;
+
*(args->ret) = read(args->connfd, args->buf, args->count);
if (*(args->ret) < 0)
+ goto end;
+
+ end:
+ if (*(args->ret) < 0)
*(args->ret) = hostnet_map_errno(-errno);
WAKE_COROUTINE(args);
return NULL;
-};
+}
static ssize_t hostnet_tcp_read(implements_net_stream_conn *_conn, void *buf, size_t count) {
struct _hostnet_tcp_conn *conn =
@@ -205,6 +227,20 @@ static ssize_t hostnet_tcp_read(implements_net_stream_conn *_conn, void *buf, si
.ret = &ret,
};
+ if (conn->read_deadline_ns) {
+ uint64_t now_ns = bootclock_get_ns();
+ if (conn->read_deadline_ns < now_ns)
+ return -NET_ETIMEDOUT;
+ uint64_t timeout_ns = conn->read_deadline_ns-now_ns;
+ args.timeout.tv_sec = timeout_ns
+ /NS_PER_S;
+ args.timeout.tv_usec = (timeout_ns - ((uint64_t)args.timeout.tv_sec)*NS_PER_S)
+ /(NS_PER_S/US_PER_S);
+ } else {
+ args.timeout.tv_sec = 0;
+ args.timeout.tv_usec = 0;
+ }
+
if (RUN_PTHREAD(hostnet_pthread_read, &args))
return -NET_ETHREAD;
return ret;
@@ -238,7 +274,7 @@ static void *hostnet_pthread_write(void *_args) {
*(args->ret) = done;
WAKE_COROUTINE(args);
return NULL;
-};
+}
static ssize_t hostnet_tcp_write(implements_net_stream_conn *_conn, void *buf, size_t count) {
struct _hostnet_tcp_conn *conn =
@@ -282,6 +318,8 @@ static int hostnet_tcp_close(implements_net_stream_conn *_conn, bool rd, bool wr
/* UDP init() *****************************************************************/
+static void hostnet_udp_set_read_deadline(implements_net_packet_conn *self,
+ uint64_t ts_ns);
static ssize_t hostnet_udp_sendto(implements_net_packet_conn *self, void *buf, size_t len,
struct net_ip4_addr addr, uint16_t port);
static ssize_t hostnet_udp_recvfrom(implements_net_packet_conn *self, void *buf, size_t len,
@@ -289,9 +327,10 @@ static ssize_t hostnet_udp_recvfrom(implements_net_packet_conn *self, void *buf,
static int hostnet_udp_close(implements_net_packet_conn *self);
static struct net_packet_conn_vtable hostnet_udp_conn_vtable = {
- .sendto = hostnet_udp_sendto,
- .recvfrom = hostnet_udp_recvfrom,
- .close = hostnet_udp_close,
+ .set_read_deadline = hostnet_udp_set_read_deadline,
+ .sendto = hostnet_udp_sendto,
+ .recvfrom = hostnet_udp_recvfrom,
+ .close = hostnet_udp_close,
};
void hostnet_udp_conn_init(struct hostnet_udp_conn *self, uint16_t port) {
@@ -354,7 +393,7 @@ static void *hostnet_pthread_sendto(void *_args) {
}
static ssize_t hostnet_udp_sendto(implements_net_packet_conn *_conn, void *buf, size_t count,
- struct net_ip4_addr node, uint16_t port) {
+ struct net_ip4_addr node, uint16_t port) {
struct hostnet_udp_conn *conn =
VCALL_SELF(struct hostnet_udp_conn, implements_net_packet_conn, _conn);
assert(conn);
@@ -379,11 +418,21 @@ static ssize_t hostnet_udp_sendto(implements_net_packet_conn *_conn, void *buf,
/* UDP recvfrom() *************************************************************/
+static void hostnet_udp_set_read_deadline(implements_net_packet_conn *_conn,
+ uint64_t ts_ns) {
+ struct hostnet_udp_conn *conn =
+ VCALL_SELF(struct hostnet_udp_conn, implements_net_packet_conn, _conn);
+ assert(conn);
+
+ conn->read_deadline_ns = ts_ns;
+}
+
struct hostnet_pthread_recvfrom_args {
pthread_t cr_thread;
cid_t cr_coroutine;
int connfd;
+ struct timeval timeout;
void *buf;
size_t count;
@@ -402,26 +451,35 @@ static void *hostnet_pthread_recvfrom(void *_args) {
} addr = { 0 };
socklen_t addr_size;
+ *(args->ret_size) = setsockopt(args->connfd, SOL_SOCKET, SO_RCVTIMEO,
+ &args->timeout, sizeof(args->timeout));
+ if (*(args->ret_size) < 0)
+ goto end;
+
*(args->ret_size) = recvfrom(args->connfd, args->buf, args->count, 0, &addr.gen, &addr_size);
if (*(args->ret_size) < 0)
- *(args->ret_size) = hostnet_map_errno(-errno);
- else {
- assert(addr.in.sin_family == AF_INET);
- if (args->ret_node) {
- args->ret_node->octets[0] = (addr.in.sin_addr.s_addr >> 24) & 0xFF;
- args->ret_node->octets[1] = (addr.in.sin_addr.s_addr >> 16) & 0xFF;
- args->ret_node->octets[2] = (addr.in.sin_addr.s_addr >> 8) & 0xFF;
- args->ret_node->octets[3] = (addr.in.sin_addr.s_addr >> 0) & 0xFF;
- }
- if (args->ret_port)
- (*args->ret_port) = ntohs(addr.in.sin_port);
+ goto end;
+
+ assert(addr.in.sin_family == AF_INET);
+ if (args->ret_node) {
+ args->ret_node->octets[0] = (addr.in.sin_addr.s_addr >> 24) & 0xFF;
+ args->ret_node->octets[1] = (addr.in.sin_addr.s_addr >> 16) & 0xFF;
+ args->ret_node->octets[2] = (addr.in.sin_addr.s_addr >> 8) & 0xFF;
+ args->ret_node->octets[3] = (addr.in.sin_addr.s_addr >> 0) & 0xFF;
}
+ if (args->ret_port) {
+ (*args->ret_port) = ntohs(addr.in.sin_port);
+ }
+
+ end:
+ if (*(args->ret_size) < 0)
+ *(args->ret_size) = hostnet_map_errno(-errno);
WAKE_COROUTINE(args);
return NULL;
}
static ssize_t hostnet_udp_recvfrom(implements_net_packet_conn *_conn, void *buf, size_t count,
- struct net_ip4_addr *ret_node, uint16_t *ret_port) {
+ struct net_ip4_addr *ret_node, uint16_t *ret_port) {
struct hostnet_udp_conn *conn =
VCALL_SELF(struct hostnet_udp_conn, implements_net_packet_conn, _conn);
assert(conn);
@@ -439,6 +497,20 @@ static ssize_t hostnet_udp_recvfrom(implements_net_packet_conn *_conn, void *buf
.ret_node = ret_node,
.ret_port = ret_port,
};
+ if (conn->read_deadline_ns) {
+ uint64_t now_ns = bootclock_get_ns();
+ if (conn->read_deadline_ns < now_ns)
+ return -NET_ETIMEDOUT;
+ uint64_t timeout_ns = conn->read_deadline_ns-now_ns;
+ args.timeout.tv_sec = timeout_ns
+ /NS_PER_S;
+ args.timeout.tv_usec = (timeout_ns - ((uint64_t)args.timeout.tv_sec)*NS_PER_S)
+ /(NS_PER_S/US_PER_S);
+ } else {
+ args.timeout.tv_sec = 0;
+ args.timeout.tv_usec = 0;
+ }
+
if (RUN_PTHREAD(hostnet_pthread_recvfrom, &args))
return -NET_ETHREAD;
return ret;
diff --git a/libhw/rp2040_bootclock.c b/libhw/rp2040_bootclock.c
new file mode 100644
index 0000000..0484910
--- /dev/null
+++ b/libhw/rp2040_bootclock.c
@@ -0,0 +1,13 @@
+/* libhw/rp2040_bootclock.c - <libhw/generic/bootclock.h> implementation for pico-sdk
+ *
+ * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <hardware/timer.h> /* pico-sdk:hardware_timer */
+
+#include <libhw/generic/bootclock.h>
+
+uint64_t bootclock_get_ns(void) {
+ return time_us_64()*(NS_PER_S/US_PER_S);
+}