summaryrefslogtreecommitdiff
path: root/libhw_generic
diff options
context:
space:
mode:
Diffstat (limited to 'libhw_generic')
-rw-r--r--libhw_generic/CMakeLists.txt15
-rw-r--r--libhw_generic/alarmclock.c21
-rw-r--r--libhw_generic/include/libhw/generic/alarmclock.h98
-rw-r--r--libhw_generic/include/libhw/generic/net.h154
-rw-r--r--libhw_generic/include/libhw/generic/spi.h47
5 files changed, 335 insertions, 0 deletions
diff --git a/libhw_generic/CMakeLists.txt b/libhw_generic/CMakeLists.txt
new file mode 100644
index 0000000..9c88937
--- /dev/null
+++ b/libhw_generic/CMakeLists.txt
@@ -0,0 +1,15 @@
+# libhw_generic/CMakeLists.txt - UAPI device interfaces
+#
+# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+add_library(libhw_generic INTERFACE)
+target_include_directories(libhw_generic SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
+target_link_libraries(libhw_generic INTERFACE
+ libmisc
+ libcr
+)
+
+target_sources(libhw_generic INTERFACE
+ alarmclock.c
+)
diff --git a/libhw_generic/alarmclock.c b/libhw_generic/alarmclock.c
new file mode 100644
index 0000000..d539496
--- /dev/null
+++ b/libhw_generic/alarmclock.c
@@ -0,0 +1,21 @@
+/* libhw_generic/alarmclock.c - Device-independent <libhw/generic/alarmclock.h> utilities
+ *
+ * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libcr/coroutine.h>
+#include <libmisc/vcall.h>
+
+#include <libhw/generic/alarmclock.h>
+
+static void alarmclock_sleep_intrhandler(void *_arg) {
+ cid_t *cid = _arg;
+ cr_unpause_from_intrhandler(*cid);
+}
+
+void alarmclock_sleep_until_ns(implements_alarmclock *clock, uint64_t abstime_ns) {
+ cid_t cid = cr_getcid();
+ struct alarmclock_trigger trigger;
+ VCALL(clock, add_trigger, &trigger, abstime_ns, alarmclock_sleep_intrhandler, &cid);
+}
diff --git a/libhw_generic/include/libhw/generic/alarmclock.h b/libhw_generic/include/libhw/generic/alarmclock.h
new file mode 100644
index 0000000..a9d816b
--- /dev/null
+++ b/libhw_generic/include/libhw/generic/alarmclock.h
@@ -0,0 +1,98 @@
+/* libhw/generic/alarmclock.h - Device-independent alarmclock definitions
+ *
+ * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIBHW_GENERIC_ALARMCLOCK_H_
+#define _LIBHW_GENERIC_ALARMCLOCK_H_
+
+#include <stdbool.h> /* for bool */
+#include <stdint.h> /* for uint{n}_t and UINT{n}_C */
+
+#include <libmisc/private.h>
+#include <libmisc/vcall.h>
+
+/* Useful constants ***********************************************************/
+
+#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 */
+
+/* Structs ********************************************************************/
+
+struct alarmclock_trigger;
+struct alarmclock_trigger {
+ BEGIN_PRIVATE(LIBHW_GENERIC_ALARMCLOCK_H)
+ void *alarmclock;
+ struct alarmclock_trigger *prev, *next;
+
+ uint64_t fire_at_ns;
+ void (*cb)(void *);
+ void *cb_arg;
+ END_PRIVATE(LIBHW_GENERIC_ALARMCLOCK_H)
+};
+
+/* Interface ******************************************************************/
+
+struct alarmclock_vtable;
+
+typedef struct {
+ struct alarmclock_vtable *vtable;
+} implements_alarmclock;
+
+struct alarmclock_vtable {
+ /**
+ * (2⁶⁴-1 nanoseconds is more than 500 years; there is little
+ * risk of this overflowing)
+ */
+ uint64_t (*get_time_ns)(implements_alarmclock *self);
+
+ /**
+ * Returns true on error.
+ *
+ * Implementations may return an error if fire_at_ns is more
+ * than UINT32_MAX µs (72 minutes) in the future.
+ *
+ * If fire_at_ns is in the past, then it will fire
+ * immediately.
+ */
+ bool (*add_trigger)(implements_alarmclock *self, struct alarmclock_trigger *trigger,
+ uint64_t fire_at_ns,
+ void (*cb)(void *),
+ void *cb_arg);
+
+ void (*del_trigger)(implements_alarmclock *self, struct alarmclock_trigger *trigger);
+};
+
+/* Utilities ******************************************************************/
+
+void alarmclock_sleep_until_ns(implements_alarmclock *clock, uint64_t abstime_ns);
+
+static inline void alarmclock_sleep_for_ns(implements_alarmclock *clock, uint64_t delta_ns) {
+ alarmclock_sleep_until_ns(clock, VCALL(clock, get_time_ns) + delta_ns);
+}
+
+static inline void alarmclock_sleep_for_us(implements_alarmclock *clock, uint64_t delta_us) {
+ alarmclock_sleep_for_ns(clock, delta_us * (NS_PER_S/US_PER_S));
+}
+
+static inline void alarmclock_sleep_for_ms(implements_alarmclock *clock, uint64_t delta_ms) {
+ alarmclock_sleep_for_ns(clock, delta_ms * (NS_PER_S/MS_PER_S));
+}
+
+static inline void alarmclock_sleep_for_s(implements_alarmclock *clock, uint64_t delta_s) {
+ alarmclock_sleep_for_ns(clock, delta_s * NS_PER_S);
+}
+
+/* Globals ********************************************************************/
+
+extern implements_alarmclock *bootclock;
+
+#define sleep_until_ns(t) alarmclock_sleep_until_ns(bootclock, t)
+#define sleep_for_ns(t) alarmclock_sleep_for_ns(bootclock, t)
+#define sleep_for_us(t) alarmclock_sleep_for_us(bootclock, t)
+#define sleep_for_ms(t) alarmclock_sleep_for_ms(bootclock, t)
+#define sleep_for_s(t) alarmclock_sleep_for_s(bootclock, t)
+
+#endif /* _LIBHW_GENERIC_ALARMCLOCK_H_ */
diff --git a/libhw_generic/include/libhw/generic/net.h b/libhw_generic/include/libhw/generic/net.h
new file mode 100644
index 0000000..def533c
--- /dev/null
+++ b/libhw_generic/include/libhw/generic/net.h
@@ -0,0 +1,154 @@
+/* libhw/generic/net.h - Device-independent network definitions
+ *
+ * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIBHW_GENERIC_NET_H_
+#define _LIBHW_GENERIC_NET_H_
+
+#include <stdbool.h> /* for bool */
+#include <stddef.h> /* for size_t */
+#include <stdint.h> /* for uint{n}_t} */
+#include <sys/types.h> /* for ssize_t */
+
+/* Errnos *********************************************************************/
+
+#define NET_EOTHER 1
+#define NET_ETIMEDOUT 2
+#define NET_ETHREAD 3
+#define NET_ECLOSED 4
+#define NET_EMSGSIZE 5
+
+/* Address types **************************************************************/
+
+struct net_ip4_addr {
+ unsigned char octets[4];
+};
+
+static const struct net_ip4_addr net_ip4_addr_broadcast = {{255, 255, 255, 255}};
+static const struct net_ip4_addr net_ip4_addr_zero = {{0, 0, 0, 0}};
+
+struct net_eth_addr {
+ unsigned char octets[6];
+};
+
+/* Streams (e.g. TCP) *********************************************************/
+
+struct net_stream_listener_vtable;
+struct net_stream_conn_vtable;
+
+typedef struct {
+ struct net_stream_listener_vtable *vtable;
+} implements_net_stream_listener;
+
+typedef struct {
+ struct net_stream_conn_vtable *vtable;
+} implements_net_stream_conn;
+
+struct net_stream_listener_vtable {
+ /**
+ * It is invalid to accept() a new connection if an existing
+ * connection is still open.
+ */
+ implements_net_stream_conn *(*accept)(implements_net_stream_listener *self);
+
+ /**
+ * The net_stream_conn returned from accept() may still be
+ * valid after the listener is closed.
+ *
+ * Return 0 on success, -errno on error.
+ */
+ int (*close)(implements_net_stream_listener *self);
+};
+
+struct net_stream_conn_vtable {
+ /**
+ * Return bytes-read on success, 0 on EOF, -errno on error; a
+ * short read is *not* an error.
+ */
+ ssize_t (*read)(implements_net_stream_conn *self,
+ 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.
+ *
+ * Writes are *not* guaranteed to be atomic (as this would be
+ * expensive to implement), so if you have concurrent writers then you
+ * should arrange for a mutex to protect the connection.
+ */
+ ssize_t (*write)(implements_net_stream_conn *self,
+ void *buf, size_t count);
+
+ /**
+ * Return 0 on success, -errno on error.
+ */
+ int (*close)(implements_net_stream_conn *self,
+ bool rd, bool wr);
+};
+
+/* Packets (e.g. UDP) *********************************************************/
+
+struct net_packet_conn_vtable;
+
+typedef struct {
+ struct net_packet_conn_vtable *vtable;
+} implements_net_packet_conn;
+
+struct net_packet_conn_vtable {
+ ssize_t (*sendto )(implements_net_packet_conn *self,
+ void *buf, size_t len,
+ struct net_ip4_addr node, uint16_t port);
+ /**
+ * @return The full length of the message, which may be more
+ * than the given `len` (as if the Linux MSG_TRUNC flag were
+ * given).
+ */
+ 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);
+};
+
+/* Interfaces *****************************************************************/
+
+struct net_iface_config {
+ struct net_ip4_addr addr;
+ struct net_ip4_addr gateway_addr;
+ struct net_ip4_addr subnet_mask;
+};
+
+struct net_iface_vtable;
+
+typedef struct {
+ struct net_iface_vtable *vtable;
+} implements_net_iface;
+
+struct net_iface_vtable {
+ struct net_eth_addr (*hwaddr )(implements_net_iface *);
+ void (*ifup )(implements_net_iface *, struct net_iface_config);
+ void (*ifdown )(implements_net_iface *);
+
+ implements_net_stream_listener *(*tcp_listen)(implements_net_iface *, uint16_t local_port);
+ implements_net_stream_conn *(*tcp_dial )(implements_net_iface *, struct net_ip4_addr remote_node, uint16_t remote_port);
+ implements_net_packet_conn *(*udp_conn )(implements_net_iface *, uint16_t local_port);
+};
+
+#endif /* _LIBHW_GENERIC_NET_H_ */
diff --git a/libhw_generic/include/libhw/generic/spi.h b/libhw_generic/include/libhw/generic/spi.h
new file mode 100644
index 0000000..2207a2c
--- /dev/null
+++ b/libhw_generic/include/libhw/generic/spi.h
@@ -0,0 +1,47 @@
+/* libhw/generic/spi.h - Device-independent SPI definitions
+ *
+ * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIBHW_GENERIC_SPI_H_
+#define _LIBHW_GENERIC_SPI_H_
+
+#include <stddef.h> /* for size_t */
+
+enum spi_mode {
+ SPI_MODE_0 = 0, /* clk_polarity=0 (idle low), clk_phase=0 (sample on rise) */
+ SPI_MODE_1 = 1, /* clk_polarity=0 (idle low), clk_phase=1 (sample on fall) */
+ SPI_MODE_2 = 2, /* clk_polarity=1 (idle high), clk_phase=0 (sample on rise) */
+ SPI_MODE_3 = 3, /* clk_polarity=1 (idle high), clk_phase=1 (sample on fall) */
+};
+
+struct bidi_iovec {
+ void *iov_read_dst;
+ void *iov_write_src;
+ size_t iov_len;
+};
+
+struct spi_vtable;
+
+typedef struct {
+ struct spi_vtable *vtable;
+} implements_spi;
+
+/* This API assumes that an SPI frame is a multiple of 8-bits.
+ *
+ * It is my understanding that this is a common constraint of SPI
+ * hardware, and that the RP2040 is somewhat unusual in that it allows
+ * frames of any length 4-16 bits (we disconnect the CS pin from the
+ * PL022 SSP and manually GPIO it from the CPU in order to achieve
+ * longer frames).
+ *
+ * But, more relevantly: The W5500's protocol uses frames that are 4-N
+ * octets; so we have no need for an API that allows a
+ * non-multiple-of-8 number of bits.
+ */
+struct spi_vtable {
+ void (*readwritev)(implements_spi *, const struct bidi_iovec *iov, int iovcnt);
+};
+
+#endif /* _LIBHW_GENERIC_SPI_H_ */