summaryrefslogtreecommitdiff
path: root/libhw
diff options
context:
space:
mode:
Diffstat (limited to 'libhw')
-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
6 files changed, 235 insertions, 109 deletions
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; \