diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-02-23 09:24:31 -0700 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-03-26 12:15:17 -0600 |
commit | 98a06a9452c2a48076487e3b84e877cde83e89b8 (patch) | |
tree | b348e84b9ff94848f6c5ade9981973da926fd46b | |
parent | 467c9e2bdbe1192635c786b4ae2120e2ffc7fb63 (diff) |
libhw/generic/io.h: Add reader/writer interfaces to enforce iovecs everywhere
-rw-r--r-- | lib9p/srv.c | 12 | ||||
-rw-r--r-- | libhw/host_net.c | 97 | ||||
-rw-r--r-- | libhw/rp2040_hwspi.c | 1 | ||||
-rw-r--r-- | libhw/rp2040_include/libhw/rp2040_hwspi.h | 1 | ||||
-rw-r--r-- | libhw/rp2040_include/libhw/w5500.h | 3 | ||||
-rw-r--r-- | libhw/w5500.c | 40 | ||||
-rw-r--r-- | libhw/w5500_ll.h | 141 | ||||
-rw-r--r-- | libhw_generic/include/libhw/generic/io.h | 42 | ||||
-rw-r--r-- | libhw_generic/include/libhw/generic/net.h | 92 | ||||
-rw-r--r-- | libhw_generic/include/libhw/generic/spi.h | 2 |
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_ */ |