summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2025-05-26 18:16:57 -0400
committerLuke T. Shumaker <lukeshu@lukeshu.com>2025-05-26 18:16:57 -0400
commitc50edb6000c9ac10f0cc3d5d9f43f82ec2041e56 (patch)
tree29e75a14a6d90a1a480327d744e6a16fe8b8e8d0
parentcf4af09e9a20e9cdaec4b3896eb6d10c27f89eba (diff)
Try to clean up w5500_ll.h
-rw-r--r--libhw_cr/w5500.c2
-rw-r--r--libhw_cr/w5500_ll.c48
-rw-r--r--libhw_cr/w5500_ll.h133
-rw-r--r--libhw_generic/include/libhw/generic/io.h2
4 files changed, 103 insertions, 82 deletions
diff --git a/libhw_cr/w5500.c b/libhw_cr/w5500.c
index b7c2ad1..8bdb95b 100644
--- a/libhw_cr/w5500.c
+++ b/libhw_cr/w5500.c
@@ -67,6 +67,8 @@
* SPDX-License-Identifier: MIT
*/
+#include <string.h> /* for memcmp() */
+
/* TODO: Write a <libhw/generic/gpio.h> to avoid w5500.c being
* pico-sdk-specific. */
#include "rp2040_gpioirq.h"
diff --git a/libhw_cr/w5500_ll.c b/libhw_cr/w5500_ll.c
index d9f4fc5..c60e045 100644
--- a/libhw_cr/w5500_ll.c
+++ b/libhw_cr/w5500_ll.c
@@ -1,12 +1,14 @@
/* libhw_cr/w5500_ll.c - Low-level library for the WIZnet W5500 chip
*
- * Based entirely on the W5500 datasheet, v1.1.0.
+ * Based entirely on the W5500 datasheet (v1.1.0), not on reference code.
* https://docs.wiznet.io/img/products/w5500/W5500_ds_v110e.pdf
*
* Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
+#include <string.h> /* for memcmp() and mempy() */
+
#include <libmisc/alloc.h> /* for stack_alloc() */
#include <libmisc/fmt.h>
@@ -24,15 +26,10 @@ static void fmt_print_ctl_block(lo_interface fmt_dest, uint8_t b) {
}
#endif
-void
-#if CONFIG_W5500_LL_DEBUG
-_w5500ll_writev(const char *func,
-#else
-w5500ll_writev(
-#endif
- lo_interface spi spidev, uint16_t addr, uint8_t block,
- const struct iovec *iov, int iovcnt,
- size_t skip, size_t max)
+void _w5500ll_n_writev(const char *func,
+ 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);
@@ -64,15 +61,11 @@ w5500ll_writev(
LO_CALL(spidev, readwritev, inner, inner_cnt);
}
-void
-#if CONFIG_W5500_LL_DEBUG
-_w5500ll_readv(const char *func,
-#else
-w5500ll_readv(
-#endif
- lo_interface spi spidev, uint16_t addr, uint8_t block,
- const struct iovec *iov, int iovcnt,
- size_t max)
+
+void _w5500ll_n_readv(const char *func,
+ 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);
@@ -103,3 +96,20 @@ w5500ll_readv(
io_slice_rd_to_duplex(&inner[1], iov, iovcnt, 0, max);
LO_CALL(spidev, readwritev, inner, inner_cnt);
}
+
+void _w5500ll_n_read_repeated(const char *func,
+ lo_interface spi spidev, uint16_t addr, uint8_t block,
+ void *buf, void *buf_scratch, size_t len)
+{
+ _w5500ll_n_readv(func, spidev, addr, block,
+ &((struct iovec){.iov_base=buf, .iov_len=len}), 1,
+ 0);
+ for (;;) {
+ _w5500ll_n_readv(func, spidev, addr, block,
+ &((struct iovec){.iov_base=buf_scratch, .iov_len=len}), 1,
+ 0);
+ if (memcmp(buf, buf_scratch, len) == 0)
+ break;
+ memcpy(buf, buf_scratch, len);
+ }
+}
diff --git a/libhw_cr/w5500_ll.h b/libhw_cr/w5500_ll.h
index eeb2fb7..28116a4 100644
--- a/libhw_cr/w5500_ll.h
+++ b/libhw_cr/w5500_ll.h
@@ -1,6 +1,6 @@
/* libhw_cr/w5500_ll.h - Low-level library for the WIZnet W5500 chip
*
- * Based entirely on the W5500 datasheet, v1.1.0.
+ * Based entirely on the W5500 datasheet (v1.1.0), not on reference code.
* https://docs.wiznet.io/img/products/w5500/W5500_ds_v110e.pdf
*
* Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
@@ -11,7 +11,6 @@
#define _LIBHW_CR_W5500_LL_H_
#include <stdint.h> /* for uint{n}_t */
-#include <string.h> /* for memcmp() */
#include <libmisc/assert.h> /* for static_assert() */
#include <libmisc/endian.h> /* for uint16be_t */
@@ -19,7 +18,7 @@
#include <libhw/generic/net.h> /* for struct net_eth_addr, struct net_ip4_addr */
#include <libhw/generic/spi.h> /* for lo_interface spi */
-/* Config *********************************************************************/
+/* Config. ***************************************************************************************/
#include "config.h"
@@ -27,7 +26,7 @@
#error config.h must define CONFIG_W5500_LL_DEBUG
#endif
-/* Low-level protocol built on SPI frames. ***********************************/
+/* Low-level protocol built on SPI frames. *******************************************************/
/* A u8 control byte has 3 parts: block-ID, R/W, and operating-mode. */
@@ -52,28 +51,81 @@
#define CTL_OM_FDM2 0b10 /* fixed-length data mode: 2 byte data length */
#define CTL_OM_FDM4 0b11 /* fixed-length data mode: 4 byte data length */
-/* Even though SPI is a full-duplex protocol, the W5500's spiframe on top of it is only half-duplex.
- * Lame. */
+/* Even though SPI is a full-duplex protocol, the W5500's spiframe on
+ * top of it is only half-duplex. Lame. */
#if CONFIG_W5500_LL_DEBUG
-#define w5500ll_writev(...) _w5500ll_writev(__func__, __VA_ARGS__)
-void _w5500ll_writev(const char *func,
+ #define w5500ll_writev(...) _w5500ll_n_writev (__func__, __VA_ARGS__)
+ #define w5500ll_readv(...) _w5500ll_n_readv (__func__, __VA_ARGS__)
#else
-void w5500ll_writev(
+ #define _w5500ll_n_writev(func, ...) w5500ll_writev (__VA_ARGS__)
+ #define _w5500ll_n_readv(func, ...) w5500ll_readv (__VA_ARGS__)
#endif
- lo_interface spi spidev, uint16_t addr, uint8_t block,
- const struct iovec *iov, int iovcnt,
- size_t skip, size_t max);
+/* `skip` and `max` correspond to the <libhw/generic/io.h> `slice`
+ * functions' `byte_start` and `max_byte_cnt` arguments for working on
+ * just a subset of the iovec list. */
+
+void _w5500ll_n_writev(const char *func,
+ lo_interface spi spidev, uint16_t addr, uint8_t block,
+ const struct iovec *iov, int iovcnt,
+ size_t skip, size_t max);
+void _w5500ll_n_readv(const char *func,
+ lo_interface spi spidev, uint16_t addr, uint8_t block,
+ const struct iovec *iov, int iovcnt,
+ size_t max);
+
+/* Operate on registers. *************************************************************************/
+
+/*
+ * Given a `blocktyp` that is a struct describing the layout of
+ * registers in a block (e.g. `blocktyp = struct
+ * w5500_ll_block_common_reg`), read or write the `field` register
+ * (where `field` must be a field in that struct).
+ */
+
+#define w5500ll_write_reg(spidev, blockid, blocktyp, field, val) do { \
+ typeof((blocktyp){}.field) lval = val; \
+ w5500ll_writev(spidev, \
+ offsetof(blocktyp, field), blockid, \
+ &((struct iovec){ \
+ .iov_base = &lval, \
+ .iov_len = sizeof(lval), \
+ }), 1, \
+ 0, 0); \
+ } while (0)
+
+/* The datasheet tells us that multi-byte reads are non-atomic and
+ * that "it is recommended that you read all 16-bits twice or more
+ * until getting the same value". */
#if CONFIG_W5500_LL_DEBUG
-#define w5500ll_readv(...) _w5500ll_read(__func__, __VA_ARGS__)
-void _w5500ll_readv(const char *func,
+ #define _w5500ll_read_repeated(...) _w5500ll_n_read_repeated (__func__, __VA_ARGS__)
#else
-void w5500ll_readv(
+ #define _w5500ll_n_read_repeated(func, ...) _w5500ll_read_repeated (__VA_ARGS__)
#endif
- lo_interface spi spidev, uint16_t addr, uint8_t block,
- const struct iovec *iov, int iovcnt,
- size_t max);
+void _w5500ll_n_read_repeated(const char *func,
+ lo_interface spi spidev, uint16_t addr, uint8_t block,
+ void *buf, void *buf_scratch, size_t len);
+#define w5500ll_read_reg(spidev, blockid, blocktyp, field) ({ \
+ typeof((blocktyp){}.field) val; \
+ if (sizeof(val) == 1) { \
+ w5500ll_readv(spidev, \
+ offsetof(blocktyp, field), blockid, \
+ &((struct iovec){ \
+ .iov_base = &val, \
+ .iov_len = sizeof(val), \
+ }), 1, \
+ 0); \
+ } else { \
+ typeof(val) val2; \
+ _w5500ll_read_repeated(spidev, \
+ offsetof(blocktyp, field), blockid, \
+ &val, &val2, sizeof(val)); \
+ } \
+ val; \
+ })
+
+/* Register blocks. ******************************************************************************/
/* Common chip-wide registers. ***********************************************/
@@ -312,49 +364,4 @@ static_assert(sizeof(struct w5500ll_block_sock_reg) == 0x30);
struct w5500ll_block_sock_reg, \
field)
-/******************************************************************************/
-
-#define w5500ll_write_reg(spidev, blockid, blocktyp, field, val) do { \
- typeof((blocktyp){}.field) lval = val; \
- 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
- * that "it is recommended that you read all 16-bits twice or more
- * until getting the same value". */
-#define w5500ll_read_reg(spidev, blockid, blocktyp, field) ({ \
- typeof((blocktyp){}.field) 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_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; \
- } \
- val; \
- })
-
#endif /* _LIBHW_CR_W5500_LL_H_ */
diff --git a/libhw_generic/include/libhw/generic/io.h b/libhw_generic/include/libhw/generic/io.h
index 96680bb..8b3fb9c 100644
--- a/libhw_generic/include/libhw/generic/io.h
+++ b/libhw_generic/include/libhw/generic/io.h
@@ -39,6 +39,8 @@ struct duplex_iovec {
/* utilities ******************************************************************/
+/* If byte_max_cnt == 0, then there is no maximum. */
+
/* slice iovec lists */
int io_slice_cnt ( const struct iovec *src, int src_cnt, size_t byte_start, size_t byte_max_cnt);
void io_slice (struct iovec *dst, const struct iovec *src, int src_cnt, size_t byte_start, size_t byte_max_cnt);