summaryrefslogtreecommitdiff
path: root/libhw_generic
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2025-02-23 09:24:31 -0700
committerLuke T. Shumaker <lukeshu@lukeshu.com>2025-03-01 17:28:46 -0700
commit5a84ba2361cecade0343b88f696b6a63454cc3c6 (patch)
tree905a389bc9fdf072cbdf7e9f21b2b936881eb620 /libhw_generic
parent188ac62a0c0f5519f5d45519fa7d224cb25305c6 (diff)
Pull out libhw/generic/io.h, have it enforce iovecs everywhere
Diffstat (limited to 'libhw_generic')
-rw-r--r--libhw_generic/include/libhw/generic/io.h94
-rw-r--r--libhw_generic/include/libhw/generic/net.h112
-rw-r--r--libhw_generic/include/libhw/generic/spi.h10
3 files changed, 143 insertions, 73 deletions
diff --git a/libhw_generic/include/libhw/generic/io.h b/libhw_generic/include/libhw/generic/io.h
new file mode 100644
index 0000000..a7f7378
--- /dev/null
+++ b/libhw_generic/include/libhw/generic/io.h
@@ -0,0 +1,94 @@
+/* libhw/generic/io.h - Device-independent I/O definitions
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIBHW_GENERIC_IO_H_
+#define _LIBHW_GENERIC_IO_H_
+
+#include <stddef.h> /* for size_t */
+#include <sys/types.h> /* for ssize_t */
+
+#include <libobj/obj.h>
+
+/* structs ********************************************************************/
+
+#if __unix__
+#include <sys/uio.h>
+#else
+struct iovec {
+ void *iov_base;
+ size_t iov_len;
+};
+#endif
+
+struct duplex_iovec {
+ void *iov_read_dst;
+ void *iov_write_src;
+ size_t iov_len;
+};
+
+/* basic interfaces ***********************************************************/
+
+/**
+ * Return bytes-read on success, 0 on EOF, -errno on error; a short
+ * read is *not* an error.
+ */
+#define io_reader_LO_IFACE \
+ LO_FUNC(ssize_t, readv, const struct iovec *iov, int iovcnt)
+LO_INTERFACE(io_reader)
+#define io_readv(r, iov, iovcnt) LO_CALL(r, readv, iov, iovcnt)
+#define io_read(r, buf, count) LO_CALL(r, readv, &((struct iovec){.iov_base = buf, .iov_len = count}), 1)
+
+/**
+ * Return `count` on success, -errno on error; a short write *is* an
+ * error.
+ *
+ * Writes are *not* guaranteed to be atomic, so if you have concurrent
+ * writers then you should arrange for a mutex to protect the writer.
+ */
+#define io_writer_LO_IFACE \
+ LO_FUNC(ssize_t, writev, const struct iovec *iov, int iovcnt)
+LO_INTERFACE(io_writer)
+#define io_writev(w, iov, iovcnt) LO_CALL(w, writev, iov, iovcnt)
+#define io_write(w, buf, count) LO_CALL(w, writev, &((struct iovec){.iov_base = buf, .iov_len = count}), 1)
+
+/**
+ * Return 0 on success, -errno on error.
+ */
+#define io_closer_LO_IFACE \
+ LO_FUNC(int, close)
+LO_INTERFACE(io_closer)
+#define io_close(c) LO_CALL(c, close)
+
+/**
+ * All methods return 0 on success, -errno on error.
+ */
+#define io_bidi_closer_LO_IFACE \
+ LO_NEST(io_closer) \
+ LO_FUNC(int, close_read) \
+ LO_FUNC(int, close_write)
+LO_INTERFACE(io_bidi_closer)
+#define io_close_read(c) LO_CALL(c, close_read)
+#define io_close_write(c) LO_CALL(c, close_write)
+
+/**
+ * Return bytes-read and bytes-written on success, -errno on error; a
+ * short read/write *is* an error.
+ */
+#define io_duplex_readwriter_LO_IFACE \
+ LO_FUNC(void, readwritev, const struct duplex_iovec *iov, int iovcnt)
+LO_INTERFACE(io_duplex_readwriter)
+
+#define io_readwritev(rw, iov, iovcnt) \
+ LO_CALL(rw, readwritev, iov, iovcnt)
+
+/* aggregate interfaces *******************************************************/
+
+#define io_readwriter_LO_IFACE \
+ LO_NEST(io_reader) \
+ LO_NEST(io_writer)
+LO_INTERFACE(io_readwriter)
+
+#endif /* _LIBHW_GENERIC_IO_H_ */
diff --git a/libhw_generic/include/libhw/generic/net.h b/libhw_generic/include/libhw/generic/net.h
index 8d81573..2703b26 100644
--- a/libhw_generic/include/libhw/generic/net.h
+++ b/libhw_generic/include/libhw/generic/net.h
@@ -13,16 +13,7 @@
#include <stdint.h> /* for uint{n}_t} */
#include <sys/types.h> /* for ssize_t */
-#if __unix__
-#include <sys/uio.h>
-#else
-struct iovec {
- void *iov_base;
- size_t iov_len;
-};
-#endif
-
-#include <libobj/obj.h>
+#include <libhw/generic/io.h>
/* Errnos *********************************************************************/
@@ -77,68 +68,59 @@ lo_interface net_stream_conn;
/** \
* The net_stream_conn returned from accept() may still be \
* valid after the listener is closed. \
- * \
- * Return 0 on success, -errno on error. \
*/ \
- LO_FUNC(int, close)
+ LO_NEST(io_closer)
LO_INTERFACE(net_stream_listener)
-#define net_stream_conn_LO_IFACE \
- /** \
- * Return bytes-read on success, 0 on EOF, -errno on error; a \
- * short read is *not* an error. \
- */ \
- LO_FUNC(ssize_t, read, 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) \
- */ \
- LO_FUNC(void, set_read_deadline, 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. \
- */ \
- LO_FUNC(ssize_t, write, void *buf, size_t count) \
- \
- /** \
- * Return 0 on success, -errno on error. \
- */ \
- LO_FUNC(int, close, bool rd, bool wr)
+#define net_stream_conn_LO_IFACE \
+ LO_NEST(io_readwriter) \
+ LO_NEST(io_bidi_closer) \
+ \
+ /** \
+ * 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) \
+ */ \
+ LO_FUNC(void, set_read_deadline, uint64_t ns_since_boot)
LO_INTERFACE(net_stream_conn)
/* Packets (e.g. UDP) *********************************************************/
-#define net_packet_conn_LO_IFACE \
- LO_FUNC(ssize_t, sendto, \
- 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). \
- */ \
- LO_FUNC(ssize_t, recvfrom, \
- void *buf, size_t len, \
- struct net_ip4_addr *ret_node, uint16_t *ret_port) \
- \
- LO_FUNC(void, set_read_deadline, \
- uint64_t ns_since_boot) \
- \
- LO_FUNC(int, close)
+#define net_packet_conn_LO_IFACE \
+ LO_FUNC(ssize_t, sendto, \
+ 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). \
+ */ \
+ LO_FUNC(ssize_t, recvfrom, \
+ void *buf, size_t len, \
+ struct net_ip4_addr *ret_node, uint16_t *ret_port) \
+ \
+ /** \
+ * Set a timestamp after which calls to recvfrom() 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) \
+ */ \
+ LO_FUNC(void, set_recv_deadline, \
+ uint64_t ns_since_boot) \
+ \
+ LO_NEST(io_closer)
LO_INTERFACE(net_packet_conn)
/* Interfaces *****************************************************************/
diff --git a/libhw_generic/include/libhw/generic/spi.h b/libhw_generic/include/libhw/generic/spi.h
index aeeca37..a4dbcae 100644
--- a/libhw_generic/include/libhw/generic/spi.h
+++ b/libhw_generic/include/libhw/generic/spi.h
@@ -9,7 +9,7 @@
#include <stddef.h> /* for size_t */
-#include <libobj/obj.h>
+#include <libhw/generic/io.h>
enum spi_mode {
SPI_MODE_0 = 0, /* clk_polarity=0 (idle low), clk_phase=0 (sample on rise) */
@@ -18,12 +18,6 @@ enum spi_mode {
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;
-};
-
/* 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
@@ -37,7 +31,7 @@ struct bidi_iovec {
* non-multiple-of-8 number of bits.
*/
#define spi_LO_IFACE \
- LO_FUNC(void, readwritev, const struct bidi_iovec *iov, int iovcnt)
+ LO_NEST(io_duplex_readwriter)
LO_INTERFACE(spi)
#endif /* _LIBHW_GENERIC_SPI_H_ */