summaryrefslogtreecommitdiff
path: root/cmd/sbc_harness/hw/w5500_ll.h
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/sbc_harness/hw/w5500_ll.h')
-rw-r--r--cmd/sbc_harness/hw/w5500_ll.h362
1 files changed, 0 insertions, 362 deletions
diff --git a/cmd/sbc_harness/hw/w5500_ll.h b/cmd/sbc_harness/hw/w5500_ll.h
deleted file mode 100644
index 4cc3b11..0000000
--- a/cmd/sbc_harness/hw/w5500_ll.h
+++ /dev/null
@@ -1,362 +0,0 @@
-/* hw/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 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-Licence-Identifier: AGPL-3.0-or-later
- */
-
-#ifndef _HW_W5500_LL_H_
-#define _HW_W5500_LL_H_
-
-#include <assert.h> /* for assert(), static_assert() */
-#include <stdint.h> /* for uint{n}_t */
-#include <string.h> /* for memcmp() */
-
-#include <libmisc/net.h> /* for struct net_eth_addr, struct net_ip4_addr */
-#include <libmisc/vcall.h> /* for VCALL() */
-#include <libmisc/endian.h> /* for uint16be_t */
-#include "hw/spi.h" /* for implements_spi */
-
-
-/* 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
-#define _CTL_BLOCK_REG 0b01000
-#define _CTL_BLOCK_TX 0b10000
-#define _CTL_BLOCK_RX 0b11000
-#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
-#define CTL_OM_FDM1 0b01
-#define CTL_OM_FDM2 0b10
-#define CTL_OM_FDM4 0b11
-
-/* Even though SPI is a full-duplex protocol, the W5500's spiframe on top of it is only half-duplex.
- * Lame. */
-
-static inline void
-w5500ll_write(implements_spi *spidev, uint16_t addr, uint8_t block, void *data, size_t data_len) {
- assert(spidev);
- assert((block & ~CTL_MASK_BLOCK) == 0);
- assert(data);
- assert(data_len);
-
- uint8_t header[3] = {
- (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},
- };
- VCALL(spidev, readwritev, iov, 2);
-}
-
-static inline void
-w5500ll_read(implements_spi *spidev, uint16_t addr, uint8_t block, void *data, size_t data_len) {
- assert(spidev);
- assert((block & ~CTL_MASK_BLOCK) == 0);
- assert(data);
- assert(data_len);
-
- uint8_t header[3] = {
- (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},
- };
- VCALL(spidev, readwritev, iov, 2);
-}
-
-/* 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))
-#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_V6 ((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)
-#define SOCKINTR_TIMEOUT ((uint8_t)1<<3)
-#define SOCKINTR_RECV ((uint8_t)1<<2)
-#define SOCKINTR_FIN ((uint8_t)1<<1)
-#define SOCKINTR_CONN ((uint8_t)1<<1) /* 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_write(spidev, \
- offsetof(blocktyp, field), \
- blockid, \
- &lval, \
- sizeof(lval)); \
- } 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_read(spidev, \
- offsetof(blocktyp, field), \
- blockid, \
- &val, \
- sizeof(val)); \
- if (sizeof(val) > 1) \
- for (;;) { \
- typeof(val) val2; \
- w5500ll_read(spidev, \
- offsetof(blocktyp, field), \
- blockid, \
- &val2, \
- sizeof(val)); \
- if (memcmp(&val2, &val, sizeof(val)) == 0) \
- break; \
- val = val2; \
- } \
- val; \
- })
-
-#endif /* _HW_W5500_LL_H_ */