diff options
-rw-r--r-- | lib9p/srv.c | 20 | ||||
-rw-r--r-- | libdhcp/dhcp_client.c | 10 | ||||
-rw-r--r-- | libhw/host_net.c | 130 | ||||
-rw-r--r-- | libhw/rp2040_hwspi.c | 5 | ||||
-rw-r--r-- | libhw/rp2040_include/libhw/rp2040_hwspi.h | 1 | ||||
-rw-r--r-- | libhw/rp2040_include/libhw/w5500.h | 10 | ||||
-rw-r--r-- | libhw/w5500.c | 57 | ||||
-rw-r--r-- | libhw/w5500_ll.h | 141 | ||||
-rw-r--r-- | libhw_generic/include/libhw/generic/io.h | 94 | ||||
-rw-r--r-- | libhw_generic/include/libhw/generic/net.h | 112 | ||||
-rw-r--r-- | libhw_generic/include/libhw/generic/spi.h | 10 |
11 files changed, 391 insertions, 199 deletions
diff --git a/lib9p/srv.c b/lib9p/srv.c index c624fa8..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; @@ -293,12 +287,14 @@ static void handle_message(struct _lib9p_srv_req *ctx); _lib9p_srv_reqch_send_req(&srv->_reqch, &req); } close: - LO_CALL(conn.fd, close, true, sess.reqs.len == 0); - if (sess.reqs.len) { + if (sess.reqs.len == 0) + io_close(conn.fd); + else { + io_close_read(conn.fd); sess.closing = true; cr_pause_and_yield(); assert(sess.reqs.len == 0); - LO_CALL(conn.fd, close, true, true); + io_close_write(conn.fd); } } } diff --git a/libdhcp/dhcp_client.c b/libdhcp/dhcp_client.c index e50c5f3..8ec3647 100644 --- a/libdhcp/dhcp_client.c +++ b/libdhcp/dhcp_client.c @@ -775,7 +775,7 @@ static void dhcp_client_setstate(struct dhcp_client *client, dhcp_client_setstate(client, STATE_SELECTING, DHCP_MSGTYP_DISCOVER, NULL, scratch_msg); break; case STATE_SELECTING: - LO_CALL(client->sock, set_read_deadline, client->time_ns_init+CONFIG_DHCP_SELECTING_NS); + LO_CALL(client->sock, set_recv_deadline, client->time_ns_init+CONFIG_DHCP_SELECTING_NS); switch ((r = dhcp_client_recv(client, scratch_msg))) { case 0: switch (scratch_msg->option_dat[scratch_msg->options[DHCP_OPT_DHCP_MSG_TYPE].off]) { @@ -797,7 +797,7 @@ static void dhcp_client_setstate(struct dhcp_client *client, } break; case STATE_REQUESTING: - LO_CALL(client->sock, set_read_deadline, 0); + LO_CALL(client->sock, set_recv_deadline, 0); switch ((r = dhcp_client_recv(client, scratch_msg))) { case 0: switch (scratch_msg->option_dat[scratch_msg->options[DHCP_OPT_DHCP_MSG_TYPE].off]) { @@ -824,7 +824,7 @@ static void dhcp_client_setstate(struct dhcp_client *client, } break; case STATE_BOUND: - LO_CALL(client->sock, set_read_deadline, client->lease_time_ns_t1); + LO_CALL(client->sock, set_recv_deadline, client->lease_time_ns_t1); switch ((r = dhcp_client_recv(client, scratch_msg))) { case 0: /* discard */ @@ -841,7 +841,7 @@ static void dhcp_client_setstate(struct dhcp_client *client, client->xid = rand_uint63n(UINT32_MAX); client->time_ns_init = LO_CALL(bootclock, get_time_ns); - LO_CALL(client->sock, set_read_deadline, client->lease_time_ns_t2); + LO_CALL(client->sock, set_recv_deadline, client->lease_time_ns_t2); switch ((r = dhcp_client_recv(client, scratch_msg))) { case 0: switch (scratch_msg->option_dat[scratch_msg->options[DHCP_OPT_DHCP_MSG_TYPE].off]) { @@ -867,7 +867,7 @@ static void dhcp_client_setstate(struct dhcp_client *client, } break; case STATE_REBINDING: - LO_CALL(client->sock, set_read_deadline, client->lease_time_ns_end); + LO_CALL(client->sock, set_recv_deadline, client->lease_time_ns_end); switch ((r = dhcp_client_recv(client, scratch_msg))) { case 0: switch (scratch_msg->option_dat[scratch_msg->options[DHCP_OPT_DHCP_MSG_TYPE].off]) { diff --git a/libhw/host_net.c b/libhw/host_net.c index eee293e..9d3e97b 100644 --- a/libhw/host_net.c +++ b/libhw/host_net.c @@ -30,8 +30,18 @@ #include "host_util.h" /* for host_sigrt_alloc(), ns_to_host_us_time() */ -LO_IMPLEMENTATION_C(net_stream_conn, struct _hostnet_tcp_conn, hostnet_tcp, static) +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) + + +LO_IMPLEMENTATION_C(io_closer, struct hostnet_udp_conn, hostnet_udp, static) LO_IMPLEMENTATION_C(net_packet_conn, struct hostnet_udp_conn, hostnet_udp, static) /* common *********************************************************************/ @@ -196,27 +206,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; @@ -227,20 +237,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, }; @@ -253,78 +262,93 @@ 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; } /* TCP close() ****************************************************************/ -static int hostnet_tcp_close(struct _hostnet_tcp_conn *conn, bool rd, bool wr) { +static int hostnet_tcp_close(struct _hostnet_tcp_conn *conn) { assert(conn); - - int how; - if (rd && wr) - how = SHUT_RDWR; - else if (rd && !wr) - how = SHUT_RD; - else if (!rd && wr) - how = SHUT_WR; - else - assert_notreached("invalid arguments to stream_conn.close()"); - return hostnet_map_negerrno(shutdown(conn->fd, how) ? -errno : 0, OP_NONE); + return hostnet_map_negerrno(shutdown(conn->fd, SHUT_RDWR) ? -errno : 0, OP_NONE); +} +static int hostnet_tcp_close_read(struct _hostnet_tcp_conn *conn) { + assert(conn); + return hostnet_map_negerrno(shutdown(conn->fd, SHUT_RD) ? -errno : 0, OP_NONE); +} +static int hostnet_tcp_close_write(struct _hostnet_tcp_conn *conn) { + assert(conn); + return hostnet_map_negerrno(shutdown(conn->fd, SHUT_WR) ? -errno : 0, OP_NONE); } /* UDP init() *****************************************************************/ @@ -412,7 +436,7 @@ static ssize_t hostnet_udp_sendto(struct hostnet_udp_conn *conn, void *buf, size /* UDP recvfrom() *************************************************************/ -static void hostnet_udp_set_read_deadline(struct hostnet_udp_conn *conn, +static void hostnet_udp_set_recv_deadline(struct hostnet_udp_conn *conn, uint64_t ts_ns) { assert(conn); diff --git a/libhw/rp2040_hwspi.c b/libhw/rp2040_hwspi.c index 23f3e8c..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, @@ -83,7 +84,7 @@ void _rp2040_hwspi_init(struct rp2040_hwspi *self, self->pin_cs = pin_cs; } -static void rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct bidi_iovec *iov, int iovcnt) { +static void rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct duplex_iovec *iov, int iovcnt) { assert(self); spi_inst_t *inst = self->inst; @@ -101,7 +102,7 @@ static void rp2040_hwspi_readwritev(struct rp2040_hwspi *self, const struct bidi else if (iov[i].iov_read_dst) spi_read_blocking(inst, 0, iov[i].iov_read_dst, iov[i].iov_len); else - assert_notreached("bidi_iovec is neither read nor write"); + assert_notreached("duplex_iovec is neither read nor write"); } gpio_put(self->pin_cs, 1); } 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 98e54ec..51effba 100644 --- a/libhw/rp2040_include/libhw/w5500.h +++ b/libhw/rp2040_include/libhw/w5500.h @@ -40,8 +40,18 @@ struct _w5500_socket { END_PRIVATE(LIBHW_W5500_H) }; + +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) + +LO_IMPLEMENTATION_H(io_closer, struct _w5500_socket, w5500_udp) LO_IMPLEMENTATION_H(net_packet_conn, struct _w5500_socket, w5500_udp) struct w5500 { diff --git a/libhw/w5500.c b/libhw/w5500.c index a8777db..64b373b 100644 --- a/libhw/w5500.c +++ b/libhw/w5500.c @@ -125,9 +125,19 @@ static const char *w5500_state_str(uint8_t state) { /* libobj *********************************************************************/ +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) + +LO_IMPLEMENTATION_C(io_closer, struct _w5500_socket, w5500_udp, static) LO_IMPLEMENTATION_C(net_packet_conn, struct _w5500_socket, w5500_udp, static) + LO_IMPLEMENTATION_C(net_iface, struct w5500, w5500_if, static) /* mid-level utilities ********************************************************/ @@ -586,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; @@ -639,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. */ @@ -675,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; @@ -736,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); @@ -746,7 +764,7 @@ static ssize_t w5500_tcp_read(struct _w5500_socket *socket, void *buf, size_t co return avail; } -static int w5500_tcp_close(struct _w5500_socket *socket, bool rd, bool wr) { +static int w5500_tcp_close_inner(struct _w5500_socket *socket, bool rd, bool wr) { debugf("tcp_conn.close(rd=%s, wr=%s)", rd ? "true" : "false", wr ? "true" : "false"); ASSERT_SELF(stream_conn, TCP); @@ -777,6 +795,10 @@ static int w5500_tcp_close(struct _w5500_socket *socket, bool rd, bool wr) { return 0; } +static int w5500_tcp_close(struct _w5500_socket *socket) { return w5500_tcp_close_inner(socket, true, true); } +static int w5500_tcp_close_read(struct _w5500_socket *socket) { return w5500_tcp_close_inner(socket, true, false); } +static int w5500_tcp_close_write(struct _w5500_socket *socket) { return w5500_tcp_close_inner(socket, false, true); } + /* udp_conn methods ***********************************************************/ static ssize_t w5500_udp_sendto(struct _w5500_socket *socket, void *buf, size_t count, @@ -816,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); @@ -836,8 +861,8 @@ static ssize_t w5500_udp_sendto(struct _w5500_socket *socket, void *buf, size_t } } -static void w5500_udp_set_read_deadline(struct _w5500_socket *socket, uint64_t ns) { - debugf("udp_conn.set_read_deadline(%"PRIu64")", ns); +static void w5500_udp_set_recv_deadline(struct _w5500_socket *socket, uint64_t ns) { + debugf("udp_conn.set_recv_deadline(%"PRIu64")", ns); ASSERT_SELF(packet_conn, UDP); socket->read_deadline_ns = ns; } @@ -891,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]; @@ -905,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 92d9f14..cad20a4 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 bidi_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+(skipped-skip), + .iov_len = iov[i].iov_len-(skipped-skip), + }; + 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 bidi_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 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_ */ |