summaryrefslogtreecommitdiff
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-26 12:15:17 -0600
commit98a06a9452c2a48076487e3b84e877cde83e89b8 (patch)
treeb348e84b9ff94848f6c5ade9981973da926fd46b
parent467c9e2bdbe1192635c786b4ae2120e2ffc7fb63 (diff)
libhw/generic/io.h: Add reader/writer interfaces to enforce iovecs everywhere
-rw-r--r--lib9p/srv.c12
-rw-r--r--libhw/host_net.c97
-rw-r--r--libhw/rp2040_hwspi.c1
-rw-r--r--libhw/rp2040_include/libhw/rp2040_hwspi.h1
-rw-r--r--libhw/rp2040_include/libhw/w5500.h3
-rw-r--r--libhw/w5500.c40
-rw-r--r--libhw/w5500_ll.h141
-rw-r--r--libhw_generic/include/libhw/generic/io.h42
-rw-r--r--libhw_generic/include/libhw/generic/net.h92
-rw-r--r--libhw_generic/include/libhw/generic/spi.h2
10 files changed, 283 insertions, 148 deletions
diff --git a/lib9p/srv.c b/lib9p/srv.c
index 580c5f5..932cd4d 100644
--- a/lib9p/srv.c
+++ b/lib9p/srv.c
@@ -141,15 +141,9 @@ struct _lib9p_srv_req {
#define nonrespond_errorf errorf
static ssize_t write_Rmsg(struct _lib9p_srv_req *req, struct lib9p_Rmsg_send_buf *resp) {
- ssize_t r = 0, _r;
+ ssize_t r;
cr_mutex_lock(&req->parent_sess->parent_conn->writelock);
- for (size_t i = 0; i < resp->iov_cnt; i++) {
- _r = LO_CALL(req->parent_sess->parent_conn->fd, write,
- resp->iov[i].iov_base, resp->iov[i].iov_len);
- if (_r < 0)
- return _r;
- r += _r;
- }
+ r = io_writev(req->parent_sess->parent_conn->fd, resp->iov, resp->iov_cnt);
cr_mutex_unlock(&req->parent_sess->parent_conn->writelock);
return r;
}
@@ -195,7 +189,7 @@ static bool read_exactly(lo_interface net_stream_conn fd, uint8_t *buf, size_t g
assert(goal);
assert(done);
while (*done < goal) {
- ssize_t r = LO_CALL(fd, read, &buf[*done], goal - *done);
+ ssize_t r = io_read(fd, &buf[*done], goal - *done);
if (r < 0) {
nonrespond_errorf("read: %s", net_strerror(-r));
return true;
diff --git a/libhw/host_net.c b/libhw/host_net.c
index f977739..e89cd66 100644
--- a/libhw/host_net.c
+++ b/libhw/host_net.c
@@ -33,6 +33,9 @@
LO_IMPLEMENTATION_C(io_closer, struct hostnet_tcp_listener, hostnet_tcplist, static)
LO_IMPLEMENTATION_C(net_stream_listener, struct hostnet_tcp_listener, hostnet_tcplist, static)
+LO_IMPLEMENTATION_C(io_reader, struct _hostnet_tcp_conn, hostnet_tcp, static)
+LO_IMPLEMENTATION_C(io_writer, struct _hostnet_tcp_conn, hostnet_tcp, static)
+LO_IMPLEMENTATION_C(io_readwriter, struct _hostnet_tcp_conn, hostnet_tcp, static)
LO_IMPLEMENTATION_C(io_closer, struct _hostnet_tcp_conn, hostnet_tcp, static)
LO_IMPLEMENTATION_C(io_bidi_closer, struct _hostnet_tcp_conn, hostnet_tcp, static)
LO_IMPLEMENTATION_C(net_stream_conn, struct _hostnet_tcp_conn, hostnet_tcp, static)
@@ -202,27 +205,27 @@ static void hostnet_tcp_set_read_deadline(struct _hostnet_tcp_conn *conn, uint64
conn->read_deadline_ns = ts_ns;
}
-struct hostnet_pthread_read_args {
- pthread_t cr_thread;
- cid_t cr_coroutine;
+struct hostnet_pthread_readv_args {
+ pthread_t cr_thread;
+ cid_t cr_coroutine;
- int connfd;
- struct timeval timeout;
- void *buf;
- size_t count;
+ int connfd;
+ struct timeval timeout;
+ const struct iovec *iov;
+ int iovcnt;
- ssize_t *ret;
+ ssize_t *ret;
};
-static void *hostnet_pthread_read(void *_args) {
- struct hostnet_pthread_read_args *args = _args;
+static void *hostnet_pthread_readv(void *_args) {
+ struct hostnet_pthread_readv_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);
+ *(args->ret) = readv(args->connfd, args->iov, args->iovcnt);
if (*(args->ret) < 0)
goto end;
@@ -233,20 +236,19 @@ static void *hostnet_pthread_read(void *_args) {
return NULL;
}
-static ssize_t hostnet_tcp_read(struct _hostnet_tcp_conn *conn, void *buf, size_t count) {
+static ssize_t hostnet_tcp_readv(struct _hostnet_tcp_conn *conn, const struct iovec *iov, int iovcnt) {
assert(conn);
- assert(count == 0 || buf);
- if (count == 0)
- return 0;
+ assert(iov);
+ assert(iovcnt > 0);
ssize_t ret;
- struct hostnet_pthread_read_args args = {
+ struct hostnet_pthread_readv_args args = {
.cr_thread = pthread_self(),
.cr_coroutine = cr_getcid(),
.connfd = conn->fd,
- .buf = buf,
- .count = count,
+ .iov = iov,
+ .iovcnt = iovcnt,
.ret = &ret,
};
@@ -259,59 +261,76 @@ static ssize_t hostnet_tcp_read(struct _hostnet_tcp_conn *conn, void *buf, size_
args.timeout = (host_us_time_t){0};
}
- if (RUN_PTHREAD(hostnet_pthread_read, &args))
+ if (RUN_PTHREAD(hostnet_pthread_readv, &args))
return -NET_ETHREAD;
return ret;
}
/* TCP write() ****************************************************************/
-struct hostnet_pthread_write_args {
- pthread_t cr_thread;
- cid_t cr_coroutine;
+struct hostnet_pthread_writev_args {
+ pthread_t cr_thread;
+ cid_t cr_coroutine;
- int connfd;
- void *buf;
- size_t count;
+ int connfd;
+ const struct iovec *iov;
+ int iovcnt;
- ssize_t *ret;
+ ssize_t *ret;
};
-static void *hostnet_pthread_write(void *_args) {
- struct hostnet_pthread_write_args *args = _args;
+static void *hostnet_pthread_writev(void *_args) {
+ struct hostnet_pthread_writev_args *args = _args;
+
+ size_t count = 0;
+ struct iovec *iov = alloca(sizeof(struct iovec)*args->iovcnt);
+ for (int i = 0; i < args->iovcnt; i++) {
+ iov[i] = args->iov[i];
+ count += args->iov[i].iov_len;
+ }
+ int iovcnt = args->iovcnt;
+
size_t done = 0;
- while (done < args->count) {
- ssize_t r = write(args->connfd, args->buf, args->count);
+ while (done < count) {
+ ssize_t r = writev(args->connfd, iov, iovcnt);
if (r < 0) {
hostnet_map_negerrno(-errno, OP_RECV);
break;
}
done += r;
+ while (iovcnt && (size_t)r >= iov[0].iov_len) {
+ r -= iov[0].iov_len;
+ iov++;
+ iovcnt--;
+ }
+ if (r > 0) {
+ iov[0].iov_base += r;
+ iov[0].iov_len -= r;
+ }
}
- if (done == args->count)
+ if (done == count)
*(args->ret) = done;
WAKE_COROUTINE(args);
return NULL;
}
-static ssize_t hostnet_tcp_write(struct _hostnet_tcp_conn *conn, void *buf, size_t count) {
+static ssize_t hostnet_tcp_writev(struct _hostnet_tcp_conn *conn, const struct iovec *iov, int iovcnt) {
assert(conn);
- assert(count == 0 || buf);
- if (count == 0)
- return 0;
+ assert(iov);
+ assert(iovcnt > 0);
ssize_t ret;
- struct hostnet_pthread_write_args args = {
+ struct hostnet_pthread_writev_args args = {
.cr_thread = pthread_self(),
.cr_coroutine = cr_getcid(),
.connfd = conn->fd,
- .buf = buf,
- .count = count,
+ .iov = iov,
+ .iovcnt = iovcnt,
.ret = &ret,
};
- if (RUN_PTHREAD(hostnet_pthread_write, &args))
+ if (RUN_PTHREAD(hostnet_pthread_writev, &args))
return -NET_ETHREAD;
return ret;
}
diff --git a/libhw/rp2040_hwspi.c b/libhw/rp2040_hwspi.c
index c60d6b9..8c87666 100644
--- a/libhw/rp2040_hwspi.c
+++ b/libhw/rp2040_hwspi.c
@@ -12,6 +12,7 @@
#define IMPLEMENTATION_FOR_LIBHW_RP2040_HWSPI_H YES
#include <libhw/rp2040_hwspi.h>
+LO_IMPLEMENTATION_C(io_duplex_readwriter, struct rp2040_hwspi, rp2040_hwspi, static)
LO_IMPLEMENTATION_C(spi, struct rp2040_hwspi, rp2040_hwspi, static)
void _rp2040_hwspi_init(struct rp2040_hwspi *self,
diff --git a/libhw/rp2040_include/libhw/rp2040_hwspi.h b/libhw/rp2040_include/libhw/rp2040_hwspi.h
index 53a7b03..76e0709 100644
--- a/libhw/rp2040_include/libhw/rp2040_hwspi.h
+++ b/libhw/rp2040_include/libhw/rp2040_hwspi.h
@@ -25,6 +25,7 @@ struct rp2040_hwspi {
uint pin_cs;
END_PRIVATE(LIBHW_RP2040_HWSPI_H)
};
+LO_IMPLEMENTATION_H(io_duplex_readwriter, struct rp2040_hwspi, rp2040_hwspi)
LO_IMPLEMENTATION_H(spi, struct rp2040_hwspi, rp2040_hwspi)
/**
diff --git a/libhw/rp2040_include/libhw/w5500.h b/libhw/rp2040_include/libhw/w5500.h
index a035069..51effba 100644
--- a/libhw/rp2040_include/libhw/w5500.h
+++ b/libhw/rp2040_include/libhw/w5500.h
@@ -44,6 +44,9 @@ struct _w5500_socket {
LO_IMPLEMENTATION_H(io_closer, struct _w5500_socket, w5500_tcplist)
LO_IMPLEMENTATION_H(net_stream_listener, struct _w5500_socket, w5500_tcplist)
+LO_IMPLEMENTATION_H(io_reader, struct _w5500_socket, w5500_tcp)
+LO_IMPLEMENTATION_H(io_writer, struct _w5500_socket, w5500_tcp)
+LO_IMPLEMENTATION_H(io_readwriter, struct _w5500_socket, w5500_tcp)
LO_IMPLEMENTATION_H(io_closer, struct _w5500_socket, w5500_tcp)
LO_IMPLEMENTATION_H(io_bidi_closer, struct _w5500_socket, w5500_tcp)
LO_IMPLEMENTATION_H(net_stream_conn, struct _w5500_socket, w5500_tcp)
diff --git a/libhw/w5500.c b/libhw/w5500.c
index cd96696..64b373b 100644
--- a/libhw/w5500.c
+++ b/libhw/w5500.c
@@ -128,6 +128,9 @@ static const char *w5500_state_str(uint8_t state) {
LO_IMPLEMENTATION_C(io_closer, struct _w5500_socket, w5500_tcplist, static)
LO_IMPLEMENTATION_C(net_stream_listener, struct _w5500_socket, w5500_tcplist, static)
+LO_IMPLEMENTATION_C(io_reader, struct _w5500_socket, w5500_tcp, static)
+LO_IMPLEMENTATION_C(io_writer, struct _w5500_socket, w5500_tcp, static)
+LO_IMPLEMENTATION_C(io_readwriter, struct _w5500_socket, w5500_tcp, static)
LO_IMPLEMENTATION_C(io_closer, struct _w5500_socket, w5500_tcp, static)
LO_IMPLEMENTATION_C(io_bidi_closer, struct _w5500_socket, w5500_tcp, static)
LO_IMPLEMENTATION_C(net_stream_conn, struct _w5500_socket, w5500_tcp, static)
@@ -593,10 +596,14 @@ static int w5500_tcplist_close(struct _w5500_socket *socket) {
/* tcp_conn methods ***********************************************************/
-static ssize_t w5500_tcp_write(struct _w5500_socket *socket, void *buf, size_t count) {
+static ssize_t w5500_tcp_writev(struct _w5500_socket *socket, const struct iovec *iov, int iovcnt) {
+ assert(iov);
+ assert(iovcnt > 0);
+ size_t count = 0;
+ for (int i = 0; i < iovcnt; i++)
+ count += iov[i].iov_len;
debugf("tcp_conn.write(%zu)", count);
ASSERT_SELF(stream_conn, TCP);
- assert(count == 0 || buf);
if (count == 0)
return 0;
@@ -646,7 +653,7 @@ static ssize_t w5500_tcp_write(struct _w5500_socket *socket, void *buf, size_t c
if ((size_t)freesize > count-done)
freesize = count-done;
uint16_t ptr = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, tx_write_pointer));
- w5500ll_write(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, TX), &((char *)buf)[done], freesize);
+ w5500ll_writev(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, TX), iov, iovcnt, done, freesize);
w5500ll_write_sock_reg(chip->spidev, socknum, tx_write_pointer, uint16be_marshal(ptr+freesize));
/* Submit the queue. */
@@ -682,10 +689,14 @@ static void w5500_tcp_alarm_handler(void *_arg) {
cr_sema_signal_from_intrhandler(&socket->read_sema);
}
-static ssize_t w5500_tcp_read(struct _w5500_socket *socket, void *buf, size_t count) {
- debugf("tcp_conn.read()");
+static ssize_t w5500_tcp_readv(struct _w5500_socket *socket, const struct iovec *iov, int iovcnt) {
+ assert(iov);
+ assert(iovcnt > 0);
+ size_t count = 0;
+ for (int i = 0; i < iovcnt; i++)
+ count += iov[i].iov_len;
+ debugf("tcp_conn.read(%zu)", count);
ASSERT_SELF(stream_conn, TCP);
- assert(count == 0 || buf);
if (count == 0)
return 0;
@@ -743,7 +754,7 @@ static ssize_t w5500_tcp_read(struct _w5500_socket *socket, void *buf, size_t co
/* Read the data. */
if ((size_t)avail > count)
avail = count;
- w5500ll_read(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, RX), buf, avail);
+ w5500ll_readv(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, RX), iov, iovcnt, avail);
/* Tell the chip that we read the data. */
w5500ll_write_sock_reg(chip->spidev, socknum, rx_read_pointer, uint16be_marshal(ptr+avail));
w5500_socket_cmd(socket, CMD_RECV);
@@ -827,7 +838,10 @@ static ssize_t w5500_udp_sendto(struct _w5500_socket *socket, void *buf, size_t
w5500ll_write_sock_reg(chip->spidev, socknum, remote_port, uint16be_marshal(port));
/* Queue data to be sent. */
uint16_t ptr = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, tx_write_pointer));
- w5500ll_write(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, TX), buf, count);
+ w5500ll_writev(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, TX), &((struct iovec){
+ .iov_base = buf,
+ .iov_len = count,
+ }), 1, 0, 0);
w5500ll_write_sock_reg(chip->spidev, socknum, tx_write_pointer, uint16be_marshal(ptr+count));
/* Submit the queue. */
w5500_socket_cmd(socket, CMD_SEND);
@@ -902,7 +916,10 @@ static ssize_t w5500_udp_recvfrom(struct _w5500_socket *socket, void *buf, size_
* can't find in the datasheet where it describes
* this; this is based off of socket.c:recvfrom(). */
uint8_t hdr[8];
- w5500ll_read(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, RX), hdr, sizeof(hdr));
+ w5500ll_readv(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, RX), &((struct iovec){
+ .iov_base = hdr,
+ .iov_len = sizeof(hdr),
+ }), 1, 0);
if (ret_node) {
ret_node->octets[0] = hdr[0];
ret_node->octets[1] = hdr[1];
@@ -916,7 +933,10 @@ static ssize_t w5500_udp_recvfrom(struct _w5500_socket *socket, void *buf, size_
/* Now read the actual data. */
if (count > len)
count = len;
- w5500ll_read(chip->spidev, ptr+8, CTL_BLOCK_SOCK(socknum, RX), buf, len);
+ w5500ll_readv(chip->spidev, ptr+8, CTL_BLOCK_SOCK(socknum, RX), &((struct iovec){
+ .iov_base = buf,
+ .iov_len = len,
+ }), 1, 0);
/* Tell the chip that we read the data. */
w5500ll_write_sock_reg(chip->spidev, socknum, rx_read_pointer, uint16be_marshal(ptr+8+len));
w5500_socket_cmd(socket, CMD_RECV);
diff --git a/libhw/w5500_ll.h b/libhw/w5500_ll.h
index 2b89353..3822ad4 100644
--- a/libhw/w5500_ll.h
+++ b/libhw/w5500_ll.h
@@ -10,6 +10,7 @@
#ifndef _LIBHW_W5500_LL_H_
#define _LIBHW_W5500_LL_H_
+#include <alloca.h> /* for alloca() */
#include <stdint.h> /* for uint{n}_t */
#include <string.h> /* for memcmp() */
@@ -68,62 +69,111 @@ static char *_ctl_block_part_strs[] = {
static inline void
#if CONFIG_W5500_LL_DEBUG
-#define w5500ll_write(...) _w5500ll_write(__func__, __VA_ARGS__)
-_w5500ll_write(const char *func,
+#define w5500ll_writev(...) _w5500ll_writev(__func__, __VA_ARGS__)
+_w5500ll_writev(const char *func,
#else
-w5500ll_write(
+w5500ll_writev(
#endif
- lo_interface spi spidev, uint16_t addr, uint8_t block, void *data, size_t data_len) {
+ lo_interface spi spidev, uint16_t addr, uint8_t block,
+ const struct iovec *iov, int iovcnt,
+ size_t skip, size_t max)
+{
assert(!LO_IS_NULL(spidev));
assert((block & ~CTL_MASK_BLOCK) == 0);
- assert(data);
- assert(data_len);
+ assert(iov);
+ assert(iovcnt > 0);
#if CONFIG_W5500_LL_DEBUG
n_debugf(W5500_LL,
- "%s(): w5500ll_write(spidev, addr=%#04x, block="PRI_ctl_block", data, data_len=%zu)",
- func, addr, ARG_ctl_block(block), data_len);
+ "%s(): w5500ll_write(spidev, addr=%#04x, block="PRI_ctl_block", iov, iovcnt=%d)",
+ func, addr, ARG_ctl_block(block), iovcnt);
#endif
- uint8_t header[3] = {
+ uint8_t header[] = {
(uint8_t)((addr >> 8) & 0xFF),
(uint8_t)(addr & 0xFF),
(block & CTL_MASK_BLOCK) | CTL_W | CTL_OM_VDM,
};
- struct duplex_iovec iov[] = {
- {.iov_read_dst = NULL, .iov_write_src = header, .iov_len = sizeof(header)},
- {.iov_read_dst = NULL, .iov_write_src = data, .iov_len = data_len},
+ struct duplex_iovec *inner = alloca(sizeof(struct duplex_iovec)*(iovcnt+1));
+ inner[0] = (struct duplex_iovec){
+ .iov_read_dst = NULL,
+ .iov_write_src = header,
+ .iov_len = sizeof(header),
};
- LO_CALL(spidev, readwritev, iov, 2);
+ int j = 1;
+ size_t skipped = 0, done = 0;
+ for (int i = 0; i < iovcnt && (max == 0 || done < max); i++) {
+ if (skipped < skip) {
+ if (skip - skipped >= iov[i].iov_len) {
+ skipped += iov[i].iov_len;
+ continue;
+ }
+ inner[j] = (struct duplex_iovec){
+ .iov_read_dst = NULL,
+ .iov_write_src = iov[i].iov_base+(skip-skipped),
+ .iov_len = iov[i].iov_len-(skip-skipped),
+ };
+ skipped = skip;
+ } else {
+ inner[j] = (struct duplex_iovec){
+ .iov_read_dst = NULL,
+ .iov_write_src = iov[i].iov_base,
+ .iov_len = iov[i].iov_len,
+ };
+ }
+ done += inner[j].iov_len;
+ if (max > 0 && done > max)
+ inner[j].iov_len -= done - max;
+ j++;
+ };
+ LO_CALL(spidev, readwritev, inner, j);
}
static inline void
#if CONFIG_W5500_LL_DEBUG
-#define w5500ll_read(...) _w5500ll_read(__func__, __VA_ARGS__)
-_w5500ll_read(const char *func,
+#define w5500ll_readv(...) _w5500ll_read(__func__, __VA_ARGS__)
+_w5500ll_readv(const char *func,
#else
-w5500ll_read(
+w5500ll_readv(
#endif
- lo_interface spi spidev, uint16_t addr, uint8_t block, void *data, size_t data_len) {
+ lo_interface spi spidev, uint16_t addr, uint8_t block,
+ const struct iovec *iov, int iovcnt,
+ size_t max)
+{
assert(!LO_IS_NULL(spidev));
assert((block & ~CTL_MASK_BLOCK) == 0);
- assert(data);
- assert(data_len);
+ assert(iov);
+ assert(iovcnt > 0);
#if CONFIG_W5500_LL_DEBUG
n_debugf(W5500_LL,
- "%s(): w5500ll_read(spidev, addr=%#04x, block="PRI_ctl_block", data, data_len=%zu)",
- func, addr, ARG_ctl_block(block), data_len);
+ "%s(): w5500ll_read(spidev, addr=%#04x, block="PRI_ctl_block", iov, iovcnt=%d)",
+ func, addr, ARG_ctl_block(block), iovcnt);
#endif
- uint8_t header[3] = {
+ uint8_t header[] = {
(uint8_t)((addr >> 8) & 0xFF),
(uint8_t)(addr & 0xFF),
(block & CTL_MASK_BLOCK) | CTL_R | CTL_OM_VDM,
};
- struct duplex_iovec iov[] = {
- {.iov_read_dst = NULL, .iov_write_src = header, .iov_len = sizeof(header)},
- {.iov_read_dst = data, .iov_write_src = NULL, .iov_len = data_len},
+ struct duplex_iovec *inner = alloca(sizeof(struct duplex_iovec)*(iovcnt+1));
+ inner[0] = (struct duplex_iovec){
+ .iov_read_dst = NULL,
+ .iov_write_src = header,
+ .iov_len = sizeof(header),
+ };
+ int j = 1;
+ size_t done = 0;
+ for (int i = 0; i < iovcnt && (max == 0 || done < max); i++) {
+ inner[j] = (struct duplex_iovec){
+ .iov_read_dst = iov[i].iov_base,
+ .iov_write_src = NULL,
+ .iov_len = iov[i].iov_len,
+ };
+ done += inner[j].iov_len;
+ if (max > 0 && done > max)
+ inner[j].iov_len -= done - max;
+ j++;
};
- LO_CALL(spidev, readwritev, iov, 2);
+ LO_CALL(spidev, readwritev, inner, j);
}
/* Common chip-wide registers. ***********************************************/
@@ -367,11 +417,14 @@ static_assert(sizeof(struct w5500ll_block_sock_reg) == 0x30);
#define w5500ll_write_reg(spidev, blockid, blocktyp, field, val) do { \
typeof((blocktyp){}.field) lval = val; \
- w5500ll_write(spidev, \
- offsetof(blocktyp, field), \
- blockid, \
- &lval, \
- sizeof(lval)); \
+ w5500ll_writev(spidev, \
+ offsetof(blocktyp, field), \
+ blockid, \
+ &((struct iovec){ \
+ &lval, \
+ sizeof(lval), \
+ }), \
+ 1, 0, 0); \
} while (0)
/* The datasheet tells us that multi-byte reads are non-atomic and
@@ -379,19 +432,25 @@ static_assert(sizeof(struct w5500ll_block_sock_reg) == 0x30);
* until getting the same value". */
#define w5500ll_read_reg(spidev, blockid, blocktyp, field) ({ \
typeof((blocktyp){}.field) val; \
- w5500ll_read(spidev, \
- offsetof(blocktyp, field), \
- blockid, \
- &val, \
- sizeof(val)); \
+ w5500ll_readv(spidev, \
+ offsetof(blocktyp, field), \
+ blockid, \
+ &((struct iovec){ \
+ .iov_base = &val, \
+ .iov_len = sizeof(val), \
+ }), \
+ 1, 0); \
if (sizeof(val) > 1) \
for (;;) { \
typeof(val) val2; \
- w5500ll_read(spidev, \
- offsetof(blocktyp, field), \
- blockid, \
- &val2, \
- sizeof(val)); \
+ w5500ll_readv(spidev, \
+ offsetof(blocktyp, field), \
+ blockid, \
+ &((struct iovec){ \
+ .iov_base = &val2, \
+ .iov_len = sizeof(val), \
+ }), \
+ 1, 0); \
if (memcmp(&val2, &val, sizeof(val)) == 0) \
break; \
val = val2; \
diff --git a/libhw_generic/include/libhw/generic/io.h b/libhw_generic/include/libhw/generic/io.h
index 9128bd3..a7f7378 100644
--- a/libhw_generic/include/libhw/generic/io.h
+++ b/libhw_generic/include/libhw/generic/io.h
@@ -8,6 +8,7 @@
#define _LIBHW_GENERIC_IO_H_
#include <stddef.h> /* for size_t */
+#include <sys/types.h> /* for ssize_t */
#include <libobj/obj.h>
@@ -31,6 +32,29 @@ struct duplex_iovec {
/* 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 \
@@ -49,4 +73,22 @@ 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 d70034e..e88d705 100644
--- a/libhw_generic/include/libhw/generic/net.h
+++ b/libhw_generic/include/libhw/generic/net.h
@@ -72,58 +72,54 @@ lo_interface net_stream_conn;
LO_NEST(io_closer)
LO_INTERFACE(net_stream_listener)
-#define net_stream_conn_LO_IFACE \
- LO_NEST(io_bidi_closer) \
- \
- /** \
- * 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)
+#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_recv_deadline, \
- uint64_t ns_since_boot) \
- \
+#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)
diff --git a/libhw_generic/include/libhw/generic/spi.h b/libhw_generic/include/libhw/generic/spi.h
index a4b0b22..a4dbcae 100644
--- a/libhw_generic/include/libhw/generic/spi.h
+++ b/libhw_generic/include/libhw/generic/spi.h
@@ -31,7 +31,7 @@ enum spi_mode {
* non-multiple-of-8 number of bits.
*/
#define spi_LO_IFACE \
- LO_FUNC(void, readwritev, const struct duplex_iovec *iov, int iovcnt)
+ LO_NEST(io_duplex_readwriter)
LO_INTERFACE(spi)
#endif /* _LIBHW_GENERIC_SPI_H_ */