summaryrefslogtreecommitdiff
path: root/libhw/w5500_ll.h
diff options
context:
space:
mode:
Diffstat (limited to 'libhw/w5500_ll.h')
-rw-r--r--libhw/w5500_ll.h141
1 files changed, 100 insertions, 41 deletions
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; \