summaryrefslogtreecommitdiff
path: root/libhw
diff options
context:
space:
mode:
Diffstat (limited to 'libhw')
-rw-r--r--libhw/CMakeLists.txt40
-rw-r--r--libhw/common_alarmclock.c21
-rw-r--r--libhw/common_include/libhw/generic/alarmclock.h98
-rw-r--r--libhw/common_include/libhw/generic/net.h154
-rw-r--r--libhw/common_include/libhw/generic/spi.h47
-rw-r--r--libhw/host_alarmclock.c174
-rw-r--r--libhw/host_include/libhw/host_net.h47
-rw-r--r--libhw/host_net.c536
-rw-r--r--libhw/host_util.c21
-rw-r--r--libhw/host_util.h47
-rw-r--r--libhw/rp2040_hwspi.c123
-rw-r--r--libhw/rp2040_hwtimer.c166
-rw-r--r--libhw/rp2040_include/libhw/rp2040_hwspi.h66
-rw-r--r--libhw/rp2040_include/libhw/rp2040_hwtimer.h26
-rw-r--r--libhw/rp2040_include/libhw/w5500.h94
-rw-r--r--libhw/w5500.c836
-rw-r--r--libhw/w5500_ll.h363
17 files changed, 0 insertions, 2859 deletions
diff --git a/libhw/CMakeLists.txt b/libhw/CMakeLists.txt
deleted file mode 100644
index 3627641..0000000
--- a/libhw/CMakeLists.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-# libhw/CMakeLists.txt - TODO
-#
-# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-add_library(libhw INTERFACE)
-target_include_directories(libhw SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/common_include)
-target_link_libraries(libhw INTERFACE
- libcr
- libcr_ipc
- libmisc
-)
-
-target_sources(libhw INTERFACE
- common_alarmclock.c
-)
-
-if (PICO_PLATFORM STREQUAL "rp2040")
- target_include_directories(libhw SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/rp2040_include)
- target_sources(libhw INTERFACE
- rp2040_hwtimer.c
- rp2040_hwspi.c
- w5500.c
- )
- target_link_libraries(libhw INTERFACE
- hardware_gpio
- hardware_irq
- hardware_spi
- hardware_timer
- )
-endif()
-
-if (PICO_PLATFORM STREQUAL "host")
- target_include_directories(libhw SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/host_include)
- target_sources(libhw INTERFACE
- host_util.c
- host_alarmclock.c
- host_net.c
- )
-endif()
diff --git a/libhw/common_alarmclock.c b/libhw/common_alarmclock.c
deleted file mode 100644
index 9812d44..0000000
--- a/libhw/common_alarmclock.c
+++ /dev/null
@@ -1,21 +0,0 @@
-/* libhw/common_alarmclock.c - Device-independent <libhw/generic/alarmclock.h> utilities
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#include <libcr/coroutine.h>
-#include <libmisc/vcall.h>
-
-#include <libhw/generic/alarmclock.h>
-
-static void alarmclock_sleep_intrhandler(void *_arg) {
- cid_t *cid = _arg;
- cr_unpause_from_intrhandler(*cid);
-}
-
-void alarmclock_sleep_until_ns(implements_alarmclock *clock, uint64_t abstime_ns) {
- cid_t cid = cr_getcid();
- struct alarmclock_trigger trigger;
- VCALL(clock, add_trigger, &trigger, abstime_ns, alarmclock_sleep_intrhandler, &cid);
-}
diff --git a/libhw/common_include/libhw/generic/alarmclock.h b/libhw/common_include/libhw/generic/alarmclock.h
deleted file mode 100644
index a9d816b..0000000
--- a/libhw/common_include/libhw/generic/alarmclock.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/* libhw/generic/alarmclock.h - Device-independent alarmclock definitions
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#ifndef _LIBHW_GENERIC_ALARMCLOCK_H_
-#define _LIBHW_GENERIC_ALARMCLOCK_H_
-
-#include <stdbool.h> /* for bool */
-#include <stdint.h> /* for uint{n}_t and UINT{n}_C */
-
-#include <libmisc/private.h>
-#include <libmisc/vcall.h>
-
-/* Useful constants ***********************************************************/
-
-#define NS_PER_S UINT64_C(1000000000) /* needs at least log₂(10⁹) ≈ 29.9 bits */
-#define US_PER_S UINT64_C(1000000) /* needs at least log₂(10⁶) ≈ 19.9 bits */
-#define MS_PER_S UINT64_C(1000) /* needs at least log₂(10³) ≈ 9.9 bits */
-
-/* Structs ********************************************************************/
-
-struct alarmclock_trigger;
-struct alarmclock_trigger {
- BEGIN_PRIVATE(LIBHW_GENERIC_ALARMCLOCK_H)
- void *alarmclock;
- struct alarmclock_trigger *prev, *next;
-
- uint64_t fire_at_ns;
- void (*cb)(void *);
- void *cb_arg;
- END_PRIVATE(LIBHW_GENERIC_ALARMCLOCK_H)
-};
-
-/* Interface ******************************************************************/
-
-struct alarmclock_vtable;
-
-typedef struct {
- struct alarmclock_vtable *vtable;
-} implements_alarmclock;
-
-struct alarmclock_vtable {
- /**
- * (2⁶⁴-1 nanoseconds is more than 500 years; there is little
- * risk of this overflowing)
- */
- uint64_t (*get_time_ns)(implements_alarmclock *self);
-
- /**
- * Returns true on error.
- *
- * Implementations may return an error if fire_at_ns is more
- * than UINT32_MAX µs (72 minutes) in the future.
- *
- * If fire_at_ns is in the past, then it will fire
- * immediately.
- */
- bool (*add_trigger)(implements_alarmclock *self, struct alarmclock_trigger *trigger,
- uint64_t fire_at_ns,
- void (*cb)(void *),
- void *cb_arg);
-
- void (*del_trigger)(implements_alarmclock *self, struct alarmclock_trigger *trigger);
-};
-
-/* Utilities ******************************************************************/
-
-void alarmclock_sleep_until_ns(implements_alarmclock *clock, uint64_t abstime_ns);
-
-static inline void alarmclock_sleep_for_ns(implements_alarmclock *clock, uint64_t delta_ns) {
- alarmclock_sleep_until_ns(clock, VCALL(clock, get_time_ns) + delta_ns);
-}
-
-static inline void alarmclock_sleep_for_us(implements_alarmclock *clock, uint64_t delta_us) {
- alarmclock_sleep_for_ns(clock, delta_us * (NS_PER_S/US_PER_S));
-}
-
-static inline void alarmclock_sleep_for_ms(implements_alarmclock *clock, uint64_t delta_ms) {
- alarmclock_sleep_for_ns(clock, delta_ms * (NS_PER_S/MS_PER_S));
-}
-
-static inline void alarmclock_sleep_for_s(implements_alarmclock *clock, uint64_t delta_s) {
- alarmclock_sleep_for_ns(clock, delta_s * NS_PER_S);
-}
-
-/* Globals ********************************************************************/
-
-extern implements_alarmclock *bootclock;
-
-#define sleep_until_ns(t) alarmclock_sleep_until_ns(bootclock, t)
-#define sleep_for_ns(t) alarmclock_sleep_for_ns(bootclock, t)
-#define sleep_for_us(t) alarmclock_sleep_for_us(bootclock, t)
-#define sleep_for_ms(t) alarmclock_sleep_for_ms(bootclock, t)
-#define sleep_for_s(t) alarmclock_sleep_for_s(bootclock, t)
-
-#endif /* _LIBHW_GENERIC_ALARMCLOCK_H_ */
diff --git a/libhw/common_include/libhw/generic/net.h b/libhw/common_include/libhw/generic/net.h
deleted file mode 100644
index def533c..0000000
--- a/libhw/common_include/libhw/generic/net.h
+++ /dev/null
@@ -1,154 +0,0 @@
-/* libhw/generic/net.h - Device-independent network definitions
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#ifndef _LIBHW_GENERIC_NET_H_
-#define _LIBHW_GENERIC_NET_H_
-
-#include <stdbool.h> /* for bool */
-#include <stddef.h> /* for size_t */
-#include <stdint.h> /* for uint{n}_t} */
-#include <sys/types.h> /* for ssize_t */
-
-/* Errnos *********************************************************************/
-
-#define NET_EOTHER 1
-#define NET_ETIMEDOUT 2
-#define NET_ETHREAD 3
-#define NET_ECLOSED 4
-#define NET_EMSGSIZE 5
-
-/* Address types **************************************************************/
-
-struct net_ip4_addr {
- unsigned char octets[4];
-};
-
-static const struct net_ip4_addr net_ip4_addr_broadcast = {{255, 255, 255, 255}};
-static const struct net_ip4_addr net_ip4_addr_zero = {{0, 0, 0, 0}};
-
-struct net_eth_addr {
- unsigned char octets[6];
-};
-
-/* Streams (e.g. TCP) *********************************************************/
-
-struct net_stream_listener_vtable;
-struct net_stream_conn_vtable;
-
-typedef struct {
- struct net_stream_listener_vtable *vtable;
-} implements_net_stream_listener;
-
-typedef struct {
- struct net_stream_conn_vtable *vtable;
-} implements_net_stream_conn;
-
-struct net_stream_listener_vtable {
- /**
- * It is invalid to accept() a new connection if an existing
- * connection is still open.
- */
- implements_net_stream_conn *(*accept)(implements_net_stream_listener *self);
-
- /**
- * The net_stream_conn returned from accept() may still be
- * valid after the listener is closed.
- *
- * Return 0 on success, -errno on error.
- */
- int (*close)(implements_net_stream_listener *self);
-};
-
-struct net_stream_conn_vtable {
- /**
- * Return bytes-read on success, 0 on EOF, -errno on error; a
- * short read is *not* an error.
- */
- ssize_t (*read)(implements_net_stream_conn *self,
- void *buf, size_t count);
-
- /**
- * Set a timestamp after which calls to read() will return
- * NET_ETIMEDOUT. The timestamp is in nanoseconds on the
- * system monotonic clock, which is usually (on pico-sdk and
- * on the Linux kernel) nanoseconds-since-boot.
- *
- * A zero value disables the deadline.
- *
- * (2⁶⁴-1 nanoseconds is more than 500 years; there is little
- * risk of this overflowing)
- */
- void (*set_read_deadline)(implements_net_stream_conn *self,
- uint64_t ns_since_boot);
-
- /**
- * Return `count` on success, -errno on error; a short write *is* an
- * error.
- *
- * Writes are *not* guaranteed to be atomic (as this would be
- * expensive to implement), so if you have concurrent writers then you
- * should arrange for a mutex to protect the connection.
- */
- ssize_t (*write)(implements_net_stream_conn *self,
- void *buf, size_t count);
-
- /**
- * Return 0 on success, -errno on error.
- */
- int (*close)(implements_net_stream_conn *self,
- bool rd, bool wr);
-};
-
-/* Packets (e.g. UDP) *********************************************************/
-
-struct net_packet_conn_vtable;
-
-typedef struct {
- struct net_packet_conn_vtable *vtable;
-} implements_net_packet_conn;
-
-struct net_packet_conn_vtable {
- ssize_t (*sendto )(implements_net_packet_conn *self,
- void *buf, size_t len,
- struct net_ip4_addr node, uint16_t port);
- /**
- * @return The full length of the message, which may be more
- * than the given `len` (as if the Linux MSG_TRUNC flag were
- * given).
- */
- ssize_t (*recvfrom)(implements_net_packet_conn *self,
- void *buf, size_t len,
- struct net_ip4_addr *ret_node, uint16_t *ret_port);
- void (*set_read_deadline)(implements_net_packet_conn *self,
- uint64_t ns_since_boot);
- int (*close )(implements_net_packet_conn *self);
-};
-
-/* Interfaces *****************************************************************/
-
-struct net_iface_config {
- struct net_ip4_addr addr;
- struct net_ip4_addr gateway_addr;
- struct net_ip4_addr subnet_mask;
-};
-
-struct net_iface_vtable;
-
-typedef struct {
- struct net_iface_vtable *vtable;
-} implements_net_iface;
-
-struct net_iface_vtable {
- struct net_eth_addr (*hwaddr )(implements_net_iface *);
- void (*ifup )(implements_net_iface *, struct net_iface_config);
- void (*ifdown )(implements_net_iface *);
-
- implements_net_stream_listener *(*tcp_listen)(implements_net_iface *, uint16_t local_port);
- implements_net_stream_conn *(*tcp_dial )(implements_net_iface *, struct net_ip4_addr remote_node, uint16_t remote_port);
- implements_net_packet_conn *(*udp_conn )(implements_net_iface *, uint16_t local_port);
-};
-
-#endif /* _LIBHW_GENERIC_NET_H_ */
diff --git a/libhw/common_include/libhw/generic/spi.h b/libhw/common_include/libhw/generic/spi.h
deleted file mode 100644
index 2207a2c..0000000
--- a/libhw/common_include/libhw/generic/spi.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/* libhw/generic/spi.h - Device-independent SPI definitions
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#ifndef _LIBHW_GENERIC_SPI_H_
-#define _LIBHW_GENERIC_SPI_H_
-
-#include <stddef.h> /* for size_t */
-
-enum spi_mode {
- SPI_MODE_0 = 0, /* clk_polarity=0 (idle low), clk_phase=0 (sample on rise) */
- SPI_MODE_1 = 1, /* clk_polarity=0 (idle low), clk_phase=1 (sample on fall) */
- SPI_MODE_2 = 2, /* clk_polarity=1 (idle high), clk_phase=0 (sample on rise) */
- SPI_MODE_3 = 3, /* clk_polarity=1 (idle high), clk_phase=1 (sample on fall) */
-};
-
-struct bidi_iovec {
- void *iov_read_dst;
- void *iov_write_src;
- size_t iov_len;
-};
-
-struct spi_vtable;
-
-typedef struct {
- struct spi_vtable *vtable;
-} implements_spi;
-
-/* This API assumes that an SPI frame is a multiple of 8-bits.
- *
- * It is my understanding that this is a common constraint of SPI
- * hardware, and that the RP2040 is somewhat unusual in that it allows
- * frames of any length 4-16 bits (we disconnect the CS pin from the
- * PL022 SSP and manually GPIO it from the CPU in order to achieve
- * longer frames).
- *
- * But, more relevantly: The W5500's protocol uses frames that are 4-N
- * octets; so we have no need for an API that allows a
- * non-multiple-of-8 number of bits.
- */
-struct spi_vtable {
- void (*readwritev)(implements_spi *, const struct bidi_iovec *iov, int iovcnt);
-};
-
-#endif /* _LIBHW_GENERIC_SPI_H_ */
diff --git a/libhw/host_alarmclock.c b/libhw/host_alarmclock.c
deleted file mode 100644
index 141927c..0000000
--- a/libhw/host_alarmclock.c
+++ /dev/null
@@ -1,174 +0,0 @@
-/* libhw/host_alarmclock.c - <libhw/generic/alarmclock.h> implementation for POSIX hosts
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#include <assert.h>
-#include <errno.h>
-#include <error.h>
-#include <signal.h>
-#include <time.h>
-
-#include <libcr/coroutine.h>
-#include <libmisc/vcall.h>
-
-#define IMPLEMENTATION_FOR_LIBHW_GENERIC_ALARMCLOCK_H YES
-#include <libhw/generic/alarmclock.h>
-
-#include "host_util.h" /* for host_sigrt_alloc(), ns_to_host_ns_time() */
-
-/* Types **********************************************************************/
-
-struct hostclock {
- implements_alarmclock;
- bool initialized;
- clockid_t clock_id;
- timer_t timer_id;
- struct alarmclock_trigger *queue;
-};
-
-/* Globals ********************************************************************/
-
-static uint64_t hostclock_get_time_ns(implements_alarmclock *self);
-static bool hostclock_add_trigger(implements_alarmclock *self,
- struct alarmclock_trigger *trigger,
- uint64_t fire_at_ns,
- void (*cb)(void *),
- void *cb_arg);
-static void hostclock_del_trigger(implements_alarmclock *self,
- struct alarmclock_trigger *trigger);
-
-static struct alarmclock_vtable hostclock_vtable = {
- .get_time_ns = hostclock_get_time_ns,
- .add_trigger = hostclock_add_trigger,
- .del_trigger = hostclock_del_trigger,
-};
-
-static struct hostclock clock_monotonic = {
- .vtable = &hostclock_vtable,
- .clock_id = CLOCK_MONOTONIC,
-};
-
-implements_alarmclock *bootclock = &clock_monotonic;
-
-/* Main implementation ********************************************************/
-
-#define UNUSED(name) /* name __attribute__ ((unused)) */
-
-static uint64_t hostclock_get_time_ns(implements_alarmclock *_alarmclock) {
- struct hostclock *alarmclock =
- VCALL_SELF(struct hostclock, implements_alarmclock, _alarmclock);
- assert(alarmclock);
-
- struct timespec ts;
-
- if (clock_gettime(alarmclock->clock_id, &ts) != 0)
- error(1, errno, "clock_gettime(%d)", (int)alarmclock->clock_id);
-
- return ns_from_host_ns_time(ts);
-}
-
-static void hostclock_handle_sig_alarm(int UNUSED(sig), siginfo_t *info, void *UNUSED(ucontext)) {
- struct hostclock *alarmclock = info->si_value.sival_ptr;
- assert(alarmclock);
-
- while (alarmclock->queue &&
- alarmclock->queue->fire_at_ns <= hostclock_get_time_ns(alarmclock)) {
- struct alarmclock_trigger *trigger = alarmclock->queue;
- trigger->cb(trigger->cb_arg);
- alarmclock->queue = trigger->next;
- trigger->alarmclock = NULL;
- trigger->next = NULL;
- trigger->prev = NULL;
- }
-
- if (alarmclock->queue) {
- struct itimerspec alarmspec = {
- .it_value = ns_to_host_ns_time(alarmclock->queue->fire_at_ns),
- .it_interval = {0},
- };
- if (timer_settime(alarmclock->timer_id, TIMER_ABSTIME, &alarmspec, NULL) != 0)
- error(1, errno, "timer_settime");
- }
-}
-
-static bool hostclock_add_trigger(implements_alarmclock *_alarmclock,
- struct alarmclock_trigger *trigger,
- uint64_t fire_at_ns,
- void (*cb)(void *),
- void *cb_arg) {
- struct hostclock *alarmclock =
- VCALL_SELF(struct hostclock, implements_alarmclock, _alarmclock);
- assert(alarmclock);
- assert(trigger);
- assert(fire_at_ns);
- assert(cb);
-
- trigger->alarmclock = alarmclock;
- trigger->fire_at_ns = fire_at_ns;
- trigger->cb = cb;
- trigger->cb_arg = cb_arg;
-
- cr_disable_interrupts();
- struct alarmclock_trigger **dst = &alarmclock->queue;
- while (*dst && fire_at_ns >= (*dst)->fire_at_ns)
- dst = &(*dst)->next;
- trigger->next = *dst;
- trigger->prev = *dst ? (*dst)->prev : NULL;
- if (*dst)
- (*dst)->prev = trigger;
- *dst = trigger;
- if (!alarmclock->initialized) {
- struct sigevent how_to_notify = {
- .sigev_notify = SIGEV_SIGNAL,
- .sigev_signo = host_sigrt_alloc(),
- .sigev_value = {
- .sival_ptr = alarmclock,
- },
- };
- struct sigaction action = {
- .sa_flags = SA_SIGINFO,
- .sa_sigaction = hostclock_handle_sig_alarm,
- };
- if (sigaction(how_to_notify.sigev_signo, &action, NULL) != 0)
- error(1, errno, "sigaction");
- if (timer_create(alarmclock->clock_id, &how_to_notify, &alarmclock->timer_id) != 0)
- error(1, errno, "timer_create(%d)", (int)alarmclock->clock_id);
- alarmclock->initialized = true;
- }
- if (alarmclock->queue == trigger) {
- struct itimerspec alarmspec = {
- .it_value = ns_to_host_ns_time(trigger->fire_at_ns),
- .it_interval = {0},
- };
- if (timer_settime(alarmclock->timer_id, TIMER_ABSTIME, &alarmspec, NULL) != 0)
- error(1, errno, "timer_settime");
- }
- cr_enable_interrupts();
-
- return false;
-}
-
-static void hostclock_del_trigger(implements_alarmclock *_alarmclock,
- struct alarmclock_trigger *trigger) {
- struct hostclock *alarmclock =
- VCALL_SELF(struct hostclock, implements_alarmclock, _alarmclock);
-
- assert(alarmclock);
- assert(trigger);
-
- cr_disable_interrupts();
- if (trigger->alarmclock == alarmclock) {
- if (!trigger->prev)
- alarmclock->queue = trigger->next;
- else
- trigger->prev->next = trigger->next;
- if (trigger->next)
- trigger->next->prev = trigger->prev;
- trigger->alarmclock = NULL;
- trigger->prev = NULL;
- trigger->next = NULL;
- }
- cr_enable_interrupts();
-}
diff --git a/libhw/host_include/libhw/host_net.h b/libhw/host_include/libhw/host_net.h
deleted file mode 100644
index bfef5c9..0000000
--- a/libhw/host_include/libhw/host_net.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/* libhw/host_net.h - <libhw/generic/net.h> implementation for hosted glibc
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#ifndef _LIBHW_HOST_NET_H_
-#define _LIBHW_HOST_NET_H_
-
-#include <stdint.h> /* for uint16_6 */
-
-#include <libmisc/private.h>
-
-#include <libhw/generic/net.h>
-
-struct _hostnet_tcp_conn {
- implements_net_stream_conn;
-
- BEGIN_PRIVATE(LIBHW_HOST_NET_H)
- int fd;
- uint64_t read_deadline_ns;
- END_PRIVATE(LIBHW_HOST_NET_H)
-};
-
-struct hostnet_tcp_listener {
- implements_net_stream_listener;
-
- BEGIN_PRIVATE(LIBHW_HOST_NET_H)
- int fd;
- struct _hostnet_tcp_conn active_conn;
- END_PRIVATE(LIBHW_HOST_NET_H)
-};
-
-void hostnet_tcp_listener_init(struct hostnet_tcp_listener *self, uint16_t port);
-
-struct hostnet_udp_conn {
- implements_net_packet_conn;
-
- BEGIN_PRIVATE(LIBHW_HOST_NET_H)
- int fd;
- uint64_t read_deadline_ns;
- END_PRIVATE(LIBHW_HOST_NET_H)
-};
-
-void hostnet_udp_conn_init(struct hostnet_udp_conn *self, uint16_t port);
-
-#endif /* _LIBHW_HOST_NET_H_ */
diff --git a/libhw/host_net.c b/libhw/host_net.c
deleted file mode 100644
index 88bda49..0000000
--- a/libhw/host_net.c
+++ /dev/null
@@ -1,536 +0,0 @@
-/* libhw/host_net.c - <libhw/generic/net.h> implementation for hosted glibc
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#define _GNU_SOURCE /* for pthread_sigqueue(3gnu) */
-/* misc */
-#include <assert.h> /* for assert() */
-#include <errno.h> /* for errno, EAGAIN, EINVAL */
-#include <error.h> /* for error(3gnu) */
-#include <stdlib.h> /* for abs(), shutdown(), SHUT_RD, SHUT_WR, SHUT_RDWR */
-#include <unistd.h> /* for read(), write() */
-/* net */
-#include <arpa/inet.h> /* for htons(3p) */
-#include <netinet/in.h> /* for struct sockaddr_in */
-#include <sys/socket.h> /* for struct sockaddr{,_storage}, SOCK_*, SOL_*, SO_*, socket(), setsockopt(), bind(), listen(), accept() */
-/* async */
-#include <pthread.h> /* for pthread_* */
-#include <signal.h> /* for siginfo_t, struct sigaction, enum sigval, sigaction(), SA_SIGINFO */
-
-#include <libcr/coroutine.h>
-#include <libmisc/vcall.h>
-
-#include <libhw/generic/alarmclock.h>
-
-#define IMPLEMENTATION_FOR_LIBHW_HOST_NET_H YES
-#include <libhw/host_net.h>
-
-#include "host_util.h" /* for host_sigrt_alloc(), ns_to_host_us_time() */
-
-/* common *********************************************************************/
-
-#define UNUSED(name) /* name __attribute__ ((unused)) */
-
-static int hostnet_sig_io = 0;
-
-static void hostnet_handle_sig_io(int UNUSED(sig), siginfo_t *info, void *UNUSED(ucontext)) {
- cr_unpause_from_intrhandler((cid_t)info->si_value.sival_int);
-}
-
-static void hostnet_init(void) {
- struct sigaction action = {0};
-
- if (hostnet_sig_io)
- return;
-
- hostnet_sig_io = host_sigrt_alloc();
-
- action.sa_flags = SA_SIGINFO;
- action.sa_sigaction = hostnet_handle_sig_io;
- if (sigaction(hostnet_sig_io, &action, NULL) < 0)
- error(1, errno, "sigaction");
-}
-
-#define WAKE_COROUTINE(args) do { \
- int r; \
- union sigval val = {0}; \
- val.sival_int = (int)((args)->cr_coroutine); \
- do { \
- r = pthread_sigqueue((args)->cr_thread, \
- hostnet_sig_io, \
- val); \
- assert(r == 0 || r == EAGAIN); \
- } while (r == EAGAIN); \
- } while (0)
-
-static inline bool RUN_PTHREAD(void *(*fn)(void *), void *args) {
- pthread_t thread;
- if (pthread_create(&thread, NULL, fn, args))
- return true;
- cr_pause_and_yield();
- if (pthread_join(thread, NULL))
- return true;
- return false;
-}
-
-static inline ssize_t hostnet_map_negerrno(ssize_t v) {
- if (v >= 0)
- return v;
- switch (v) {
- case -ETIMEDOUT:
- return -NET_ETIMEDOUT;
- case -EBADF:
- return -NET_ECLOSED;
- case -EMSGSIZE:
- return -NET_EMSGSIZE;
- default:
- return -NET_EOTHER;
- }
-}
-
-/* TCP init() ( AKA socket(3) + listen(3) )************************************/
-
-static implements_net_stream_conn *hostnet_tcplist_accept(implements_net_stream_listener *);
-static int hostnet_tcplist_close(implements_net_stream_listener *);
-static void hostnet_tcp_set_read_deadline(implements_net_stream_conn *conn, uint64_t ts_ns);
-static ssize_t hostnet_tcp_read(implements_net_stream_conn *conn, void *buf, size_t count);
-static ssize_t hostnet_tcp_write(implements_net_stream_conn *conn, void *buf, size_t count);
-static int hostnet_tcp_close(implements_net_stream_conn *conn, bool rd, bool wr);
-
-static struct net_stream_listener_vtable hostnet_tcp_listener_vtable = {
- .accept = hostnet_tcplist_accept,
- .close = hostnet_tcplist_close,
-};
-
-static struct net_stream_conn_vtable hostnet_tcp_conn_vtable = {
- .set_read_deadline = hostnet_tcp_set_read_deadline,
- .read = hostnet_tcp_read,
- .write = hostnet_tcp_write,
- .close = hostnet_tcp_close,
-};
-
-void hostnet_tcp_listener_init(struct hostnet_tcp_listener *self, uint16_t port) {
- int listenerfd;
- union {
- struct sockaddr_in in;
- struct sockaddr gen;
- } addr = { 0 };
-
- hostnet_init();
-
- addr.in.sin_family = AF_INET;
- addr.in.sin_port = htons(port);
- listenerfd = socket(AF_INET, SOCK_STREAM, 0);
- if (listenerfd < 0)
- error(1, errno, "socket");
- if (setsockopt(listenerfd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) < 0)
- error(1, errno, "setsockopt(fd=%d, SO_REUSEADDR=1)", listenerfd);
- if (setsockopt(listenerfd, SOL_SOCKET, SO_REUSEPORT, &(int){1}, sizeof(int)) < 0)
- error(1, errno, "setsockopt(fd=%d, SO_REUSEPORT=1)", listenerfd);
- if (bind(listenerfd, &addr.gen, sizeof addr) < 0)
- error(1, errno, "bind(fd=%d)", listenerfd);
- if (listen(listenerfd, 0) < 0)
- error(1, errno, "listen(fd=%d)", listenerfd);
-
- self->vtable = &hostnet_tcp_listener_vtable;
- self->fd = listenerfd;
-}
-
-/* TCP listener accept() ******************************************************/
-
-struct hostnet_pthread_accept_args {
- pthread_t cr_thread;
- cid_t cr_coroutine;
-
- int listenerfd;
-
- int *ret_connfd;
-};
-
-static void *hostnet_pthread_accept(void *_args) {
- struct hostnet_pthread_accept_args *args = _args;
- *(args->ret_connfd) = accept(args->listenerfd, NULL, NULL);
- if (*(args->ret_connfd) < 0)
- *(args->ret_connfd) = -errno;
- WAKE_COROUTINE(args);
- return NULL;
-}
-
-static implements_net_stream_conn *hostnet_tcplist_accept(implements_net_stream_listener *_listener) {
- struct hostnet_tcp_listener *listener =
- VCALL_SELF(struct hostnet_tcp_listener, implements_net_stream_listener, _listener);
- assert(listener);
-
- int ret_connfd;
- struct hostnet_pthread_accept_args args = {
- .cr_thread = pthread_self(),
- .cr_coroutine = cr_getcid(),
- .listenerfd = listener->fd,
- .ret_connfd = &ret_connfd,
- };
- if (RUN_PTHREAD(hostnet_pthread_accept, &args))
- return NULL;
-
- listener->active_conn.vtable = &hostnet_tcp_conn_vtable;
- listener->active_conn.fd = ret_connfd;
- return &listener->active_conn;
-}
-
-/* TCP listener close() *******************************************************/
-
-static int hostnet_tcplist_close(implements_net_stream_listener *_listener) {
- struct hostnet_tcp_listener *listener =
- VCALL_SELF(struct hostnet_tcp_listener, implements_net_stream_listener, _listener);
- assert(listener);
-
- return hostnet_map_negerrno(close(listener->fd) ? -errno : 0);
-}
-
-/* TCP read() *****************************************************************/
-
-static void hostnet_tcp_set_read_deadline(implements_net_stream_conn *_conn, uint64_t ts_ns) {
- struct _hostnet_tcp_conn *conn =
- VCALL_SELF(struct _hostnet_tcp_conn, implements_net_stream_conn, _conn);
- assert(conn);
-
- conn->read_deadline_ns = ts_ns;
-}
-
-struct hostnet_pthread_read_args {
- pthread_t cr_thread;
- cid_t cr_coroutine;
-
- int connfd;
- struct timeval timeout;
- void *buf;
- size_t count;
-
- ssize_t *ret;
-};
-
-static void *hostnet_pthread_read(void *_args) {
- struct hostnet_pthread_read_args *args = _args;
-
- *(args->ret) = setsockopt(args->connfd, SOL_SOCKET, SO_RCVTIMEO,
- &args->timeout, sizeof(args->timeout));
- if (*(args->ret) < 0)
- goto end;
-
- *(args->ret) = read(args->connfd, args->buf, args->count);
- if (*(args->ret) < 0)
- goto end;
-
- end:
- if (*(args->ret) < 0)
- *(args->ret) = hostnet_map_negerrno(-errno);
- WAKE_COROUTINE(args);
- return NULL;
-}
-
-static ssize_t hostnet_tcp_read(implements_net_stream_conn *_conn, void *buf, size_t count) {
- struct _hostnet_tcp_conn *conn =
- VCALL_SELF(struct _hostnet_tcp_conn, implements_net_stream_conn, _conn);
- assert(conn);
-
- ssize_t ret;
- struct hostnet_pthread_read_args args = {
- .cr_thread = pthread_self(),
- .cr_coroutine = cr_getcid(),
-
- .connfd = conn->fd,
- .buf = buf,
- .count = count,
-
- .ret = &ret,
- };
- if (conn->read_deadline_ns) {
- uint64_t now_ns = VCALL(bootclock, get_time_ns);
- if (conn->read_deadline_ns < now_ns)
- return -NET_ETIMEDOUT;
- args.timeout = ns_to_host_us_time(conn->read_deadline_ns-now_ns);
- } else {
- args.timeout = (host_us_time_t){0};
- }
-
- if (RUN_PTHREAD(hostnet_pthread_read, &args))
- return -NET_ETHREAD;
- return ret;
-}
-
-/* TCP write() ****************************************************************/
-
-struct hostnet_pthread_write_args {
- pthread_t cr_thread;
- cid_t cr_coroutine;
-
- int connfd;
- void *buf;
- size_t count;
-
- ssize_t *ret;
-};
-
-static void *hostnet_pthread_write(void *_args) {
- struct hostnet_pthread_read_args *args = _args;
- size_t done = 0;
- while (done < args->count) {
- ssize_t r = write(args->connfd, args->buf, args->count);
- if (r < 0) {
- hostnet_map_negerrno(-errno);
- break;
- }
- done += r;
- }
- if (done == args->count)
- *(args->ret) = done;
- WAKE_COROUTINE(args);
- return NULL;
-}
-
-static ssize_t hostnet_tcp_write(implements_net_stream_conn *_conn, void *buf, size_t count) {
- struct _hostnet_tcp_conn *conn =
- VCALL_SELF(struct _hostnet_tcp_conn, implements_net_stream_conn, _conn);
- assert(conn);
-
- ssize_t ret;
- struct hostnet_pthread_write_args args = {
- .cr_thread = pthread_self(),
- .cr_coroutine = cr_getcid(),
-
- .connfd = conn->fd,
- .buf = buf,
- .count = count,
-
- .ret = &ret,
- };
- if (RUN_PTHREAD(hostnet_pthread_write, &args))
- return -NET_ETHREAD;
- return ret;
-}
-
-/* TCP close() ****************************************************************/
-
-static int hostnet_tcp_close(implements_net_stream_conn *_conn, bool rd, bool wr) {
- struct _hostnet_tcp_conn *conn =
- VCALL_SELF(struct _hostnet_tcp_conn, implements_net_stream_conn, _conn);
- assert(conn);
-
- int how;
- if (rd && wr)
- how = SHUT_RDWR;
- else if (rd && !wr)
- how = SHUT_RD;
- else if (!rd && wr)
- how = SHUT_WR;
- else
- assert(false);
- return hostnet_map_negerrno(shutdown(conn->fd, how) ? -errno : 0);
-}
-
-/* UDP init() *****************************************************************/
-
-static void hostnet_udp_set_read_deadline(implements_net_packet_conn *self,
- uint64_t ts_ns);
-static ssize_t hostnet_udp_sendto(implements_net_packet_conn *self, void *buf, size_t len,
- struct net_ip4_addr addr, uint16_t port);
-static ssize_t hostnet_udp_recvfrom(implements_net_packet_conn *self, void *buf, size_t len,
- struct net_ip4_addr *ret_addr, uint16_t *ret_port);
-static int hostnet_udp_close(implements_net_packet_conn *self);
-
-static struct net_packet_conn_vtable hostnet_udp_conn_vtable = {
- .set_read_deadline = hostnet_udp_set_read_deadline,
- .sendto = hostnet_udp_sendto,
- .recvfrom = hostnet_udp_recvfrom,
- .close = hostnet_udp_close,
-};
-
-void hostnet_udp_conn_init(struct hostnet_udp_conn *self, uint16_t port) {
- int fd;
- union {
- struct sockaddr_in in;
- struct sockaddr gen;
- struct sockaddr_storage stor;
- } addr = { 0 };
-
- hostnet_init();
-
- addr.in.sin_family = AF_INET;
- addr.in.sin_port = htons(port);
- fd = socket(AF_INET, SOCK_DGRAM, 0);
- if (fd < 0)
- error(1, errno, "socket");
- if (bind(fd, &addr.gen, sizeof addr) < 0)
- error(1, errno, "bind");
-
- self->vtable = &hostnet_udp_conn_vtable;
- self->fd = fd;
-}
-
-/* UDP sendto() ***************************************************************/
-
-struct hostnet_pthread_sendto_args {
- pthread_t cr_thread;
- cid_t cr_coroutine;
-
- int connfd;
- void *buf;
- size_t count;
- struct net_ip4_addr node;
- uint16_t port;
-
- ssize_t *ret;
-};
-
-static void *hostnet_pthread_sendto(void *_args) {
- struct hostnet_pthread_sendto_args *args = _args;
- union {
- struct sockaddr_in in;
- struct sockaddr gen;
- struct sockaddr_storage stor;
- } addr = { 0 };
-
- addr.in.sin_family = AF_INET;
- addr.in.sin_addr.s_addr =
- (((uint32_t)args->node.octets[0])<<24) |
- (((uint32_t)args->node.octets[1])<<16) |
- (((uint32_t)args->node.octets[2])<< 8) |
- (((uint32_t)args->node.octets[3])<< 0) ;
- addr.in.sin_port = htons(args->port);
- *(args->ret) = sendto(args->connfd, args->buf, args->count, 0, &addr.gen, sizeof(addr));
- if (*(args->ret) < 0)
- *(args->ret) = hostnet_map_negerrno(-errno);
- WAKE_COROUTINE(args);
- return NULL;
-}
-
-static ssize_t hostnet_udp_sendto(implements_net_packet_conn *_conn, void *buf, size_t count,
- struct net_ip4_addr node, uint16_t port) {
- struct hostnet_udp_conn *conn =
- VCALL_SELF(struct hostnet_udp_conn, implements_net_packet_conn, _conn);
- assert(conn);
-
- ssize_t ret;
- struct hostnet_pthread_sendto_args args = {
- .cr_thread = pthread_self(),
- .cr_coroutine = cr_getcid(),
-
- .connfd = conn->fd,
- .buf = buf,
- .count = count,
- .node = node,
- .port = port,
-
- .ret = &ret,
- };
- if (RUN_PTHREAD(hostnet_pthread_sendto, &args))
- return -NET_ETHREAD;
- return ret;
-}
-
-/* UDP recvfrom() *************************************************************/
-
-static void hostnet_udp_set_read_deadline(implements_net_packet_conn *_conn,
- uint64_t ts_ns) {
- struct hostnet_udp_conn *conn =
- VCALL_SELF(struct hostnet_udp_conn, implements_net_packet_conn, _conn);
- assert(conn);
-
- conn->read_deadline_ns = ts_ns;
-}
-
-struct hostnet_pthread_recvfrom_args {
- pthread_t cr_thread;
- cid_t cr_coroutine;
-
- int connfd;
- struct timeval timeout;
- void *buf;
- size_t count;
-
- ssize_t *ret_size;
- struct net_ip4_addr *ret_node;
- uint16_t *ret_port;
-};
-
-static void *hostnet_pthread_recvfrom(void *_args) {
- struct hostnet_pthread_recvfrom_args *args = _args;
-
- union {
- struct sockaddr_in in;
- struct sockaddr gen;
- struct sockaddr_storage stor;
- } addr = { 0 };
- socklen_t addr_size;
-
- *(args->ret_size) = setsockopt(args->connfd, SOL_SOCKET, SO_RCVTIMEO,
- &args->timeout, sizeof(args->timeout));
- if (*(args->ret_size) < 0)
- goto end;
-
- *(args->ret_size) = recvfrom(args->connfd, args->buf, args->count,
- MSG_TRUNC, &addr.gen, &addr_size);
- if (*(args->ret_size) < 0)
- goto end;
-
- assert(addr.in.sin_family == AF_INET);
- if (args->ret_node) {
- args->ret_node->octets[0] = (addr.in.sin_addr.s_addr >> 24) & 0xFF;
- args->ret_node->octets[1] = (addr.in.sin_addr.s_addr >> 16) & 0xFF;
- args->ret_node->octets[2] = (addr.in.sin_addr.s_addr >> 8) & 0xFF;
- args->ret_node->octets[3] = (addr.in.sin_addr.s_addr >> 0) & 0xFF;
- }
- if (args->ret_port) {
- (*args->ret_port) = ntohs(addr.in.sin_port);
- }
-
- end:
- if (*(args->ret_size) < 0)
- *(args->ret_size) = hostnet_map_negerrno(-errno);
- WAKE_COROUTINE(args);
- return NULL;
-}
-
-static ssize_t hostnet_udp_recvfrom(implements_net_packet_conn *_conn, void *buf, size_t count,
- struct net_ip4_addr *ret_node, uint16_t *ret_port) {
- struct hostnet_udp_conn *conn =
- VCALL_SELF(struct hostnet_udp_conn, implements_net_packet_conn, _conn);
- assert(conn);
-
- ssize_t ret;
- struct hostnet_pthread_recvfrom_args args = {
- .cr_thread = pthread_self(),
- .cr_coroutine = cr_getcid(),
-
- .connfd = conn->fd,
- .buf = buf,
- .count = count,
-
- .ret_size = &ret,
- .ret_node = ret_node,
- .ret_port = ret_port,
- };
- if (conn->read_deadline_ns) {
- uint64_t now_ns = VCALL(bootclock, get_time_ns);
- if (conn->read_deadline_ns < now_ns)
- return -NET_ETIMEDOUT;
- args.timeout = ns_to_host_us_time(conn->read_deadline_ns-now_ns);
- } else {
- args.timeout = (host_us_time_t){0};
- }
-
- if (RUN_PTHREAD(hostnet_pthread_recvfrom, &args))
- return -NET_ETHREAD;
- return ret;
-}
-
-/* UDP close() ****************************************************************/
-
-static int hostnet_udp_close(implements_net_packet_conn *_conn) {
- struct hostnet_udp_conn *conn =
- VCALL_SELF(struct hostnet_udp_conn, implements_net_packet_conn, _conn);
- assert(conn);
-
- return hostnet_map_negerrno(close(conn->fd) ? -errno : 0);
-}
diff --git a/libhw/host_util.c b/libhw/host_util.c
deleted file mode 100644
index b862e39..0000000
--- a/libhw/host_util.c
+++ /dev/null
@@ -1,21 +0,0 @@
-/* libhw/host_util.c - Utilities for GNU/Linux hosts
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#include <error.h> /* for error(3gnu) */
-#include <signal.h> /* for SIGRTMIN, SIGRTMAX */
-
-#include "host_util.h"
-
-int host_sigrt_alloc(void) {
- static int next = 0;
-
- if (!next)
- next = SIGRTMIN;
- int ret = next++;
- if (ret > SIGRTMAX)
- error(1, 0, "SIGRTMAX exceeded");
- return ret;
-}
diff --git a/libhw/host_util.h b/libhw/host_util.h
deleted file mode 100644
index edb6da4..0000000
--- a/libhw/host_util.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/* libhw/host_util.h - Utilities for GNU/Linux hosts
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#ifndef _LIBHW_HOST_SIGRT_H_
-#define _LIBHW_HOST_SIGRT_H_
-
-#include <time.h> /* for struct timespec */
-#include <sys/time.h> /* for struct timeval */
-
-#include <libhw/generic/alarmclock.h> /* for {X}S_PER_S */
-
-int host_sigrt_alloc(void);
-
-typedef struct timeval host_us_time_t;
-typedef struct timespec host_ns_time_t;
-
-static inline host_us_time_t ns_to_host_us_time(uint64_t time_ns) {
- host_us_time_t ret;
- ret.tv_sec = time_ns
- /NS_PER_S;
- ret.tv_usec = (time_ns - ((uint64_t)ret.tv_sec)*NS_PER_S)
- /(NS_PER_S/US_PER_S);
- return ret;
-}
-
-static inline host_ns_time_t ns_to_host_ns_time(uint64_t time_ns) {
- host_ns_time_t ret;
- ret.tv_sec = time_ns
- /NS_PER_S;
- ret.tv_nsec = time_ns - ((uint64_t)ret.tv_sec)*NS_PER_S;
- return ret;
-}
-
-static inline uint64_t ns_from_host_us_time(host_us_time_t host_time) {
- return (((uint64_t)host_time.tv_sec) * NS_PER_S) +
- ((uint64_t)host_time.tv_usec * (NS_PER_S/US_PER_S));
-}
-
-static inline uint64_t ns_from_host_ns_time(host_ns_time_t host_time) {
- return (((uint64_t)host_time.tv_sec) * NS_PER_S) +
- ((uint64_t)host_time.tv_nsec);
-}
-
-#endif /* _LIBHW_HOST_SIGRT_H_ */
diff --git a/libhw/rp2040_hwspi.c b/libhw/rp2040_hwspi.c
deleted file mode 100644
index 4edcdf7..0000000
--- a/libhw/rp2040_hwspi.c
+++ /dev/null
@@ -1,123 +0,0 @@
-/* libhw/rp2040_hwspi.c - <libhw/generic/spi.h> implementation for the RP2040's ARM Primecell SSP (PL022)
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#include <assert.h>
-
-#include <hardware/spi.h> /* pico-sdk:hardware_spi */
-#include <hardware/gpio.h> /* pico-sdk:hardware_gpio */
-
-#include <libmisc/vcall.h>
-
-#define IMPLEMENTATION_FOR_LIBHW_RP2040_HWSPI_H YES
-#include <libhw/rp2040_hwspi.h>
-
-static void rp2040_hwspi_readwritev(implements_spi *, const struct bidi_iovec *iov, int iovcnt);
-
-struct spi_vtable rp2040_hwspi_vtable = {
- .readwritev = rp2040_hwspi_readwritev,
-};
-
-void _rp2040_hwspi_init(struct rp2040_hwspi *self,
- enum rp2040_hwspi_instance inst_num,
- enum spi_mode mode,
- uint baudrate_hz,
- uint pin_miso,
- uint pin_mosi,
- uint pin_clk,
- uint pin_cs) {
- /* Be not weary: This is but 12 lines of actual code; and many
- * lines of comments and assert()s. */
- spi_inst_t *inst;
-
- assert(self);
- assert(baudrate_hz);
- assert(pin_miso != pin_mosi);
- assert(pin_miso != pin_clk);
- assert(pin_miso != pin_cs);
- assert(pin_mosi != pin_clk);
- assert(pin_mosi != pin_cs);
- assert(pin_clk != pin_cs);
-
- /* I know we called this "hwspi", but we're actually going to
- * disconnect the CS pin from the PL022 SSP and manually drive
- * it from software. This is because the PL022 has a maximum
- * of 16-bit frames, while we need to be able to do *at least*
- * 32-bit frames (and ideally, much larger). By managing it
- * ourselves, we can just keep CS pulled low extra-long,
- * making the frame extra-long. */
-
- /* Regarding the constraints on pin assignments: see the
- * RP2040 datasheet, table 2, in §1.4.3 "GPIO Functions". */
- switch (inst_num) {
- case RP2040_HWSPI_0:
- inst = spi0;
- assert(pin_miso == 0 || pin_miso == 4 || pin_miso == 16 || pin_miso == 20);
- /*assert(pin_cs == 1 || pin_cs == 5 || pin_cs == 17 || pin_cs == 21);*/
- assert(pin_clk == 2 || pin_clk == 6 || pin_clk == 18 || pin_clk == 22);
- assert(pin_mosi == 3 || pin_mosi == 7 || pin_mosi == 19 || pin_mosi == 23);
- break;
- case RP2040_HWSPI_1:
- inst = spi1;
- assert(pin_miso == 8 || pin_miso == 12 || pin_miso == 24 || pin_miso == 28);
- /*assert(pin_cs == 9 || pin_cs == 13 || pin_cs == 25 || pin_cs == 29);*/
- assert(pin_clk == 10 || pin_clk == 14 || pin_clk == 26);
- assert(pin_mosi == 11 || pin_mosi == 15 || pin_mosi == 27);
- break;
- default:
- assert(false);
- }
-
- spi_init(inst, baudrate_hz);
- spi_set_format(inst, 8,
- (mode & 0b10) ? SPI_CPOL_1 : SPI_CPOL_0,
- (mode & 0b01) ? SPI_CPHA_1 : SPI_CPHA_0,
- SPI_MSB_FIRST);
-
- /* Connect the pins to the PL022; set them each to "function
- * 1" (again, see the RP2040 datasheet, table 2, in §1.4.3
- * "GPIO Functions").
- *
- * ("GPIO_FUNC_SPI" is how the pico-sdk spells "function 1",
- * since on the RP2040 all of the "function 1" functions are
- * some part of SPI.) */
- gpio_set_function(pin_clk, GPIO_FUNC_SPI);
- gpio_set_function(pin_mosi, GPIO_FUNC_SPI);
- gpio_set_function(pin_miso, GPIO_FUNC_SPI);
-
- /* Initialize the CS pin for software control. */
- gpio_init(pin_cs);
- gpio_set_dir(pin_cs, GPIO_OUT);
- gpio_put(pin_cs, 1);
-
- /* Return. */
- self->vtable = &rp2040_hwspi_vtable;
- self->inst = inst;
- self->pin_cs = pin_cs;
-}
-
-static void rp2040_hwspi_readwritev(implements_spi *_self, const struct bidi_iovec *iov, int iovcnt) {
- struct rp2040_hwspi *self = VCALL_SELF(struct rp2040_hwspi, implements_spi, _self);
- assert(self);
- spi_inst_t *inst = self->inst;
-
- assert(inst);
- assert(iov);
- assert(iovcnt);
-
- gpio_put(self->pin_cs, 0);
- /* TODO: Replace blocking reads+writes with DMA. */
- for (int i = 0; i < iovcnt; i++) {
- if (iov[i].iov_write_src && iov[i].iov_read_dst)
- spi_write_read_blocking(inst, iov[i].iov_write_src, iov[i].iov_read_dst, iov[i].iov_len);
- else if (iov[i].iov_write_src)
- spi_write_blocking(inst, iov[i].iov_write_src, iov[i].iov_len);
- else if (iov[i].iov_read_dst)
- spi_read_blocking(inst, 0, iov[i].iov_read_dst, iov[i].iov_len);
- else
- assert(false);
- }
- gpio_put(self->pin_cs, 1);
-}
diff --git a/libhw/rp2040_hwtimer.c b/libhw/rp2040_hwtimer.c
deleted file mode 100644
index 54bdab3..0000000
--- a/libhw/rp2040_hwtimer.c
+++ /dev/null
@@ -1,166 +0,0 @@
-/* libhw/rp2040_hwtimer.c - <libhw/generic/alarmclock.h> implementation for the RP2040's hardware timer
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#include <assert.h>
-
-#include <hardware/irq.h> /* pico-sdk:hardware_irq */
-#include <hardware/timer.h> /* pico-sdk:hardware_timer */
-
-#include <libcr/coroutine.h>
-#include <libmisc/vcall.h>
-
-#define IMPLEMENTATION_FOR_LIBHW_GENERIC_ALARMCLOCK_H YES
-#include <libhw/generic/alarmclock.h>
-#include <libhw/rp2040_hwtimer.h>
-
-/* Types **********************************************************************/
-
-struct rp2040_hwtimer {
- implements_alarmclock;
- enum rp2040_hwalarm_instance alarm_num;
- bool initialized;
- struct alarmclock_trigger *queue;
-};
-
-/* Globals ********************************************************************/
-
-static uint64_t rp2040_hwtimer_get_time_ns(implements_alarmclock *self);
-static bool rp2040_hwtimer_add_trigger(implements_alarmclock *self,
- struct alarmclock_trigger *trigger,
- uint64_t fire_at_ns,
- void (*cb)(void *),
- void *cb_arg);
-static void rp2040_hwtimer_del_trigger(implements_alarmclock *self,
- struct alarmclock_trigger *trigger);
-
-static struct alarmclock_vtable rp2040_hwtimer_vtable = {
- .get_time_ns = rp2040_hwtimer_get_time_ns,
- .add_trigger = rp2040_hwtimer_add_trigger,
- .del_trigger = rp2040_hwtimer_del_trigger,
-};
-
-static struct rp2040_hwtimer hwtimers[] = {
- { .vtable = &rp2040_hwtimer_vtable, .alarm_num = 0 },
- { .vtable = &rp2040_hwtimer_vtable, .alarm_num = 1 },
- { .vtable = &rp2040_hwtimer_vtable, .alarm_num = 2 },
- { .vtable = &rp2040_hwtimer_vtable, .alarm_num = 3 },
-};
-static_assert(sizeof(hwtimers)/sizeof(hwtimers[0]) == _RP2040_HWALARM_NUM);
-
-implements_alarmclock *bootclock = &hwtimers[0];
-
-/* Main implementation ********************************************************/
-
-implements_alarmclock *rp2040_hwtimer(enum rp2040_hwalarm_instance alarm_num) {
- assert(alarm_num < _RP2040_HWALARM_NUM);
- return &hwtimers[alarm_num];
-}
-
-
-static uint64_t rp2040_hwtimer_get_time_ns(implements_alarmclock *) {
- return timer_time_us_64(timer_hw) * (NS_PER_S/US_PER_S);
-}
-
-#define NS_TO_US_ROUNDUP(x) ( ( (x) + (NS_PER_S/US_PER_S)-1) / (NS_PER_S/US_PER_S) )
-
-static void rp2040_hwtimer_intrhandler(void) {
- uint irq_num = __get_current_exception() - VTABLE_FIRST_IRQ;
- enum rp2040_hwalarm_instance alarm_num = TIMER_ALARM_NUM_FROM_IRQ(irq_num);
- assert(alarm_num < _RP2040_HWALARM_NUM);
-
- struct rp2040_hwtimer *alarmclock = &hwtimers[alarm_num];
-
- while (alarmclock->queue &&
- NS_TO_US_ROUNDUP(alarmclock->queue->fire_at_ns) <= timer_time_us_64(timer_hw)) {
- struct alarmclock_trigger *trigger = alarmclock->queue;
- trigger->cb(trigger->cb_arg);
- alarmclock->queue = trigger->next;
- trigger->alarmclock = NULL;
- trigger->next = NULL;
- trigger->prev = NULL;
- }
-
- hw_clear_bits(&timer_hw->intf, 1 << alarm_num);
- if (alarmclock->queue)
- timer_hw->alarm[alarm_num] = (uint32_t)NS_TO_US_ROUNDUP(alarmclock->queue->fire_at_ns);
-}
-
-static bool rp2040_hwtimer_add_trigger(implements_alarmclock *_alarmclock,
- struct alarmclock_trigger *trigger,
- uint64_t fire_at_ns,
- void (*cb)(void *),
- void *cb_arg) {
- struct rp2040_hwtimer *alarmclock =
- VCALL_SELF(struct rp2040_hwtimer, implements_alarmclock, _alarmclock);
- assert(alarmclock);
- assert(trigger);
- assert(fire_at_ns);
- assert(cb);
-
- uint64_t now_us = timer_time_us_64(timer_hw);
- if (NS_TO_US_ROUNDUP(fire_at_ns) > now_us &&
- (NS_TO_US_ROUNDUP(fire_at_ns) - now_us) > UINT32_MAX)
- /* Too far in the future. */
- return true;
-
- trigger->alarmclock = alarmclock;
- trigger->fire_at_ns = fire_at_ns;
- trigger->cb = cb;
- trigger->cb_arg = cb_arg;
-
- cr_disable_interrupts();
- struct alarmclock_trigger **dst = &alarmclock->queue;
- while (*dst && fire_at_ns >= (*dst)->fire_at_ns)
- dst = &(*dst)->next;
- trigger->next = *dst;
- trigger->prev = *dst ? (*dst)->prev : NULL;
- if (*dst)
- (*dst)->prev = trigger;
- *dst = trigger;
- if (!alarmclock->initialized) {
- hw_set_bits(&timer_hw->inte, 1 << alarmclock->alarm_num);
- irq_set_exclusive_handler(TIMER_ALARM_IRQ_NUM(timer_hw, alarmclock->alarm_num),
- rp2040_hwtimer_intrhandler);
- irq_set_enabled(TIMER_ALARM_IRQ_NUM(timer_hw, alarmclock->alarm_num), true);
- alarmclock->initialized = true;
- }
- if (alarmclock->queue == trigger) {
- /* Force the interrupt handler to trigger as soon as
- * we enable interrupts. This handles the case of
- * when fire_at_ns is before when we called
- * cr_disable_interrupts(). We could check
- * timer_time_us_64() again after calling
- * cr_disable_interrupts() and do this conditionally,
- * but I don't think that would be any more efficient
- * than just letting the interrupt fire. */
- hw_set_bits(&timer_hw->intf, 1 << alarmclock->alarm_num);
- }
- cr_enable_interrupts();
-
- return false;
-}
-
-static void rp2040_hwtimer_del_trigger(implements_alarmclock *_alarmclock,
- struct alarmclock_trigger *trigger) {
- struct rp2040_hwtimer *alarmclock =
- VCALL_SELF(struct rp2040_hwtimer, implements_alarmclock, _alarmclock);
- assert(alarmclock);
- assert(trigger);
-
- cr_disable_interrupts();
- if (trigger->alarmclock == alarmclock) {
- if (!trigger->prev)
- alarmclock->queue = trigger->next;
- else
- trigger->prev->next = trigger->next;
- if (trigger->next)
- trigger->next->prev = trigger->prev;
- trigger->alarmclock = NULL;
- trigger->prev = NULL;
- trigger->next = NULL;
- }
- cr_enable_interrupts();
-}
diff --git a/libhw/rp2040_include/libhw/rp2040_hwspi.h b/libhw/rp2040_include/libhw/rp2040_hwspi.h
deleted file mode 100644
index 19fb4ba..0000000
--- a/libhw/rp2040_include/libhw/rp2040_hwspi.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/* libhw/rp2040_hwspi.h - <libhw/generic/spi.h> implementation for the RP2040's ARM Primecell SSP (PL022)
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#ifndef _LIBHW_RP2040_HWSPI_H_
-#define _LIBHW_RP2040_HWSPI_H_
-
-#include <pico/binary_info.h> /* for bi_* */
-
-#include <libmisc/private.h>
-
-#include <libhw/generic/spi.h>
-
-enum rp2040_hwspi_instance {
- RP2040_HWSPI_0 = 0,
- RP2040_HWSPI_1 = 1,
-};
-
-struct rp2040_hwspi {
- implements_spi;
-
- BEGIN_PRIVATE(LIBHW_RP2040_HWSPI_H)
- void /*spi_inst_t*/ *inst;
- uint pin_cs;
- END_PRIVATE(LIBHW_RP2040_HWSPI_H)
-};
-
-/**
- * Initialize an instance of `struct rp2040_hwspi`.
- *
- * @param self : struct rp2040_hwspi : the structure to initialize
- * @param name : char * : a name for the SPI port; to include in the bininfo
- * @param inst_num : enum rp2040_hwspi_instance : the PL220 instance number; RP2040_HWSPI_{0,1}
- * @param mode : enum spi_mode : the SPI mode; SPI_MODE_{0..3}
- * @param pin_miso : uint : pin number; 0, 4, 16, or 20 for _HWSPI_0; 8, 12, 24, or 28 for _HWSPI_1
- * @param pin_mosi : uint : pin number; 3, 7, 19, or 23 for _HWSPI_0; 11, 15, or 27 for _HWSPI_1
- * @param pin_clk : uint : pin number; 2, 6, 18, or 22 for _HWSPI_0; 10, 14, or 26 for _HWSPI_1
- * @param pin_cs : uint : pin number; any unused GPIO pin
- *
- * There is no bit-order argument; the RP2040's hardware SPI always
- * uses MSB-first bit order.
- */
-#define rp2040_hwspi_init(self, name, \
- inst_num, mode, baudrate_hz, \
- pin_miso, pin_mosi, pin_clk, pin_cs) \
- do { \
- bi_decl(bi_4pins_with_names(pin_miso, name" SPI MISO", \
- pin_mosi, name" SPI MOSI", \
- pin_mosi, name" SPI CLK", \
- pin_mosi, name" SPI CS")); \
- _rp2040_hwspi_init(self, \
- inst_num, mode, baudrate_hz, \
- pin_miso, pin_mosi, pin_clk, pin_cs); \
- } while(0)
-void _rp2040_hwspi_init(struct rp2040_hwspi *self,
- enum rp2040_hwspi_instance inst_num,
- enum spi_mode mode,
- uint baudrate_hz,
- uint pin_miso,
- uint pin_mosi,
- uint pin_clk,
- uint pin_cs);
-
-#endif /* _LIBHW_RP2040_HWSPI_H_ */
diff --git a/libhw/rp2040_include/libhw/rp2040_hwtimer.h b/libhw/rp2040_include/libhw/rp2040_hwtimer.h
deleted file mode 100644
index 0885edf..0000000
--- a/libhw/rp2040_include/libhw/rp2040_hwtimer.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* libhw/rp2040_hwtimer.h - <libhw/generic/alarmclock.h> implementation for the RP2040's hardware timer
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-#ifndef _LIBHW_RP2040_HWALARM_H_
-#define _LIBHW_RP2040_HWALARM_H_
-
-#include <libhw/generic/alarmclock.h>
-
-/**
- * The RP2040 has one system "timer" (which we also use for
- * ./rp2040_bootclock.c) with 4 alarm interrupts.
- */
-enum rp2040_hwalarm_instance {
- RP2040_HWALARM_0 = 0,
- RP2040_HWALARM_1 = 1,
- RP2040_HWALARM_2 = 2,
- RP2040_HWALARM_3 = 3,
- _RP2040_HWALARM_NUM,
-};
-
-implements_alarmclock *rp2040_hwtimer(enum rp2040_hwalarm_instance alarm_num);
-
-#endif /* _LIBHW_RP2040_HWALARM_H_ */
diff --git a/libhw/rp2040_include/libhw/w5500.h b/libhw/rp2040_include/libhw/w5500.h
deleted file mode 100644
index 7538bbc..0000000
--- a/libhw/rp2040_include/libhw/w5500.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/* libhw/w5500.h - <libhw/generic/net.h> implementation for the WIZnet W5500 chip
- *
- * Copyright (C) 2024 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/sema.h>
-#include <libcr_ipc/mutex.h>
-#include <libmisc/private.h>
-
-#include <libhw/generic/net.h>
-#include <libhw/generic/spi.h>
-
-struct _w5500_socket {
- /* const-after-init */
- implements_net_stream_listener implements_net_stream_listener;
- implements_net_stream_conn implements_net_stream_conn;
- implements_net_packet_conn implements_net_packet_conn;
- BEGIN_PRIVATE(LIBHW_W5500_H)
- 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} */
- bool list_open, read_open, write_open; /* MODE_TCP */
-
- cr_mutex_t cmd_mu;
- END_PRIVATE(LIBHW_W5500_H)
-};
-
-struct w5500 {
- /* const-after-init */
- implements_net_iface;
- BEGIN_PRIVATE(LIBHW_W5500_H)
- implements_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;
- END_PRIVATE(LIBHW_W5500_H)
-};
-
-/**
- * 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,
- implements_spi *spi, uint pin_intr, uint pin_reset,
- struct net_eth_addr addr);
-
-/**
- * TODO.
- */
-void w5500_hard_reset(struct w5500 *self);
-
-/**
- * TODO.
- */
-void w5500_soft_reset(struct w5500 *self);
-
-#endif /* _LIBHW_W5500_H_ */
diff --git a/libhw/w5500.c b/libhw/w5500.c
deleted file mode 100644
index ae48a61..0000000
--- a/libhw/w5500.c
+++ /dev/null
@@ -1,836 +0,0 @@
-/* libhw/w5500.c - <libhw/generic/net.h> implementation for the WIZnet W5500 chip
- *
- * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
- * SPDX-License-Identifier: AGPL-3.0-or-later
- *
- * -----------------------------------------------------------------------------
- * https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/Ethernet/wizchip_conf.c
- * https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/Ethernet/W5500/w5500.h
- * https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/Ethernet/W5500/w5500.c
- * https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/Ethernet/socket.c
- *
- * Copyright (c) 2013, WIZnet Co., LTD.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of the <ORGANIZATION> nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- *
- * -----------------------------------------------------------------------------
- * https://github.com/Wiznet/ioLibrary_Driver/blob/b981401e7f3d07015619adf44c13998e13e777f9/license.txt
- *
- * Copyright (c) 2014 WIZnet Co.,Ltd.
- * Copyright (c) WIZnet ioLibrary Project.
- * All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- *
- * SPDX-License-Identifier: MIT
- */
-
-/* TODO: Write a <libhw/generic/gpio.h> to avoid w5500.c being
- * pico-sdk-specific. */
-#include <hardware/gpio.h> /* pico-sdk:hardware_gpio */
-
-#include <libcr/coroutine.h> /* for cr_yield() */
-#include <libmisc/vcall.h> /* for VCALL_SELF() */
-
-#include <libhw/generic/alarmclock.h> /* for sleep_*() */
-
-#define IMPLEMENTATION_FOR_LIBHW_W5500_H YES
-#include <libhw/w5500.h>
-
-#include "w5500_ll.h"
-
-/* Config *********************************************************************/
-
-#include "config.h"
-
-/* These are the default values of the Linux kernel's
- * net.ipv4.ip_local_port_range, so I figure they're probably good
- * values to use. */
-#ifndef CONFIG_W5500_LOCAL_PORT_MIN
- #define CONFIG_W5500_LOCAL_PORT_MIN 32768
-#endif
-
-#ifndef CONFIG_W5500_LOCAL_PORT_MAX
- #define CONFIG_W5500_LOCAL_PORT_MAX 60999
-#endif
-
-#ifndef CONFIG_W5500_NUM
- #error config.h must define CONFIG_W5500_NUM
-#endif
-
-/* C language *****************************************************************/
-
-#define UNUSED(name)
-#define ARRAY_LEN(ary) (sizeof(ary)/sizeof((ary)[0]))
-
-/* vtables ********************************************************************/
-
-/* iface */
-static struct net_eth_addr w5500_if_hwaddr (implements_net_iface *);
-static void w5500_if_up (implements_net_iface *, struct net_iface_config);
-static void w5500_if_down (implements_net_iface *);
-static implements_net_stream_listener *w5500_if_tcp_listen (implements_net_iface *, uint16_t local_port);
-static implements_net_stream_conn *w5500_if_tcp_dial (implements_net_iface *, struct net_ip4_addr, uint16_t remote_port);
-static implements_net_packet_conn *w5500_if_udp_conn (implements_net_iface *, uint16_t local_port);
-
-/* stream_listener */
-static implements_net_stream_conn *w5500_tcplist_accept(implements_net_stream_listener *);
-static int w5500_tcplist_close (implements_net_stream_listener *);
-
-/* stream_conn */
-static void w5500_tcp_set_read_deadline (implements_net_stream_conn *, uint64_t ns);
-static ssize_t w5500_tcp_read (implements_net_stream_conn *, void *, size_t);
-static ssize_t w5500_tcp_write (implements_net_stream_conn *, void *, size_t);
-static int w5500_tcp_close (implements_net_stream_conn *, bool rd, bool wr);
-
-/* packet_conn */
-static void w5500_udp_set_read_deadline (implements_net_packet_conn *, uint64_t ns);
-static ssize_t w5500_udp_recvfrom (implements_net_packet_conn *, void *, size_t, struct net_ip4_addr *, uint16_t *);
-static ssize_t w5500_udp_sendto (implements_net_packet_conn *, void *, size_t, struct net_ip4_addr, uint16_t);
-static int w5500_udp_close (implements_net_packet_conn *);
-
-/* tables */
-
-static struct net_iface_vtable w5500_iface_vtable = {
- .hwaddr = w5500_if_hwaddr,
- .ifup = w5500_if_up,
- .ifdown = w5500_if_down,
- .tcp_listen = w5500_if_tcp_listen,
- .tcp_dial = w5500_if_tcp_dial,
- .udp_conn = w5500_if_udp_conn,
-};
-
-static struct net_stream_listener_vtable w5500_tcp_listener_vtable = {
- .accept = w5500_tcplist_accept,
- .close = w5500_tcplist_close,
-};
-
-static struct net_stream_conn_vtable w5500_tcp_conn_vtable = {
- .set_read_deadline = w5500_tcp_set_read_deadline,
- .read = w5500_tcp_read,
- .write = w5500_tcp_write,
- .close = w5500_tcp_close,
-};
-
-static struct net_packet_conn_vtable w5500_udp_conn_vtable = {
- .set_read_deadline = w5500_udp_set_read_deadline,
- .recvfrom = w5500_udp_recvfrom,
- .sendto = w5500_udp_sendto,
- .close = w5500_udp_close,
-};
-
-/* mid-level utilities ********************************************************/
-
-static uint16_t w5500_alloc_local_port(struct w5500 *self) {
- assert(self);
- uint16_t ret = self->next_local_port++;
- if (self->next_local_port > CONFIG_W5500_LOCAL_PORT_MAX)
- self->next_local_port = CONFIG_W5500_LOCAL_PORT_MIN;
- return ret;
-}
-
-static struct _w5500_socket *w5500_alloc_socket(struct w5500 *self) {
- assert(self);
- struct _w5500_socket *sock = self->free;
- if (!sock)
- return NULL;
- self->free = sock->next_free;
- sock->next_free = NULL;
- assert(sock->mode == W5500_MODE_NONE);
- return sock;
-}
-
-static void w5500_free_socket(struct w5500 *self, struct _w5500_socket *sock) {
- assert(self);
- assert(sock);
- sock->mode = W5500_MODE_NONE;
- sock->next_free = self->free;
- self->free = sock;
-}
-
-static void w5500_tcp_maybe_free(struct w5500 *chip, struct _w5500_socket *sock) {
- assert(chip);
- assert(sock);
- assert(sock->mode == W5500_MODE_TCP);
- if (!sock->list_open && !sock->read_open && !sock->write_open)
- w5500_free_socket(chip, sock);
-}
-
-static COROUTINE w5500_irq_cr(void *_chip) {
- struct w5500 *chip = _chip;
- cr_begin();
-
- for (;;) {
- cr_sema_wait(&chip->intr);
- if (w5500ll_read_common_reg(chip->spidev, chip_interrupt))
- w5500ll_write_common_reg(chip->spidev, chip_interrupt, 0xFF);
-
- uint8_t sockmask = w5500ll_read_common_reg(chip->spidev, sock_interrupt);
- for (uint8_t socknum = 0; socknum < 8; socknum++) {
- if (!(sockmask & (1<<socknum)))
- continue;
- struct _w5500_socket *socket = &chip->sockets[socknum];
-
- uint8_t sockintr = w5500ll_read_sock_reg(chip->spidev, socknum, interrupt);
-
- switch (socket->mode) {
- case W5500_MODE_NONE:
- break;
- case W5500_MODE_TCP:
- /* SOCKINTR_SEND_OK is useless; just count on the write methods to
- * poll instead of waiting on notification from here. */
- uint8_t listen_bits = sockintr & (SOCKINTR_TIMEOUT|SOCKINTR_CONN),
- read_bits = sockintr & (SOCKINTR_TIMEOUT|SOCKINTR_RECV|SOCKINTR_FIN);
-
- if (listen_bits)
- cr_sema_signal(&socket->listen_sema);
- /* fallthrough */
- case W5500_MODE_UDP:
- if (read_bits)
- cr_sema_signal(&socket->read_sema);
- }
-
- w5500ll_write_sock_reg(chip->spidev, socknum, interrupt, sockintr);
- }
- }
-
- cr_end();
-}
-
-static struct w5500 *w5500_socket_chip(struct _w5500_socket *socket) {
- assert(socket);
- assert(socket->socknum < 8);
-
- struct _w5500_socket *sock0 = &socket[-(socket->socknum)];
- assert(sock0);
- struct w5500 *chip =
- ((void *)sock0) - offsetof(struct w5500, sockets);
- assert(chip);
- return chip;
-}
-
-static inline void w5500_socket_cmd(struct _w5500_socket *socket, uint8_t cmd) {
- assert(socket);
- struct w5500 *chip = w5500_socket_chip(socket);
- uint8_t socknum = socket->socknum;
-
- cr_mutex_lock(&socket->cmd_mu);
- w5500ll_write_sock_reg(chip->spidev, socknum, command, cmd);
- while (w5500ll_read_sock_reg(chip->spidev, socknum, command) != 0x00)
- cr_yield();
- cr_mutex_unlock(&socket->cmd_mu);
-}
-
-static inline void w5500_socket_close(struct _w5500_socket *socket) {
- assert(socket);
- struct w5500 *chip = w5500_socket_chip(socket);
- uint8_t socknum = socket->socknum;
-
- /* Send CMD_CLOSE. */
- w5500_socket_cmd(socket, CMD_CLOSE);
- /* Wait for it to transition to STATE_CLOSED. */
- while (w5500ll_read_sock_reg(chip->spidev, socknum, state) != STATE_CLOSED)
- cr_yield();
-
- /* Update our MCU-side bookkeeping. */
- socket->mode = W5500_MODE_NONE;
- socket->port = 0;
- socket->read_deadline_ns = 0;
- socket->read_open = socket->write_open = false;
-}
-
-#define ASSERT_SELF(_iface, _mode) \
- struct _w5500_socket *socket = \
- VCALL_SELF(struct _w5500_socket, \
- implements_net_##_iface, _socket); \
- assert(socket); \
- uint8_t socknum = socket->socknum; \
- assert(socknum < 8); \
- assert(socket->mode == W5500_MODE_##_mode); \
- struct w5500 *chip = w5500_socket_chip(socket); \
- assert(chip);
-
-/* init() *********************************************************************/
-
-static struct w5500 *w5500_chips[CONFIG_W5500_NUM] = {0};
-
-static void w5500_intrhandler(uint gpio, uint32_t UNUSED(event_mask)) {
- for (size_t i = 0; i < ARRAY_LEN(w5500_chips); i++)
- if (w5500_chips[i] && w5500_chips[i]->pin_intr == gpio)
- cr_sema_signal_from_intrhandler(&w5500_chips[i]->intr);
-}
-
-void _w5500_init(struct w5500 *chip,
- implements_spi *spi, uint pin_intr, uint pin_reset,
- struct net_eth_addr addr) {
- assert(chip);
- assert(spi);
-
- /* Initialize the data structures. */
- *chip = (struct w5500){
- /* const-after-init */
- .implements_net_iface = { .vtable = &w5500_iface_vtable },
- .spidev = spi,
- .pin_intr = pin_intr,
- .pin_reset = pin_reset,
- .hwaddr = addr,
- /* mutable */
- .next_local_port = CONFIG_W5500_LOCAL_PORT_MIN,
- };
- chip->free = &chip->sockets[0];
- for (uint8_t i = 0; i < 8; i++) {
- chip->sockets[i] = (struct _w5500_socket){
- /* const-after-init */
- .implements_net_stream_listener = { .vtable = &w5500_tcp_listener_vtable },
- .implements_net_stream_conn = { .vtable = &w5500_tcp_conn_vtable },
- .implements_net_packet_conn = { .vtable = &w5500_udp_conn_vtable },
- .socknum = i,
- /* mutable */
- .next_free = (i + 1 < 8) ? &chip->sockets[i+1] : NULL,
- /* the rest of the mutable members get
- * initialized to the zero values */
- };
- }
-
- /* Initialize the hardware. */
- gpio_set_irq_enabled_with_callback(pin_intr, GPIO_IRQ_EDGE_FALL, true, w5500_intrhandler);
- gpio_set_dir(chip->pin_reset, GPIO_OUT);
- w5500_hard_reset(chip);
-
- /* Finally, wire in the interrupt handler. */
- cr_disable_interrupts();
- for (size_t i = 0; i < ARRAY_LEN(w5500_chips); i++) {
- if (w5500_chips[i] == NULL) {
- w5500_chips[i] = chip;
- break;
- }
- }
- cr_enable_interrupts();
- coroutine_add(w5500_irq_cr, chip);
-}
-
-/* chip methods ***************************************************************/
-
-static inline void w5500_post_reset(struct w5500 *chip) {
- /* The W5500 does not have a built-in MAC address, we must
- * provide one. */
- w5500ll_write_common_reg(chip->spidev, eth_addr, chip->hwaddr);
-
- /* The RP2040 needs a 1/sys_clk hysteresis between interrupts
- * for us to notice them. At the maximum-rated clock-rate of
- * 133MHz, that means 7.5ns (but the sbc-harness overclocks
- * the RP2040, so we could get away with even shorter).
- *
- * If intlevel is non-zero, then the hysteresis is
- * (intlevel+1)*4/(150MHz), or (intlevel+1)*26.7ns; so even
- * the shortest-possible hysteresis much larger than necessary
- * for us. */
- w5500ll_write_common_reg(chip->spidev, intlevel, uint16be_marshal(1));
-
- /* This implementation does not care about any of the
- * chip-level interrupts. */
- w5500ll_write_common_reg(chip->spidev, chip_interrupt_mask, 0);
-
- /* This implementation cares about interrupts for each
- * socket. */
- w5500ll_write_common_reg(chip->spidev, sock_interrupt_mask, 0xFF);
-
- /* Configure retry/timeout.
- *
- * timeout_arp = 0.1ms * retry_time * (retry_count+1)
- *
- * retry_count
- * timeout_tcp = 0.1ms * retry_time * Σ 2^min(n, floor(1+log_2(65535/retry_time)))
- * n=0
- *
- * For retry_time=2000, retry_count=3, this means
- *
- * timeout_arp = 0.8s
- * timeout_tcp = 3.0s
- */
- w5500ll_write_common_reg(chip->spidev, retry_time, uint16be_marshal(2000));
- w5500ll_write_common_reg(chip->spidev, retry_count, 3);
-}
-
-void w5500_hard_reset(struct w5500 *chip) {
- gpio_put(chip->pin_reset, 0);
- sleep_for_ms(1); /* minimum of 500us */
- gpio_put(chip->pin_reset, 1);
- sleep_for_ms(2); /* minimum of 1ms */
-
- w5500_post_reset(chip);
-}
-
-void w5500_soft_reset(struct w5500 *chip) {
- w5500ll_write_common_reg(chip->spidev, mode, CHIPMODE_RST);
- while (w5500ll_read_common_reg(chip->spidev, mode) & CHIPMODE_RST)
- cr_yield();
-
- w5500_post_reset(chip);
-}
-
-static struct net_eth_addr w5500_if_hwaddr(implements_net_iface *_chip) {
- struct w5500 *chip = VCALL_SELF(struct w5500, implements_net_iface, _chip);
- assert(chip);
-
- return chip->hwaddr;
-}
-
-static void w5500_if_up(implements_net_iface *_chip, struct net_iface_config cfg) {
- struct w5500 *chip = VCALL_SELF(struct w5500, implements_net_iface, _chip);
- assert(chip);
-
- w5500ll_write_common_reg(chip->spidev, ip_gateway_addr, cfg.gateway_addr);
- w5500ll_write_common_reg(chip->spidev, ip_subnet_mask, cfg.subnet_mask);
- w5500ll_write_common_reg(chip->spidev, ip_addr, cfg.addr);
-}
-
-static void w5500_if_down(implements_net_iface *_chip) {
- w5500_if_up(_chip, (struct net_iface_config){0});
-}
-
-static implements_net_stream_listener *w5500_if_tcp_listen(implements_net_iface *_chip, uint16_t local_port) {
- struct w5500 *chip = VCALL_SELF(struct w5500, implements_net_iface, _chip);
- assert(chip);
-
- struct _w5500_socket *sock = w5500_alloc_socket(chip);
- if (!sock)
- return NULL;
-
- if (!local_port)
- local_port = w5500_alloc_local_port(chip);
-
- assert(sock->mode == W5500_MODE_NONE);
- sock->mode = W5500_MODE_TCP;
- sock->port = local_port;
- sock->read_deadline_ns = 0;
- sock->list_open = true;
-
- return &sock->implements_net_stream_listener;
-}
-
-static implements_net_stream_conn *w5500_if_tcp_dial(implements_net_iface *_chip,
- struct net_ip4_addr node, uint16_t port) {
- struct w5500 *chip = VCALL_SELF(struct w5500, implements_net_iface, _chip);
- assert(chip);
- assert(memcmp(node.octets, net_ip4_addr_zero.octets, 4));
- assert(memcmp(node.octets, net_ip4_addr_broadcast.octets, 4));
- assert(port);
-
- struct _w5500_socket *socket = w5500_alloc_socket(chip);
- if (!socket)
- return NULL;
- uint8_t socknum = socket->socknum;
-
- uint16_t local_port = w5500_alloc_local_port(chip);
-
- assert(socket->mode == W5500_MODE_NONE);
- socket->mode = W5500_MODE_TCP;
- socket->port = local_port;
- socket->read_deadline_ns = 0;
- socket->read_open = socket->write_open = true;
-
- restart:
- /* Mimics socket.c:socket(). */
- w5500_socket_close(socket);
- w5500ll_write_sock_reg(chip->spidev, socknum, mode, SOCKMODE_TCP);
- w5500ll_write_sock_reg(chip->spidev, socknum, local_port, uint16be_marshal(socket->port));
- w5500_socket_cmd(socket, CMD_OPEN);
- while (w5500ll_read_sock_reg(chip->spidev, socknum, state) != STATE_TCP_INIT)
- cr_yield();
-
- /* Mimics socket.c:connect(). */
- w5500ll_write_sock_reg(chip->spidev, socknum, remote_ip_addr, node);
- w5500ll_write_sock_reg(chip->spidev, socknum, remote_port, uint16be_marshal(port));
- w5500_socket_cmd(socket, CMD_CONNECT);
- for (;;) {
- uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
- switch (state) {
- case STATE_TCP_SYNSENT:
- cr_yield();
- break;
- case STATE_TCP_ESTABLISHED:
- return &socket->implements_net_stream_conn;
- default:
- goto restart;
- }
- }
-}
-
-implements_net_packet_conn *w5500_if_udp_conn(implements_net_iface *_chip, uint16_t local_port) {
- struct w5500 *chip = VCALL_SELF(struct w5500, implements_net_iface, _chip);
- assert(chip);
-
- struct _w5500_socket *socket = w5500_alloc_socket(chip);
- if (!socket)
- return NULL;
-
- if (!local_port)
- local_port = w5500_alloc_local_port(chip);
-
- assert(socket->mode == W5500_MODE_NONE);
- socket->mode = W5500_MODE_UDP;
- socket->port = local_port;
- socket->read_deadline_ns = 0;
-
- return &socket->implements_net_packet_conn;
-}
-
-/* tcp_listener methods *******************************************************/
-
-static implements_net_stream_conn *w5500_tcplist_accept(implements_net_stream_listener *_socket) {
- ASSERT_SELF(stream_listener, TCP);
-
- restart:
- if (!socket->list_open)
- return NULL;
-
- /* Mimics socket.c:socket(). */
- w5500_socket_close(socket);
- w5500ll_write_sock_reg(chip->spidev, socknum, mode, SOCKMODE_TCP);
- w5500ll_write_sock_reg(chip->spidev, socknum, local_port, uint16be_marshal(socket->port));
- w5500_socket_cmd(socket, CMD_OPEN);
- while (w5500ll_read_sock_reg(chip->spidev, socknum, state) != STATE_TCP_INIT)
- cr_yield();
-
- /* Mimics socket.c:listen(). */
- w5500_socket_cmd(socket, CMD_LISTEN);
- for (;;) {
- uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
- switch (state) {
- case STATE_TCP_LISTEN:
- case STATE_TCP_SYNRECV:
- cr_sema_wait(&socket->listen_sema);
- break;
- case STATE_TCP_ESTABLISHED:
- socket->read_open = true;
- /* fall-through */
- case STATE_TCP_CLOSE_WAIT:
- socket->write_open = true;
- return &socket->implements_net_stream_conn;
- default:
- goto restart;
- }
- }
-}
-
-static int w5500_tcplist_close(implements_net_stream_listener *_socket) {
- ASSERT_SELF(stream_listener, TCP);
-
- socket->list_open = false;
- w5500_tcp_maybe_free(chip, socket);
- return 0;
-}
-
-/* tcp_conn methods ***********************************************************/
-
-static ssize_t w5500_tcp_write(implements_net_stream_conn *_socket, void *buf, size_t count) {
- ASSERT_SELF(stream_conn, TCP);
- assert(buf);
- assert(count);
-
- /* What we really want is to pause until we receive an ACK for
- * some data we just queued, so that we can line up some new
- * data to keep the buffer full. But that's not what
- * SEND_FINISHED does AIUI, the SEND_FINISHED interrupt
- * doesn't fire until we receive the *last* ACK for the data,
- * when the buffer is entirely empty.
- *
- * Which means we basically have to busy-poll for space in the
- * buffer becoming available.
- *
- * We'll add more data to the buffer whenever there is
- * `min_free_space` in the buffer (or the rest of the data
- * fits in the buffer).
- *
- * This `min_free_space` can probably stand to be tuned; must
- * be >0, <=bufsize. `1500-58` is the 100BaseT MTU minus the
- * Ethernet+IP+TCP overhead. */
- uint16_t bufsize = ((uint16_t)w5500ll_read_sock_reg(chip->spidev, socknum, tx_buf_size))*1024;
- uint16_t min_free_space = MIN(1500-58, bufsize/4);
-
- size_t done = 0;
- while (done < count) {
- if (!socket->write_open)
- return -NET_ECLOSED;
- uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
- if (state != STATE_TCP_ESTABLISHED && state != STATE_TCP_CLOSE_WAIT)
- return -NET_ECLOSED;
-
- uint16_t freesize = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, tx_free_size));
- if (freesize < count-done && freesize < min_free_space) {
- /* Wait for more buffer space. */
- cr_yield();
- continue;
- }
-
- /* Queue data to be sent. */
- if ((size_t)freesize > count-done)
- freesize = count-done;
- uint16_t ptr = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, tx_write_pointer));
- w5500ll_write(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, TX), &((char *)buf)[done], freesize);
- w5500ll_write_sock_reg(chip->spidev, socknum, tx_write_pointer, uint16be_marshal(ptr+freesize));
-
- /* Submit the queue. */
- w5500_socket_cmd(socket, CMD_SEND);
- done += freesize;
- }
- return done;
-}
-
-static void w5500_tcp_set_read_deadline(implements_net_stream_conn *_socket, uint64_t ns) {
- ASSERT_SELF(stream_conn, TCP);
- socket->read_deadline_ns = ns;
-}
-
-static void w5500_tcp_alarm_handler(void *_arg) {
- struct _w5500_socket *socket = _arg;
- cr_sema_signal(&socket->read_sema);
-}
-
-static ssize_t w5500_tcp_read(implements_net_stream_conn *_socket, void *buf, size_t count) {
- ASSERT_SELF(stream_conn, TCP);
- assert(buf);
- assert(count);
-
- struct alarmclock_trigger trigger = {0};
- if (socket->read_deadline_ns)
- VCALL(bootclock, add_trigger, &trigger,
- socket->read_deadline_ns,
- w5500_tcp_alarm_handler,
- socket);
-
- /* Wait until there is data to read. */
- uint16_t avail = 0;
- for (;;) {
- if (!socket->read_open) {
- VCALL(bootclock, del_trigger, &trigger);
- return -NET_ECLOSED;
- }
- if (socket->read_deadline_ns && socket->read_deadline_ns >= VCALL(bootclock, get_time_ns)) {
- VCALL(bootclock, del_trigger, &trigger);
- return -NET_ETIMEDOUT;
- }
- uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
- switch (state) {
- case STATE_TCP_CLOSE_WAIT:
- case STATE_TCP_ESTABLISHED:
- case STATE_TCP_FIN_WAIT:
- break; /* OK */
- default:
- VCALL(bootclock, del_trigger, &trigger);
- return -NET_ECLOSED;
- }
-
- avail = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, rx_size));
- if (avail)
- /* We have data to read. */
- break;
- if (state == STATE_TCP_CLOSE_WAIT) {
- VCALL(bootclock, del_trigger, &trigger);
- return 0; /* EOF */
- }
-
- cr_sema_wait(&socket->read_sema);
- }
- assert(avail);
- uint16_t ptr = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, rx_read_pointer));
- /* Read the data. */
- if ((size_t)avail > count)
- avail = count;
- w5500ll_read(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, RX), buf, avail);
- /* Tell the chip that we read the data. */
- w5500ll_write_sock_reg(chip->spidev, socknum, rx_read_pointer, uint16be_marshal(ptr+avail));
- w5500_socket_cmd(socket, CMD_RECV);
- /* Return. */
- VCALL(bootclock, del_trigger, &trigger);
- return avail;
-}
-
-static int w5500_tcp_close(implements_net_stream_conn *_socket, bool rd, bool wr) {
- ASSERT_SELF(stream_conn, TCP);
-
- if (rd)
- socket->read_open = false;
-
- if (wr && socket->write_open) {
- w5500_socket_cmd(socket, CMD_DISCON);
- while (socket->write_open) {
- uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
- switch (state) {
- case STATE_TCP_FIN_WAIT:
- socket->write_open = false;
- /* Can still read */
- if (!socket->read_open)
- w5500_socket_close(socket);
- break;
- case STATE_CLOSED:
- socket->write_open = false;
- break;
- }
- }
- }
-
- w5500_tcp_maybe_free(chip, socket);
- return 0;
-}
-
-/* udp_conn methods ***********************************************************/
-
-static ssize_t w5500_udp_sendto(implements_net_packet_conn *_socket, void *buf, size_t count,
- struct net_ip4_addr node, uint16_t port) {
- ASSERT_SELF(packet_conn, UDP);
- assert(buf);
- assert(count);
-
- uint16_t bufsize = ((uint16_t)w5500ll_read_sock_reg(chip->spidev, socknum, tx_buf_size))*1024;
- if (count < bufsize)
- return -NET_EMSGSIZE;
-
- for (;;) {
- uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
- if (state != STATE_UDP)
- return -NET_ECLOSED;
-
- uint16_t freesize = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, tx_free_size));
- if (freesize >= count)
- /* We can send. */
- break;
-
- /* Wait for more buffer space. */
- cr_yield();
- }
-
- /* Where we're sending it. */
- w5500ll_write_sock_reg(chip->spidev, socknum, remote_ip_addr, node);
- w5500ll_write_sock_reg(chip->spidev, socknum, remote_port, uint16be_marshal(port));
- /* Queue data to be sent. */
- uint16_t ptr = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, tx_write_pointer));
- w5500ll_write(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, TX), buf, count);
- w5500ll_write_sock_reg(chip->spidev, socknum, tx_write_pointer, uint16be_marshal(ptr+count));
- /* Submit the queue. */
- w5500_socket_cmd(socket, CMD_SEND);
-
- return count;
-}
-
-static void w5500_udp_set_read_deadline(implements_net_packet_conn *_socket, uint64_t ns) {
- ASSERT_SELF(packet_conn, UDP);
- socket->read_deadline_ns = ns;
-}
-
-static void w5500_udp_alarm_handler(void *_arg) {
- struct _w5500_socket *socket = _arg;
- cr_sema_signal(&socket->read_sema);
-}
-
-static ssize_t w5500_udp_recvfrom(implements_net_packet_conn *_socket, void *buf, size_t count,
- struct net_ip4_addr *ret_node, uint16_t *ret_port) {
- ASSERT_SELF(packet_conn, UDP);
- assert(buf);
- assert(count);
-
- struct alarmclock_trigger trigger = {0};
- if (socket->read_deadline_ns)
- VCALL(bootclock, add_trigger, &trigger,
- socket->read_deadline_ns,
- w5500_udp_alarm_handler,
- socket);
-
- /* Wait until there is data to read. */
- uint16_t avail = 0;
- for (;;) {
- if (socket->read_deadline_ns && socket->read_deadline_ns >= VCALL(bootclock, get_time_ns)) {
- VCALL(bootclock, del_trigger, &trigger);
- return -NET_ETIMEDOUT;
- }
- uint8_t state = w5500ll_read_sock_reg(chip->spidev, socknum, state);
- if (state != STATE_UDP) {
- VCALL(bootclock, del_trigger, &trigger);
- return -NET_ECLOSED;
- }
-
- uint16_t avail = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, rx_size));
- if (avail)
- /* We have data to read. */
- break;
-
- cr_sema_wait(&socket->read_sema);
- }
- assert(avail >= 8);
- uint16_t ptr = uint16be_unmarshal(w5500ll_read_sock_reg(chip->spidev, socknum, rx_read_pointer));
- /* Read a munged form of the UDP packet header. I
- * can't find in the datasheet where it describes
- * this; this is based off of socket.c:recvfrom(). */
- uint8_t hdr[8];
- w5500ll_read(chip->spidev, ptr, CTL_BLOCK_SOCK(socknum, RX), hdr, sizeof(hdr));
- if (ret_node) {
- ret_node->octets[0] = hdr[0];
- ret_node->octets[1] = hdr[1];
- ret_node->octets[2] = hdr[2];
- ret_node->octets[3] = hdr[3];
- }
- if (ret_port)
- *ret_port = uint16be_decode(&hdr[4]);
- uint16_t len = uint16be_decode(&hdr[6]);
- /* Now read the actual data. */
- if (count > len)
- count = len;
- w5500ll_read(chip->spidev, ptr+8, CTL_BLOCK_SOCK(socknum, RX), buf, len);
- /* Tell the chip that we read the data. */
- w5500ll_write_sock_reg(chip->spidev, socknum, rx_read_pointer, uint16be_marshal(ptr+8+len));
- w5500_socket_cmd(socket, CMD_RECV);
- /* Return. */
- VCALL(bootclock, del_trigger, &trigger);
- return len;
-}
-
-static int w5500_udp_close(implements_net_packet_conn *_socket) {
- ASSERT_SELF(packet_conn, UDP);
-
- w5500_socket_close(socket);
- w5500_free_socket(chip, socket);
- return 0;
-}
diff --git a/libhw/w5500_ll.h b/libhw/w5500_ll.h
deleted file mode 100644
index db66f01..0000000
--- a/libhw/w5500_ll.h
+++ /dev/null
@@ -1,363 +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 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 <assert.h> /* for assert(), static_assert() */
-#include <stdint.h> /* for uint{n}_t */
-#include <string.h> /* for memcmp() */
-
-#include <libmisc/vcall.h> /* for VCALL() */
-#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 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 /* _LIBHW_W5500_LL_H_ */