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.h461
1 files changed, 0 insertions, 461 deletions
diff --git a/libhw/w5500_ll.h b/libhw/w5500_ll.h
deleted file mode 100644
index 3822ad4..0000000
--- a/libhw/w5500_ll.h
+++ /dev/null
@@ -1,461 +0,0 @@
-/* libhw/w5500_ll.h - Low-level header library for the WIZnet W5500 chip
- *
- * Based entirely on the W5500 datasheet, v1.1.0.
- * 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
- */
-
-#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() */
-
-#include <libmisc/assert.h> /* for assert(), static_assert() */
-#include <libmisc/endian.h> /* for uint16be_t */
-
-#include <libhw/generic/net.h> /* for struct net_eth_addr, struct net_ip4_addr */
-#include <libhw/generic/spi.h> /* for lo_interface spi */
-
-/* Config *********************************************************************/
-
-#include "config.h"
-
-#ifndef CONFIG_W5500_LL_DEBUG
- #error config.h must define CONFIG_W5500_LL_DEBUG
-#endif
-
-/* Low-level protocol built on SPI frames. ***********************************/
-
-/* A u8 control byte has 3 parts: block-ID, R/W, and operating-mode. */
-
-/* Part 1: Block ID. */
-#define CTL_MASK_BLOCK 0b11111000
-#define _CTL_BLOCK_RES 0b00000 /* chip-wide registers on socknum=0, REServed on socknum>=1 */
-#define _CTL_BLOCK_REG 0b01000 /* socknum-specific registers */
-#define _CTL_BLOCK_TX 0b10000 /* socknum-specific transmit buffer */
-#define _CTL_BLOCK_RX 0b11000 /* socknum-specific receive buffer */
-#define CTL_BLOCK_SOCK(n,part) (((n)<<5)|(_CTL_BLOCK_##part))
-#define CTL_BLOCK_COMMON_REG CTL_BLOCK_SOCK(0,RES)
-
-/* Part 2: R/W. */
-#define CTL_MASK_RW 0b100
-#define CTL_R 0b000
-#define CTL_W 0b100
-
-/* Part 3: Operating mode. */
-#define CTL_MASK_OM 0b11
-#define CTL_OM_VDM 0b00 /* variable-length data mode */
-#define CTL_OM_FDM1 0b01 /* fixed-length data mode: 1 byte data length */
-#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 */
-
-#if CONFIG_W5500_LL_DEBUG
-static char *_ctl_block_part_strs[] = {
- "RES",
- "REG",
- "TX",
- "RX",
-};
-#define PRI_ctl_block "CTL_BLOCK_SOCK(%d, %s)"
-#define ARG_ctl_block(b) (((b)>>5) & 0b111), _ctl_block_part_strs[((b)>>3)&0b11]
-#endif
-
-/* Even though SPI is a full-duplex protocol, the W5500's spiframe on top of it is only half-duplex.
- * Lame. */
-
-static inline void
-#if CONFIG_W5500_LL_DEBUG
-#define w5500ll_writev(...) _w5500ll_writev(__func__, __VA_ARGS__)
-_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)
-{
- assert(!LO_IS_NULL(spidev));
- assert((block & ~CTL_MASK_BLOCK) == 0);
- assert(iov);
- assert(iovcnt > 0);
-#if CONFIG_W5500_LL_DEBUG
- n_debugf(W5500_LL,
- "%s(): w5500ll_write(spidev, addr=%#04x, block="PRI_ctl_block", iov, iovcnt=%d)",
- func, addr, ARG_ctl_block(block), iovcnt);
-#endif
-
- uint8_t header[] = {
- (uint8_t)((addr >> 8) & 0xFF),
- (uint8_t)(addr & 0xFF),
- (block & CTL_MASK_BLOCK) | CTL_W | CTL_OM_VDM,
- };
- 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 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+(skip-skipped),
- .iov_len = iov[i].iov_len-(skip-skipped),
- };
- 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_readv(...) _w5500ll_read(__func__, __VA_ARGS__)
-_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)
-{
- assert(!LO_IS_NULL(spidev));
- assert((block & ~CTL_MASK_BLOCK) == 0);
- assert(iov);
- assert(iovcnt > 0);
-#if CONFIG_W5500_LL_DEBUG
- n_debugf(W5500_LL,
- "%s(): w5500ll_read(spidev, addr=%#04x, block="PRI_ctl_block", iov, iovcnt=%d)",
- func, addr, ARG_ctl_block(block), iovcnt);
-#endif
-
- uint8_t header[] = {
- (uint8_t)((addr >> 8) & 0xFF),
- (uint8_t)(addr & 0xFF),
- (block & CTL_MASK_BLOCK) | CTL_R | CTL_OM_VDM,
- };
- 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, inner, j);
-}
-
-/* Common chip-wide registers. ***********************************************/
-
-struct w5500ll_block_common_reg {
- uint8_t mode; /* MR; bitfield, see CHIPMODE_{x} below */
- struct net_ip4_addr ip_gateway_addr; /* GAR0 ... GAR3 */
- struct net_ip4_addr ip_subnet_mask; /* SUBR0 ... SUBR3 */
- struct net_eth_addr eth_addr; /* SHAR0 ... SHAR5 */
- struct net_ip4_addr ip_addr; /* SIPR0 ... SIPR3 */
-
- uint16be_t intlevel; /* INTLEVEL0, INTLEVEL1; if non-zero,
- * hysteresis between pin_intr being pulled
- * low (hysteresis=(intlevel+1)*4/(150MHz)) */
- uint8_t chip_interrupt; /* IR; bitfield, see CHIPINTR_{x} below */
- uint8_t chip_interrupt_mask; /* IMR; bitfield, see CHIPINTR_{x} below, 0=disable, 1=enable */
- uint8_t sock_interrupt; /* SIR; bitfield of which sockets have their .interrupt set */
- uint8_t sock_interrupt_mask; /* SIMR; bitfield of sockets, 0=disable, 1=enable */
- uint16be_t retry_time; /* RTR0, RTR0; configures re-transmission period, in units of 100µs */
- uint8_t retry_count; /* RCR; configures max re-transmission count */
-
- uint8_t ppp_lcp_request_timer; /* PTIMER */
- uint8_t ppp_lcp_magic_bumber; /* PMAGIC */
- struct net_eth_addr ppp_dst_eth_addr; /* PHAR0 ... PHAR5 */
- uint16be_t ppp_sess_id; /* PSID0 ... PSID1 */
- uint16be_t ppp_max_seg_size; /* PMRU0 ... PMRU1 */
-
- struct net_ip4_addr unreachable_ip_addr; /* UIPR0 ... UIPR3 */
- uint16be_t unreachable_port; /* UPORTR0, UPORTR1 */
-
- uint8_t phy_cfg; /* PHYCFGR */
-
- uint8_t _reserved[10];
-
- uint8_t chip_version; /* VERSIONR */
-};
-static_assert(sizeof(struct w5500ll_block_common_reg) == 0x3A);
-
-/* bitfield */
-#define CHIPMODE_RST ((uint8_t)(1<<7)) /* software reset */
-#define _CHIPMODE_UNUSED6 ((uint8_t)(1<<6))
-#define CHIPMODE_WOL ((uint8_t)(1<<5)) /* wake-on-lan */
-#define CHIPMODE_BLOCK_PING ((uint8_t)(1<<4))
-#define CHIPMODE_PPP ((uint8_t)(1<<3))
-#define _CHIPMODE_UNUSED2 ((uint8_t)(1<<2))
-#define CHIPMODE_FORCE_ARP ((uint8_t)(1<<1))
-#define _CHIPMODE_UNUSED0 ((uint8_t)(1<<0))
-
-#define CHIPINTR_CONFLICT ((uint8_t)(1<<7)) /* ARP says remote IP is self */
-#define CHIPINTR_UNREACH ((uint8_t)(1<<6))
-#define CHIPINTR_PPP_CLOSE ((uint8_t)(1<<6))
-#define CHIPINTR_WOL ((uint8_t)(1<<4)) /* wake-on-LAN */
-#define _CHIPINTR_UNUSED3 ((uint8_t)(1<<3))
-#define _CHIPINTR_UNUSED2 ((uint8_t)(1<<2))
-#define _CHIPINTR_UNUSED1 ((uint8_t)(1<<1))
-#define _CHIPINTR_UNUSED0 ((uint8_t)(1<<0))
-
-#define w5500ll_write_common_reg(spidev, field, val) \
- w5500ll_write_reg(spidev, \
- CTL_BLOCK_COMMON_REG, \
- struct w5500ll_block_common_reg, \
- field, val)
-
-
-#define w5500ll_read_common_reg(spidev, field) \
- w5500ll_read_reg(spidev, \
- CTL_BLOCK_COMMON_REG, \
- struct w5500ll_block_common_reg, \
- field)
-
-/* Per-socket registers. *****************************************************/
-
-struct w5500ll_block_sock_reg {
- uint8_t mode; /* Sn_MR; see SOCKMODE_{x} below */
- uint8_t command; /* Sn_CR; see CMD_{x} below */
- uint8_t interrupt; /* Sn_IR; bitfield, see SOCKINTR_{x} below */
- uint8_t state; /* Sn_SR; see STATE_{x} below */
- uint16be_t local_port; /* Sn_PORT0, Sn_PORT1 */
- struct net_eth_addr remote_eth_addr; /* Sn_DHAR0 ... SnDHAR5 */
- struct net_ip4_addr remote_ip_addr; /* Sn_DIPR0 ... Sn_DIP3 */
- uint16be_t remote_port; /* Sn_DPORT0 ... Sn_DPORT1 */
-
- uint16be_t max_seg_size; /* Sn_MSSR0, Sn_MSSR1 */
- uint8_t _reserved0[1];
- uint8_t ip_tos; /* Sn_TOS */
- uint8_t ip_ttl; /* Sn_TTL */
- uint8_t _reserved1[7];
-
- uint8_t rx_buf_size; /* Sn_RXBUF_SIZE; in KiB, power of 2, <= 16 */
- uint8_t tx_buf_size; /* Sn_TXBUF_SIZE; in KiB, power of 2, <= 16 */
- uint16be_t tx_free_size; /* Sn_TX_FSR0, Sn_TX_FSR1 */
- uint16be_t tx_read_pointer; /* Sn_TX_RD0, Sn_TX_RD1 */
- uint16be_t tx_write_pointer; /* Sn_TX_WR0, Sn_TX_WR1 */
- uint16be_t rx_size; /* Sn_RX_RSR0, Sn_RX_RSR1 */
- uint16be_t rx_read_pointer; /* Sn_RX_RD0, Sn_RX_RD1 */
- uint16be_t rx_write_pointer; /* Sn_RX_WR0, Sn_RX_WR1 */
-
- uint8_t interrupt_mask; /* Sn_IMR */
- uint16be_t fragment_offset; /* Sn_FRAG0, Sn_FRAG1 */
- uint8_t keepalive_timer; /* Sn_KPALVTR */
-};
-static_assert(sizeof(struct w5500ll_block_sock_reg) == 0x30);
-
-/* low 4 bits are the main enum, high 4 bits are flags */
-#define SOCKMODE_CLOSED ((uint8_t)0b0000)
-#define SOCKMODE_TCP ((uint8_t)0b0001)
-#define SOCKMODE_UDP ((uint8_t)0b0010)
-#define SOCKMODE_MACRAW ((uint8_t)0b0100)
-
-#define SOCKMODE_FLAG_TCP_NODELAY_ACK ((uint8_t)(1<<5))
-
-#define SOCKMODE_FLAG_UDP_ENABLE_MULTICAST ((uint8_t)(1<<7))
-#define SOCKMODE_FLAG_UDP_BLOCK_BROADCAST ((uint8_t)(1<<6))
-#define SOCKMODE_FLAG_UDP_MULTICAST_DOWNGRADE ((uint8_t)(1<<5)) /* Use IGMPv1 instead of v2 */
-#define SOCKMODE_FLAG_UDP_BLOCK_UNICAST ((uint8_t)(1<<4))
-
-#define SOCKMODE_FLAG_MACRAW_MAC_FILTERING ((uint8_t)(1<<7))
-#define SOCKMODE_FLAG_MACRAW_BLOCK_BROADCAST ((uint8_t)(1<<6))
-#define SOCKMODE_FLAG_MACRAW_BLOCK_MULTICAST ((uint8_t)(1<<5))
-#define SOCKMODE_FLAG_MACRAW_BLOCK_IPV6 ((uint8_t)(1<<4))
-
-#define CMD_OPEN ((uint8_t)0x01)
-#define CMD_LISTEN ((uint8_t)0x02) /* TCP-only */
-#define CMD_CONNECT ((uint8_t)0x04) /* TCP-only: dial */
-#define CMD_DISCON ((uint8_t)0x08) /* TCP-only: send FIN */
-#define CMD_CLOSE ((uint8_t)0x10)
-#define CMD_SEND ((uint8_t)0x20)
-#define CMD_SEND_MAC ((uint8_t)0x21) /* UDP-only: send to remote_eth_addr without doing ARP on remote_ip_addr */
-#define CMD_SEND_KEEP ((uint8_t)0x22) /* TCP-only: send a keepalive without any data */
-#define CMD_RECV ((uint8_t)0x40)
-
-#define _SOCKINTR_SEND_UNUSED7 ((uint8_t)1<<7)
-#define _SOCKINTR_SEND_UNUSED6 ((uint8_t)1<<6)
-#define _SOCKINTR_SEND_UNUSED5 ((uint8_t)1<<5)
-#define SOCKINTR_SEND_OK ((uint8_t)1<<4) /* TODO: determine precise meaning */
-#define SOCKINTR_SEND_TIMEOUT ((uint8_t)1<<3) /* ARP or TCP */
-#define SOCKINTR_RECV_DAT ((uint8_t)1<<2) /* received data */
-#define SOCKINTR_RECV_FIN ((uint8_t)1<<1) /* received FIN */
-#define SOCKINTR_CONN ((uint8_t)1<<0) /* first for SYN, then when SOCKMODE_ESTABLISHED */
-
-#define STATE_CLOSED ((uint8_t)0x00)
-
-/**
- * The TCP state diagram is as follows.
- * - Reading the W5500's "state" register does not distinguish between FIN_WAIT_1 and FIN_WAIT_2;
- * it just has a single FIN_WAIT.
- * - At any point the state can jump to "CLOSED" either by CMD_CLOSE or by a timeout.
- * - Writing data is valid in ESTABLISHED and CLOSE_WAIT.
- * - Reading data is valid in ESTABLISHED and FIN_WAIT.
- *
- * TCP state diagram, showing the flow of │ CLOSED │ ━━ role separator ┌───────┐
- * SYN, FIN, and their assocaited ACKs. └────────┘ ══ state transition │ state │
- * V ┈┈ packet flow └───────┘
- * (CMD_OPEN) ║
- * V (action/event)
- * ┌────────┐
- * ┏━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━│ INIT │━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓
- * ┃ server ┃ └────────┘ ┃ client ┃
- * ┣━━━━━━━━━━━━━━━━┛ V ┃┃ V ┗━━━━━━━━━━━━━━━━┫
- * ┃ ╔═══════════╝ ┃┃ ╚═══════════╗ ┃
- * ┃ (CMD_LISTEN) ┃┃ (CMD_CONNECT) ┃
- * ┃ V ┌┃┃┈┈┈┈┈┈┈┈<(send SYN) ┃
- * ┃ ┌────────┐ ┊┃┃ V ┃
- * ┃ │ LISTEN │ ┊┃┃ ┌─────────┐ ┃
- * ┃ └────────┘ ┊┃┃ │ SYNSENT │ ┃
- * ┃ V ┊┃┃ └─────────┘ ┃
- * ┃ (recv SYN)<┈┈┈┈┈┈┘┃┃ V ┃
- * ┃ (send SYN+ACK)>┈┈┈┈┐┃┃ ║ ┃
- * ┃ V └┃┃┈┈┈┈┈┈>(recv SYN+ACK) ┃
- * ┃ ┌─────────┐ ┌┃┃┈┈┈┈┈┈┈┈<(send ack) ┃
- * ┃ │ SYNRECV │ ┊┃┃ ║ ┃
- * ┃ └─────────┘ ┊┃┃ ║ ┃
- * ┃ V V ┊┃┃ ║ ┃
- * ┃ ║ (recv ACK)<┈┈┈┘┃┃ ║ ┃
- * ┃ ║ ╚═════════╗ ┃┃ ╔═══════════╝ ┃
- * ┃ ╚═══╗ V ┃┃ V ┃
- * ┃ ║ ┌─────────────┐ ┃
- * ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━║━━━━━━━│ ESTABLISHED │━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
- * ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━║━━━━━━━└─────────────┘━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
- * ┃ ║ V ┃┃ V ┃
- * ┃ ╠═══════════╝ ┃┃ ╚═══════════╗ ┃
- * ┃ (CMD_DISCON) ┃┃ ║ ┃
- * ┃ Both sides sent ┌┈┈┈<(send FIN)>┈┈┈┈┈┈┐┃┃ ║ ┃
- * ┃ FIN at the "same" ┊ V └┃┃┈┈┈┈┈┈┈┈>(recv FIN) ┃
- * ┃ time; both are ┊ ┌────────────┐ ┌┃┃┈┈┈┈┈┈┈┈<(send ACK) ┃
- * ┃ active closers ┊ │ FIN_WAIT_1 │ ┊┃┃ V ┃
- * ┃ / \ ┊ └────────────┘ ┊┃┃ ┌────────────┐ ┃
- * ┃ ,------' '------, ┊ V V ┊┃┃ │ CLOSE_WAIT │ ┃
- * ┃ ╔════════════════╝ ║ ┊┃┃ └────────────┘ ┃
- * ┃ (recv FIN)<┈┈┈┈┤ ╔══╝ ┊┃┃ V ┃
- * ┃ ┌┈┈<(send ACK)>┈┈┐ ┊ ║ ┊┃┃ ║ ┃
- * ┃ ┊ ║ └┈┈┈┈┈>(recv ACK)<┈┈┈┈┈┈┘┃┃ ║ ┃
- * ┃ ┊ V ┊ V ┃┃ ║ ┃
- * ┃ ┊ ┌─────────┐ ┊ ┌────────────┐ ┃┃ ║ ┃
- * ┃ ┊ │ CLOSING │ ┊ │ FIN_WAIT_2 │ ┃┃ ║ ┃
- * ┃ ┊ └─────────┘ ┊ └────────────┘ ┃┃ (CMD_DISCON) ┃
- * ┃ ┊ V ┊ V ┌┃┃┈┈┈┈┈┈┈┈<(send FIN) ┃
- * ┃ ┊ ║ └┈┈┈>(recv FIN)<┈┈┈┈┈┈┘┃┃ ║ ┃
- * ┃ ┊ ║ ┌┈┈┈┈┈<(send ACK)>┈┈┈┈┈┈┐┃┃ V ┃
- * ┃ └┈┈>(recv ACK)<┈┈┘ ╚═╗ ┊┃┃ ┌──────────┐ ┃
- * ┃ ╚════════════════╗ ║ ┊┃┃ │ LAST_ACK │ ┃
- * ┃ V V ┊┃┃ └──────────┘ ┃
- * ┃ ┌───────────┐ ┊┃┃ V ┃
- * ┃ │ TIME_WAIT │ ┊┃┃ ║ ┃
- * ┃ └───────────┘ └┃┃┈┈┈┈┈┈┈┈>(recv ACK) ┃
- * ┃ V ┃┃ ║ ┃
- * ┣━━━━━━━━━━━━━━━━┓ (2*MSL has elapsed) ┃┃ ║ ┏━━━━━━━━━━━━━━━━┫
- * ┃ active closer ┃ ╚═══════════╗ ┃┃ ╔═══════════╝ ┃ passive closer ┃
- * ┗━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━V━┛┗━V━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━┛
- * ┌────────┐
- * │ CLOSED │
- */
-#define STATE_TCP_INIT ((uint8_t)0x13)
-#define STATE_TCP_LISTEN ((uint8_t)0x14) /* server */
-#define STATE_TCP_SYNSENT ((uint8_t)0x15) /* client; during dial */
-#define STATE_TCP_SYNRECV ((uint8_t)0x16) /* server; during accept */
-#define STATE_TCP_ESTABLISHED ((uint8_t)0x17)
-#define STATE_TCP_FIN_WAIT ((uint8_t)0x18) /* during active close */
-#define STATE_TCP_CLOSING ((uint8_t)0x1a) /* during active close */
-#define STATE_TCP_TIME_WAIT ((uint8_t)0x1b) /* during active close */
-#define STATE_TCP_CLOSE_WAIT ((uint8_t)0x1c) /* during passive close */
-#define STATE_TCP_LAST_ACK ((uint8_t)0x1d) /* during passive close */
-
-#define STATE_UDP ((uint8_t)0x22)
-
-#define STATE_MACRAW ((uint8_t)0x42)
-
-#define w5500ll_write_sock_reg(spidev, socknum, field, val) \
- w5500ll_write_reg(spidev, \
- CTL_BLOCK_SOCK(socknum, REG), \
- struct w5500ll_block_sock_reg, \
- field, val)
-
-#define w5500ll_read_sock_reg(spidev, socknum, field) \
- w5500ll_read_reg(spidev, \
- CTL_BLOCK_SOCK(socknum, REG), \
- 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_W5500_LL_H_ */