diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-03-02 13:30:05 -0700 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-03-02 13:30:05 -0700 |
commit | 255baf6db201dcdf8da017d55c577622f217a888 (patch) | |
tree | 905a389bc9fdf072cbdf7e9f21b2b936881eb620 /libhw/w5500_ll.h | |
parent | 188ac62a0c0f5519f5d45519fa7d224cb25305c6 (diff) | |
parent | 5a84ba2361cecade0343b88f696b6a63454cc3c6 (diff) |
Merge branch 'lukeshu/iov'
Diffstat (limited to 'libhw/w5500_ll.h')
-rw-r--r-- | libhw/w5500_ll.h | 141 |
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; \ |