summaryrefslogtreecommitdiff
path: root/libhw_cr/rp2040_include/libhw/w5500.h
diff options
context:
space:
mode:
Diffstat (limited to 'libhw_cr/rp2040_include/libhw/w5500.h')
-rw-r--r--libhw_cr/rp2040_include/libhw/w5500.h114
1 files changed, 114 insertions, 0 deletions
diff --git a/libhw_cr/rp2040_include/libhw/w5500.h b/libhw_cr/rp2040_include/libhw/w5500.h
new file mode 100644
index 0000000..51effba
--- /dev/null
+++ b/libhw_cr/rp2040_include/libhw/w5500.h
@@ -0,0 +1,114 @@
+/* libhw/w5500.h - <libhw/generic/net.h> implementation for the WIZnet W5500 chip
+ *
+ * Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#ifndef _LIBHW_W5500_H_
+#define _LIBHW_W5500_H_
+
+#include <pico/binary_info.h> /* for bi_* */
+
+#include <libcr_ipc/chan.h>
+#include <libcr_ipc/mutex.h>
+#include <libcr_ipc/sema.h>
+#include <libmisc/private.h>
+
+#include <libhw/generic/net.h>
+#include <libhw/generic/spi.h>
+
+CR_CHAN_DECLARE(_w5500_sockintr_ch, uint8_t)
+
+struct _w5500_socket {
+ BEGIN_PRIVATE(LIBHW_W5500_H)
+ /* const-after-init */
+ uint8_t socknum;
+
+ /* mutable */
+ struct _w5500_socket *next_free;
+ enum {
+ W5500_MODE_NONE = 0,
+ W5500_MODE_TCP,
+ W5500_MODE_UDP,
+ } mode;
+ uint16_t port; /* MODE_{TCP,UDP} */
+ uint64_t read_deadline_ns; /* MODE_{TCP,UDP} */
+ cr_sema_t listen_sema; /* MODE_TCP */
+ cr_sema_t read_sema; /* MODE_{TCP,UDP} */
+ _w5500_sockintr_ch_t write_ch; /* MODE_{TCP,UDP} */
+ bool list_open, read_open, write_open; /* MODE_TCP */
+
+ END_PRIVATE(LIBHW_W5500_H)
+};
+
+LO_IMPLEMENTATION_H(io_closer, struct _w5500_socket, w5500_tcplist)
+LO_IMPLEMENTATION_H(net_stream_listener, struct _w5500_socket, w5500_tcplist)
+
+LO_IMPLEMENTATION_H(io_reader, struct _w5500_socket, w5500_tcp)
+LO_IMPLEMENTATION_H(io_writer, struct _w5500_socket, w5500_tcp)
+LO_IMPLEMENTATION_H(io_readwriter, struct _w5500_socket, w5500_tcp)
+LO_IMPLEMENTATION_H(io_closer, struct _w5500_socket, w5500_tcp)
+LO_IMPLEMENTATION_H(io_bidi_closer, struct _w5500_socket, w5500_tcp)
+LO_IMPLEMENTATION_H(net_stream_conn, struct _w5500_socket, w5500_tcp)
+
+LO_IMPLEMENTATION_H(io_closer, struct _w5500_socket, w5500_udp)
+LO_IMPLEMENTATION_H(net_packet_conn, struct _w5500_socket, w5500_udp)
+
+struct w5500 {
+ BEGIN_PRIVATE(LIBHW_W5500_H)
+ /* const-after-init */
+ lo_interface spi spidev;
+ uint pin_intr;
+ uint pin_reset;
+ struct net_eth_addr hwaddr;
+
+ /* mutable */
+ uint16_t next_local_port;
+ struct _w5500_socket sockets[8];
+ struct _w5500_socket *free;
+ cr_sema_t intr;
+ cr_mutex_t mu;
+ END_PRIVATE(LIBHW_W5500_H)
+};
+LO_IMPLEMENTATION_H(net_iface, struct w5500, w5500_if)
+
+/**
+ * Initialize a WIZnet W5500 Ethernet-and-TCP/IP-offload chip.
+ *
+ * The W5500 has 3 lines of communication with the MCU:
+ *
+ * - An SPI-based RPC protocol:
+ * + mode: mode 0 or mode 3
+ * + bit-order: MSB-first
+ * + clock frequency: 33.3MHz - 80MHz
+ * - An interrupt pin that it pulls low when an event happens (to let
+ * the MCU know that it should do an SPI RPC "get" to see what
+ * happened.)
+ * - A reset pin that the MCU can pull low to reset the W5500.
+ */
+#define w5500_init(self, name, spi, pin_intr, pin_reset, eth_addr) do { \
+ bi_decl(bi_2pins_with_names(pin_intr, name" interrupt", \
+ pin_reset, name" reset")); \
+ _w5500_init(self, spi, pin_intr, pin_reset, eth_addr); \
+ } while (0)
+void _w5500_init(struct w5500 *self,
+ lo_interface spi spi, uint pin_intr, uint pin_reset,
+ struct net_eth_addr addr);
+
+/**
+ * Perform a hard reset on the chip (pull the reset line low).
+ *
+ * If you have any in-use sockets when you call this, you're going to
+ * have a bad time.
+ */
+void w5500_hard_reset(struct w5500 *self);
+
+/**
+ * Perform a soft reset on the chip (send the RST command).
+ *
+ * If you have any in-use sockets when you call this, you're going to
+ * have a bad time.
+ */
+void w5500_soft_reset(struct w5500 *self);
+
+#endif /* _LIBHW_W5500_H_ */