summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib9p/srv.c20
-rw-r--r--libdhcp/dhcp_client.c10
-rw-r--r--libhw/host_net.c130
-rw-r--r--libhw/rp2040_hwspi.c5
-rw-r--r--libhw/rp2040_include/libhw/rp2040_hwspi.h1
-rw-r--r--libhw/rp2040_include/libhw/w5500.h10
-rw-r--r--libhw/w5500.c57
-rw-r--r--libhw/w5500_ll.h141
-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
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_ */